@baliola/auth-sdk 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md ADDED
@@ -0,0 +1,76 @@
1
+ # Changelog
2
+
3
+ All notable changes to `@baliola/auth-sdk` are documented here.
4
+
5
+ ## 0.2.0
6
+
7
+ Distribution change and clean-ups; no method-level API changes.
8
+
9
+ - chore(release): publish to **public npm** (`https://registry.npmjs.org`) instead of GitHub Packages. Installing no longer requires a Personal Access Token, an `.npmrc` registry override, or SSO authorization — plain `bun add @baliola/auth-sdk` works in every environment.
10
+ - docs: rewrite `README.md`, `docs/install.md`, `docs/ciSetup.md`, and `docs/troubleshooting.md` to reflect public-npm install. CI snippets no longer need build secrets.
11
+ - Consumers previously installing from GitHub Packages should remove any `@baliola:registry=https://npm.pkg.github.com` entries from `~/.npmrc` and project-local `.npmrc` files before upgrading.
12
+ - fix(types): realign `AccessTokenPayload` with the server's current JWT shape. The flat `projectId?: string | null` / `projectName?: string | null` pair is replaced by `projects: AccessTokenPayloadProject[]` (each entry carries `{ id, name, clientId }`). Type-only change — affects consumers that decode the JWT manually and reference this type. A scoped login (`clientId` supplied) yields a single-entry array; a central login yields every project the account has active roles in.
13
+ - chore!: remove the `./internal` subpath export and the `internal/` source folder (`createValidateCache`, `newIdempotencyKey`, `consumeHeaders`). Those helpers targeted the retired `/internal/api-keys/*` endpoints and the static "internal token" concept. Service-to-service callers now authenticate with an admin-tier API key (`baliola_admin_…`) against `/admin/api-keys/*`; cache + idempotency helpers for that surface will ship in a dedicated admin SDK.
14
+
15
+ ## 0.1.0
16
+
17
+ Breaking redesign of the auth API and SDK surface to eliminate dev confusion between the two email flows. Old method names and endpoint paths are removed; no aliases.
18
+
19
+ ### Breaking changes
20
+
21
+ - feat(sdk)!: replace the flat method surface with namespaced flows — `auth.emailOtp.*`, `auth.emailPassword.*`, `auth.google.*`. The namespaces match the DB provider names (`email_otp`, `email_password`).
22
+ - feat(sdk)!: rename verb-overloaded methods so each one's purpose is unambiguous at the call site:
23
+ - `auth.requestEmailOtp` → `auth.emailOtp.sendLoginCode`
24
+ - `auth.verifyEmailOtp` → `auth.emailOtp.verifyLoginCode`
25
+ - `auth.register` → `auth.emailPassword.register`
26
+ - `auth.verifyRegistration` → `auth.emailPassword.verifyRegistrationCode`
27
+ - `auth.loginWithPassword` → `auth.emailPassword.login`
28
+ - `auth.setPassword` → `auth.emailPassword.setPassword`
29
+ - `auth.changePassword` → `auth.emailPassword.changePassword`
30
+ - `auth.loginWithGoogle` → `auth.google.login`
31
+ - feat(sdk)!: verify-code methods take `{ email, code }` instead of `{ email, otp }`.
32
+ - feat(sdk)!: `auth.emailPassword.register` no longer branches on `'otp_sent' | 'linked'` — it always sends a verification OTP and returns `{ otp: OtpInfo }`. The single-step "linked" path is removed.
33
+ - feat(sdk)!: send/resend/register success responses replace `expiresInMinutes` with a structured `OtpInfo` payload — `expiresInSeconds`, `expiresAt`, `canResendInSeconds`, `resendsRemaining`.
34
+ - feat(sdk)!: backend endpoint paths renamed to match the namespaces: `POST /auth/email-otp/send-login-code`, `/auth/email-otp/verify-login-code`, `/auth/email-otp/resend-login-code`, `/auth/email-password/register`, `/auth/email-password/verify-registration-code`, `/auth/email-password/resend-registration-code`, etc.
35
+
36
+ ### New features
37
+
38
+ - feat(sdk): add typed error subclasses under `AuthError` — `OtpInvalidError` (with `attemptsRemaining`, `canResendInSeconds`), `OtpExpiredError`, `MaxAttemptsError` (with `retryAfterSeconds`), `NoPendingOtpError`, `ResendCooldownError` (with `canResendInSeconds`), `MaxResendsError`, `InvalidCredentialsError`, `NoPasswordSetError`, `AccountSuspendedError`, `EmailAlreadyHasPasswordError`, `CaptchaFailedError`, `RateLimitedError` (with `retryAfterSeconds`, `retryAfterHuman`), `InvalidPasswordError` (with `reason`), `InvalidEmailError`. Branch on `instanceof` for TS narrowing, or `err.code` for stable string matching. Backend responses now carry the structured context fields via `error.details` so the SDK lifts them into the typed properties.
39
+ - feat(sdk): add dedicated resend methods — `auth.emailOtp.resendLoginCode` and `auth.emailPassword.resendRegistrationCode`. Each enforces its own resend cooldown (`OTP_RESEND_COOLDOWN_SECONDS`, default 60s) and per-OTP cap (`OTP_MAX_RESENDS`, default 3) on top of the per-OTP attempts limit.
40
+ - feat(sdk): add `captchaToken` option on every public unauthenticated entrypoint (`sendLoginCode`, `resendLoginCode`, `register`, `resendRegistrationCode`, `login`). Backend verifies via Cloudflare Turnstile when `TURNSTILE_SECRET_KEY` is set; dev bypass when unset; server-to-server clients can be allowlisted via `CAPTCHA_BYPASS_CLIENT_IDS`.
41
+ - feat(sdk): `auth.emailOtp.sendLoginCode` response includes `flow: 'login' | 'signup'` and `methods: ('password' | 'passwordless')[]` so the UI can adapt to the account state without an extra round-trip.
42
+
43
+ ### Internal
44
+
45
+ - refactor(sdk): backend collapses the two OTP namespaces (`otp:*` and `email_verify:*`) into a single Redis record per email, stamped with `purpose: 'passwordless' | 'register_password'`. Latest-wins on conflicts; the pending bcrypt hash for password registration is held inside the record until verify, so no account/provider rows are written before email ownership is proven.
46
+ - docs(sdk): rewrite README around namespaced flows + typed error taxonomy; rewrite `docs/concepts.md` with sequence diagrams for each flow and the shared OTP-store model.
47
+
48
+ ## 0.0.5
49
+
50
+ - feat(sdk): add cross-tab session sync via `BroadcastChannel` — when one tab logs in, logs out, or refreshes the access token, every other tab on the same origin updates its in-memory mirror in real time.
51
+ - feat(sdk): add `auth.fetch({ requireAuth: true })` per-call option that throws `AuthError(401)` before sending when no session is loaded.
52
+ - feat(sdk): add `auth.fetch({ disableRefresh: true })` per-call option that skips both proactive and reactive refresh, letting a 401 propagate to the caller.
53
+
54
+ ## 0.0.4
55
+
56
+ - feat(sdk): add `changePassword({ currentPassword, newPassword })` paired with `POST /auth/email-password/change-password` server route — verifies the current password, rotates the bcrypt hash, revokes every other session for the account (caller stays signed in), and sends a confirmation email out-of-band.
57
+ - chore(auth): relax password policy to require only 8+ characters and at least one uppercase letter (number and special-character requirements dropped).
58
+
59
+ ## 0.0.3
60
+
61
+ - chore(sdk): bump version to 0.0.3
62
+ - feat(sdk): expose `Project.allowedOrigins` on `AuthSession`
63
+
64
+ ## 0.0.2
65
+
66
+ - chore(sdk): bump version to 0.0.2
67
+ - refactor(sdk): drop pre-release version labels from public JSDoc
68
+ - docs(sdk): inline install in README and move roadmap to ROADMAP.md
69
+ - docs(sdk): add user install, troubleshooting, and CI setup guides
70
+ - docs(sdk): add README with usage and configuration reference
71
+ - test(sdk): add unit tests for client, transport, and store
72
+ - feat(sdk): implement auth client with auto-refresh, session mirror, and consumer fetch helper
73
+
74
+ ## 0.0.1
75
+
76
+ - feat(sdk): scaffold `@baliola/auth-sdk` package config
package/LICENSE ADDED
@@ -0,0 +1,37 @@
1
+ Copyright (c) 2026 Baliola. All rights reserved.
2
+
3
+ PROPRIETARY AND CONFIDENTIAL
4
+
5
+ This software, including its source code, compiled output, type definitions,
6
+ documentation, and any associated materials (collectively, the "Software"), is
7
+ the proprietary property of Baliola. All rights, title, and interest in and to
8
+ the Software are and shall remain the exclusive property of Baliola.
9
+
10
+ Access to the Software is granted only to parties expressly authorized by
11
+ Baliola, and solely under the terms of a separate written agreement with
12
+ Baliola. No rights or licenses are granted by implication, estoppel, or
13
+ otherwise.
14
+
15
+ Without Baliola's prior written consent, the following are prohibited:
16
+
17
+ 1. Redistribution of the Software, in whole or in part, in source or
18
+ compiled form, to any third party.
19
+ 2. Modification, adaptation, translation, or creation of derivative works
20
+ based on the Software.
21
+ 3. Reverse engineering, decompilation, or disassembly of the Software,
22
+ except to the extent such restriction is expressly prohibited by
23
+ applicable law.
24
+ 4. Sublicensing, leasing, renting, selling, or otherwise transferring any
25
+ rights in the Software.
26
+ 5. Removing, altering, or obscuring any copyright, trademark, or other
27
+ proprietary notices appearing in the Software.
28
+
29
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
30
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
31
+ FITNESS FOR A PARTICULAR PURPOSE, TITLE, AND NONINFRINGEMENT. IN NO EVENT
32
+ SHALL BALIOLA, ITS AFFILIATES, OR CONTRIBUTORS BE LIABLE FOR ANY CLAIM,
33
+ DAMAGES, OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT, OR
34
+ OTHERWISE, ARISING FROM, OUT OF, OR IN CONNECTION WITH THE SOFTWARE OR THE
35
+ USE OR OTHER DEALINGS IN THE SOFTWARE.
36
+
37
+ For licensing inquiries, contact: <info@baliola.io>
package/README.md ADDED
@@ -0,0 +1,305 @@
1
+ # @baliola/auth-sdk
2
+
3
+ Client SDK for the Baliola Auth service. Two clearly separated email flows (passwordless OTP, password + verification OTP) plus Google login, with typed methods, typed errors, auto-refresh, and cross-tab sync.
4
+
5
+ For deeper detail see [docs/concepts.md](./docs/concepts.md).
6
+
7
+ ## Install
8
+
9
+ ```sh
10
+ bun add @baliola/auth-sdk
11
+ # or: npm install @baliola/auth-sdk
12
+ # or: pnpm add @baliola/auth-sdk
13
+ ```
14
+
15
+ Published to public npm under the `@baliola` scope — no registry config or auth token required.
16
+
17
+ **Runtime**: Bun (any recent), Node 20.3+, Chrome 116+, Safari 17+, Firefox 124+. Zero runtime deps. ESM only.
18
+
19
+ ## Quick start
20
+
21
+ ```ts
22
+ import { createAuthClient, localStorageStore, OtpInvalidError } from '@baliola/auth-sdk';
23
+
24
+ const auth = createAuthClient({
25
+ baseUrl: 'https://baliola-auth.baliola.dev',
26
+ clientId: 'your-project-client-id', // omit for a central multi-project login
27
+ store: localStorageStore(), // omit for in-memory (server-side / tests)
28
+ });
29
+ await auth.loadSession(); // rehydrate from store at app boot
30
+
31
+ // Passwordless login
32
+ const { otp } = await auth.emailOtp.sendLoginCode({
33
+ email: 'user@example.com',
34
+ captchaToken: turnstileToken,
35
+ });
36
+ showOtpForm({ resendIn: otp.canResendInSeconds });
37
+
38
+ try {
39
+ const session = await auth.emailOtp.verifyLoginCode({
40
+ email: 'user@example.com',
41
+ code: '123456',
42
+ });
43
+ } catch (e) {
44
+ if (e instanceof OtpInvalidError) {
45
+ showError(`Wrong code. ${e.attemptsRemaining} tries left.`);
46
+ } else throw e;
47
+ }
48
+
49
+ auth.isAuthenticated(); // true
50
+ await auth.logout();
51
+ ```
52
+
53
+ ## API
54
+
55
+ The SDK is namespaced by flow so that each method's purpose is unambiguous at the call site.
56
+
57
+ ### `auth.emailOtp` — passwordless flow
58
+
59
+ ```ts
60
+ auth.emailOtp.sendLoginCode({ email, captchaToken });
61
+ // → { flow: 'login'|'signup', methods: ('password'|'passwordless')[], otp: OtpInfo }
62
+
63
+ auth.emailOtp.verifyLoginCode({ email, code });
64
+ // → AuthSession (also populates the session mirror)
65
+
66
+ auth.emailOtp.resendLoginCode({ email, captchaToken });
67
+ // → { otp: OtpInfo }
68
+ ```
69
+
70
+ ### `auth.emailPassword` — password flow
71
+
72
+ ```ts
73
+ auth.emailPassword.register({ email, password, captchaToken });
74
+ // → { otp: OtpInfo } (sends a verification OTP; bcrypt hash held server-side)
75
+
76
+ auth.emailPassword.verifyRegistrationCode({ email, code });
77
+ // → AuthSession (finalizes the account; populates the session mirror)
78
+
79
+ auth.emailPassword.resendRegistrationCode({ email, captchaToken });
80
+ // → { otp: OtpInfo }
81
+
82
+ auth.emailPassword.login({ email, password, captchaToken });
83
+ // → AuthSession
84
+
85
+ // Authenticated:
86
+ auth.emailPassword.setPassword({ password });
87
+ auth.emailPassword.changePassword({ currentPassword, newPassword });
88
+ // ↑ revokes every other session for the account on success
89
+ ```
90
+
91
+ ### `auth.google`
92
+
93
+ ```ts
94
+ // idToken is obtained from Google Sign-In on the frontend.
95
+ auth.google.login({ idToken }); // → AuthSession
96
+ ```
97
+
98
+ ### Session lifecycle
99
+
100
+ ```ts
101
+ await auth.refresh(); // → AuthSession (manual; SDK also refreshes proactively/reactively)
102
+ await auth.logout(); // → void
103
+
104
+ auth.getSession(); // AuthSession | null (sync)
105
+ auth.isAuthenticated(); // boolean (sync)
106
+ await auth.loadSession(); // rehydrate from store at app boot
107
+
108
+ const unsub = auth.onSessionChange((session) => {
109
+ /* AuthSession | null */
110
+ });
111
+ auth.onSessionChange(handler, { immediate: false }); // future-only
112
+ unsub();
113
+ ```
114
+
115
+ ### `auth.fetch` — call your own backend
116
+
117
+ `Authorization: Bearer …` is attached automatically; `X-Session-ID` is **never** sent to consumer URLs. 401 → `/auth/refresh` → retry-once happens transparently.
118
+
119
+ ```ts
120
+ const res = await auth.fetch('https://api.example/things', {
121
+ method: 'POST',
122
+ body: JSON.stringify({ ping: 1 }),
123
+ signal: ctrl.signal,
124
+ requireAuth: true, // throw AuthError(401) before sending if no session
125
+ disableRefresh: true, // skip both proactive + reactive refresh for this call
126
+ });
127
+ if (!res.ok) {
128
+ /* res.status, res.json(), etc. */
129
+ }
130
+ ```
131
+
132
+ `auth.fetch` returns the `Response` for any consumer-URL non-2xx. It only throws `AuthError` when the SDK's own `/auth/refresh` sub-call fails.
133
+
134
+ ### Lifecycle
135
+
136
+ ```ts
137
+ auth.close(); // release cross-tab BroadcastChannel; optional
138
+ ```
139
+
140
+ ## Errors
141
+
142
+ Every error subclass extends `AuthError`. Branch on `instanceof` (preferred for TS narrowing) or `err.code`.
143
+
144
+ ```ts
145
+ import {
146
+ AuthError,
147
+ OtpInvalidError,
148
+ OtpExpiredError,
149
+ MaxAttemptsError,
150
+ ResendCooldownError,
151
+ MaxResendsError,
152
+ NoPendingOtpError,
153
+ InvalidCredentialsError,
154
+ NoPasswordSetError,
155
+ AccountSuspendedError,
156
+ EmailAlreadyHasPasswordError,
157
+ CaptchaFailedError,
158
+ RateLimitedError,
159
+ InvalidPasswordError,
160
+ InvalidEmailError,
161
+ } from '@baliola/auth-sdk';
162
+
163
+ try {
164
+ await auth.emailOtp.verifyLoginCode({ email, code });
165
+ } catch (e) {
166
+ if (e instanceof OtpInvalidError)
167
+ return show(`${e.attemptsRemaining} tries left, resend in ${e.canResendInSeconds}s`);
168
+ if (e instanceof OtpExpiredError) return showResendPrompt();
169
+ if (e instanceof MaxAttemptsError) return show(`Too many tries. Wait ${e.retryAfterSeconds}s`);
170
+ if (e instanceof RateLimitedError) return show(`Rate limited. ${e.retryAfterHuman}`);
171
+ throw e;
172
+ }
173
+ ```
174
+
175
+ | Error class | `code` | Status | Carries |
176
+ | ------------------------------ | ---------------------------- | ------ | ------------------------------------------ |
177
+ | `OtpInvalidError` | `otp_invalid` | 400 | `attemptsRemaining`, `canResendInSeconds` |
178
+ | `OtpExpiredError` | `otp_expired` | 400 | — |
179
+ | `MaxAttemptsError` | `max_attempts_exceeded` | 429 | `retryAfterSeconds` |
180
+ | `NoPendingOtpError` | `no_pending_otp` | 400 | — |
181
+ | `ResendCooldownError` | `resend_cooldown` | 429 | `canResendInSeconds` |
182
+ | `MaxResendsError` | `max_resends_exceeded` | 429 | — |
183
+ | `InvalidCredentialsError` | `invalid_credentials` | 401 | — |
184
+ | `NoPasswordSetError` | `no_password_set` | 400 | — |
185
+ | `AccountSuspendedError` | `account_suspended` | 403 | — |
186
+ | `EmailAlreadyHasPasswordError` | `email_already_has_password` | 409 | — |
187
+ | `CaptchaFailedError` | `captcha_failed` | 400 | — |
188
+ | `InvalidPasswordError` | `invalid_password` | 400 | `reason: 'too_short'\|'no_uppercase'\|...` |
189
+ | `InvalidEmailError` | `invalid_email` | 400 | — |
190
+ | `RateLimitedError` | (legacy 429) | 429 | `retryAfterSeconds`, `retryAfterHuman` |
191
+ | `AuthError` (base) | varies | varies | `status: 0` for network/timeout |
192
+
193
+ ## Stores
194
+
195
+ ```ts
196
+ import { memoryStore, localStorageStore, type SessionStore } from '@baliola/auth-sdk';
197
+
198
+ memoryStore(); // default — per-process, no persistence
199
+ localStorageStore({ key? }); // browser only, persists across reloads
200
+
201
+ const cookieStore: SessionStore = {
202
+ get() { /* AuthSession | null */ },
203
+ set(s) { /* persist or clear when s === null */ },
204
+ };
205
+ ```
206
+
207
+ Cross-tab sync (login / logout / refresh) is automatic via `BroadcastChannel`, regardless of store choice.
208
+
209
+ ## Configuration
210
+
211
+ ```ts
212
+ createAuthClient({
213
+ baseUrl: 'https://baliola-auth.baliola.dev', // required, no trailing slash
214
+ clientId: 'your-project-client-id', // optional; scopes the session to one project. Omit for a central login that carries every project the account has active roles in.
215
+ store: memoryStore(), // default; or localStorageStore() / custom
216
+ fetch: globalThis.fetch, // override for tests / interceptors
217
+ timeoutMs: 15_000, // per-request, default 15s
218
+ proactiveRefresh: { leadTimeMs: 60_000 }, // or `false` to disable; default 60s lead
219
+ onError: (e, source) => console.error('[auth-sdk]', source, e),
220
+ });
221
+ ```
222
+
223
+ `clientId` is constructor-only — there is no per-call override. To switch projects, create a new client.
224
+
225
+ ## Decoding the JWT
226
+
227
+ The SDK does not parse the access token; consumers that need claims (e.g. server-side authorization in a Node service) decode it with their own library. The exported `AccessTokenPayload` type matches the server's current claim shape.
228
+
229
+ ```ts
230
+ import { decodeJwt } from 'jose';
231
+ import type { AccessTokenPayload } from '@baliola/auth-sdk';
232
+
233
+ const session = auth.getSession();
234
+ if (session) {
235
+ const claims = decodeJwt<AccessTokenPayload>(session.accessToken);
236
+ // claims.accountId, claims.email
237
+ // claims.projects: { id, name, clientId }[] // single entry for a scoped login; many for a central login
238
+ // claims.roles?: string[]; claims.permissions?: string[]
239
+ }
240
+ ```
241
+
242
+ ## Sample response shapes
243
+
244
+ **`auth.emailOtp.sendLoginCode` success:**
245
+
246
+ ```json
247
+ {
248
+ "flow": "signup",
249
+ "methods": ["passwordless"],
250
+ "otp": {
251
+ "expiresInSeconds": 300,
252
+ "expiresAt": "2026-05-04T12:05:00.000Z",
253
+ "canResendInSeconds": 60,
254
+ "resendsRemaining": 3
255
+ }
256
+ }
257
+ ```
258
+
259
+ **`auth.emailPassword.register` / resend success:**
260
+
261
+ ```json
262
+ {
263
+ "otp": {
264
+ "expiresInSeconds": 300,
265
+ "expiresAt": "...",
266
+ "canResendInSeconds": 60,
267
+ "resendsRemaining": 3
268
+ }
269
+ }
270
+ ```
271
+
272
+ **`auth.emailOtp.verifyLoginCode` / `auth.emailPassword.verifyRegistrationCode` / `auth.emailPassword.login` / `auth.google.login` success (`AuthSession`):**
273
+
274
+ ```json
275
+ {
276
+ "accessToken": "eyJhbGciOiJIUzI1NiIs...",
277
+ "refreshToken": "session-uuid",
278
+ "expiresIn": 3600,
279
+ "account": { "id": "...", "email": "user@example.com", "status": "active" },
280
+ "project": { "id": "...", "name": "...", "displayName": null, "allowedOrigins": [...] },
281
+ "roles": ["user"],
282
+ "permissions": []
283
+ }
284
+ ```
285
+
286
+ **Verify-code failure (e.g. `OtpInvalidError`):**
287
+
288
+ ```json
289
+ {
290
+ "message": "Invalid verification code. 2 attempt(s) remaining.",
291
+ "data": null,
292
+ "error": {
293
+ "code": "otp_invalid",
294
+ "details": {
295
+ "name": "OtpInvalidError",
296
+ "attemptsRemaining": 2,
297
+ "canResendInSeconds": 30
298
+ }
299
+ }
300
+ }
301
+ ```
302
+
303
+ ## License
304
+
305
+ Proprietary — see [LICENSE](./LICENSE).
@@ -0,0 +1,102 @@
1
+ //#region src/errors/authError.d.ts
2
+ type AuthErrorInit = {
3
+ status: number; /** Stable machine-readable code (e.g. 'otp_invalid'). May be empty for network failures. */
4
+ code?: string;
5
+ serverMessage: string;
6
+ serverName?: string;
7
+ details?: unknown;
8
+ cause?: unknown;
9
+ };
10
+ /**
11
+ * Base class for every error thrown by the SDK. Concrete error subclasses
12
+ * (`OtpInvalidError`, `RateLimitedError`, etc.) extend this. Consumers can
13
+ * branch on either `instanceof` (preferred for TS narrowing) or `err.code`
14
+ * (when narrowing by string).
15
+ *
16
+ * - `status`: HTTP status from the server, or `0` for network/timeout failures.
17
+ * - `code`: stable machine-readable code (see error subclasses for values).
18
+ * - `serverMessage`: server's `message` field (for display or logs).
19
+ * - `serverName`: server's error class name from `error.details.name`.
20
+ * - `details`: server's `error.details` as-is (carries context fields like
21
+ * `attemptsRemaining`, `canResendInSeconds`, `retryAfterSeconds`).
22
+ * - `cause`: original underlying error (e.g. the `TypeError` from a network failure).
23
+ */
24
+ declare class AuthError extends Error {
25
+ readonly status: number;
26
+ readonly code: string;
27
+ readonly serverMessage: string;
28
+ readonly serverName?: string;
29
+ readonly details?: unknown;
30
+ readonly cause?: unknown;
31
+ constructor(init: AuthErrorInit);
32
+ }
33
+ declare class OtpInvalidError extends AuthError {
34
+ readonly attemptsRemaining: number;
35
+ readonly canResendInSeconds: number;
36
+ constructor(init: AuthErrorInit & {
37
+ attemptsRemaining: number;
38
+ canResendInSeconds: number;
39
+ });
40
+ }
41
+ declare class OtpExpiredError extends AuthError {
42
+ constructor(init: AuthErrorInit);
43
+ }
44
+ declare class MaxAttemptsError extends AuthError {
45
+ readonly retryAfterSeconds: number;
46
+ constructor(init: AuthErrorInit & {
47
+ retryAfterSeconds: number;
48
+ });
49
+ }
50
+ declare class NoPendingOtpError extends AuthError {
51
+ constructor(init: AuthErrorInit);
52
+ }
53
+ declare class ResendCooldownError extends AuthError {
54
+ readonly canResendInSeconds: number;
55
+ constructor(init: AuthErrorInit & {
56
+ canResendInSeconds: number;
57
+ });
58
+ }
59
+ declare class MaxResendsError extends AuthError {
60
+ constructor(init: AuthErrorInit);
61
+ }
62
+ declare class InvalidCredentialsError extends AuthError {
63
+ constructor(init: AuthErrorInit);
64
+ }
65
+ declare class NoPasswordSetError extends AuthError {
66
+ constructor(init: AuthErrorInit);
67
+ }
68
+ declare class AccountSuspendedError extends AuthError {
69
+ constructor(init: AuthErrorInit);
70
+ }
71
+ declare class EmailAlreadyHasPasswordError extends AuthError {
72
+ constructor(init: AuthErrorInit);
73
+ }
74
+ declare class CaptchaFailedError extends AuthError {
75
+ constructor(init: AuthErrorInit);
76
+ }
77
+ declare class RateLimitedError extends AuthError {
78
+ readonly retryAfterSeconds: number;
79
+ readonly retryAfterHuman: string;
80
+ constructor(init: AuthErrorInit & {
81
+ retryAfterSeconds: number;
82
+ retryAfterHuman: string;
83
+ });
84
+ }
85
+ declare class InvalidPasswordError extends AuthError {
86
+ readonly reason: 'too_short' | 'no_uppercase' | 'too_weak' | 'unknown';
87
+ constructor(init: AuthErrorInit & {
88
+ reason: 'too_short' | 'no_uppercase' | 'too_weak' | 'unknown';
89
+ });
90
+ }
91
+ declare class InvalidEmailError extends AuthError {
92
+ constructor(init: AuthErrorInit);
93
+ }
94
+ /**
95
+ * Map a backend error response onto the right AuthError subclass.
96
+ * Falls back to `AuthError` (with status + code preserved) for unknown codes.
97
+ */
98
+ declare function authErrorFromResponse(response: Response): Promise<AuthError>;
99
+ /** Network/timeout failures land here. */
100
+ declare function authErrorFromFetchFailure(cause: unknown, isTimeout?: boolean): AuthError;
101
+ //#endregion
102
+ export { authErrorFromFetchFailure as _, EmailAlreadyHasPasswordError as a, InvalidPasswordError as c, NoPasswordSetError as d, NoPendingOtpError as f, ResendCooldownError as g, RateLimitedError as h, CaptchaFailedError as i, MaxAttemptsError as l, OtpInvalidError as m, AuthError as n, InvalidCredentialsError as o, OtpExpiredError as p, AuthErrorInit as r, InvalidEmailError as s, AccountSuspendedError as t, MaxResendsError as u, authErrorFromResponse as v };