@marktiderman/genesis-connect 0.1.3 → 0.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.
- package/README.md +80 -7
- package/dist/index.cjs +151 -37
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +54 -3
- package/dist/index.d.ts +54 -3
- package/dist/index.js +151 -37
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/providers.ts","../src/oauth.ts"],"names":["secretKeyBytes","utf8","base64url","fromBase64","sha256Hex","body","randomToken","extra","code","fresh"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * An OAuth 2.0 provider is plain data: where to send the person, where to\n * trade codes and refresh tokens, where to revoke. Google is built in; any\n * other authorization-code provider is an object of the same shape.\n */\nexport interface OAuthProvider {\n /** A short id, used in the default cookie name and the state key's derivation: `\"google\"`. */\n readonly id: string;\n /** The authorization endpoint the browser is sent to. */\n readonly authorizeUrl: string;\n /** The token endpoint (code and refresh-token grants). */\n readonly tokenUrl: string;\n /** The revocation endpoint (RFC 7009), when the provider has one. */\n readonly revokeUrl?: string | undefined;\n /** Scopes asked for when neither the connector nor `start` names any. */\n readonly defaultScopes?: readonly string[] | undefined;\n /**\n * Extra authorization parameters, such as Google's `access_type=offline`\n * and `prompt=consent` (which is what makes it send a refresh token).\n */\n readonly authorizeParams?: Readonly<Record<string, string>> | undefined;\n /**\n * How the client authenticates at the token endpoint: in the form body\n * (`client_secret_post`, the default) or with HTTP Basic (`client_secret_basic`).\n */\n readonly tokenAuth?: \"client_secret_post\" | \"client_secret_basic\" | undefined;\n /** Whether the revocation request also carries the client's credentials. Default false. */\n readonly revokeWithClientAuth?: boolean | undefined;\n /** How the provider joins scopes. Default a space (RFC 6749). */\n readonly scopeSeparator?: string | undefined;\n /**\n * Shorthand scopes the provider answers with their full name, so the\n * granted-scope check compares like with like: Google grants `email` as\n * `https://www.googleapis.com/auth/userinfo.email`. Both what is asked\n * for and what is granted are mapped through it.\n */\n readonly scopeAliases?: Readonly<Record<string, string>> | undefined;\n}\n\n/** Google's OAuth 2.0 endpoints, asking for offline access (a refresh token) every time. */\nexport const googleProvider: OAuthProvider = {\n id: \"google\",\n authorizeUrl: \"https://accounts.google.com/o/oauth2/v2/auth\",\n tokenUrl: \"https://oauth2.googleapis.com/token\",\n revokeUrl: \"https://oauth2.googleapis.com/revoke\",\n defaultScopes: [],\n authorizeParams: {\n access_type: \"offline\",\n // Without consent on every sign-in, Google sends a refresh token only the first time.\n prompt: \"consent\",\n // Only the scopes asked for now, so the granted-scope check means what it says.\n include_granted_scopes: \"false\",\n },\n tokenAuth: \"client_secret_post\",\n scopeAliases: {\n email: \"https://www.googleapis.com/auth/userinfo.email\",\n profile: \"https://www.googleapis.com/auth/userinfo.profile\",\n openid: \"openid\",\n },\n};\n\n/** Google scopes apps commonly connect with. Prefer the narrowest. */\nexport const GOOGLE_SCOPES = {\n /** OpenID Connect: an ID token naming the account. */\n openid: \"openid\",\n /** The account's email address (Google grants it as `…/auth/userinfo.email`). */\n email: \"email\",\n /** The account's name and picture (granted as `…/auth/userinfo.profile`). */\n profile: \"profile\",\n /** Files the person picked or created through the app, and nothing else. */\n driveFile: \"https://www.googleapis.com/auth/drive.file\",\n /** Every spreadsheet the person can reach. Prefer `driveFile`. */\n spreadsheets: \"https://www.googleapis.com/auth/spreadsheets\",\n /** Events on the person's calendars. */\n calendarEvents: \"https://www.googleapis.com/auth/calendar.events\",\n /** Send mail as the person. */\n gmailSend: \"https://www.googleapis.com/auth/gmail.send\",\n} as const;\n","/**\n * \"Connect an outside account\" on Web `Request` and `Response`: the OAuth 2.0\n * authorization-code flow with PKCE, for any framework that speaks fetch\n * (Next.js route handlers, TanStack Start, Hono, Remix, Workers).\n *\n * start the app has signed the person in and says who they are; the\n * kit answers the provider's sign-in URL and a cookie. The URL\n * carries a signed, short-lived state (who, what for, where to\n * come back to, a nonce); the cookie (HttpOnly, SameSite=Lax)\n * carries the nonce and the PKCE verifier, so only the browser\n * that started can finish.\n * callback checks the state's signature and age and that the cookie's\n * nonce matches, handles a refusal, trades the code (PKCE, never\n * following a redirect), checks every scope asked for was\n * granted, seals the refresh token with the keyring and hands it\n * to the app's `store`. Then back to where it started with a\n * short result: connected, expired, browser, denied, scope or\n * failed.\n * accessToken / revoke open a sealed refresh token to get a fresh access\n * token (cached in memory until a minute before it expires), or\n * to revoke the grant.\n *\n * One callback origin can serve many: pin `redirectOrigin` (the one address\n * registered with the provider) and set `sharedDomain`, and a flow started on\n * any https address under that domain finishes on the pinned one and returns\n * to the address it started on, and to no other.\n *\n * Never logs or returns a token, a code, a client secret or a key; token\n * requests never follow redirects.\n */\nimport {\n base64url,\n fromBase64,\n randomToken,\n sha256Hex,\n utf8,\n type WebCryptoKey,\n} from \"./encoding\";\nimport type { OAuthProvider } from \"./providers\";\nimport { secretKeyBytes, type Secrets } from \"./secrets\";\n\n/** How a connection attempt ended, as the `connect` query parameter on the way back. */\nexport type ConnectResult =\n /** Connected; the app's `store` kept the account. */\n | \"connected\"\n /** The signed state was missing, forged or too old: start again. */\n | \"expired\"\n /** The browser that came back is not the one that started (no or another cookie). */\n | \"browser\"\n /** The person said no at the provider (or it sent no code). */\n | \"denied\"\n /** The person unticked a scope the app needs. */\n | \"scope\"\n /** The provider or the app's `store` failed. */\n | \"failed\";\n\n/** Every result `callback` can send back with. */\nexport const CONNECT_RESULTS: readonly ConnectResult[] = [\n \"connected\",\n \"expired\",\n \"browser\",\n \"denied\",\n \"scope\",\n \"failed\",\n];\n\n/** What went wrong outside the callback, for code to branch on. */\nexport type ConnectErrorCode =\n /** The connector is set up wrong (a missing secret, a bad origin). */\n | \"config\"\n /** A call was given something it can't use (no subject, an absolute returnTo). */\n | \"usage\"\n /** The grant is gone (`invalid_grant`): access was removed. Connect again. */\n | \"revoked\"\n /** The provider is down, limiting, or timed out. Worth retrying. */\n | \"unavailable\"\n /** The provider refused for another reason. */\n | \"failed\";\n\n/** A failure with a message safe to show and log: never a token, a code or a secret. */\nexport class ConnectError extends Error {\n override name = \"ConnectError\";\n readonly code: ConnectErrorCode;\n readonly retryable: boolean;\n readonly status: number | undefined;\n readonly retryAfterSeconds: number | undefined;\n /**\n * Set when the provider rotated the refresh token but it could not be\n * handed over (no `onRotate`, or `onRotate` threw): the new one, sealed to\n * the same row. Store it in place of the old one, or the grant is lost.\n */\n readonly sealedRefreshToken: string | undefined;\n constructor(\n code: ConnectErrorCode,\n message: string,\n options: {\n status?: number | undefined;\n retryAfterSeconds?: number | undefined;\n sealedRefreshToken?: string | undefined;\n } = {},\n ) {\n super(message);\n this.code = code;\n this.retryable = code === \"unavailable\";\n this.status = options.status;\n this.retryAfterSeconds = options.retryAfterSeconds;\n this.sealedRefreshToken = options.sealedRefreshToken;\n }\n}\n\nconst CONNECT_ERROR_CODES: readonly string[] = [\n \"config\",\n \"usage\",\n \"revoked\",\n \"unavailable\",\n \"failed\",\n];\n\n/**\n * Whether `error` is a `ConnectError`, by its name and code rather than\n * `instanceof`, so it also matches a copy from another bundle.\n */\nexport function isConnectError(error: unknown): error is ConnectError {\n return (\n error instanceof Error &&\n error.name === \"ConnectError\" &&\n CONNECT_ERROR_CODES.includes((error as { code?: unknown }).code as string)\n );\n}\n\n/** Small app data carried in the signed state. Signed, not encrypted: never put a secret in it. */\nexport type StateData = Readonly<\n Record<string, string | number | boolean | null>\n>;\n\n/** What `start` is told about this attempt. */\nexport interface StartOptions<D extends StateData = StateData> {\n /**\n * Who is connecting: the app's verified user id. Handed back to `store`.\n * The state is signed, not encrypted, so this is readable in the sign-in\n * URL (and by the provider): prefer an opaque id over an email address.\n */\n subject: string;\n /** What the connection is for, app-defined (`\"sheets\"`, `\"calendar\"`). At most 64 characters. */\n intent?: string | undefined;\n /**\n * Where to come back to: a path on the app (`\"/settings/integrations\"`),\n * never a full URL. The result is added as `?connect=…`.\n */\n returnTo: string;\n /** Scopes for this attempt, instead of the connector's. */\n scopes?: readonly string[] | undefined;\n /** Small app data to have back in `store` (a form id, a chosen file). Not secret. */\n data?: D | undefined;\n /** Extra authorization parameters (`login_hint`). The flow's own can't be overridden. */\n params?: Readonly<Record<string, string>> | undefined;\n}\n\n/** `start`'s answer: send the browser to `url` with `setCookie` set. */\nexport interface StartResult {\n /** The provider's sign-in URL. */\n url: string;\n /** The `Set-Cookie` header value holding the nonce and the PKCE verifier. */\n setCookie: string;\n /** A 303 to `url` with the cookie: for a start reached by a link or a form post. */\n redirect(): Response;\n /** `{ url }` as JSON with the cookie: for a start the page calls with fetch, then navigates. */\n json(): Response;\n}\n\n/** What `store` is handed: the new account, its refresh token already sealed. */\nexport interface ConnectedAccount<D extends StateData = StateData> {\n /** A new id for this grant (a UUID): the row to keep it under. */\n id: string;\n /** The associated data the refresh token is sealed to (`aad(id)`): pass it to `accessToken` and `revoke`. */\n aad: string;\n /** The refresh token, sealed with the keyring. Store it; it never needs opening by the app. */\n sealedRefreshToken: string;\n /** A fresh access token, to look the account up now (an email, a file). Don't store it. */\n accessToken: string;\n /** Seconds the access token is good for. */\n expiresIn: number;\n /** The granted scopes, as the provider joined them. */\n scope: string;\n /** The granted scopes. */\n scopes: string[];\n /** The OpenID Connect ID token, when the provider sent one (an `openid` scope). */\n idToken?: string | undefined;\n /** From `start`. */\n subject: string;\n intent: string;\n returnTo: string;\n data: D;\n}\n\n/** What `store` may add to the way back, as query parameters (a grant id to finish with). */\nexport type StoreResult = void | undefined | Readonly<Record<string, string>>;\n\n/** A safe log line: never a token, a code, a secret or a provider's error text. */\nexport interface ConnectLogEvent {\n step: \"callback\" | \"refresh\" | \"revoke\";\n result: ConnectResult | ConnectErrorCode | \"revoked\";\n /** A short, safe reason: \"token endpoint answered 400\", \"store threw\". */\n detail?: string | undefined;\n}\n\n/** Options shared by the requests the connector makes. */\nexport interface HttpOptions {\n /** Defaults to the global `fetch`. */\n fetch?: typeof fetch | undefined;\n /** Per request, in milliseconds. Default 10 000. */\n timeoutMs?: number | undefined;\n}\n\nexport interface ConnectorConfig<\n D extends StateData = StateData,\n> extends HttpOptions {\n provider: OAuthProvider;\n /** The app's OAuth client. Server side only. */\n clientId: string;\n clientSecret: string;\n /** 32 random bytes, base64 (`openssl rand -base64 32`): signs the state. Not the keyring's key. */\n stateSecret: string | Uint8Array;\n /** Seals refresh tokens (`createSecrets(keyring, { purpose: \"google-refresh-token\" })`). */\n secrets: Secrets;\n /** The callback's path on the app (`\"/api/connect/google/callback\"`). Register `<origin><path>` with the provider. */\n callbackPath: string;\n /** Scopes asked for when `start` names none. Default the provider's `defaultScopes`. */\n scopes?: readonly string[] | undefined;\n /**\n * The one origin the provider sends everyone back to (`https://app.example.com`),\n * when the app runs on many. Without it, each request's own origin.\n */\n redirectOrigin?: string | undefined;\n /**\n * The parent domain the PKCE cookie is shared on (`example.com`), so a flow\n * started on `team.example.com` can finish on the pinned `app.example.com`\n * and come back to `team.example.com`. Only https origins on it are ever\n * returned to. Leave it out to keep the cookie on one host.\n */\n sharedDomain?: string | undefined;\n /** The cookie's name. Default `genesis_connect_<provider id>`. */\n cookieName?: string | undefined;\n /** The cookie's path. Default `callbackPath`, so it is only ever sent to the callback. */\n cookiePath?: string | undefined;\n /** How long the trip to the provider and back may take. Default 600 seconds, at most 3600. */\n stateTtlSeconds?: number | undefined;\n /** The query parameter the result comes back in. Default `\"connect\"`. */\n resultParam?: string | undefined;\n /** Where to go back to when the state can't be read (so there is no `returnTo`). Default `\"/\"`. */\n fallbackPath?: string | undefined;\n /** The associated data a grant's refresh token is sealed to. Default `<provider id>-grant:<id>`. */\n aad?: ((id: string) => string) | undefined;\n /** Fail with `failed` when the provider sends no refresh token. Default true. */\n requireRefreshToken?: boolean | undefined;\n /**\n * Keeps the new account: the app's row, under `account.id`, for\n * `account.subject` (the user who started). Use that, never a session read\n * from the callback's request: the callback is a top-level navigation the\n * provider sends, and the session there may be another one. May return\n * query parameters for the way back. Throwing ends the attempt as `failed`.\n */\n store: (\n account: ConnectedAccount<D>,\n context: { request: Request },\n ) => StoreResult | Promise<StoreResult>;\n /** Safe log lines (see `ConnectLogEvent`). */\n log?: ((event: ConnectLogEvent) => void) | undefined;\n /**\n * Whether a domain is a public suffix (where a cookie would be dropped), for\n * the `sharedDomain` check: pass a Public Suffix List lookup to cover every\n * suffix. The built-in list only knows common hosting suffixes.\n */\n isPublicSuffix?: ((domain: string) => boolean) | undefined;\n now?: (() => number) | undefined;\n randomId?: (() => string) | undefined;\n}\n\n/** A fresh access token, and the refresh token again when the provider rotated it. */\nexport interface RefreshedToken {\n accessToken: string;\n expiresIn: number;\n scope: string;\n /** Set when the provider sent a new refresh token: store it in place of the old one. */\n sealedRefreshToken?: string | undefined;\n}\n\nexport interface TokenCallOptions {\n /** Aborts the request. Concurrent `accessToken` calls share one refresh: the first caller's signal aborts it. */\n signal?: AbortSignal | undefined;\n /**\n * Stores the new sealed refresh token when the provider rotates it (some\n * do on every refresh; Google does not). `accessToken` needs it for such a\n * provider: a rotation without it throws a `usage` `ConnectError` carrying\n * the new token on `sealedRefreshToken`, so it is never silently lost. If\n * it throws, a `failed` `ConnectError` carries the token the same way. The\n * access token is cached before it runs.\n */\n onRotate?: ((sealedRefreshToken: string) => void | Promise<void>) | undefined;\n}\n\nexport interface Connector<D extends StateData = StateData> {\n readonly provider: OAuthProvider;\n /** The redirect URI for a request: `<redirectOrigin or the request's origin><callbackPath>`. */\n redirectUri(request: Request): string;\n /** Begins a connection (see the module comment). The app checks who is signed in first. */\n start(request: Request, options: StartOptions<D>): Promise<StartResult>;\n /** Finishes one: always a 303 back to the app, with the result and a cleared cookie. */\n callback(request: Request): Promise<Response>;\n /**\n * A fresh access token for a sealed refresh token, cached until a minute\n * before it expires; concurrent calls for one grant share one refresh.\n * Needs `onRotate` for providers that rotate refresh tokens.\n */\n accessToken(\n sealedRefreshToken: string,\n aad: string,\n options?: TokenCallOptions,\n ): Promise<string>;\n /**\n * Trades a sealed refresh token for an access token, uncached. A rotated\n * refresh token comes back sealed on the result (and goes to `onRotate`).\n */\n refresh(\n sealedRefreshToken: string,\n aad: string,\n options?: TokenCallOptions,\n ): Promise<RefreshedToken>;\n /**\n * Revokes the grant at the provider and forgets its cached access token.\n * True when it is gone (revoked now, or already); false when the provider\n * could not be reached or has no revocation endpoint. Some providers\n * (Google) end every token of the person's grant to this client, so call\n * it only when the app no longer uses any.\n */\n revoke(\n sealedRefreshToken: string,\n aad: string,\n options?: { signal?: AbortSignal | undefined },\n ): Promise<boolean>;\n}\n\n// Validation ---------------------------------------------------------------------------------------\n\n/** Shared hosting suffixes on the Public Suffix List: a cookie scoped to one is dropped. */\nconst PUBLIC_SUFFIXES = new Set([\n \"vercel.app\",\n \"netlify.app\",\n \"pages.dev\",\n \"workers.dev\",\n \"github.io\",\n \"herokuapp.com\",\n \"onrender.com\",\n \"fly.dev\",\n \"web.app\",\n \"firebaseapp.com\",\n \"azurewebsites.net\",\n \"appspot.com\",\n \"ngrok-free.app\",\n \"ngrok.io\",\n \"cloudfront.net\",\n \"up.railway.app\",\n \"deno.dev\",\n \"co.uk\",\n \"com.au\",\n \"co.jp\",\n \"com.br\",\n \"co.in\",\n]);\n\nconst COOKIE_NAME = /^[!#$%&'*+\\-.^_`|~0-9A-Za-z]+$/;\nconst SCOPE_TOKEN = /^[\\x21\\x23-\\x5B\\x5D-\\x7E]+$/;\nconst HOSTNAME =\n /^(?=.{1,253}$)([a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?)(\\.[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?)+$/;\nconst RESERVED_PARAMS = new Set([\n \"client_id\",\n \"redirect_uri\",\n \"response_type\",\n \"scope\",\n \"state\",\n \"code_challenge\",\n \"code_challenge_method\",\n]);\n\nconst isLocal = (hostname: string) =>\n hostname === \"localhost\" || hostname === \"127.0.0.1\" || hostname === \"[::1]\";\n\nfunction checkOrigin(value: string, what: string): string {\n let url: URL;\n try {\n url = new URL(value);\n } catch {\n throw new ConnectError(\"config\", `${what} is not a URL`);\n }\n const origin = value.replace(/\\/+$/, \"\");\n if (url.origin !== origin)\n throw new ConnectError(\n \"config\",\n `${what} must be an origin only (scheme, host and port)`,\n );\n if (\n url.protocol !== \"https:\" &&\n !(url.protocol === \"http:\" && isLocal(url.hostname))\n ) {\n throw new ConnectError(\n \"config\",\n `${what} must be https (http only on localhost)`,\n );\n }\n return origin;\n}\n\nfunction checkPath(value: string, what: string): string {\n if (\n typeof value !== \"string\" ||\n !value.startsWith(\"/\") ||\n value.startsWith(\"//\") ||\n /[\\\\\\s\\x00-\\x1f\\x7f]/.test(value) ||\n value.length > 512\n ) {\n throw new ConnectError(\n \"usage\",\n `${what} must be a path on this app, starting with one /`,\n );\n }\n // Resolved against a placeholder origin it must not leave.\n const url = new URL(value, \"https://origin.invalid\");\n if (url.origin !== \"https://origin.invalid\")\n throw new ConnectError(\n \"usage\",\n `${what} must be a path on this app, starting with one /`,\n );\n return `${url.pathname}${url.search}${url.hash}`;\n}\n\nconst onDomain = (hostname: string, domain: string) =>\n hostname === domain || hostname.endsWith(`.${domain}`);\n\n/** Whether `origin` is exactly an https origin on `domain`, so it is safe to send the browser back to. */\nexport function isSafeReturnOrigin(origin: string, domain: string): boolean {\n try {\n const url = new URL(origin);\n return (\n url.protocol === \"https:\" &&\n url.port === \"\" &&\n url.origin === origin &&\n onDomain(url.hostname, domain)\n );\n } catch {\n return false;\n }\n}\n\nfunction scopeList(\n scopes: readonly string[] | undefined,\n separator: string,\n): string[] {\n const list = [...new Set(scopes ?? [])];\n for (const scope of list) {\n if (\n typeof scope !== \"string\" ||\n !SCOPE_TOKEN.test(scope) ||\n scope.includes(separator)\n )\n throw new ConnectError(\n \"usage\",\n \"A scope is malformed (no spaces, quotes or the provider's separator)\",\n );\n }\n return list;\n}\n\n// HTTP ---------------------------------------------------------------------------------------------\n\nconst DEFAULT_TIMEOUT_MS = 10_000;\n\nasync function timedFetch(\n deps: HttpOptions,\n url: string,\n init: RequestInit,\n signal?: AbortSignal,\n): Promise<Response> {\n const timeout = AbortSignal.timeout(deps.timeoutMs ?? DEFAULT_TIMEOUT_MS);\n const combined = signal ? anySignal([timeout, signal]) : timeout;\n try {\n // Never follow a redirect: the body holds a code, a token or the client secret.\n return await (deps.fetch ?? fetch)(url, {\n ...init,\n redirect: \"error\",\n signal: combined,\n });\n } catch (error) {\n const name = error instanceof Error ? error.name : \"\";\n if (name === \"TimeoutError\")\n throw new ConnectError(\n \"unavailable\",\n \"The provider did not answer in time\",\n );\n if (name === \"AbortError\")\n throw new ConnectError(\"unavailable\", \"The request was aborted\");\n throw new ConnectError(\"unavailable\", \"The provider could not be reached\");\n }\n}\n\n/** `AbortSignal.any`, combined by hand where the runtime lacks it, so the timeout always holds. */\nfunction anySignal(signals: AbortSignal[]): AbortSignal {\n const any = (\n AbortSignal as unknown as { any?: (signals: AbortSignal[]) => AbortSignal }\n ).any;\n if (any) return any(signals);\n const controller = new AbortController();\n for (const signal of signals) {\n if (signal.aborted) {\n controller.abort(signal.reason);\n break;\n }\n signal.addEventListener(\"abort\", () => controller.abort(signal.reason), {\n once: true,\n });\n }\n return controller.signal;\n}\n\nfunction discard(response: Response): void {\n try {\n void response.body?.cancel().catch(() => {});\n } catch {\n // A mocked or already-read body.\n }\n}\n\nasync function jsonBody(response: Response): Promise<Record<string, unknown>> {\n const value = (await response.json().catch(() => null)) as unknown;\n return value && typeof value === \"object\" && !Array.isArray(value)\n ? (value as Record<string, unknown>)\n : {};\n}\n\n/** A provider's error code, when it is one (`invalid_grant`), never its free text. */\nfunction errorCode(body: Record<string, unknown>): string | undefined {\n const code = body[\"error\"];\n return typeof code === \"string\" && /^[a-z_]{1,64}$/.test(code)\n ? code\n : undefined;\n}\n\nfunction retryAfter(response: Response): number | undefined {\n const header = response.headers.get(\"retry-after\")?.trim();\n return header && /^\\d+$/.test(header)\n ? Math.min(86_400, Math.max(1, Number(header)))\n : undefined;\n}\n\nconst isRetryableStatus = (status: number) =>\n status === 408 || status === 425 || status === 429 || status >= 500;\n\n// Access-token cache -------------------------------------------------------------------------------\n\n// One per process, shared by connectors: keyed by a digest of the provider, the\n// client and the refresh token, so a hit is only ever for the same grant.\nconst tokenCache = new Map<string, { token: string; expiresAt: number }>();\n// Refreshes in flight, by the same key: concurrent cold calls share one.\nconst inFlight = new Map<string, Promise<Refreshed>>();\nconst TOKEN_CACHE_LIMIT = 1000;\n\n/** Forgets every cached access token (tests, or after a key or client change). */\nexport function clearAccessTokenCache(): void {\n tokenCache.clear();\n}\n\n// The connector ------------------------------------------------------------------------------------\n\n/** A refresh as `refreshWith` does it: the result, plus the rotated token in plain text to cache under. */\ninterface Refreshed extends RefreshedToken {\n rotatedPlain?: string | undefined;\n}\n\ninterface SignedState {\n subject: string;\n intent: string;\n returnTo: string;\n origin: string;\n nonce: string;\n expiresAt: number;\n scopes: string[];\n data: StateData;\n}\n\n/** Sets up a connector for one provider. Checks its config now, so a mistake shows at boot. */\nexport function createConnector<D extends StateData = StateData>(\n config: ConnectorConfig<D>,\n): Connector<D> {\n const { provider } = config;\n if (!provider?.id || !provider.authorizeUrl || !provider.tokenUrl)\n throw new ConnectError(\n \"config\",\n \"The provider needs an id, an authorize URL and a token URL\",\n );\n if (!config.clientId || !config.clientSecret)\n throw new ConnectError(\n \"config\",\n `The ${provider.id} OAuth client id or secret is not set`,\n );\n if (!config.secrets)\n throw new ConnectError(\n \"config\",\n \"The connector needs secrets (createSecrets) to seal refresh tokens\",\n );\n if (typeof config.store !== \"function\")\n throw new ConnectError(\"config\", \"The connector needs a store function\");\n let stateKeyBytes: Uint8Array<ArrayBuffer>;\n try {\n stateKeyBytes = secretKeyBytes(config.stateSecret);\n } catch (error) {\n throw new ConnectError(\n \"config\",\n `The state secret: ${(error as Error).message}`,\n );\n }\n const callbackPath = checkPath(config.callbackPath, \"callbackPath\");\n const redirectOrigin = config.redirectOrigin\n ? checkOrigin(config.redirectOrigin, \"redirectOrigin\")\n : undefined;\n const sharedDomain =\n config.sharedDomain?.trim().toLowerCase().replace(/^\\./, \"\") || undefined;\n if (sharedDomain) {\n if (\n !HOSTNAME.test(sharedDomain) ||\n PUBLIC_SUFFIXES.has(sharedDomain) ||\n config.isPublicSuffix?.(sharedDomain)\n ) {\n throw new ConnectError(\n \"config\",\n \"sharedDomain must be a registrable domain such as example.com, not a public suffix\",\n );\n }\n if (\n redirectOrigin &&\n !onDomain(new URL(redirectOrigin).hostname, sharedDomain)\n ) {\n throw new ConnectError(\n \"config\",\n \"redirectOrigin must be on sharedDomain\",\n );\n }\n }\n const cookieName =\n config.cookieName ??\n `genesis_connect_${provider.id.replace(/[^A-Za-z0-9_-]/g, \"_\")}`;\n if (!COOKIE_NAME.test(cookieName))\n throw new ConnectError(\"config\", \"cookieName is not a valid cookie name\");\n const cookiePath = checkPath(\n config.cookiePath ?? callbackPath,\n \"cookiePath\",\n ).replace(/[?#].*$/, \"\");\n if (/[;,]/.test(cookiePath)) {\n throw new ConnectError(\"config\", \"cookiePath can't hold ; or ,\");\n }\n // RFC 6265 path-match: the same path, or a parent directory of it.\n const callbackPathOnly = callbackPath.replace(/[?#].*$/, \"\");\n if (\n cookiePath !== callbackPathOnly &&\n !callbackPathOnly.startsWith(\n cookiePath.endsWith(\"/\") ? cookiePath : `${cookiePath}/`,\n )\n )\n throw new ConnectError(\"config\", \"cookiePath must cover callbackPath\");\n const ttl = Math.floor(config.stateTtlSeconds ?? 600);\n if (!(ttl >= 60 && ttl <= 3600))\n throw new ConnectError(\n \"config\",\n \"stateTtlSeconds must be between 60 and 3600\",\n );\n const resultParam = config.resultParam ?? \"connect\";\n const fallbackPath = checkPath(config.fallbackPath ?? \"/\", \"fallbackPath\");\n const aadFor = config.aad ?? ((id: string) => `${provider.id}-grant:${id}`);\n const separator = provider.scopeSeparator ?? \" \";\n const defaultScopes = scopeList(\n config.scopes ?? provider.defaultScopes,\n separator,\n );\n const aliases = provider.scopeAliases ?? {};\n const normalScope = (scope: string) => aliases[scope] ?? scope;\n const now = () => config.now?.() ?? Date.now();\n const log = (event: ConnectLogEvent) => {\n try {\n config.log?.(event);\n } catch {\n // A logger never breaks the flow.\n }\n };\n\n let stateKey: Promise<WebCryptoKey> | undefined;\n const hmacKey = () =>\n (stateKey ??= crypto.subtle\n .importKey(\"raw\", stateKeyBytes, \"HKDF\", false, [\"deriveKey\"])\n .then((base) =>\n crypto.subtle.deriveKey(\n {\n name: \"HKDF\",\n hash: \"SHA-256\",\n salt: new Uint8Array(0),\n info: utf8.encode(`genesis-connect/state/${provider.id}`),\n },\n base,\n { name: \"HMAC\", hash: \"SHA-256\", length: 256 },\n false,\n [\"sign\", \"verify\"],\n ),\n ));\n\n async function signState(state: SignedState): Promise<string> {\n const body = base64url(\n JSON.stringify({\n v: 1,\n s: state.subject,\n i: state.intent,\n r: state.returnTo,\n o: state.origin,\n n: state.nonce,\n e: state.expiresAt,\n c: state.scopes,\n d: state.data,\n }),\n );\n const mac = await crypto.subtle.sign(\n \"HMAC\",\n await hmacKey(),\n utf8.encode(body),\n );\n return `${body}.${base64url(new Uint8Array(mac))}`;\n }\n\n /** The state, or null when it is missing, forged, malformed or expired. */\n async function verifyState(\n signed: string | null,\n ): Promise<SignedState | null> {\n if (!signed || signed.length > 4096) return null;\n const [body, mac, extra] = signed.split(\".\");\n if (!body || !mac || extra !== undefined) return null;\n try {\n if (\n !(await crypto.subtle.verify(\n \"HMAC\",\n await hmacKey(),\n fromBase64(mac),\n utf8.encode(body),\n ))\n )\n return null;\n const raw = JSON.parse(\n new TextDecoder().decode(fromBase64(body)),\n ) as Record<string, unknown>;\n const state: SignedState = {\n subject: String(raw[\"s\"] ?? \"\"),\n intent: String(raw[\"i\"] ?? \"\"),\n returnTo: checkPath(String(raw[\"r\"] ?? \"\"), \"returnTo\"),\n origin: String(raw[\"o\"] ?? \"\"),\n nonce: String(raw[\"n\"] ?? \"\"),\n expiresAt: Number(raw[\"e\"]),\n scopes: Array.isArray(raw[\"c\"])\n ? raw[\"c\"].filter((s): s is string => typeof s === \"string\")\n : [],\n data:\n raw[\"d\"] && typeof raw[\"d\"] === \"object\" && !Array.isArray(raw[\"d\"])\n ? (raw[\"d\"] as StateData)\n : {},\n };\n if (\n raw[\"v\"] !== 1 ||\n !state.subject ||\n !state.nonce ||\n !Number.isFinite(state.expiresAt)\n )\n return null;\n if (state.expiresAt * 1000 <= now()) return null;\n return state;\n } catch {\n return null;\n }\n }\n\n const originOf = (request: Request) => new URL(request.url).origin;\n const redirectUri = (request: Request) =>\n `${redirectOrigin ?? originOf(request)}${callbackPath}`;\n\n /** The cookie's Domain: the shared domain when this request is on it, else none (this host only). */\n const cookieDomain = (request: Request) =>\n sharedDomain && onDomain(new URL(request.url).hostname, sharedDomain)\n ? sharedDomain\n : undefined;\n\n /** Secure everywhere but a local development host. */\n const isLocalRequest = (request: Request) =>\n isLocal(new URL(request.url).hostname);\n\n function cookie(\n request: Request,\n value: string,\n maxAge: number,\n domain: string | null = cookieDomain(request) ?? null,\n ): string {\n const secure = isLocalRequest(request) ? \"\" : \"; Secure\";\n return `${cookieName}=${value}; Path=${cookiePath}; HttpOnly; SameSite=Lax; Max-Age=${maxAge}${secure}${domain ? `; Domain=${domain}` : \"\"}`;\n }\n\n /** Every value of our cookie the browser sent (a host-only and a domain one may both be there). */\n function cookieValues(request: Request): string[] {\n const values: string[] = [];\n for (const part of (request.headers.get(\"cookie\") ?? \"\").split(\";\")) {\n const at = part.indexOf(\"=\");\n if (at > 0 && part.slice(0, at).trim() === cookieName)\n values.push(part.slice(at + 1).trim());\n }\n return values;\n }\n\n /** Where to send the browser back to: where it started, when that is this origin or a safe one on the shared domain. */\n function returnOrigin(request: Request, started: string | undefined): string {\n const here = originOf(request);\n if (!started || started === here) return here;\n if (\n sharedDomain &&\n cookieDomain(request) &&\n isSafeReturnOrigin(started, sharedDomain)\n )\n return started;\n return here;\n }\n\n function back(\n request: Request,\n state: SignedState | null,\n result: ConnectResult,\n extra: Readonly<Record<string, string>> = {},\n detail?: string,\n ): Response {\n const url = new URL(\n state?.returnTo ?? fallbackPath,\n returnOrigin(request, state?.origin),\n );\n for (const [key, value] of Object.entries(extra)) {\n if (key !== resultParam && typeof value === \"string\")\n url.searchParams.set(key, value);\n }\n url.searchParams.set(resultParam, result);\n log({ step: \"callback\", result, ...(detail ? { detail } : {}) });\n const headers = new Headers({\n location: url.toString(),\n \"cache-control\": \"no-store\",\n \"referrer-policy\": \"no-referrer\",\n });\n // Clear both variants: a host-only cookie and a shared-domain one may both be there.\n headers.append(\"set-cookie\", cookie(request, \"\", 0, null));\n const domain = cookieDomain(request);\n if (domain) headers.append(\"set-cookie\", cookie(request, \"\", 0, domain));\n return new Response(null, { status: 303, headers });\n }\n\n function clientAuth(\n body: URLSearchParams,\n headers: Record<string, string>,\n ): void {\n if (provider.tokenAuth === \"client_secret_basic\") {\n const encode = (s: string) => encodeURIComponent(s).replace(/%20/g, \"+\");\n headers[\"authorization\"] =\n `Basic ${btoa(`${encode(config.clientId)}:${encode(config.clientSecret)}`)}`;\n } else {\n body.set(\"client_id\", config.clientId);\n body.set(\"client_secret\", config.clientSecret);\n }\n }\n\n async function tokenRequest(\n params: Record<string, string>,\n signal?: AbortSignal,\n ): Promise<Response> {\n const body = new URLSearchParams(params);\n const headers: Record<string, string> = {\n \"content-type\": \"application/x-www-form-urlencoded\",\n accept: \"application/json\",\n };\n clientAuth(body, headers);\n return timedFetch(\n config,\n provider.tokenUrl,\n { method: \"POST\", headers, body: body.toString() },\n signal,\n );\n }\n\n const granted = (body: Record<string, unknown>, asked: readonly string[]) => {\n const scope =\n typeof body[\"scope\"] === \"string\" ? body[\"scope\"] : asked.join(separator);\n return {\n scope,\n scopes: scope.split(separator).filter(Boolean).map(normalScope),\n };\n };\n\n const expiresIn = (body: Record<string, unknown>) => {\n const value = Number(body[\"expires_in\"]);\n return Number.isFinite(value) && value > 0 ? value : 3600;\n };\n\n async function cacheKey(refreshToken: string) {\n return sha256Hex(`${provider.id}\\n${config.clientId}\\n${refreshToken}`);\n }\n\n async function refreshWith(\n refreshToken: string,\n aad: string,\n signal: AbortSignal | undefined,\n ): Promise<Refreshed> {\n const response = await tokenRequest(\n { grant_type: \"refresh_token\", refresh_token: refreshToken },\n signal,\n );\n if (!response.ok) {\n const body = await jsonBody(response);\n const code = errorCode(body);\n if (response.status === 400 && code === \"invalid_grant\") {\n log({ step: \"refresh\", result: \"revoked\" });\n throw new ConnectError(\n \"revoked\",\n `The ${provider.id} connection was removed. Connect it again.`,\n { status: 400 },\n );\n }\n const detail = `token endpoint answered ${response.status}${code ? ` (${code})` : \"\"}`;\n if (isRetryableStatus(response.status)) {\n log({ step: \"refresh\", result: \"unavailable\", detail });\n throw new ConnectError(\"unavailable\", `The ${provider.id} ${detail}`, {\n status: response.status,\n retryAfterSeconds: retryAfter(response),\n });\n }\n log({ step: \"refresh\", result: \"failed\", detail });\n throw new ConnectError(\"failed\", `The ${provider.id} ${detail}`, {\n status: response.status,\n });\n }\n const body = await jsonBody(response);\n const accessToken = body[\"access_token\"];\n if (typeof accessToken !== \"string\" || !accessToken)\n throw new ConnectError(\n \"unavailable\",\n `The ${provider.id} token endpoint sent no access token`,\n );\n const rotated =\n typeof body[\"refresh_token\"] === \"string\" &&\n body[\"refresh_token\"] &&\n body[\"refresh_token\"] !== refreshToken\n ? body[\"refresh_token\"]\n : undefined;\n const sealedRefreshToken = rotated\n ? await config.secrets.seal(rotated, aad)\n : undefined;\n return {\n accessToken,\n expiresIn: expiresIn(body),\n scope: granted(body, []).scope,\n ...(sealedRefreshToken\n ? { sealedRefreshToken, rotatedPlain: rotated }\n : {}),\n };\n }\n\n /** Hands a rotated refresh token to `onRotate`; never lets it be lost. */\n async function handOver(\n fresh: Refreshed,\n options: TokenCallOptions,\n required: boolean,\n ): Promise<void> {\n const sealedRefreshToken = fresh.sealedRefreshToken;\n if (!sealedRefreshToken) return;\n if (!options.onRotate) {\n if (!required) return;\n log({\n step: \"refresh\",\n result: \"usage\",\n detail: \"rotated without onRotate\",\n });\n throw new ConnectError(\n \"usage\",\n `The ${provider.id} refresh token was rotated and no onRotate was given to store it: store this error's sealedRefreshToken`,\n { sealedRefreshToken },\n );\n }\n try {\n await options.onRotate(sealedRefreshToken);\n } catch {\n log({ step: \"refresh\", result: \"failed\", detail: \"onRotate threw\" });\n throw new ConnectError(\n \"failed\",\n `Storing the rotated ${provider.id} refresh token failed: store this error's sealedRefreshToken`,\n { sealedRefreshToken },\n );\n }\n }\n\n const publicResult = (fresh: Refreshed): RefreshedToken => ({\n accessToken: fresh.accessToken,\n expiresIn: fresh.expiresIn,\n scope: fresh.scope,\n ...(fresh.sealedRefreshToken\n ? { sealedRefreshToken: fresh.sealedRefreshToken }\n : {}),\n });\n\n async function cacheToken(\n refreshToken: string,\n fresh: Refreshed,\n at: number,\n ) {\n const entry = {\n token: fresh.accessToken,\n expiresAt: at + fresh.expiresIn * 1000,\n };\n for (const plain of [refreshToken, fresh.rotatedPlain]) {\n if (!plain) continue;\n if (tokenCache.size >= TOKEN_CACHE_LIMIT)\n tokenCache.delete(tokenCache.keys().next().value!);\n tokenCache.set(await cacheKey(plain), entry);\n }\n }\n\n return {\n provider,\n redirectUri,\n\n async start(request, options) {\n const subject =\n typeof options.subject === \"string\" ? options.subject.trim() : \"\";\n if (!subject || subject.length > 256)\n throw new ConnectError(\n \"usage\",\n \"start needs the signed-in subject (at most 256 characters)\",\n );\n const intent = options.intent ?? \"\";\n if (typeof intent !== \"string\" || intent.length > 64)\n throw new ConnectError(\"usage\", \"intent must be at most 64 characters\");\n const returnTo = checkPath(options.returnTo, \"returnTo\");\n // The cookie is always Secure off localhost, so http elsewhere could never finish.\n if (\n new URL(request.url).protocol !== \"https:\" &&\n !isLocalRequest(request)\n ) {\n throw new ConnectError(\n \"config\",\n \"start needs https (http only on localhost); behind a TLS-ending proxy, pass a Request with the public https URL\",\n );\n }\n // The PKCE cookie must reach the pinned callback: this origin is it, or shares its domain.\n if (\n redirectOrigin &&\n originOf(request) !== redirectOrigin &&\n !cookieDomain(request)\n ) {\n throw new ConnectError(\n \"config\",\n \"This origin can't finish on the pinned redirectOrigin: it is not on sharedDomain\",\n );\n }\n const scopes = options.scopes\n ? scopeList(options.scopes, separator)\n : defaultScopes;\n if (scopes.length === 0)\n throw new ConnectError(\n \"usage\",\n \"No scopes to ask for: give the connector or start some\",\n );\n const nonce = randomToken(16);\n const verifier = randomToken(32);\n const state = await signState({\n subject,\n intent,\n returnTo,\n origin: originOf(request),\n nonce,\n expiresAt: Math.floor(now() / 1000) + ttl,\n scopes,\n data: options.data ?? {},\n });\n if (state.length > 2048)\n throw new ConnectError(\n \"usage\",\n \"The state is too large: keep data small\",\n );\n const challenge = base64url(\n new Uint8Array(\n await crypto.subtle.digest(\"SHA-256\", utf8.encode(verifier)),\n ),\n );\n const url = new URL(provider.authorizeUrl);\n for (const [key, value] of Object.entries(provider.authorizeParams ?? {}))\n url.searchParams.set(key, value);\n for (const [key, value] of Object.entries(options.params ?? {})) {\n if (RESERVED_PARAMS.has(key))\n throw new ConnectError(\n \"usage\",\n `start can't set the ${key} parameter`,\n );\n url.searchParams.set(key, value);\n }\n url.searchParams.set(\"client_id\", config.clientId);\n url.searchParams.set(\"redirect_uri\", redirectUri(request));\n url.searchParams.set(\"response_type\", \"code\");\n url.searchParams.set(\"scope\", scopes.join(separator));\n url.searchParams.set(\"state\", state);\n url.searchParams.set(\"code_challenge\", challenge);\n url.searchParams.set(\"code_challenge_method\", \"S256\");\n const href = url.toString();\n const setCookie = cookie(request, `${nonce}.${verifier}`, ttl);\n const headers = { \"set-cookie\": setCookie, \"cache-control\": \"no-store\" };\n return {\n url: href,\n setCookie,\n redirect: () =>\n new Response(null, {\n status: 303,\n headers: { ...headers, location: href },\n }),\n json: () => Response.json({ url: href }, { headers }),\n };\n },\n\n async callback(request) {\n const query = new URL(request.url).searchParams;\n const state = await verifyState(query.get(\"state\"));\n if (!state) return back(request, null, \"expired\");\n // Another browser, or the cookie expired: never finish someone else's sign-in.\n const verifier = cookieValues(request)\n .map((value) => value.split(\".\"))\n .find(\n ([nonce, v, extra]) =>\n nonce === state.nonce && v && extra === undefined,\n )?.[1];\n if (!verifier) return back(request, state, \"browser\");\n if (query.get(\"error\"))\n return back(\n request,\n state,\n query.get(\"error\") === \"access_denied\" ? \"denied\" : \"failed\",\n );\n const code = query.get(\"code\");\n if (!code) return back(request, state, \"denied\");\n\n let body: Record<string, unknown>;\n try {\n const response = await tokenRequest({\n grant_type: \"authorization_code\",\n code,\n code_verifier: verifier,\n redirect_uri: redirectUri(request),\n });\n body = await jsonBody(response);\n if (!response.ok) {\n const code = errorCode(body);\n return back(\n request,\n state,\n \"failed\",\n {},\n `token endpoint answered ${response.status}${code ? ` (${code})` : \"\"}`,\n );\n }\n } catch (error) {\n return back(\n request,\n state,\n \"failed\",\n {},\n error instanceof ConnectError\n ? error.message\n : \"token request failed\",\n );\n }\n const accessToken = body[\"access_token\"];\n const refreshToken = body[\"refresh_token\"];\n if (typeof accessToken !== \"string\" || !accessToken) {\n return back(request, state, \"failed\", {}, \"no access token\");\n }\n const { scope, scopes } = granted(body, state.scopes);\n // The person can untick scopes on the consent screen; the app needs every one it asked for.\n if (!state.scopes.every((s) => scopes.includes(normalScope(s))))\n return back(request, state, \"scope\");\n if (\n (config.requireRefreshToken ?? true) &&\n (typeof refreshToken !== \"string\" || !refreshToken)\n ) {\n return back(\n request,\n state,\n \"failed\",\n {},\n \"no refresh token (is offline access asked for?)\",\n );\n }\n\n let extra: StoreResult;\n try {\n const id = config.randomId?.() ?? crypto.randomUUID();\n const aad = aadFor(id);\n const sealedRefreshToken =\n typeof refreshToken === \"string\" && refreshToken\n ? await config.secrets.seal(refreshToken, aad)\n : \"\";\n extra = await config.store(\n {\n id,\n aad,\n sealedRefreshToken,\n accessToken,\n expiresIn: expiresIn(body),\n scope,\n scopes,\n ...(typeof body[\"id_token\"] === \"string\"\n ? { idToken: body[\"id_token\"] }\n : {}),\n subject: state.subject,\n intent: state.intent,\n returnTo: state.returnTo,\n data: state.data as D,\n },\n { request },\n );\n } catch {\n return back(request, state, \"failed\", {}, \"store threw\");\n }\n return back(request, state, \"connected\", extra ?? {});\n },\n\n async refresh(sealedRefreshToken, aad, options = {}) {\n const refreshToken = await config.secrets.open(sealedRefreshToken, aad);\n const fresh = await refreshWith(refreshToken, aad, options.signal);\n await handOver(fresh, options, false);\n return publicResult(fresh);\n },\n\n async accessToken(sealedRefreshToken, aad, options = {}) {\n const refreshToken = await config.secrets.open(sealedRefreshToken, aad);\n const key = await cacheKey(refreshToken);\n const hit = tokenCache.get(key);\n if (hit && hit.expiresAt - 60_000 > now()) return hit.token;\n let flight = inFlight.get(key);\n if (!flight) {\n const at = now();\n flight = refreshWith(refreshToken, aad, options.signal)\n .then(async (fresh) => {\n // Cached before onRotate runs, so a failing store never costs a refresh.\n await cacheToken(refreshToken, fresh, at);\n return fresh;\n })\n .finally(() => inFlight.delete(key));\n inFlight.set(key, flight);\n }\n const fresh = await flight;\n await handOver(fresh, options, true);\n return fresh.accessToken;\n },\n\n async revoke(sealedRefreshToken, aad, options = {}) {\n const refreshToken = await config.secrets.open(sealedRefreshToken, aad);\n tokenCache.delete(await cacheKey(refreshToken));\n if (!provider.revokeUrl) return false;\n const body = new URLSearchParams({\n token: refreshToken,\n token_type_hint: \"refresh_token\",\n });\n const headers: Record<string, string> = {\n \"content-type\": \"application/x-www-form-urlencoded\",\n accept: \"application/json\",\n };\n if (provider.revokeWithClientAuth) clientAuth(body, headers);\n try {\n const response = await timedFetch(\n config,\n provider.revokeUrl,\n { method: \"POST\", headers, body: body.toString() },\n options.signal,\n );\n // RFC 7009: 200 for a revoked token and for one that was already invalid; Google says invalid_token.\n let gone = response.ok;\n if (!gone && response.status === 400) {\n gone = errorCode(await jsonBody(response)) === \"invalid_token\";\n } else {\n discard(response);\n }\n log({\n step: \"revoke\",\n result: gone ? \"revoked\" : \"failed\",\n ...(gone\n ? {}\n : { detail: `revoke endpoint answered ${response.status}` }),\n });\n return gone;\n } catch {\n log({ step: \"revoke\", result: \"unavailable\" });\n return false;\n }\n },\n };\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/providers.ts","../src/oauth.ts"],"names":["fromBase64","secretKeyBytes","utf8","base64url","sha256Hex","body","randomToken","extra","code","fresh"],"mappings":";;;;;AA6CO,IAAM,cAAA,GAAgC;AAAA,EAC3C,EAAA,EAAI,QAAA;AAAA,EACJ,YAAA,EAAc,8CAAA;AAAA,EACd,QAAA,EAAU,qCAAA;AAAA,EACV,SAAA,EAAW,sCAAA;AAAA,EACX,MAAA,EAAQ,CAAC,6BAAA,EAA+B,qBAAqB,CAAA;AAAA,EAC7D,eAAe,EAAC;AAAA,EAChB,eAAA,EAAiB;AAAA,IACf,WAAA,EAAa,SAAA;AAAA;AAAA,IAEb,MAAA,EAAQ,SAAA;AAAA;AAAA,IAER,sBAAA,EAAwB;AAAA,GAC1B;AAAA,EACA,SAAA,EAAW,oBAAA;AAAA,EACX,YAAA,EAAc;AAAA,IACZ,KAAA,EAAO,gDAAA;AAAA,IACP,OAAA,EAAS,kDAAA;AAAA,IACT,MAAA,EAAQ;AAAA;AAEZ;AAGO,IAAM,aAAA,GAAgB;AAAA;AAAA,EAE3B,MAAA,EAAQ,QAAA;AAAA;AAAA,EAER,KAAA,EAAO,OAAA;AAAA;AAAA,EAEP,OAAA,EAAS,SAAA;AAAA;AAAA,EAET,SAAA,EAAW,4CAAA;AAAA;AAAA,EAEX,YAAA,EAAc,8CAAA;AAAA;AAAA,EAEd,cAAA,EAAgB,iDAAA;AAAA;AAAA,EAEhB,sBAAA,EACE,0DAAA;AAAA;AAAA,EAEF,oBAAA,EACE,gEAAA;AAAA;AAAA,EAEF,gBAAA,EAAkB,mDAAA;AAAA;AAAA,EAElB,SAAA,EAAW;AACb;;;AClCO,IAAM,eAAA,GAA4C;AAAA,EACvD,WAAA;AAAA,EACA,SAAA;AAAA,EACA,SAAA;AAAA,EACA,QAAA;AAAA,EACA,OAAA;AAAA,EACA;AACF;AAgBO,IAAM,YAAA,GAAN,cAA2B,KAAA,CAAM;AAAA,EAC7B,IAAA,GAAO,cAAA;AAAA,EACP,IAAA;AAAA,EACA,SAAA;AAAA,EACA,MAAA;AAAA,EACA,iBAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,kBAAA;AAAA,EACT,WAAA,CACE,IAAA,EACA,OAAA,EACA,OAAA,GAII,EAAC,EACL;AACA,IAAA,KAAA,CAAM,OAAO,CAAA;AACb,IAAA,IAAA,CAAK,IAAA,GAAO,IAAA;AACZ,IAAA,IAAA,CAAK,YAAY,IAAA,KAAS,aAAA;AAC1B,IAAA,IAAA,CAAK,SAAS,OAAA,CAAQ,MAAA;AACtB,IAAA,IAAA,CAAK,oBAAoB,OAAA,CAAQ,iBAAA;AACjC,IAAA,IAAA,CAAK,qBAAqB,OAAA,CAAQ,kBAAA;AAAA,EACpC;AACF;AAEA,IAAM,mBAAA,GAAyC;AAAA,EAC7C,QAAA;AAAA,EACA,OAAA;AAAA,EACA,SAAA;AAAA,EACA,aAAA;AAAA,EACA;AACF,CAAA;AAMO,SAAS,eAAe,KAAA,EAAuC;AACpE,EAAA,OACE,KAAA,YAAiB,SACjB,KAAA,CAAM,IAAA,KAAS,kBACf,mBAAA,CAAoB,QAAA,CAAU,MAA6B,IAAc,CAAA;AAE7E;AA8GA,SAAS,yBAAA,CACP,SACA,MAAA,EACsC;AACtC,EAAA,IAAI,MAAA,CAAO,QAAQ,MAAA,KAAW,CAAA;AAC5B,IAAA,OAAO,EAAE,QAAQ,8BAAA,EAA+B;AAClD,EAAA,MAAM,IAAA,GAAO,OAAA,CAAQ,KAAA,CAAM,GAAG,EAAE,CAAC,CAAA;AACjC,EAAA,IAAI,CAAC,IAAA,EAAM,OAAO,EAAE,QAAQ,WAAA,EAAY;AACxC,EAAA,IAAI,MAAA;AACJ,EAAA,IAAI;AACF,IAAA,MAAM,QAAQ,IAAA,CAAK,KAAA;AAAA,MACjB,IAAI,WAAA,EAAY,CAAE,MAAA,CAAOA,4BAAA,CAAW,IAAI,CAAC;AAAA,KAC3C;AACA,IAAA,IAAI,CAAC,KAAA,IAAS,OAAO,UAAU,QAAA,IAAY,KAAA,CAAM,QAAQ,KAAK,CAAA;AAC5D,MAAA,OAAO,EAAE,QAAQ,mBAAA,EAAoB;AACvC,IAAA,MAAA,GAAS,KAAA;AAAA,EACX,CAAA,CAAA,MAAQ;AACN,IAAA,OAAO,EAAE,QAAQ,mBAAA,EAAoB;AAAA,EACvC;AACA,EAAA,IACE,OAAO,MAAA,CAAO,KAAK,CAAA,KAAM,QAAA,IACzB,CAAC,MAAA,CAAO,OAAA,CAAQ,QAAA,CAAS,MAAA,CAAO,KAAK,CAAC,CAAA;AAEtC,IAAA,OAAO,EAAE,QAAQ,iBAAA,EAAkB;AACrC,EAAA,MAAM,GAAA,GAAM,OAAO,KAAK,CAAA;AACxB,EAAA,MAAM,SAAA,GACJ,OAAO,GAAA,KAAQ,QAAA,GAAW,CAAC,GAAG,CAAA,GAAI,KAAA,CAAM,OAAA,CAAQ,GAAG,CAAA,GAAI,GAAA,GAAM,EAAC;AAChE,EAAA,IAAI,CAAC,SAAA,CAAU,QAAA,CAAS,MAAA,CAAO,QAAQ,CAAA;AACrC,IAAA,OAAO,EAAE,QAAQ,mBAAA,EAAoB;AAEvC,EAAA,IACE,SAAA,CAAU,IAAA;AAAA,IACR,CAAC,CAAA,KACC,CAAA,KAAM,MAAA,CAAO,QAAA,IACb,EAAE,OAAO,CAAA,KAAM,QAAA,IAAY,MAAA,CAAO,gBAAA,CAAiB,QAAA,CAAS,CAAC,CAAA;AAAA,GACjE;AAEA,IAAA,OAAO,EAAE,QAAQ,oBAAA,EAAqB;AAExC,EAAA,MAAM,GAAA,GAAM,OAAO,KAAK,CAAA;AACxB,EAAA,IACG,GAAA,KAAQ,UAAa,GAAA,KAAQ,MAAA,CAAO,YACpC,SAAA,CAAU,MAAA,GAAS,KAAK,GAAA,KAAQ,MAAA;AAEjC,IAAA,OAAO,EAAE,QAAQ,2BAAA,EAA4B;AAC/C,EAAA,MAAM,GAAA,GAAM,OAAO,KAAK,CAAA;AACxB,EAAA,IAAI,OAAO,GAAA,KAAQ,QAAA,IAAY,GAAA,GAAM,OAAQ,MAAA,CAAO,GAAA;AAClD,IAAA,OAAO,EAAE,QAAQ,SAAA,EAAU;AAC7B,EAAA,MAAM,IAAA,GAAO,CAAC,GAAA,KACZ,OAAO,MAAA,CAAO,GAAG,CAAA,KAAM,QAAA,IAAY,MAAA,CAAO,GAAG,CAAA,GACxC,MAAA,CAAO,GAAG,CAAA,GACX,MAAA;AACN,EAAA,MAAM,OAAA,GAAU,KAAK,KAAK,CAAA;AAC1B,EAAA,IAAI,CAAC,OAAA,EAAS,OAAO,EAAE,QAAQ,YAAA,EAAa;AAC5C,EAAA,MAAM,WACJ,MAAA,CAAO,gBAAgB,MAAM,IAAA,IAAQ,MAAA,CAAO,gBAAgB,CAAA,KAAM,MAAA;AACpE,EAAA,OAAO;AAAA,IACL,OAAA;AAAA;AAAA,IAEA,GAAI,QAAA,IAAY,IAAA,CAAK,OAAO,CAAA,GACxB,EAAE,KAAA,EAAO,IAAA,CAAK,OAAO,CAAA,EAAG,aAAA,EAAe,IAAA,KACvC,EAAC;AAAA,IACL,GAAI,IAAA,CAAK,MAAM,CAAA,GAAI,EAAE,MAAM,IAAA,CAAK,MAAM,CAAA,EAAE,GAAI,EAAC;AAAA,IAC7C,GAAI,IAAA,CAAK,SAAS,CAAA,GAAI,EAAE,SAAS,IAAA,CAAK,SAAS,CAAA,EAAE,GAAI;AAAC,GACxD;AACF;AA2KA,IAAM,eAAA,uBAAsB,GAAA,CAAI;AAAA,EAC9B,YAAA;AAAA,EACA,aAAA;AAAA,EACA,WAAA;AAAA,EACA,aAAA;AAAA,EACA,WAAA;AAAA,EACA,eAAA;AAAA,EACA,cAAA;AAAA,EACA,SAAA;AAAA,EACA,SAAA;AAAA,EACA,iBAAA;AAAA,EACA,mBAAA;AAAA,EACA,aAAA;AAAA,EACA,gBAAA;AAAA,EACA,UAAA;AAAA,EACA,gBAAA;AAAA,EACA,gBAAA;AAAA,EACA,UAAA;AAAA,EACA,OAAA;AAAA,EACA,QAAA;AAAA,EACA,OAAA;AAAA,EACA,QAAA;AAAA,EACA;AACF,CAAC,CAAA;AAED,IAAM,WAAA,GAAc,gCAAA;AACpB,IAAM,WAAA,GAAc,6BAAA;AACpB,IAAM,QAAA,GACJ,gGAAA;AACF,IAAM,eAAA,uBAAsB,GAAA,CAAI;AAAA,EAC9B,WAAA;AAAA,EACA,cAAA;AAAA,EACA,eAAA;AAAA,EACA,OAAA;AAAA,EACA,OAAA;AAAA,EACA,gBAAA;AAAA,EACA;AACF,CAAC,CAAA;AAED,IAAM,UAAU,CAAC,QAAA,KACf,aAAa,WAAA,IAAe,QAAA,KAAa,eAAe,QAAA,KAAa,OAAA;AAEvE,SAAS,WAAA,CAAY,OAAe,IAAA,EAAsB;AACxD,EAAA,IAAI,GAAA;AACJ,EAAA,IAAI;AACF,IAAA,GAAA,GAAM,IAAI,IAAI,KAAK,CAAA;AAAA,EACrB,CAAA,CAAA,MAAQ;AACN,IAAA,MAAM,IAAI,YAAA,CAAa,QAAA,EAAU,CAAA,EAAG,IAAI,CAAA,aAAA,CAAe,CAAA;AAAA,EACzD;AACA,EAAA,MAAM,MAAA,GAAS,KAAA,CAAM,OAAA,CAAQ,MAAA,EAAQ,EAAE,CAAA;AACvC,EAAA,IAAI,IAAI,MAAA,KAAW,MAAA;AACjB,IAAA,MAAM,IAAI,YAAA;AAAA,MACR,QAAA;AAAA,MACA,GAAG,IAAI,CAAA,+CAAA;AAAA,KACT;AACF,EAAA,IACE,GAAA,CAAI,QAAA,KAAa,QAAA,IACjB,EAAE,GAAA,CAAI,aAAa,OAAA,IAAW,OAAA,CAAQ,GAAA,CAAI,QAAQ,CAAA,CAAA,EAClD;AACA,IAAA,MAAM,IAAI,YAAA;AAAA,MACR,QAAA;AAAA,MACA,GAAG,IAAI,CAAA,uCAAA;AAAA,KACT;AAAA,EACF;AACA,EAAA,OAAO,MAAA;AACT;AAGA,SAAS,aAAA,CAAc,OAAe,IAAA,EAAsB;AAC1D,EAAA,IAAI,GAAA;AACJ,EAAA,IAAI;AACF,IAAA,GAAA,GAAM,IAAI,IAAI,KAAK,CAAA;AAAA,EACrB,CAAA,CAAA,MAAQ;AACN,IAAA,MAAM,IAAI,YAAA,CAAa,QAAA,EAAU,CAAA,eAAA,EAAkB,IAAI,CAAA,aAAA,CAAe,CAAA;AAAA,EACxE;AACA,EAAA,IACE,GAAA,CAAI,QAAA,KAAa,QAAA,IACjB,EAAE,GAAA,CAAI,aAAa,OAAA,IAAW,OAAA,CAAQ,GAAA,CAAI,QAAQ,CAAA,CAAA,EAClD;AACA,IAAA,MAAM,IAAI,YAAA;AAAA,MACR,QAAA;AAAA,MACA,kBAAkB,IAAI,CAAA,uCAAA;AAAA,KACxB;AAAA,EACF;AACA,EAAA,OAAO,KAAA;AACT;AAEA,SAAS,SAAA,CAAU,OAAe,IAAA,EAAsB;AACtD,EAAA,IACE,OAAO,KAAA,KAAU,QAAA,IACjB,CAAC,KAAA,CAAM,UAAA,CAAW,GAAG,CAAA,IACrB,KAAA,CAAM,UAAA,CAAW,IAAI,KACrB,qBAAA,CAAsB,IAAA,CAAK,KAAK,CAAA,IAChC,KAAA,CAAM,SAAS,GAAA,EACf;AACA,IAAA,MAAM,IAAI,YAAA;AAAA,MACR,OAAA;AAAA,MACA,GAAG,IAAI,CAAA,gDAAA;AAAA,KACT;AAAA,EACF;AAEA,EAAA,MAAM,GAAA,GAAM,IAAI,GAAA,CAAI,KAAA,EAAO,wBAAwB,CAAA;AACnD,EAAA,IAAI,IAAI,MAAA,KAAW,wBAAA;AACjB,IAAA,MAAM,IAAI,YAAA;AAAA,MACR,OAAA;AAAA,MACA,GAAG,IAAI,CAAA,gDAAA;AAAA,KACT;AACF,EAAA,OAAO,CAAA,EAAG,IAAI,QAAQ,CAAA,EAAG,IAAI,MAAM,CAAA,EAAG,IAAI,IAAI,CAAA,CAAA;AAChD;AAEA,IAAM,QAAA,GAAW,CAAC,QAAA,EAAkB,MAAA,KAClC,QAAA,KAAa,UAAU,QAAA,CAAS,QAAA,CAAS,CAAA,CAAA,EAAI,MAAM,CAAA,CAAE,CAAA;AAGhD,SAAS,kBAAA,CAAmB,QAAgB,MAAA,EAAyB;AAC1E,EAAA,IAAI;AACF,IAAA,MAAM,GAAA,GAAM,IAAI,GAAA,CAAI,MAAM,CAAA;AAC1B,IAAA,OACE,GAAA,CAAI,QAAA,KAAa,QAAA,IACjB,GAAA,CAAI,IAAA,KAAS,EAAA,IACb,GAAA,CAAI,MAAA,KAAW,MAAA,IACf,QAAA,CAAS,GAAA,CAAI,QAAA,EAAU,MAAM,CAAA;AAAA,EAEjC,CAAA,CAAA,MAAQ;AACN,IAAA,OAAO,KAAA;AAAA,EACT;AACF;AAEA,SAAS,SAAA,CACP,QACA,SAAA,EACU;AACV,EAAA,MAAM,IAAA,GAAO,CAAC,GAAG,IAAI,IAAI,MAAA,IAAU,EAAE,CAAC,CAAA;AACtC,EAAA,KAAA,MAAW,SAAS,IAAA,EAAM;AACxB,IAAA,IACE,OAAO,KAAA,KAAU,QAAA,IACjB,CAAC,WAAA,CAAY,KAAK,KAAK,CAAA,IACvB,KAAA,CAAM,QAAA,CAAS,SAAS,CAAA;AAExB,MAAA,MAAM,IAAI,YAAA;AAAA,QACR,OAAA;AAAA,QACA;AAAA,OACF;AAAA,EACJ;AACA,EAAA,OAAO,IAAA;AACT;AAIA,IAAM,kBAAA,GAAqB,GAAA;AAE3B,IAAM,kBAAA,GAAqB,GAAA;AAE3B,eAAe,UAAA,CACb,IAAA,EACA,GAAA,EACA,IAAA,EACA,MAAA,EACmB;AACnB,EAAA,MAAM,OAAA,GAAU,WAAA,CAAY,OAAA,CAAQ,IAAA,CAAK,aAAa,kBAAkB,CAAA;AACxE,EAAA,MAAM,WAAW,MAAA,GAAS,SAAA,CAAU,CAAC,OAAA,EAAS,MAAM,CAAC,CAAA,GAAI,OAAA;AACzD,EAAA,IAAI;AAEF,IAAA,OAAO,MAAA,CAAO,IAAA,CAAK,KAAA,IAAS,KAAA,EAAO,GAAA,EAAK;AAAA,MACtC,GAAG,IAAA;AAAA,MACH,QAAA,EAAU,OAAA;AAAA,MACV,MAAA,EAAQ;AAAA,KACT,CAAA;AAAA,EACH,SAAS,KAAA,EAAO;AACd,IAAA,MAAM,IAAA,GAAO,KAAA,YAAiB,KAAA,GAAQ,KAAA,CAAM,IAAA,GAAO,EAAA;AACnD,IAAA,IAAI,IAAA,KAAS,cAAA;AACX,MAAA,MAAM,IAAI,YAAA;AAAA,QACR,aAAA;AAAA,QACA;AAAA,OACF;AACF,IAAA,IAAI,IAAA,KAAS,YAAA;AACX,MAAA,MAAM,IAAI,YAAA,CAAa,aAAA,EAAe,yBAAyB,CAAA;AACjE,IAAA,MAAM,IAAI,YAAA,CAAa,aAAA,EAAe,mCAAmC,CAAA;AAAA,EAC3E;AACF;AAGA,SAAS,UAAU,OAAA,EAAqC;AACtD,EAAA,MAAM,MACJ,WAAA,CACA,GAAA;AACF,EAAA,IAAI,GAAA,EAAK,OAAO,GAAA,CAAI,OAAO,CAAA;AAC3B,EAAA,MAAM,UAAA,GAAa,IAAI,eAAA,EAAgB;AACvC,EAAA,KAAA,MAAW,UAAU,OAAA,EAAS;AAC5B,IAAA,IAAI,OAAO,OAAA,EAAS;AAClB,MAAA,UAAA,CAAW,KAAA,CAAM,OAAO,MAAM,CAAA;AAC9B,MAAA;AAAA,IACF;AACA,IAAA,MAAA,CAAO,iBAAiB,OAAA,EAAS,MAAM,WAAW,KAAA,CAAM,MAAA,CAAO,MAAM,CAAA,EAAG;AAAA,MACtE,IAAA,EAAM;AAAA,KACP,CAAA;AAAA,EACH;AACA,EAAA,OAAO,UAAA,CAAW,MAAA;AACpB;AAEA,SAAS,QAAQ,QAAA,EAA0B;AACzC,EAAA,IAAI;AACF,IAAA,KAAK,QAAA,CAAS,IAAA,EAAM,MAAA,EAAO,CAAE,MAAM,MAAM;AAAA,IAAC,CAAC,CAAA;AAAA,EAC7C,CAAA,CAAA,MAAQ;AAAA,EAER;AACF;AAEA,eAAe,SAAS,QAAA,EAAsD;AAC5E,EAAA,MAAM,QAAS,MAAM,QAAA,CAAS,MAAK,CAAE,KAAA,CAAM,MAAM,IAAI,CAAA;AACrD,EAAA,OAAO,KAAA,IAAS,OAAO,KAAA,KAAU,QAAA,IAAY,CAAC,MAAM,OAAA,CAAQ,KAAK,CAAA,GAC5D,KAAA,GACD,EAAC;AACP;AAGA,SAAS,UAAU,IAAA,EAAmD;AACpE,EAAA,MAAM,IAAA,GAAO,KAAK,OAAO,CAAA;AACzB,EAAA,OAAO,OAAO,IAAA,KAAS,QAAA,IAAY,iBAAiB,IAAA,CAAK,IAAI,IACzD,IAAA,GACA,MAAA;AACN;AAEA,SAAS,WAAW,QAAA,EAAwC;AAC1D,EAAA,MAAM,SAAS,QAAA,CAAS,OAAA,CAAQ,GAAA,CAAI,aAAa,GAAG,IAAA,EAAK;AACzD,EAAA,OAAO,MAAA,IAAU,OAAA,CAAQ,IAAA,CAAK,MAAM,IAChC,IAAA,CAAK,GAAA,CAAI,KAAA,EAAQ,IAAA,CAAK,IAAI,CAAA,EAAG,MAAA,CAAO,MAAM,CAAC,CAAC,CAAA,GAC5C,MAAA;AACN;AAEA,IAAM,iBAAA,GAAoB,CAAC,MAAA,KACzB,MAAA,KAAW,OAAO,MAAA,KAAW,GAAA,IAAO,MAAA,KAAW,GAAA,IAAO,MAAA,IAAU,GAAA;AAMlE,IAAM,UAAA,uBAAiB,GAAA,EAAkD;AAEzE,IAAM,QAAA,uBAAe,GAAA,EAAgC;AACrD,IAAM,iBAAA,GAAoB,GAAA;AAGnB,SAAS,qBAAA,GAA8B;AAC5C,EAAA,UAAA,CAAW,KAAA,EAAM;AACnB;AAqBO,SAAS,gBACd,MAAA,EACc;AACd,EAAA,MAAM,EAAE,UAAS,GAAI,MAAA;AACrB,EAAA,IAAI,CAAC,QAAA,EAAU,EAAA,IAAM,CAAC,QAAA,CAAS,YAAA,IAAgB,CAAC,QAAA,CAAS,QAAA;AACvD,IAAA,MAAM,IAAI,YAAA;AAAA,MACR,QAAA;AAAA,MACA;AAAA,KACF;AAGF,EAAA,aAAA,CAAc,QAAA,CAAS,UAAU,WAAW,CAAA;AAC5C,EAAA,IAAI,QAAA,CAAS,SAAA,EAAW,aAAA,CAAc,QAAA,CAAS,WAAW,YAAY,CAAA;AACtE,EAAA,IAAI,CAAC,MAAA,CAAO,QAAA,IAAY,CAAC,MAAA,CAAO,YAAA;AAC9B,IAAA,MAAM,IAAI,YAAA;AAAA,MACR,QAAA;AAAA,MACA,CAAA,IAAA,EAAO,SAAS,EAAE,CAAA,qCAAA;AAAA,KACpB;AACF,EAAA,IAAI,CAAC,MAAA,CAAO,OAAA;AACV,IAAA,MAAM,IAAI,YAAA;AAAA,MACR,QAAA;AAAA,MACA;AAAA,KACF;AACF,EAAA,IAAI,OAAO,OAAO,KAAA,KAAU,UAAA;AAC1B,IAAA,MAAM,IAAI,YAAA,CAAa,QAAA,EAAU,sCAAsC,CAAA;AACzE,EAAA,IAAI,aAAA;AACJ,EAAA,IAAI;AACF,IAAA,aAAA,GAAgBC,gCAAA,CAAe,OAAO,WAAW,CAAA;AAAA,EACnD,SAAS,KAAA,EAAO;AACd,IAAA,MAAM,IAAI,YAAA;AAAA,MACR,QAAA;AAAA,MACA,CAAA,kBAAA,EAAsB,MAAgB,OAAO,CAAA;AAAA,KAC/C;AAAA,EACF;AACA,EAAA,MAAM,YAAA,GAAe,SAAA,CAAU,MAAA,CAAO,YAAA,EAAc,cAAc,CAAA;AAClE,EAAA,MAAM,iBAAiB,MAAA,CAAO,cAAA,GAC1B,YAAY,MAAA,CAAO,cAAA,EAAgB,gBAAgB,CAAA,GACnD,MAAA;AACJ,EAAA,MAAM,YAAA,GACJ,MAAA,CAAO,YAAA,EAAc,IAAA,EAAK,CAAE,aAAY,CAAE,OAAA,CAAQ,KAAA,EAAO,EAAE,CAAA,IAAK,MAAA;AAClE,EAAA,IAAI,YAAA,EAAc;AAChB,IAAA,IACE,CAAC,QAAA,CAAS,IAAA,CAAK,YAAY,CAAA,IAC3B,eAAA,CAAgB,GAAA,CAAI,YAAY,CAAA,IAChC,MAAA,CAAO,cAAA,GAAiB,YAAY,CAAA,EACpC;AACA,MAAA,MAAM,IAAI,YAAA;AAAA,QACR,QAAA;AAAA,QACA;AAAA,OACF;AAAA,IACF;AACA,IAAA,IACE,cAAA,IACA,CAAC,QAAA,CAAS,IAAI,IAAI,cAAc,CAAA,CAAE,QAAA,EAAU,YAAY,CAAA,EACxD;AACA,MAAA,MAAM,IAAI,YAAA;AAAA,QACR,QAAA;AAAA,QACA;AAAA,OACF;AAAA,IACF;AAAA,EACF;AACA,EAAA,MAAM,UAAA,GACJ,OAAO,UAAA,IACP,CAAA,gBAAA,EAAmB,SAAS,EAAA,CAAG,OAAA,CAAQ,iBAAA,EAAmB,GAAG,CAAC,CAAA,CAAA;AAChE,EAAA,IAAI,CAAC,WAAA,CAAY,IAAA,CAAK,UAAU,CAAA;AAC9B,IAAA,MAAM,IAAI,YAAA,CAAa,QAAA,EAAU,uCAAuC,CAAA;AAC1E,EAAA,MAAM,UAAA,GAAa,SAAA;AAAA,IACjB,OAAO,UAAA,IAAc,YAAA;AAAA,IACrB;AAAA,GACF,CAAE,OAAA,CAAQ,SAAA,EAAW,EAAE,CAAA;AACvB,EAAA,IAAI,MAAA,CAAO,IAAA,CAAK,UAAU,CAAA,EAAG;AAC3B,IAAA,MAAM,IAAI,YAAA,CAAa,QAAA,EAAU,8BAA8B,CAAA;AAAA,EACjE;AAEA,EAAA,MAAM,gBAAA,GAAmB,YAAA,CAAa,OAAA,CAAQ,SAAA,EAAW,EAAE,CAAA;AAC3D,EAAA,IACE,UAAA,KAAe,gBAAA,IACf,CAAC,gBAAA,CAAiB,UAAA;AAAA,IAChB,WAAW,QAAA,CAAS,GAAG,CAAA,GAAI,UAAA,GAAa,GAAG,UAAU,CAAA,CAAA;AAAA,GACvD;AAEA,IAAA,MAAM,IAAI,YAAA,CAAa,QAAA,EAAU,oCAAoC,CAAA;AACvE,EAAA,MAAM,GAAA,GAAM,IAAA,CAAK,KAAA,CAAM,MAAA,CAAO,mBAAmB,GAAG,CAAA;AACpD,EAAA,IAAI,EAAE,GAAA,IAAO,EAAA,IAAM,GAAA,IAAO,IAAA,CAAA;AACxB,IAAA,MAAM,IAAI,YAAA;AAAA,MACR,QAAA;AAAA,MACA;AAAA,KACF;AACF,EAAA,MAAM,WAAA,GAAc,OAAO,WAAA,IAAe,SAAA;AAC1C,EAAA,MAAM,YAAA,GAAe,SAAA,CAAU,MAAA,CAAO,YAAA,IAAgB,KAAK,cAAc,CAAA;AACzE,EAAA,MAAM,MAAA,GAAS,OAAO,GAAA,KAAQ,CAAC,OAAe,CAAA,EAAG,QAAA,CAAS,EAAE,CAAA,OAAA,EAAU,EAAE,CAAA,CAAA,CAAA;AACxE,EAAA,MAAM,SAAA,GAAY,SAAS,cAAA,IAAkB,GAAA;AAC7C,EAAA,MAAM,aAAA,GAAgB,SAAA;AAAA,IACpB,MAAA,CAAO,UAAU,QAAA,CAAS,aAAA;AAAA,IAC1B;AAAA,GACF;AACA,EAAA,MAAM,OAAA,GAAU,QAAA,CAAS,YAAA,IAAgB,EAAC;AAC1C,EAAA,MAAM,WAAA,GAAc,CAAC,KAAA,KAAkB,OAAA,CAAQ,KAAK,CAAA,IAAK,KAAA;AACzD,EAAA,MAAM,UAAA,GAAa,CAAC,MAAA,KAClB,MAAA,CAAO,IAAA,CAAK,CAAC,KAAA,KAAU,WAAA,CAAY,KAAK,CAAA,KAAM,QAAQ,CAAA;AAExD,EAAA,MAAM,OAAA,GAAA,CACJ,OAAO,QAAA,CAAS,MAAA,KAAW,WACvB,CAAC,QAAA,CAAS,MAAM,CAAA,GACf,QAAA,CAAS,MAAA,IAAU,EAAC,EACzB,MAAA,CAAO,CAAC,MAAA,KAAW,OAAO,WAAW,QAAA,IAAY,MAAA,CAAO,IAAA,EAAK,KAAM,EAAE,CAAA;AACvE,EAAA,IAAI,OAAO,eAAA,EAAiB;AAC1B,IAAA,IAAI,QAAQ,MAAA,KAAW,CAAA;AACrB,MAAA,MAAM,IAAI,YAAA;AAAA,QACR,QAAA;AAAA,QACA,CAAA,0BAAA,EAA6B,SAAS,EAAE,CAAA,kBAAA;AAAA,OAC1C;AACF,IAAA,IAAI,CAAC,WAAW,aAAa,CAAA;AAC3B,MAAA,MAAM,IAAI,YAAA;AAAA,QACR,QAAA;AAAA,QACA;AAAA,OACF;AAAA,EACJ;AACA,EAAA,MAAM,mBAAmB,CAAC,GAAI,MAAA,CAAO,gBAAA,IAAoB,EAAG,CAAA;AAC5D,EAAA,MAAM,MAAM,MAAM,MAAA,CAAO,GAAA,IAAM,IAAK,KAAK,GAAA,EAAI;AAC7C,EAAA,MAAM,GAAA,GAAM,CAAC,KAAA,KAA2B;AACtC,IAAA,IAAI;AACF,MAAA,MAAA,CAAO,MAAM,KAAK,CAAA;AAAA,IACpB,CAAA,CAAA,MAAQ;AAAA,IAER;AAAA,EACF,CAAA;AAEA,EAAA,IAAI,QAAA;AACJ,EAAA,MAAM,OAAA,GAAU,MACb,QAAA,KAAa,MAAA,CAAO,MAAA,CAClB,SAAA,CAAU,KAAA,EAAO,aAAA,EAAe,MAAA,EAAQ,KAAA,EAAO,CAAC,WAAW,CAAC,CAAA,CAC5D,IAAA;AAAA,IAAK,CAAC,IAAA,KACL,MAAA,CAAO,MAAA,CAAO,SAAA;AAAA,MACZ;AAAA,QACE,IAAA,EAAM,MAAA;AAAA,QACN,IAAA,EAAM,SAAA;AAAA,QACN,IAAA,EAAM,IAAI,UAAA,CAAW,CAAC,CAAA;AAAA,QACtB,MAAMC,sBAAA,CAAK,MAAA,CAAO,CAAA,sBAAA,EAAyB,QAAA,CAAS,EAAE,CAAA,CAAE;AAAA,OAC1D;AAAA,MACA,IAAA;AAAA,MACA,EAAE,IAAA,EAAM,MAAA,EAAQ,IAAA,EAAM,SAAA,EAAW,QAAQ,GAAA,EAAI;AAAA,MAC7C,KAAA;AAAA,MACA,CAAC,QAAQ,QAAQ;AAAA;AACnB,GACF;AAEJ,EAAA,eAAe,UAAU,KAAA,EAAqC;AAC5D,IAAA,MAAM,IAAA,GAAOC,2BAAA;AAAA,MACX,KAAK,SAAA,CAAU;AAAA,QACb,CAAA,EAAG,CAAA;AAAA,QACH,GAAG,KAAA,CAAM,OAAA;AAAA,QACT,GAAG,KAAA,CAAM,MAAA;AAAA,QACT,GAAG,KAAA,CAAM,QAAA;AAAA,QACT,GAAG,KAAA,CAAM,MAAA;AAAA,QACT,GAAG,KAAA,CAAM,KAAA;AAAA,QACT,GAAG,KAAA,CAAM,SAAA;AAAA,QACT,GAAG,KAAA,CAAM,MAAA;AAAA,QACT,GAAG,KAAA,CAAM;AAAA,OACV;AAAA,KACH;AACA,IAAA,MAAM,GAAA,GAAM,MAAM,MAAA,CAAO,MAAA,CAAO,IAAA;AAAA,MAC9B,MAAA;AAAA,MACA,MAAM,OAAA,EAAQ;AAAA,MACdD,sBAAA,CAAK,OAAO,IAAI;AAAA,KAClB;AACA,IAAA,OAAO,CAAA,EAAG,IAAI,CAAA,CAAA,EAAIC,2BAAA,CAAU,IAAI,UAAA,CAAW,GAAG,CAAC,CAAC,CAAA,CAAA;AAAA,EAClD;AAGA,EAAA,eAAe,YACb,MAAA,EAC6B;AAC7B,IAAA,IAAI,CAAC,MAAA,IAAU,MAAA,CAAO,MAAA,GAAS,MAAM,OAAO,IAAA;AAC5C,IAAA,MAAM,CAAC,IAAA,EAAM,GAAA,EAAK,KAAK,CAAA,GAAI,MAAA,CAAO,MAAM,GAAG,CAAA;AAC3C,IAAA,IAAI,CAAC,IAAA,IAAQ,CAAC,GAAA,IAAO,KAAA,KAAU,QAAW,OAAO,IAAA;AACjD,IAAA,IAAI;AACF,MAAA,IACE,CAAE,MAAM,MAAA,CAAO,MAAA,CAAO,MAAA;AAAA,QACpB,MAAA;AAAA,QACA,MAAM,OAAA,EAAQ;AAAA,QACdH,6BAAW,GAAG,CAAA;AAAA,QACdE,sBAAA,CAAK,OAAO,IAAI;AAAA,OAClB;AAEA,QAAA,OAAO,IAAA;AACT,MAAA,MAAM,MAAM,IAAA,CAAK,KAAA;AAAA,QACf,IAAI,WAAA,EAAY,CAAE,MAAA,CAAOF,4BAAA,CAAW,IAAI,CAAC;AAAA,OAC3C;AACA,MAAA,MAAM,KAAA,GAAqB;AAAA,QACzB,OAAA,EAAS,MAAA,CAAO,GAAA,CAAI,GAAG,KAAK,EAAE,CAAA;AAAA,QAC9B,MAAA,EAAQ,MAAA,CAAO,GAAA,CAAI,GAAG,KAAK,EAAE,CAAA;AAAA,QAC7B,QAAA,EAAU,UAAU,MAAA,CAAO,GAAA,CAAI,GAAG,CAAA,IAAK,EAAE,GAAG,UAAU,CAAA;AAAA,QACtD,MAAA,EAAQ,MAAA,CAAO,GAAA,CAAI,GAAG,KAAK,EAAE,CAAA;AAAA,QAC7B,KAAA,EAAO,MAAA,CAAO,GAAA,CAAI,GAAG,KAAK,EAAE,CAAA;AAAA,QAC5B,SAAA,EAAW,MAAA,CAAO,GAAA,CAAI,GAAG,CAAC,CAAA;AAAA,QAC1B,QAAQ,KAAA,CAAM,OAAA,CAAQ,GAAA,CAAI,GAAG,CAAC,CAAA,GAC1B,GAAA,CAAI,GAAG,CAAA,CAAE,OAAO,CAAC,CAAA,KAAmB,OAAO,CAAA,KAAM,QAAQ,IACzD,EAAC;AAAA,QACL,MACE,GAAA,CAAI,GAAG,KAAK,OAAO,GAAA,CAAI,GAAG,CAAA,KAAM,QAAA,IAAY,CAAC,KAAA,CAAM,OAAA,CAAQ,IAAI,GAAG,CAAC,IAC9D,GAAA,CAAI,GAAG,IACR;AAAC,OACT;AACA,MAAA,IACE,GAAA,CAAI,GAAG,CAAA,KAAM,CAAA,IACb,CAAC,KAAA,CAAM,OAAA,IACP,CAAC,KAAA,CAAM,KAAA,IACP,CAAC,MAAA,CAAO,QAAA,CAAS,MAAM,SAAS,CAAA;AAEhC,QAAA,OAAO,IAAA;AACT,MAAA,IAAI,KAAA,CAAM,SAAA,GAAY,GAAA,IAAQ,GAAA,IAAO,OAAO,IAAA;AAC5C,MAAA,OAAO,KAAA;AAAA,IACT,CAAA,CAAA,MAAQ;AACN,MAAA,OAAO,IAAA;AAAA,IACT;AAAA,EACF;AAEA,EAAA,MAAM,WAAW,CAAC,OAAA,KAAqB,IAAI,GAAA,CAAI,OAAA,CAAQ,GAAG,CAAA,CAAE,MAAA;AAC5D,EAAA,MAAM,WAAA,GAAc,CAAC,OAAA,KACnB,CAAA,EAAG,kBAAkB,QAAA,CAAS,OAAO,CAAC,CAAA,EAAG,YAAY,CAAA,CAAA;AAGvD,EAAA,MAAM,YAAA,GAAe,CAAC,OAAA,KACpB,YAAA,IAAgB,QAAA,CAAS,IAAI,GAAA,CAAI,OAAA,CAAQ,GAAG,CAAA,CAAE,QAAA,EAAU,YAAY,IAChE,YAAA,GACA,MAAA;AAGN,EAAA,MAAM,cAAA,GAAiB,CAAC,OAAA,KACtB,OAAA,CAAQ,IAAI,GAAA,CAAI,OAAA,CAAQ,GAAG,CAAA,CAAE,QAAQ,CAAA;AAEvC,EAAA,SAAS,MAAA,CACP,SACA,KAAA,EACA,MAAA,EACA,SAAwB,YAAA,CAAa,OAAO,KAAK,IAAA,EACzC;AACR,IAAA,MAAM,MAAA,GAAS,cAAA,CAAe,OAAO,CAAA,GAAI,EAAA,GAAK,UAAA;AAC9C,IAAA,OAAO,CAAA,EAAG,UAAU,CAAA,CAAA,EAAI,KAAK,UAAU,UAAU,CAAA,kCAAA,EAAqC,MAAM,CAAA,EAAG,MAAM,CAAA,EAAG,MAAA,GAAS,CAAA,SAAA,EAAY,MAAM,KAAK,EAAE,CAAA,CAAA;AAAA,EAC5I;AAGA,EAAA,SAAS,aAAa,OAAA,EAA4B;AAChD,IAAA,MAAM,SAAmB,EAAC;AAC1B,IAAA,KAAA,MAAW,IAAA,IAAA,CAAS,QAAQ,OAAA,CAAQ,GAAA,CAAI,QAAQ,CAAA,IAAK,EAAA,EAAI,KAAA,CAAM,GAAG,CAAA,EAAG;AACnE,MAAA,MAAM,EAAA,GAAK,IAAA,CAAK,OAAA,CAAQ,GAAG,CAAA;AAC3B,MAAA,IAAI,EAAA,GAAK,KAAK,IAAA,CAAK,KAAA,CAAM,GAAG,EAAE,CAAA,CAAE,MAAK,KAAM,UAAA;AACzC,QAAA,MAAA,CAAO,KAAK,IAAA,CAAK,KAAA,CAAM,KAAK,CAAC,CAAA,CAAE,MAAM,CAAA;AAAA,IACzC;AACA,IAAA,OAAO,MAAA;AAAA,EACT;AAGA,EAAA,SAAS,YAAA,CAAa,SAAkB,OAAA,EAAqC;AAC3E,IAAA,MAAM,IAAA,GAAO,SAAS,OAAO,CAAA;AAC7B,IAAA,IAAI,CAAC,OAAA,IAAW,OAAA,KAAY,IAAA,EAAM,OAAO,IAAA;AACzC,IAAA,IACE,gBACA,YAAA,CAAa,OAAO,CAAA,IACpB,kBAAA,CAAmB,SAAS,YAAY,CAAA;AAExC,MAAA,OAAO,OAAA;AACT,IAAA,OAAO,IAAA;AAAA,EACT;AAEA,EAAA,SAAS,KACP,OAAA,EACA,KAAA,EACA,QACA,KAAA,GAA0C,IAC1C,MAAA,EACU;AACV,IAAA,MAAM,MAAM,IAAI,GAAA;AAAA,MACd,OAAO,QAAA,IAAY,YAAA;AAAA,MACnB,YAAA,CAAa,OAAA,EAAS,KAAA,EAAO,MAAM;AAAA,KACrC;AACA,IAAA,KAAA,MAAW,CAAC,GAAA,EAAK,KAAK,KAAK,MAAA,CAAO,OAAA,CAAQ,KAAK,CAAA,EAAG;AAChD,MAAA,IAAI,GAAA,KAAQ,WAAA,IAAe,OAAO,KAAA,KAAU,QAAA;AAC1C,QAAA,GAAA,CAAI,YAAA,CAAa,GAAA,CAAI,GAAA,EAAK,KAAK,CAAA;AAAA,IACnC;AACA,IAAA,GAAA,CAAI,YAAA,CAAa,GAAA,CAAI,WAAA,EAAa,MAAM,CAAA;AACxC,IAAA,GAAA,CAAI,EAAE,IAAA,EAAM,UAAA,EAAY,MAAA,EAAQ,GAAI,MAAA,GAAS,EAAE,MAAA,EAAO,GAAI,EAAC,EAAI,CAAA;AAC/D,IAAA,MAAM,OAAA,GAAU,IAAI,OAAA,CAAQ;AAAA,MAC1B,QAAA,EAAU,IAAI,QAAA,EAAS;AAAA,MACvB,eAAA,EAAiB,UAAA;AAAA,MACjB,iBAAA,EAAmB;AAAA,KACpB,CAAA;AAED,IAAA,OAAA,CAAQ,OAAO,YAAA,EAAc,MAAA,CAAO,SAAS,EAAA,EAAI,CAAA,EAAG,IAAI,CAAC,CAAA;AACzD,IAAA,MAAM,MAAA,GAAS,aAAa,OAAO,CAAA;AACnC,IAAA,IAAI,MAAA,UAAgB,MAAA,CAAO,YAAA,EAAc,OAAO,OAAA,EAAS,EAAA,EAAI,CAAA,EAAG,MAAM,CAAC,CAAA;AACvE,IAAA,OAAO,IAAI,QAAA,CAAS,IAAA,EAAM,EAAE,MAAA,EAAQ,GAAA,EAAK,SAAS,CAAA;AAAA,EACpD;AAEA,EAAA,SAAS,UAAA,CACP,MACA,OAAA,EACM;AACN,IAAA,IAAI,QAAA,CAAS,cAAc,qBAAA,EAAuB;AAChD,MAAA,MAAM,MAAA,GAAS,CAAC,CAAA,KAAc,kBAAA,CAAmB,CAAC,CAAA,CAAE,OAAA,CAAQ,QAAQ,GAAG,CAAA;AACvE,MAAA,OAAA,CAAQ,eAAe,CAAA,GACrB,CAAA,MAAA,EAAS,IAAA,CAAK,GAAG,MAAA,CAAO,MAAA,CAAO,QAAQ,CAAC,IAAI,MAAA,CAAO,MAAA,CAAO,YAAY,CAAC,EAAE,CAAC,CAAA,CAAA;AAAA,IAC9E,CAAA,MAAO;AACL,MAAA,IAAA,CAAK,GAAA,CAAI,WAAA,EAAa,MAAA,CAAO,QAAQ,CAAA;AACrC,MAAA,IAAA,CAAK,GAAA,CAAI,eAAA,EAAiB,MAAA,CAAO,YAAY,CAAA;AAAA,IAC/C;AAAA,EACF;AAEA,EAAA,eAAe,YAAA,CACb,QACA,MAAA,EACmB;AACnB,IAAA,MAAM,IAAA,GAAO,IAAI,eAAA,CAAgB,MAAM,CAAA;AACvC,IAAA,MAAM,OAAA,GAAkC;AAAA,MACtC,cAAA,EAAgB,mCAAA;AAAA,MAChB,MAAA,EAAQ;AAAA,KACV;AACA,IAAA,UAAA,CAAW,MAAM,OAAO,CAAA;AACxB,IAAA,OAAO,UAAA;AAAA,MACL,MAAA;AAAA,MACA,QAAA,CAAS,QAAA;AAAA,MACT,EAAE,MAAA,EAAQ,MAAA,EAAQ,SAAS,IAAA,EAAM,IAAA,CAAK,UAAS,EAAE;AAAA,MACjD;AAAA,KACF;AAAA,EACF;AAEA,EAAA,MAAM,OAAA,GAAU,CAAC,IAAA,EAA+B,KAAA,KAA6B;AAC3E,IAAA,MAAM,KAAA,GACJ,OAAO,IAAA,CAAK,OAAO,CAAA,KAAM,QAAA,GAAW,IAAA,CAAK,OAAO,CAAA,GAAI,KAAA,CAAM,IAAA,CAAK,SAAS,CAAA;AAC1E,IAAA,OAAO;AAAA,MACL,KAAA;AAAA,MACA,MAAA,EAAQ,MAAM,KAAA,CAAM,SAAS,EAAE,MAAA,CAAO,OAAO,CAAA,CAAE,GAAA,CAAI,WAAW;AAAA,KAChE;AAAA,EACF,CAAA;AAEA,EAAA,MAAM,SAAA,GAAY,CAAC,IAAA,KAAkC;AACnD,IAAA,MAAM,KAAA,GAAQ,MAAA,CAAO,IAAA,CAAK,YAAY,CAAC,CAAA;AACvC,IAAA,OAAO,OAAO,QAAA,CAAS,KAAK,CAAA,IAAK,KAAA,GAAQ,IAAI,KAAA,GAAQ,IAAA;AAAA,EACvD,CAAA;AAEA,EAAA,eAAe,SAAS,YAAA,EAAsB;AAC5C,IAAA,OAAOI,2BAAA,CAAU,CAAA,EAAG,QAAA,CAAS,EAAE;AAAA,EAAK,OAAO,QAAQ;AAAA,EAAK,YAAY,CAAA,CAAE,CAAA;AAAA,EACxE;AAEA,EAAA,eAAe,WAAA,CACb,YAAA,EACA,GAAA,EACA,MAAA,EACoB;AACpB,IAAA,MAAM,WAAW,MAAM,YAAA;AAAA,MACrB,EAAE,UAAA,EAAY,eAAA,EAAiB,aAAA,EAAe,YAAA,EAAa;AAAA,MAC3D;AAAA,KACF;AACA,IAAA,IAAI,CAAC,SAAS,EAAA,EAAI;AAChB,MAAA,MAAMC,KAAAA,GAAO,MAAM,QAAA,CAAS,QAAQ,CAAA;AACpC,MAAA,MAAM,IAAA,GAAO,UAAUA,KAAI,CAAA;AAC3B,MAAA,IAAI,QAAA,CAAS,MAAA,KAAW,GAAA,IAAO,IAAA,KAAS,eAAA,EAAiB;AACvD,QAAA,GAAA,CAAI,EAAE,IAAA,EAAM,SAAA,EAAW,MAAA,EAAQ,WAAW,CAAA;AAC1C,QAAA,MAAM,IAAI,YAAA;AAAA,UACR,SAAA;AAAA,UACA,CAAA,IAAA,EAAO,SAAS,EAAE,CAAA,0CAAA,CAAA;AAAA,UAClB,EAAE,QAAQ,GAAA;AAAI,SAChB;AAAA,MACF;AACA,MAAA,MAAM,MAAA,GAAS,2BAA2B,QAAA,CAAS,MAAM,GAAG,IAAA,GAAO,CAAA,EAAA,EAAK,IAAI,CAAA,CAAA,CAAA,GAAM,EAAE,CAAA,CAAA;AACpF,MAAA,IAAI,iBAAA,CAAkB,QAAA,CAAS,MAAM,CAAA,EAAG;AACtC,QAAA,GAAA,CAAI,EAAE,IAAA,EAAM,SAAA,EAAW,MAAA,EAAQ,aAAA,EAAe,QAAQ,CAAA;AACtD,QAAA,MAAM,IAAI,aAAa,aAAA,EAAe,CAAA,IAAA,EAAO,SAAS,EAAE,CAAA,CAAA,EAAI,MAAM,CAAA,CAAA,EAAI;AAAA,UACpE,QAAQ,QAAA,CAAS,MAAA;AAAA,UACjB,iBAAA,EAAmB,WAAW,QAAQ;AAAA,SACvC,CAAA;AAAA,MACH;AACA,MAAA,GAAA,CAAI,EAAE,IAAA,EAAM,SAAA,EAAW,MAAA,EAAQ,QAAA,EAAU,QAAQ,CAAA;AACjD,MAAA,MAAM,IAAI,aAAa,QAAA,EAAU,CAAA,IAAA,EAAO,SAAS,EAAE,CAAA,CAAA,EAAI,MAAM,CAAA,CAAA,EAAI;AAAA,QAC/D,QAAQ,QAAA,CAAS;AAAA,OAClB,CAAA;AAAA,IACH;AACA,IAAA,MAAM,IAAA,GAAO,MAAM,QAAA,CAAS,QAAQ,CAAA;AACpC,IAAA,MAAM,WAAA,GAAc,KAAK,cAAc,CAAA;AACvC,IAAA,IAAI,OAAO,WAAA,KAAgB,QAAA,IAAY,CAAC,WAAA;AACtC,MAAA,MAAM,IAAI,YAAA;AAAA,QACR,aAAA;AAAA,QACA,CAAA,IAAA,EAAO,SAAS,EAAE,CAAA,oCAAA;AAAA,OACpB;AACF,IAAA,MAAM,OAAA,GACJ,OAAO,IAAA,CAAK,eAAe,MAAM,QAAA,IACjC,IAAA,CAAK,eAAe,CAAA,IACpB,KAAK,eAAe,CAAA,KAAM,YAAA,GACtB,IAAA,CAAK,eAAe,CAAA,GACpB,MAAA;AACN,IAAA,MAAM,kBAAA,GAAqB,UACvB,MAAM,MAAA,CAAO,QAAQ,IAAA,CAAK,OAAA,EAAS,GAAG,CAAA,GACtC,MAAA;AACJ,IAAA,OAAO;AAAA,MACL,WAAA;AAAA,MACA,SAAA,EAAW,UAAU,IAAI,CAAA;AAAA,MACzB,KAAA,EAAO,OAAA,CAAQ,IAAA,EAAM,EAAE,CAAA,CAAE,KAAA;AAAA,MACzB,GAAI,kBAAA,GACA,EAAE,oBAAoB,YAAA,EAAc,OAAA,KACpC;AAAC,KACP;AAAA,EACF;AAGA,EAAA,eAAe,QAAA,CACb,KAAA,EACA,OAAA,EACA,QAAA,EACe;AACf,IAAA,MAAM,qBAAqB,KAAA,CAAM,kBAAA;AACjC,IAAA,IAAI,CAAC,kBAAA,EAAoB;AACzB,IAAA,IAAI,CAAC,QAAQ,QAAA,EAAU;AACrB,MAAA,IAAI,CAAC,QAAA,EAAU;AACf,MAAA,GAAA,CAAI;AAAA,QACF,IAAA,EAAM,SAAA;AAAA,QACN,MAAA,EAAQ,OAAA;AAAA,QACR,MAAA,EAAQ;AAAA,OACT,CAAA;AACD,MAAA,MAAM,IAAI,YAAA;AAAA,QACR,OAAA;AAAA,QACA,CAAA,IAAA,EAAO,SAAS,EAAE,CAAA,uGAAA,CAAA;AAAA,QAClB,EAAE,kBAAA;AAAmB,OACvB;AAAA,IACF;AACA,IAAA,IAAI;AACF,MAAA,MAAM,OAAA,CAAQ,SAAS,kBAAkB,CAAA;AAAA,IAC3C,CAAA,CAAA,MAAQ;AACN,MAAA,GAAA,CAAI,EAAE,IAAA,EAAM,SAAA,EAAW,QAAQ,QAAA,EAAU,MAAA,EAAQ,kBAAkB,CAAA;AACnE,MAAA,MAAM,IAAI,YAAA;AAAA,QACR,QAAA;AAAA,QACA,CAAA,oBAAA,EAAuB,SAAS,EAAE,CAAA,4DAAA,CAAA;AAAA,QAClC,EAAE,kBAAA;AAAmB,OACvB;AAAA,IACF;AAAA,EACF;AAEA,EAAA,MAAM,YAAA,GAAe,CAAC,KAAA,MAAsC;AAAA,IAC1D,aAAa,KAAA,CAAM,WAAA;AAAA,IACnB,WAAW,KAAA,CAAM,SAAA;AAAA,IACjB,OAAO,KAAA,CAAM,KAAA;AAAA,IACb,GAAI,MAAM,kBAAA,GACN,EAAE,oBAAoB,KAAA,CAAM,kBAAA,KAC5B;AAAC,GACP,CAAA;AAEA,EAAA,eAAe,UAAA,CACb,YAAA,EACA,KAAA,EACA,EAAA,EACA;AACA,IAAA,MAAM,KAAA,GAAQ;AAAA,MACZ,OAAO,KAAA,CAAM,WAAA;AAAA,MACb,SAAA,EAAW,EAAA,GAAK,KAAA,CAAM,SAAA,GAAY;AAAA,KACpC;AACA,IAAA,KAAA,MAAW,KAAA,IAAS,CAAC,YAAA,EAAc,KAAA,CAAM,YAAY,CAAA,EAAG;AACtD,MAAA,IAAI,CAAC,KAAA,EAAO;AACZ,MAAA,IAAI,WAAW,IAAA,IAAQ,iBAAA;AACrB,QAAA,UAAA,CAAW,OAAO,UAAA,CAAW,IAAA,EAAK,CAAE,IAAA,GAAO,KAAM,CAAA;AACnD,MAAA,UAAA,CAAW,GAAA,CAAI,MAAM,QAAA,CAAS,KAAK,GAAG,KAAK,CAAA;AAAA,IAC7C;AAAA,EACF;AAGA,EAAA,eAAe,WAAA,CACb,KAAA,EACA,IAAA,EACA,MAAA,EACkB;AAClB,IAAA,IAAI,CAAC,QAAA,CAAS,SAAA,EAAW,OAAO,KAAA;AAChC,IAAA,MAAM,OAAO,IAAI,eAAA,CAAgB,EAAE,KAAA,EAAO,eAAA,EAAiB,MAAM,CAAA;AACjE,IAAA,MAAM,OAAA,GAAkC;AAAA,MACtC,cAAA,EAAgB,mCAAA;AAAA,MAChB,MAAA,EAAQ;AAAA,KACV;AACA,IAAA,IAAI,QAAA,CAAS,oBAAA,EAAsB,UAAA,CAAW,IAAA,EAAM,OAAO,CAAA;AAC3D,IAAA,IAAI;AACF,MAAA,MAAM,WAAW,MAAM,UAAA;AAAA,QACrB,MAAA;AAAA,QACA,QAAA,CAAS,SAAA;AAAA,QACT,EAAE,MAAA,EAAQ,MAAA,EAAQ,SAAS,IAAA,EAAM,IAAA,CAAK,UAAS,EAAE;AAAA,QACjD;AAAA,OACF;AAEA,MAAA,IAAI,OAAO,QAAA,CAAS,EAAA;AACpB,MAAA,IAAI,CAAC,IAAA,IAAQ,QAAA,CAAS,MAAA,KAAW,GAAA,EAAK;AACpC,QAAA,IAAA,GAAO,SAAA,CAAU,MAAM,QAAA,CAAS,QAAQ,CAAC,CAAA,KAAM,eAAA;AAAA,MACjD,CAAA,MAAO;AACL,QAAA,OAAA,CAAQ,QAAQ,CAAA;AAAA,MAClB;AACA,MAAA,GAAA,CAAI;AAAA,QACF,IAAA,EAAM,QAAA;AAAA,QACN,MAAA,EAAQ,OAAO,SAAA,GAAY,QAAA;AAAA,QAC3B,GAAI,OACA,EAAC,GACD,EAAE,MAAA,EAAQ,CAAA,yBAAA,EAA4B,QAAA,CAAS,MAAM,CAAA,CAAA;AAAG,OAC7D,CAAA;AACD,MAAA,OAAO,IAAA;AAAA,IACT,CAAA,CAAA,MAAQ;AACN,MAAA,GAAA,CAAI,EAAE,IAAA,EAAM,QAAA,EAAU,MAAA,EAAQ,eAAe,CAAA;AAC7C,MAAA,OAAO,KAAA;AAAA,IACT;AAAA,EACF;AAQA,EAAA,SAAS,YAAA,CAAa,cAAuB,WAAA,EAA2B;AACtE,IAAA,IAAI,MAAA,CAAO,uBAAuB,IAAA,EAAM;AACxC,IAAA,MAAM,CAAC,KAAA,EAAO,IAAI,CAAA,GAChB,OAAO,YAAA,KAAiB,QAAA,IAAY,YAAA,GAC/B,CAAC,YAAA,EAAc,eAAe,CAAA,GAC9B,CAAC,aAAa,cAAc,CAAA;AACnC,IAAA,WAAA,CAAY,OAAO,IAAA,EAAM,WAAA,CAAY,OAAA,CAAQ,kBAAkB,CAAC,CAAA,CAAE,KAAA;AAAA,MAChE,MAAM;AAAA,MAAC;AAAA,KACT;AAAA,EACF;AAEA,EAAA,OAAO;AAAA,IACL,QAAA;AAAA,IACA,WAAA;AAAA,IAEA,MAAM,KAAA,CAAM,OAAA,EAAS,OAAA,EAAS;AAC5B,MAAA,MAAM,OAAA,GACJ,OAAO,OAAA,CAAQ,OAAA,KAAY,WAAW,OAAA,CAAQ,OAAA,CAAQ,MAAK,GAAI,EAAA;AACjE,MAAA,IAAI,CAAC,OAAA,IAAW,OAAA,CAAQ,MAAA,GAAS,GAAA;AAC/B,QAAA,MAAM,IAAI,YAAA;AAAA,UACR,OAAA;AAAA,UACA;AAAA,SACF;AACF,MAAA,MAAM,MAAA,GAAS,QAAQ,MAAA,IAAU,EAAA;AACjC,MAAA,IAAI,OAAO,MAAA,KAAW,QAAA,IAAY,MAAA,CAAO,MAAA,GAAS,EAAA;AAChD,QAAA,MAAM,IAAI,YAAA,CAAa,OAAA,EAAS,sCAAsC,CAAA;AACxE,MAAA,MAAM,QAAA,GAAW,SAAA,CAAU,OAAA,CAAQ,QAAA,EAAU,UAAU,CAAA;AAEvD,MAAA,IACE,IAAI,GAAA,CAAI,OAAA,CAAQ,GAAG,CAAA,CAAE,aAAa,QAAA,IAClC,CAAC,cAAA,CAAe,OAAO,CAAA,EACvB;AACA,QAAA,MAAM,IAAI,YAAA;AAAA,UACR,QAAA;AAAA,UACA;AAAA,SACF;AAAA,MACF;AAEA,MAAA,IACE,cAAA,IACA,SAAS,OAAO,CAAA,KAAM,kBACtB,CAAC,YAAA,CAAa,OAAO,CAAA,EACrB;AACA,QAAA,MAAM,IAAI,YAAA;AAAA,UACR,QAAA;AAAA,UACA;AAAA,SACF;AAAA,MACF;AACA,MAAA,MAAM,SAAS,OAAA,CAAQ,MAAA,GACnB,UAAU,OAAA,CAAQ,MAAA,EAAQ,SAAS,CAAA,GACnC,aAAA;AACJ,MAAA,IAAI,OAAO,MAAA,KAAW,CAAA;AACpB,QAAA,MAAM,IAAI,YAAA;AAAA,UACR,OAAA;AAAA,UACA;AAAA,SACF;AACF,MAAA,IAAI,MAAA,CAAO,eAAA,IAAmB,CAAC,UAAA,CAAW,MAAM,CAAA;AAC9C,QAAA,MAAM,IAAI,YAAA;AAAA,UACR,OAAA;AAAA,UACA;AAAA,SACF;AACF,MAAA,MAAM,KAAA,GAAQC,8BAAY,EAAE,CAAA;AAC5B,MAAA,MAAM,QAAA,GAAWA,8BAAY,EAAE,CAAA;AAC/B,MAAA,MAAM,KAAA,GAAQ,MAAM,SAAA,CAAU;AAAA,QAC5B,OAAA;AAAA,QACA,MAAA;AAAA,QACA,QAAA;AAAA,QACA,MAAA,EAAQ,SAAS,OAAO,CAAA;AAAA,QACxB,KAAA;AAAA,QACA,WAAW,IAAA,CAAK,KAAA,CAAM,GAAA,EAAI,GAAI,GAAI,CAAA,GAAI,GAAA;AAAA,QACtC,MAAA;AAAA,QACA,IAAA,EAAM,OAAA,CAAQ,IAAA,IAAQ;AAAC,OACxB,CAAA;AACD,MAAA,IAAI,MAAM,MAAA,GAAS,IAAA;AACjB,QAAA,MAAM,IAAI,YAAA;AAAA,UACR,OAAA;AAAA,UACA;AAAA,SACF;AACF,MAAA,MAAM,SAAA,GAAYH,2BAAA;AAAA,QAChB,IAAI,UAAA;AAAA,UACF,MAAM,OAAO,MAAA,CAAO,MAAA,CAAO,WAAWD,sBAAA,CAAK,MAAA,CAAO,QAAQ,CAAC;AAAA;AAC7D,OACF;AACA,MAAA,MAAM,GAAA,GAAM,IAAI,GAAA,CAAI,QAAA,CAAS,YAAY,CAAA;AACzC,MAAA,KAAA,MAAW,CAAC,KAAK,KAAK,CAAA,IAAK,OAAO,OAAA,CAAQ,QAAA,CAAS,eAAA,IAAmB,EAAE,CAAA;AACtE,QAAA,GAAA,CAAI,YAAA,CAAa,GAAA,CAAI,GAAA,EAAK,KAAK,CAAA;AACjC,MAAA,KAAA,MAAW,CAAC,GAAA,EAAK,KAAK,CAAA,IAAK,MAAA,CAAO,QAAQ,OAAA,CAAQ,MAAA,IAAU,EAAE,CAAA,EAAG;AAC/D,QAAA,IAAI,eAAA,CAAgB,IAAI,GAAG,CAAA;AACzB,UAAA,MAAM,IAAI,YAAA;AAAA,YACR,OAAA;AAAA,YACA,uBAAuB,GAAG,CAAA,UAAA;AAAA,WAC5B;AACF,QAAA,GAAA,CAAI,YAAA,CAAa,GAAA,CAAI,GAAA,EAAK,KAAK,CAAA;AAAA,MACjC;AACA,MAAA,GAAA,CAAI,YAAA,CAAa,GAAA,CAAI,WAAA,EAAa,MAAA,CAAO,QAAQ,CAAA;AACjD,MAAA,GAAA,CAAI,YAAA,CAAa,GAAA,CAAI,cAAA,EAAgB,WAAA,CAAY,OAAO,CAAC,CAAA;AACzD,MAAA,GAAA,CAAI,YAAA,CAAa,GAAA,CAAI,eAAA,EAAiB,MAAM,CAAA;AAC5C,MAAA,GAAA,CAAI,aAAa,GAAA,CAAI,OAAA,EAAS,MAAA,CAAO,IAAA,CAAK,SAAS,CAAC,CAAA;AACpD,MAAA,GAAA,CAAI,YAAA,CAAa,GAAA,CAAI,OAAA,EAAS,KAAK,CAAA;AACnC,MAAA,GAAA,CAAI,YAAA,CAAa,GAAA,CAAI,gBAAA,EAAkB,SAAS,CAAA;AAChD,MAAA,GAAA,CAAI,YAAA,CAAa,GAAA,CAAI,uBAAA,EAAyB,MAAM,CAAA;AACpD,MAAA,MAAM,IAAA,GAAO,IAAI,QAAA,EAAS;AAC1B,MAAA,MAAM,SAAA,GAAY,OAAO,OAAA,EAAS,CAAA,EAAG,KAAK,CAAA,CAAA,EAAI,QAAQ,IAAI,GAAG,CAAA;AAC7D,MAAA,MAAM,OAAA,GAAU,EAAE,YAAA,EAAc,SAAA,EAAW,iBAAiB,UAAA,EAAW;AACvE,MAAA,OAAO;AAAA,QACL,GAAA,EAAK,IAAA;AAAA,QACL,SAAA;AAAA,QACA,QAAA,EAAU,MACR,IAAI,QAAA,CAAS,IAAA,EAAM;AAAA,UACjB,MAAA,EAAQ,GAAA;AAAA,UACR,OAAA,EAAS,EAAE,GAAG,OAAA,EAAS,UAAU,IAAA;AAAK,SACvC,CAAA;AAAA,QACH,IAAA,EAAM,MAAM,QAAA,CAAS,IAAA,CAAK,EAAE,KAAK,IAAA,EAAK,EAAG,EAAE,OAAA,EAAS;AAAA,OACtD;AAAA,IACF,CAAA;AAAA,IAEA,MAAM,SAAS,OAAA,EAAS;AACtB,MAAA,MAAM,KAAA,GAAQ,IAAI,GAAA,CAAI,OAAA,CAAQ,GAAG,CAAA,CAAE,YAAA;AACnC,MAAA,MAAM,QAAQ,MAAM,WAAA,CAAY,KAAA,CAAM,GAAA,CAAI,OAAO,CAAC,CAAA;AAClD,MAAA,IAAI,CAAC,KAAA,EAAO,OAAO,IAAA,CAAK,OAAA,EAAS,MAAM,SAAS,CAAA;AAEhD,MAAA,MAAM,QAAA,GAAW,YAAA,CAAa,OAAO,CAAA,CAClC,GAAA,CAAI,CAAC,KAAA,KAAU,KAAA,CAAM,KAAA,CAAM,GAAG,CAAC,CAAA,CAC/B,IAAA;AAAA,QACC,CAAC,CAAC,KAAA,EAAO,CAAA,EAAGK,MAAK,MACf,KAAA,KAAU,KAAA,CAAM,KAAA,IAAS,CAAA,IAAKA,MAAAA,KAAU;AAAA,UACxC,CAAC,CAAA;AACP,MAAA,IAAI,CAAC,QAAA,EAAU,OAAO,IAAA,CAAK,OAAA,EAAS,OAAO,SAAS,CAAA;AACpD,MAAA,IAAI,KAAA,CAAM,IAAI,OAAO,CAAA;AACnB,QAAA,OAAO,IAAA;AAAA,UACL,OAAA;AAAA,UACA,KAAA;AAAA,UACA,KAAA,CAAM,GAAA,CAAI,OAAO,CAAA,KAAM,kBAAkB,QAAA,GAAW;AAAA,SACtD;AACF,MAAA,MAAM,IAAA,GAAO,KAAA,CAAM,GAAA,CAAI,MAAM,CAAA;AAC7B,MAAA,IAAI,CAAC,IAAA,EAAM,OAAO,IAAA,CAAK,OAAA,EAAS,OAAO,QAAQ,CAAA;AAE/C,MAAA,IAAI,IAAA;AACJ,MAAA,IAAI;AACF,QAAA,MAAM,QAAA,GAAW,MAAM,YAAA,CAAa;AAAA,UAClC,UAAA,EAAY,oBAAA;AAAA,UACZ,IAAA;AAAA,UACA,aAAA,EAAe,QAAA;AAAA,UACf,YAAA,EAAc,YAAY,OAAO;AAAA,SAClC,CAAA;AACD,QAAA,IAAA,GAAO,MAAM,SAAS,QAAQ,CAAA;AAC9B,QAAA,IAAI,CAAC,SAAS,EAAA,EAAI;AAChB,UAAA,MAAMC,KAAAA,GAAO,UAAU,IAAI,CAAA;AAC3B,UAAA,OAAO,IAAA;AAAA,YACL,OAAA;AAAA,YACA,KAAA;AAAA,YACA,QAAA;AAAA,YACA,EAAC;AAAA,YACD,CAAA,wBAAA,EAA2B,SAAS,MAAM,CAAA,EAAGA,QAAO,CAAA,EAAA,EAAKA,KAAI,MAAM,EAAE,CAAA;AAAA,WACvE;AAAA,QACF;AAAA,MACF,SAAS,KAAA,EAAO;AACd,QAAA,OAAO,IAAA;AAAA,UACL,OAAA;AAAA,UACA,KAAA;AAAA,UACA,QAAA;AAAA,UACA,EAAC;AAAA,UACD,KAAA,YAAiB,YAAA,GACb,KAAA,CAAM,OAAA,GACN;AAAA,SACN;AAAA,MACF;AACA,MAAA,MAAM,WAAA,GAAc,KAAK,cAAc,CAAA;AACvC,MAAA,MAAM,YAAA,GAAe,KAAK,eAAe,CAAA;AACzC,MAAA,IAAI,OAAO,WAAA,KAAgB,QAAA,IAAY,CAAC,WAAA,EAAa;AACnD,QAAA,OAAO,KAAK,OAAA,EAAS,KAAA,EAAO,QAAA,EAAU,IAAI,iBAAiB,CAAA;AAAA,MAC7D;AACA,MAAA,MAAM,EAAE,KAAA,EAAO,MAAA,KAAW,OAAA,CAAQ,IAAA,EAAM,MAAM,MAAM,CAAA;AAEpD,MAAA,IAAI,CAAC,KAAA,CAAM,MAAA,CAAO,KAAA,CAAM,CAAC,CAAA,KAAM,MAAA,CAAO,QAAA,CAAS,WAAA,CAAY,CAAC,CAAC,CAAC,CAAA,EAAG;AAC/D,QAAA,YAAA,CAAa,cAAc,WAAW,CAAA;AACtC,QAAA,OAAO,IAAA,CAAK,OAAA,EAAS,KAAA,EAAO,OAAO,CAAA;AAAA,MACrC;AACA,MAAA,IAAA,CACG,OAAO,mBAAA,IAAuB,IAAA,MAC9B,OAAO,YAAA,KAAiB,QAAA,IAAY,CAAC,YAAA,CAAA,EACtC;AACA,QAAA,OAAO,IAAA;AAAA,UACL,OAAA;AAAA,UACA,KAAA;AAAA,UACA,QAAA;AAAA,UACA,EAAC;AAAA,UACD;AAAA,SACF;AAAA,MACF;AAEA,MAAA,MAAM,OAAA,GACJ,OAAO,IAAA,CAAK,UAAU,MAAM,QAAA,GAAW,IAAA,CAAK,UAAU,CAAA,GAAI,MAAA;AAE5D,MAAA,MAAM,gBACJ,MAAA,CAAO,eAAA,KAAoB,IAAA,IAAQ,UAAA,CAAW,MAAM,MAAM,CAAA;AAC5D,MAAA,MAAM,OAAO,CAAC,aAAA,GACV,MAAA,GACA,OAAA,GACE,0BAA0B,OAAA,EAAS;AAAA,QACjC,OAAA;AAAA,QACA,UAAU,MAAA,CAAO,QAAA;AAAA,QACjB,gBAAA;AAAA,QACA,KAAK,GAAA;AAAI,OACV,CAAA,GACD,EAAE,MAAA,EAAQ,aAAA,EAAc;AAC9B,MAAA,MAAM,QAAA,GAAW,IAAA,IAAQ,SAAA,IAAa,IAAA,GAAO,IAAA,GAAO,MAAA;AACpD,MAAA,MAAM,eACJ,IAAA,IAAQ,QAAA,IAAY,OAAO,CAAA,aAAA,EAAgB,IAAA,CAAK,MAAM,CAAA,CAAA,GAAK,MAAA;AAC7D,MAAA,IAAI,CAAC,QAAA,IAAY,MAAA,CAAO,eAAA,EAAiB;AACvC,QAAA,YAAA,CAAa,cAAc,WAAW,CAAA;AACtC,QAAA,OAAO,KAAK,OAAA,EAAS,KAAA,EAAO,QAAA,EAAU,IAAI,YAAY,CAAA;AAAA,MACxD;AAEA,MAAA,IAAI,KAAA;AACJ,MAAA,IAAI;AACF,QAAA,MAAM,EAAA,GAAK,MAAA,CAAO,QAAA,IAAW,IAAK,OAAO,UAAA,EAAW;AACpD,QAAA,MAAM,GAAA,GAAM,OAAO,EAAE,CAAA;AACrB,QAAA,MAAM,kBAAA,GACJ,OAAO,YAAA,KAAiB,QAAA,IAAY,YAAA,GAChC,MAAM,MAAA,CAAO,OAAA,CAAQ,IAAA,CAAK,YAAA,EAAc,GAAG,CAAA,GAC3C,EAAA;AACN,QAAA,KAAA,GAAQ,MAAM,MAAA,CAAO,KAAA;AAAA,UACnB;AAAA,YACE,EAAA;AAAA,YACA,GAAA;AAAA,YACA,kBAAA;AAAA,YACA,WAAA;AAAA,YACA,SAAA,EAAW,UAAU,IAAI,CAAA;AAAA,YACzB,KAAA;AAAA,YACA,MAAA;AAAA,YACA,GAAI,OAAA,GAAU,EAAE,OAAA,KAAY,EAAC;AAAA,YAC7B,GAAI,QAAA,GAAW,EAAE,QAAA,KAAa,EAAC;AAAA,YAC/B,SAAS,KAAA,CAAM,OAAA;AAAA,YACf,QAAQ,KAAA,CAAM,MAAA;AAAA,YACd,UAAU,KAAA,CAAM,QAAA;AAAA,YAChB,MAAM,KAAA,CAAM;AAAA,WACd;AAAA,UACA,EAAE,OAAA;AAAQ,SACZ;AAAA,MACF,CAAA,CAAA,MAAQ;AAGN,QAAA,OAAO,KAAK,OAAA,EAAS,KAAA,EAAO,QAAA,EAAU,IAAI,aAAa,CAAA;AAAA,MACzD;AACA,MAAA,OAAO,KAAK,OAAA,EAAS,KAAA,EAAO,aAAa,KAAA,IAAS,IAAI,YAAY,CAAA;AAAA,IACpE,CAAA;AAAA,IAEA,MAAM,OAAA,CAAQ,kBAAA,EAAoB,GAAA,EAAK,OAAA,GAAU,EAAC,EAAG;AACnD,MAAA,MAAM,eAAe,MAAM,MAAA,CAAO,OAAA,CAAQ,IAAA,CAAK,oBAAoB,GAAG,CAAA;AACtE,MAAA,MAAM,QAAQ,MAAM,WAAA,CAAY,YAAA,EAAc,GAAA,EAAK,QAAQ,MAAM,CAAA;AACjE,MAAA,MAAM,QAAA,CAAS,KAAA,EAAO,OAAA,EAAS,KAAK,CAAA;AACpC,MAAA,OAAO,aAAa,KAAK,CAAA;AAAA,IAC3B,CAAA;AAAA,IAEA,MAAM,WAAA,CAAY,kBAAA,EAAoB,GAAA,EAAK,OAAA,GAAU,EAAC,EAAG;AACvD,MAAA,MAAM,eAAe,MAAM,MAAA,CAAO,OAAA,CAAQ,IAAA,CAAK,oBAAoB,GAAG,CAAA;AACtE,MAAA,MAAM,GAAA,GAAM,MAAM,QAAA,CAAS,YAAY,CAAA;AACvC,MAAA,MAAM,GAAA,GAAM,UAAA,CAAW,GAAA,CAAI,GAAG,CAAA;AAC9B,MAAA,IAAI,OAAO,GAAA,CAAI,SAAA,GAAY,MAAS,GAAA,EAAI,SAAU,GAAA,CAAI,KAAA;AACtD,MAAA,IAAI,MAAA,GAAS,QAAA,CAAS,GAAA,CAAI,GAAG,CAAA;AAC7B,MAAA,IAAI,CAAC,MAAA,EAAQ;AACX,QAAA,MAAM,KAAK,GAAA,EAAI;AACf,QAAA,MAAA,GAAS,WAAA,CAAY,cAAc,GAAA,EAAK,OAAA,CAAQ,MAAM,CAAA,CACnD,IAAA,CAAK,OAAOC,MAAAA,KAAU;AAErB,UAAA,MAAM,UAAA,CAAW,YAAA,EAAcA,MAAAA,EAAO,EAAE,CAAA;AACxC,UAAA,OAAOA,MAAAA;AAAA,QACT,CAAC,CAAA,CACA,OAAA,CAAQ,MAAM,QAAA,CAAS,MAAA,CAAO,GAAG,CAAC,CAAA;AACrC,QAAA,QAAA,CAAS,GAAA,CAAI,KAAK,MAAM,CAAA;AAAA,MAC1B;AACA,MAAA,MAAM,QAAQ,MAAM,MAAA;AACpB,MAAA,MAAM,QAAA,CAAS,KAAA,EAAO,OAAA,EAAS,IAAI,CAAA;AACnC,MAAA,OAAO,KAAA,CAAM,WAAA;AAAA,IACf,CAAA;AAAA,IAEA,MAAM,MAAA,CAAO,kBAAA,EAAoB,GAAA,EAAK,OAAA,GAAU,EAAC,EAAG;AAClD,MAAA,MAAM,eAAe,MAAM,MAAA,CAAO,OAAA,CAAQ,IAAA,CAAK,oBAAoB,GAAG,CAAA;AACtE,MAAA,UAAA,CAAW,MAAA,CAAO,MAAM,QAAA,CAAS,YAAY,CAAC,CAAA;AAC9C,MAAA,OAAO,WAAA,CAAY,YAAA,EAAc,eAAA,EAAiB,OAAA,CAAQ,MAAM,CAAA;AAAA,IAClE;AAAA,GACF;AACF","file":"index.cjs","sourcesContent":["/**\n * An OAuth 2.0 provider is plain data: where to send the person, where to\n * trade codes and refresh tokens, where to revoke. Google is built in; any\n * other authorization-code provider is an object of the same shape.\n */\nexport interface OAuthProvider {\n /** A short id, used in the default cookie name and the state key's derivation: `\"google\"`. */\n readonly id: string;\n /** The authorization endpoint the browser is sent to. */\n readonly authorizeUrl: string;\n /** The token endpoint (code and refresh-token grants). */\n readonly tokenUrl: string;\n /**\n * The OpenID Connect issuer(s) its ID tokens name in `iss`. Without it, no `identity` is read\n * from an ID token.\n */\n readonly issuer?: string | readonly string[] | undefined;\n /** The revocation endpoint (RFC 7009), when the provider has one. */\n readonly revokeUrl?: string | undefined;\n /** Scopes asked for when neither the connector nor `start` names any. */\n readonly defaultScopes?: readonly string[] | undefined;\n /**\n * Extra authorization parameters, such as Google's `access_type=offline`\n * and `prompt=consent` (which is what makes it send a refresh token).\n */\n readonly authorizeParams?: Readonly<Record<string, string>> | undefined;\n /**\n * How the client authenticates at the token endpoint: in the form body\n * (`client_secret_post`, the default) or with HTTP Basic (`client_secret_basic`).\n */\n readonly tokenAuth?: \"client_secret_post\" | \"client_secret_basic\" | undefined;\n /** Whether the revocation request also carries the client's credentials. Default false. */\n readonly revokeWithClientAuth?: boolean | undefined;\n /** How the provider joins scopes. Default a space (RFC 6749). */\n readonly scopeSeparator?: string | undefined;\n /**\n * Shorthand scopes the provider answers with their full name, so the\n * granted-scope check compares like with like: Google grants `email` as\n * `https://www.googleapis.com/auth/userinfo.email`. Both what is asked\n * for and what is granted are mapped through it.\n */\n readonly scopeAliases?: Readonly<Record<string, string>> | undefined;\n}\n\n/** Google's OAuth 2.0 endpoints, asking for offline access (a refresh token) every time. */\nexport const googleProvider: OAuthProvider = {\n id: \"google\",\n authorizeUrl: \"https://accounts.google.com/o/oauth2/v2/auth\",\n tokenUrl: \"https://oauth2.googleapis.com/token\",\n revokeUrl: \"https://oauth2.googleapis.com/revoke\",\n issuer: [\"https://accounts.google.com\", \"accounts.google.com\"],\n defaultScopes: [],\n authorizeParams: {\n access_type: \"offline\",\n // Without consent on every sign-in, Google sends a refresh token only the first time.\n prompt: \"consent\",\n // Only the scopes asked for now, so the granted-scope check means what it says.\n include_granted_scopes: \"false\",\n },\n tokenAuth: \"client_secret_post\",\n scopeAliases: {\n email: \"https://www.googleapis.com/auth/userinfo.email\",\n profile: \"https://www.googleapis.com/auth/userinfo.profile\",\n openid: \"openid\",\n },\n};\n\n/** Google scopes apps commonly connect with. Prefer the narrowest. */\nexport const GOOGLE_SCOPES = {\n /** OpenID Connect: an ID token naming the account. */\n openid: \"openid\",\n /** The account's email address (Google grants it as `…/auth/userinfo.email`). */\n email: \"email\",\n /** The account's name and picture (granted as `…/auth/userinfo.profile`). */\n profile: \"profile\",\n /** Files the person picked or created through the app, and nothing else. */\n driveFile: \"https://www.googleapis.com/auth/drive.file\",\n /** Every spreadsheet the person can reach. Prefer `driveFile`. */\n spreadsheets: \"https://www.googleapis.com/auth/spreadsheets\",\n /** Read and change events on the person's calendars. Prefer `calendarEventsReadonly` to only read. */\n calendarEvents: \"https://www.googleapis.com/auth/calendar.events\",\n /** Read events on every calendar the person can see, and nothing else. */\n calendarEventsReadonly:\n \"https://www.googleapis.com/auth/calendar.events.readonly\",\n /** The list of calendars the person has (names, colors, which are shown), not their events. */\n calendarListReadonly:\n \"https://www.googleapis.com/auth/calendar.calendarlist.readonly\",\n /** Read everything on the person's calendars (calendars, events, settings). Prefer the two above. */\n calendarReadonly: \"https://www.googleapis.com/auth/calendar.readonly\",\n /** Send mail as the person. */\n gmailSend: \"https://www.googleapis.com/auth/gmail.send\",\n} as const;\n","/**\n * \"Connect an outside account\" on Web `Request` and `Response`: the OAuth 2.0\n * authorization-code flow with PKCE, for any framework that speaks fetch\n * (Next.js route handlers, TanStack Start, Hono, Remix, Workers).\n *\n * start the app has signed the person in and says who they are; the\n * kit answers the provider's sign-in URL and a cookie. The URL\n * carries a signed, short-lived state (who, what for, where to\n * come back to, a nonce); the cookie (HttpOnly, SameSite=Lax)\n * carries the nonce and the PKCE verifier, so only the browser\n * that started can finish.\n * callback checks the state's signature and age and that the cookie's\n * nonce matches, handles a refusal, trades the code (PKCE, never\n * following a redirect), checks every scope asked for was\n * granted, seals the refresh token with the keyring and hands it\n * to the app's `store`. Then back to where it started with a\n * short result: connected, expired, browser, denied, scope or\n * failed.\n * accessToken / revoke open a sealed refresh token to get a fresh access\n * token (cached in memory until a minute before it expires), or\n * to revoke the grant.\n *\n * One callback origin can serve many: pin `redirectOrigin` (the one address\n * registered with the provider) and set `sharedDomain`, and a flow started on\n * any https address under that domain finishes on the pinned one and returns\n * to the address it started on, and to no other.\n *\n * Never logs or returns a token, a code, a client secret or a key; token\n * requests never follow redirects.\n */\nimport {\n base64url,\n fromBase64,\n randomToken,\n sha256Hex,\n utf8,\n type WebCryptoKey,\n} from \"./encoding\";\nimport type { OAuthProvider } from \"./providers\";\nimport { secretKeyBytes, type Secrets } from \"./secrets\";\n\n/** How a connection attempt ended, as the `connect` query parameter on the way back. */\nexport type ConnectResult =\n /** Connected; the app's `store` kept the account. */\n | \"connected\"\n /** The signed state was missing, forged or too old: start again. */\n | \"expired\"\n /** The browser that came back is not the one that started (no or another cookie). */\n | \"browser\"\n /** The person said no at the provider (or it sent no code). */\n | \"denied\"\n /** The person unticked a scope the app needs. */\n | \"scope\"\n /** The provider or the app's `store` failed. */\n | \"failed\";\n\n/** Every result `callback` can send back with. */\nexport const CONNECT_RESULTS: readonly ConnectResult[] = [\n \"connected\",\n \"expired\",\n \"browser\",\n \"denied\",\n \"scope\",\n \"failed\",\n];\n\n/** What went wrong outside the callback, for code to branch on. */\nexport type ConnectErrorCode =\n /** The connector is set up wrong (a missing secret, a bad origin). */\n | \"config\"\n /** A call was given something it can't use (no subject, an absolute returnTo). */\n | \"usage\"\n /** The grant is gone (`invalid_grant`): access was removed. Connect again. */\n | \"revoked\"\n /** The provider is down, limiting, or timed out. Worth retrying. */\n | \"unavailable\"\n /** The provider refused for another reason. */\n | \"failed\";\n\n/** A failure with a message safe to show and log: never a token, a code or a secret. */\nexport class ConnectError extends Error {\n override name = \"ConnectError\";\n readonly code: ConnectErrorCode;\n readonly retryable: boolean;\n readonly status: number | undefined;\n readonly retryAfterSeconds: number | undefined;\n /**\n * Set when the provider rotated the refresh token but it could not be\n * handed over (no `onRotate`, or `onRotate` threw): the new one, sealed to\n * the same row. Store it in place of the old one, or the grant is lost.\n */\n readonly sealedRefreshToken: string | undefined;\n constructor(\n code: ConnectErrorCode,\n message: string,\n options: {\n status?: number | undefined;\n retryAfterSeconds?: number | undefined;\n sealedRefreshToken?: string | undefined;\n } = {},\n ) {\n super(message);\n this.code = code;\n this.retryable = code === \"unavailable\";\n this.status = options.status;\n this.retryAfterSeconds = options.retryAfterSeconds;\n this.sealedRefreshToken = options.sealedRefreshToken;\n }\n}\n\nconst CONNECT_ERROR_CODES: readonly string[] = [\n \"config\",\n \"usage\",\n \"revoked\",\n \"unavailable\",\n \"failed\",\n];\n\n/**\n * Whether `error` is a `ConnectError`, by its name and code rather than\n * `instanceof`, so it also matches a copy from another bundle.\n */\nexport function isConnectError(error: unknown): error is ConnectError {\n return (\n error instanceof Error &&\n error.name === \"ConnectError\" &&\n CONNECT_ERROR_CODES.includes((error as { code?: unknown }).code as string)\n );\n}\n\n/** Small app data carried in the signed state. Signed, not encrypted: never put a secret in it. */\nexport type StateData = Readonly<\n Record<string, string | number | boolean | null>\n>;\n\n/** What `start` is told about this attempt. */\nexport interface StartOptions<D extends StateData = StateData> {\n /**\n * Who is connecting: the app's verified user id. Handed back to `store`.\n * The state is signed, not encrypted, so this is readable in the sign-in\n * URL (and by the provider): prefer an opaque id over an email address.\n */\n subject: string;\n /** What the connection is for, app-defined (`\"sheets\"`, `\"calendar\"`). At most 64 characters. */\n intent?: string | undefined;\n /**\n * Where to come back to: a path on the app (`\"/settings/integrations\"`),\n * never a full URL. The result is added as `?connect=…`.\n */\n returnTo: string;\n /** Scopes for this attempt, instead of the connector's. */\n scopes?: readonly string[] | undefined;\n /** Small app data to have back in `store` (a form id, a chosen file). Not secret. */\n data?: D | undefined;\n /** Extra authorization parameters (`login_hint`). The flow's own can't be overridden. */\n params?: Readonly<Record<string, string>> | undefined;\n}\n\n/** `start`'s answer: send the browser to `url` with `setCookie` set. */\nexport interface StartResult {\n /** The provider's sign-in URL. */\n url: string;\n /** The `Set-Cookie` header value holding the nonce and the PKCE verifier. */\n setCookie: string;\n /** A 303 to `url` with the cookie: for a start reached by a link or a form post. */\n redirect(): Response;\n /** `{ url }` as JSON with the cookie: for a start the page calls with fetch, then navigates. */\n json(): Response;\n}\n\n/** What `store` is handed: the new account, its refresh token already sealed. */\nexport interface ConnectedAccount<D extends StateData = StateData> {\n /** A new id for this grant (a UUID): the row to keep it under. */\n id: string;\n /** The associated data the refresh token is sealed to (`aad(id)`): pass it to `accessToken` and `revoke`. */\n aad: string;\n /** The refresh token, sealed with the keyring. Store it; it never needs opening by the app. */\n sealedRefreshToken: string;\n /** A fresh access token, to look the account up now (an email, a file). Don't store it. */\n accessToken: string;\n /** Seconds the access token is good for. */\n expiresIn: number;\n /** The granted scopes, as the provider joined them. */\n scope: string;\n /** The granted scopes. */\n scopes: string[];\n /**\n * The OpenID Connect ID token, when the provider sent one (an `openid` scope), as it came.\n * Its claims are NOT checked here: read who the account is from `identity`, which is.\n */\n idToken?: string | undefined;\n /**\n * Who the account is, read from the ID token: to show \"connected as …\" and to tell two\n * accounts of one person apart. Only with an `openid` scope (add `email` for the address), a\n * provider with an `issuer` and an https token endpoint; left out when the token's issuer,\n * audience, authorized party or expiry don't check out (the connection itself still succeeds).\n */\n identity?: AccountIdentity | undefined;\n /** From `start`. */\n subject: string;\n intent: string;\n returnTo: string;\n data: D;\n}\n\n/** The account an ID token names. `subject` is the provider's stable id for it; the rest may change. */\nexport interface AccountIdentity {\n /** The provider's id for the account (`sub`): stable, unique per provider. */\n subject: string;\n /** Only when the provider says it's verified. Never key an account on it: use `subject`. */\n email?: string | undefined;\n emailVerified?: boolean | undefined;\n /** As the person set them: untrusted text and an untrusted URL. Escape or check before use. */\n name?: string | undefined;\n picture?: string | undefined;\n}\n\n/** What the callback checks an ID token's claims against. */\ninterface IdTokenExpectations {\n /** The provider's issuer(s): `iss` must be one. */\n issuers: readonly string[];\n /** This connector's client id: `aud` must name it, and `azp` must be it when present (and is required when `aud` names several). */\n clientId: string;\n /** Other audiences `aud` may also name. Any other one fails the check. */\n trustedAudiences: readonly string[];\n /** Now, in milliseconds: `exp` must be later. */\n now: number;\n}\n\n/**\n * The account the ID token from the token endpoint names, or undefined. It never checks the\n * signature, which OpenID Connect allows only for a token taken straight from the token endpoint\n * over TLS (Core 3.1.3.7, step 6; `createConnector` refuses a token URL that isn't https); the\n * claims are still checked (steps 2, 3, 4, 5 and 9): the issuer is the provider's, the audience is\n * this client and names no audience it doesn't trust, `azp` is this client when present (and is\n * present when there are several audiences), and the token hasn't expired. Anything else is undefined. Internal to the callback on purpose: never\n * export it, or apps would trust tokens from elsewhere.\n */\nfunction identityFromTokenEndpoint(\n idToken: string,\n expect: IdTokenExpectations,\n): AccountIdentity | { reason: string } {\n if (expect.issuers.length === 0)\n return { reason: \"the provider names no issuer\" };\n const part = idToken.split(\".\")[1];\n if (!part) return { reason: \"not a JWT\" };\n let claims: Record<string, unknown>;\n try {\n const value = JSON.parse(\n new TextDecoder().decode(fromBase64(part)),\n ) as unknown;\n if (!value || typeof value !== \"object\" || Array.isArray(value))\n return { reason: \"unreadable claims\" };\n claims = value as Record<string, unknown>;\n } catch {\n return { reason: \"unreadable claims\" };\n }\n if (\n typeof claims[\"iss\"] !== \"string\" ||\n !expect.issuers.includes(claims[\"iss\"])\n )\n return { reason: \"issuer mismatch\" };\n const aud = claims[\"aud\"];\n const audiences: unknown[] =\n typeof aud === \"string\" ? [aud] : Array.isArray(aud) ? aud : [];\n if (!audiences.includes(expect.clientId))\n return { reason: \"audience mismatch\" };\n // Step 3: reject a token that also names an audience this client doesn't trust.\n if (\n audiences.some(\n (a) =>\n a !== expect.clientId &&\n !(typeof a === \"string\" && expect.trustedAudiences.includes(a)),\n )\n )\n return { reason: \"untrusted audience\" };\n // Steps 4 and 5: azp, when present, is this client; with several audiences it must be present.\n const azp = claims[\"azp\"];\n if (\n (azp !== undefined && azp !== expect.clientId) ||\n (audiences.length > 1 && azp === undefined)\n )\n return { reason: \"authorized party mismatch\" };\n const exp = claims[\"exp\"];\n if (typeof exp !== \"number\" || exp * 1000 <= expect.now)\n return { reason: \"expired\" };\n const text = (key: string) =>\n typeof claims[key] === \"string\" && claims[key]\n ? (claims[key] as string)\n : undefined;\n const subject = text(\"sub\");\n if (!subject) return { reason: \"no subject\" };\n const verified =\n claims[\"email_verified\"] === true || claims[\"email_verified\"] === \"true\";\n return {\n subject,\n // The address only when the provider says it's verified: never key an account on it.\n ...(verified && text(\"email\")\n ? { email: text(\"email\"), emailVerified: true }\n : {}),\n ...(text(\"name\") ? { name: text(\"name\") } : {}),\n ...(text(\"picture\") ? { picture: text(\"picture\") } : {}),\n };\n}\n\n/** What `store` may add to the way back, as query parameters (a grant id to finish with). */\nexport type StoreResult = void | undefined | Readonly<Record<string, string>>;\n\n/** A safe log line: never a token, a code, a secret or a provider's error text. */\nexport interface ConnectLogEvent {\n step: \"callback\" | \"refresh\" | \"revoke\";\n result: ConnectResult | ConnectErrorCode | \"revoked\";\n /** A short, safe reason: \"token endpoint answered 400\", \"store threw\". */\n detail?: string | undefined;\n}\n\n/** Options shared by the requests the connector makes. */\nexport interface HttpOptions {\n /** Defaults to the global `fetch`. */\n fetch?: typeof fetch | undefined;\n /** Per request, in milliseconds. Default 10 000. */\n timeoutMs?: number | undefined;\n}\n\nexport interface ConnectorConfig<\n D extends StateData = StateData,\n> extends HttpOptions {\n provider: OAuthProvider;\n /** The app's OAuth client. Server side only. */\n clientId: string;\n clientSecret: string;\n /** 32 random bytes, base64 (`openssl rand -base64 32`): signs the state. Not the keyring's key. */\n stateSecret: string | Uint8Array;\n /** Seals refresh tokens (`createSecrets(keyring, { purpose: \"google-refresh-token\" })`). */\n secrets: Secrets;\n /** The callback's path on the app (`\"/api/connect/google/callback\"`). Register `<origin><path>` with the provider. */\n callbackPath: string;\n /** Scopes asked for when `start` names none. Default the provider's `defaultScopes`. */\n scopes?: readonly string[] | undefined;\n /**\n * The one origin the provider sends everyone back to (`https://app.example.com`),\n * when the app runs on many. Without it, each request's own origin.\n */\n redirectOrigin?: string | undefined;\n /**\n * The parent domain the PKCE cookie is shared on (`example.com`), so a flow\n * started on `team.example.com` can finish on the pinned `app.example.com`\n * and come back to `team.example.com`. Only https origins on it are ever\n * returned to. Leave it out to keep the cookie on one host.\n */\n sharedDomain?: string | undefined;\n /** The cookie's name. Default `genesis_connect_<provider id>`. */\n cookieName?: string | undefined;\n /** The cookie's path. Default `callbackPath`, so it is only ever sent to the callback. */\n cookiePath?: string | undefined;\n /** How long the trip to the provider and back may take. Default 600 seconds, at most 3600. */\n stateTtlSeconds?: number | undefined;\n /** The query parameter the result comes back in. Default `\"connect\"`. */\n resultParam?: string | undefined;\n /** Where to go back to when the state can't be read (so there is no `returnTo`). Default `\"/\"`. */\n fallbackPath?: string | undefined;\n /** The associated data a grant's refresh token is sealed to. Default `<provider id>-grant:<id>`. */\n aad?: ((id: string) => string) | undefined;\n /** Fail with `failed` when the provider sends no refresh token. Default true. */\n requireRefreshToken?: boolean | undefined;\n /**\n * Fail with `failed` when no `identity` can be read (no ID token, or one whose issuer,\n * audience or expiry don't check out). Default false: the connection succeeds without one,\n * and the reason is logged.\n */\n requireIdentity?: boolean | undefined;\n /**\n * Audiences other than `clientId` an ID token's `aud` may also name (another client of the\n * same app). Default none: a token naming any other audience gives no `identity`.\n */\n trustedAudiences?: readonly string[] | undefined;\n /**\n * Revoke the new grant (best effort, through the provider's `revokeUrl`) when the callback\n * turns it down after the code was traded: a missing scope or, with `requireIdentity`, no\n * identity. Default false: Google ends every token of the person's grant across the project\n * on a revoke, so a returning person who unticks a scope would also lose the connection they\n * already have. Set it true only when each grant is isolated (one connection per client).\n */\n revokeFailedGrants?: boolean | undefined;\n /**\n * Keeps the new account: the app's row, under `account.id`, for\n * `account.subject` (the user who started). Use that, never a session read\n * from the callback's request: the callback is a top-level navigation the\n * provider sends, and the session there may be another one. May return\n * query parameters for the way back. Throwing ends the attempt as `failed`.\n */\n store: (\n account: ConnectedAccount<D>,\n context: { request: Request },\n ) => StoreResult | Promise<StoreResult>;\n /** Safe log lines (see `ConnectLogEvent`). */\n log?: ((event: ConnectLogEvent) => void) | undefined;\n /**\n * Whether a domain is a public suffix (where a cookie would be dropped), for\n * the `sharedDomain` check: pass a Public Suffix List lookup to cover every\n * suffix. The built-in list only knows common hosting suffixes.\n */\n isPublicSuffix?: ((domain: string) => boolean) | undefined;\n now?: (() => number) | undefined;\n randomId?: (() => string) | undefined;\n}\n\n/** A fresh access token, and the refresh token again when the provider rotated it. */\nexport interface RefreshedToken {\n accessToken: string;\n expiresIn: number;\n scope: string;\n /** Set when the provider sent a new refresh token: store it in place of the old one. */\n sealedRefreshToken?: string | undefined;\n}\n\nexport interface TokenCallOptions {\n /** Aborts the request. Concurrent `accessToken` calls share one refresh: the first caller's signal aborts it. */\n signal?: AbortSignal | undefined;\n /**\n * Stores the new sealed refresh token when the provider rotates it (some\n * do on every refresh; Google does not). `accessToken` needs it for such a\n * provider: a rotation without it throws a `usage` `ConnectError` carrying\n * the new token on `sealedRefreshToken`, so it is never silently lost. If\n * it throws, a `failed` `ConnectError` carries the token the same way. The\n * access token is cached before it runs.\n */\n onRotate?: ((sealedRefreshToken: string) => void | Promise<void>) | undefined;\n}\n\nexport interface Connector<D extends StateData = StateData> {\n readonly provider: OAuthProvider;\n /** The redirect URI for a request: `<redirectOrigin or the request's origin><callbackPath>`. */\n redirectUri(request: Request): string;\n /** Begins a connection (see the module comment). The app checks who is signed in first. */\n start(request: Request, options: StartOptions<D>): Promise<StartResult>;\n /** Finishes one: always a 303 back to the app, with the result and a cleared cookie. */\n callback(request: Request): Promise<Response>;\n /**\n * A fresh access token for a sealed refresh token, cached until a minute\n * before it expires; concurrent calls for one grant share one refresh.\n * Needs `onRotate` for providers that rotate refresh tokens.\n */\n accessToken(\n sealedRefreshToken: string,\n aad: string,\n options?: TokenCallOptions,\n ): Promise<string>;\n /**\n * Trades a sealed refresh token for an access token, uncached. A rotated\n * refresh token comes back sealed on the result (and goes to `onRotate`).\n */\n refresh(\n sealedRefreshToken: string,\n aad: string,\n options?: TokenCallOptions,\n ): Promise<RefreshedToken>;\n /**\n * Revokes the grant at the provider and forgets its cached access token.\n * True when it is gone (revoked now, or already); false when the provider\n * could not be reached or has no revocation endpoint. Some providers\n * (Google) end every token of the person's grant to this client, so call\n * it only when the app no longer uses any.\n */\n revoke(\n sealedRefreshToken: string,\n aad: string,\n options?: { signal?: AbortSignal | undefined },\n ): Promise<boolean>;\n}\n\n// Validation ---------------------------------------------------------------------------------------\n\n/** Shared hosting suffixes on the Public Suffix List: a cookie scoped to one is dropped. */\nconst PUBLIC_SUFFIXES = new Set([\n \"vercel.app\",\n \"netlify.app\",\n \"pages.dev\",\n \"workers.dev\",\n \"github.io\",\n \"herokuapp.com\",\n \"onrender.com\",\n \"fly.dev\",\n \"web.app\",\n \"firebaseapp.com\",\n \"azurewebsites.net\",\n \"appspot.com\",\n \"ngrok-free.app\",\n \"ngrok.io\",\n \"cloudfront.net\",\n \"up.railway.app\",\n \"deno.dev\",\n \"co.uk\",\n \"com.au\",\n \"co.jp\",\n \"com.br\",\n \"co.in\",\n]);\n\nconst COOKIE_NAME = /^[!#$%&'*+\\-.^_`|~0-9A-Za-z]+$/;\nconst SCOPE_TOKEN = /^[\\x21\\x23-\\x5B\\x5D-\\x7E]+$/;\nconst HOSTNAME =\n /^(?=.{1,253}$)([a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?)(\\.[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?)+$/;\nconst RESERVED_PARAMS = new Set([\n \"client_id\",\n \"redirect_uri\",\n \"response_type\",\n \"scope\",\n \"state\",\n \"code_challenge\",\n \"code_challenge_method\",\n]);\n\nconst isLocal = (hostname: string) =>\n hostname === \"localhost\" || hostname === \"127.0.0.1\" || hostname === \"[::1]\";\n\nfunction checkOrigin(value: string, what: string): string {\n let url: URL;\n try {\n url = new URL(value);\n } catch {\n throw new ConnectError(\"config\", `${what} is not a URL`);\n }\n const origin = value.replace(/\\/+$/, \"\");\n if (url.origin !== origin)\n throw new ConnectError(\n \"config\",\n `${what} must be an origin only (scheme, host and port)`,\n );\n if (\n url.protocol !== \"https:\" &&\n !(url.protocol === \"http:\" && isLocal(url.hostname))\n ) {\n throw new ConnectError(\n \"config\",\n `${what} must be https (http only on localhost)`,\n );\n }\n return origin;\n}\n\n/** An endpoint the client secret or a token is sent to: https, or http only on localhost. */\nfunction checkEndpoint(value: string, what: string): string {\n let url: URL;\n try {\n url = new URL(value);\n } catch {\n throw new ConnectError(\"config\", `The provider's ${what} is not a URL`);\n }\n if (\n url.protocol !== \"https:\" &&\n !(url.protocol === \"http:\" && isLocal(url.hostname))\n ) {\n throw new ConnectError(\n \"config\",\n `The provider's ${what} must be https (http only on localhost)`,\n );\n }\n return value;\n}\n\nfunction checkPath(value: string, what: string): string {\n if (\n typeof value !== \"string\" ||\n !value.startsWith(\"/\") ||\n value.startsWith(\"//\") ||\n /[\\\\\\s\\x00-\\x1f\\x7f]/.test(value) ||\n value.length > 512\n ) {\n throw new ConnectError(\n \"usage\",\n `${what} must be a path on this app, starting with one /`,\n );\n }\n // Resolved against a placeholder origin it must not leave.\n const url = new URL(value, \"https://origin.invalid\");\n if (url.origin !== \"https://origin.invalid\")\n throw new ConnectError(\n \"usage\",\n `${what} must be a path on this app, starting with one /`,\n );\n return `${url.pathname}${url.search}${url.hash}`;\n}\n\nconst onDomain = (hostname: string, domain: string) =>\n hostname === domain || hostname.endsWith(`.${domain}`);\n\n/** Whether `origin` is exactly an https origin on `domain`, so it is safe to send the browser back to. */\nexport function isSafeReturnOrigin(origin: string, domain: string): boolean {\n try {\n const url = new URL(origin);\n return (\n url.protocol === \"https:\" &&\n url.port === \"\" &&\n url.origin === origin &&\n onDomain(url.hostname, domain)\n );\n } catch {\n return false;\n }\n}\n\nfunction scopeList(\n scopes: readonly string[] | undefined,\n separator: string,\n): string[] {\n const list = [...new Set(scopes ?? [])];\n for (const scope of list) {\n if (\n typeof scope !== \"string\" ||\n !SCOPE_TOKEN.test(scope) ||\n scope.includes(separator)\n )\n throw new ConnectError(\n \"usage\",\n \"A scope is malformed (no spaces, quotes or the provider's separator)\",\n );\n }\n return list;\n}\n\n// HTTP ---------------------------------------------------------------------------------------------\n\nconst DEFAULT_TIMEOUT_MS = 10_000;\n/** How long the callback's best-effort revoke of a turned-down grant may run in the background. */\nconst ABANDON_TIMEOUT_MS = 3_000;\n\nasync function timedFetch(\n deps: HttpOptions,\n url: string,\n init: RequestInit,\n signal?: AbortSignal,\n): Promise<Response> {\n const timeout = AbortSignal.timeout(deps.timeoutMs ?? DEFAULT_TIMEOUT_MS);\n const combined = signal ? anySignal([timeout, signal]) : timeout;\n try {\n // Never follow a redirect: the body holds a code, a token or the client secret.\n return await (deps.fetch ?? fetch)(url, {\n ...init,\n redirect: \"error\",\n signal: combined,\n });\n } catch (error) {\n const name = error instanceof Error ? error.name : \"\";\n if (name === \"TimeoutError\")\n throw new ConnectError(\n \"unavailable\",\n \"The provider did not answer in time\",\n );\n if (name === \"AbortError\")\n throw new ConnectError(\"unavailable\", \"The request was aborted\");\n throw new ConnectError(\"unavailable\", \"The provider could not be reached\");\n }\n}\n\n/** `AbortSignal.any`, combined by hand where the runtime lacks it, so the timeout always holds. */\nfunction anySignal(signals: AbortSignal[]): AbortSignal {\n const any = (\n AbortSignal as unknown as { any?: (signals: AbortSignal[]) => AbortSignal }\n ).any;\n if (any) return any(signals);\n const controller = new AbortController();\n for (const signal of signals) {\n if (signal.aborted) {\n controller.abort(signal.reason);\n break;\n }\n signal.addEventListener(\"abort\", () => controller.abort(signal.reason), {\n once: true,\n });\n }\n return controller.signal;\n}\n\nfunction discard(response: Response): void {\n try {\n void response.body?.cancel().catch(() => {});\n } catch {\n // A mocked or already-read body.\n }\n}\n\nasync function jsonBody(response: Response): Promise<Record<string, unknown>> {\n const value = (await response.json().catch(() => null)) as unknown;\n return value && typeof value === \"object\" && !Array.isArray(value)\n ? (value as Record<string, unknown>)\n : {};\n}\n\n/** A provider's error code, when it is one (`invalid_grant`), never its free text. */\nfunction errorCode(body: Record<string, unknown>): string | undefined {\n const code = body[\"error\"];\n return typeof code === \"string\" && /^[a-z_]{1,64}$/.test(code)\n ? code\n : undefined;\n}\n\nfunction retryAfter(response: Response): number | undefined {\n const header = response.headers.get(\"retry-after\")?.trim();\n return header && /^\\d+$/.test(header)\n ? Math.min(86_400, Math.max(1, Number(header)))\n : undefined;\n}\n\nconst isRetryableStatus = (status: number) =>\n status === 408 || status === 425 || status === 429 || status >= 500;\n\n// Access-token cache -------------------------------------------------------------------------------\n\n// One per process, shared by connectors: keyed by a digest of the provider, the\n// client and the refresh token, so a hit is only ever for the same grant.\nconst tokenCache = new Map<string, { token: string; expiresAt: number }>();\n// Refreshes in flight, by the same key: concurrent cold calls share one.\nconst inFlight = new Map<string, Promise<Refreshed>>();\nconst TOKEN_CACHE_LIMIT = 1000;\n\n/** Forgets every cached access token (tests, or after a key or client change). */\nexport function clearAccessTokenCache(): void {\n tokenCache.clear();\n}\n\n// The connector ------------------------------------------------------------------------------------\n\n/** A refresh as `refreshWith` does it: the result, plus the rotated token in plain text to cache under. */\ninterface Refreshed extends RefreshedToken {\n rotatedPlain?: string | undefined;\n}\n\ninterface SignedState {\n subject: string;\n intent: string;\n returnTo: string;\n origin: string;\n nonce: string;\n expiresAt: number;\n scopes: string[];\n data: StateData;\n}\n\n/** Sets up a connector for one provider. Checks its config now, so a mistake shows at boot. */\nexport function createConnector<D extends StateData = StateData>(\n config: ConnectorConfig<D>,\n): Connector<D> {\n const { provider } = config;\n if (!provider?.id || !provider.authorizeUrl || !provider.tokenUrl)\n throw new ConnectError(\n \"config\",\n \"The provider needs an id, an authorize URL and a token URL\",\n );\n // The client secret, the code and every token go to these; the ID token is trusted unsigned\n // only because it comes from the token endpoint over TLS (OpenID Connect Core 3.1.3.7).\n checkEndpoint(provider.tokenUrl, \"token URL\");\n if (provider.revokeUrl) checkEndpoint(provider.revokeUrl, \"revoke URL\");\n if (!config.clientId || !config.clientSecret)\n throw new ConnectError(\n \"config\",\n `The ${provider.id} OAuth client id or secret is not set`,\n );\n if (!config.secrets)\n throw new ConnectError(\n \"config\",\n \"The connector needs secrets (createSecrets) to seal refresh tokens\",\n );\n if (typeof config.store !== \"function\")\n throw new ConnectError(\"config\", \"The connector needs a store function\");\n let stateKeyBytes: Uint8Array<ArrayBuffer>;\n try {\n stateKeyBytes = secretKeyBytes(config.stateSecret);\n } catch (error) {\n throw new ConnectError(\n \"config\",\n `The state secret: ${(error as Error).message}`,\n );\n }\n const callbackPath = checkPath(config.callbackPath, \"callbackPath\");\n const redirectOrigin = config.redirectOrigin\n ? checkOrigin(config.redirectOrigin, \"redirectOrigin\")\n : undefined;\n const sharedDomain =\n config.sharedDomain?.trim().toLowerCase().replace(/^\\./, \"\") || undefined;\n if (sharedDomain) {\n if (\n !HOSTNAME.test(sharedDomain) ||\n PUBLIC_SUFFIXES.has(sharedDomain) ||\n config.isPublicSuffix?.(sharedDomain)\n ) {\n throw new ConnectError(\n \"config\",\n \"sharedDomain must be a registrable domain such as example.com, not a public suffix\",\n );\n }\n if (\n redirectOrigin &&\n !onDomain(new URL(redirectOrigin).hostname, sharedDomain)\n ) {\n throw new ConnectError(\n \"config\",\n \"redirectOrigin must be on sharedDomain\",\n );\n }\n }\n const cookieName =\n config.cookieName ??\n `genesis_connect_${provider.id.replace(/[^A-Za-z0-9_-]/g, \"_\")}`;\n if (!COOKIE_NAME.test(cookieName))\n throw new ConnectError(\"config\", \"cookieName is not a valid cookie name\");\n const cookiePath = checkPath(\n config.cookiePath ?? callbackPath,\n \"cookiePath\",\n ).replace(/[?#].*$/, \"\");\n if (/[;,]/.test(cookiePath)) {\n throw new ConnectError(\"config\", \"cookiePath can't hold ; or ,\");\n }\n // RFC 6265 path-match: the same path, or a parent directory of it.\n const callbackPathOnly = callbackPath.replace(/[?#].*$/, \"\");\n if (\n cookiePath !== callbackPathOnly &&\n !callbackPathOnly.startsWith(\n cookiePath.endsWith(\"/\") ? cookiePath : `${cookiePath}/`,\n )\n )\n throw new ConnectError(\"config\", \"cookiePath must cover callbackPath\");\n const ttl = Math.floor(config.stateTtlSeconds ?? 600);\n if (!(ttl >= 60 && ttl <= 3600))\n throw new ConnectError(\n \"config\",\n \"stateTtlSeconds must be between 60 and 3600\",\n );\n const resultParam = config.resultParam ?? \"connect\";\n const fallbackPath = checkPath(config.fallbackPath ?? \"/\", \"fallbackPath\");\n const aadFor = config.aad ?? ((id: string) => `${provider.id}-grant:${id}`);\n const separator = provider.scopeSeparator ?? \" \";\n const defaultScopes = scopeList(\n config.scopes ?? provider.defaultScopes,\n separator,\n );\n const aliases = provider.scopeAliases ?? {};\n const normalScope = (scope: string) => aliases[scope] ?? scope;\n const asksOpenid = (scopes: readonly string[]) =>\n scopes.some((scope) => normalScope(scope) === \"openid\");\n // Only non-empty issuers count: `\"\"` would pass a length check and then match no token.\n const issuers: readonly string[] = (\n typeof provider.issuer === \"string\"\n ? [provider.issuer]\n : (provider.issuer ?? [])\n ).filter((issuer) => typeof issuer === \"string\" && issuer.trim() !== \"\");\n if (config.requireIdentity) {\n if (issuers.length === 0)\n throw new ConnectError(\n \"config\",\n `requireIdentity needs the ${provider.id} provider's issuer`,\n );\n if (!asksOpenid(defaultScopes))\n throw new ConnectError(\n \"config\",\n \"requireIdentity needs the openid scope in the connector's scopes\",\n );\n }\n const trustedAudiences = [...(config.trustedAudiences ?? [])];\n const now = () => config.now?.() ?? Date.now();\n const log = (event: ConnectLogEvent) => {\n try {\n config.log?.(event);\n } catch {\n // A logger never breaks the flow.\n }\n };\n\n let stateKey: Promise<WebCryptoKey> | undefined;\n const hmacKey = () =>\n (stateKey ??= crypto.subtle\n .importKey(\"raw\", stateKeyBytes, \"HKDF\", false, [\"deriveKey\"])\n .then((base) =>\n crypto.subtle.deriveKey(\n {\n name: \"HKDF\",\n hash: \"SHA-256\",\n salt: new Uint8Array(0),\n info: utf8.encode(`genesis-connect/state/${provider.id}`),\n },\n base,\n { name: \"HMAC\", hash: \"SHA-256\", length: 256 },\n false,\n [\"sign\", \"verify\"],\n ),\n ));\n\n async function signState(state: SignedState): Promise<string> {\n const body = base64url(\n JSON.stringify({\n v: 1,\n s: state.subject,\n i: state.intent,\n r: state.returnTo,\n o: state.origin,\n n: state.nonce,\n e: state.expiresAt,\n c: state.scopes,\n d: state.data,\n }),\n );\n const mac = await crypto.subtle.sign(\n \"HMAC\",\n await hmacKey(),\n utf8.encode(body),\n );\n return `${body}.${base64url(new Uint8Array(mac))}`;\n }\n\n /** The state, or null when it is missing, forged, malformed or expired. */\n async function verifyState(\n signed: string | null,\n ): Promise<SignedState | null> {\n if (!signed || signed.length > 4096) return null;\n const [body, mac, extra] = signed.split(\".\");\n if (!body || !mac || extra !== undefined) return null;\n try {\n if (\n !(await crypto.subtle.verify(\n \"HMAC\",\n await hmacKey(),\n fromBase64(mac),\n utf8.encode(body),\n ))\n )\n return null;\n const raw = JSON.parse(\n new TextDecoder().decode(fromBase64(body)),\n ) as Record<string, unknown>;\n const state: SignedState = {\n subject: String(raw[\"s\"] ?? \"\"),\n intent: String(raw[\"i\"] ?? \"\"),\n returnTo: checkPath(String(raw[\"r\"] ?? \"\"), \"returnTo\"),\n origin: String(raw[\"o\"] ?? \"\"),\n nonce: String(raw[\"n\"] ?? \"\"),\n expiresAt: Number(raw[\"e\"]),\n scopes: Array.isArray(raw[\"c\"])\n ? raw[\"c\"].filter((s): s is string => typeof s === \"string\")\n : [],\n data:\n raw[\"d\"] && typeof raw[\"d\"] === \"object\" && !Array.isArray(raw[\"d\"])\n ? (raw[\"d\"] as StateData)\n : {},\n };\n if (\n raw[\"v\"] !== 1 ||\n !state.subject ||\n !state.nonce ||\n !Number.isFinite(state.expiresAt)\n )\n return null;\n if (state.expiresAt * 1000 <= now()) return null;\n return state;\n } catch {\n return null;\n }\n }\n\n const originOf = (request: Request) => new URL(request.url).origin;\n const redirectUri = (request: Request) =>\n `${redirectOrigin ?? originOf(request)}${callbackPath}`;\n\n /** The cookie's Domain: the shared domain when this request is on it, else none (this host only). */\n const cookieDomain = (request: Request) =>\n sharedDomain && onDomain(new URL(request.url).hostname, sharedDomain)\n ? sharedDomain\n : undefined;\n\n /** Secure everywhere but a local development host. */\n const isLocalRequest = (request: Request) =>\n isLocal(new URL(request.url).hostname);\n\n function cookie(\n request: Request,\n value: string,\n maxAge: number,\n domain: string | null = cookieDomain(request) ?? null,\n ): string {\n const secure = isLocalRequest(request) ? \"\" : \"; Secure\";\n return `${cookieName}=${value}; Path=${cookiePath}; HttpOnly; SameSite=Lax; Max-Age=${maxAge}${secure}${domain ? `; Domain=${domain}` : \"\"}`;\n }\n\n /** Every value of our cookie the browser sent (a host-only and a domain one may both be there). */\n function cookieValues(request: Request): string[] {\n const values: string[] = [];\n for (const part of (request.headers.get(\"cookie\") ?? \"\").split(\";\")) {\n const at = part.indexOf(\"=\");\n if (at > 0 && part.slice(0, at).trim() === cookieName)\n values.push(part.slice(at + 1).trim());\n }\n return values;\n }\n\n /** Where to send the browser back to: where it started, when that is this origin or a safe one on the shared domain. */\n function returnOrigin(request: Request, started: string | undefined): string {\n const here = originOf(request);\n if (!started || started === here) return here;\n if (\n sharedDomain &&\n cookieDomain(request) &&\n isSafeReturnOrigin(started, sharedDomain)\n )\n return started;\n return here;\n }\n\n function back(\n request: Request,\n state: SignedState | null,\n result: ConnectResult,\n extra: Readonly<Record<string, string>> = {},\n detail?: string,\n ): Response {\n const url = new URL(\n state?.returnTo ?? fallbackPath,\n returnOrigin(request, state?.origin),\n );\n for (const [key, value] of Object.entries(extra)) {\n if (key !== resultParam && typeof value === \"string\")\n url.searchParams.set(key, value);\n }\n url.searchParams.set(resultParam, result);\n log({ step: \"callback\", result, ...(detail ? { detail } : {}) });\n const headers = new Headers({\n location: url.toString(),\n \"cache-control\": \"no-store\",\n \"referrer-policy\": \"no-referrer\",\n });\n // Clear both variants: a host-only cookie and a shared-domain one may both be there.\n headers.append(\"set-cookie\", cookie(request, \"\", 0, null));\n const domain = cookieDomain(request);\n if (domain) headers.append(\"set-cookie\", cookie(request, \"\", 0, domain));\n return new Response(null, { status: 303, headers });\n }\n\n function clientAuth(\n body: URLSearchParams,\n headers: Record<string, string>,\n ): void {\n if (provider.tokenAuth === \"client_secret_basic\") {\n const encode = (s: string) => encodeURIComponent(s).replace(/%20/g, \"+\");\n headers[\"authorization\"] =\n `Basic ${btoa(`${encode(config.clientId)}:${encode(config.clientSecret)}`)}`;\n } else {\n body.set(\"client_id\", config.clientId);\n body.set(\"client_secret\", config.clientSecret);\n }\n }\n\n async function tokenRequest(\n params: Record<string, string>,\n signal?: AbortSignal,\n ): Promise<Response> {\n const body = new URLSearchParams(params);\n const headers: Record<string, string> = {\n \"content-type\": \"application/x-www-form-urlencoded\",\n accept: \"application/json\",\n };\n clientAuth(body, headers);\n return timedFetch(\n config,\n provider.tokenUrl,\n { method: \"POST\", headers, body: body.toString() },\n signal,\n );\n }\n\n const granted = (body: Record<string, unknown>, asked: readonly string[]) => {\n const scope =\n typeof body[\"scope\"] === \"string\" ? body[\"scope\"] : asked.join(separator);\n return {\n scope,\n scopes: scope.split(separator).filter(Boolean).map(normalScope),\n };\n };\n\n const expiresIn = (body: Record<string, unknown>) => {\n const value = Number(body[\"expires_in\"]);\n return Number.isFinite(value) && value > 0 ? value : 3600;\n };\n\n async function cacheKey(refreshToken: string) {\n return sha256Hex(`${provider.id}\\n${config.clientId}\\n${refreshToken}`);\n }\n\n async function refreshWith(\n refreshToken: string,\n aad: string,\n signal: AbortSignal | undefined,\n ): Promise<Refreshed> {\n const response = await tokenRequest(\n { grant_type: \"refresh_token\", refresh_token: refreshToken },\n signal,\n );\n if (!response.ok) {\n const body = await jsonBody(response);\n const code = errorCode(body);\n if (response.status === 400 && code === \"invalid_grant\") {\n log({ step: \"refresh\", result: \"revoked\" });\n throw new ConnectError(\n \"revoked\",\n `The ${provider.id} connection was removed. Connect it again.`,\n { status: 400 },\n );\n }\n const detail = `token endpoint answered ${response.status}${code ? ` (${code})` : \"\"}`;\n if (isRetryableStatus(response.status)) {\n log({ step: \"refresh\", result: \"unavailable\", detail });\n throw new ConnectError(\"unavailable\", `The ${provider.id} ${detail}`, {\n status: response.status,\n retryAfterSeconds: retryAfter(response),\n });\n }\n log({ step: \"refresh\", result: \"failed\", detail });\n throw new ConnectError(\"failed\", `The ${provider.id} ${detail}`, {\n status: response.status,\n });\n }\n const body = await jsonBody(response);\n const accessToken = body[\"access_token\"];\n if (typeof accessToken !== \"string\" || !accessToken)\n throw new ConnectError(\n \"unavailable\",\n `The ${provider.id} token endpoint sent no access token`,\n );\n const rotated =\n typeof body[\"refresh_token\"] === \"string\" &&\n body[\"refresh_token\"] &&\n body[\"refresh_token\"] !== refreshToken\n ? body[\"refresh_token\"]\n : undefined;\n const sealedRefreshToken = rotated\n ? await config.secrets.seal(rotated, aad)\n : undefined;\n return {\n accessToken,\n expiresIn: expiresIn(body),\n scope: granted(body, []).scope,\n ...(sealedRefreshToken\n ? { sealedRefreshToken, rotatedPlain: rotated }\n : {}),\n };\n }\n\n /** Hands a rotated refresh token to `onRotate`; never lets it be lost. */\n async function handOver(\n fresh: Refreshed,\n options: TokenCallOptions,\n required: boolean,\n ): Promise<void> {\n const sealedRefreshToken = fresh.sealedRefreshToken;\n if (!sealedRefreshToken) return;\n if (!options.onRotate) {\n if (!required) return;\n log({\n step: \"refresh\",\n result: \"usage\",\n detail: \"rotated without onRotate\",\n });\n throw new ConnectError(\n \"usage\",\n `The ${provider.id} refresh token was rotated and no onRotate was given to store it: store this error's sealedRefreshToken`,\n { sealedRefreshToken },\n );\n }\n try {\n await options.onRotate(sealedRefreshToken);\n } catch {\n log({ step: \"refresh\", result: \"failed\", detail: \"onRotate threw\" });\n throw new ConnectError(\n \"failed\",\n `Storing the rotated ${provider.id} refresh token failed: store this error's sealedRefreshToken`,\n { sealedRefreshToken },\n );\n }\n }\n\n const publicResult = (fresh: Refreshed): RefreshedToken => ({\n accessToken: fresh.accessToken,\n expiresIn: fresh.expiresIn,\n scope: fresh.scope,\n ...(fresh.sealedRefreshToken\n ? { sealedRefreshToken: fresh.sealedRefreshToken }\n : {}),\n });\n\n async function cacheToken(\n refreshToken: string,\n fresh: Refreshed,\n at: number,\n ) {\n const entry = {\n token: fresh.accessToken,\n expiresAt: at + fresh.expiresIn * 1000,\n };\n for (const plain of [refreshToken, fresh.rotatedPlain]) {\n if (!plain) continue;\n if (tokenCache.size >= TOKEN_CACHE_LIMIT)\n tokenCache.delete(tokenCache.keys().next().value!);\n tokenCache.set(await cacheKey(plain), entry);\n }\n }\n\n /** RFC 7009 revocation of one token; true when it is gone. Never throws. */\n async function revokeToken(\n token: string,\n hint: \"refresh_token\" | \"access_token\",\n signal?: AbortSignal,\n ): Promise<boolean> {\n if (!provider.revokeUrl) return false;\n const body = new URLSearchParams({ token, token_type_hint: hint });\n const headers: Record<string, string> = {\n \"content-type\": \"application/x-www-form-urlencoded\",\n accept: \"application/json\",\n };\n if (provider.revokeWithClientAuth) clientAuth(body, headers);\n try {\n const response = await timedFetch(\n config,\n provider.revokeUrl,\n { method: \"POST\", headers, body: body.toString() },\n signal,\n );\n // RFC 7009: 200 for a revoked token and for one that was already invalid; Google says invalid_token.\n let gone = response.ok;\n if (!gone && response.status === 400) {\n gone = errorCode(await jsonBody(response)) === \"invalid_token\";\n } else {\n discard(response);\n }\n log({\n step: \"revoke\",\n result: gone ? \"revoked\" : \"failed\",\n ...(gone\n ? {}\n : { detail: `revoke endpoint answered ${response.status}` }),\n });\n return gone;\n } catch {\n log({ step: \"revoke\", result: \"unavailable\" });\n return false;\n }\n }\n\n /**\n * Revokes a grant the callback turned down after the code was traded, so it isn't left\n * standing at the provider with no row to end it from. Best effort and fired without waiting,\n * so a slow provider never holds up the person's redirect: bounded by `ABANDON_TIMEOUT_MS`,\n * and every failure (a `log` that throws included) is swallowed, never an unhandled rejection.\n */\n function abandonGrant(refreshToken: unknown, accessToken: string): void {\n if (config.revokeFailedGrants !== true) return;\n const [token, hint] =\n typeof refreshToken === \"string\" && refreshToken\n ? ([refreshToken, \"refresh_token\"] as const)\n : ([accessToken, \"access_token\"] as const);\n revokeToken(token, hint, AbortSignal.timeout(ABANDON_TIMEOUT_MS)).catch(\n () => {},\n );\n }\n\n return {\n provider,\n redirectUri,\n\n async start(request, options) {\n const subject =\n typeof options.subject === \"string\" ? options.subject.trim() : \"\";\n if (!subject || subject.length > 256)\n throw new ConnectError(\n \"usage\",\n \"start needs the signed-in subject (at most 256 characters)\",\n );\n const intent = options.intent ?? \"\";\n if (typeof intent !== \"string\" || intent.length > 64)\n throw new ConnectError(\"usage\", \"intent must be at most 64 characters\");\n const returnTo = checkPath(options.returnTo, \"returnTo\");\n // The cookie is always Secure off localhost, so http elsewhere could never finish.\n if (\n new URL(request.url).protocol !== \"https:\" &&\n !isLocalRequest(request)\n ) {\n throw new ConnectError(\n \"config\",\n \"start needs https (http only on localhost); behind a TLS-ending proxy, pass a Request with the public https URL\",\n );\n }\n // The PKCE cookie must reach the pinned callback: this origin is it, or shares its domain.\n if (\n redirectOrigin &&\n originOf(request) !== redirectOrigin &&\n !cookieDomain(request)\n ) {\n throw new ConnectError(\n \"config\",\n \"This origin can't finish on the pinned redirectOrigin: it is not on sharedDomain\",\n );\n }\n const scopes = options.scopes\n ? scopeList(options.scopes, separator)\n : defaultScopes;\n if (scopes.length === 0)\n throw new ConnectError(\n \"usage\",\n \"No scopes to ask for: give the connector or start some\",\n );\n if (config.requireIdentity && !asksOpenid(scopes))\n throw new ConnectError(\n \"usage\",\n \"requireIdentity needs the openid scope: start's scopes leave it out\",\n );\n const nonce = randomToken(16);\n const verifier = randomToken(32);\n const state = await signState({\n subject,\n intent,\n returnTo,\n origin: originOf(request),\n nonce,\n expiresAt: Math.floor(now() / 1000) + ttl,\n scopes,\n data: options.data ?? {},\n });\n if (state.length > 2048)\n throw new ConnectError(\n \"usage\",\n \"The state is too large: keep data small\",\n );\n const challenge = base64url(\n new Uint8Array(\n await crypto.subtle.digest(\"SHA-256\", utf8.encode(verifier)),\n ),\n );\n const url = new URL(provider.authorizeUrl);\n for (const [key, value] of Object.entries(provider.authorizeParams ?? {}))\n url.searchParams.set(key, value);\n for (const [key, value] of Object.entries(options.params ?? {})) {\n if (RESERVED_PARAMS.has(key))\n throw new ConnectError(\n \"usage\",\n `start can't set the ${key} parameter`,\n );\n url.searchParams.set(key, value);\n }\n url.searchParams.set(\"client_id\", config.clientId);\n url.searchParams.set(\"redirect_uri\", redirectUri(request));\n url.searchParams.set(\"response_type\", \"code\");\n url.searchParams.set(\"scope\", scopes.join(separator));\n url.searchParams.set(\"state\", state);\n url.searchParams.set(\"code_challenge\", challenge);\n url.searchParams.set(\"code_challenge_method\", \"S256\");\n const href = url.toString();\n const setCookie = cookie(request, `${nonce}.${verifier}`, ttl);\n const headers = { \"set-cookie\": setCookie, \"cache-control\": \"no-store\" };\n return {\n url: href,\n setCookie,\n redirect: () =>\n new Response(null, {\n status: 303,\n headers: { ...headers, location: href },\n }),\n json: () => Response.json({ url: href }, { headers }),\n };\n },\n\n async callback(request) {\n const query = new URL(request.url).searchParams;\n const state = await verifyState(query.get(\"state\"));\n if (!state) return back(request, null, \"expired\");\n // Another browser, or the cookie expired: never finish someone else's sign-in.\n const verifier = cookieValues(request)\n .map((value) => value.split(\".\"))\n .find(\n ([nonce, v, extra]) =>\n nonce === state.nonce && v && extra === undefined,\n )?.[1];\n if (!verifier) return back(request, state, \"browser\");\n if (query.get(\"error\"))\n return back(\n request,\n state,\n query.get(\"error\") === \"access_denied\" ? \"denied\" : \"failed\",\n );\n const code = query.get(\"code\");\n if (!code) return back(request, state, \"denied\");\n\n let body: Record<string, unknown>;\n try {\n const response = await tokenRequest({\n grant_type: \"authorization_code\",\n code,\n code_verifier: verifier,\n redirect_uri: redirectUri(request),\n });\n body = await jsonBody(response);\n if (!response.ok) {\n const code = errorCode(body);\n return back(\n request,\n state,\n \"failed\",\n {},\n `token endpoint answered ${response.status}${code ? ` (${code})` : \"\"}`,\n );\n }\n } catch (error) {\n return back(\n request,\n state,\n \"failed\",\n {},\n error instanceof ConnectError\n ? error.message\n : \"token request failed\",\n );\n }\n const accessToken = body[\"access_token\"];\n const refreshToken = body[\"refresh_token\"];\n if (typeof accessToken !== \"string\" || !accessToken) {\n return back(request, state, \"failed\", {}, \"no access token\");\n }\n const { scope, scopes } = granted(body, state.scopes);\n // The person can untick scopes on the consent screen; the app needs every one it asked for.\n if (!state.scopes.every((s) => scopes.includes(normalScope(s)))) {\n abandonGrant(refreshToken, accessToken);\n return back(request, state, \"scope\");\n }\n if (\n (config.requireRefreshToken ?? true) &&\n (typeof refreshToken !== \"string\" || !refreshToken)\n ) {\n return back(\n request,\n state,\n \"failed\",\n {},\n \"no refresh token (is offline access asked for?)\",\n );\n }\n\n const idToken =\n typeof body[\"id_token\"] === \"string\" ? body[\"id_token\"] : undefined;\n // Only read (and only log about) an identity this attempt asked for.\n const wantsIdentity =\n config.requireIdentity === true || asksOpenid(state.scopes);\n const read = !wantsIdentity\n ? undefined\n : idToken\n ? identityFromTokenEndpoint(idToken, {\n issuers,\n clientId: config.clientId,\n trustedAudiences,\n now: now(),\n })\n : { reason: \"no ID token\" };\n const identity = read && \"subject\" in read ? read : undefined;\n const identityNote =\n read && \"reason\" in read ? `no identity: ${read.reason}` : undefined;\n if (!identity && config.requireIdentity) {\n abandonGrant(refreshToken, accessToken);\n return back(request, state, \"failed\", {}, identityNote);\n }\n\n let extra: StoreResult;\n try {\n const id = config.randomId?.() ?? crypto.randomUUID();\n const aad = aadFor(id);\n const sealedRefreshToken =\n typeof refreshToken === \"string\" && refreshToken\n ? await config.secrets.seal(refreshToken, aad)\n : \"\";\n extra = await config.store(\n {\n id,\n aad,\n sealedRefreshToken,\n accessToken,\n expiresIn: expiresIn(body),\n scope,\n scopes,\n ...(idToken ? { idToken } : {}),\n ...(identity ? { identity } : {}),\n subject: state.subject,\n intent: state.intent,\n returnTo: state.returnTo,\n data: state.data as D,\n },\n { request },\n );\n } catch {\n // Not revoked, on purpose: `store` may have half-written the row before it threw, and\n // revoking here would leave that row holding a dead token. The grant is left to the app.\n return back(request, state, \"failed\", {}, \"store threw\");\n }\n return back(request, state, \"connected\", extra ?? {}, identityNote);\n },\n\n async refresh(sealedRefreshToken, aad, options = {}) {\n const refreshToken = await config.secrets.open(sealedRefreshToken, aad);\n const fresh = await refreshWith(refreshToken, aad, options.signal);\n await handOver(fresh, options, false);\n return publicResult(fresh);\n },\n\n async accessToken(sealedRefreshToken, aad, options = {}) {\n const refreshToken = await config.secrets.open(sealedRefreshToken, aad);\n const key = await cacheKey(refreshToken);\n const hit = tokenCache.get(key);\n if (hit && hit.expiresAt - 60_000 > now()) return hit.token;\n let flight = inFlight.get(key);\n if (!flight) {\n const at = now();\n flight = refreshWith(refreshToken, aad, options.signal)\n .then(async (fresh) => {\n // Cached before onRotate runs, so a failing store never costs a refresh.\n await cacheToken(refreshToken, fresh, at);\n return fresh;\n })\n .finally(() => inFlight.delete(key));\n inFlight.set(key, flight);\n }\n const fresh = await flight;\n await handOver(fresh, options, true);\n return fresh.accessToken;\n },\n\n async revoke(sealedRefreshToken, aad, options = {}) {\n const refreshToken = await config.secrets.open(sealedRefreshToken, aad);\n tokenCache.delete(await cacheKey(refreshToken));\n return revokeToken(refreshToken, \"refresh_token\", options.signal);\n },\n };\n}\n"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -13,6 +13,11 @@ interface OAuthProvider {
|
|
|
13
13
|
readonly authorizeUrl: string;
|
|
14
14
|
/** The token endpoint (code and refresh-token grants). */
|
|
15
15
|
readonly tokenUrl: string;
|
|
16
|
+
/**
|
|
17
|
+
* The OpenID Connect issuer(s) its ID tokens name in `iss`. Without it, no `identity` is read
|
|
18
|
+
* from an ID token.
|
|
19
|
+
*/
|
|
20
|
+
readonly issuer?: string | readonly string[] | undefined;
|
|
16
21
|
/** The revocation endpoint (RFC 7009), when the provider has one. */
|
|
17
22
|
readonly revokeUrl?: string | undefined;
|
|
18
23
|
/** Scopes asked for when neither the connector nor `start` names any. */
|
|
@@ -53,8 +58,14 @@ declare const GOOGLE_SCOPES: {
|
|
|
53
58
|
readonly driveFile: "https://www.googleapis.com/auth/drive.file";
|
|
54
59
|
/** Every spreadsheet the person can reach. Prefer `driveFile`. */
|
|
55
60
|
readonly spreadsheets: "https://www.googleapis.com/auth/spreadsheets";
|
|
56
|
-
/**
|
|
61
|
+
/** Read and change events on the person's calendars. Prefer `calendarEventsReadonly` to only read. */
|
|
57
62
|
readonly calendarEvents: "https://www.googleapis.com/auth/calendar.events";
|
|
63
|
+
/** Read events on every calendar the person can see, and nothing else. */
|
|
64
|
+
readonly calendarEventsReadonly: "https://www.googleapis.com/auth/calendar.events.readonly";
|
|
65
|
+
/** The list of calendars the person has (names, colors, which are shown), not their events. */
|
|
66
|
+
readonly calendarListReadonly: "https://www.googleapis.com/auth/calendar.calendarlist.readonly";
|
|
67
|
+
/** Read everything on the person's calendars (calendars, events, settings). Prefer the two above. */
|
|
68
|
+
readonly calendarReadonly: "https://www.googleapis.com/auth/calendar.readonly";
|
|
58
69
|
/** Send mail as the person. */
|
|
59
70
|
readonly gmailSend: "https://www.googleapis.com/auth/gmail.send";
|
|
60
71
|
};
|
|
@@ -162,14 +173,35 @@ interface ConnectedAccount<D extends StateData = StateData> {
|
|
|
162
173
|
scope: string;
|
|
163
174
|
/** The granted scopes. */
|
|
164
175
|
scopes: string[];
|
|
165
|
-
/**
|
|
176
|
+
/**
|
|
177
|
+
* The OpenID Connect ID token, when the provider sent one (an `openid` scope), as it came.
|
|
178
|
+
* Its claims are NOT checked here: read who the account is from `identity`, which is.
|
|
179
|
+
*/
|
|
166
180
|
idToken?: string | undefined;
|
|
181
|
+
/**
|
|
182
|
+
* Who the account is, read from the ID token: to show "connected as …" and to tell two
|
|
183
|
+
* accounts of one person apart. Only with an `openid` scope (add `email` for the address), a
|
|
184
|
+
* provider with an `issuer` and an https token endpoint; left out when the token's issuer,
|
|
185
|
+
* audience, authorized party or expiry don't check out (the connection itself still succeeds).
|
|
186
|
+
*/
|
|
187
|
+
identity?: AccountIdentity | undefined;
|
|
167
188
|
/** From `start`. */
|
|
168
189
|
subject: string;
|
|
169
190
|
intent: string;
|
|
170
191
|
returnTo: string;
|
|
171
192
|
data: D;
|
|
172
193
|
}
|
|
194
|
+
/** The account an ID token names. `subject` is the provider's stable id for it; the rest may change. */
|
|
195
|
+
interface AccountIdentity {
|
|
196
|
+
/** The provider's id for the account (`sub`): stable, unique per provider. */
|
|
197
|
+
subject: string;
|
|
198
|
+
/** Only when the provider says it's verified. Never key an account on it: use `subject`. */
|
|
199
|
+
email?: string | undefined;
|
|
200
|
+
emailVerified?: boolean | undefined;
|
|
201
|
+
/** As the person set them: untrusted text and an untrusted URL. Escape or check before use. */
|
|
202
|
+
name?: string | undefined;
|
|
203
|
+
picture?: string | undefined;
|
|
204
|
+
}
|
|
173
205
|
/** What `store` may add to the way back, as query parameters (a grant id to finish with). */
|
|
174
206
|
type StoreResult = void | undefined | Readonly<Record<string, string>>;
|
|
175
207
|
/** A safe log line: never a token, a code, a secret or a provider's error text. */
|
|
@@ -225,6 +257,25 @@ interface ConnectorConfig<D extends StateData = StateData> extends HttpOptions {
|
|
|
225
257
|
aad?: ((id: string) => string) | undefined;
|
|
226
258
|
/** Fail with `failed` when the provider sends no refresh token. Default true. */
|
|
227
259
|
requireRefreshToken?: boolean | undefined;
|
|
260
|
+
/**
|
|
261
|
+
* Fail with `failed` when no `identity` can be read (no ID token, or one whose issuer,
|
|
262
|
+
* audience or expiry don't check out). Default false: the connection succeeds without one,
|
|
263
|
+
* and the reason is logged.
|
|
264
|
+
*/
|
|
265
|
+
requireIdentity?: boolean | undefined;
|
|
266
|
+
/**
|
|
267
|
+
* Audiences other than `clientId` an ID token's `aud` may also name (another client of the
|
|
268
|
+
* same app). Default none: a token naming any other audience gives no `identity`.
|
|
269
|
+
*/
|
|
270
|
+
trustedAudiences?: readonly string[] | undefined;
|
|
271
|
+
/**
|
|
272
|
+
* Revoke the new grant (best effort, through the provider's `revokeUrl`) when the callback
|
|
273
|
+
* turns it down after the code was traded: a missing scope or, with `requireIdentity`, no
|
|
274
|
+
* identity. Default false: Google ends every token of the person's grant across the project
|
|
275
|
+
* on a revoke, so a returning person who unticks a scope would also lose the connection they
|
|
276
|
+
* already have. Set it true only when each grant is isolated (one connection per client).
|
|
277
|
+
*/
|
|
278
|
+
revokeFailedGrants?: boolean | undefined;
|
|
228
279
|
/**
|
|
229
280
|
* Keeps the new account: the app's row, under `account.id`, for
|
|
230
281
|
* `account.subject` (the user who started). Use that, never a session read
|
|
@@ -304,4 +355,4 @@ declare function clearAccessTokenCache(): void;
|
|
|
304
355
|
/** Sets up a connector for one provider. Checks its config now, so a mistake shows at boot. */
|
|
305
356
|
declare function createConnector<D extends StateData = StateData>(config: ConnectorConfig<D>): Connector<D>;
|
|
306
357
|
|
|
307
|
-
export { CONNECT_RESULTS, ConnectError, type ConnectErrorCode, type ConnectLogEvent, type ConnectResult, type ConnectedAccount, type Connector, type ConnectorConfig, GOOGLE_SCOPES, type HttpOptions, type OAuthProvider, type RefreshedToken, Secrets, type StartOptions, type StartResult, type StateData, type StoreResult, type TokenCallOptions, clearAccessTokenCache, createConnector, googleProvider, isConnectError, isSafeReturnOrigin };
|
|
358
|
+
export { type AccountIdentity, CONNECT_RESULTS, ConnectError, type ConnectErrorCode, type ConnectLogEvent, type ConnectResult, type ConnectedAccount, type Connector, type ConnectorConfig, GOOGLE_SCOPES, type HttpOptions, type OAuthProvider, type RefreshedToken, Secrets, type StartOptions, type StartResult, type StateData, type StoreResult, type TokenCallOptions, clearAccessTokenCache, createConnector, googleProvider, isConnectError, isSafeReturnOrigin };
|