@synap-core/sdk 1.2.0 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/README.md +80 -0
  2. package/dist/chunk-J7XHAAFJ.js +79 -0
  3. package/dist/chunk-J7XHAAFJ.js.map +1 -0
  4. package/dist/{client-C5sF20f1.d.cts → client-CVUT1Mcf.d.cts} +1 -1
  5. package/dist/{client-C5sF20f1.d.ts → client-CVUT1Mcf.d.ts} +1 -1
  6. package/dist/connect.browser.cjs +22 -0
  7. package/dist/connect.browser.cjs.map +1 -0
  8. package/dist/connect.browser.d.cts +2 -0
  9. package/dist/connect.browser.d.ts +2 -0
  10. package/dist/connect.browser.js +5 -0
  11. package/dist/connect.browser.js.map +1 -0
  12. package/dist/connect.cjs +1206 -0
  13. package/dist/connect.cjs.map +1 -0
  14. package/dist/connect.d.cts +442 -0
  15. package/dist/connect.d.ts +442 -0
  16. package/dist/connect.js +1092 -0
  17. package/dist/connect.js.map +1 -0
  18. package/dist/public.d.cts +2 -2
  19. package/dist/public.d.ts +2 -2
  20. package/dist/testing.cjs +304 -19
  21. package/dist/testing.cjs.map +1 -1
  22. package/dist/testing.d.cts +73 -2
  23. package/dist/testing.d.ts +73 -2
  24. package/dist/testing.js +281 -19
  25. package/dist/testing.js.map +1 -1
  26. package/dist/types-DpqPNaaa.d.cts +76 -0
  27. package/dist/types-DpqPNaaa.d.ts +76 -0
  28. package/package.json +11 -3
  29. package/src/connect/auth.ts +405 -0
  30. package/src/connect/browser.ts +10 -0
  31. package/src/connect/client.ts +193 -0
  32. package/src/connect/discovery.ts +262 -0
  33. package/src/connect/encoding.ts +93 -0
  34. package/src/connect/errors.ts +115 -0
  35. package/src/connect/flow.ts +449 -0
  36. package/src/connect/guard.ts +25 -0
  37. package/src/connect/index.ts +59 -0
  38. package/src/connect/seal.ts +119 -0
  39. package/src/connect/store.ts +151 -0
  40. package/src/connect/types.ts +80 -0
  41. package/src/testing/auth-server.ts +380 -0
  42. package/src/testing/index.ts +5 -0
