@atlasauth/backend 0.8.0 → 0.9.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/CHANGELOG.md CHANGED
@@ -1,5 +1,27 @@
1
1
  # @atlasauth/backend changelog
2
2
 
3
+ ## 0.9.1
4
+ - **Allowed origins with an integration.** `UpdateInstanceBody.allowed_origins` and the platform app, instance and accountless-app bodies accept `AllowedOriginInput` entries: an origin string or `{ origin, integration }` (`OriginIntegration`: `redirect` | `embedded` | `auto`, or `null` to clear). `Instance`, `InstanceUpdateResult` and `PlatformApp` gain optional `allowed_origin_integrations`. New types: `AllowedOriginEntry`, `AllowedOriginInput`, `OriginIntegration`.
5
+ - **Login readiness.** `LoginReadiness` gains `integration` (`LoginReadinessIntegration`: `assumed`, `source`, `reason`); `LoginReadinessCheck` gains `applies`.
6
+ - **Branding audiences.** `Branding` accepts `audiences` (hint → `BrandingAudience` `{ heading, subheading, logoUrl, primaryColor }`, `null` deletes one); `ResolvedBranding` gains `audiences`. Start a hosted sign-in with `audienceHint` (or `audience_hint` on the URL) to use one.
7
+ - **Error codes.** `AtlasErrorCodes` adds `READ_ONLY_FIELD`, `DEVELOPMENT_EMAIL_NOT_ALLOWED`, `DEVELOPMENT_EMAIL_UNAVAILABLE`, `DEVELOPMENT_EMAIL_LIMIT_REACHED` and `DEVELOPMENT_EMAIL_TEMPLATE_NOT_ALLOWED`.
8
+ - **Webhooks, several signatures.** `verifyWebhook` accepts an `atlas-signature` header with several space-separated `v1,<signature>` entries and verifies when any one matches (each compared in constant time). This prepares for webhook secret rotation with an overlap. A header with a single signature works as before.
9
+ - **Compatible.** New types, optional fields and error codes only; a single-signature webhook header verifies as before.
10
+
11
+ ## 0.9.0
12
+ - **OIDC relying party.** New `oidc` namespace and standalone functions for the hosted-redirect sign-in: `oidcStart` (`oidc.start`), `oidcCallback` (`oidc.callback`), `oidcLogoutUrl` (`oidc.logoutUrl`), `oidcVerifyIdToken` (`oidc.verifyIdToken`), `oidcClearCookie` (`oidc.clearCookie`). `start({ issuer, clientId, redirectUri, scope?, prompt?, loginHint?, audienceHint?, extraParams?, cookieName?, secret? })` returns `{ url, cookie, state, nonce }`: a PKCE (S256) authorize URL from discovery and a `Set-Cookie` for a 10-minute, httpOnly, `SameSite=Lax`, callback-path flow cookie, AES-GCM sealed when `secret` is set. `callback({ …, clientSecret?, clientAuth?: 'client_secret_basic' | 'client_secret_post' | 'none', query, cookie, secret?, clockToleranceSec? })` returns `OidcIdentity` (`sub`, `namespacedSub` = `${issuer}#${sub}`, `email`, `emailVerified`, `name`, `idToken`, `accessToken`, `refreshToken?`, `expiresAt` in epoch ms, `claims`, `clearCookie`). The ID token is verified with RS256 pinned, exact `iss`, `aud` containing the client id (and `azp` when present or when there are several audiences), `nonce`, `exp`/`iat` with tolerance, and `at_hash`. Discovery is cached per issuer for an hour.
13
+ - **Errors.** `AtlasOidcError` with `code` (`AtlasOidcErrorCode`: `state_mismatch`, `issuer_mismatch`, `invalid_request`, `invalid_token`, `token_exchange_failed`, `access_denied`, `discovery_failed`), `oauthError`, `description`, `status`, `clearCookie` and `cause`.
14
+ - **`AtlasBackend#oidc`.** The same helpers with the backend's `issuer`, `fetchImpl` and `now` filled in (`AtlasBackendOidc`).
15
+ - **Test issuer.** `createTestIssuer` accepts `clientId`, `clientSecret` and `idTokenTtlSeconds`, and the returned `TestIssuer` gains `clientId`, `mintIdToken(claims?, { aud?, nonce?, accessToken?, ttlSeconds?, tamper? })`, `authorize(authorizeUrl, { sub?, claims?, tamper?, error? })`, `fetch` (an offline discovery, JWKS and token endpoint) and `endpoints`. `mintSession(claims?, { tamper? })` accepts a tamper too. `TestTokenTamper` is `wrong_aud`, `wrong_iss`, `foreign_key`, `expired`, `wrong_nonce` or `alg_none`; `TEST_TOKEN_TAMPERS` lists all six. New types: `MintIdTokenOptions`, `TestAuthorizeOptions`, `TestAuthorizeResult`.
16
+ - **OAuth client secret rotation with overlap.** `oauthClients.rotateSecret(id, { overlap_seconds? }, idempotencyKey?)` takes how long the previous secret keeps working: default 86400 (24 hours), `0` revokes it now, at most 604800 (7 days). It returns `OAuthClientSecretRotation` (`client_secret`, `overlap_seconds`, `previous_secret_expires_at`: epoch ms, or null when the old secret already stopped working). New type `RotateOAuthClientSecretBody`. `OAuthClient` gains optional `previous_secret_expires_at`.
17
+ - **Issuer and verification endpoints.** `Instance` (`instance.get()`) and the platform client's `PlatformApp` gain `issuer`, `discovery_url` and `jwks_uri`; `PlatformApp` also gains `frontend_api_host`. They change when a custom domain goes active, so read them rather than hard-coding them.
18
+ - **Email verification.** `EmailAddress` gains optional `verification` (`{ status: 'verified' | 'unverified', strategy?: string | null }`). `verified` and `email_verified` are now true only for a proven address: one that signed up while `requireEmailVerification` was false reads `verified: false`, with `verification.strategy: 'skipped'`. Do not grant access on an unverified address.
19
+ - **Email templates.** `EmailTemplateName` adds `account_secured` (variable `when`): sent when the owner of an address proves it and the account is secured from whoever had set it up without proof.
20
+ - **Managed email uses your own provider account.** `messaging.managedEmail.create` now sets the domain up on YOUR Resend or Brevo account: `CreateManagedEmailBody` gains `transport` (`resend` | `brevo`) + `config.api_key`, or `provider_id`. `ManagedEmail` gains `backend`, `provider_id` and `feedback_webhook`; `limits` is now always `null` and `reputation` is `your_provider_account`. `DeletedManagedEmail` gains `provider_removed`. `ManagedEmailRecord.purpose` adds `ownership` and `dmarc`.
21
+ - **Development email sender.** New `messaging.developmentEmail.get / enable / disable` (`DevelopmentEmail`, `EnableDevelopmentEmailBody`): your own email provider in development mode, for a development instance without a verified domain. `MessagingProviders` gains `development_email`.
22
+ - **Provider mode.** Email provider bodies accept `mode` (`standard` | `development`); provider responses gain optional `id` and `mode` (`standard` | `development` | `managed_domain`).
23
+ - **Compatible** except `ManagedEmail.limits` (was `{ hourly, daily }`, now `null`) and `ManagedEmail.reputation` (new constant). Everything else is new exports, optional parameters and fields; `rotateSecret(id)` behaves as before.
24
+
3
25
  ## 0.8.0
