@koolbase/core 11.4.0 → 12.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/aggregate.d.ts +115 -0
- package/dist/cjs/aggregate.js +26 -0
- package/dist/cjs/auth-errors.d.ts +8 -0
- package/dist/cjs/auth-errors.js +27 -14
- package/dist/cjs/auth.d.ts +130 -7
- package/dist/cjs/auth.js +252 -10
- package/dist/cjs/database-errors.d.ts +46 -0
- package/dist/cjs/database-errors.js +71 -1
- package/dist/cjs/database.d.ts +23 -0
- package/dist/cjs/database.js +64 -0
- package/dist/cjs/device-metadata.js +7 -1
- package/dist/cjs/fiscal.d.ts +114 -0
- package/dist/cjs/fiscal.js +167 -0
- package/dist/cjs/index.d.ts +3 -0
- package/dist/cjs/index.js +8 -1
- package/dist/cjs/types.d.ts +58 -0
- package/dist/esm/aggregate.d.ts +115 -0
- package/dist/esm/aggregate.js +25 -0
- package/dist/esm/auth-errors.d.ts +8 -0
- package/dist/esm/auth-errors.js +25 -13
- package/dist/esm/auth.d.ts +130 -7
- package/dist/esm/auth.js +253 -11
- package/dist/esm/database-errors.d.ts +46 -0
- package/dist/esm/database-errors.js +66 -0
- package/dist/esm/database.d.ts +23 -0
- package/dist/esm/database.js +64 -0
- package/dist/esm/device-metadata.js +7 -1
- package/dist/esm/fiscal.d.ts +114 -0
- package/dist/esm/fiscal.js +159 -0
- package/dist/esm/index.d.ts +3 -0
- package/dist/esm/index.js +2 -0
- package/dist/esm/types.d.ts +58 -0
- package/package.json +1 -1
package/dist/esm/auth.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AuthStateListener, KoolbaseConfig, KoolbaseSession, KoolbaseUser, LinkPhoneParams, LoginParams, OtpSendResult, PhoneVerifyResult, RegisterParams, RestoreResult, ResendVerificationResult, SendOtpParams, SignUpResult, SignInWithAppleParams, VerifyOtpParams } from './types.js';
|
|
1
|
+
import { AuthStateListener, KoolbaseConfig, KoolbaseSession, KoolbaseUser, LinkPhoneParams, LoginParams, OtpSendResult, PhoneVerifyResult, RegisterParams, RestoreResult, KoolbaseAuditPage, KoolbaseSessionInfo, ResendVerificationResult, SendOtpParams, SignUpResult, SignInWithAppleParams, VerifyOtpParams } from './types.js';
|
|
2
2
|
import type { SignInWithGoogleParams } from './types.js';
|
|
3
3
|
export declare class KoolbaseAuth {
|
|
4
4
|
private config;
|
|
@@ -55,6 +55,18 @@ export declare class KoolbaseAuth {
|
|
|
55
55
|
* delegates to {@link authRequest} with includeAuth=true.
|
|
56
56
|
*/
|
|
57
57
|
private authedRequest;
|
|
58
|
+
/**
|
|
59
|
+
* Replace the user inside the current session and persist it.
|
|
60
|
+
*
|
|
61
|
+
* A profile update or a fresh fetch changes the user, not the tokens.
|
|
62
|
+
* Without this the new values live only in the returned object: the cached
|
|
63
|
+
* `currentUser` stays stale, anything bound to `onAuthStateChange` never
|
|
64
|
+
* hears about the change, and a reload restores the old name.
|
|
65
|
+
*
|
|
66
|
+
* No session means nobody is signed in, and there is nothing to update —
|
|
67
|
+
* the caller's own request would have failed first.
|
|
68
|
+
*/
|
|
69
|
+
private updateUserAndPersist;
|
|
58
70
|
private setSessionInternal;
|
|
59
71
|
/**
|
|
60
72
|
* Discards the stored session without contacting the server.
|
|
@@ -191,6 +203,92 @@ export declare class KoolbaseAuth {
|
|
|
191
203
|
* taking an email address — otherwise this would be an open mail relay.
|
|
192
204
|
*/
|
|
193
205
|
resendVerificationEmail(): Promise<ResendVerificationResult>;
|
|
206
|
+
/**
|
|
207
|
+
* The signed-in user, fetched fresh from the server.
|
|
208
|
+
*
|
|
209
|
+
* `currentUser` returns what was cached at sign-in; this asks. Use it when
|
|
210
|
+
* something may have changed the user server-side — a verification
|
|
211
|
+
* completing, a profile updated from another device — and persist the
|
|
212
|
+
* result so the cached copy stops being stale.
|
|
213
|
+
*/
|
|
214
|
+
getCurrentUser(): Promise<KoolbaseUser>;
|
|
215
|
+
/**
|
|
216
|
+
* Update the signed-in user's profile.
|
|
217
|
+
*
|
|
218
|
+
* Only the fields given are sent, so passing one leaves the other
|
|
219
|
+
* untouched. The updated user is persisted, so `currentUser` reflects it
|
|
220
|
+
* without a refetch.
|
|
221
|
+
*/
|
|
222
|
+
updateProfile(params: {
|
|
223
|
+
fullName?: string;
|
|
224
|
+
avatarUrl?: string;
|
|
225
|
+
}): Promise<KoolbaseUser>;
|
|
226
|
+
/**
|
|
227
|
+
* Change the signed-in user's password.
|
|
228
|
+
*
|
|
229
|
+
* The current password is required: a stolen session should not be enough
|
|
230
|
+
* to lock the real owner out. A wrong one throws
|
|
231
|
+
* CurrentPasswordIncorrectError — the server returns `invalid_password`,
|
|
232
|
+
* which reads like a rejected new password and means the opposite.
|
|
233
|
+
*
|
|
234
|
+
* An account that signed up through Google or Apple has no password to
|
|
235
|
+
* change and gets the same error, deliberately, so a caller cannot probe
|
|
236
|
+
* which sign-in methods an account has.
|
|
237
|
+
*/
|
|
238
|
+
changePassword(params: {
|
|
239
|
+
currentPassword: string;
|
|
240
|
+
newPassword: string;
|
|
241
|
+
}): Promise<void>;
|
|
242
|
+
/**
|
|
243
|
+
* Delete the signed-in user's account, and sign out.
|
|
244
|
+
*
|
|
245
|
+
* Deletes the caller only — there is no user id to pass, which is the
|
|
246
|
+
* security property. The server removes the auth record and every session
|
|
247
|
+
* on every device, then fires an `auth.user.deleted` trigger.
|
|
248
|
+
*
|
|
249
|
+
* It does NOT delete the account's records in your collections. Koolbase
|
|
250
|
+
* cannot know whether a row should be deleted, anonymised, or kept for a
|
|
251
|
+
* statutory retention period. Subscribe a Function to `auth.user.deleted`
|
|
252
|
+
* and clean up there — it runs server-side, so a tab closing mid-flow
|
|
253
|
+
* cannot strand anything.
|
|
254
|
+
*/
|
|
255
|
+
deleteAccount(): Promise<void>;
|
|
256
|
+
/**
|
|
257
|
+
* Every session the signed-in user has — a "where you're signed in" list.
|
|
258
|
+
*
|
|
259
|
+
* Newest first is not guaranteed; sort by createdAt if the order matters.
|
|
260
|
+
* The entry with `isCurrent` is this device.
|
|
261
|
+
*/
|
|
262
|
+
listSessions(): Promise<KoolbaseSessionInfo[]>;
|
|
263
|
+
/**
|
|
264
|
+
* Sign out one session by id.
|
|
265
|
+
*
|
|
266
|
+
* Revoking the current one ends this session too — the server does not
|
|
267
|
+
* refuse it, and the local session is cleared here so the app does not keep
|
|
268
|
+
* a token the server has dropped. Use logout() when that is what you mean.
|
|
269
|
+
*/
|
|
270
|
+
revokeSession(sessionId: string): Promise<void>;
|
|
271
|
+
/**
|
|
272
|
+
* Sign out every OTHER session, keeping this one.
|
|
273
|
+
*
|
|
274
|
+
* What "sign out my other devices" means after a lost phone. Returns how
|
|
275
|
+
* many were ended, so an app can say so rather than guess.
|
|
276
|
+
*/
|
|
277
|
+
revokeAllOtherSessions(): Promise<number>;
|
|
278
|
+
/**
|
|
279
|
+
* What has happened to this account — sign-ins, failures, lockouts,
|
|
280
|
+
* password changes.
|
|
281
|
+
*
|
|
282
|
+
* For a "recent security activity" screen. The server sanitizes each
|
|
283
|
+
* event, so this carries what that event type is allowed to say and
|
|
284
|
+
* nothing more.
|
|
285
|
+
*
|
|
286
|
+
* @param limit up to 200; the server caps it there.
|
|
287
|
+
*/
|
|
288
|
+
auditLog(options?: {
|
|
289
|
+
limit?: number;
|
|
290
|
+
offset?: number;
|
|
291
|
+
}): Promise<KoolbaseAuditPage>;
|
|
194
292
|
unlock(token: string): Promise<void>;
|
|
195
293
|
get currentUser(): KoolbaseUser | null;
|
|
196
294
|
get accessToken(): string | null;
|
|
@@ -204,12 +302,10 @@ export declare class KoolbaseAuth {
|
|
|
204
302
|
validAccessToken(): Promise<string | null>;
|
|
205
303
|
setSession(session: KoolbaseSession | null): Promise<void>;
|
|
206
304
|
/**
|
|
207
|
-
* @deprecated
|
|
208
|
-
*
|
|
209
|
-
*
|
|
210
|
-
*
|
|
211
|
-
* server endpoints under /v1/sdk/auth/oauth/{apple,google,github}.
|
|
212
|
-
* 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.
|
|
213
309
|
*
|
|
214
310
|
* @throws Always throws KoolbaseAuthError('not_implemented').
|
|
215
311
|
*/
|
|
@@ -220,6 +316,33 @@ export declare class KoolbaseAuth {
|
|
|
220
316
|
name?: string;
|
|
221
317
|
avatarUrl?: string;
|
|
222
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>;
|
|
223
346
|
sendOtp(params: SendOtpParams): Promise<OtpSendResult>;
|
|
224
347
|
verifyOtp(params: VerifyOtpParams): Promise<PhoneVerifyResult>;
|
|
225
348
|
linkPhone(params: LinkPhoneParams): Promise<void>;
|
package/dist/esm/auth.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { RestoreResult, } from './types.js';
|
|
2
|
-
import { AccountLockedError, EmailAlreadyInUseError, InvalidCredentialsError, InvalidPhoneNumberError, KoolbaseAuthError, OtpExpiredError, OtpInvalidError, OtpMaxAttemptsError, OtpRateLimitError, PhoneAlreadyLinkedError, IdentityNotFoundError, InsufficientScopeError, MalformedSessionResponseError, ProviderIdentityAlreadyLinkedError, AccountExistsError, ContactNotVerifiedError, CurrentPasswordIncorrectError, HideRequiresVerificationError, InsufficientAuthorityError, LastCredentialError, OAuthOnlyAccountError, SessionRequiredError, SignupsDisabledError, TokenAlreadyUsedError, TokenExpiredError, UnsupportedOAuthProviderError, RateLimitError, VerificationResendCooldownError, VerificationResendDailyCapError, SessionExpiredError, SmsConfigMissingError, UnlockTokenInvalidError, UserDisabledError, WeakPasswordError, AppleEmailRequiredError, AppleSignInNotConfiguredError, InvalidAppleTokenError, OAuthEmailConflictError, GoogleEmailRequiredError, GoogleSignInNotConfiguredError, InvalidGoogleTokenError, } from './auth-errors.js';
|
|
2
|
+
import { AccountLockedError, EmailAlreadyInUseError, InvalidCredentialsError, InvalidPhoneNumberError, KoolbaseAuthError, OtpExpiredError, OtpInvalidError, OtpMaxAttemptsError, OtpRateLimitError, PhoneAlreadyLinkedError, IdentityNotFoundError, InsufficientScopeError, MalformedSessionResponseError, ProviderIdentityAlreadyLinkedError, AccountExistsError, ContactNotVerifiedError, CurrentPasswordIncorrectError, HideRequiresVerificationError, InsufficientAuthorityError, LastCredentialError, OAuthOnlyAccountError, SessionRequiredError, SignupsDisabledError, TokenAlreadyUsedError, TokenExpiredError, UnsupportedOAuthProviderError, RateLimitError, VerificationResendCooldownError, VerificationResendDailyCapError, SessionExpiredError, SmsConfigMissingError, UnlockTokenInvalidError, UserDisabledError, WeakPasswordError, AppleEmailRequiredError, AppleSignInNotConfiguredError, InvalidAppleTokenError, OAuthEmailConflictError, GoogleEmailRequiredError, GoogleSignInNotConfiguredError, InvalidGoogleTokenError, EmailCodeDisabledError, } from './auth-errors.js';
|
|
3
3
|
import { getPlatform } from './platform.js';
|
|
4
4
|
import { DeviceMetadata } from './device-metadata.js';
|
|
5
5
|
export class KoolbaseAuth {
|
|
@@ -130,6 +130,22 @@ export class KoolbaseAuth {
|
|
|
130
130
|
return this.authRequest(path, { ...options, includeAuth: true });
|
|
131
131
|
}
|
|
132
132
|
// ─── Internal session lifecycle ─────────────────────────────────────────
|
|
133
|
+
/**
|
|
134
|
+
* Replace the user inside the current session and persist it.
|
|
135
|
+
*
|
|
136
|
+
* A profile update or a fresh fetch changes the user, not the tokens.
|
|
137
|
+
* Without this the new values live only in the returned object: the cached
|
|
138
|
+
* `currentUser` stays stale, anything bound to `onAuthStateChange` never
|
|
139
|
+
* hears about the change, and a reload restores the old name.
|
|
140
|
+
*
|
|
141
|
+
* No session means nobody is signed in, and there is nothing to update —
|
|
142
|
+
* the caller's own request would have failed first.
|
|
143
|
+
*/
|
|
144
|
+
async updateUserAndPersist(user) {
|
|
145
|
+
if (!this.session)
|
|
146
|
+
return;
|
|
147
|
+
await this.setSessionInternal({ ...this.session, user });
|
|
148
|
+
}
|
|
133
149
|
async setSessionInternal(session) {
|
|
134
150
|
this.session = session;
|
|
135
151
|
if (this.storage) {
|
|
@@ -583,6 +599,188 @@ export class KoolbaseAuth {
|
|
|
583
599
|
cooldownUntil: body.cooldown_until ? new Date(body.cooldown_until) : null,
|
|
584
600
|
};
|
|
585
601
|
}
|
|
602
|
+
/**
|
|
603
|
+
* The signed-in user, fetched fresh from the server.
|
|
604
|
+
*
|
|
605
|
+
* `currentUser` returns what was cached at sign-in; this asks. Use it when
|
|
606
|
+
* something may have changed the user server-side — a verification
|
|
607
|
+
* completing, a profile updated from another device — and persist the
|
|
608
|
+
* result so the cached copy stops being stale.
|
|
609
|
+
*/
|
|
610
|
+
async getCurrentUser() {
|
|
611
|
+
const res = await this.authRequest('/v1/sdk/auth/me', {
|
|
612
|
+
method: 'GET',
|
|
613
|
+
includeAuth: true,
|
|
614
|
+
});
|
|
615
|
+
await this.checkResponse(res);
|
|
616
|
+
const user = this.mapUser(await res.json());
|
|
617
|
+
await this.updateUserAndPersist(user);
|
|
618
|
+
return user;
|
|
619
|
+
}
|
|
620
|
+
/**
|
|
621
|
+
* Update the signed-in user's profile.
|
|
622
|
+
*
|
|
623
|
+
* Only the fields given are sent, so passing one leaves the other
|
|
624
|
+
* untouched. The updated user is persisted, so `currentUser` reflects it
|
|
625
|
+
* without a refetch.
|
|
626
|
+
*/
|
|
627
|
+
async updateProfile(params) {
|
|
628
|
+
const body = {};
|
|
629
|
+
if (params.fullName !== undefined)
|
|
630
|
+
body.full_name = params.fullName;
|
|
631
|
+
if (params.avatarUrl !== undefined)
|
|
632
|
+
body.avatar_url = params.avatarUrl;
|
|
633
|
+
const res = await this.authRequest('/v1/sdk/auth/me', {
|
|
634
|
+
method: 'PATCH',
|
|
635
|
+
body,
|
|
636
|
+
includeAuth: true,
|
|
637
|
+
});
|
|
638
|
+
await this.checkResponse(res);
|
|
639
|
+
const user = this.mapUser(await res.json());
|
|
640
|
+
await this.updateUserAndPersist(user);
|
|
641
|
+
return user;
|
|
642
|
+
}
|
|
643
|
+
/**
|
|
644
|
+
* Change the signed-in user's password.
|
|
645
|
+
*
|
|
646
|
+
* The current password is required: a stolen session should not be enough
|
|
647
|
+
* to lock the real owner out. A wrong one throws
|
|
648
|
+
* CurrentPasswordIncorrectError — the server returns `invalid_password`,
|
|
649
|
+
* which reads like a rejected new password and means the opposite.
|
|
650
|
+
*
|
|
651
|
+
* An account that signed up through Google or Apple has no password to
|
|
652
|
+
* change and gets the same error, deliberately, so a caller cannot probe
|
|
653
|
+
* which sign-in methods an account has.
|
|
654
|
+
*/
|
|
655
|
+
async changePassword(params) {
|
|
656
|
+
if (params.newPassword.length < 8)
|
|
657
|
+
throw new WeakPasswordError();
|
|
658
|
+
const res = await this.authRequest('/v1/sdk/auth/me/password', {
|
|
659
|
+
method: 'PATCH',
|
|
660
|
+
body: {
|
|
661
|
+
current_password: params.currentPassword,
|
|
662
|
+
new_password: params.newPassword,
|
|
663
|
+
},
|
|
664
|
+
includeAuth: true,
|
|
665
|
+
});
|
|
666
|
+
await this.checkResponse(res);
|
|
667
|
+
}
|
|
668
|
+
/**
|
|
669
|
+
* Delete the signed-in user's account, and sign out.
|
|
670
|
+
*
|
|
671
|
+
* Deletes the caller only — there is no user id to pass, which is the
|
|
672
|
+
* security property. The server removes the auth record and every session
|
|
673
|
+
* on every device, then fires an `auth.user.deleted` trigger.
|
|
674
|
+
*
|
|
675
|
+
* It does NOT delete the account's records in your collections. Koolbase
|
|
676
|
+
* cannot know whether a row should be deleted, anonymised, or kept for a
|
|
677
|
+
* statutory retention period. Subscribe a Function to `auth.user.deleted`
|
|
678
|
+
* and clean up there — it runs server-side, so a tab closing mid-flow
|
|
679
|
+
* cannot strand anything.
|
|
680
|
+
*/
|
|
681
|
+
async deleteAccount() {
|
|
682
|
+
const res = await this.authRequest('/v1/sdk/auth/me', {
|
|
683
|
+
method: 'DELETE',
|
|
684
|
+
includeAuth: true,
|
|
685
|
+
});
|
|
686
|
+
await this.checkResponse(res);
|
|
687
|
+
await this.clearSessionInternal();
|
|
688
|
+
}
|
|
689
|
+
/**
|
|
690
|
+
* Every session the signed-in user has — a "where you're signed in" list.
|
|
691
|
+
*
|
|
692
|
+
* Newest first is not guaranteed; sort by createdAt if the order matters.
|
|
693
|
+
* The entry with `isCurrent` is this device.
|
|
694
|
+
*/
|
|
695
|
+
async listSessions() {
|
|
696
|
+
const res = await this.authRequest('/v1/sdk/auth/sessions', {
|
|
697
|
+
method: 'GET',
|
|
698
|
+
includeAuth: true,
|
|
699
|
+
});
|
|
700
|
+
await this.checkResponse(res);
|
|
701
|
+
const body = (await res.json());
|
|
702
|
+
return (body.sessions ?? []).map((s) => {
|
|
703
|
+
const r = s;
|
|
704
|
+
return {
|
|
705
|
+
id: String(r.id),
|
|
706
|
+
ip: r.ip,
|
|
707
|
+
userAgent: r.user_agent,
|
|
708
|
+
deviceLabel: r.device_label,
|
|
709
|
+
createdAt: String(r.created_at),
|
|
710
|
+
expiresAt: String(r.expires_at),
|
|
711
|
+
isCurrent: Boolean(r.is_current),
|
|
712
|
+
};
|
|
713
|
+
});
|
|
714
|
+
}
|
|
715
|
+
/**
|
|
716
|
+
* Sign out one session by id.
|
|
717
|
+
*
|
|
718
|
+
* Revoking the current one ends this session too — the server does not
|
|
719
|
+
* refuse it, and the local session is cleared here so the app does not keep
|
|
720
|
+
* a token the server has dropped. Use logout() when that is what you mean.
|
|
721
|
+
*/
|
|
722
|
+
async revokeSession(sessionId) {
|
|
723
|
+
const res = await this.authRequest(`/v1/sdk/auth/sessions/${encodeURIComponent(sessionId)}/revoke`, { method: 'POST', includeAuth: true });
|
|
724
|
+
await this.checkResponse(res);
|
|
725
|
+
// Revoking your own session leaves a token the server has dropped. The
|
|
726
|
+
// SDK cannot tell from the response which one it was, and re-listing to
|
|
727
|
+
// find out would cost a request on every revoke — so it asks the only
|
|
728
|
+
// party that already knows: the next request. If it was this session,
|
|
729
|
+
// that request 401s and the normal refresh path clears it.
|
|
730
|
+
//
|
|
731
|
+
// A UI listing sessions knows which row is current from isCurrent, and
|
|
732
|
+
// should call logout() rather than revokeSession() for that one, which
|
|
733
|
+
// is both clearer to the user and one fewer round trip.
|
|
734
|
+
}
|
|
735
|
+
/**
|
|
736
|
+
* Sign out every OTHER session, keeping this one.
|
|
737
|
+
*
|
|
738
|
+
* What "sign out my other devices" means after a lost phone. Returns how
|
|
739
|
+
* many were ended, so an app can say so rather than guess.
|
|
740
|
+
*/
|
|
741
|
+
async revokeAllOtherSessions() {
|
|
742
|
+
const res = await this.authRequest('/v1/sdk/auth/sessions/revoke-all', {
|
|
743
|
+
method: 'POST',
|
|
744
|
+
includeAuth: true,
|
|
745
|
+
});
|
|
746
|
+
await this.checkResponse(res);
|
|
747
|
+
const body = (await res.json().catch(() => ({})));
|
|
748
|
+
return body.revoked_count ?? 0;
|
|
749
|
+
}
|
|
750
|
+
/**
|
|
751
|
+
* What has happened to this account — sign-ins, failures, lockouts,
|
|
752
|
+
* password changes.
|
|
753
|
+
*
|
|
754
|
+
* For a "recent security activity" screen. The server sanitizes each
|
|
755
|
+
* event, so this carries what that event type is allowed to say and
|
|
756
|
+
* nothing more.
|
|
757
|
+
*
|
|
758
|
+
* @param limit up to 200; the server caps it there.
|
|
759
|
+
*/
|
|
760
|
+
async auditLog(options) {
|
|
761
|
+
const params = new URLSearchParams();
|
|
762
|
+
if (options?.limit !== undefined)
|
|
763
|
+
params.set('limit', String(options.limit));
|
|
764
|
+
if (options?.offset !== undefined)
|
|
765
|
+
params.set('offset', String(options.offset));
|
|
766
|
+
const qs = params.toString();
|
|
767
|
+
const res = await this.authRequest(`/v1/sdk/auth/audit${qs ? `?${qs}` : ''}`, { method: 'GET', includeAuth: true });
|
|
768
|
+
await this.checkResponse(res);
|
|
769
|
+
const body = (await res.json());
|
|
770
|
+
return {
|
|
771
|
+
events: (body.events ?? []).map((e) => ({
|
|
772
|
+
id: String(e.id),
|
|
773
|
+
eventType: String(e.event_type),
|
|
774
|
+
occurredAt: String(e.occurred_at),
|
|
775
|
+
ip: e.ip,
|
|
776
|
+
userAgent: e.user_agent,
|
|
777
|
+
eventData: e.event_data ?? {},
|
|
778
|
+
})),
|
|
779
|
+
total: Number(body.total ?? 0),
|
|
780
|
+
limit: Number(body.limit ?? 0),
|
|
781
|
+
offset: Number(body.offset ?? 0),
|
|
782
|
+
};
|
|
783
|
+
}
|
|
586
784
|
async unlock(token) {
|
|
587
785
|
const res = await this.authRequest('/v1/sdk/auth/unlock', {
|
|
588
786
|
method: 'POST',
|
|
@@ -623,20 +821,54 @@ export class KoolbaseAuth {
|
|
|
623
821
|
}
|
|
624
822
|
// ─── OAuth (DEPRECATED — see v1.10.0) ───────────────────────────────────
|
|
625
823
|
/**
|
|
626
|
-
* @deprecated
|
|
627
|
-
*
|
|
628
|
-
*
|
|
629
|
-
*
|
|
630
|
-
* server endpoints under /v1/sdk/auth/oauth/{apple,google,github}.
|
|
631
|
-
* Use email/password sign-in for now.
|
|
824
|
+
* @deprecated Never implemented; kept only so existing code compiles.
|
|
825
|
+
* Google and Apple sign-in are supported — use signInWithGoogle() or
|
|
826
|
+
* signInWithApple(). This used to say OAuth was not yet shipped and to
|
|
827
|
+
* use email and password instead, which stopped being true long ago.
|
|
632
828
|
*
|
|
633
829
|
* @throws Always throws KoolbaseAuthError('not_implemented').
|
|
634
830
|
*/
|
|
635
831
|
async oauthLogin(_params) {
|
|
636
|
-
throw new KoolbaseAuthError('
|
|
637
|
-
'
|
|
638
|
-
|
|
639
|
-
|
|
832
|
+
throw new KoolbaseAuthError('oauthLogin() was never implemented and is kept only so existing ' +
|
|
833
|
+
'code compiles. Use signInWithGoogle() or signInWithApple().', 'not_implemented');
|
|
834
|
+
}
|
|
835
|
+
// ─── Email code ─────────────────────────────────────────────────────────
|
|
836
|
+
/**
|
|
837
|
+
* Emails a six-digit sign-in code.
|
|
838
|
+
*
|
|
839
|
+
* Resolves the same way whether or not the address has an account, so it
|
|
840
|
+
* cannot be used to check who is registered. A new address receives a code
|
|
841
|
+
* only while the project accepts sign-ups; a disabled account receives
|
|
842
|
+
* nothing.
|
|
843
|
+
*
|
|
844
|
+
* @throws EmailCodeDisabledError if the project has switched this off
|
|
845
|
+
* @throws RateLimitError after too many requests for one address
|
|
846
|
+
*/
|
|
847
|
+
async requestEmailCode(params) {
|
|
848
|
+
const res = await this.authRequest('/v1/sdk/auth/email/code', {
|
|
849
|
+
method: 'POST',
|
|
850
|
+
body: { email: params.email },
|
|
851
|
+
});
|
|
852
|
+
if (!res.ok)
|
|
853
|
+
await this.throwTypedError(res, false);
|
|
854
|
+
}
|
|
855
|
+
/**
|
|
856
|
+
* Signs in with a code from requestEmailCode(), storing the session exactly
|
|
857
|
+
* as login() does. An unknown address becomes an account only if the
|
|
858
|
+
* project still accepts sign-ups at this moment. Each code works once and
|
|
859
|
+
* allows three attempts.
|
|
860
|
+
*
|
|
861
|
+
* @throws OtpInvalidError, OtpExpiredError, OtpMaxAttemptsError
|
|
862
|
+
* @throws EmailCodeDisabledError if the project has switched this off
|
|
863
|
+
*/
|
|
864
|
+
async signInWithEmailCode(params) {
|
|
865
|
+
const res = await this.authRequest('/v1/sdk/auth/email/code/verify', {
|
|
866
|
+
method: 'POST',
|
|
867
|
+
body: { email: params.email, code: params.code },
|
|
868
|
+
});
|
|
869
|
+
const session = await this.parseSessionResponse(res, false);
|
|
870
|
+
await this.setSessionInternal(session);
|
|
871
|
+
return session;
|
|
640
872
|
}
|
|
641
873
|
// ─── Phone OTP ──────────────────────────────────────────────────────────
|
|
642
874
|
async sendOtp(params) {
|
|
@@ -829,6 +1061,16 @@ export class KoolbaseAuth {
|
|
|
829
1061
|
case 'contact_not_verified':
|
|
830
1062
|
case 'email_not_verified':
|
|
831
1063
|
throw new ContactNotVerifiedError(msg || undefined);
|
|
1064
|
+
// Sign-in codes. The otp_* codes are shared with phone sign-in; mapped
|
|
1065
|
+
// here, every path that returns them throws the class an app catches.
|
|
1066
|
+
case 'email_code_disabled':
|
|
1067
|
+
throw new EmailCodeDisabledError();
|
|
1068
|
+
case 'otp_expired':
|
|
1069
|
+
throw new OtpExpiredError();
|
|
1070
|
+
case 'otp_invalid':
|
|
1071
|
+
throw new OtpInvalidError();
|
|
1072
|
+
case 'otp_max_attempts':
|
|
1073
|
+
throw new OtpMaxAttemptsError();
|
|
832
1074
|
// Registration
|
|
833
1075
|
case 'signups_disabled':
|
|
834
1076
|
throw new SignupsDisabledError(msg || undefined);
|
|
@@ -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` /
|
|
@@ -272,6 +272,62 @@ export class KoolbaseConflictError extends KoolbaseDataError {
|
|
|
272
272
|
Object.setPrototypeOf(this, KoolbaseConflictError.prototype);
|
|
273
273
|
}
|
|
274
274
|
}
|
|
275
|
+
/**
|
|
276
|
+
* A reference field points at a record that does not exist, is deleted, or
|
|
277
|
+
* lives in another collection — the server responds 400 `reference_invalid`.
|
|
278
|
+
*
|
|
279
|
+
* Checked when the write commits, so it can surface from an insert, an update,
|
|
280
|
+
* an upsert or a batch. In a batch the whole transaction is refused: nothing
|
|
281
|
+
* in it was written.
|
|
282
|
+
*/
|
|
283
|
+
export class KoolbaseReferenceInvalidError extends KoolbaseDataError {
|
|
284
|
+
constructor(message) {
|
|
285
|
+
super(message ?? 'A reference points at a record that does not exist', 'reference_invalid');
|
|
286
|
+
this.name = 'KoolbaseReferenceInvalidError';
|
|
287
|
+
Object.setPrototypeOf(this, KoolbaseReferenceInvalidError.prototype);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
/**
|
|
291
|
+
* A record could not be deleted because live records still reference it, under
|
|
292
|
+
* a reference declared `on_delete: restrict` — 409 `reference_in_use`.
|
|
293
|
+
*
|
|
294
|
+
* Delete the referencing records first, or in the same batch: the check runs at
|
|
295
|
+
* commit, so one batch may delete a parent and its children in any order.
|
|
296
|
+
*/
|
|
297
|
+
export class KoolbaseReferenceInUseError extends KoolbaseDataError {
|
|
298
|
+
constructor(message) {
|
|
299
|
+
super(message ?? 'This record is still referenced by other records', 'reference_in_use');
|
|
300
|
+
this.name = 'KoolbaseReferenceInUseError';
|
|
301
|
+
Object.setPrototypeOf(this, KoolbaseReferenceInUseError.prototype);
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
/**
|
|
305
|
+
* A reference could not be declared because existing records already point at
|
|
306
|
+
* records that do not exist — 409 `dangling_references`.
|
|
307
|
+
*
|
|
308
|
+
* `dangling` lists the offending records so they can be repaired; Koolbase
|
|
309
|
+
* never repairs them for you. It is capped at the first 50.
|
|
310
|
+
*/
|
|
311
|
+
export class KoolbaseDanglingReferencesError extends KoolbaseDataError {
|
|
312
|
+
constructor(message, dangling) {
|
|
313
|
+
super(message ?? 'Existing records point at records that do not exist', 'dangling_references');
|
|
314
|
+
this.dangling = dangling ?? [];
|
|
315
|
+
this.name = 'KoolbaseDanglingReferencesError';
|
|
316
|
+
Object.setPrototypeOf(this, KoolbaseDanglingReferencesError.prototype);
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
/**
|
|
320
|
+
* A collection could not be deleted because another collection has a reference
|
|
321
|
+
* field pointing at it — 409 `collection_referenced`. Remove that reference
|
|
322
|
+
* first.
|
|
323
|
+
*/
|
|
324
|
+
export class KoolbaseCollectionReferencedError extends KoolbaseDataError {
|
|
325
|
+
constructor(message) {
|
|
326
|
+
super(message ?? 'Another collection references this one', 'collection_referenced');
|
|
327
|
+
this.name = 'KoolbaseCollectionReferencedError';
|
|
328
|
+
Object.setPrototypeOf(this, KoolbaseCollectionReferencedError.prototype);
|
|
329
|
+
}
|
|
330
|
+
}
|
|
275
331
|
/**
|
|
276
332
|
* Thrown when the requested record or collection does not exist — the server
|
|
277
333
|
* responds with 404 and code `not_found` / `record_not_found` /
|
|
@@ -396,6 +452,16 @@ export function koolbaseDataError(status, body, fallbackMessage = 'Request faile
|
|
|
396
452
|
switch (code) {
|
|
397
453
|
case 'unique_violation':
|
|
398
454
|
return attach(new KoolbaseConflictError(message, field));
|
|
455
|
+
case 'reference_invalid':
|
|
456
|
+
return attach(new KoolbaseReferenceInvalidError(message));
|
|
457
|
+
case 'reference_in_use':
|
|
458
|
+
return attach(new KoolbaseReferenceInUseError(message));
|
|
459
|
+
case 'dangling_references': {
|
|
460
|
+
const d = (body?.details ?? {});
|
|
461
|
+
return attach(new KoolbaseDanglingReferencesError(message, d.dangling));
|
|
462
|
+
}
|
|
463
|
+
case 'collection_referenced':
|
|
464
|
+
return attach(new KoolbaseCollectionReferencedError(message));
|
|
399
465
|
case 'plan_limit_reached': {
|
|
400
466
|
const d = (body?.details ?? {});
|
|
401
467
|
return new KoolbasePlanLimitError(message, d.resource, d.limit, d.plan);
|
package/dist/esm/database.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { KoolbaseAggregateRequest, KoolbaseAggregateResult } from './aggregate.js';
|
|
1
2
|
import { KoolbaseConflict } from './conflict.js';
|
|
2
3
|
import { PendingWrite } from './pending-write.js';
|
|
3
4
|
import { KoolbaseConfig, KoolbaseRecord, QueryOptions, QueryResult, UpsertResult, BatchOp, BatchResult, KoolbaseVector, SemanticSearchResult, SearchMode } from './types.js';
|
|
@@ -127,6 +128,28 @@ export declare class KoolbaseDatabase {
|
|
|
127
128
|
* moment matters. Snapshot, not a live handle; per-user.
|
|
128
129
|
*/
|
|
129
130
|
pendingWrites(): Promise<PendingWrite[]>;
|
|
131
|
+
/**
|
|
132
|
+
* Count and total over a whole collection, with the read rule applied
|
|
133
|
+
* inside the query.
|
|
134
|
+
*
|
|
135
|
+
* Not a paged query you add up yourself: the server aggregates the entire
|
|
136
|
+
* authorized set. And never a bare number — collections are schemaless, so
|
|
137
|
+
* a sum that quietly skipped three malformed rows would be a wrong number
|
|
138
|
+
* that looks right. Check `accounting` for what contributed.
|
|
139
|
+
*
|
|
140
|
+
* ```ts
|
|
141
|
+
* const r = await db.aggregate({
|
|
142
|
+
* collection: 'orders',
|
|
143
|
+
* groupBy: { field: 'created_at', bucket: 'month', timezone: 'Africa/Accra' },
|
|
144
|
+
* measures: [{ aggregate: 'sum', field: 'total', as: 'revenue' }],
|
|
145
|
+
* });
|
|
146
|
+
* ```
|
|
147
|
+
*
|
|
148
|
+
* A calendar bucket requires a timezone and the types enforce it: midnight
|
|
149
|
+
* means nothing without one, and Accra and UTC disagree about which day a
|
|
150
|
+
* 23:30 sale belongs to.
|
|
151
|
+
*/
|
|
152
|
+
aggregate(request: KoolbaseAggregateRequest): Promise<KoolbaseAggregateResult>;
|
|
130
153
|
conflicts(): Promise<KoolbaseConflict[]>;
|
|
131
154
|
/**
|
|
132
155
|
* Resolves by id, reloading the stored conflict first.
|