@k9o/gate 1.0.0 → 1.1.1

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.d.ts CHANGED
@@ -58,7 +58,7 @@ type RemoveMagicLinkRedirectResult = {
58
58
 
59
59
  /**
60
60
  * `gate.identityProviders` namespace — project-scoped end-user IdP
61
- * provider-config management (ch04 §1.6).
61
+ * provider-config management.
62
62
  *
63
63
  * Wraps the live gateway route family shipped in
64
64
  * `services/gateway/src/routes/project-identity-providers.ts`, mounted on
@@ -90,7 +90,7 @@ type RemoveMagicLinkRedirectResult = {
90
90
  * the write-only fix). `serviceAccountKey` (directory sync) follows the same write-only rule.
91
91
  */
92
92
 
93
- /** Closed end-user-class provider enum per ch04 §1.6 — 11 values since v221 (the project-plane substrate
93
+ /** Closed end-user-class provider enum — 11 values since v221 (the project-plane substrate
94
94
  * `_sys.end_user_idp_provider_configs`); the org plane carries the same 11 since v222/v223. */
95
95
  type IdpProviderKind = "google" | "github" | "microsoft" | "oidc" | "saml" | "scim" | "ldap" | "active_directory" | "apple" | "facebook" | "twitter";
96
96
  /**
@@ -104,7 +104,7 @@ type FederationTestProviderResult = {
104
104
  ok: boolean;
105
105
  reason?: "endpoint_unreachable" | "endpoint_forbidden" | "metadata_invalid" | "secret_not_provisioned" | "kind_unsupported";
106
106
  };
107
- /** Per ch04 §1.6 the provider→tier pairing is structurally fixed:
107
+ /** Per the provider→tier pairing is structurally fixed:
108
108
  * `free_tier_ok ∈ {google, github, microsoft, apple, facebook, twitter}`,
109
109
  * `ridge_plus ∈ {oidc, saml, scim, ldap, active_directory}`. The gateway rejects a mismatched
110
110
  * pairing (`K9O_INVALID_FIELD_VALUE`); the SDK forwards the caller value
@@ -117,7 +117,7 @@ type IdpTierGating = "free_tier_ok" | "ridge_plus";
117
117
  *
118
118
  * Reads carry `clientSecretSet` (presence only), never the secret or a handle (see the module header).
119
119
  * `metadataUrl`/`updatedAt` are `null` until set; `attributeMap` defaults to `{}`; `allowedRedirectUris`
120
- * defaults to `[]`. The `directory*` fields are GS-F7 scheduled directory sync (Google only, ch03 §5.11):
120
+ * defaults to `[]`. The `directory*` fields are GS-F7 scheduled directory sync (Google only,):
121
121
  * settings plus the scheduler's last-run status; the service-account key itself is never returned.
122
122
  */
123
123
  type IdpConfig = {
@@ -187,7 +187,7 @@ type CreateIdpInput = {
187
187
  * `tierGating` are immutable after create. An empty patch returns the current config unchanged.
188
188
  *
189
189
  * 🔴 For `oidc`/`microsoft`, changing `metadataUrl` WITHOUT `clientSecretRef` in the same patch CLEARS the
190
- * stored client secret (the gateway's secret-invalidate-on-host-change control, ch04 §1.6): re-supply the
190
+ * stored client secret (the gateway's secret-invalidate-on-host-change control,): re-supply the
191
191
  * secret whenever the metadata URL changes.
192
192
  */
193
193
  type PatchIdpInput = {
@@ -210,8 +210,8 @@ declare function makeIdentityProviders(getConfig: () => GateConfig): {
210
210
  * client secret is encrypted-at-rest server-side; the returned config
211
211
  * carries only the opaque secret handle.
212
212
  *
213
- * The gateway validates the `idpProvider`→`tierGating` pairing per ch04
214
- * §1.6 and rejects a mismatch with `K9O_INVALID_FIELD_VALUE`.
213
+ * The gateway validates the `idpProvider`→`tierGating` pairing
214
+ * and rejects a mismatch with `K9O_INVALID_FIELD_VALUE`.
215
215
  */
216
216
  create(input: CreateIdpInput): Promise<Envelope<IdpConfig>>;
217
217
  /**
@@ -249,10 +249,10 @@ type IdentityProvidersNamespace = ReturnType<typeof makeIdentityProviders>;
249
249
  declare function makeGateNamespace(getConfig: () => GateConfig): {
250
250
  /**
251
251
  * Create a new end-user identity for the configured project. Returns
252
- * the new user record + an initial session per ch01 §12.2. Emits a
253
- * `data.write.auth.signup` audit event per ch01 §12.5.
252
+ * the new user record + an initial session. Emits a
253
+ * `data.write.auth.signup` audit event.
254
254
  *
255
- * Errors (closed taxonomy per ch01 §2):
255
+ * Errors (closed taxonomy):
256
256
  * - `EMAIL_ALREADY_REGISTERED` (409) when the email collides
257
257
  * - `INVALID_FIELD_VALUE` (400) on schema violation (password policy, malformed email)
258
258
  * - `TIER_FEATURE_GATED` (403) when the project's tier blocks email/password
@@ -354,7 +354,7 @@ declare function makeGateNamespace(getConfig: () => GateConfig): {
354
354
  */
355
355
  parseSignInCallback(hash: string): SignInCallbackResult;
356
356
  /**
357
- * Passwordless sub-namespace per ch01 §12.1 — OTP + Magic Link flows
357
+ * Passwordless sub-namespace — OTP + Magic Link flows
358
358
  * for email-based authentication without a stored password.
359
359
  *
360
360
  * Wire endpoints (LIVE on the Gate end-user plane — verified against
@@ -363,13 +363,12 @@ declare function makeGateNamespace(getConfig: () => GateConfig): {
363
363
  * {send,redeem}` (routes/magic-link.ts). NOTE: these are the gateway's
364
364
  * canonical paths; the SDK ergonomic method names (`requestMagicLink`/
365
365
  * `verifyMagicLink`) do not mirror the path segments (`send`/`redeem`).
366
- * (The earlier `passwordless/*` SDK paths were interface-first scaffold
367
- * that never matched the shipped gateway routes — reconciled here.)
366
+ * (The SDK paths match the shipped gateway routes.)
368
367
  */
369
368
  passwordless: {
370
369
  /**
371
370
  * Request a one-time passcode delivered to the supplied email.
372
- * The OTP is short-lived (typically 5 min per ch03 §3.4) and
371
+ * The OTP is short-lived (typically 5 min) and
373
372
  * single-use; redemption via `verifyOtp` mints a session.
374
373
  */
375
374
  requestOtp(input: RequestOtpInput): Promise<Envelope<{
@@ -387,8 +386,8 @@ declare function makeGateNamespace(getConfig: () => GateConfig): {
387
386
  verifyOtp(input: VerifyOtpInput): Promise<Envelope<AuthResult>>;
388
387
  /**
389
388
  * Request a magic-link emailed to the supplied address. The link
390
- * carries an opaque short-lived token (typically 15 min per ch03
391
- * §3.3); clicking the link lands at the customer's chosen
389
+ * carries an opaque short-lived token (typically 15 min
390
+ *); clicking the link lands at the customer's chosen
392
391
  * redirect URL with the token in the query string, which the
393
392
  * customer's frontend then exchanges via `verifyMagicLink`.
394
393
  */
@@ -418,7 +417,7 @@ declare function makeGateNamespace(getConfig: () => GateConfig): {
418
417
  parseMagicLinkCallback(search: string): MagicLinkCallbackResult;
419
418
  };
420
419
  /**
421
- * Project-scoped end-user IdP provider-config management (ch04 §1.6).
420
+ * Project-scoped end-user IdP provider-config management.
422
421
  * PROJECT_ADMIN+ required; end-user JWTs rejected at the route. Only
423
422
  * `create()` is bound — the REST create the Gate Studio
424
423
  * `FederationPresetsPanel` routes to when it drops the `gate_flows`
@@ -447,14 +446,14 @@ declare function makeGateNamespace(getConfig: () => GateConfig): {
447
446
  };
448
447
  };
449
448
  /** Singleton bound to the module-level `configureGate()` config.
450
- * v0.4.0+: per-instance clients via `createGateClient(config)`. */
449
+ * Per-instance clients: `createGateClient(config)`. */
451
450
  declare const gate: {
452
451
  /**
453
452
  * Create a new end-user identity for the configured project. Returns
454
- * the new user record + an initial session per ch01 §12.2. Emits a
455
- * `data.write.auth.signup` audit event per ch01 §12.5.
453
+ * the new user record + an initial session. Emits a
454
+ * `data.write.auth.signup` audit event.
456
455
  *
457
- * Errors (closed taxonomy per ch01 §2):
456
+ * Errors (closed taxonomy):
458
457
  * - `EMAIL_ALREADY_REGISTERED` (409) when the email collides
459
458
  * - `INVALID_FIELD_VALUE` (400) on schema violation (password policy, malformed email)
460
459
  * - `TIER_FEATURE_GATED` (403) when the project's tier blocks email/password
@@ -556,7 +555,7 @@ declare const gate: {
556
555
  */
557
556
  parseSignInCallback(hash: string): SignInCallbackResult;
558
557
  /**
559
- * Passwordless sub-namespace per ch01 §12.1 — OTP + Magic Link flows
558
+ * Passwordless sub-namespace — OTP + Magic Link flows
560
559
  * for email-based authentication without a stored password.
561
560
  *
562
561
  * Wire endpoints (LIVE on the Gate end-user plane — verified against
@@ -565,13 +564,12 @@ declare const gate: {
565
564
  * {send,redeem}` (routes/magic-link.ts). NOTE: these are the gateway's
566
565
  * canonical paths; the SDK ergonomic method names (`requestMagicLink`/
567
566
  * `verifyMagicLink`) do not mirror the path segments (`send`/`redeem`).
568
- * (The earlier `passwordless/*` SDK paths were interface-first scaffold
569
- * that never matched the shipped gateway routes — reconciled here.)
567
+ * (The SDK paths match the shipped gateway routes.)
570
568
  */
571
569
  passwordless: {
572
570
  /**
573
571
  * Request a one-time passcode delivered to the supplied email.
574
- * The OTP is short-lived (typically 5 min per ch03 §3.4) and
572
+ * The OTP is short-lived (typically 5 min) and
575
573
  * single-use; redemption via `verifyOtp` mints a session.
576
574
  */
577
575
  requestOtp(input: RequestOtpInput): Promise<Envelope<{
@@ -589,8 +587,8 @@ declare const gate: {
589
587
  verifyOtp(input: VerifyOtpInput): Promise<Envelope<AuthResult>>;
590
588
  /**
591
589
  * Request a magic-link emailed to the supplied address. The link
592
- * carries an opaque short-lived token (typically 15 min per ch03
593
- * §3.3); clicking the link lands at the customer's chosen
590
+ * carries an opaque short-lived token (typically 15 min
591
+ *); clicking the link lands at the customer's chosen
594
592
  * redirect URL with the token in the query string, which the
595
593
  * customer's frontend then exchanges via `verifyMagicLink`.
596
594
  */
@@ -620,7 +618,7 @@ declare const gate: {
620
618
  parseMagicLinkCallback(search: string): MagicLinkCallbackResult;
621
619
  };
622
620
  /**
623
- * Project-scoped end-user IdP provider-config management (ch04 §1.6).
621
+ * Project-scoped end-user IdP provider-config management.
624
622
  * PROJECT_ADMIN+ required; end-user JWTs rejected at the route. Only
625
623
  * `create()` is bound — the REST create the Gate Studio
626
624
  * `FederationPresetsPanel` routes to when it drops the `gate_flows`
@@ -654,7 +652,7 @@ declare const gate: {
654
652
  type GateNamespace = ReturnType<typeof makeGateNamespace>;
655
653
 
656
654
  /**
657
- * `createGateClient(config)` — per-instance Gate SDK factory (v0.4.0).
655
+ * `createGateClient(config)` — per-instance Gate SDK factory.
658
656
  *
659
657
  * Returns a self-contained `GateClient` that does not depend on the
660
658
  * module-level `configureGate()` singleton. Multiple clients can coexist
@@ -663,7 +661,7 @@ type GateNamespace = ReturnType<typeof makeGateNamespace>;
663
661
  * incoming request's resolved tenant) or for apps that maintain both a
664
662
  * Standalone and Native binding against the same SDK instance.
665
663
  *
666
- * Per v2.5.68 URL grammar, supply `projectSlug` for
664
+ * Per the URL grammar, supply `projectSlug` for
667
665
  * canonical `/@<slug>/gate/...` routing. Slug is the only customer-facing
668
666
  * project addressing form — project ids are internal-only
669
667
  * (`feedback_slug_addressing_v2_5_68`); the retired `/v2/projects/...`
package/dist/index.js CHANGED
@@ -2,17 +2,17 @@ import {
2
2
  extractClaims,
3
3
  requireScope,
4
4
  verifyJwt
5
- } from "./chunk-ZNFI2CQA.js";
5
+ } from "./chunk-YINWOWJC.js";
6
6
  import {
7
7
  GateError,
8
8
  gateErrorFromWire
9
- } from "./chunk-PEW6HZFB.js";
9
+ } from "./chunk-JEV37YEC.js";
10
10
  import {
11
11
  configureGate,
12
12
  getGateConfig,
13
13
  resetGateConfig,
14
14
  validateGateConfig
15
- } from "./chunk-LZQ7MBXS.js";
15
+ } from "./chunk-XP6U5QKF.js";
16
16
 
17
17
  // src/region.ts
18
18
  var REGION_FREE_ORG_HOST = /^([a-z][a-z0-9-]{1,62})\.(api|api-staging)\.k9o\.io$/;
@@ -189,8 +189,8 @@ function makeIdentityProviders(getConfig) {
189
189
  * client secret is encrypted-at-rest server-side; the returned config
190
190
  * carries only the opaque secret handle.
191
191
  *
192
- * The gateway validates the `idpProvider`→`tierGating` pairing per ch04
193
- * §1.6 and rejects a mismatch with `K9O_INVALID_FIELD_VALUE`.
192
+ * The gateway validates the `idpProvider`→`tierGating` pairing
193
+ * and rejects a mismatch with `K9O_INVALID_FIELD_VALUE`.
194
194
  */
195
195
  async create(input) {
196
196
  const config = getConfig();
@@ -297,10 +297,10 @@ function makeGateNamespace(getConfig) {
297
297
  return {
298
298
  /**
299
299
  * Create a new end-user identity for the configured project. Returns
300
- * the new user record + an initial session per ch01 §12.2. Emits a
301
- * `data.write.auth.signup` audit event per ch01 §12.5.
300
+ * the new user record + an initial session. Emits a
301
+ * `data.write.auth.signup` audit event.
302
302
  *
303
- * Errors (closed taxonomy per ch01 §2):
303
+ * Errors (closed taxonomy):
304
304
  * - `EMAIL_ALREADY_REGISTERED` (409) when the email collides
305
305
  * - `INVALID_FIELD_VALUE` (400) on schema violation (password policy, malformed email)
306
306
  * - `TIER_FEATURE_GATED` (403) when the project's tier blocks email/password
@@ -477,7 +477,7 @@ function makeGateNamespace(getConfig) {
477
477
  };
478
478
  },
479
479
  /**
480
- * Passwordless sub-namespace per ch01 §12.1 — OTP + Magic Link flows
480
+ * Passwordless sub-namespace — OTP + Magic Link flows
481
481
  * for email-based authentication without a stored password.
482
482
  *
483
483
  * Wire endpoints (LIVE on the Gate end-user plane — verified against
@@ -486,13 +486,12 @@ function makeGateNamespace(getConfig) {
486
486
  * {send,redeem}` (routes/magic-link.ts). NOTE: these are the gateway's
487
487
  * canonical paths; the SDK ergonomic method names (`requestMagicLink`/
488
488
  * `verifyMagicLink`) do not mirror the path segments (`send`/`redeem`).
489
- * (The earlier `passwordless/*` SDK paths were interface-first scaffold
490
- * that never matched the shipped gateway routes — reconciled here.)
489
+ * (The SDK paths match the shipped gateway routes.)
491
490
  */
492
491
  passwordless: {
493
492
  /**
494
493
  * Request a one-time passcode delivered to the supplied email.
495
- * The OTP is short-lived (typically 5 min per ch03 §3.4) and
494
+ * The OTP is short-lived (typically 5 min) and
496
495
  * single-use; redemption via `verifyOtp` mints a session.
497
496
  */
498
497
  async requestOtp(input) {
@@ -520,8 +519,8 @@ function makeGateNamespace(getConfig) {
520
519
  },
521
520
  /**
522
521
  * Request a magic-link emailed to the supplied address. The link
523
- * carries an opaque short-lived token (typically 15 min per ch03
524
- * §3.3); clicking the link lands at the customer's chosen
522
+ * carries an opaque short-lived token (typically 15 min
523
+ *); clicking the link lands at the customer's chosen
525
524
  * redirect URL with the token in the query string, which the
526
525
  * customer's frontend then exchanges via `verifyMagicLink`.
527
526
  */
@@ -573,7 +572,7 @@ function makeGateNamespace(getConfig) {
573
572
  }
574
573
  },
575
574
  /**
576
- * Project-scoped end-user IdP provider-config management (ch04 §1.6).
575
+ * Project-scoped end-user IdP provider-config management.
577
576
  * PROJECT_ADMIN+ required; end-user JWTs rejected at the route. Only
578
577
  * `create()` is bound — the REST create the Gate Studio
579
578
  * `FederationPresetsPanel` routes to when it drops the `gate_flows`
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/region.ts","../src/client.ts","../src/paths.ts","../src/idp-providers/identity-providers.ts","../src/magic-link-redirects.ts","../src/gate.ts","../src/client-factory.ts"],"sourcesContent":["/**\n * Regional host resolution.\n *\n * The region-free org host `<org>.api[-staging].k9o.io` redirects GET/HEAD only and REFUSES a request that carries a\n * body, an `Authorization` header or a cookie (`400 K9O_INVALID_REQUEST_SHAPE`, `region_free_host_takes_no_credentials`),\n * so credentials must go to `<org>.api[-staging]-<rgn>.k9o.io`. An SDK learns the region with ONE unauthenticated\n * `HEAD https://<org>.api[-staging].k9o.io/`, takes the `308` `Location`, and caches the regional origin.\n *\n * Properties this module holds:\n * - Only a region-free org host (shape `<org>.api.k9o.io` or `<org>.api-staging.k9o.io`) is ever probed. A regional,\n * custom, self-hosted or dev baseUrl is used exactly as given.\n * - The probe carries NO credential and none of the configured default headers (they may hold one).\n * - The `Location` is trusted only if it is `https`, has no userinfo/port, and its host is exactly the SAME org on\n * the SAME apex family with a 4-character region code. Anything else is ignored and the configured baseUrl stays,\n * so a hostile redirect can never receive a credential.\n * - Not a redirect (the platform has not cut over, or the host answers directly), or any failure: the configured\n * baseUrl stays and the server's own refusal, if any, is what the caller sees. A definite answer is cached for the\n * life of the client's config; a network failure is not cached.\n */\n\n/** The slice of a client config this module needs (structural, so core and gate share one implementation). */\nexport type RegionConfig = { baseUrl: string; fetch?: typeof globalThis.fetch | undefined };\n\nconst REGION_FREE_ORG_HOST = /^([a-z][a-z0-9-]{1,62})\\.(api|api-staging)\\.k9o\\.io$/;\nconst REGION_CODE = /^[a-z]{2}\\d{2}$/;\n\nconst cache = new WeakMap<RegionConfig, Promise<string>>();\n\n/** The regional origin a `Location` may name for `host`, or null if it is not exactly that. Pure. */\nexport function regionalOriginFromLocation(host: string, location: string, base: string): string | null {\n const m = REGION_FREE_ORG_HOST.exec(host);\n if (m === null) return null;\n let u: URL;\n try {\n u = new URL(location, base);\n } catch {\n return null;\n }\n if (u.protocol !== \"https:\" || u.username !== \"\" || u.password !== \"\" || u.port !== \"\") return null;\n const [, org, apex] = m;\n const want = new RegExp(`^${org}\\\\.${apex}-([a-z0-9]+)\\\\.k9o\\\\.io$`);\n const r = want.exec(u.hostname);\n if (r === null || !REGION_CODE.test(r[1]!)) return null;\n return `https://${u.hostname}`;\n}\n\nasync function probe(config: RegionConfig): Promise<{ origin: string; cacheable: boolean }> {\n const fetchImpl = config.fetch ?? globalThis.fetch;\n const configured = config.baseUrl;\n if (typeof fetchImpl !== \"function\") return { origin: configured, cacheable: false };\n let host: string;\n try {\n host = new URL(configured).hostname;\n } catch {\n return { origin: configured, cacheable: true };\n }\n if (!REGION_FREE_ORG_HOST.test(host)) return { origin: configured, cacheable: true };\n try {\n const res = await fetchImpl(`${configured.replace(/\\/+$/, \"\")}/`, {\n method: \"HEAD\",\n redirect: \"manual\",\n headers: { accept: \"*/*\" },\n } as Parameters<typeof fetchImpl>[1]);\n if (res.status === 308) {\n const loc = res.headers.get(\"location\");\n const origin = loc === null ? null : regionalOriginFromLocation(host, loc, configured);\n if (origin !== null) return { origin, cacheable: true };\n }\n return { origin: configured, cacheable: true };\n } catch {\n return { origin: configured, cacheable: false };\n }\n}\n\n/** The origin requests for this client go to: the cached regional origin once learned, else the configured baseUrl. */\nexport function resolveRegionalOrigin(config: RegionConfig): Promise<string> {\n const hit = cache.get(config);\n if (hit !== undefined) return hit;\n const p = probe(config).then((r) => {\n if (!r.cacheable) cache.delete(config);\n return r.origin;\n });\n cache.set(config, p);\n return p;\n}\n","/**\n * HTTP client abstraction for the Gate SDK.\n *\n * Centralises:\n * - URL construction against the configured `baseUrl` + per-call path\n * - the standard ch01 §1 envelope unwrap on success\n * - the standard ch01 §2 `KError` rehydration on failure via `gateErrorFromWire`\n * - mode-routing via the `X-Gate-Mode` header (so the gateway routes\n * consumption to the right billing surface without per-function\n * branching at the SDK)\n *\n * Customers don't reach for this directly; it's the internal client\n * every `gate.*` function consumes. `request`/`requestWith` are\n * module-internal only — neither `index.ts` nor the `package.json`\n * `exports` map re-exports them, so they are not part of the public\n * `@k9o/gate` surface. In-package `gate.*` bindings and the test suite\n * import them from this module directly.\n */\n\nimport { getGateConfig, type GateConfig } from \"./mode.ts\";\nimport { resolveRegionalOrigin } from \"./region.ts\";\nimport { GateError, type KErrorWire, gateErrorFromWire } from \"./errors.ts\";\nimport type { Envelope } from \"./types.ts\";\n\nexport type RequestInit = {\n method: \"GET\" | \"POST\" | \"PUT\" | \"PATCH\" | \"DELETE\";\n path: string;\n /** Optional JSON body — serialised by the client. */\n body?: unknown;\n /** Optional per-call bearer token. Falls back to `partnerToken` from config when omitted. */\n bearerToken?: string;\n /** Optional additional headers; merged with the defaults. */\n headers?: Record<string, string>;\n /** Optional query parameters. */\n query?: Record<string, string | number | boolean | undefined>;\n};\n\n/**\n * Issue a request against the Gate API. Returns the unwrapped envelope\n * data on 2xx; throws a `GateError` carrying the closed-taxonomy `code`\n * + structured `context` on non-2xx.\n *\n * Implementation notes:\n * - The function pulls config lazily so `configureGate()` order doesn't\n * have to predate import-time module evaluation.\n * - `fetch` is taken from config when set, else from the global; this\n * keeps the SDK portable across Bun + Node 18+ + browsers + test\n * fixtures that supply a stub.\n * - JSON parsing of non-JSON responses throws — gateway always returns\n * JSON per ch01 §1 + §2, so a parse failure is an upstream defect.\n */\nexport async function request<T>(init: RequestInit): Promise<Envelope<T>> {\n return requestWith<T>(getGateConfig(), init);\n}\n\n/**\n * Issue a request using an explicit config (used by per-instance clients\n * returned from `createGateClient`). Singleton callers reach for\n * `request()` which delegates here against the module-level config.\n */\nexport async function requestWith<T>(\n config: GateConfig,\n init: RequestInit,\n): Promise<Envelope<T>> {\n const fetchImpl = config.fetch ?? globalThis.fetch;\n if (typeof fetchImpl !== \"function\") {\n throw new GateError({\n code: \"INTERNAL_ERROR\",\n message:\n \"No fetch implementation available — configure {fetch} in configureGate() for non-browser, non-Bun, non-Node-18+ environments\",\n context: {},\n recommendation: null,\n });\n }\n\n const url = buildUrl(await resolveRegionalOrigin(config), init.path, init.query);\n const headers: Record<string, string> = {\n accept: \"application/json\",\n \"x-gate-mode\": config.mode,\n ...(init.headers ?? {}),\n };\n const bearer = init.bearerToken ?? config.partnerToken;\n if (typeof bearer === \"string\" && bearer.length > 0) {\n headers[\"authorization\"] = `Bearer ${bearer}`;\n }\n const requestInit: Record<string, unknown> = {\n method: init.method,\n headers,\n };\n if (init.body !== undefined) {\n headers[\"content-type\"] = \"application/json\";\n requestInit[\"body\"] = JSON.stringify(init.body);\n }\n\n let response: Response;\n try {\n response = await fetchImpl(url, requestInit as Parameters<typeof fetchImpl>[1]);\n } catch (e) {\n throw new GateError({\n code: \"UPSTREAM_UNAVAILABLE\",\n message: `Network error reaching ${url}: ${e instanceof Error ? e.message : String(e)}`,\n context: { url },\n recommendation: null,\n });\n }\n\n let parsed: unknown;\n try {\n parsed = await response.json();\n } catch (e) {\n throw new GateError({\n code: \"INTERNAL_ERROR\",\n message: `Failed to parse JSON response from ${url}: ${e instanceof Error ? e.message : String(e)}`,\n context: { url, status: response.status },\n recommendation: null,\n status: response.status,\n });\n }\n\n if (response.status >= 200 && response.status < 300) {\n if (!isEnvelope<T>(parsed)) {\n throw new GateError({\n code: \"INTERNAL_ERROR\",\n message: `Malformed envelope from ${url}: expected {data, meta}`,\n context: { url, status: response.status },\n recommendation: null,\n status: response.status,\n });\n }\n return parsed;\n }\n\n // Non-2xx: the gateway returns a KError-shaped JSON payload per ch01 §2.\n if (isKErrorWire(parsed)) {\n throw gateErrorFromWire(parsed, response.status);\n }\n throw new GateError({\n code: \"INTERNAL_ERROR\",\n message: `Upstream returned ${response.status} with non-KError body`,\n context: { url, status: response.status, body: parsed },\n recommendation: null,\n status: response.status,\n });\n}\n\nfunction buildUrl(\n baseUrl: string,\n path: string,\n query: RequestInit[\"query\"],\n): string {\n const trimmedBase = baseUrl.endsWith(\"/\") ? baseUrl.slice(0, -1) : baseUrl;\n const trimmedPath = path.startsWith(\"/\") ? path : `/${path}`;\n let url = `${trimmedBase}${trimmedPath}`;\n if (query !== undefined) {\n const params = new URLSearchParams();\n for (const [k, v] of Object.entries(query)) {\n if (v !== undefined) params.set(k, String(v));\n }\n const qs = params.toString();\n if (qs.length > 0) url += `?${qs}`;\n }\n return url;\n}\n\nfunction isEnvelope<T>(value: unknown): value is Envelope<T> {\n if (value === null || typeof value !== \"object\") return false;\n const v = value as { data?: unknown; meta?: unknown };\n return \"data\" in v && \"meta\" in v && typeof v.meta === \"object\" && v.meta !== null;\n}\n\nfunction isKErrorWire(value: unknown): value is KErrorWire {\n if (value === null || typeof value !== \"object\") return false;\n const v = value as { code?: unknown; message?: unknown };\n return typeof v.code === \"string\" && typeof v.message === \"string\";\n}\n","/**\n * Shared path-resolution helper for project-scoped routes.\n *\n * v2.5.68 URL grammar: project-scoped resources address\n * via `/@<projectSlug>/...` against the org subdomain. Slug addressing is\n * the only customer-facing form — project ids are internal-only and never\n * appear in customer paths (`feedback_slug_addressing_v2_5_68`); the\n * retired `/v2/projects/<bareId>/...` form was dropped at pass17\n * (`feedback_v2_path_is_an_error`).\n *\n * Caller is expected to validate that the config carries a `projectSlug`\n * at construction time (see `validateGateConfig`).\n */\n\nimport type { GateConfig } from \"./mode.ts\";\n\n/** Return the project-scoped path root for the supplied config, e.g.\n * `/@partner-console-staging`. */\nexport function resolveProjectPathRoot(config: GateConfig): string {\n return `/@${config.projectSlug}`;\n}\n","/**\n * `gate.identityProviders` namespace — project-scoped end-user IdP\n * provider-config management (ch04 §1.6).\n *\n * Wraps the live gateway route family shipped in\n * `services/gateway/src/routes/project-identity-providers.ts`, mounted on\n * the project plane (`<orgSlug>.api.k9o.io/@<projectSlug>/identity-providers`):\n *\n * POST /@<projectSlug>/identity-providers — create (PROJECT_ADMIN)\n * GET /@<projectSlug>/identity-providers — list (READ_ROLES)\n * PATCH /@<projectSlug>/identity-providers/:epcId — update (PROJECT_ADMIN)\n * POST /@<projectSlug>/identity-providers/:epcId/test — testProvider (PROJECT_ADMIN)\n *\n * `create()` + `list()` back the Gate Studio `FederationPresetsPanel`; `update()` backs its\n * `DirectorySyncPanel` (directory-sync settings are read off `list()` and written via `update()`, replacing\n * the unanswered `federation.getDirectorySync`/`updateDirectorySync` bridge messages). Read-one/delete stay\n * unbound until a caller needs them (the SDK does not speculatively surface routes).\n *\n * Auth: operator JWT with PROJECT_ADMIN+ role, server-enforced by the\n * gateway RBAC layer (`requireProjectAdmin`); end-user JWTs (`eu_`-prefixed\n * subjects) are rejected at the route. The bearer is taken from\n * `partnerToken` in config, or per-call via the client's bearer plumbing.\n *\n * 🔴 `clientSecretRef` is a RAW SECRET on the way in, not a reference.\n * Post-v105 the gateway encrypts the submitted value (AES-256-GCM under\n * `K9O_SECRET_STORAGE_MASTER_KEY`) into the opaque `client_secret_enc`\n * handle and persists only that; the field name is retained for wire\n * compatibility but the create handler comment\n * (`project-identity-providers.ts`, the v105 INSERT) is authoritative —\n * there is NO Kibo custody on this path. Reads NEVER carry the secret or its\n * handle: they carry only `clientSecretSet` (a presence boolean,\n * the write-only fix). `serviceAccountKey` (directory sync) follows the same write-only rule.\n */\n\nimport { getGateConfig, type GateConfig } from \"../mode.ts\";\nimport { requestWith } from \"../client.ts\";\nimport { resolveProjectPathRoot } from \"../paths.ts\";\nimport type { Envelope } from \"../types.ts\";\n\n/** Closed end-user-class provider enum per ch04 §1.6 — 11 values since v221 (the project-plane substrate\n * `_sys.end_user_idp_provider_configs`); the org plane carries the same 11 since v222/v223. */\nexport type IdpProviderKind =\n | \"google\" | \"github\" | \"microsoft\" | \"oidc\" | \"saml\" | \"scim\"\n | \"ldap\" | \"active_directory\" | \"apple\" | \"facebook\" | \"twitter\";\n\n/**\n * The result of a provider connectivity test (`testProvider`). `ok` is the reachability + metadata-validity\n * verdict; `reason` (present only when `!ok`) is a CLOSED-enum machine code, NEVER the upstream response body\n * or any credential material. v1 is a NO-SECRET probe: it validates the issuer/metadata\n * endpoint is reachable + structurally valid; it never transmits the stored client secret. `credentials_rejected`\n * is deliberately NOT a v1 reason — the secret is never used, so it cannot be validated (a v2 concern).\n */\nexport type FederationTestProviderResult = {\n ok: boolean;\n reason?:\n | \"endpoint_unreachable\"\n | \"endpoint_forbidden\"\n | \"metadata_invalid\"\n | \"secret_not_provisioned\"\n | \"kind_unsupported\";\n};\n\n/** Per ch04 §1.6 the provider→tier pairing is structurally fixed:\n * `free_tier_ok ∈ {google, github, microsoft, apple, facebook, twitter}`,\n * `ridge_plus ∈ {oidc, saml, scim, ldap, active_directory}`. The gateway rejects a mismatched\n * pairing (`K9O_INVALID_FIELD_VALUE`); the SDK forwards the caller value\n * verbatim rather than second-guessing it. */\nexport type IdpTierGating = \"free_tier_ok\" | \"ridge_plus\";\n\n/**\n * The provider-config metadata shape returned by every read surface and\n * mutation response (`shapeIdpConfig`). Mirrors the gateway wire shape\n * one-to-one.\n *\n * Reads carry `clientSecretSet` (presence only), never the secret or a handle (see the module header).\n * `metadataUrl`/`updatedAt` are `null` until set; `attributeMap` defaults to `{}`; `allowedRedirectUris`\n * defaults to `[]`. The `directory*` fields are GS-F7 scheduled directory sync (Google only, ch03 §5.11):\n * settings plus the scheduler's last-run status; the service-account key itself is never returned.\n */\nexport type IdpConfig = {\n id: string;\n projectId: string;\n idpProvider: IdpProviderKind;\n displayName: string;\n clientId: string;\n /** Whether a client secret is stored. The secret (and any handle to it) is write-only. */\n clientSecretSet: boolean;\n redirectUri: string;\n metadataUrl: string | null;\n attributeMap: Record<string, unknown>;\n tierGating: IdpTierGating;\n enabled: boolean;\n allowedRedirectUris: string[];\n directoryPullEnabled: boolean;\n directorySyncSubject: string | null;\n directorySyncIntervalMins: number | null;\n directorySyncLastRunAt: string | null;\n directorySyncLastStatus: string | null;\n createdAt: string;\n updatedAt: string | null;\n};\n\n/** GS-F7 directory-sync write fields — accepted on create AND update (one PROJECT_ADMIN surface). */\nexport type DirectorySyncInput = {\n /** Enabling requires `idpProvider: \"google\"`, a `serviceAccountKey` (now or already stored) and an interval. */\n directoryPullEnabled?: boolean;\n directorySyncIntervalMins?: number;\n /** The raw Google service-account key JSON. Write-only: encrypted at rest server-side, never returned. */\n serviceAccountKey?: string;\n /** The Workspace admin user the service account impersonates (domain-wide delegation subject). */\n directorySyncSubject?: string;\n};\n\n/**\n * Create-request body — mirrors the gateway `CreateIdpBody` (t.Object)\n * exactly, including which fields are optional.\n */\nexport type CreateIdpInput = {\n idpProvider: IdpProviderKind;\n displayName: string;\n clientId: string;\n /**\n * The OAuth2 client secret. Sent over TLS; encrypted-at-rest server-side\n * (AES-256-GCM) and never returned. Named `clientSecretRef` for wire\n * compatibility — it is NOT a reference on the way in (see module header).\n */\n clientSecretRef: string;\n redirectUri: string;\n /** Optional; `null`/omitted for providers that don't use discovery metadata. */\n metadataUrl?: string | null;\n /** Optional claim→attribute mapping; defaults to `{}` server-side. */\n attributeMap?: Record<string, string>;\n tierGating: IdpTierGating;\n /** Defaults to `false` server-side — a provider is created disabled. */\n enabled?: boolean;\n /**\n * Customer SPA origins the SDK gate flow (`gate.signInWithProvider`) may\n * hand session tokens back to. Origin-matched + fail-closed at authorize,\n * so an empty/omitted list means SDK sign-in is disabled for this provider\n * until an origin is registered. Defaults to `[]` server-side.\n */\n allowedRedirectUris?: string[] | null;\n} & DirectorySyncInput;\n\n/**\n * Update-request body — mirrors the gateway `PatchIdpBody` exactly: every field optional; `idpProvider` and\n * `tierGating` are immutable after create. An empty patch returns the current config unchanged.\n *\n * 🔴 For `oidc`/`microsoft`, changing `metadataUrl` WITHOUT `clientSecretRef` in the same patch CLEARS the\n * stored client secret (the gateway's secret-invalidate-on-host-change control, ch04 §1.6): re-supply the\n * secret whenever the metadata URL changes.\n */\nexport type PatchIdpInput = {\n displayName?: string;\n clientId?: string;\n /** The raw client secret (write-only; see the module header). */\n clientSecretRef?: string;\n redirectUri?: string;\n metadataUrl?: string | null;\n attributeMap?: Record<string, string>;\n enabled?: boolean;\n allowedRedirectUris?: string[] | null;\n} & DirectorySyncInput;\n\nfunction identityProvidersPath(config: GateConfig): string {\n return `${resolveProjectPathRoot(config)}/identity-providers`;\n}\n\n/** Factory — produces an `identityProviders` namespace bound to the supplied\n * config getter. Used by both the `gate` singleton + `createGateClient`. */\nexport function makeIdentityProviders(getConfig: () => GateConfig) {\n return {\n /**\n * Register a new end-user IdP provider config for the configured\n * project. PROJECT_ADMIN+ required (server-enforced). The plaintext\n * client secret is encrypted-at-rest server-side; the returned config\n * carries only the opaque secret handle.\n *\n * The gateway validates the `idpProvider`→`tierGating` pairing per ch04\n * §1.6 and rejects a mismatch with `K9O_INVALID_FIELD_VALUE`.\n */\n async create(input: CreateIdpInput): Promise<Envelope<IdpConfig>> {\n const config = getConfig();\n return requestWith<IdpConfig>(config, {\n method: \"POST\",\n path: identityProvidersPath(config),\n body: input,\n });\n },\n\n /**\n * List the configured end-user IdP provider configs for the project.\n * READ_ROLES-gated server-side (PROJECT_ADMIN/EDITOR/MEMBER/VIEWER/AUTHOR/\n * ANALYST) — the SAME shape + roles as the existing GET route\n * (`listProviders` returns the existing route's shape, no\n * confidentiality-narrower endpoint). Each `IdpConfig` carries the opaque\n * secret handle, never the secret. Callers that only need the summary\n * (`id`/`idpProvider`/`displayName`/`enabled`/`tierGating`) pick fields off\n * the result — no server change needed for that shaping.\n */\n async list(): Promise<Envelope<IdpConfig[]>> {\n const config = getConfig();\n return requestWith<IdpConfig[]>(config, {\n method: \"GET\",\n path: identityProvidersPath(config),\n });\n },\n\n /**\n * Test connectivity to a configured provider by id — a NO-SECRET reachability + metadata-validity probe\n * (no-secret): the gateway resolve-and-pins the issuer/metadata endpoint (SSRF-fenced) and\n * validates it structurally, WITHOUT decrypting or transmitting the stored client secret. Returns\n * `{ok, reason?}`; the `reason` is a closed machine code, never the upstream body or any credential.\n * PROJECT_ADMIN required (server-enforced) — it triggers live external egress on the platform's behalf.\n */\n /**\n * Update a provider config by id (PATCH; PROJECT_ADMIN, server-enforced). Partial: only supplied fields\n * change; `idpProvider`/`tierGating` are immutable. Returns the updated config (secrets never included).\n * GS-F7: write directory-sync settings here and read their status back off `list()`. See `PatchIdpInput`\n * for the metadataUrl-change-clears-secret rule.\n */\n async update(configId: string, patch: PatchIdpInput): Promise<Envelope<IdpConfig>> {\n const config = getConfig();\n return requestWith<IdpConfig>(config, {\n method: \"PATCH\",\n path: `${identityProvidersPath(config)}/${encodeURIComponent(configId)}`,\n body: patch,\n });\n },\n\n async testProvider(configId: string): Promise<Envelope<FederationTestProviderResult>> {\n const config = getConfig();\n return requestWith<FederationTestProviderResult>(config, {\n method: \"POST\",\n path: `${identityProvidersPath(config)}/${encodeURIComponent(configId)}/test`,\n });\n },\n };\n}\n\n/** Singleton bound to the module-level `configureGate()` config. */\nexport const identityProviders = makeIdentityProviders(getGateConfig);\n\nexport type IdentityProvidersNamespace = ReturnType<typeof makeIdentityProviders>;\n","/**\n * `gate.magicLinkRedirects` sub-namespace — the per-project magic-link\n * redirect-URL allowlist, PROJECT_ADMIN+ managed. Blessed shape (\n * 2026-07-16): `list()`/`add()`/`remove()`, mirroring the\n * `gate.idpProviders.*` sub-namespace convention (gate-admin surface,\n * not the end-user-facing `passwordless.*` send/verify flow).\n *\n * Wraps the live gateway routes (`services/gateway/src/routes/\n * gate-magic-link-redirects.ts`):\n *\n * GET /@<projectSlug>/gate/magic-link-redirects\n * POST /@<projectSlug>/gate/magic-link-redirects { redirectUrl, isDefault? }\n * DELETE /@<projectSlug>/gate/magic-link-redirects/:id\n *\n * One deliberate deviation from the shorthand in the original blessing\n * relay (\"remove(redirectUrl)\"): the shipped route deletes by the\n * redirect row's `id`, not by re-presenting its `redirectUrl` value —\n * verified directly against the route, not assumed from the relay's\n * informal shape sketch. `remove(id)` matches what the gateway actually\n * does; a `redirectUrl`-keyed removal would need an extra `list()`\n * round-trip to resolve the id first, which the SDK should not do\n * silently on the caller's behalf.\n *\n * Auth: operator JWT with PROJECT_ADMIN+ (or ORG_ADMIN/ORG_OWNER) role —\n * the gateway rejects end-user JWTs and lower-role operators alike\n * (K9O_RBAC_DENY). The allowlist is a security control: registering a\n * redirect target is an admin act, same posture as `idpProviders.*`.\n */\n\nimport { getGateConfig, type GateConfig } from \"./mode.ts\";\nimport { requestWith } from \"./client.ts\";\nimport { resolveProjectPathRoot } from \"./paths.ts\";\nimport type { Envelope } from \"./types.ts\";\n\n/** A single allowlisted magic-link redirect target. */\nexport type MagicLinkRedirect = {\n id: string;\n redirectUrl: string;\n isDefault: boolean;\n createdAt: string;\n};\n\nexport type AddMagicLinkRedirectInput = {\n redirectUrl: string;\n /**\n * When true, this target becomes the default the gateway falls back to\n * when a `requestMagicLink` call doesn't specify one. The gateway\n * atomically demotes any previously-default entry — never two\n * simultaneous defaults.\n */\n isDefault?: boolean;\n};\n\nexport type RemoveMagicLinkRedirectResult = { removed: true; id: string };\n\nfunction magicLinkRedirectsPath(config: GateConfig, id?: string): string {\n const root = `${resolveProjectPathRoot(config)}/gate/magic-link-redirects`;\n return id !== undefined ? `${root}/${id}` : root;\n}\n\n/** Factory — produces a `magicLinkRedirects` namespace bound to the\n * supplied config getter. Used by both the `gate` singleton +\n * `createGateClient`. */\nexport function makeMagicLinkRedirects(getConfig: () => GateConfig) {\n return {\n /** List every allowlisted redirect target for the active project. */\n async list(): Promise<Envelope<{ redirects: MagicLinkRedirect[] }>> {\n const config = getConfig();\n return requestWith<{ redirects: MagicLinkRedirect[] }>(config, {\n method: \"GET\",\n path: magicLinkRedirectsPath(config),\n });\n },\n\n /**\n * Register a new redirect target. `redirectUrl` must exact-match\n * what a customer later passes to `requestMagicLink` — the gateway\n * enforces exact match, never a prefix or wildcard.\n */\n async add(input: AddMagicLinkRedirectInput): Promise<Envelope<{ redirect: MagicLinkRedirect }>> {\n const config = getConfig();\n return requestWith<{ redirect: MagicLinkRedirect }>(config, {\n method: \"POST\",\n path: magicLinkRedirectsPath(config),\n body: input,\n });\n },\n\n /** Remove a previously-registered redirect target by its `id`. */\n async remove(id: string): Promise<Envelope<RemoveMagicLinkRedirectResult>> {\n const config = getConfig();\n return requestWith<RemoveMagicLinkRedirectResult>(config, {\n method: \"DELETE\",\n path: magicLinkRedirectsPath(config, id),\n });\n },\n };\n}\n\n/** Singleton bound to the module-level `configureGate()` config. */\nexport const magicLinkRedirects = makeMagicLinkRedirects(getGateConfig);\n\nexport type MagicLinkRedirectsNamespace = ReturnType<typeof makeMagicLinkRedirects>;\n","/**\n * Marquee `gate.*` namespace per ch01 §12.1.\n *\n * v0.1.0 surfaces (canonical v2.5.68 slug routing — `<orgSlug>.api.k9o.io/@<projectSlug>/...`):\n * - `gate.signUp` — POST /@<projectSlug>/gate/signup\n * - `gate.signIn` — POST /@<projectSlug>/gate/signin\n * - `gate.signOut` — POST /@<projectSlug>/gate/signout\n * - `gate.whoami` — GET /@<projectSlug>/gate/whoami\n * - `gate.refreshToken` — POST /@<projectSlug>/gate/refresh\n * - `gate.verifySession` — GET /@<projectSlug>/gate/verify\n * - `gate.hosted` — GET /@<projectSlug>/gate/hosted (unauthenticated; `{ signInUrl, rpId }`)\n *\n * Sub-namespaces (passwordless / passkeys / social / mfa / password /\n * users / email / sms / federation / admin) deferred to subsequent\n * cycles per the README catalogue.\n *\n * Wire shape: every function returns the standard envelope\n * `{ data, meta }` per ch01 §1 and throws a `GateError` on non-2xx\n * gateway responses. Native vs Standalone mode does not affect SDK\n * ergonomics per ch01 §12.4; the mode header is set internally by the\n * client + does not surface in the function-call ergonomics.\n *\n * Endpoint paths are reconciled to the LIVE gateway routes (verified\n * against staging): signup/signin/signout/whoami/refresh/verify under\n * `/@<projectSlug>/gate/*`, OTP under `/gate/otp/{request,verify}`,\n * Magic Link under `/gate/magic-link/{send,redeem}`, IdP under\n * `/gate/idp/:provider/{authorize,callback}`.\n */\n\nimport { getGateConfig, type GateConfig } from \"./mode.ts\";\nimport { requestWith } from \"./client.ts\";\nimport { resolveProjectPathRoot } from \"./paths.ts\";\nimport { makeIdentityProviders } from \"./idp-providers/identity-providers.ts\";\nimport { makeMagicLinkRedirects } from \"./magic-link-redirects.ts\";\nimport type {\n AuthResult,\n Envelope,\n HostedGateInfo,\n MagicLinkCallbackResult,\n RefreshTokenInput,\n RequestMagicLinkInput,\n RequestOtpInput,\n SignInCallbackResult,\n SignInInput,\n SignOutInput,\n SignUpInput,\n VerifyMagicLinkInput,\n VerifyOtpInput,\n VerifySessionResult,\n WhoamiResult,\n} from \"./types.ts\";\n\nfunction gatePath(config: GateConfig, suffix: string): string {\n return `${resolveProjectPathRoot(config)}/gate/${suffix}`;\n}\n\n/** Factory — produces a `gate.*` namespace bound to the supplied config getter.\n * Used by both the module-level `gate` singleton (config from `configureGate`)\n * and `createGateClient(config)` (config supplied at construction time). */\nexport function makeGateNamespace(getConfig: () => GateConfig) {\n return {\n /**\n * Create a new end-user identity for the configured project. Returns\n * the new user record + an initial session per ch01 §12.2. Emits a\n * `data.write.auth.signup` audit event per ch01 §12.5.\n *\n * Errors (closed taxonomy per ch01 §2):\n * - `EMAIL_ALREADY_REGISTERED` (409) when the email collides\n * - `INVALID_FIELD_VALUE` (400) on schema violation (password policy, malformed email)\n * - `TIER_FEATURE_GATED` (403) when the project's tier blocks email/password\n */\n async signUp(input: SignUpInput): Promise<Envelope<AuthResult>> {\n return requestWith<AuthResult>(getConfig(), {\n method: \"POST\",\n path: gatePath(getConfig(), \"signup\"),\n body: input,\n });\n },\n\n /**\n * Sign in an existing end-user with email + password. Returns the\n * user record + a fresh session. Emits a `data.write.auth.signin`\n * audit event.\n *\n * Errors:\n * - `INVALID_CREDENTIALS` (401) on bad email/password (ambiguous on\n * purpose — does not distinguish \"user not found\" vs \"password\n * wrong\" to avoid enumeration)\n */\n async signIn(input: SignInInput): Promise<Envelope<AuthResult>> {\n return requestWith<AuthResult>(getConfig(), {\n method: \"POST\",\n path: gatePath(getConfig(), \"signin\"),\n body: input,\n });\n },\n\n /**\n * Sign out — revoke the bound refresh token (or an explicit one, if\n * supplied). Emits a `data.write.auth.signout` audit event.\n *\n * Errors:\n * - `UNAUTHENTICATED` (401) when no session is present\n * - `REFRESH_TOKEN_INVALID` (400) when the supplied refresh token\n * doesn't match the bearer's session\n */\n async signOut(input: SignOutInput = {}): Promise<Envelope<{ revoked: boolean }>> {\n return requestWith<{ revoked: boolean }>(getConfig(), {\n method: \"POST\",\n path: gatePath(getConfig(), \"signout\"),\n body: input,\n });\n },\n\n /**\n * Return the caller's identity claims from the bearer JWT. Useful in\n * Standalone mode where the SDK consumer wants to surface the user's\n * roles / groups without parsing the JWT itself.\n *\n * Errors:\n * - `UNAUTHENTICATED` (401) when no bearer is present\n * - `SESSION_EXPIRED` (401) when the bearer's exp has passed\n */\n async whoami(): Promise<Envelope<WhoamiResult>> {\n return requestWith<WhoamiResult>(getConfig(), {\n method: \"GET\",\n path: gatePath(getConfig(), \"whoami\"),\n });\n },\n\n /**\n * Rotate the access token using a refresh token. Returns a fresh\n * session pair (new access token + same refresh token; gateway may\n * also rotate the refresh token per project policy).\n *\n * Errors:\n * - `REFRESH_TOKEN_INVALID` (400) when the refresh token is unknown,\n * expired, or revoked\n */\n async refreshToken(input: RefreshTokenInput): Promise<Envelope<{ session: AuthResult[\"session\"] }>> {\n return requestWith<{ session: AuthResult[\"session\"] }>(getConfig(), {\n method: \"POST\",\n path: gatePath(getConfig(), \"refresh\"),\n body: input,\n });\n },\n\n /**\n * Check whether the bearer's session is currently valid. Does NOT\n * throw on expiry or invalid token — returns `{ valid: false }`\n * inside the envelope. The intended use is health-checking a session\n * without surfacing an error to the end-user (the SDK consumer\n * typically wants to redirect to sign-in rather than surface a 401).\n */\n async verifySession(): Promise<Envelope<VerifySessionResult>> {\n return requestWith<VerifySessionResult>(getConfig(), {\n method: \"GET\",\n path: gatePath(getConfig(), \"verify\"),\n });\n },\n\n /**\n * The project's hosted sign-in page URL and WebAuthn `rpId`. Unauthenticated and cacheable; no credential is sent,\n * even when a partner token is configured. Take both values as given: an unknown org, a deleted project and a\n * non-Gate project all answer the same `PROJECT_NOT_FOUND`, so a failure here says nothing about which.\n */\n async hosted(): Promise<Envelope<HostedGateInfo>> {\n return requestWith<HostedGateInfo>(getConfig(), {\n method: \"GET\",\n path: gatePath(getConfig(), \"hosted\"),\n bearerToken: \"\",\n });\n },\n\n /**\n * Initiate an external-IdP sign-in flow (Google / GitHub / Microsoft).\n * The SDK POSTs to the gateway's authorize endpoint, receives the\n * provider's authorization URL with a signed-state JWT (carrying PKCE\n * verifier + the supplied `redirectUri`), and redirects the browser.\n *\n * After the user authenticates with the IdP, the gateway processes the\n * callback and redirects the browser back to `redirectUri` with session\n * tokens in the URL fragment. Use `parseSignInCallback()` on the\n * destination page to extract them.\n *\n * Example:\n * await gate.signInWithProvider({\n * provider: 'google',\n * redirectUri: 'https://app.example.com/auth/callback',\n * });\n * // …user lands at /auth/callback#access_token=…&refresh_token=…\n *\n * Generic OIDC requires authorization-endpoint discovery and is not\n * yet wired; use a hosted provider (google/github/microsoft) for now.\n */\n async signInWithProvider(input: {\n provider: \"google\" | \"github\" | \"microsoft\" | \"oidc\" | \"saml\";\n redirectUri: string;\n }): Promise<void> {\n const env = await requestWith<{ authorizationUrl: string }>(getConfig(), {\n method: \"POST\",\n path: `${resolveProjectPathRoot(getConfig())}/gate/idp/${input.provider}/authorize`,\n body: { redirectUri: input.redirectUri },\n });\n const w = (globalThis as { window?: { location: { assign: (url: string) => void } } }).window;\n if (w !== undefined) {\n w.location.assign(env.data.authorizationUrl);\n } else {\n throw new Error(\n \"signInWithProvider requires a browser environment (window.location); for server-side initiation, fetch the authorize endpoint directly.\",\n );\n }\n },\n\n /**\n * Parse session tokens out of the URL fragment delivered by an\n * IdP callback redirect. Returns the session payload on success or an\n * error object on failure. Pass `window.location.hash` (or any\n * `#`-prefixed string) — leading `#` is stripped automatically.\n *\n * Side-effect-free: does not call the gateway and does not mutate\n * any session storage. The consumer is responsible for persisting\n * the returned tokens (e.g. via the app's auth context).\n */\n parseSignInCallback(hash: string): SignInCallbackResult {\n const raw = hash.startsWith(\"#\") ? hash.slice(1) : hash;\n const params = new URLSearchParams(raw);\n const error = params.get(\"error\");\n if (error !== null) {\n return {\n ok: false,\n error,\n errorDescription: params.get(\"error_description\") ?? error,\n };\n }\n const accessToken = params.get(\"access_token\");\n const refreshToken = params.get(\"refresh_token\");\n const expiresAt = params.get(\"expires_at\");\n if (accessToken === null || refreshToken === null || expiresAt === null) {\n return {\n ok: false,\n error: \"malformed_callback\",\n errorDescription:\n \"Callback URL fragment missing one or more required fields (access_token, refresh_token, expires_at).\",\n };\n }\n return {\n ok: true,\n accessToken,\n refreshToken,\n expiresAt,\n tokenType: params.get(\"token_type\") ?? \"Bearer\",\n };\n },\n\n /**\n * Passwordless sub-namespace per ch01 §12.1 — OTP + Magic Link flows\n * for email-based authentication without a stored password.\n *\n * Wire endpoints (LIVE on the Gate end-user plane — verified against\n * staging): OTP → `POST /@<projectSlug>/gate/otp/{request,verify}`\n * (routes/otp.ts), Magic Link → `POST /@<projectSlug>/gate/magic-link/\n * {send,redeem}` (routes/magic-link.ts). NOTE: these are the gateway's\n * canonical paths; the SDK ergonomic method names (`requestMagicLink`/\n * `verifyMagicLink`) do not mirror the path segments (`send`/`redeem`).\n * (The earlier `passwordless/*` SDK paths were interface-first scaffold\n * that never matched the shipped gateway routes — reconciled here.)\n */\n passwordless: {\n /**\n * Request a one-time passcode delivered to the supplied email.\n * The OTP is short-lived (typically 5 min per ch03 §3.4) and\n * single-use; redemption via `verifyOtp` mints a session.\n */\n async requestOtp(input: RequestOtpInput): Promise<Envelope<{ requested: boolean }>> {\n return requestWith<{ requested: boolean }>(getConfig(), {\n method: \"POST\",\n path: gatePath(getConfig(), \"otp/request\"),\n body: input,\n });\n },\n\n /**\n * Verify a one-time passcode previously sent via `requestOtp`.\n * On success returns the same `AuthResult` shape as `signIn` —\n * a fresh session bound to the end-user identity.\n *\n * Errors:\n * - INVALID_CREDENTIALS (401) on wrong / expired / consumed OTP\n * (ambiguous on purpose — enumeration defense)\n */\n async verifyOtp(input: VerifyOtpInput): Promise<Envelope<AuthResult>> {\n return requestWith<AuthResult>(getConfig(), {\n method: \"POST\",\n path: gatePath(getConfig(), \"otp/verify\"),\n body: input,\n });\n },\n\n /**\n * Request a magic-link emailed to the supplied address. The link\n * carries an opaque short-lived token (typically 15 min per ch03\n * §3.3); clicking the link lands at the customer's chosen\n * redirect URL with the token in the query string, which the\n * customer's frontend then exchanges via `verifyMagicLink`.\n */\n async requestMagicLink(\n input: RequestMagicLinkInput,\n ): Promise<Envelope<{ requested: boolean }>> {\n return requestWith<{ requested: boolean }>(getConfig(), {\n method: \"POST\",\n path: gatePath(getConfig(), \"magic-link/send\"),\n body: input,\n });\n },\n\n /**\n * Verify a magic-link token previously delivered via\n * `requestMagicLink`. On success returns the same `AuthResult`\n * shape as `signIn`.\n *\n * Errors:\n * - INVALID_CREDENTIALS (401) on wrong / expired / consumed\n * token (same enumeration-defense rationale as verifyOtp)\n */\n async verifyMagicLink(input: VerifyMagicLinkInput): Promise<Envelope<AuthResult>> {\n return requestWith<AuthResult>(getConfig(), {\n method: \"POST\",\n path: gatePath(getConfig(), \"magic-link/redeem\"),\n body: input,\n });\n },\n\n /**\n * Extract the opaque magic-link token from the landing-page URL's query\n * string (the gateway redirects to `<configured-redirect>?token=…&project=…`).\n * Pure + side-effect-free — pass `window.location.search`. Feed the\n * returned `token` to `verifyMagicLink({ token })` to mint the session.\n *\n * This is the magic-link counterpart to `parseSignInCallback` (which parses\n * the SSO *hash fragment* for session tokens) — magic-link delivers an\n * opaque token in the *query string*, so the two are not interchangeable.\n */\n parseMagicLinkCallback(search: string): MagicLinkCallbackResult {\n const raw = search.startsWith(\"?\") ? search.slice(1) : search;\n const params = new URLSearchParams(raw);\n const error = params.get(\"error\");\n if (error !== null) {\n return { ok: false, error };\n }\n const token = params.get(\"token\");\n if (token === null || token.length === 0) {\n return { ok: false, error: \"missing_token\" };\n }\n return { ok: true, token };\n },\n },\n\n /**\n * Project-scoped end-user IdP provider-config management (ch04 §1.6).\n * PROJECT_ADMIN+ required; end-user JWTs rejected at the route. Only\n * `create()` is bound — the REST create the Gate Studio\n * `FederationPresetsPanel` routes to when it drops the `gate_flows`\n * `__addFromPreset` sentinel. Gate-admin surface.\n */\n identityProviders: makeIdentityProviders(getConfig),\n\n /**\n * Per-project magic-link redirect-URL allowlist management (blessed\n * shape). Gate-admin surface alongside `identityProviders`\n * — PROJECT_ADMIN+ required, distinct from the end-user-facing\n * `passwordless.requestMagicLink`/`verifyMagicLink` send/verify flow.\n */\n magicLinkRedirects: makeMagicLinkRedirects(getConfig),\n };\n}\n\n/** Singleton bound to the module-level `configureGate()` config.\n * v0.4.0+: per-instance clients via `createGateClient(config)`. */\nexport const gate = makeGateNamespace(getGateConfig);\n\n/** Convenience type alias mirroring the `gate` runtime export.\n * Identical shape as the per-instance `GateClient` returned from\n * `createGateClient`. */\nexport type GateNamespace = ReturnType<typeof makeGateNamespace>;\n","/**\n * `createGateClient(config)` — per-instance Gate SDK factory (v0.4.0).\n *\n * Returns a self-contained `GateClient` that does not depend on the\n * module-level `configureGate()` singleton. Multiple clients can coexist\n * in the same process, each scoped to its own project + baseUrl + mode —\n * useful for multi-tenant Backends-For-Frontends (one client per\n * incoming request's resolved tenant) or for apps that maintain both a\n * Standalone and Native binding against the same SDK instance.\n *\n * Per v2.5.68 URL grammar, supply `projectSlug` for\n * canonical `/@<slug>/gate/...` routing. Slug is the only customer-facing\n * project addressing form — project ids are internal-only\n * (`feedback_slug_addressing_v2_5_68`); the retired `/v2/projects/...`\n * mounts were dropped at pass17 (`feedback_v2_path_is_an_error`).\n *\n * Example:\n * const client = createGateClient({\n * baseUrl: 'https://formelo.api.k9o.io',\n * projectSlug: 'partner-console-staging',\n * mode: 'native',\n * });\n * await client.signIn({ email, password });\n */\n\nimport { makeGateNamespace } from \"./gate.ts\";\nimport { validateGateConfig, type GateConfig } from \"./mode.ts\";\n\n/** Shape of the per-instance client returned by `createGateClient`. */\nexport type GateClient = ReturnType<typeof makeGateNamespace>;\n\n/** Construct a Gate client bound to the supplied config. The config is\n * validated up-front and stored on the instance; subsequent calls do not\n * re-read process-level state. Throws `TypeError` on invalid config. */\nexport function createGateClient(config: GateConfig): GateClient {\n validateGateConfig(config);\n const frozen: GateConfig = { ...config };\n return makeGateNamespace(() => frozen);\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/region.ts","../src/client.ts","../src/paths.ts","../src/idp-providers/identity-providers.ts","../src/magic-link-redirects.ts","../src/gate.ts","../src/client-factory.ts"],"sourcesContent":["/**\n * Regional host resolution.\n *\n * The region-free org host `<org>.api[-staging].k9o.io` redirects GET/HEAD only and REFUSES a request that carries a\n * body, an `Authorization` header or a cookie (`400 K9O_INVALID_REQUEST_SHAPE`, `region_free_host_takes_no_credentials`),\n * so credentials must go to `<org>.api[-staging]-<rgn>.k9o.io`. An SDK learns the region with ONE unauthenticated\n * `HEAD https://<org>.api[-staging].k9o.io/`, takes the `308` `Location`, and caches the regional origin.\n *\n * Properties this module holds:\n * - Only a region-free org host (shape `<org>.api.k9o.io` or `<org>.api-staging.k9o.io`) is ever probed. A regional,\n * custom, self-hosted or dev baseUrl is used exactly as given.\n * - The probe carries NO credential and none of the configured default headers (they may hold one).\n * - The `Location` is trusted only if it is `https`, has no userinfo/port, and its host is exactly the SAME org on\n * the SAME apex family with a 4-character region code. Anything else is ignored and the configured baseUrl stays,\n * so a hostile redirect can never receive a credential.\n * - Not a redirect (the platform has not cut over, or the host answers directly), or any failure: the configured\n * baseUrl stays and the server's own refusal, if any, is what the caller sees. A definite answer is cached for the\n * life of the client's config; a network failure is not cached.\n */\n\n/** The slice of a client config this module needs (structural, so core and gate share one implementation). */\nexport type RegionConfig = { baseUrl: string; fetch?: typeof globalThis.fetch | undefined };\n\nconst REGION_FREE_ORG_HOST = /^([a-z][a-z0-9-]{1,62})\\.(api|api-staging)\\.k9o\\.io$/;\nconst REGION_CODE = /^[a-z]{2}\\d{2}$/;\n\nconst cache = new WeakMap<RegionConfig, Promise<string>>();\n\n/** The regional origin a `Location` may name for `host`, or null if it is not exactly that. Pure. */\nexport function regionalOriginFromLocation(host: string, location: string, base: string): string | null {\n const m = REGION_FREE_ORG_HOST.exec(host);\n if (m === null) return null;\n let u: URL;\n try {\n u = new URL(location, base);\n } catch {\n return null;\n }\n if (u.protocol !== \"https:\" || u.username !== \"\" || u.password !== \"\" || u.port !== \"\") return null;\n const [, org, apex] = m;\n const want = new RegExp(`^${org}\\\\.${apex}-([a-z0-9]+)\\\\.k9o\\\\.io$`);\n const r = want.exec(u.hostname);\n if (r === null || !REGION_CODE.test(r[1]!)) return null;\n return `https://${u.hostname}`;\n}\n\nasync function probe(config: RegionConfig): Promise<{ origin: string; cacheable: boolean }> {\n const fetchImpl = config.fetch ?? globalThis.fetch;\n const configured = config.baseUrl;\n if (typeof fetchImpl !== \"function\") return { origin: configured, cacheable: false };\n let host: string;\n try {\n host = new URL(configured).hostname;\n } catch {\n return { origin: configured, cacheable: true };\n }\n if (!REGION_FREE_ORG_HOST.test(host)) return { origin: configured, cacheable: true };\n try {\n const res = await fetchImpl(`${configured.replace(/\\/+$/, \"\")}/`, {\n method: \"HEAD\",\n redirect: \"manual\",\n headers: { accept: \"*/*\" },\n } as Parameters<typeof fetchImpl>[1]);\n if (res.status === 308) {\n const loc = res.headers.get(\"location\");\n const origin = loc === null ? null : regionalOriginFromLocation(host, loc, configured);\n if (origin !== null) return { origin, cacheable: true };\n }\n return { origin: configured, cacheable: true };\n } catch {\n return { origin: configured, cacheable: false };\n }\n}\n\n/** The origin requests for this client go to: the cached regional origin once learned, else the configured baseUrl. */\nexport function resolveRegionalOrigin(config: RegionConfig): Promise<string> {\n const hit = cache.get(config);\n if (hit !== undefined) return hit;\n const p = probe(config).then((r) => {\n if (!r.cacheable) cache.delete(config);\n return r.origin;\n });\n cache.set(config, p);\n return p;\n}\n","/**\n * HTTP client abstraction for the Gate SDK.\n *\n * Centralises:\n * - URL construction against the configured `baseUrl` + per-call path\n * - the standard envelope unwrap on success\n * - the standard `KError` rehydration on failure via `gateErrorFromWire`\n * - mode-routing via the `X-Gate-Mode` header (so the gateway routes\n * consumption to the right billing surface without per-function\n * branching at the SDK)\n *\n * Customers don't reach for this directly; it's the internal client\n * every `gate.*` function consumes. `request`/`requestWith` are\n * module-internal only — neither `index.ts` nor the `package.json`\n * `exports` map re-exports them, so they are not part of the public\n * `@k9o/gate` surface. In-package `gate.*` bindings and the test suite\n * import them from this module directly.\n */\n\nimport { getGateConfig, type GateConfig } from \"./mode.ts\";\nimport { resolveRegionalOrigin } from \"./region.ts\";\nimport { GateError, type KErrorWire, gateErrorFromWire } from \"./errors.ts\";\nimport type { Envelope } from \"./types.ts\";\n\nexport type RequestInit = {\n method: \"GET\" | \"POST\" | \"PUT\" | \"PATCH\" | \"DELETE\";\n path: string;\n /** Optional JSON body — serialised by the client. */\n body?: unknown;\n /** Optional per-call bearer token. Falls back to `partnerToken` from config when omitted. */\n bearerToken?: string;\n /** Optional additional headers; merged with the defaults. */\n headers?: Record<string, string>;\n /** Optional query parameters. */\n query?: Record<string, string | number | boolean | undefined>;\n};\n\n/**\n * Issue a request against the Gate API. Returns the unwrapped envelope\n * data on 2xx; throws a `GateError` carrying the closed-taxonomy `code`\n * + structured `context` on non-2xx.\n *\n * Implementation notes:\n * - The function pulls config lazily so `configureGate()` order doesn't\n * have to predate import-time module evaluation.\n * - `fetch` is taken from config when set, else from the global; this\n * keeps the SDK portable across Bun + Node 18+ + browsers + test\n * fixtures that supply a stub.\n * - JSON parsing of non-JSON responses throws — gateway always returns\n * JSON +, so a parse failure is an upstream defect.\n */\nexport async function request<T>(init: RequestInit): Promise<Envelope<T>> {\n return requestWith<T>(getGateConfig(), init);\n}\n\n/**\n * Issue a request using an explicit config (used by per-instance clients\n * returned from `createGateClient`). Singleton callers reach for\n * `request()` which delegates here against the module-level config.\n */\nexport async function requestWith<T>(\n config: GateConfig,\n init: RequestInit,\n): Promise<Envelope<T>> {\n const fetchImpl = config.fetch ?? globalThis.fetch;\n if (typeof fetchImpl !== \"function\") {\n throw new GateError({\n code: \"INTERNAL_ERROR\",\n message:\n \"No fetch implementation available — configure {fetch} in configureGate() for non-browser, non-Bun, non-Node-18+ environments\",\n context: {},\n recommendation: null,\n });\n }\n\n const url = buildUrl(await resolveRegionalOrigin(config), init.path, init.query);\n const headers: Record<string, string> = {\n accept: \"application/json\",\n \"x-gate-mode\": config.mode,\n ...(init.headers ?? {}),\n };\n const bearer = init.bearerToken ?? config.partnerToken;\n if (typeof bearer === \"string\" && bearer.length > 0) {\n headers[\"authorization\"] = `Bearer ${bearer}`;\n }\n const requestInit: Record<string, unknown> = {\n method: init.method,\n headers,\n };\n if (init.body !== undefined) {\n headers[\"content-type\"] = \"application/json\";\n requestInit[\"body\"] = JSON.stringify(init.body);\n }\n\n let response: Response;\n try {\n response = await fetchImpl(url, requestInit as Parameters<typeof fetchImpl>[1]);\n } catch (e) {\n throw new GateError({\n code: \"UPSTREAM_UNAVAILABLE\",\n message: `Network error reaching ${url}: ${e instanceof Error ? e.message : String(e)}`,\n context: { url },\n recommendation: null,\n });\n }\n\n let parsed: unknown;\n try {\n parsed = await response.json();\n } catch (e) {\n throw new GateError({\n code: \"INTERNAL_ERROR\",\n message: `Failed to parse JSON response from ${url}: ${e instanceof Error ? e.message : String(e)}`,\n context: { url, status: response.status },\n recommendation: null,\n status: response.status,\n });\n }\n\n if (response.status >= 200 && response.status < 300) {\n if (!isEnvelope<T>(parsed)) {\n throw new GateError({\n code: \"INTERNAL_ERROR\",\n message: `Malformed envelope from ${url}: expected {data, meta}`,\n context: { url, status: response.status },\n recommendation: null,\n status: response.status,\n });\n }\n return parsed;\n }\n\n // Non-2xx: the gateway returns a KError-shaped JSON payload.\n if (isKErrorWire(parsed)) {\n throw gateErrorFromWire(parsed, response.status);\n }\n throw new GateError({\n code: \"INTERNAL_ERROR\",\n message: `Upstream returned ${response.status} with non-KError body`,\n context: { url, status: response.status, body: parsed },\n recommendation: null,\n status: response.status,\n });\n}\n\nfunction buildUrl(\n baseUrl: string,\n path: string,\n query: RequestInit[\"query\"],\n): string {\n const trimmedBase = baseUrl.endsWith(\"/\") ? baseUrl.slice(0, -1) : baseUrl;\n const trimmedPath = path.startsWith(\"/\") ? path : `/${path}`;\n let url = `${trimmedBase}${trimmedPath}`;\n if (query !== undefined) {\n const params = new URLSearchParams();\n for (const [k, v] of Object.entries(query)) {\n if (v !== undefined) params.set(k, String(v));\n }\n const qs = params.toString();\n if (qs.length > 0) url += `?${qs}`;\n }\n return url;\n}\n\nfunction isEnvelope<T>(value: unknown): value is Envelope<T> {\n if (value === null || typeof value !== \"object\") return false;\n const v = value as { data?: unknown; meta?: unknown };\n return \"data\" in v && \"meta\" in v && typeof v.meta === \"object\" && v.meta !== null;\n}\n\nfunction isKErrorWire(value: unknown): value is KErrorWire {\n if (value === null || typeof value !== \"object\") return false;\n const v = value as { code?: unknown; message?: unknown };\n return typeof v.code === \"string\" && typeof v.message === \"string\";\n}\n","/**\n * Shared path-resolution helper for project-scoped routes.\n *\n * URL grammar: project-scoped resources address\n * via `/@<projectSlug>/...` against the org subdomain. Slug addressing is\n * the only customer-facing form — project ids are internal-only and never\n * appear in customer paths (`feedback_slug_addressing_v2_5_68`); the\n * retired `/v2/projects/<bareId>/...` form was dropped at pass17\n * (`feedback_v2_path_is_an_error`).\n *\n * Caller is expected to validate that the config carries a `projectSlug`\n * at construction time (see `validateGateConfig`).\n */\n\nimport type { GateConfig } from \"./mode.ts\";\n\n/** Return the project-scoped path root for the supplied config, e.g.\n * `/@partner-console-staging`. */\nexport function resolveProjectPathRoot(config: GateConfig): string {\n return `/@${config.projectSlug}`;\n}\n","/**\n * `gate.identityProviders` namespace — project-scoped end-user IdP\n * provider-config management.\n *\n * Wraps the live gateway route family shipped in\n * `services/gateway/src/routes/project-identity-providers.ts`, mounted on\n * the project plane (`<orgSlug>.api.k9o.io/@<projectSlug>/identity-providers`):\n *\n * POST /@<projectSlug>/identity-providers — create (PROJECT_ADMIN)\n * GET /@<projectSlug>/identity-providers — list (READ_ROLES)\n * PATCH /@<projectSlug>/identity-providers/:epcId — update (PROJECT_ADMIN)\n * POST /@<projectSlug>/identity-providers/:epcId/test — testProvider (PROJECT_ADMIN)\n *\n * `create()` + `list()` back the Gate Studio `FederationPresetsPanel`; `update()` backs its\n * `DirectorySyncPanel` (directory-sync settings are read off `list()` and written via `update()`, replacing\n * the unanswered `federation.getDirectorySync`/`updateDirectorySync` bridge messages). Read-one/delete stay\n * unbound until a caller needs them (the SDK does not speculatively surface routes).\n *\n * Auth: operator JWT with PROJECT_ADMIN+ role, server-enforced by the\n * gateway RBAC layer (`requireProjectAdmin`); end-user JWTs (`eu_`-prefixed\n * subjects) are rejected at the route. The bearer is taken from\n * `partnerToken` in config, or per-call via the client's bearer plumbing.\n *\n * 🔴 `clientSecretRef` is a RAW SECRET on the way in, not a reference.\n * Post-v105 the gateway encrypts the submitted value (AES-256-GCM under\n * `K9O_SECRET_STORAGE_MASTER_KEY`) into the opaque `client_secret_enc`\n * handle and persists only that; the field name is retained for wire\n * compatibility but the create handler comment\n * (`project-identity-providers.ts`, the v105 INSERT) is authoritative —\n * there is NO Kibo custody on this path. Reads NEVER carry the secret or its\n * handle: they carry only `clientSecretSet` (a presence boolean,\n * the write-only fix). `serviceAccountKey` (directory sync) follows the same write-only rule.\n */\n\nimport { getGateConfig, type GateConfig } from \"../mode.ts\";\nimport { requestWith } from \"../client.ts\";\nimport { resolveProjectPathRoot } from \"../paths.ts\";\nimport type { Envelope } from \"../types.ts\";\n\n/** Closed end-user-class provider enum — 11 values since v221 (the project-plane substrate\n * `_sys.end_user_idp_provider_configs`); the org plane carries the same 11 since v222/v223. */\nexport type IdpProviderKind =\n | \"google\" | \"github\" | \"microsoft\" | \"oidc\" | \"saml\" | \"scim\"\n | \"ldap\" | \"active_directory\" | \"apple\" | \"facebook\" | \"twitter\";\n\n/**\n * The result of a provider connectivity test (`testProvider`). `ok` is the reachability + metadata-validity\n * verdict; `reason` (present only when `!ok`) is a CLOSED-enum machine code, NEVER the upstream response body\n * or any credential material. v1 is a NO-SECRET probe: it validates the issuer/metadata\n * endpoint is reachable + structurally valid; it never transmits the stored client secret. `credentials_rejected`\n * is deliberately NOT a v1 reason — the secret is never used, so it cannot be validated (a v2 concern).\n */\nexport type FederationTestProviderResult = {\n ok: boolean;\n reason?:\n | \"endpoint_unreachable\"\n | \"endpoint_forbidden\"\n | \"metadata_invalid\"\n | \"secret_not_provisioned\"\n | \"kind_unsupported\";\n};\n\n/** Per the provider→tier pairing is structurally fixed:\n * `free_tier_ok ∈ {google, github, microsoft, apple, facebook, twitter}`,\n * `ridge_plus ∈ {oidc, saml, scim, ldap, active_directory}`. The gateway rejects a mismatched\n * pairing (`K9O_INVALID_FIELD_VALUE`); the SDK forwards the caller value\n * verbatim rather than second-guessing it. */\nexport type IdpTierGating = \"free_tier_ok\" | \"ridge_plus\";\n\n/**\n * The provider-config metadata shape returned by every read surface and\n * mutation response (`shapeIdpConfig`). Mirrors the gateway wire shape\n * one-to-one.\n *\n * Reads carry `clientSecretSet` (presence only), never the secret or a handle (see the module header).\n * `metadataUrl`/`updatedAt` are `null` until set; `attributeMap` defaults to `{}`; `allowedRedirectUris`\n * defaults to `[]`. The `directory*` fields are GS-F7 scheduled directory sync (Google only,):\n * settings plus the scheduler's last-run status; the service-account key itself is never returned.\n */\nexport type IdpConfig = {\n id: string;\n projectId: string;\n idpProvider: IdpProviderKind;\n displayName: string;\n clientId: string;\n /** Whether a client secret is stored. The secret (and any handle to it) is write-only. */\n clientSecretSet: boolean;\n redirectUri: string;\n metadataUrl: string | null;\n attributeMap: Record<string, unknown>;\n tierGating: IdpTierGating;\n enabled: boolean;\n allowedRedirectUris: string[];\n directoryPullEnabled: boolean;\n directorySyncSubject: string | null;\n directorySyncIntervalMins: number | null;\n directorySyncLastRunAt: string | null;\n directorySyncLastStatus: string | null;\n createdAt: string;\n updatedAt: string | null;\n};\n\n/** GS-F7 directory-sync write fields — accepted on create AND update (one PROJECT_ADMIN surface). */\nexport type DirectorySyncInput = {\n /** Enabling requires `idpProvider: \"google\"`, a `serviceAccountKey` (now or already stored) and an interval. */\n directoryPullEnabled?: boolean;\n directorySyncIntervalMins?: number;\n /** The raw Google service-account key JSON. Write-only: encrypted at rest server-side, never returned. */\n serviceAccountKey?: string;\n /** The Workspace admin user the service account impersonates (domain-wide delegation subject). */\n directorySyncSubject?: string;\n};\n\n/**\n * Create-request body — mirrors the gateway `CreateIdpBody` (t.Object)\n * exactly, including which fields are optional.\n */\nexport type CreateIdpInput = {\n idpProvider: IdpProviderKind;\n displayName: string;\n clientId: string;\n /**\n * The OAuth2 client secret. Sent over TLS; encrypted-at-rest server-side\n * (AES-256-GCM) and never returned. Named `clientSecretRef` for wire\n * compatibility — it is NOT a reference on the way in (see module header).\n */\n clientSecretRef: string;\n redirectUri: string;\n /** Optional; `null`/omitted for providers that don't use discovery metadata. */\n metadataUrl?: string | null;\n /** Optional claim→attribute mapping; defaults to `{}` server-side. */\n attributeMap?: Record<string, string>;\n tierGating: IdpTierGating;\n /** Defaults to `false` server-side — a provider is created disabled. */\n enabled?: boolean;\n /**\n * Customer SPA origins the SDK gate flow (`gate.signInWithProvider`) may\n * hand session tokens back to. Origin-matched + fail-closed at authorize,\n * so an empty/omitted list means SDK sign-in is disabled for this provider\n * until an origin is registered. Defaults to `[]` server-side.\n */\n allowedRedirectUris?: string[] | null;\n} & DirectorySyncInput;\n\n/**\n * Update-request body — mirrors the gateway `PatchIdpBody` exactly: every field optional; `idpProvider` and\n * `tierGating` are immutable after create. An empty patch returns the current config unchanged.\n *\n * 🔴 For `oidc`/`microsoft`, changing `metadataUrl` WITHOUT `clientSecretRef` in the same patch CLEARS the\n * stored client secret (the gateway's secret-invalidate-on-host-change control,): re-supply the\n * secret whenever the metadata URL changes.\n */\nexport type PatchIdpInput = {\n displayName?: string;\n clientId?: string;\n /** The raw client secret (write-only; see the module header). */\n clientSecretRef?: string;\n redirectUri?: string;\n metadataUrl?: string | null;\n attributeMap?: Record<string, string>;\n enabled?: boolean;\n allowedRedirectUris?: string[] | null;\n} & DirectorySyncInput;\n\nfunction identityProvidersPath(config: GateConfig): string {\n return `${resolveProjectPathRoot(config)}/identity-providers`;\n}\n\n/** Factory — produces an `identityProviders` namespace bound to the supplied\n * config getter. Used by both the `gate` singleton + `createGateClient`. */\nexport function makeIdentityProviders(getConfig: () => GateConfig) {\n return {\n /**\n * Register a new end-user IdP provider config for the configured\n * project. PROJECT_ADMIN+ required (server-enforced). The plaintext\n * client secret is encrypted-at-rest server-side; the returned config\n * carries only the opaque secret handle.\n *\n * The gateway validates the `idpProvider`→`tierGating` pairing\n * and rejects a mismatch with `K9O_INVALID_FIELD_VALUE`.\n */\n async create(input: CreateIdpInput): Promise<Envelope<IdpConfig>> {\n const config = getConfig();\n return requestWith<IdpConfig>(config, {\n method: \"POST\",\n path: identityProvidersPath(config),\n body: input,\n });\n },\n\n /**\n * List the configured end-user IdP provider configs for the project.\n * READ_ROLES-gated server-side (PROJECT_ADMIN/EDITOR/MEMBER/VIEWER/AUTHOR/\n * ANALYST) — the SAME shape + roles as the existing GET route\n * (`listProviders` returns the existing route's shape, no\n * confidentiality-narrower endpoint). Each `IdpConfig` carries the opaque\n * secret handle, never the secret. Callers that only need the summary\n * (`id`/`idpProvider`/`displayName`/`enabled`/`tierGating`) pick fields off\n * the result — no server change needed for that shaping.\n */\n async list(): Promise<Envelope<IdpConfig[]>> {\n const config = getConfig();\n return requestWith<IdpConfig[]>(config, {\n method: \"GET\",\n path: identityProvidersPath(config),\n });\n },\n\n /**\n * Test connectivity to a configured provider by id — a NO-SECRET reachability + metadata-validity probe\n * (no-secret): the gateway resolve-and-pins the issuer/metadata endpoint (SSRF-fenced) and\n * validates it structurally, WITHOUT decrypting or transmitting the stored client secret. Returns\n * `{ok, reason?}`; the `reason` is a closed machine code, never the upstream body or any credential.\n * PROJECT_ADMIN required (server-enforced) — it triggers live external egress on the platform's behalf.\n */\n /**\n * Update a provider config by id (PATCH; PROJECT_ADMIN, server-enforced). Partial: only supplied fields\n * change; `idpProvider`/`tierGating` are immutable. Returns the updated config (secrets never included).\n * GS-F7: write directory-sync settings here and read their status back off `list()`. See `PatchIdpInput`\n * for the metadataUrl-change-clears-secret rule.\n */\n async update(configId: string, patch: PatchIdpInput): Promise<Envelope<IdpConfig>> {\n const config = getConfig();\n return requestWith<IdpConfig>(config, {\n method: \"PATCH\",\n path: `${identityProvidersPath(config)}/${encodeURIComponent(configId)}`,\n body: patch,\n });\n },\n\n async testProvider(configId: string): Promise<Envelope<FederationTestProviderResult>> {\n const config = getConfig();\n return requestWith<FederationTestProviderResult>(config, {\n method: \"POST\",\n path: `${identityProvidersPath(config)}/${encodeURIComponent(configId)}/test`,\n });\n },\n };\n}\n\n/** Singleton bound to the module-level `configureGate()` config. */\nexport const identityProviders = makeIdentityProviders(getGateConfig);\n\nexport type IdentityProvidersNamespace = ReturnType<typeof makeIdentityProviders>;\n","/**\n * `gate.magicLinkRedirects` sub-namespace — the per-project magic-link\n * redirect-URL allowlist, PROJECT_ADMIN+ managed. Blessed shape (\n * 2026-07-16): `list()`/`add()`/`remove()`, mirroring the\n * `gate.idpProviders.*` sub-namespace convention (gate-admin surface,\n * not the end-user-facing `passwordless.*` send/verify flow).\n *\n * Wraps the live gateway routes (`services/gateway/src/routes/\n * gate-magic-link-redirects.ts`):\n *\n * GET /@<projectSlug>/gate/magic-link-redirects\n * POST /@<projectSlug>/gate/magic-link-redirects { redirectUrl, isDefault? }\n * DELETE /@<projectSlug>/gate/magic-link-redirects/:id\n *\n * One deliberate deviation from the shorthand in the original blessing\n * relay (\"remove(redirectUrl)\"): the shipped route deletes by the\n * redirect row's `id`, not by re-presenting its `redirectUrl` value —\n * verified directly against the route, not assumed from the relay's\n * informal shape sketch. `remove(id)` matches what the gateway actually\n * does; a `redirectUrl`-keyed removal would need an extra `list()`\n * round-trip to resolve the id first, which the SDK should not do\n * silently on the caller's behalf.\n *\n * Auth: operator JWT with PROJECT_ADMIN+ (or ORG_ADMIN/ORG_OWNER) role —\n * the gateway rejects end-user JWTs and lower-role operators alike\n * (K9O_RBAC_DENY). The allowlist is a security control: registering a\n * redirect target is an admin act, same posture as `idpProviders.*`.\n */\n\nimport { getGateConfig, type GateConfig } from \"./mode.ts\";\nimport { requestWith } from \"./client.ts\";\nimport { resolveProjectPathRoot } from \"./paths.ts\";\nimport type { Envelope } from \"./types.ts\";\n\n/** A single allowlisted magic-link redirect target. */\nexport type MagicLinkRedirect = {\n id: string;\n redirectUrl: string;\n isDefault: boolean;\n createdAt: string;\n};\n\nexport type AddMagicLinkRedirectInput = {\n redirectUrl: string;\n /**\n * When true, this target becomes the default the gateway falls back to\n * when a `requestMagicLink` call doesn't specify one. The gateway\n * atomically demotes any previously-default entry — never two\n * simultaneous defaults.\n */\n isDefault?: boolean;\n};\n\nexport type RemoveMagicLinkRedirectResult = { removed: true; id: string };\n\nfunction magicLinkRedirectsPath(config: GateConfig, id?: string): string {\n const root = `${resolveProjectPathRoot(config)}/gate/magic-link-redirects`;\n return id !== undefined ? `${root}/${id}` : root;\n}\n\n/** Factory — produces a `magicLinkRedirects` namespace bound to the\n * supplied config getter. Used by both the `gate` singleton +\n * `createGateClient`. */\nexport function makeMagicLinkRedirects(getConfig: () => GateConfig) {\n return {\n /** List every allowlisted redirect target for the active project. */\n async list(): Promise<Envelope<{ redirects: MagicLinkRedirect[] }>> {\n const config = getConfig();\n return requestWith<{ redirects: MagicLinkRedirect[] }>(config, {\n method: \"GET\",\n path: magicLinkRedirectsPath(config),\n });\n },\n\n /**\n * Register a new redirect target. `redirectUrl` must exact-match\n * what a customer later passes to `requestMagicLink` — the gateway\n * enforces exact match, never a prefix or wildcard.\n */\n async add(input: AddMagicLinkRedirectInput): Promise<Envelope<{ redirect: MagicLinkRedirect }>> {\n const config = getConfig();\n return requestWith<{ redirect: MagicLinkRedirect }>(config, {\n method: \"POST\",\n path: magicLinkRedirectsPath(config),\n body: input,\n });\n },\n\n /** Remove a previously-registered redirect target by its `id`. */\n async remove(id: string): Promise<Envelope<RemoveMagicLinkRedirectResult>> {\n const config = getConfig();\n return requestWith<RemoveMagicLinkRedirectResult>(config, {\n method: \"DELETE\",\n path: magicLinkRedirectsPath(config, id),\n });\n },\n };\n}\n\n/** Singleton bound to the module-level `configureGate()` config. */\nexport const magicLinkRedirects = makeMagicLinkRedirects(getGateConfig);\n\nexport type MagicLinkRedirectsNamespace = ReturnType<typeof makeMagicLinkRedirects>;\n","/**\n * Marquee `gate.*` namespace.\n *\n * surfaces (canonical slug routing — `<orgSlug>.api.k9o.io/@<projectSlug>/...`):\n * - `gate.signUp` — POST /@<projectSlug>/gate/signup\n * - `gate.signIn` — POST /@<projectSlug>/gate/signin\n * - `gate.signOut` — POST /@<projectSlug>/gate/signout\n * - `gate.whoami` — GET /@<projectSlug>/gate/whoami\n * - `gate.refreshToken` — POST /@<projectSlug>/gate/refresh\n * - `gate.verifySession` — GET /@<projectSlug>/gate/verify\n * - `gate.hosted` — GET /@<projectSlug>/gate/hosted (unauthenticated; `{ signInUrl, rpId }`)\n *\n * Sub-namespaces (passwordless / passkeys / social / mfa / password /\n * users / email / sms / federation / admin) deferred to subsequent\n * cycles per the README catalogue.\n *\n * Wire shape: every function returns the standard envelope\n * `{ data, meta }` and throws a `GateError` on non-2xx\n * gateway responses. Native vs Standalone mode does not affect SDK\n * ergonomics; the mode header is set internally by the\n * client + does not surface in the function-call ergonomics.\n *\n * Endpoint paths are reconciled to the LIVE gateway routes (verified\n * against staging): signup/signin/signout/whoami/refresh/verify under\n * `/@<projectSlug>/gate/*`, OTP under `/gate/otp/{request,verify}`,\n * Magic Link under `/gate/magic-link/{send,redeem}`, IdP under\n * `/gate/idp/:provider/{authorize,callback}`.\n */\n\nimport { getGateConfig, type GateConfig } from \"./mode.ts\";\nimport { requestWith } from \"./client.ts\";\nimport { resolveProjectPathRoot } from \"./paths.ts\";\nimport { makeIdentityProviders } from \"./idp-providers/identity-providers.ts\";\nimport { makeMagicLinkRedirects } from \"./magic-link-redirects.ts\";\nimport type {\n AuthResult,\n Envelope,\n HostedGateInfo,\n MagicLinkCallbackResult,\n RefreshTokenInput,\n RequestMagicLinkInput,\n RequestOtpInput,\n SignInCallbackResult,\n SignInInput,\n SignOutInput,\n SignUpInput,\n VerifyMagicLinkInput,\n VerifyOtpInput,\n VerifySessionResult,\n WhoamiResult,\n} from \"./types.ts\";\n\nfunction gatePath(config: GateConfig, suffix: string): string {\n return `${resolveProjectPathRoot(config)}/gate/${suffix}`;\n}\n\n/** Factory — produces a `gate.*` namespace bound to the supplied config getter.\n * Used by both the module-level `gate` singleton (config from `configureGate`)\n * and `createGateClient(config)` (config supplied at construction time). */\nexport function makeGateNamespace(getConfig: () => GateConfig) {\n return {\n /**\n * Create a new end-user identity for the configured project. Returns\n * the new user record + an initial session. Emits a\n * `data.write.auth.signup` audit event.\n *\n * Errors (closed taxonomy):\n * - `EMAIL_ALREADY_REGISTERED` (409) when the email collides\n * - `INVALID_FIELD_VALUE` (400) on schema violation (password policy, malformed email)\n * - `TIER_FEATURE_GATED` (403) when the project's tier blocks email/password\n */\n async signUp(input: SignUpInput): Promise<Envelope<AuthResult>> {\n return requestWith<AuthResult>(getConfig(), {\n method: \"POST\",\n path: gatePath(getConfig(), \"signup\"),\n body: input,\n });\n },\n\n /**\n * Sign in an existing end-user with email + password. Returns the\n * user record + a fresh session. Emits a `data.write.auth.signin`\n * audit event.\n *\n * Errors:\n * - `INVALID_CREDENTIALS` (401) on bad email/password (ambiguous on\n * purpose — does not distinguish \"user not found\" vs \"password\n * wrong\" to avoid enumeration)\n */\n async signIn(input: SignInInput): Promise<Envelope<AuthResult>> {\n return requestWith<AuthResult>(getConfig(), {\n method: \"POST\",\n path: gatePath(getConfig(), \"signin\"),\n body: input,\n });\n },\n\n /**\n * Sign out — revoke the bound refresh token (or an explicit one, if\n * supplied). Emits a `data.write.auth.signout` audit event.\n *\n * Errors:\n * - `UNAUTHENTICATED` (401) when no session is present\n * - `REFRESH_TOKEN_INVALID` (400) when the supplied refresh token\n * doesn't match the bearer's session\n */\n async signOut(input: SignOutInput = {}): Promise<Envelope<{ revoked: boolean }>> {\n return requestWith<{ revoked: boolean }>(getConfig(), {\n method: \"POST\",\n path: gatePath(getConfig(), \"signout\"),\n body: input,\n });\n },\n\n /**\n * Return the caller's identity claims from the bearer JWT. Useful in\n * Standalone mode where the SDK consumer wants to surface the user's\n * roles / groups without parsing the JWT itself.\n *\n * Errors:\n * - `UNAUTHENTICATED` (401) when no bearer is present\n * - `SESSION_EXPIRED` (401) when the bearer's exp has passed\n */\n async whoami(): Promise<Envelope<WhoamiResult>> {\n return requestWith<WhoamiResult>(getConfig(), {\n method: \"GET\",\n path: gatePath(getConfig(), \"whoami\"),\n });\n },\n\n /**\n * Rotate the access token using a refresh token. Returns a fresh\n * session pair (new access token + same refresh token; gateway may\n * also rotate the refresh token per project policy).\n *\n * Errors:\n * - `REFRESH_TOKEN_INVALID` (400) when the refresh token is unknown,\n * expired, or revoked\n */\n async refreshToken(input: RefreshTokenInput): Promise<Envelope<{ session: AuthResult[\"session\"] }>> {\n return requestWith<{ session: AuthResult[\"session\"] }>(getConfig(), {\n method: \"POST\",\n path: gatePath(getConfig(), \"refresh\"),\n body: input,\n });\n },\n\n /**\n * Check whether the bearer's session is currently valid. Does NOT\n * throw on expiry or invalid token — returns `{ valid: false }`\n * inside the envelope. The intended use is health-checking a session\n * without surfacing an error to the end-user (the SDK consumer\n * typically wants to redirect to sign-in rather than surface a 401).\n */\n async verifySession(): Promise<Envelope<VerifySessionResult>> {\n return requestWith<VerifySessionResult>(getConfig(), {\n method: \"GET\",\n path: gatePath(getConfig(), \"verify\"),\n });\n },\n\n /**\n * The project's hosted sign-in page URL and WebAuthn `rpId`. Unauthenticated and cacheable; no credential is sent,\n * even when a partner token is configured. Take both values as given: an unknown org, a deleted project and a\n * non-Gate project all answer the same `PROJECT_NOT_FOUND`, so a failure here says nothing about which.\n */\n async hosted(): Promise<Envelope<HostedGateInfo>> {\n return requestWith<HostedGateInfo>(getConfig(), {\n method: \"GET\",\n path: gatePath(getConfig(), \"hosted\"),\n bearerToken: \"\",\n });\n },\n\n /**\n * Initiate an external-IdP sign-in flow (Google / GitHub / Microsoft).\n * The SDK POSTs to the gateway's authorize endpoint, receives the\n * provider's authorization URL with a signed-state JWT (carrying PKCE\n * verifier + the supplied `redirectUri`), and redirects the browser.\n *\n * After the user authenticates with the IdP, the gateway processes the\n * callback and redirects the browser back to `redirectUri` with session\n * tokens in the URL fragment. Use `parseSignInCallback()` on the\n * destination page to extract them.\n *\n * Example:\n * await gate.signInWithProvider({\n * provider: 'google',\n * redirectUri: 'https://app.example.com/auth/callback',\n * });\n * // …user lands at /auth/callback#access_token=…&refresh_token=…\n *\n * Generic OIDC requires authorization-endpoint discovery and is not\n * yet wired; use a hosted provider (google/github/microsoft) for now.\n */\n async signInWithProvider(input: {\n provider: \"google\" | \"github\" | \"microsoft\" | \"oidc\" | \"saml\";\n redirectUri: string;\n }): Promise<void> {\n const env = await requestWith<{ authorizationUrl: string }>(getConfig(), {\n method: \"POST\",\n path: `${resolveProjectPathRoot(getConfig())}/gate/idp/${input.provider}/authorize`,\n body: { redirectUri: input.redirectUri },\n });\n const w = (globalThis as { window?: { location: { assign: (url: string) => void } } }).window;\n if (w !== undefined) {\n w.location.assign(env.data.authorizationUrl);\n } else {\n throw new Error(\n \"signInWithProvider requires a browser environment (window.location); for server-side initiation, fetch the authorize endpoint directly.\",\n );\n }\n },\n\n /**\n * Parse session tokens out of the URL fragment delivered by an\n * IdP callback redirect. Returns the session payload on success or an\n * error object on failure. Pass `window.location.hash` (or any\n * `#`-prefixed string) — leading `#` is stripped automatically.\n *\n * Side-effect-free: does not call the gateway and does not mutate\n * any session storage. The consumer is responsible for persisting\n * the returned tokens (e.g. via the app's auth context).\n */\n parseSignInCallback(hash: string): SignInCallbackResult {\n const raw = hash.startsWith(\"#\") ? hash.slice(1) : hash;\n const params = new URLSearchParams(raw);\n const error = params.get(\"error\");\n if (error !== null) {\n return {\n ok: false,\n error,\n errorDescription: params.get(\"error_description\") ?? error,\n };\n }\n const accessToken = params.get(\"access_token\");\n const refreshToken = params.get(\"refresh_token\");\n const expiresAt = params.get(\"expires_at\");\n if (accessToken === null || refreshToken === null || expiresAt === null) {\n return {\n ok: false,\n error: \"malformed_callback\",\n errorDescription:\n \"Callback URL fragment missing one or more required fields (access_token, refresh_token, expires_at).\",\n };\n }\n return {\n ok: true,\n accessToken,\n refreshToken,\n expiresAt,\n tokenType: params.get(\"token_type\") ?? \"Bearer\",\n };\n },\n\n /**\n * Passwordless sub-namespace — OTP + Magic Link flows\n * for email-based authentication without a stored password.\n *\n * Wire endpoints (LIVE on the Gate end-user plane — verified against\n * staging): OTP → `POST /@<projectSlug>/gate/otp/{request,verify}`\n * (routes/otp.ts), Magic Link → `POST /@<projectSlug>/gate/magic-link/\n * {send,redeem}` (routes/magic-link.ts). NOTE: these are the gateway's\n * canonical paths; the SDK ergonomic method names (`requestMagicLink`/\n * `verifyMagicLink`) do not mirror the path segments (`send`/`redeem`).\n * (The SDK paths match the shipped gateway routes.)\n */\n passwordless: {\n /**\n * Request a one-time passcode delivered to the supplied email.\n * The OTP is short-lived (typically 5 min) and\n * single-use; redemption via `verifyOtp` mints a session.\n */\n async requestOtp(input: RequestOtpInput): Promise<Envelope<{ requested: boolean }>> {\n return requestWith<{ requested: boolean }>(getConfig(), {\n method: \"POST\",\n path: gatePath(getConfig(), \"otp/request\"),\n body: input,\n });\n },\n\n /**\n * Verify a one-time passcode previously sent via `requestOtp`.\n * On success returns the same `AuthResult` shape as `signIn` —\n * a fresh session bound to the end-user identity.\n *\n * Errors:\n * - INVALID_CREDENTIALS (401) on wrong / expired / consumed OTP\n * (ambiguous on purpose — enumeration defense)\n */\n async verifyOtp(input: VerifyOtpInput): Promise<Envelope<AuthResult>> {\n return requestWith<AuthResult>(getConfig(), {\n method: \"POST\",\n path: gatePath(getConfig(), \"otp/verify\"),\n body: input,\n });\n },\n\n /**\n * Request a magic-link emailed to the supplied address. The link\n * carries an opaque short-lived token (typically 15 min\n *); clicking the link lands at the customer's chosen\n * redirect URL with the token in the query string, which the\n * customer's frontend then exchanges via `verifyMagicLink`.\n */\n async requestMagicLink(\n input: RequestMagicLinkInput,\n ): Promise<Envelope<{ requested: boolean }>> {\n return requestWith<{ requested: boolean }>(getConfig(), {\n method: \"POST\",\n path: gatePath(getConfig(), \"magic-link/send\"),\n body: input,\n });\n },\n\n /**\n * Verify a magic-link token previously delivered via\n * `requestMagicLink`. On success returns the same `AuthResult`\n * shape as `signIn`.\n *\n * Errors:\n * - INVALID_CREDENTIALS (401) on wrong / expired / consumed\n * token (same enumeration-defense rationale as verifyOtp)\n */\n async verifyMagicLink(input: VerifyMagicLinkInput): Promise<Envelope<AuthResult>> {\n return requestWith<AuthResult>(getConfig(), {\n method: \"POST\",\n path: gatePath(getConfig(), \"magic-link/redeem\"),\n body: input,\n });\n },\n\n /**\n * Extract the opaque magic-link token from the landing-page URL's query\n * string (the gateway redirects to `<configured-redirect>?token=…&project=…`).\n * Pure + side-effect-free — pass `window.location.search`. Feed the\n * returned `token` to `verifyMagicLink({ token })` to mint the session.\n *\n * This is the magic-link counterpart to `parseSignInCallback` (which parses\n * the SSO *hash fragment* for session tokens) — magic-link delivers an\n * opaque token in the *query string*, so the two are not interchangeable.\n */\n parseMagicLinkCallback(search: string): MagicLinkCallbackResult {\n const raw = search.startsWith(\"?\") ? search.slice(1) : search;\n const params = new URLSearchParams(raw);\n const error = params.get(\"error\");\n if (error !== null) {\n return { ok: false, error };\n }\n const token = params.get(\"token\");\n if (token === null || token.length === 0) {\n return { ok: false, error: \"missing_token\" };\n }\n return { ok: true, token };\n },\n },\n\n /**\n * Project-scoped end-user IdP provider-config management.\n * PROJECT_ADMIN+ required; end-user JWTs rejected at the route. Only\n * `create()` is bound — the REST create the Gate Studio\n * `FederationPresetsPanel` routes to when it drops the `gate_flows`\n * `__addFromPreset` sentinel. Gate-admin surface.\n */\n identityProviders: makeIdentityProviders(getConfig),\n\n /**\n * Per-project magic-link redirect-URL allowlist management (blessed\n * shape). Gate-admin surface alongside `identityProviders`\n * — PROJECT_ADMIN+ required, distinct from the end-user-facing\n * `passwordless.requestMagicLink`/`verifyMagicLink` send/verify flow.\n */\n magicLinkRedirects: makeMagicLinkRedirects(getConfig),\n };\n}\n\n/** Singleton bound to the module-level `configureGate()` config.\n * Per-instance clients: `createGateClient(config)`. */\nexport const gate = makeGateNamespace(getGateConfig);\n\n/** Convenience type alias mirroring the `gate` runtime export.\n * Identical shape as the per-instance `GateClient` returned from\n * `createGateClient`. */\nexport type GateNamespace = ReturnType<typeof makeGateNamespace>;\n","/**\n * `createGateClient(config)` — per-instance Gate SDK factory.\n *\n * Returns a self-contained `GateClient` that does not depend on the\n * module-level `configureGate()` singleton. Multiple clients can coexist\n * in the same process, each scoped to its own project + baseUrl + mode —\n * useful for multi-tenant Backends-For-Frontends (one client per\n * incoming request's resolved tenant) or for apps that maintain both a\n * Standalone and Native binding against the same SDK instance.\n *\n * Per the URL grammar, supply `projectSlug` for\n * canonical `/@<slug>/gate/...` routing. Slug is the only customer-facing\n * project addressing form — project ids are internal-only\n * (`feedback_slug_addressing_v2_5_68`); the retired `/v2/projects/...`\n * mounts were dropped at pass17 (`feedback_v2_path_is_an_error`).\n *\n * Example:\n * const client = createGateClient({\n * baseUrl: 'https://formelo.api.k9o.io',\n * projectSlug: 'partner-console-staging',\n * mode: 'native',\n * });\n * await client.signIn({ email, password });\n */\n\nimport { makeGateNamespace } from \"./gate.ts\";\nimport { validateGateConfig, type GateConfig } from \"./mode.ts\";\n\n/** Shape of the per-instance client returned by `createGateClient`. */\nexport type GateClient = ReturnType<typeof makeGateNamespace>;\n\n/** Construct a Gate client bound to the supplied config. The config is\n * validated up-front and stored on the instance; subsequent calls do not\n * re-read process-level state. Throws `TypeError` on invalid config. */\nexport function createGateClient(config: GateConfig): GateClient {\n validateGateConfig(config);\n const frozen: GateConfig = { ...config };\n return makeGateNamespace(() => frozen);\n}\n"],"mappings":"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","names":[]}
package/dist/jwt.d.ts CHANGED
@@ -14,7 +14,7 @@ import { GateJwtClaims, VerifyJwtResult } from './types.js';
14
14
  *
15
15
  * Native-mode customers don't typically reach for this directly — the
16
16
  * gateway verifies project-issued tokens on every request and rehydrates
17
- * `subject.*` claims per ch06 §3.2. But the same primitives are
17
+ * `subject.*` claims. But the same primitives are
18
18
  * portable: Native customers running their own services downstream of
19
19
  * the gateway can verify Gate-issued JWTs identically.
20
20
  */
package/dist/jwt.js CHANGED
@@ -2,8 +2,8 @@ import {
2
2
  extractClaims,
3
3
  requireScope,
4
4
  verifyJwt
5
- } from "./chunk-ZNFI2CQA.js";
6
- import "./chunk-PEW6HZFB.js";
5
+ } from "./chunk-YINWOWJC.js";
6
+ import "./chunk-JEV37YEC.js";
7
7
  export {
8
8
  extractClaims,
9
9
  requireScope,
package/dist/mode.d.ts CHANGED
@@ -1,10 +1,10 @@
1
1
  /**
2
- * Standalone vs Native mode wiring per ch01 §12.4.
2
+ * Standalone vs Native mode wiring.
3
3
  *
4
4
  * The SDK surface is identical across modes; mode determines gateway-
5
5
  * side behavior (whether the end-user row scope applies on read/write paths),
6
6
  * not SDK ergonomics. The mode boundary is a deployment-time decision
7
- * per ch02; the SDK does not branch behavior at function-call time.
7
+ *; the SDK does not branch behavior at function-call time.
8
8
  *
9
9
  * What mode affects (today + forward):
10
10
  *
@@ -24,8 +24,8 @@
24
24
  * What mode does NOT affect:
25
25
  *
26
26
  * - Function names + signatures (identical across modes)
27
- * - Wire-envelope shape (identical per ch01 §1)
28
- * - Error taxonomy (identical per ch01 §2)
27
+ * - Wire-envelope shape (identical)
28
+ * - Error taxonomy (identical)
29
29
  */
30
30
  /**
31
31
  * Two-mode discriminant. The third candidate "Hybrid" was rejected per
@@ -40,17 +40,17 @@ type GateMode = "standalone" | "native";
40
40
  */
41
41
  type GateConfig = {
42
42
  mode: GateMode;
43
- /** Project slug per v2.5.68 URL grammar (e.g. `partner-console-staging`).
43
+ /** Project slug per the URL grammar (e.g. `partner-console-staging`).
44
44
  * The only customer-facing project addressing form; routes resolve to
45
45
  * `/@<slug>/gate/...` (`feedback_slug_addressing_v2_5_68`). Required. */
46
46
  projectSlug: string;
47
- /** Gateway base URL — e.g. `https://<orgSlug>.api.k9o.io` per v2.5.68 §5.4. */
47
+ /** Gateway base URL — e.g. `https://<orgSlug>.api.k9o.io` per the URL grammar */
48
48
  baseUrl: string;
49
49
  /**
50
50
  * Optional partner-credentialled JWT for Standalone mode — used as the
51
51
  * default Authorization header when no per-call bearer is supplied.
52
52
  * Not required in Native mode; the gateway resolves credentials from
53
- * the standard project context per ch02.
53
+ * the standard project context.
54
54
  */
55
55
  partnerToken?: string;
56
56
  /**