@@ -0,0 +1,442 @@
1
+ import { b as PodConnection, P as PodGrant, C as ClientMetadataDocument, A as AppRequest, c as PodIssuer } from './types-DpqPNaaa.cjs';
2
+ export { a as PodUser } from './types-DpqPNaaa.cjs';
3
+
4
+ /**
5
+ * The one error type of `@synap-core/sdk/connect`. `code` is the contract a UI
6
+ * branches on; `message` and `detail` are for the developer's logs, never for
7
+ * the person (a sign-in failure page says one sentence per code, never why).
8
+ */
9
+ type ConnectErrorCode =
10
+ /** The typed pod address is not a usable URL. */
11
+ "invalid_pod_address"
12
+ /** Something answered, but it is not a Synap pod (no AS metadata). */
13
+ | "not_a_pod"
14
+ /** The pod did not answer (network, timeout, 5xx). */
15
+ | "pod_unreachable"
16
+ /** A Synap pod, but too old for app sign-in (no CIMD, no `iss`, no S256). */
17
+ | "pod_unsupported"
18
+ /** Connecting through Synap Cloud is not available yet. */
19
+ | "cp_unavailable"
20
+ /** The person said no on the consent screen. */
21
+ | "denied"
22
+ /** No sign-in in progress here, or it is older than 10 minutes. */
23
+ | "expired_transaction"
24
+ /** The answer does not belong to the sign-in this browser started (`state` or `iss`). */
25
+ | "mismatch"
26
+ /** The app's own setup is wrong (client metadata, scopes, secret, redirect). */
27
+ | "misconfigured"
28
+ /** The pod asked to slow down; see `retryAfterSec`. */
29
+ | "rate_limited"
30
+ /** The connection lapsed (refresh token idle past its life, or none was issued). */
31
+ | "expired"
32
+ /** The pod refused the connection's refresh token while it should still live. */
33
+ | "revoked"
34
+ /** The pod answered something the SDK cannot use. */
35
+ | "server_error";
36
+ declare class ConnectError extends Error {
37
+ readonly code: ConnectErrorCode;
38
+ /** Seconds to wait, from `Retry-After`, when `code` is `rate_limited`. */
39
+ readonly retryAfterSec?: number;
40
+ /** The OAuth error code the pod sent, when there was one (logs only). */
41
+ readonly oauthError?: string;
42
+ constructor(code: ConnectErrorCode, message: string, extra?: {
43
+ retryAfterSec?: number;
44
+ oauthError?: string;
45
+ });
46
+ }
47
+ declare function isConnectError(err: unknown): err is ConnectError;
48
+ declare const CONNECT_ERROR_CODES: readonly ConnectErrorCode[];
49
+ /** Parse an untrusted `?error=` value back into a code (null when unknown). */
50
+ declare function parseConnectErrorCode(value: string | null | undefined): ConnectErrorCode | null;
51
+
52
+ type PodErrorCode = "expired" | "unauthorized" | "forbidden" | "door_unsupported" | "not_found" | "invalid" | "conflict" | "rate_limited" | "pod_unreachable" | "server";
53
+ interface PodError {
54
+ code: PodErrorCode;
55
+ /** The HTTP status, or null when nothing was answered. */
56
+ status: number | null;
57
+ retryAfterSec?: number;
58
+ /** The pod's own error text, for logs only. */
59
+ detail?: string;
60
+ }
61
+ type PodResult<T> = {
62
+ ok: true;
63
+ data: T;
64
+ } | {
65
+ ok: false;
66
+ error: PodError;
67
+ };
68
+ type PodWriteResult<T> = {
69
+ ok: true;
70
+ status: "applied";
71
+ data: T;
72
+ } | {
73
+ ok: true;
74
+ status: "proposed";
75
+ proposalId: string | null;
76
+ reviewUrl: string;
77
+ data: T;
78
+ } | {
79
+ ok: false;
80
+ error: PodError;
81
+ };
82
+ type QueryValue = string | number | boolean | null | undefined | ReadonlyArray<string | number | boolean>;
83
+ interface PodClientOptions {
84
+ fetch?: typeof fetch;
85
+ now?: () => number;
86
+ }
87
+ interface PodClient {
88
+ podUrl: string;
89
+ user: PodConnection["user"];
90
+ grant: PodGrant[];
91
+ /** Whether the connection was granted `permission` (in `workspaceId` when given). */
92
+ can(permission: string, workspaceId?: string): boolean;
93
+ get<T = unknown>(path: string, query?: Record<string, QueryValue>): Promise<PodResult<T>>;
94
+ post<T = unknown>(path: string, body?: unknown): Promise<PodWriteResult<T>>;
95
+ patch<T = unknown>(path: string, body?: unknown): Promise<PodWriteResult<T>>;
96
+ put<T = unknown>(path: string, body?: unknown): Promise<PodWriteResult<T>>;
97
+ delete<T = unknown>(path: string, body?: unknown): Promise<PodWriteResult<T>>;
98
+ }
99
+ /** Whether `connection` was granted `permission` (in `workspaceId` when given). Exact match: grants carry no wildcard. */
100
+ declare function can(connection: Pick<PodConnection, "grant">, permission: string, workspaceId?: string): boolean;
101
+ /**
102
+ * Where to review a proposal: the pod's answer, resolved against the pod and
103
+ * kept only on the pod's own origin; else the pod's deep link `/open/<id>`.
104
+ */
105
+ declare function reviewUrlFor(podUrl: string, reviewUrl: unknown, proposalId: string | null): string;
106
+ declare function createPodClient(connection: PodConnection, opts?: PodClientOptions): PodClient;
107
+
108
+ type SealPurpose = "synap-connect/transaction" | "synap-connect/session";
109
+ /** The app secret: one value, or several to rotate (the first seals, all open). */
110
+ type AppSecret = string | readonly string[];
111
+ declare const MIN_SECRET_LENGTH = 32;
112
+ declare function seal(value: unknown, opts: {
113
+ secret: AppSecret;
114
+ purpose: SealPurpose;
115
+ expiresAt: number;
116
+ }): Promise<string>;
117
+ type Opened<T> = {
118
+ ok: true;
119
+ value: T;
120
+ expiresAt: number;
121
+ } | {
122
+ ok: false;
123
+ reason: "invalid" | "expired";
124
+ };
125
+ /**
126
+ * `invalid` = not ours (malformed, tampered, other secret, other purpose);
127
+ * `expired` = ours, but past its `exp`.
128
+ */
129
+ declare function unseal<T = unknown>(sealed: string, opts: {
130
+ secret: AppSecret;
131
+ purpose: SealPurpose;
132
+ now: number;
133
+ }): Promise<Opened<T>>;
134
+
135
+ interface ConnectionStore {
136
+ /** The request's connection, or null (absent, tampered, foreign, expired). */
137
+ read(request: Request): Promise<PodConnection | null>;
138
+ /** Persist; returns the Set-Cookie header values to send. */
139
+ write(connection: PodConnection, request: Request): Promise<string[]>;
140
+ /** Forget; returns the Set-Cookie header values to send. */
141
+ clear(request: Request): Promise<string[]>;
142
+ }
143
+ interface CookieNames {
144
+ session: string;
145
+ transaction: string;
146
+ }
147
+ /** `__Host-` names on https (Secure, Path=/, no Domain: bound to this exact host). */
148
+ declare function cookieNames(secure: boolean): CookieNames;
149
+ /** When the stored value stops being useful. */
150
+ declare function connectionExpiresAt(c: PodConnection): number;
151
+ interface SealedCookieStoreOptions {
152
+ secret: AppSecret;
153
+ /** True on https (the default when the app URL is https). */
154
+ secure: boolean;
155
+ name?: string;
156
+ now?: () => number;
157
+ }
158
+ declare function createSealedCookieStore(opts: SealedCookieStoreOptions): ConnectionStore;
159
+ /**
160
+ * Apply Set-Cookie values to a request's Cookie header, so code that runs
161
+ * later IN THE SAME REQUEST (a server component after `proxy.ts`) reads the
162
+ * refreshed value. A `Max-Age=0` removes the cookie.
163
+ */
164
+ declare function applySetCookies(cookieHeader: string | null, setCookies: readonly string[]): string;
165
+
166
+ interface PodAppPaths {
167
+ /** The client metadata document; its absolute URL is the client_id. */
168
+ metadata: string;
169
+ connect: string;
170
+ callback: string;
171
+ disconnect: string;
172
+ /** The app's connect page (also the CIMD `initiate_login_uri`). */
173
+ connectPage: string;
174
+ /** Where a failed callback lands, with `?error=<code>`. */
175
+ resultPage: string;
176
+ /** Where a sign-in lands when it named no `next`. */
177
+ afterConnect: string;
178
+ }
179
+ declare const DEFAULT_PATHS: PodAppPaths;
180
+ /** Refresh only inside the last 5 minutes of the access token's life. */
181
+ declare const REFRESH_WINDOW_MS: number;
182
+ interface PodAppAuthConfig {
183
+ /** The app's origin, e.g. https://studio.example.com (http only on localhost). */
184
+ appUrl: string;
185
+ /** Shown on the pod's consent screen (at most 100 characters). */
186
+ clientName: string;
187
+ /** What the app asks for, each with the reason the consent screen shows. */
188
+ requests: readonly AppRequest[];
189
+ /** Workspace-template slugs the consent may offer to create (`synap_templates`). */
190
+ templates?: readonly string[];
191
+ /** SERVER-ONLY secret, at least 32 characters (SYNAP_APP_SESSION_SECRET). Several to rotate. */
192
+ secret: AppSecret;
193
+ paths?: Partial<PodAppPaths>;
194
+ /** Defaults to the sealed session cookie. */
195
+ store?: ConnectionStore;
196
+ /** RFC 8707 resource asked for (default the Hub API). */
197
+ resource?: "api/hub" | "mcp" | null;
198
+ /** Accept pods on http://localhost (development against a local pod). */
199
+ allowInsecureLoopback?: boolean;
200
+ /** Developer log of every refused sign-in; messages never carry a token. Default: console.warn. */
201
+ onError?: (error: ConnectError, where: "connect" | "callback" | "disconnect" | "refresh") => void;
202
+ fetch?: typeof fetch;
203
+ now?: () => number;
204
+ }
205
+ type RefreshStatus =
206
+ /** No connection in this request. */
207
+ "none"
208
+ /** Prefetch or static asset: nothing refreshed (the connection is read as is). */
209
+ | "skipped"
210
+ /** Valid beyond the refresh window, or no refresh token to use. */
211
+ | "fresh"
212
+ /** Rotated: send `setCookies`, forward `cookieHeader`. */
213
+ | "refreshed"
214
+ /** The refresh failed (a lost rotation race, the pod busy) but the access token still works: keep it, set nothing. */
215
+ | "kept"
216
+ /** The access token is spent and cannot be renewed: connect again. */
217
+ | "expired"
218
+ /** The pod refused the refresh token (access removed): connect again. */
219
+ | "revoked"
220
+ /** The pod could not be reached and the access token is spent: try later. */
221
+ | "unavailable";
222
+ interface RefreshOutcome {
223
+ status: RefreshStatus;
224
+ /** A usable connection, or null. */
225
+ connection: PodConnection | null;
226
+ /** Set-Cookie values to append to the response. */
227
+ setCookies: string[];
228
+ /** The request's Cookie header with the refreshed value, to forward to the render (null = unchanged). */
229
+ cookieHeader: string | null;
230
+ error?: ConnectError;
231
+ }
232
+ interface PodAppAuth {
233
+ clientId: string;
234
+ redirectUri: string;
235
+ metadata: ClientMetadataDocument;
236
+ paths: PodAppPaths;
237
+ handlers: {
238
+ clientMetadata(request: Request): Promise<Response>;
239
+ connect(request: Request): Promise<Response>;
240
+ callback(request: Request): Promise<Response>;
241
+ disconnect(request: Request): Promise<Response>;
242
+ };
243
+ /**
244
+ * The stored connection (no refresh, no expiry check), or null. Takes the
245
+ * request, or just its Cookie header (a server component has no Request:
246
+ * pass `(await headers()).get("cookie")`).
247
+ */
248
+ readConnection(source: Request | string | null): Promise<PodConnection | null>;
249
+ /** For `proxy.ts` / a route handler: rotate near expiry, lost-race safe. */
250
+ refreshIfNeeded(request: Request): Promise<RefreshOutcome>;
251
+ /** A Hub REST client for the connection. */
252
+ client(connection: PodConnection, opts?: PodClientOptions): PodClient;
253
+ }
254
+ /** The CIMD document (spec 17 §6.1), validated the way the pod will validate it. */
255
+ declare function buildClientMetadata(opts: {
256
+ appUrl: string;
257
+ clientName: string;
258
+ requests: readonly AppRequest[];
259
+ templates?: readonly string[];
260
+ paths?: Partial<PodAppPaths>;
261
+ }): ClientMetadataDocument;
262
+ declare function createPodAppAuth(config: PodAppAuthConfig): PodAppAuth;
263
+
264
+ interface DiscoverOptions {
265
+ fetch?: typeof fetch;
266
+ /** Allow `http://localhost` (and 127.0.0.1, [::1]) pods: development only. */
267
+ allowInsecureLoopback?: boolean;
268
+ now?: () => number;
269
+ }
270
+ /**
271
+ * The canonical issuer for a typed address, or a ConnectError. Adds `https://`
272
+ * when no scheme is given; drops a trailing slash.
273
+ */
274
+ declare function canonicalPodUrl(input: string, opts?: {
275
+ allowInsecureLoopback?: boolean;
276
+ }): string;
277
+ /**
278
+ * A typed pod address (or a launch `iss`) → its validated authorization server.
279
+ * Tries the address, then `pod.<host>` (so "antoine.synap.live" finds
280
+ * "pod.antoine.synap.live").
281
+ */
282
+ declare function discoverPod(input: string, opts?: DiscoverOptions): Promise<PodIssuer>;
283
+ /** Whether "Continue with Synap Cloud" can be offered. False until the CP pod chooser (C1) ships. */
284
+ declare function cloudConnectAvailable(): boolean;
285
+ /** The Synap Cloud pod chooser (C1). Not available yet: always throws `cp_unavailable`. */
286
+ declare function startCloudConnect(_opts: {
287
+ clientId: string;
288
+ state?: string;
289
+ }): never;
290
+ /**
291
+ * A same-origin path to return to after connecting, or null. Accepts a
292
+ * relative path (never `//host` or `/\host`) or an absolute URL on the app's
293
+ * own origin; anything else is dropped (no open redirect).
294
+ */
295
+ declare function safeNextPath(value: string | null | undefined, appOrigin: string): string | null;
296
+ interface LaunchHint {
297
+ /** The pod issuer the launcher named (`iss`), to pre-fill and CONFIRM (T6), never to auto-connect. */
298
+ pod: string | null;
299
+ /** Where to land after connecting (`target_link_uri`), same origin only. */
300
+ next: string | null;
301
+ /** A space to preselect on consent (`synap_space_hint`). */
302
+ spaceHint: string | null;
303
+ }
304
+ /**
305
+ * Read a third-party-initiated login (OIDC Core §4, spec 17 §7.2): the browser
306
+ * opens the app's `initiate_login_uri?iss=…&target_link_uri=…`. One parser for
307
+ * the browser launch and, later, the Synap Cloud chooser's return.
308
+ */
309
+ declare function acceptLaunch(url: string | URL, appOrigin: string, opts?: {
310
+ allowInsecureLoopback?: boolean;
311
+ }): LaunchHint;
312
+
313
+ /** How long a started sign-in stays valid. */
314
+ declare const TRANSACTION_TTL_MS: number;
315
+ /** The refresh token's idle life when the pod does not say (spec 17 F5: 30 days). */
316
+ declare const DEFAULT_REFRESH_IDLE_MS: number;
317
+ /** A sealed `ConnectTransaction`: opaque, safe to put in an HttpOnly cookie. */
318
+ type SealedTransaction = string & {
319
+ readonly __sealedTransaction: true;
320
+ };
321
+ /** What a started sign-in remembers (sealed; never sent to the pod). */
322
+ interface ConnectTransaction {
323
+ v: 1;
324
+ issuer: string;
325
+ tokenEndpoint: string;
326
+ revocationEndpoint: string | null;
327
+ clientId: string;
328
+ redirectUri: string;
329
+ state: string;
330
+ verifier: string;
331
+ resource: string | null;
332
+ /** A same-origin path to land on afterwards. */
333
+ next: string | null;
334
+ createdAt: number;
335
+ }
336
+ interface FlowOptions {
337
+ fetch?: typeof fetch;
338
+ now?: () => number;
339
+ }
340
+ interface StartConnectOptions extends FlowOptions {
341
+ /** A validated issuer (`discoverPod`), or an address / launch `iss` to discover. */
342
+ pod: PodIssuer | string;
343
+ clientId: string;
344
+ redirectUri: string;
345
+ /** Grant-grammar permissions, each declared in the app's client metadata. */
346
+ permissions: readonly string[];
347
+ secret: AppSecret;
348
+ /** RFC 8707 resource: the pod's Hub API (default) or its MCP endpoint; null = none. */
349
+ resource?: "api/hub" | "mcp" | null;
350
+ /** A space to preselect on consent. */
351
+ spaceHint?: string | null;
352
+ /** A same-origin path to land on afterwards (validate it first: `safeNextPath`). */
353
+ next?: string | null;
354
+ allowInsecureLoopback?: boolean;
355
+ }
356
+ interface StartedConnect {
357
+ /** Redirect the browser here (303). */
358
+ authorizeUrl: string;
359
+ /** Store in an HttpOnly cookie until the callback. */
360
+ transaction: SealedTransaction;
361
+ /** ms since epoch. */
362
+ expiresAt: number;
363
+ issuer: PodIssuer;
364
+ }
365
+ declare function startConnect(opts: StartConnectOptions): Promise<StartedConnect>;
366
+ /** Open a sealed transaction; refuses tampered, foreign and stale ones. */
367
+ declare function openTransaction(sealed: string | null | undefined, opts: {
368
+ secret: AppSecret;
369
+ now?: () => number;
370
+ }): Promise<ConnectTransaction>;
371
+ interface HandleCallbackOptions extends FlowOptions {
372
+ /** The full URL the pod redirected the browser to. */
373
+ callbackUrl: string | URL;
374
+ transaction: string | null | undefined;
375
+ clientId: string;
376
+ redirectUri: string;
377
+ secret: AppSecret;
378
+ }
379
+ interface CallbackResult {
380
+ connection: PodConnection;
381
+ /** The same-origin path the sign-in was started for. */
382
+ next: string | null;
383
+ }
384
+ declare function handleCallback(opts: HandleCallbackOptions): Promise<CallbackResult>;
385
+ /**
386
+ * A token response → a connection. On a refresh, `previous` supplies what the
387
+ * response may omit (grant, user). A refresh response WITHOUT a new
388
+ * refresh_token drops the old one: with single-use rotation, resending a
389
+ * consumed token reads as theft and revokes the whole family.
390
+ */
391
+ declare function parseTokenResponse(body: Record<string, unknown>, ctx: {
392
+ podUrl: string;
393
+ clientId: string;
394
+ tokenEndpoint: string;
395
+ revocationEndpoint: string | null;
396
+ now: number;
397
+ previous?: PodConnection;
398
+ }): PodConnection;
399
+ /**
400
+ * How long a rotation's result is remembered for its SPENT refresh token.
401
+ * The pod (PB3) refuses a concurrent second redeem softly, but a token
402
+ * presented AGAIN after its use is a reuse: the whole family and the live
403
+ * access key are revoked. A browser easily sends that replay: parallel
404
+ * requests, a second tab, a response it had not stored yet, all still
405
+ * carrying the old cookie. Within this window such a request gets the
406
+ * rotation's result (and re-sends the new cookie) instead of reaching the pod.
407
+ */
408
+ declare const ROTATION_MEMORY_MS: number;
409
+ /**
410
+ * Exchange the refresh token for a new access token and the rotated refresh
411
+ * token (PB3: single-use, 30 days from issue, reach re-derived by the pod).
412
+ *
413
+ * SINGLE-FLIGHT per connection within this process: concurrent callers share
414
+ * one request, and for `ROTATION_MEMORY_MS` after it a caller still holding
415
+ * the spent token gets the same result, so this process never replays a
416
+ * spent token. Across processes nothing here can coordinate: a server-side
417
+ * `ConnectionStore` (one row per connection) is the way to rule that out.
418
+ * The caller must PERSIST the result before using its access token
419
+ * (`refreshIfNeeded` does).
420
+ *
421
+ * Throws `expired` (no refresh token, or past its idle life), `revoked`
422
+ * (invalid_grant while it should live: spent, revoked, the app removed or
423
+ * narrowed to nothing, or a concurrent redeem lost), `misconfigured`
424
+ * (invalid_client, unauthorized_client), `rate_limited`, `pod_unreachable`,
425
+ * `server_error`.
426
+ */
427
+ declare function refreshConnection(connection: PodConnection, opts?: FlowOptions): Promise<PodConnection>;
428
+ /**
429
+ * RFC 7009, best effort: the refresh token (the pod retires its whole family
430
+ * and the live access key) then the access token (for a pod without refresh).
431
+ * Never throws; `revoked` says whether the pod confirmed.
432
+ */
433
+ declare function revokeConnection(connection: PodConnection, opts?: FlowOptions): Promise<{
434
+ revoked: boolean;
435
+ }>;
436
+
437
+ /** RFC 7636 §4.2: `BASE64URL(SHA256(ASCII(code_verifier)))`. */
438
+ declare function pkceChallenge(verifier: string): Promise<string>;
439
+
440
+ declare function isBrowserRuntime(): boolean;
441
+
442
+ export { AppRequest, type AppSecret, CONNECT_ERROR_CODES, type CallbackResult, ClientMetadataDocument, ConnectError, type ConnectErrorCode, type ConnectTransaction, type ConnectionStore, type CookieNames, DEFAULT_PATHS, DEFAULT_REFRESH_IDLE_MS, type DiscoverOptions, type FlowOptions, type HandleCallbackOptions, type LaunchHint, MIN_SECRET_LENGTH, type Opened, type PodAppAuth, type PodAppAuthConfig, type PodAppPaths, type PodClient, type PodClientOptions, PodConnection, type PodError, type PodErrorCode, PodGrant, PodIssuer, type PodResult, type PodWriteResult, type QueryValue, REFRESH_WINDOW_MS, ROTATION_MEMORY_MS, type RefreshOutcome, type RefreshStatus, type SealPurpose, type SealedCookieStoreOptions, type SealedTransaction, type StartConnectOptions, type StartedConnect, TRANSACTION_TTL_MS, acceptLaunch, applySetCookies, buildClientMetadata, can, canonicalPodUrl, cloudConnectAvailable, connectionExpiresAt, cookieNames, createPodAppAuth, createPodClient, createSealedCookieStore, discoverPod, handleCallback, isBrowserRuntime, isConnectError, openTransaction, parseConnectErrorCode, parseTokenResponse, pkceChallenge, refreshConnection, reviewUrlFor, revokeConnection, safeNextPath, seal, startCloudConnect, startConnect, unseal };