4
26
  - **Back-channel logout, retryable failures.** A failed `LogoutTokenResult` gains `retryable: boolean`. It is `true` for `no_keys` (the issuer's keys could not be fetched: a network error, a JWKS outage, or a just-rotated key whose refetch failed) and `false` for `malformed`, `invalid` and `replayed`. Answer `503` with `Retry-After` when it is true, so Atlas retries the delivery, and `400` otherwise. The README example does this.
5
27
  - **Back-channel delivery log and test.** `oauthClients.listBackchannelDeliveries(id, { limit?, starting_after? })` returns `BackchannelLogoutDeliveryPage` (`BackchannelLogoutDelivery`: `status`, `http_status`, `latency_ms`, `attempt_number`, `error`, `location`, `test`, timestamps). `oauthClients.testBackchannelLogout(id)` returns `BackchannelLogoutTest` (`ok`, `status`, `latency_ms`, `error`, `location`, `blocked_reason`, `body_excerpt`, `sid`, `delivery_id`). The platform client's `atlasIdApps` gains the same two methods plus `preflight(id)` (`AtlasIdAppPreflight`).
package/README.md CHANGED
@@ -3,6 +3,7 @@
3
3
  Server-side Atlas for Node, Deno, Bun and edge runtimes. It does three things:
4
4
 
5
5
  - verifies Atlas session tokens, webhooks and back-channel logout tokens
6
+ - runs the server side of a hosted-redirect (OpenID Connect) sign-in
6
7
  - gives you a typed client for the Backend API, using your secret key (`sk_...`)
7
8
  - gives you a typed client for the Platform API, using your platform key (`psk_...`)
8
9
 
@@ -24,6 +25,150 @@ if (result.ok) console.log(result.claims.sub);
24
25
 
25
26
  The JWKS is found from the issuer and cached. Instances on the shared host also pass `instanceId` (or `publishableKey`).
26
27
 
28
+ ## Sign in with a redirect (OpenID Connect)
29
+
30
+ `oidc` runs the relying-party side of the hosted-redirect integration: state, nonce and PKCE, the flow cookie, the code exchange and the ID-token checks (RS256 only, `iss`, `aud` = your client id, `nonce`, `exp`).
31
+
32
+ ```ts
33
+ import { oidc, AtlasOidcError } from '@atlasauth/backend';
34
+
35
+ const config = {
36
+ issuer: 'https://bright-fox-42-fapi.atlasauth.net', // https:// + frontend_api_host
37
+ clientId: process.env.ATLAS_CLIENT_ID!,
38
+ redirectUri: 'https://app.example.com/auth/callback',
39
+ secret: process.env.OIDC_COOKIE_SECRET!, // 32+ random characters; seals the flow cookie
40
+ };
41
+
42
+ // Start: set the flow cookie, redirect.
43
+ const { url, cookie } = await oidc.start({ ...config, scope: 'openid profile email' });
44
+
45
+ // Callback: one call does every check.
46
+ const identity = await oidc.callback({
47
+ ...config,
48
+ clientSecret: process.env.ATLAS_CLIENT_SECRET!,
49
+ query: request.url, // or URLSearchParams, or req.query
50
+ cookie: request.headers.get('cookie'),
51
+ });
52
+ // identity: { sub, namespacedSub, email, emailVerified, name, idToken, accessToken, refreshToken?, expiresAt, claims, clearCookie }
53
+
54
+ // Sign out: end your session, then end the Atlas one.
55
+ const logout = await oidc.logoutUrl({ ...config, postLogoutRedirectUri: 'https://app.example.com/', idTokenHint });
56
+ ```
57
+
58
+ - The flow cookie is `httpOnly`, `SameSite=Lax`, `Secure` (except on a `http://localhost` callback), scoped to the callback path, and lasts 10 minutes. With `secret` it is encrypted (AES-GCM), so it can't be read or forged. Without `secret` it is only encoded. Always pass one in production.
59
+ - `namespacedSub` is `${issuer}#${sub}`. Key users on it if you accept more than one instance, so two instances' users never collide.
60
+ - `clientAuth` is `client_secret_basic` (default with a secret), `client_secret_post`, or `none` (public client, PKCE only).
61
+ - `new AtlasBackend({ issuer }).oidc` has the same methods with `issuer` filled in.
62
+ - Did your own code exchange? `oidc.verifyIdToken(idToken, { issuer, clientId, nonce })` runs just the token checks.
63
+
64
+ Failures throw `AtlasOidcError` with a stable `code`:
65
+
66
+ | `code` | Meaning |
67
+ |---|---|
68
+ | `state_mismatch` | No flow cookie, a different `state`, a cookie from another sign-in or secret, or a flow older than 10 minutes. Start again. |
69
+ | `access_denied` | Atlas redirected back with an OAuth `error`. The exact value is in `oauthError` (`access_denied`, `login_required`, …). |
70
+ | `issuer_mismatch` | The callback's `iss` parameter names a different server. |
71
+ | `invalid_request` | The callback has no `code`. |
72
+ | `token_exchange_failed` | The token endpoint refused the code or client (`oauthError`, `status`). |
73
+ | `invalid_token` | The ID token failed a check: algorithm, signature, `iss`, `aud`/`azp`, `nonce`, `exp`/`iat` or `at_hash`. |
74
+ | `discovery_failed` | The discovery document or the signing keys could not be fetched or don't match the issuer. |
75
+
76
+ Every error from `callback` carries `clearCookie`: send it with your error page too.
77
+
78
+ ### Next.js (App Router)
79
+
80
+ ```ts
81
+ // app/auth/login/route.ts
82
+ import { oidc } from '@atlasauth/backend';
83
+ import { config } from '@/lib/atlas';
84
+
85
+ export async function GET() {
86
+ const { url, cookie } = await oidc.start(config);
87
+ return new Response(null, { status: 302, headers: { location: url, 'set-cookie': cookie } });
88
+ }
89
+
90
+ // app/auth/callback/route.ts
91
+ import { oidc, AtlasOidcError } from '@atlasauth/backend';
92
+ import { config } from '@/lib/atlas';
93
+
94
+ export async function GET(request: Request) {
95
+ try {
96
+ const user = await oidc.callback({
97
+ ...config,
98
+ clientSecret: process.env.ATLAS_CLIENT_SECRET!,
99
+ query: request.url,
100
+ cookie: request.headers.get('cookie'),
101
+ });
102
+ const headers = new Headers({ location: '/' });
103
+ headers.append('set-cookie', user.clearCookie);
104
+ headers.append('set-cookie', await createAppSession(user)); // your own session cookie
105
+ return new Response(null, { status: 302, headers });
106
+ } catch (error) {
107
+ if (!(error instanceof AtlasOidcError)) throw error;
108
+ return new Response(`Sign-in failed (${error.code})`, {
109
+ status: 400,
110
+ headers: error.clearCookie ? { 'set-cookie': error.clearCookie } : {},
111
+ });
112
+ }
113
+ }
114
+ ```
115
+
116
+ ### Express
117
+
118
+ ```ts
119
+ import express from 'express';
120
+ import { oidc, AtlasOidcError } from '@atlasauth/backend';
121
+
122
+ const app = express();
123
+
124
+ app.get('/auth/login', async (req, res) => {
125
+ const { url, cookie } = await oidc.start(config);
126
+ res.append('Set-Cookie', cookie).redirect(url);
127
+ });
128
+
129
+ app.get('/auth/callback', async (req, res) => {
130
+ try {
131
+ const user = await oidc.callback({
132
+ ...config,
133
+ clientSecret: process.env.ATLAS_CLIENT_SECRET!,
134
+ query: req.originalUrl,
135
+ cookie: req.headers.cookie,
136
+ });
137
+ res.append('Set-Cookie', user.clearCookie);
138
+ await startYourSession(req, res, user.namespacedSub);
139
+ res.redirect('/');
140
+ } catch (error) {
141
+ if (!(error instanceof AtlasOidcError)) throw error;
142
+ if (error.clearCookie) res.append('Set-Cookie', error.clearCookie);
143
+ res.status(400).send(`Sign-in failed (${error.code})`);
144
+ }
145
+ });
146
+ ```
147
+
148
+ ## Test offline
149
+
150
+ `createTestIssuer()` mints session tokens and ID tokens with a throwaway key, and its `fetch` serves discovery, the JWKS and a token endpoint, so a full redirect sign-in runs in a unit test with no network.
151
+
152
+ ```ts
153
+ import { createTestIssuer, oidc, TEST_TOKEN_TAMPERS } from '@atlasauth/backend';
154
+
155
+ const fx = await createTestIssuer({ clientSecret: 'test-secret' });
156
+ const config = { issuer: fx.issuer, clientId: fx.clientId, redirectUri: 'https://app.example.com/auth/callback', secret: '…32+ chars…', fetchImpl: fx.fetch };
157
+
158
+ const { url, cookie } = await oidc.start(config);
159
+ const { callbackUrl } = await fx.authorize(url, { sub: 'user_1', claims: { email: 'a@example.com', email_verified: true } });
160
+ const user = await oidc.callback({ ...config, clientSecret: 'test-secret', query: callbackUrl, cookie });
161
+
162
+ // Negative cases: wrong_aud, wrong_iss, foreign_key, expired, wrong_nonce, alg_none.
163
+ for (const tamper of TEST_TOKEN_TAMPERS) {
164
+ const { url, cookie } = await oidc.start(config);
165
+ const { callbackUrl } = await fx.authorize(url, { tamper });
166
+ await expect(oidc.callback({ ...config, clientSecret: 'test-secret', query: callbackUrl, cookie })).rejects.toMatchObject({ code: 'invalid_token' });
167
+ }
168
+ ```
169
+
170
+ `fx.mintIdToken(claims, { aud, nonce, tamper })` and `fx.mintSession(claims, { tamper })` mint single tokens. `@atlasauth/testing` re-exports `createTestIssuer`.
171
+
27
172
  ## Management client (secret key)
28
173
 
29
174
  ```ts
@@ -254,3 +399,5 @@ To check the route end to end, `oauthClients.testBackchannelLogout(id)` sends a
254
399
  ## Webhooks
255
400
 
256
401
  `verifyWebhook` checks the signature on events Atlas sends to your webhook endpoints. You manage endpoints with `atlas.webhooks.endpoints`: create, update, reveal or rotate a secret, send a test event, and redeliver a past delivery.
402
+
403
+ The `atlas-signature` header can carry more than one signature, separated by spaces (`v1,<sig> v1,<sig>`). While an endpoint secret rotates, Atlas may sign each delivery with the old and the new secret. `verifyWebhook` accepts the delivery when any one signature matches your secret.
@@ -51,6 +51,13 @@ export interface Branding {
51
51
  };
52
52
  /** How a verification code is entered — segmented `boxes` (default) or `single`. */
53
53
  codeInput?: 'boxes' | 'single';
54
+ /**
55
+ * Per-audience branding for the hosted pages, keyed by audience hint
56
+ * (`[a-z0-9_-]{1,32}`, at most 20). A sign-in started with
57
+ * `audience_hint=<hint>` uses that entry. An update merges per hint: an entry
58
+ * replaces that hint, `null` deletes it, `audiences: null` removes them all.
59
+ */
60
+ audiences?: Record<string, BrandingAudience | null> | null;
54
61
  card?: {
55
62
  width?: 'narrow' | 'default' | 'wide';
56
63
  align?: 'left' | 'center';
@@ -108,6 +115,17 @@ export interface HostedBackground {
108
115
  angle?: number;
109
116
  imageUrl?: string;
110
117
  }
118
+ /** The hosted-page branding for one audience hint. */
119
+ export interface BrandingAudience {
120
+ /** Replaces the card heading (up to 120 characters). */
121
+ heading?: string;
122
+ /** Replaces the line under the heading (up to 240 characters). */
123
+ subheading?: string;
124
+ /** Replaces the logo (https). */
125
+ logoUrl?: string;
126
+ /** Replaces the primary colour (hex, rgb() or a CSS colour name). */
127
+ primaryColor?: string;
128
+ }
111
129
  /** What the renderers actually use — everything already escaped or dropped. */
112
130
  export interface ResolvedBranding {
113
131
  applicationName: string;
@@ -137,6 +155,8 @@ export interface ResolvedBranding {
137
155
  providerHidden?: string[];
138
156
  socialButtons?: Record<string, unknown>;
139
157
  codeInput?: 'boxes' | 'single';
158
+ /** Every audience the hosted page can theme for, as it reads them (invalid values dropped). */
159
+ audiences?: Record<string, BrandingAudience>;
140
160
  card?: {
141
161
  width: string;
142
162
  align: string;
@@ -1 +1 @@
1
- {"version":3,"file":"branding.js","sourceRoot":"","sources":["../../src/client/branding.ts"],"names":[],"mappings":";;AAkLA,4CAcC;AAdD,SAAgB,gBAAgB,CAAC,OAAkB;IACjD,OAAO;QACL,GAAG;YACD,OAAO,OAAO,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,cAAc,EAAE,CAAC,CAAC;QAC1D,CAAC;QACD,kEAAkE;QAClE,MAAM,CAAC,IAAuB;YAC5B,OAAO,OAAO,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,cAAc,EAAE,IAAI,EAAE,CAAC,CAAC;QAClE,CAAC;QACD,0EAA0E;QAC1E,OAAO,CAAC,IAAuB;YAC7B,OAAO,OAAO,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,sBAAsB,EAAE,IAAI,EAAE,CAAC,CAAC;QACzE,CAAC;KACF,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"branding.js","sourceRoot":"","sources":["../../src/client/branding.ts"],"names":[],"mappings":";;AAuMA,4CAcC;AAdD,SAAgB,gBAAgB,CAAC,OAAkB;IACjD,OAAO;QACL,GAAG;YACD,OAAO,OAAO,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,cAAc,EAAE,CAAC,CAAC;QAC1D,CAAC;QACD,kEAAkE;QAClE,MAAM,CAAC,IAAuB;YAC5B,OAAO,OAAO,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,cAAc,EAAE,IAAI,EAAE,CAAC,CAAC;QAClE,CAAC;QACD,0EAA0E;QAC1E,OAAO,CAAC,IAAuB;YAC7B,OAAO,OAAO,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,sBAAsB,EAAE,IAAI,EAAE,CAAC,CAAC;QACzE,CAAC;KACF,CAAC;AACJ,CAAC"}
@@ -8,6 +8,11 @@ export interface LoginReadinessParams {
8
8
  export interface LoginReadinessCheck {
9
9
  name: string;
10
10
  pass: boolean;
11
+ /**
12
+ * `false` when the check does not matter for the assumed integration (it
13
+ * then never makes `ready` false). Absent from older servers.
14
+ */
15
+ applies?: boolean;
11
16
  observed: unknown;
12
17
  /** What was seen, and the fix when it fails. */
13
18
  detail: string;
@@ -17,9 +22,20 @@ export interface LoginReadiness {
17
22
  origin: string;
18
23
  live: boolean;
19
24
  data_region?: string | null;
25
+ /** Every check that applies to the assumed integration passes. */
20
26
  ready: boolean;
27
+ /** The integration the checks assumed for this origin, and why. */
28
+ integration?: LoginReadinessIntegration;
21
29
  checks: LoginReadinessCheck[];
22
30
  }
31
+ export interface LoginReadinessIntegration {
32
+ /** `redirect`: hosted-redirect OIDC, so the `cors` and `session_cookie` checks do not apply. */
33
+ assumed: 'redirect' | 'embedded';
34
+ /** `declared` | `embedded_traffic` | `redirect_clients` | `default`. */
35
+ source: 'declared' | 'embedded_traffic' | 'redirect_clients' | 'default';
36
+ /** A plain sentence saying why. */
37
+ reason: string;
38
+ }
23
39
  export interface RequestInspectorParams {
24
40
  /** The browser origin making the request. */
25
41
  origin: string;
@@ -1 +1 @@
1
- {"version":3,"file":"diagnostics.js","sourceRoot":"","sources":["../../src/client/diagnostics.ts"],"names":[],"mappings":";;AAsDA,kDAmBC;AAnBD,SAAgB,mBAAmB,CAAC,OAAkB;IACpD,OAAO;QACL,yGAAyG;QACzG,cAAc,CAAC,MAA4B;YACzC,OAAO,OAAO,CAAC;gBACb,MAAM,EAAE,KAAK;gBACb,IAAI,EAAE,iCAAiC;gBACvC,KAAK,EAAE,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE;aACpD,CAAC,CAAC;QACL,CAAC;QACD,uFAAuF;QACvF,gBAAgB,CAAC,MAA8B;YAC7C,OAAO,OAAO,CAAC;gBACb,MAAM,EAAE,KAAK;gBACb,IAAI,EAAE,mCAAmC;gBACzC,KAAK,EAAE,EAAE,GAAG,MAAM,EAAE;aACrB,CAAC,CAAC;QACL,CAAC;KACF,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"diagnostics.js","sourceRoot":"","sources":["../../src/client/diagnostics.ts"],"names":[],"mappings":";;AAuEA,kDAmBC;AAnBD,SAAgB,mBAAmB,CAAC,OAAkB;IACpD,OAAO;QACL,yGAAyG;QACzG,cAAc,CAAC,MAA4B;YACzC,OAAO,OAAO,CAAC;gBACb,MAAM,EAAE,KAAK;gBACb,IAAI,EAAE,iCAAiC;gBACvC,KAAK,EAAE,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE;aACpD,CAAC,CAAC;QACL,CAAC;QACD,uFAAuF;QACvF,gBAAgB,CAAC,MAA8B;YAC7C,OAAO,OAAO,CAAC;gBACb,MAAM,EAAE,KAAK;gBACb,IAAI,EAAE,mCAAmC;gBACzC,KAAK,EAAE,EAAE,GAAG,MAAM,EAAE;aACrB,CAAC,CAAC;QACL,CAAC;KACF,CAAC;AACJ,CAAC"}
@@ -1,7 +1,7 @@
1
1
  import { type RequestFn } from './request';
2
2
  import type { ListPage } from './types';
3
3
  /** The transactional email a template drives. */
4
- export type EmailTemplateName = 'verification_code' | 'magic_link' | 'password_reset' | 'password_changed' | 'account_exists_notice' | 'organization_invitation' | 'new_device_sign_in' | 'session_revoked_token_reuse' | 'account_locked' | 'team_member_added' | 'team_role_changed';
4
+ export type EmailTemplateName = 'verification_code' | 'magic_link' | 'password_reset' | 'password_changed' | 'account_exists_notice' | 'organization_invitation' | 'new_device_sign_in' | 'session_revoked_token_reuse' | 'account_locked' | 'account_secured' | 'team_member_added' | 'team_role_changed';
5
5
  /** The customizable body of a template: a subject line and a text body. */
6
6
  export interface EmailTemplateBody {
7
7
  subject?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"email-templates.js","sourceRoot":"","sources":["../../src/client/email-templates.ts"],"names":[],"mappings":";;AAwDA,wDAkBC;AA1ED,uCAAgD;AAwDhD,SAAgB,sBAAsB,CAAC,OAAkB;IACvD,OAAO;QACL,IAAI;YACF,OAAO,OAAO,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,qBAAqB,EAAE,CAAC,CAAC;QACjE,CAAC;QACD,2EAA2E;QAC3E,OAAO,CAAC,IAAY,EAAE,OAA0B,EAAE;YAChD,OAAO,OAAO,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,uBAAuB,IAAA,aAAG,EAAC,IAAI,CAAC,UAAU,EAAE,IAAI,EAAE,CAAC,CAAC;QAC7F,CAAC;QACD,2DAA2D;QAC3D,MAAM,CAAC,IAAY,EAAE,IAAuB;YAC1C,OAAO,OAAO,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,uBAAuB,IAAA,aAAG,EAAC,IAAI,CAAC,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC;QACpF,CAAC;QACD,8BAA8B;QAC9B,MAAM,CAAC,IAAY;YACjB,OAAO,OAAO,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,uBAAuB,IAAA,aAAG,EAAC,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC;QACjF,CAAC;KACF,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"email-templates.js","sourceRoot":"","sources":["../../src/client/email-templates.ts"],"names":[],"mappings":";;AAyDA,wDAkBC;AA3ED,uCAAgD;AAyDhD,SAAgB,sBAAsB,CAAC,OAAkB;IACvD,OAAO;QACL,IAAI;YACF,OAAO,OAAO,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,qBAAqB,EAAE,CAAC,CAAC;QACjE,CAAC;QACD,2EAA2E;QAC3E,OAAO,CAAC,IAAY,EAAE,OAA0B,EAAE;YAChD,OAAO,OAAO,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,uBAAuB,IAAA,aAAG,EAAC,IAAI,CAAC,UAAU,EAAE,IAAI,EAAE,CAAC,CAAC;QAC7F,CAAC;QACD,2DAA2D;QAC3D,MAAM,CAAC,IAAY,EAAE,IAAuB;YAC1C,OAAO,OAAO,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,uBAAuB,IAAA,aAAG,EAAC,IAAI,CAAC,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC;QACpF,CAAC;QACD,8BAA8B;QAC9B,MAAM,CAAC,IAAY;YACjB,OAAO,OAAO,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,uBAAuB,IAAA,aAAG,EAAC,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC;QACjF,CAAC;KACF,CAAC;AACJ,CAAC"}
@@ -47,6 +47,16 @@ export declare const AtlasErrorCodes: {
47
47
  readonly CAPTCHA_IN_USE: "CAPTCHA_IN_USE";
48
48
  /** Strict mode: the body had a field this endpoint does not accept (`param` names it). */
49
49
  readonly UNKNOWN_FIELD: "UNKNOWN_FIELD";
50
+ /** 422 — the body set a field that is not writable on this endpoint (`param` names it; the message names the call that changes it). */
51
+ readonly READ_ONLY_FIELD: "READ_ONLY_FIELD";
52
+ /** 409 — the development email sender is not available on a production instance. */
53
+ readonly DEVELOPMENT_EMAIL_NOT_ALLOWED: "DEVELOPMENT_EMAIL_NOT_ALLOWED";
54
+ /** 503 — the development email sender is not available on this deployment. */
55
+ readonly DEVELOPMENT_EMAIL_UNAVAILABLE: "DEVELOPMENT_EMAIL_UNAVAILABLE";
56
+ /** 429 — the development sender's daily or per-recipient hourly cap is spent (`meta.window`, `meta.retry_after_seconds`). */
57
+ readonly DEVELOPMENT_EMAIL_LIMIT_REACHED: "DEVELOPMENT_EMAIL_LIMIT_REACHED";
58
+ /** 422 — the development sender sends only Atlas's own sign-in emails, never custom templates. */
59
+ readonly DEVELOPMENT_EMAIL_TEMPLATE_NOT_ALLOWED: "DEVELOPMENT_EMAIL_TEMPLATE_NOT_ALLOWED";
50
60
  /** The billing plan still has subscribers and cannot be removed. */
51
61
  readonly PLAN_IN_USE: "PLAN_IN_USE";
52
62
  /** The key is valid but for another API surface (e.g. a platform key on the Backend API). */
@@ -43,6 +43,16 @@ exports.AtlasErrorCodes = {
43
43
  CAPTCHA_IN_USE: 'CAPTCHA_IN_USE',
44
44
  /** Strict mode: the body had a field this endpoint does not accept (`param` names it). */
45
45
  UNKNOWN_FIELD: 'UNKNOWN_FIELD',
46
+ /** 422 — the body set a field that is not writable on this endpoint (`param` names it; the message names the call that changes it). */
47
+ READ_ONLY_FIELD: 'READ_ONLY_FIELD',
48
+ /** 409 — the development email sender is not available on a production instance. */
49
+ DEVELOPMENT_EMAIL_NOT_ALLOWED: 'DEVELOPMENT_EMAIL_NOT_ALLOWED',
50
+ /** 503 — the development email sender is not available on this deployment. */
51
+ DEVELOPMENT_EMAIL_UNAVAILABLE: 'DEVELOPMENT_EMAIL_UNAVAILABLE',
52
+ /** 429 — the development sender's daily or per-recipient hourly cap is spent (`meta.window`, `meta.retry_after_seconds`). */
53
+ DEVELOPMENT_EMAIL_LIMIT_REACHED: 'DEVELOPMENT_EMAIL_LIMIT_REACHED',
54
+ /** 422 — the development sender sends only Atlas's own sign-in emails, never custom templates. */
55
+ DEVELOPMENT_EMAIL_TEMPLATE_NOT_ALLOWED: 'DEVELOPMENT_EMAIL_TEMPLATE_NOT_ALLOWED',
46
56
  /** The billing plan still has subscribers and cannot be removed. */
47
57
  PLAN_IN_USE: 'PLAN_IN_USE',
48
58
  /** The key is valid but for another API surface (e.g. a platform key on the Backend API). */
@@ -1 +1 @@
1
- {"version":3,"file":"error.js","sourceRoot":"","sources":["../../src/client/error.ts"],"names":[],"mappings":";;;AAoBA,MAAa,aAAc,SAAQ,KAAK;IACpB,IAAI,GAAG,eAAe,CAAC;IACzC,0CAA0C;IACjC,MAAM,CAAS;IACxB,8CAA8C;IACrC,MAAM,CAAmB;IAElC,YAAY,MAAc,EAAE,MAAwB,EAAE,OAAgB;QACpE,KAAK,CAAC,OAAO,IAAI,MAAM,CAAC,CAAC,CAAC,EAAE,OAAO,IAAI,wCAAwC,MAAM,EAAE,CAAC,CAAC;QACzF,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,uEAAuE;QACvE,sEAAsE;QACtE,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,aAAa,CAAC,SAAS,CAAC,CAAC;IACvD,CAAC;IAED,uEAAuE;IACvE,IAAI,IAAI;QACN,OAAO,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC;IAC9B,CAAC;IAED,yEAAyE;IACzE,OAAO,CAAC,IAAY;QAClB,OAAO,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC;IAClD,CAAC;CACF;AAzBD,sCAyBC;AAED;;;;GAIG;AACU,QAAA,eAAe,GAAG;IAC7B,+EAA+E;IAC/E,6BAA6B,EAAE,+BAA+B;IAC9D,mEAAmE;IACnE,uBAAuB,EAAE,yBAAyB;IAClD,oGAAoG;IACpG,qBAAqB,EAAE,uBAAuB;IAC9C,oGAAoG;IACpG,qBAAqB,EAAE,uBAAuB;IAC9C,wGAAwG;IACxG,cAAc,EAAE,gBAAgB;IAChC,0FAA0F;IAC1F,aAAa,EAAE,eAAe;IAC9B,oEAAoE;IACpE,WAAW,EAAE,aAAa;IAC1B,6FAA6F;IAC7F,iBAAiB,EAAE,mBAAmB;IACtC,gDAAgD;IAChD,WAAW,EAAE,aAAa;IAC1B,SAAS,EAAE,WAAW;IACtB,iBAAiB,EAAE,mBAAmB;IACtC,YAAY,EAAE,cAAc;IAC5B,4FAA4F;IAC5F,oBAAoB,EAAE,sBAAsB;IAC5C,yFAAyF;IACzF,cAAc,EAAE,gBAAgB;IAChC,eAAe,EAAE,iBAAiB;IAClC,oBAAoB,EAAE,sBAAsB;IAC5C,eAAe,EAAE,iBAAiB;IAClC,aAAa,EAAE,eAAe;IAC9B,SAAS,EAAE,WAAW;IACtB,QAAQ,EAAE,UAAU;CACZ,CAAC"}
1
+ {"version":3,"file":"error.js","sourceRoot":"","sources":["../../src/client/error.ts"],"names":[],"mappings":";;;AAoBA,MAAa,aAAc,SAAQ,KAAK;IACpB,IAAI,GAAG,eAAe,CAAC;IACzC,0CAA0C;IACjC,MAAM,CAAS;IACxB,8CAA8C;IACrC,MAAM,CAAmB;IAElC,YAAY,MAAc,EAAE,MAAwB,EAAE,OAAgB;QACpE,KAAK,CAAC,OAAO,IAAI,MAAM,CAAC,CAAC,CAAC,EAAE,OAAO,IAAI,wCAAwC,MAAM,EAAE,CAAC,CAAC;QACzF,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,uEAAuE;QACvE,sEAAsE;QACtE,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,aAAa,CAAC,SAAS,CAAC,CAAC;IACvD,CAAC;IAED,uEAAuE;IACvE,IAAI,IAAI;QACN,OAAO,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC;IAC9B,CAAC;IAED,yEAAyE;IACzE,OAAO,CAAC,IAAY;QAClB,OAAO,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC;IAClD,CAAC;CACF;AAzBD,sCAyBC;AAED;;;;GAIG;AACU,QAAA,eAAe,GAAG;IAC7B,+EAA+E;IAC/E,6BAA6B,EAAE,+BAA+B;IAC9D,mEAAmE;IACnE,uBAAuB,EAAE,yBAAyB;IAClD,oGAAoG;IACpG,qBAAqB,EAAE,uBAAuB;IAC9C,oGAAoG;IACpG,qBAAqB,EAAE,uBAAuB;IAC9C,wGAAwG;IACxG,cAAc,EAAE,gBAAgB;IAChC,0FAA0F;IAC1F,aAAa,EAAE,eAAe;IAC9B,uIAAuI;IACvI,eAAe,EAAE,iBAAiB;IAClC,oFAAoF;IACpF,6BAA6B,EAAE,+BAA+B;IAC9D,8EAA8E;IAC9E,6BAA6B,EAAE,+BAA+B;IAC9D,6HAA6H;IAC7H,+BAA+B,EAAE,iCAAiC;IAClE,kGAAkG;IAClG,sCAAsC,EAAE,wCAAwC;IAChF,oEAAoE;IACpE,WAAW,EAAE,aAAa;IAC1B,6FAA6F;IAC7F,iBAAiB,EAAE,mBAAmB;IACtC,gDAAgD;IAChD,WAAW,EAAE,aAAa;IAC1B,SAAS,EAAE,WAAW;IACtB,iBAAiB,EAAE,mBAAmB;IACtC,YAAY,EAAE,cAAc;IAC5B,4FAA4F;IAC5F,oBAAoB,EAAE,sBAAsB;IAC5C,yFAAyF;IACzF,cAAc,EAAE,gBAAgB;IAChC,eAAe,EAAE,iBAAiB;IAClC,oBAAoB,EAAE,sBAAsB;IAC5C,eAAe,EAAE,iBAAiB;IAClC,aAAa,EAAE,eAAe;IAC9B,SAAS,EAAE,WAAW;IACtB,QAAQ,EAAE,UAAU;CACZ,CAAC"}
@@ -341,7 +341,7 @@ declare function buildNamespaces(request: RequestFn): {
341
341
  get(id: string): Promise<import("./oauth-clients").OAuthClient>;
342
342
  create(body: import("./oauth-clients").CreateOAuthClientBody, idempotencyKey?: string): Promise<import("./oauth-clients").OAuthClientWithSecret>;
343
343
  update(id: string, body: import("./oauth-clients").UpdateOAuthClientBody): Promise<import("./oauth-clients").OAuthClient>;
344
- rotateSecret(id: string): Promise<import("./oauth-clients").OAuthClientWithSecret>;
344
+ rotateSecret(id: string, body?: import("./oauth-clients").RotateOAuthClientSecretBody, idempotencyKey?: string): Promise<import("./oauth-clients").OAuthClientSecretRotation>;
345
345
  delete(id: string): Promise<{
346
346
  object: "oauth_client";
347
347
  id: string;
@@ -841,6 +841,16 @@ declare function buildNamespaces(request: RequestFn): {
841
841
  deleteChannel(channel: "email" | "sms", options?: {
842
842
  force?: boolean;
843
843
  }): Promise<import("./messaging").DeletedMessagingProvider>;
844
+ developmentEmail: {
845
+ get(): Promise<import("./messaging").DevelopmentEmail>;
846
+ enable(body: import("./messaging").EnableDevelopmentEmailBody): Promise<import("./messaging").DevelopmentEmail>;
847
+ disable(): Promise<{
848
+ object: "development_email";
849
+ deleted: true;
850
+ provider_id: string;
851
+ mode: "standard";
852
+ }>;
853
+ };
844
854
  managedEmail: {
845
855
  get(): Promise<import("./messaging").ManagedEmail>;
846
856
  create(body: import("./messaging").CreateManagedEmailBody): Promise<import("./messaging").ManagedEmail>;
@@ -12,13 +12,40 @@ export interface Instance {
12
12
  environment: string;
13
13
  publishable_key: string;
14
14
  frontend_api_host: string;
15
+ /**
16
+ * The `iss` this instance's session tokens carry. It changes when a custom
17
+ * domain goes active, so read it (and the two URLs below) rather than hard-coding it.
18
+ */
19
+ issuer: string;
20
+ /** The OpenID discovery document whose `issuer` equals {@link issuer}. */
21
+ discovery_url: string;
22
+ /** The JWKS holding this instance's signing keys. */
23
+ jwks_uri: string;
15
24
  allowed_origins: string[];
25
+ /**
26
+ * The declared integration per allowed origin (`redirect` or `embedded`). An
27
+ * origin not listed is inferred by preflight and login readiness.
28
+ */
29
+ allowed_origin_integrations?: Record<string, 'redirect' | 'embedded'>;
16
30
  auth_config: Metadata;
17
31
  created_at: number;
18
32
  }
33
+ /**
34
+ * How an allowed origin signs in: `redirect` (hosted-redirect OIDC: the app
35
+ * never runs the embedded components), `embedded` (the sign-in components run
36
+ * on the origin), or `auto` / `null` (clear the hint; Atlas infers it).
37
+ */
38
+ export type OriginIntegration = 'redirect' | 'embedded' | 'auto';
39
+ /** An `allowed_origins` entry that also declares how the origin signs in. */
40
+ export interface AllowedOriginEntry {
41
+ origin: string;
42
+ integration?: OriginIntegration | null;
43
+ }
44
+ /** An origin string, or `{ origin, integration }`. A plain string keeps the origin's current hint. */
45
+ export type AllowedOriginInput = string | AllowedOriginEntry;
19
46
  export interface UpdateInstanceBody {
20
- /** Exact origins only — a wildcard or a path is rejected (§13.1). */
21
- allowed_origins?: string[];
47
+ /** Exact origins only — a wildcard or a path is rejected (§13.1). The list replaces the stored one. */
48
+ allowed_origins?: AllowedOriginInput[];
22
49
  auth_config?: Record<string, unknown>;
23
50
  }
24
51
  /** `PATCH /v1/instance` echoes only the mutable fields, not a full Instance. */
@@ -26,6 +53,7 @@ export interface InstanceUpdateResult {
26
53
  object: 'instance';
27
54
  id: string;
28
55
  allowed_origins: string[];
56
+ allowed_origin_integrations?: Record<string, 'redirect' | 'embedded'>;
29
57
  auth_config: Metadata;
30
58
  }
31
59
  /** One problem (or note) the setup check found. */
@@ -1 +1 @@
1
- {"version":3,"file":"instance.js","sourceRoot":"","sources":["../../src/client/instance.ts"],"names":[],"mappings":";;AAyRA,4CAiEC;AAjED,SAAgB,gBAAgB,CAAC,OAAkB;IACjD,OAAO;QACL,GAAG;YACD,OAAO,OAAO,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,cAAc,EAAE,CAAC,CAAC;QAC1D,CAAC;QACD,MAAM,CAAC,IAAwB;YAC7B,OAAO,OAAO,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,cAAc,EAAE,IAAI,EAAE,CAAC,CAAC;QAClE,CAAC;QACD,yFAAyF;QACzF,SAAS;YACP,OAAO,OAAO,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,wBAAwB,EAAE,CAAC,CAAC;QACpE,CAAC;QACD;;;;WAIG;QACH,UAAU,CAAC,IAAuB;YAChC,OAAO,OAAO,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,0BAA0B,EAAE,IAAI,EAAE,CAAC,CAAC;QAC7E,CAAC;QACD,2EAA2E;QAC3E,WAAW;YACT,OAAO,OAAO,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,0BAA0B,EAAE,CAAC,CAAC;QACtE,CAAC;QACD,+CAA+C;QAC/C,YAAY,EAAE;YACZ,MAAM,EAAE;gBACN,GAAG;oBACD,OAAO,OAAO,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,kCAAkC,EAAE,CAAC,CAAC;gBAC9E,CAAC;gBACD,GAAG,CAAC,IAA8B;oBAChC,OAAO,OAAO,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,kCAAkC,EAAE,IAAI,EAAE,CAAC,CAAC;gBACpF,CAAC;gBACD,MAAM;oBACJ,OAAO,OAAO,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,kCAAkC,EAAE,CAAC,CAAC;gBACjF,CAAC;aACF;YACD,UAAU,EAAE;gBACV,GAAG;oBACD,OAAO,OAAO,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,sCAAsC,EAAE,CAAC,CAAC;gBAClF,CAAC;gBACD,qCAAqC;gBACrC,GAAG,CAAC,IAAkC;oBACpC,OAAO,OAAO,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,sCAAsC,EAAE,IAAI,EAAE,CAAC,CAAC;gBACxF,CAAC;gBACD,MAAM;oBACJ,OAAO,OAAO,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,sCAAsC,EAAE,CAAC,CAAC;gBACrF,CAAC;aACF;SACF;QACD,oEAAoE;QACpE,aAAa,EAAE;YACb,GAAG;gBACD,OAAO,OAAO,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,6BAA6B,EAAE,CAAC,CAAC;YACzE,CAAC;YACD,kCAAkC;YAClC,GAAG,CAAC,IAA0B;gBAC5B,OAAO,OAAO,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,6BAA6B,EAAE,IAAI,EAAE,CAAC,CAAC;YAC/E,CAAC;YACD,+EAA+E;YAC/E,MAAM;gBACJ,OAAO,OAAO,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,6BAA6B,EAAE,CAAC,CAAC;YAC5E,CAAC;SACF;KACF,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"instance.js","sourceRoot":"","sources":["../../src/client/instance.ts"],"names":[],"mappings":";;AAwTA,4CAiEC;AAjED,SAAgB,gBAAgB,CAAC,OAAkB;IACjD,OAAO;QACL,GAAG;YACD,OAAO,OAAO,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,cAAc,EAAE,CAAC,CAAC;QAC1D,CAAC;QACD,MAAM,CAAC,IAAwB;YAC7B,OAAO,OAAO,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,cAAc,EAAE,IAAI,EAAE,CAAC,CAAC;QAClE,CAAC;QACD,yFAAyF;QACzF,SAAS;YACP,OAAO,OAAO,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,wBAAwB,EAAE,CAAC,CAAC;QACpE,CAAC;QACD;;;;WAIG;QACH,UAAU,CAAC,IAAuB;YAChC,OAAO,OAAO,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,0BAA0B,EAAE,IAAI,EAAE,CAAC,CAAC;QAC7E,CAAC;QACD,2EAA2E;QAC3E,WAAW;YACT,OAAO,OAAO,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,0BAA0B,EAAE,CAAC,CAAC;QACtE,CAAC;QACD,+CAA+C;QAC/C,YAAY,EAAE;YACZ,MAAM,EAAE;gBACN,GAAG;oBACD,OAAO,OAAO,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,kCAAkC,EAAE,CAAC,CAAC;gBAC9E,CAAC;gBACD,GAAG,CAAC,IAA8B;oBAChC,OAAO,OAAO,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,kCAAkC,EAAE,IAAI,EAAE,CAAC,CAAC;gBACpF,CAAC;gBACD,MAAM;oBACJ,OAAO,OAAO,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,kCAAkC,EAAE,CAAC,CAAC;gBACjF,CAAC;aACF;YACD,UAAU,EAAE;gBACV,GAAG;oBACD,OAAO,OAAO,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,sCAAsC,EAAE,CAAC,CAAC;gBAClF,CAAC;gBACD,qCAAqC;gBACrC,GAAG,CAAC,IAAkC;oBACpC,OAAO,OAAO,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,sCAAsC,EAAE,IAAI,EAAE,CAAC,CAAC;gBACxF,CAAC;gBACD,MAAM;oBACJ,OAAO,OAAO,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,sCAAsC,EAAE,CAAC,CAAC;gBACrF,CAAC;aACF;SACF;QACD,oEAAoE;QACpE,aAAa,EAAE;YACb,GAAG;gBACD,OAAO,OAAO,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,6BAA6B,EAAE,CAAC,CAAC;YACzE,CAAC;YACD,kCAAkC;YAClC,GAAG,CAAC,IAA0B;gBAC5B,OAAO,OAAO,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,6BAA6B,EAAE,IAAI,EAAE,CAAC,CAAC;YAC/E,CAAC;YACD,+EAA+E;YAC/E,MAAM;gBACJ,OAAO,OAAO,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,6BAA6B,EAAE,CAAC,CAAC;YAC5E,CAAC;SACF;KACF,CAAC;AACJ,CAAC"}
@@ -193,6 +193,12 @@ interface ProviderBodyBase<T extends MessagingTransport> {
193
193
  * provider accepts. SMS: the sender number in E.164 format, e.g. `+15551234567`.
194
194
  */
195
195
  from_address?: string;
196
+ /**
197
+ * Email only. `development`: a development instance sends without a verified
198
+ * domain (Atlas sign-in templates only, `[Atlas test] ` subjects, capped);
199
+ * 409 on a production instance. Omitted on an update: the stored mode is kept.
200
+ */
201
+ mode?: 'standard' | 'development';
196
202
  }
197
203
  /**
198
204
  * First setup of transport `T` (or a switch to it): `config` carries every
@@ -246,8 +252,12 @@ type PublicConfigKey<T extends MessagingTransport> = Exclude<keyof MessagingTran
246
252
  */
247
253
  export interface MessagingProviderOf<C extends 'email' | 'sms', T extends MessagingTransport> {
248
254
  object: 'messaging_provider';
255
+ /** The provider id: pass it as `provider_id` to managed email or the development sender. */
256
+ id?: string;
249
257
  channel: C;
250
258
  transport: T;
259
+ /** How it sends: `standard` | `development` | `managed_domain`. */
260
+ mode?: 'standard' | 'development' | 'managed_domain';
251
261
  from_address: string | null;
252
262
  status: string;
253
263
  /** Non-secret config only — secret values are never returned. */
@@ -280,10 +290,16 @@ export interface MessagingProviders {
280
290
  status: string;
281
291
  verified: boolean;
282
292
  } | null;
293
+ /** The email provider in development mode, when it is. */
294
+ development_email?: {
295
+ object: 'development_email';
296
+ enabled: true;
297
+ from_address: string | null;
298
+ } | null;
283
299
  }
284
300
  /** A DNS record to publish for the managed sending domain. */
285
301
  export interface ManagedEmailRecord {
286
- purpose: 'dkim' | 'spf' | 'return_path' | 'other';
302
+ purpose: 'dkim' | 'spf' | 'return_path' | 'ownership' | 'dmarc' | 'other';
287
303
  type: 'TXT' | 'MX' | 'CNAME';
288
304
  /** Fully-qualified record name. */
289
305
  name: string;
@@ -291,22 +307,24 @@ export interface ManagedEmailRecord {
291
307
  priority?: number;
292
308
  status?: string;
293
309
  }
294
- /** Atlas-managed email: your own sending domain on Atlas's email account. */
310
+ /** Managed email: Atlas sets your sending domain up on your own Resend or Brevo account and sends through your key. */
295
311
  export interface ManagedEmail {
296
312
  object: 'managed_email';
297
313
  id: string;
298
314
  from_address: string;
299
315
  domain: string;
300
316
  provider: string;
317
+ /** The email service the domain lives on (your own account there): `resend` | `brevo`. */
318
+ backend?: 'resend' | 'brevo';
319
+ /** Your email provider (mode `managed_domain`) that holds the API key. */
320
+ provider_id?: string | null;
301
321
  region: string | null;
302
322
  /** `not_started` | `pending` | `verified` | `failed` | `temporary_failure`. Email sends only when verified. */
303
323
  status: string;
304
324
  verified: boolean;
305
325
  records: ManagedEmailRecord[];
306
- limits: {
307
- hourly: number;
308
- daily: number;
309
- };
326
+ /** Always null: no Atlas limits apply, your provider account's own do. */
327
+ limits: null;
310
328
  usage: {
311
329
  sent: number;
312
330
  bounces: number;
@@ -314,7 +332,9 @@ export interface ManagedEmail {
314
332
  last_bounce_at: number | null;
315
333
  last_complaint_at: number | null;
316
334
  };
317
- reputation: 'shared_platform_account';
335
+ reputation: 'your_provider_account';
336
+ /** Brevo, on create: whether the bounce / complaint webhook was registered on your account. */
337
+ feedback_webhook?: 'registered' | 'not_registered';
318
338
  /** DNS automation, when set. The token is never returned. */
319
339
  dns: {
320
340
  provider: string;
@@ -332,6 +352,14 @@ export interface ManagedEmail {
332
352
  export interface CreateManagedEmailBody {
333
353
  /** The From address, bare or `"Name <address>"`. Its domain becomes the sending domain. */
334
354
  from_address: string;
355
+ /** Your own provider account Atlas sets the domain up on. Required with `config` unless `provider_id` is given. */
356
+ transport?: 'resend' | 'brevo';
357
+ /** Your provider API key (write-only). */
358
+ config?: {
359
+ api_key: string;
360
+ };
361
+ /** Instead of `transport` + `config`: your configured Resend or Brevo email provider. */
362
+ provider_id?: string;
335
363
  region?: 'us-east-1' | 'eu-west-1' | 'sa-east-1' | 'ap-northeast-1';
336
364
  /** Let Atlas write the DNS records. The token is write-only. */
337
365
  dns?: {
@@ -346,7 +374,39 @@ export interface DeletedManagedEmail {
346
374
  object: 'managed_email';
347
375
  domain: string;
348
376
  deleted: true;
377
+ /** Whether the email provider that held the key (mode `managed_domain`) was removed too. */
378
+ provider_removed?: boolean;
349
379
  }
380
+ /** Your own email provider in development mode (development instances, no verified domain). */
381
+ export interface DevelopmentEmail {
382
+ object: 'development_email';
383
+ provider_id: string;
384
+ enabled: true;
385
+ /** False on a production instance (never sends) or when the provider is disabled. */
386
+ active: boolean;
387
+ transport: string;
388
+ from_address: string | null;
389
+ subject_prefix: string;
390
+ footer: string;
391
+ templates: string[];
392
+ limits: {
393
+ daily: number;
394
+ per_recipient_hourly: number;
395
+ };
396
+ /** Present for onboarding@resend.dev: Resend delivers it only to your Resend account address. */
397
+ note?: string;
398
+ next_step?: string;
399
+ updated_at: number;
400
+ }
401
+ /** `{ provider_id }`, or your provider exactly as `messaging.setEmail` takes it. */
402
+ export type EnableDevelopmentEmailBody = {
403
+ provider_id: string;
404
+ from_address?: string;
405
+ } | {
406
+ transport: 'resend' | 'ses' | 'smtp' | 'brevo' | 'mailgun';
407
+ config?: Record<string, string>;
408
+ from_address?: string;
409
+ };
350
410
  export interface MessagingTestResult {
351
411
  object: 'messaging_test';
352
412
  channel: string;
@@ -397,7 +457,24 @@ export declare function messagingResource(request: RequestFn): {
397
457
  deleteChannel(channel: "email" | "sms", options?: {
398
458
  force?: boolean;
399
459
  }): Promise<DeletedMessagingProvider>;
400
- /** Atlas-managed email: send from your own domain without your own email provider. */
460
+ /**
461
+ * Your own email provider in development mode: a development instance sends
462
+ * Atlas's sign-in emails without a verified domain ([Atlas test] subjects, capped).
463
+ */
464
+ developmentEmail: {
465
+ /** 404 when the provider is not in development mode. */
466
+ get(): Promise<DevelopmentEmail>;
467
+ /** Turn development mode on. 409 `DEVELOPMENT_EMAIL_NOT_ALLOWED` on a production instance. */
468
+ enable(body: EnableDevelopmentEmailBody): Promise<DevelopmentEmail>;
469
+ /** Back to standard mode; the provider stays. */
470
+ disable(): Promise<{
471
+ object: "development_email";
472
+ deleted: true;
473
+ provider_id: string;
474
+ mode: "standard";
475
+ }>;
476
+ };
477
+ /** Managed email: Atlas sets your sending domain up on your own Resend or Brevo account. */
401
478
  managedEmail: {
402
479
  /** 404 when none is configured. */
403
480
  get(): Promise<ManagedEmail>;
@@ -40,7 +40,25 @@ function messagingResource(request) {
40
40
  ...(options.force ? { query: { force: 'true' } } : {}),
41
41
  });
42
42
  },
43
- /** Atlas-managed email: send from your own domain without your own email provider. */
43
+ /**
44
+ * Your own email provider in development mode: a development instance sends
45
+ * Atlas's sign-in emails without a verified domain ([Atlas test] subjects, capped).
46
+ */
47
+ developmentEmail: {
48
+ /** 404 when the provider is not in development mode. */
49
+ get() {
50
+ return request({ method: 'GET', path: '/v1/messaging_providers/email/development' });
51
+ },
52
+ /** Turn development mode on. 409 `DEVELOPMENT_EMAIL_NOT_ALLOWED` on a production instance. */
53
+ enable(body) {
54
+ return request({ method: 'POST', path: '/v1/messaging_providers/email/development', body });
55
+ },
56
+ /** Back to standard mode; the provider stays. */
57
+ disable() {
58
+ return request({ method: 'DELETE', path: '/v1/messaging_providers/email/development' });
59
+ },
60
+ },
61
+ /** Managed email: Atlas sets your sending domain up on your own Resend or Brevo account. */
44
62
  managedEmail: {
45
63
  /** 404 when none is configured. */
46
64
  get() {