@k9o/gate 0.0.0-stage → 1.1.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/dist/index.js ADDED
@@ -0,0 +1,613 @@
1
+ import {
2
+ extractClaims,
3
+ requireScope,
4
+ verifyJwt
5
+ } from "./chunk-YINWOWJC.js";
6
+ import {
7
+ GateError,
8
+ gateErrorFromWire
9
+ } from "./chunk-JEV37YEC.js";
10
+ import {
11
+ configureGate,
12
+ getGateConfig,
13
+ resetGateConfig,
14
+ validateGateConfig
15
+ } from "./chunk-XP6U5QKF.js";
16
+
17
+ // src/region.ts
18
+ var REGION_FREE_ORG_HOST = /^([a-z][a-z0-9-]{1,62})\.(api|api-staging)\.k9o\.io$/;
19
+ var REGION_CODE = /^[a-z]{2}\d{2}$/;
20
+ var cache = /* @__PURE__ */ new WeakMap();
21
+ function regionalOriginFromLocation(host, location, base) {
22
+ const m = REGION_FREE_ORG_HOST.exec(host);
23
+ if (m === null) return null;
24
+ let u;
25
+ try {
26
+ u = new URL(location, base);
27
+ } catch {
28
+ return null;
29
+ }
30
+ if (u.protocol !== "https:" || u.username !== "" || u.password !== "" || u.port !== "") return null;
31
+ const [, org, apex] = m;
32
+ const want = new RegExp(`^${org}\\.${apex}-([a-z0-9]+)\\.k9o\\.io$`);
33
+ const r = want.exec(u.hostname);
34
+ if (r === null || !REGION_CODE.test(r[1])) return null;
35
+ return `https://${u.hostname}`;
36
+ }
37
+ async function probe(config) {
38
+ const fetchImpl = config.fetch ?? globalThis.fetch;
39
+ const configured = config.baseUrl;
40
+ if (typeof fetchImpl !== "function") return { origin: configured, cacheable: false };
41
+ let host;
42
+ try {
43
+ host = new URL(configured).hostname;
44
+ } catch {
45
+ return { origin: configured, cacheable: true };
46
+ }
47
+ if (!REGION_FREE_ORG_HOST.test(host)) return { origin: configured, cacheable: true };
48
+ try {
49
+ const res = await fetchImpl(`${configured.replace(/\/+$/, "")}/`, {
50
+ method: "HEAD",
51
+ redirect: "manual",
52
+ headers: { accept: "*/*" }
53
+ });
54
+ if (res.status === 308) {
55
+ const loc = res.headers.get("location");
56
+ const origin = loc === null ? null : regionalOriginFromLocation(host, loc, configured);
57
+ if (origin !== null) return { origin, cacheable: true };
58
+ }
59
+ return { origin: configured, cacheable: true };
60
+ } catch {
61
+ return { origin: configured, cacheable: false };
62
+ }
63
+ }
64
+ function resolveRegionalOrigin(config) {
65
+ const hit = cache.get(config);
66
+ if (hit !== void 0) return hit;
67
+ const p = probe(config).then((r) => {
68
+ if (!r.cacheable) cache.delete(config);
69
+ return r.origin;
70
+ });
71
+ cache.set(config, p);
72
+ return p;
73
+ }
74
+
75
+ // src/client.ts
76
+ async function requestWith(config, init) {
77
+ const fetchImpl = config.fetch ?? globalThis.fetch;
78
+ if (typeof fetchImpl !== "function") {
79
+ throw new GateError({
80
+ code: "INTERNAL_ERROR",
81
+ message: "No fetch implementation available \u2014 configure {fetch} in configureGate() for non-browser, non-Bun, non-Node-18+ environments",
82
+ context: {},
83
+ recommendation: null
84
+ });
85
+ }
86
+ const url = buildUrl(await resolveRegionalOrigin(config), init.path, init.query);
87
+ const headers = {
88
+ accept: "application/json",
89
+ "x-gate-mode": config.mode,
90
+ ...init.headers ?? {}
91
+ };
92
+ const bearer = init.bearerToken ?? config.partnerToken;
93
+ if (typeof bearer === "string" && bearer.length > 0) {
94
+ headers["authorization"] = `Bearer ${bearer}`;
95
+ }
96
+ const requestInit = {
97
+ method: init.method,
98
+ headers
99
+ };
100
+ if (init.body !== void 0) {
101
+ headers["content-type"] = "application/json";
102
+ requestInit["body"] = JSON.stringify(init.body);
103
+ }
104
+ let response;
105
+ try {
106
+ response = await fetchImpl(url, requestInit);
107
+ } catch (e) {
108
+ throw new GateError({
109
+ code: "UPSTREAM_UNAVAILABLE",
110
+ message: `Network error reaching ${url}: ${e instanceof Error ? e.message : String(e)}`,
111
+ context: { url },
112
+ recommendation: null
113
+ });
114
+ }
115
+ let parsed;
116
+ try {
117
+ parsed = await response.json();
118
+ } catch (e) {
119
+ throw new GateError({
120
+ code: "INTERNAL_ERROR",
121
+ message: `Failed to parse JSON response from ${url}: ${e instanceof Error ? e.message : String(e)}`,
122
+ context: { url, status: response.status },
123
+ recommendation: null,
124
+ status: response.status
125
+ });
126
+ }
127
+ if (response.status >= 200 && response.status < 300) {
128
+ if (!isEnvelope(parsed)) {
129
+ throw new GateError({
130
+ code: "INTERNAL_ERROR",
131
+ message: `Malformed envelope from ${url}: expected {data, meta}`,
132
+ context: { url, status: response.status },
133
+ recommendation: null,
134
+ status: response.status
135
+ });
136
+ }
137
+ return parsed;
138
+ }
139
+ if (isKErrorWire(parsed)) {
140
+ throw gateErrorFromWire(parsed, response.status);
141
+ }
142
+ throw new GateError({
143
+ code: "INTERNAL_ERROR",
144
+ message: `Upstream returned ${response.status} with non-KError body`,
145
+ context: { url, status: response.status, body: parsed },
146
+ recommendation: null,
147
+ status: response.status
148
+ });
149
+ }
150
+ function buildUrl(baseUrl, path, query) {
151
+ const trimmedBase = baseUrl.endsWith("/") ? baseUrl.slice(0, -1) : baseUrl;
152
+ const trimmedPath = path.startsWith("/") ? path : `/${path}`;
153
+ let url = `${trimmedBase}${trimmedPath}`;
154
+ if (query !== void 0) {
155
+ const params = new URLSearchParams();
156
+ for (const [k, v] of Object.entries(query)) {
157
+ if (v !== void 0) params.set(k, String(v));
158
+ }
159
+ const qs = params.toString();
160
+ if (qs.length > 0) url += `?${qs}`;
161
+ }
162
+ return url;
163
+ }
164
+ function isEnvelope(value) {
165
+ if (value === null || typeof value !== "object") return false;
166
+ const v = value;
167
+ return "data" in v && "meta" in v && typeof v.meta === "object" && v.meta !== null;
168
+ }
169
+ function isKErrorWire(value) {
170
+ if (value === null || typeof value !== "object") return false;
171
+ const v = value;
172
+ return typeof v.code === "string" && typeof v.message === "string";
173
+ }
174
+
175
+ // src/paths.ts
176
+ function resolveProjectPathRoot(config) {
177
+ return `/@${config.projectSlug}`;
178
+ }
179
+
180
+ // src/idp-providers/identity-providers.ts
181
+ function identityProvidersPath(config) {
182
+ return `${resolveProjectPathRoot(config)}/identity-providers`;
183
+ }
184
+ function makeIdentityProviders(getConfig) {
185
+ return {
186
+ /**
187
+ * Register a new end-user IdP provider config for the configured
188
+ * project. PROJECT_ADMIN+ required (server-enforced). The plaintext
189
+ * client secret is encrypted-at-rest server-side; the returned config
190
+ * carries only the opaque secret handle.
191
+ *
192
+ * The gateway validates the `idpProvider`→`tierGating` pairing
193
+ * and rejects a mismatch with `K9O_INVALID_FIELD_VALUE`.
194
+ */
195
+ async create(input) {
196
+ const config = getConfig();
197
+ return requestWith(config, {
198
+ method: "POST",
199
+ path: identityProvidersPath(config),
200
+ body: input
201
+ });
202
+ },
203
+ /**
204
+ * List the configured end-user IdP provider configs for the project.
205
+ * READ_ROLES-gated server-side (PROJECT_ADMIN/EDITOR/MEMBER/VIEWER/AUTHOR/
206
+ * ANALYST) — the SAME shape + roles as the existing GET route
207
+ * (`listProviders` returns the existing route's shape, no
208
+ * confidentiality-narrower endpoint). Each `IdpConfig` carries the opaque
209
+ * secret handle, never the secret. Callers that only need the summary
210
+ * (`id`/`idpProvider`/`displayName`/`enabled`/`tierGating`) pick fields off
211
+ * the result — no server change needed for that shaping.
212
+ */
213
+ async list() {
214
+ const config = getConfig();
215
+ return requestWith(config, {
216
+ method: "GET",
217
+ path: identityProvidersPath(config)
218
+ });
219
+ },
220
+ /**
221
+ * Test connectivity to a configured provider by id — a NO-SECRET reachability + metadata-validity probe
222
+ * (no-secret): the gateway resolve-and-pins the issuer/metadata endpoint (SSRF-fenced) and
223
+ * validates it structurally, WITHOUT decrypting or transmitting the stored client secret. Returns
224
+ * `{ok, reason?}`; the `reason` is a closed machine code, never the upstream body or any credential.
225
+ * PROJECT_ADMIN required (server-enforced) — it triggers live external egress on the platform's behalf.
226
+ */
227
+ /**
228
+ * Update a provider config by id (PATCH; PROJECT_ADMIN, server-enforced). Partial: only supplied fields
229
+ * change; `idpProvider`/`tierGating` are immutable. Returns the updated config (secrets never included).
230
+ * GS-F7: write directory-sync settings here and read their status back off `list()`. See `PatchIdpInput`
231
+ * for the metadataUrl-change-clears-secret rule.
232
+ */
233
+ async update(configId, patch) {
234
+ const config = getConfig();
235
+ return requestWith(config, {
236
+ method: "PATCH",
237
+ path: `${identityProvidersPath(config)}/${encodeURIComponent(configId)}`,
238
+ body: patch
239
+ });
240
+ },
241
+ async testProvider(configId) {
242
+ const config = getConfig();
243
+ return requestWith(config, {
244
+ method: "POST",
245
+ path: `${identityProvidersPath(config)}/${encodeURIComponent(configId)}/test`
246
+ });
247
+ }
248
+ };
249
+ }
250
+ var identityProviders = makeIdentityProviders(getGateConfig);
251
+
252
+ // src/magic-link-redirects.ts
253
+ function magicLinkRedirectsPath(config, id) {
254
+ const root = `${resolveProjectPathRoot(config)}/gate/magic-link-redirects`;
255
+ return id !== void 0 ? `${root}/${id}` : root;
256
+ }
257
+ function makeMagicLinkRedirects(getConfig) {
258
+ return {
259
+ /** List every allowlisted redirect target for the active project. */
260
+ async list() {
261
+ const config = getConfig();
262
+ return requestWith(config, {
263
+ method: "GET",
264
+ path: magicLinkRedirectsPath(config)
265
+ });
266
+ },
267
+ /**
268
+ * Register a new redirect target. `redirectUrl` must exact-match
269
+ * what a customer later passes to `requestMagicLink` — the gateway
270
+ * enforces exact match, never a prefix or wildcard.
271
+ */
272
+ async add(input) {
273
+ const config = getConfig();
274
+ return requestWith(config, {
275
+ method: "POST",
276
+ path: magicLinkRedirectsPath(config),
277
+ body: input
278
+ });
279
+ },
280
+ /** Remove a previously-registered redirect target by its `id`. */
281
+ async remove(id) {
282
+ const config = getConfig();
283
+ return requestWith(config, {
284
+ method: "DELETE",
285
+ path: magicLinkRedirectsPath(config, id)
286
+ });
287
+ }
288
+ };
289
+ }
290
+ var magicLinkRedirects = makeMagicLinkRedirects(getGateConfig);
291
+
292
+ // src/gate.ts
293
+ function gatePath(config, suffix) {
294
+ return `${resolveProjectPathRoot(config)}/gate/${suffix}`;
295
+ }
296
+ function makeGateNamespace(getConfig) {
297
+ return {
298
+ /**
299
+ * Create a new end-user identity for the configured project. Returns
300
+ * the new user record + an initial session. Emits a
301
+ * `data.write.auth.signup` audit event.
302
+ *
303
+ * Errors (closed taxonomy):
304
+ * - `EMAIL_ALREADY_REGISTERED` (409) when the email collides
305
+ * - `INVALID_FIELD_VALUE` (400) on schema violation (password policy, malformed email)
306
+ * - `TIER_FEATURE_GATED` (403) when the project's tier blocks email/password
307
+ */
308
+ async signUp(input) {
309
+ return requestWith(getConfig(), {
310
+ method: "POST",
311
+ path: gatePath(getConfig(), "signup"),
312
+ body: input
313
+ });
314
+ },
315
+ /**
316
+ * Sign in an existing end-user with email + password. Returns the
317
+ * user record + a fresh session. Emits a `data.write.auth.signin`
318
+ * audit event.
319
+ *
320
+ * Errors:
321
+ * - `INVALID_CREDENTIALS` (401) on bad email/password (ambiguous on
322
+ * purpose — does not distinguish "user not found" vs "password
323
+ * wrong" to avoid enumeration)
324
+ */
325
+ async signIn(input) {
326
+ return requestWith(getConfig(), {
327
+ method: "POST",
328
+ path: gatePath(getConfig(), "signin"),
329
+ body: input
330
+ });
331
+ },
332
+ /**
333
+ * Sign out — revoke the bound refresh token (or an explicit one, if
334
+ * supplied). Emits a `data.write.auth.signout` audit event.
335
+ *
336
+ * Errors:
337
+ * - `UNAUTHENTICATED` (401) when no session is present
338
+ * - `REFRESH_TOKEN_INVALID` (400) when the supplied refresh token
339
+ * doesn't match the bearer's session
340
+ */
341
+ async signOut(input = {}) {
342
+ return requestWith(getConfig(), {
343
+ method: "POST",
344
+ path: gatePath(getConfig(), "signout"),
345
+ body: input
346
+ });
347
+ },
348
+ /**
349
+ * Return the caller's identity claims from the bearer JWT. Useful in
350
+ * Standalone mode where the SDK consumer wants to surface the user's
351
+ * roles / groups without parsing the JWT itself.
352
+ *
353
+ * Errors:
354
+ * - `UNAUTHENTICATED` (401) when no bearer is present
355
+ * - `SESSION_EXPIRED` (401) when the bearer's exp has passed
356
+ */
357
+ async whoami() {
358
+ return requestWith(getConfig(), {
359
+ method: "GET",
360
+ path: gatePath(getConfig(), "whoami")
361
+ });
362
+ },
363
+ /**
364
+ * Rotate the access token using a refresh token. Returns a fresh
365
+ * session pair (new access token + same refresh token; gateway may
366
+ * also rotate the refresh token per project policy).
367
+ *
368
+ * Errors:
369
+ * - `REFRESH_TOKEN_INVALID` (400) when the refresh token is unknown,
370
+ * expired, or revoked
371
+ */
372
+ async refreshToken(input) {
373
+ return requestWith(getConfig(), {
374
+ method: "POST",
375
+ path: gatePath(getConfig(), "refresh"),
376
+ body: input
377
+ });
378
+ },
379
+ /**
380
+ * Check whether the bearer's session is currently valid. Does NOT
381
+ * throw on expiry or invalid token — returns `{ valid: false }`
382
+ * inside the envelope. The intended use is health-checking a session
383
+ * without surfacing an error to the end-user (the SDK consumer
384
+ * typically wants to redirect to sign-in rather than surface a 401).
385
+ */
386
+ async verifySession() {
387
+ return requestWith(getConfig(), {
388
+ method: "GET",
389
+ path: gatePath(getConfig(), "verify")
390
+ });
391
+ },
392
+ /**
393
+ * The project's hosted sign-in page URL and WebAuthn `rpId`. Unauthenticated and cacheable; no credential is sent,
394
+ * even when a partner token is configured. Take both values as given: an unknown org, a deleted project and a
395
+ * non-Gate project all answer the same `PROJECT_NOT_FOUND`, so a failure here says nothing about which.
396
+ */
397
+ async hosted() {
398
+ return requestWith(getConfig(), {
399
+ method: "GET",
400
+ path: gatePath(getConfig(), "hosted"),
401
+ bearerToken: ""
402
+ });
403
+ },
404
+ /**
405
+ * Initiate an external-IdP sign-in flow (Google / GitHub / Microsoft).
406
+ * The SDK POSTs to the gateway's authorize endpoint, receives the
407
+ * provider's authorization URL with a signed-state JWT (carrying PKCE
408
+ * verifier + the supplied `redirectUri`), and redirects the browser.
409
+ *
410
+ * After the user authenticates with the IdP, the gateway processes the
411
+ * callback and redirects the browser back to `redirectUri` with session
412
+ * tokens in the URL fragment. Use `parseSignInCallback()` on the
413
+ * destination page to extract them.
414
+ *
415
+ * Example:
416
+ * await gate.signInWithProvider({
417
+ * provider: 'google',
418
+ * redirectUri: 'https://app.example.com/auth/callback',
419
+ * });
420
+ * // …user lands at /auth/callback#access_token=…&refresh_token=…
421
+ *
422
+ * Generic OIDC requires authorization-endpoint discovery and is not
423
+ * yet wired; use a hosted provider (google/github/microsoft) for now.
424
+ */
425
+ async signInWithProvider(input) {
426
+ const env = await requestWith(getConfig(), {
427
+ method: "POST",
428
+ path: `${resolveProjectPathRoot(getConfig())}/gate/idp/${input.provider}/authorize`,
429
+ body: { redirectUri: input.redirectUri }
430
+ });
431
+ const w = globalThis.window;
432
+ if (w !== void 0) {
433
+ w.location.assign(env.data.authorizationUrl);
434
+ } else {
435
+ throw new Error(
436
+ "signInWithProvider requires a browser environment (window.location); for server-side initiation, fetch the authorize endpoint directly."
437
+ );
438
+ }
439
+ },
440
+ /**
441
+ * Parse session tokens out of the URL fragment delivered by an
442
+ * IdP callback redirect. Returns the session payload on success or an
443
+ * error object on failure. Pass `window.location.hash` (or any
444
+ * `#`-prefixed string) — leading `#` is stripped automatically.
445
+ *
446
+ * Side-effect-free: does not call the gateway and does not mutate
447
+ * any session storage. The consumer is responsible for persisting
448
+ * the returned tokens (e.g. via the app's auth context).
449
+ */
450
+ parseSignInCallback(hash) {
451
+ const raw = hash.startsWith("#") ? hash.slice(1) : hash;
452
+ const params = new URLSearchParams(raw);
453
+ const error = params.get("error");
454
+ if (error !== null) {
455
+ return {
456
+ ok: false,
457
+ error,
458
+ errorDescription: params.get("error_description") ?? error
459
+ };
460
+ }
461
+ const accessToken = params.get("access_token");
462
+ const refreshToken = params.get("refresh_token");
463
+ const expiresAt = params.get("expires_at");
464
+ if (accessToken === null || refreshToken === null || expiresAt === null) {
465
+ return {
466
+ ok: false,
467
+ error: "malformed_callback",
468
+ errorDescription: "Callback URL fragment missing one or more required fields (access_token, refresh_token, expires_at)."
469
+ };
470
+ }
471
+ return {
472
+ ok: true,
473
+ accessToken,
474
+ refreshToken,
475
+ expiresAt,
476
+ tokenType: params.get("token_type") ?? "Bearer"
477
+ };
478
+ },
479
+ /**
480
+ * Passwordless sub-namespace — OTP + Magic Link flows
481
+ * for email-based authentication without a stored password.
482
+ *
483
+ * Wire endpoints (LIVE on the Gate end-user plane — verified against
484
+ * staging): OTP → `POST /@<projectSlug>/gate/otp/{request,verify}`
485
+ * (routes/otp.ts), Magic Link → `POST /@<projectSlug>/gate/magic-link/
486
+ * {send,redeem}` (routes/magic-link.ts). NOTE: these are the gateway's
487
+ * canonical paths; the SDK ergonomic method names (`requestMagicLink`/
488
+ * `verifyMagicLink`) do not mirror the path segments (`send`/`redeem`).
489
+ * (The SDK paths match the shipped gateway routes.)
490
+ */
491
+ passwordless: {
492
+ /**
493
+ * Request a one-time passcode delivered to the supplied email.
494
+ * The OTP is short-lived (typically 5 min) and
495
+ * single-use; redemption via `verifyOtp` mints a session.
496
+ */
497
+ async requestOtp(input) {
498
+ return requestWith(getConfig(), {
499
+ method: "POST",
500
+ path: gatePath(getConfig(), "otp/request"),
501
+ body: input
502
+ });
503
+ },
504
+ /**
505
+ * Verify a one-time passcode previously sent via `requestOtp`.
506
+ * On success returns the same `AuthResult` shape as `signIn` —
507
+ * a fresh session bound to the end-user identity.
508
+ *
509
+ * Errors:
510
+ * - INVALID_CREDENTIALS (401) on wrong / expired / consumed OTP
511
+ * (ambiguous on purpose — enumeration defense)
512
+ */
513
+ async verifyOtp(input) {
514
+ return requestWith(getConfig(), {
515
+ method: "POST",
516
+ path: gatePath(getConfig(), "otp/verify"),
517
+ body: input
518
+ });
519
+ },
520
+ /**
521
+ * Request a magic-link emailed to the supplied address. The link
522
+ * carries an opaque short-lived token (typically 15 min
523
+ *); clicking the link lands at the customer's chosen
524
+ * redirect URL with the token in the query string, which the
525
+ * customer's frontend then exchanges via `verifyMagicLink`.
526
+ */
527
+ async requestMagicLink(input) {
528
+ return requestWith(getConfig(), {
529
+ method: "POST",
530
+ path: gatePath(getConfig(), "magic-link/send"),
531
+ body: input
532
+ });
533
+ },
534
+ /**
535
+ * Verify a magic-link token previously delivered via
536
+ * `requestMagicLink`. On success returns the same `AuthResult`
537
+ * shape as `signIn`.
538
+ *
539
+ * Errors:
540
+ * - INVALID_CREDENTIALS (401) on wrong / expired / consumed
541
+ * token (same enumeration-defense rationale as verifyOtp)
542
+ */
543
+ async verifyMagicLink(input) {
544
+ return requestWith(getConfig(), {
545
+ method: "POST",
546
+ path: gatePath(getConfig(), "magic-link/redeem"),
547
+ body: input
548
+ });
549
+ },
550
+ /**
551
+ * Extract the opaque magic-link token from the landing-page URL's query
552
+ * string (the gateway redirects to `<configured-redirect>?token=…&project=…`).
553
+ * Pure + side-effect-free — pass `window.location.search`. Feed the
554
+ * returned `token` to `verifyMagicLink({ token })` to mint the session.
555
+ *
556
+ * This is the magic-link counterpart to `parseSignInCallback` (which parses
557
+ * the SSO *hash fragment* for session tokens) — magic-link delivers an
558
+ * opaque token in the *query string*, so the two are not interchangeable.
559
+ */
560
+ parseMagicLinkCallback(search) {
561
+ const raw = search.startsWith("?") ? search.slice(1) : search;
562
+ const params = new URLSearchParams(raw);
563
+ const error = params.get("error");
564
+ if (error !== null) {
565
+ return { ok: false, error };
566
+ }
567
+ const token = params.get("token");
568
+ if (token === null || token.length === 0) {
569
+ return { ok: false, error: "missing_token" };
570
+ }
571
+ return { ok: true, token };
572
+ }
573
+ },
574
+ /**
575
+ * Project-scoped end-user IdP provider-config management.
576
+ * PROJECT_ADMIN+ required; end-user JWTs rejected at the route. Only
577
+ * `create()` is bound — the REST create the Gate Studio
578
+ * `FederationPresetsPanel` routes to when it drops the `gate_flows`
579
+ * `__addFromPreset` sentinel. Gate-admin surface.
580
+ */
581
+ identityProviders: makeIdentityProviders(getConfig),
582
+ /**
583
+ * Per-project magic-link redirect-URL allowlist management (blessed
584
+ * shape). Gate-admin surface alongside `identityProviders`
585
+ * — PROJECT_ADMIN+ required, distinct from the end-user-facing
586
+ * `passwordless.requestMagicLink`/`verifyMagicLink` send/verify flow.
587
+ */
588
+ magicLinkRedirects: makeMagicLinkRedirects(getConfig)
589
+ };
590
+ }
591
+ var gate = makeGateNamespace(getGateConfig);
592
+
593
+ // src/client-factory.ts
594
+ function createGateClient(config) {
595
+ validateGateConfig(config);
596
+ const frozen = { ...config };
597
+ return makeGateNamespace(() => frozen);
598
+ }
599
+ export {
600
+ GateError,
601
+ configureGate,
602
+ createGateClient,
603
+ extractClaims,
604
+ gate,
605
+ gateErrorFromWire,
606
+ getGateConfig,
607
+ regionalOriginFromLocation,
608
+ requireScope,
609
+ resetGateConfig,
610
+ resolveRegionalOrigin,
611
+ verifyJwt
612
+ };
613
+ //# sourceMappingURL=index.js.map