@koolbase/core 12.0.0 → 12.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.
@@ -101,6 +101,14 @@ export declare class NetworkError extends KoolbaseAuthError {
101
101
  export declare class InvalidPhoneNumberError extends KoolbaseAuthError {
102
102
  constructor();
103
103
  }
104
+ /**
105
+ * The project has switched off signing in with an emailed code — 403
106
+ * `email_code_disabled`. Applies to every address alike, so it reveals
107
+ * nothing about which accounts exist.
108
+ */
109
+ export declare class EmailCodeDisabledError extends KoolbaseAuthError {
110
+ constructor();
111
+ }
104
112
  export declare class OtpExpiredError extends KoolbaseAuthError {
105
113
  constructor();
106
114
  }
@@ -249,3 +257,29 @@ export declare class InvalidGoogleTokenError extends KoolbaseAuthError {
249
257
  export declare class GoogleEmailRequiredError extends KoolbaseAuthError {
250
258
  constructor();
251
259
  }
260
+ /**
261
+ * The first factor passed and this account needs a second: sign-in is not
262
+ * finished. Pass `challengeToken` to `verifyMfa` with a code from the
263
+ * person's authenticator app, or to `verifyRecoveryCode`. Apps that never
264
+ * enable MFA never see this.
265
+ */
266
+ export declare class MfaRequiredError extends KoolbaseAuthError {
267
+ readonly challengeToken: string;
268
+ readonly expiresAt?: string;
269
+ constructor(challengeToken: string, expiresAt?: string);
270
+ }
271
+ export declare class RecentAuthRequiredError extends KoolbaseAuthError {
272
+ constructor();
273
+ }
274
+ export declare class RecentMfaRequiredError extends KoolbaseAuthError {
275
+ constructor();
276
+ }
277
+ export declare class MfaAlreadyEnabledError extends KoolbaseAuthError {
278
+ constructor();
279
+ }
280
+ export declare class MfaEnrollmentNotFoundError extends KoolbaseAuthError {
281
+ constructor();
282
+ }
283
+ export declare class MfaNotEnabledError extends KoolbaseAuthError {
284
+ constructor();
285
+ }
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.GoogleEmailRequiredError = exports.InvalidGoogleTokenError = exports.MalformedSessionResponseError = exports.ProviderIdentityAlreadyLinkedError = exports.IdentityNotFoundError = exports.InsufficientScopeError = exports.HideRequiresVerificationError = exports.LastCredentialError = exports.InsufficientAuthorityError = exports.SessionRequiredError = exports.UnsupportedOAuthProviderError = exports.OAuthOnlyAccountError = exports.AccountExistsError = exports.CurrentPasswordIncorrectError = exports.TokenAlreadyUsedError = exports.TokenExpiredError = exports.SignupsDisabledError = exports.ContactNotVerifiedError = exports.GoogleSignInNotConfiguredError = exports.OAuthEmailConflictError = exports.AppleEmailRequiredError = exports.InvalidAppleTokenError = exports.AppleSignInNotConfiguredError = exports.SmsConfigMissingError = exports.PhoneAlreadyLinkedError = exports.OtpRateLimitError = exports.OtpMaxAttemptsError = exports.OtpInvalidError = exports.OtpExpiredError = exports.InvalidPhoneNumberError = exports.NetworkError = exports.VerificationResendDailyCapError = exports.VerificationResendCooldownError = exports.RateLimitError = exports.UnlockTokenInvalidError = exports.AccountLockedError = exports.TokenRevokedError = exports.SessionExpiredError = exports.WeakPasswordError = exports.UserDisabledError = exports.EmailAlreadyInUseError = exports.InvalidCredentialsError = exports.KoolbaseAuthError = void 0;
3
+ exports.MfaNotEnabledError = exports.MfaEnrollmentNotFoundError = exports.MfaAlreadyEnabledError = exports.RecentMfaRequiredError = exports.RecentAuthRequiredError = exports.MfaRequiredError = exports.GoogleEmailRequiredError = exports.InvalidGoogleTokenError = exports.MalformedSessionResponseError = exports.ProviderIdentityAlreadyLinkedError = exports.IdentityNotFoundError = exports.InsufficientScopeError = exports.HideRequiresVerificationError = exports.LastCredentialError = exports.InsufficientAuthorityError = exports.SessionRequiredError = exports.UnsupportedOAuthProviderError = exports.OAuthOnlyAccountError = exports.AccountExistsError = exports.CurrentPasswordIncorrectError = exports.TokenAlreadyUsedError = exports.TokenExpiredError = exports.SignupsDisabledError = exports.ContactNotVerifiedError = exports.GoogleSignInNotConfiguredError = exports.OAuthEmailConflictError = exports.AppleEmailRequiredError = exports.InvalidAppleTokenError = exports.AppleSignInNotConfiguredError = exports.SmsConfigMissingError = exports.PhoneAlreadyLinkedError = exports.OtpRateLimitError = exports.OtpMaxAttemptsError = exports.OtpInvalidError = exports.OtpExpiredError = exports.EmailCodeDisabledError = exports.InvalidPhoneNumberError = exports.NetworkError = exports.VerificationResendDailyCapError = exports.VerificationResendCooldownError = exports.RateLimitError = exports.UnlockTokenInvalidError = exports.AccountLockedError = exports.TokenRevokedError = exports.SessionExpiredError = exports.WeakPasswordError = exports.UserDisabledError = exports.EmailAlreadyInUseError = exports.InvalidCredentialsError = exports.KoolbaseAuthError = void 0;
4
4
  const errors_js_1 = require("./errors.js");
5
5
  /**
6
6
  * Base error type for all Koolbase auth errors. Catchable via
@@ -179,6 +179,19 @@ class InvalidPhoneNumberError extends KoolbaseAuthError {
179
179
  }
180
180
  }
181
181
  exports.InvalidPhoneNumberError = InvalidPhoneNumberError;
182
+ /**
183
+ * The project has switched off signing in with an emailed code — 403
184
+ * `email_code_disabled`. Applies to every address alike, so it reveals
185
+ * nothing about which accounts exist.
186
+ */
187
+ class EmailCodeDisabledError extends KoolbaseAuthError {
188
+ constructor() {
189
+ super('Signing in with an emailed code is switched off for this project', 'email_code_disabled');
190
+ this.name = 'EmailCodeDisabledError';
191
+ Object.setPrototypeOf(this, EmailCodeDisabledError.prototype);
192
+ }
193
+ }
194
+ exports.EmailCodeDisabledError = EmailCodeDisabledError;
182
195
  class OtpExpiredError extends KoolbaseAuthError {
183
196
  constructor() {
184
197
  super('OTP has expired, please request a new code', 'otp_expired');
@@ -472,3 +485,60 @@ class GoogleEmailRequiredError extends KoolbaseAuthError {
472
485
  }
473
486
  }
474
487
  exports.GoogleEmailRequiredError = GoogleEmailRequiredError;
488
+ // ─── Two-step sign-in (MFA) ─────────────────────────────────────────────────
489
+ /**
490
+ * The first factor passed and this account needs a second: sign-in is not
491
+ * finished. Pass `challengeToken` to `verifyMfa` with a code from the
492
+ * person's authenticator app, or to `verifyRecoveryCode`. Apps that never
493
+ * enable MFA never see this.
494
+ */
495
+ class MfaRequiredError extends KoolbaseAuthError {
496
+ constructor(challengeToken, expiresAt) {
497
+ super('Two-step sign-in required', 'mfa_required');
498
+ this.name = 'MfaRequiredError';
499
+ this.challengeToken = challengeToken;
500
+ this.expiresAt = expiresAt;
501
+ Object.setPrototypeOf(this, MfaRequiredError.prototype);
502
+ }
503
+ }
504
+ exports.MfaRequiredError = MfaRequiredError;
505
+ class RecentAuthRequiredError extends KoolbaseAuthError {
506
+ constructor() {
507
+ super('Sign in again to change two-step sign-in', 'recent_auth_required');
508
+ this.name = 'RecentAuthRequiredError';
509
+ Object.setPrototypeOf(this, RecentAuthRequiredError.prototype);
510
+ }
511
+ }
512
+ exports.RecentAuthRequiredError = RecentAuthRequiredError;
513
+ class RecentMfaRequiredError extends KoolbaseAuthError {
514
+ constructor() {
515
+ super('Confirm with your authenticator first', 'recent_mfa_required');
516
+ this.name = 'RecentMfaRequiredError';
517
+ Object.setPrototypeOf(this, RecentMfaRequiredError.prototype);
518
+ }
519
+ }
520
+ exports.RecentMfaRequiredError = RecentMfaRequiredError;
521
+ class MfaAlreadyEnabledError extends KoolbaseAuthError {
522
+ constructor() {
523
+ super('Two-step sign-in is already on for this account', 'mfa_already_enabled');
524
+ this.name = 'MfaAlreadyEnabledError';
525
+ Object.setPrototypeOf(this, MfaAlreadyEnabledError.prototype);
526
+ }
527
+ }
528
+ exports.MfaAlreadyEnabledError = MfaAlreadyEnabledError;
529
+ class MfaEnrollmentNotFoundError extends KoolbaseAuthError {
530
+ constructor() {
531
+ super('No enrolment in progress; start again', 'mfa_enrollment_not_found');
532
+ this.name = 'MfaEnrollmentNotFoundError';
533
+ Object.setPrototypeOf(this, MfaEnrollmentNotFoundError.prototype);
534
+ }
535
+ }
536
+ exports.MfaEnrollmentNotFoundError = MfaEnrollmentNotFoundError;
537
+ class MfaNotEnabledError extends KoolbaseAuthError {
538
+ constructor() {
539
+ super('Two-step sign-in is not on for this account', 'mfa_not_enabled');
540
+ this.name = 'MfaNotEnabledError';
541
+ Object.setPrototypeOf(this, MfaNotEnabledError.prototype);
542
+ }
543
+ }
544
+ exports.MfaNotEnabledError = MfaNotEnabledError;
@@ -302,12 +302,10 @@ export declare class KoolbaseAuth {
302
302
  validAccessToken(): Promise<string | null>;
303
303
  setSession(session: KoolbaseSession | null): Promise<void>;
304
304
  /**
305
- * @deprecated v1.9.0: Server endpoint /v1/sdk/auth/oauth not yet
306
- * shipped. This method previously routed to /v1/auth/oauth (dashboard
307
- * developer OAuth) which never created project-scoped end-user
308
- * sessions. Properly implemented in v1.10.0 with provider-specific
309
- * server endpoints under /v1/sdk/auth/oauth/{apple,google,github}.
310
- * Use email/password sign-in for now.
305
+ * @deprecated Never implemented; kept only so existing code compiles.
306
+ * Google and Apple sign-in are supported — use signInWithGoogle() or
307
+ * signInWithApple(). This used to say OAuth was not yet shipped and to
308
+ * use email and password instead, which stopped being true long ago.
311
309
  *
312
310
  * @throws Always throws KoolbaseAuthError('not_implemented').
313
311
  */
@@ -318,6 +316,74 @@ export declare class KoolbaseAuth {
318
316
  name?: string;
319
317
  avatarUrl?: string;
320
318
  }): Promise<never>;
319
+ /**
320
+ * Emails a six-digit sign-in code.
321
+ *
322
+ * Resolves the same way whether or not the address has an account, so it
323
+ * cannot be used to check who is registered. A new address receives a code
324
+ * only while the project accepts sign-ups; a disabled account receives
325
+ * nothing.
326
+ *
327
+ * @throws EmailCodeDisabledError if the project has switched this off
328
+ * @throws RateLimitError after too many requests for one address
329
+ */
330
+ requestEmailCode(params: {
331
+ email: string;
332
+ }): Promise<void>;
333
+ /**
334
+ * Signs in with a code from requestEmailCode(), storing the session exactly
335
+ * as login() does. An unknown address becomes an account only if the
336
+ * project still accepts sign-ups at this moment. Each code works once and
337
+ * allows three attempts.
338
+ *
339
+ * @throws OtpInvalidError, OtpExpiredError, OtpMaxAttemptsError
340
+ * @throws EmailCodeDisabledError if the project has switched this off
341
+ */
342
+ signInWithEmailCode(params: {
343
+ email: string;
344
+ code: string;
345
+ }): Promise<KoolbaseSession>;
346
+ private mfaRequired;
347
+ /** Finishes sign-in with a code from the person's authenticator app. */
348
+ verifyMfa(params: {
349
+ challengeToken: string;
350
+ code: string;
351
+ }): Promise<KoolbaseSession>;
352
+ /**
353
+ * Finishes sign-in with a recovery code. Each works once; prompt the person
354
+ * to regenerate when recoveryCodesRemaining reaches zero.
355
+ */
356
+ verifyRecoveryCode(params: {
357
+ challengeToken: string;
358
+ code: string;
359
+ }): Promise<{
360
+ session: KoolbaseSession;
361
+ recoveryCodesRemaining: number;
362
+ }>;
363
+ /**
364
+ * Starts adding an authenticator; needs a sign-in within ten minutes. Show
365
+ * otpauthUri as a QR code, then call confirmMfaEnrollment with the first
366
+ * code the app shows.
367
+ */
368
+ enrollMfa(): Promise<{
369
+ otpauthUri: string;
370
+ secret: string;
371
+ }>;
372
+ /** Turns MFA on and returns ten recovery codes, shown this once only. Other devices are signed out. */
373
+ confirmMfaEnrollment(code: string): Promise<string[]>;
374
+ mfaStatus(): Promise<{
375
+ enabled: boolean;
376
+ recoveryCodesRemaining: number;
377
+ }>;
378
+ /** Re-confirms the second factor, opening ten minutes to change MFA. */
379
+ stepUpMfa(params: {
380
+ code?: string;
381
+ recoveryCode?: string;
382
+ }): Promise<void>;
383
+ /** Turns MFA off. Needs stepUpMfa() within the last ten minutes. */
384
+ disableMfa(): Promise<void>;
385
+ /** Replaces all recovery codes; the old ones stop working. Needs stepUpMfa() within ten minutes. */
386
+ regenerateRecoveryCodes(): Promise<string[]>;
321
387
  sendOtp(params: SendOtpParams): Promise<OtpSendResult>;
322
388
  verifyOtp(params: VerifyOtpParams): Promise<PhoneVerifyResult>;
323
389
  linkPhone(params: LinkPhoneParams): Promise<void>;
package/dist/cjs/auth.js CHANGED
@@ -388,6 +388,8 @@ class KoolbaseAuth {
388
388
  const errorMessage = body?.error ?? '';
389
389
  // ─── code-first ───
390
390
  switch (code) {
391
+ case 'mfa_required':
392
+ throw this.mfaRequired(body);
391
393
  case 'oauth_not_configured':
392
394
  throw new auth_errors_js_1.GoogleSignInNotConfiguredError();
393
395
  case 'invalid_oauth_token':
@@ -442,6 +444,8 @@ class KoolbaseAuth {
442
444
  const errorMessage = body?.error ?? '';
443
445
  // ─── code-first ───
444
446
  switch (code) {
447
+ case 'mfa_required':
448
+ throw this.mfaRequired(body);
445
449
  case 'oauth_not_configured':
446
450
  throw new auth_errors_js_1.AppleSignInNotConfiguredError();
447
451
  case 'invalid_oauth_token':
@@ -824,20 +828,150 @@ class KoolbaseAuth {
824
828
  }
825
829
  // ─── OAuth (DEPRECATED — see v1.10.0) ───────────────────────────────────
826
830
  /**
827
- * @deprecated v1.9.0: Server endpoint /v1/sdk/auth/oauth not yet
828
- * shipped. This method previously routed to /v1/auth/oauth (dashboard
829
- * developer OAuth) which never created project-scoped end-user
830
- * sessions. Properly implemented in v1.10.0 with provider-specific
831
- * server endpoints under /v1/sdk/auth/oauth/{apple,google,github}.
832
- * Use email/password sign-in for now.
831
+ * @deprecated Never implemented; kept only so existing code compiles.
832
+ * Google and Apple sign-in are supported — use signInWithGoogle() or
833
+ * signInWithApple(). This used to say OAuth was not yet shipped and to
834
+ * use email and password instead, which stopped being true long ago.
833
835
  *
834
836
  * @throws Always throws KoolbaseAuthError('not_implemented').
835
837
  */
836
838
  async oauthLogin(_params) {
837
- throw new auth_errors_js_1.KoolbaseAuthError('OAuth sign-in is not yet implemented for the Koolbase SDK. ' +
838
- 'Planned for v1.10.0 (server-side endpoints under ' +
839
- '/v1/sdk/auth/oauth/{provider}). Use email/password authentication ' +
840
- 'in the meantime.', 'not_implemented');
839
+ throw new auth_errors_js_1.KoolbaseAuthError('oauthLogin() was never implemented and is kept only so existing ' +
840
+ 'code compiles. Use signInWithGoogle() or signInWithApple().', 'not_implemented');
841
+ }
842
+ // ─── Email code ─────────────────────────────────────────────────────────
843
+ /**
844
+ * Emails a six-digit sign-in code.
845
+ *
846
+ * Resolves the same way whether or not the address has an account, so it
847
+ * cannot be used to check who is registered. A new address receives a code
848
+ * only while the project accepts sign-ups; a disabled account receives
849
+ * nothing.
850
+ *
851
+ * @throws EmailCodeDisabledError if the project has switched this off
852
+ * @throws RateLimitError after too many requests for one address
853
+ */
854
+ async requestEmailCode(params) {
855
+ const res = await this.authRequest('/v1/sdk/auth/email/code', {
856
+ method: 'POST',
857
+ body: { email: params.email },
858
+ });
859
+ if (!res.ok)
860
+ await this.throwTypedError(res, false);
861
+ }
862
+ /**
863
+ * Signs in with a code from requestEmailCode(), storing the session exactly
864
+ * as login() does. An unknown address becomes an account only if the
865
+ * project still accepts sign-ups at this moment. Each code works once and
866
+ * allows three attempts.
867
+ *
868
+ * @throws OtpInvalidError, OtpExpiredError, OtpMaxAttemptsError
869
+ * @throws EmailCodeDisabledError if the project has switched this off
870
+ */
871
+ async signInWithEmailCode(params) {
872
+ const res = await this.authRequest('/v1/sdk/auth/email/code/verify', {
873
+ method: 'POST',
874
+ body: { email: params.email, code: params.code },
875
+ });
876
+ const session = await this.parseSessionResponse(res, false);
877
+ await this.setSessionInternal(session);
878
+ return session;
879
+ }
880
+ // ─── Two-step sign-in (MFA) ─────────────────────────────────────────────
881
+ //
882
+ // When an account has MFA on, every sign-in method throws MfaRequiredError.
883
+ // Finish with verifyMfa() or verifyRecoveryCode().
884
+ // One builder for every parser, so the four cannot drift apart in how the
885
+ // challenge reaches the app.
886
+ mfaRequired(body) {
887
+ const d = body?.details ?? {};
888
+ return new auth_errors_js_1.MfaRequiredError(String(d.challenge_token ?? ''), d.expires_at);
889
+ }
890
+ /** Finishes sign-in with a code from the person's authenticator app. */
891
+ async verifyMfa(params) {
892
+ const res = await this.authRequest('/v1/sdk/auth/mfa/verify', {
893
+ method: 'POST',
894
+ body: { challenge_token: params.challengeToken, code: params.code },
895
+ });
896
+ const session = await this.parseSessionResponse(res, false);
897
+ await this.setSessionInternal(session);
898
+ return session;
899
+ }
900
+ /**
901
+ * Finishes sign-in with a recovery code. Each works once; prompt the person
902
+ * to regenerate when recoveryCodesRemaining reaches zero.
903
+ */
904
+ async verifyRecoveryCode(params) {
905
+ const res = await this.authRequest('/v1/sdk/auth/mfa/verify-recovery', {
906
+ method: 'POST',
907
+ body: { challenge_token: params.challengeToken, code: params.code },
908
+ });
909
+ if (!res.ok)
910
+ await this.throwTypedError(res, false);
911
+ // Read once: the session and the remaining count come from one body.
912
+ const data = await res.json();
913
+ if (!data?.access_token)
914
+ throw new auth_errors_js_1.MalformedSessionResponseError('an access token');
915
+ if (!data?.refresh_token)
916
+ throw new auth_errors_js_1.MalformedSessionResponseError('a refresh token');
917
+ const session = {
918
+ accessToken: data.access_token,
919
+ refreshToken: data.refresh_token,
920
+ expiresAt: data.expires_at,
921
+ user: this.mapUser(data.user),
922
+ };
923
+ await this.setSessionInternal(session);
924
+ return { session, recoveryCodesRemaining: Number(data.recovery_codes_remaining ?? 0) };
925
+ }
926
+ /**
927
+ * Starts adding an authenticator; needs a sign-in within ten minutes. Show
928
+ * otpauthUri as a QR code, then call confirmMfaEnrollment with the first
929
+ * code the app shows.
930
+ */
931
+ async enrollMfa() {
932
+ const res = await this.authRequest('/v1/sdk/auth/mfa/enroll', { method: 'POST', includeAuth: true });
933
+ await this.checkResponse(res);
934
+ const d = await res.json();
935
+ return { otpauthUri: d.otpauth_uri, secret: d.secret };
936
+ }
937
+ /** Turns MFA on and returns ten recovery codes, shown this once only. Other devices are signed out. */
938
+ async confirmMfaEnrollment(code) {
939
+ const res = await this.authRequest('/v1/sdk/auth/mfa/enroll/confirm', {
940
+ method: 'POST',
941
+ body: { code },
942
+ includeAuth: true,
943
+ });
944
+ await this.checkResponse(res);
945
+ return (await res.json()).recovery_codes;
946
+ }
947
+ async mfaStatus() {
948
+ const res = await this.authRequest('/v1/sdk/auth/mfa', { method: 'GET', includeAuth: true });
949
+ await this.checkResponse(res);
950
+ const d = await res.json();
951
+ return { enabled: !!d.enabled, recoveryCodesRemaining: Number(d.recovery_codes_remaining ?? 0) };
952
+ }
953
+ /** Re-confirms the second factor, opening ten minutes to change MFA. */
954
+ async stepUpMfa(params) {
955
+ const res = await this.authRequest('/v1/sdk/auth/mfa/step-up', {
956
+ method: 'POST',
957
+ body: {
958
+ ...(params.code ? { code: params.code } : {}),
959
+ ...(params.recoveryCode ? { recovery_code: params.recoveryCode } : {}),
960
+ },
961
+ includeAuth: true,
962
+ });
963
+ await this.checkResponse(res);
964
+ }
965
+ /** Turns MFA off. Needs stepUpMfa() within the last ten minutes. */
966
+ async disableMfa() {
967
+ const res = await this.authRequest('/v1/sdk/auth/mfa/disable', { method: 'POST', includeAuth: true });
968
+ await this.checkResponse(res);
969
+ }
970
+ /** Replaces all recovery codes; the old ones stop working. Needs stepUpMfa() within ten minutes. */
971
+ async regenerateRecoveryCodes() {
972
+ const res = await this.authRequest('/v1/sdk/auth/mfa/recovery-codes', { method: 'POST', includeAuth: true });
973
+ await this.checkResponse(res);
974
+ return (await res.json()).recovery_codes;
841
975
  }
842
976
  // ─── Phone OTP ──────────────────────────────────────────────────────────
843
977
  async sendOtp(params) {
@@ -1006,6 +1140,18 @@ class KoolbaseAuth {
1006
1140
  const msg = body.error ?? '';
1007
1141
  // ─── code-first ───
1008
1142
  switch (code) {
1143
+ case 'mfa_required':
1144
+ throw this.mfaRequired(body);
1145
+ case 'recent_auth_required':
1146
+ throw new auth_errors_js_1.RecentAuthRequiredError();
1147
+ case 'recent_mfa_required':
1148
+ throw new auth_errors_js_1.RecentMfaRequiredError();
1149
+ case 'mfa_already_enabled':
1150
+ throw new auth_errors_js_1.MfaAlreadyEnabledError();
1151
+ case 'mfa_enrollment_not_found':
1152
+ throw new auth_errors_js_1.MfaEnrollmentNotFoundError();
1153
+ case 'mfa_not_enabled':
1154
+ throw new auth_errors_js_1.MfaNotEnabledError();
1009
1155
  case 'invalid_credentials':
1010
1156
  throw new auth_errors_js_1.InvalidCredentialsError();
1011
1157
  case 'email_in_use':
@@ -1030,6 +1176,16 @@ class KoolbaseAuth {
1030
1176
  case 'contact_not_verified':
1031
1177
  case 'email_not_verified':
1032
1178
  throw new auth_errors_js_1.ContactNotVerifiedError(msg || undefined);
1179
+ // Sign-in codes. The otp_* codes are shared with phone sign-in; mapped
1180
+ // here, every path that returns them throws the class an app catches.
1181
+ case 'email_code_disabled':
1182
+ throw new auth_errors_js_1.EmailCodeDisabledError();
1183
+ case 'otp_expired':
1184
+ throw new auth_errors_js_1.OtpExpiredError();
1185
+ case 'otp_invalid':
1186
+ throw new auth_errors_js_1.OtpInvalidError();
1187
+ case 'otp_max_attempts':
1188
+ throw new auth_errors_js_1.OtpMaxAttemptsError();
1033
1189
  // Registration
1034
1190
  case 'signups_disabled':
1035
1191
  throw new auth_errors_js_1.SignupsDisabledError(msg || undefined);
@@ -1116,6 +1272,8 @@ class KoolbaseAuth {
1116
1272
  const msg = body.error ?? '';
1117
1273
  // ─── code-first ───
1118
1274
  switch (code) {
1275
+ case 'mfa_required':
1276
+ throw this.mfaRequired(body);
1119
1277
  case 'invalid_phone':
1120
1278
  throw new auth_errors_js_1.InvalidPhoneNumberError();
1121
1279
  case 'otp_expired':
@@ -195,6 +195,52 @@ export declare class KoolbaseConflictError extends KoolbaseDataError {
195
195
  field?: string;
196
196
  constructor(message?: string, field?: string);
197
197
  }
198
+ /**
199
+ * A reference field points at a record that does not exist, is deleted, or
200
+ * lives in another collection — the server responds 400 `reference_invalid`.
201
+ *
202
+ * Checked when the write commits, so it can surface from an insert, an update,
203
+ * an upsert or a batch. In a batch the whole transaction is refused: nothing
204
+ * in it was written.
205
+ */
206
+ export declare class KoolbaseReferenceInvalidError extends KoolbaseDataError {
207
+ constructor(message?: string);
208
+ }
209
+ /**
210
+ * A record could not be deleted because live records still reference it, under
211
+ * a reference declared `on_delete: restrict` — 409 `reference_in_use`.
212
+ *
213
+ * Delete the referencing records first, or in the same batch: the check runs at
214
+ * commit, so one batch may delete a parent and its children in any order.
215
+ */
216
+ export declare class KoolbaseReferenceInUseError extends KoolbaseDataError {
217
+ constructor(message?: string);
218
+ }
219
+ /**
220
+ * A reference could not be declared because existing records already point at
221
+ * records that do not exist — 409 `dangling_references`.
222
+ *
223
+ * `dangling` lists the offending records so they can be repaired; Koolbase
224
+ * never repairs them for you. It is capped at the first 50.
225
+ */
226
+ export declare class KoolbaseDanglingReferencesError extends KoolbaseDataError {
227
+ dangling: Array<{
228
+ record_id: string;
229
+ value: string;
230
+ }>;
231
+ constructor(message?: string, dangling?: Array<{
232
+ record_id: string;
233
+ value: string;
234
+ }>);
235
+ }
236
+ /**
237
+ * A collection could not be deleted because another collection has a reference
238
+ * field pointing at it — 409 `collection_referenced`. Remove that reference
239
+ * first.
240
+ */
241
+ export declare class KoolbaseCollectionReferencedError extends KoolbaseDataError {
242
+ constructor(message?: string);
243
+ }
198
244
  /**
199
245
  * Thrown when the requested record or collection does not exist — the server
200
246
  * responds with 404 and code `not_found` / `record_not_found` /
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.KoolbaseVectorDimensionMismatchError = exports.KoolbaseRateLimitError = exports.KoolbasePermissionError = exports.KoolbaseInsufficientAuthorityError = exports.KoolbaseValidationError = exports.KoolbaseNotFoundError = exports.KoolbaseConflictError = exports.KoolbaseConstraintNotFoundError = exports.KoolbaseConstraintExistsError = exports.KoolbaseAmbiguousMatchError = exports.KoolbaseDuplicateValuesError = exports.KoolbaseBatchFailedError = exports.KoolbaseIdempotencyKeyReusedError = exports.KoolbaseRevisionMismatchError = exports.KoolbaseProviderInvalidError = exports.KoolbaseProviderNotConfiguredError = exports.KoolbaseInvalidEmbeddingConfigError = exports.KoolbaseFieldNotAutoEmbedError = exports.KoolbaseVectorFieldExistsError = exports.KoolbaseStateConflictError = exports.KoolbaseNoChangesError = exports.KoolbaseInvalidBodyError = exports.KoolbaseProjectInvalidError = exports.KoolbaseInvitationInvalidError = exports.KoolbaseSlugTakenError = exports.KoolbaseSeedError = exports.KoolbaseDataError = void 0;
3
+ exports.KoolbaseVectorDimensionMismatchError = exports.KoolbaseRateLimitError = exports.KoolbasePermissionError = exports.KoolbaseInsufficientAuthorityError = exports.KoolbaseValidationError = exports.KoolbaseNotFoundError = exports.KoolbaseCollectionReferencedError = exports.KoolbaseDanglingReferencesError = exports.KoolbaseReferenceInUseError = exports.KoolbaseReferenceInvalidError = exports.KoolbaseConflictError = exports.KoolbaseConstraintNotFoundError = exports.KoolbaseConstraintExistsError = exports.KoolbaseAmbiguousMatchError = exports.KoolbaseDuplicateValuesError = exports.KoolbaseBatchFailedError = exports.KoolbaseIdempotencyKeyReusedError = exports.KoolbaseRevisionMismatchError = exports.KoolbaseProviderInvalidError = exports.KoolbaseProviderNotConfiguredError = exports.KoolbaseInvalidEmbeddingConfigError = exports.KoolbaseFieldNotAutoEmbedError = exports.KoolbaseVectorFieldExistsError = exports.KoolbaseStateConflictError = exports.KoolbaseNoChangesError = exports.KoolbaseInvalidBodyError = exports.KoolbaseProjectInvalidError = exports.KoolbaseInvitationInvalidError = exports.KoolbaseSlugTakenError = exports.KoolbaseSeedError = exports.KoolbaseDataError = void 0;
4
4
  exports.koolbaseDataError = koolbaseDataError;
5
5
  const errors_js_1 = require("./errors.js");
6
6
  /**
@@ -297,6 +297,66 @@ class KoolbaseConflictError extends KoolbaseDataError {
297
297
  }
298
298
  }
299
299
  exports.KoolbaseConflictError = KoolbaseConflictError;
300
+ /**
301
+ * A reference field points at a record that does not exist, is deleted, or
302
+ * lives in another collection — the server responds 400 `reference_invalid`.
303
+ *
304
+ * Checked when the write commits, so it can surface from an insert, an update,
305
+ * an upsert or a batch. In a batch the whole transaction is refused: nothing
306
+ * in it was written.
307
+ */
308
+ class KoolbaseReferenceInvalidError extends KoolbaseDataError {
309
+ constructor(message) {
310
+ super(message ?? 'A reference points at a record that does not exist', 'reference_invalid');
311
+ this.name = 'KoolbaseReferenceInvalidError';
312
+ Object.setPrototypeOf(this, KoolbaseReferenceInvalidError.prototype);
313
+ }
314
+ }
315
+ exports.KoolbaseReferenceInvalidError = KoolbaseReferenceInvalidError;
316
+ /**
317
+ * A record could not be deleted because live records still reference it, under
318
+ * a reference declared `on_delete: restrict` — 409 `reference_in_use`.
319
+ *
320
+ * Delete the referencing records first, or in the same batch: the check runs at
321
+ * commit, so one batch may delete a parent and its children in any order.
322
+ */
323
+ class KoolbaseReferenceInUseError extends KoolbaseDataError {
324
+ constructor(message) {
325
+ super(message ?? 'This record is still referenced by other records', 'reference_in_use');
326
+ this.name = 'KoolbaseReferenceInUseError';
327
+ Object.setPrototypeOf(this, KoolbaseReferenceInUseError.prototype);
328
+ }
329
+ }
330
+ exports.KoolbaseReferenceInUseError = KoolbaseReferenceInUseError;
331
+ /**
332
+ * A reference could not be declared because existing records already point at
333
+ * records that do not exist — 409 `dangling_references`.
334
+ *
335
+ * `dangling` lists the offending records so they can be repaired; Koolbase
336
+ * never repairs them for you. It is capped at the first 50.
337
+ */
338
+ class KoolbaseDanglingReferencesError extends KoolbaseDataError {
339
+ constructor(message, dangling) {
340
+ super(message ?? 'Existing records point at records that do not exist', 'dangling_references');
341
+ this.dangling = dangling ?? [];
342
+ this.name = 'KoolbaseDanglingReferencesError';
343
+ Object.setPrototypeOf(this, KoolbaseDanglingReferencesError.prototype);
344
+ }
345
+ }
346
+ exports.KoolbaseDanglingReferencesError = KoolbaseDanglingReferencesError;
347
+ /**
348
+ * A collection could not be deleted because another collection has a reference
349
+ * field pointing at it — 409 `collection_referenced`. Remove that reference
350
+ * first.
351
+ */
352
+ class KoolbaseCollectionReferencedError extends KoolbaseDataError {
353
+ constructor(message) {
354
+ super(message ?? 'Another collection references this one', 'collection_referenced');
355
+ this.name = 'KoolbaseCollectionReferencedError';
356
+ Object.setPrototypeOf(this, KoolbaseCollectionReferencedError.prototype);
357
+ }
358
+ }
359
+ exports.KoolbaseCollectionReferencedError = KoolbaseCollectionReferencedError;
300
360
  /**
301
361
  * Thrown when the requested record or collection does not exist — the server
302
362
  * responds with 404 and code `not_found` / `record_not_found` /
@@ -427,6 +487,16 @@ function koolbaseDataError(status, body, fallbackMessage = 'Request failed') {
427
487
  switch (code) {
428
488
  case 'unique_violation':
429
489
  return attach(new KoolbaseConflictError(message, field));
490
+ case 'reference_invalid':
491
+ return attach(new KoolbaseReferenceInvalidError(message));
492
+ case 'reference_in_use':
493
+ return attach(new KoolbaseReferenceInUseError(message));
494
+ case 'dangling_references': {
495
+ const d = (body?.details ?? {});
496
+ return attach(new KoolbaseDanglingReferencesError(message, d.dangling));
497
+ }
498
+ case 'collection_referenced':
499
+ return attach(new KoolbaseCollectionReferencedError(message));
430
500
  case 'plan_limit_reached': {
431
501
  const d = (body?.details ?? {});
432
502
  return new errors_js_1.KoolbasePlanLimitError(message, d.resource, d.limit, d.plan);