@koolbase/core 11.4.0 → 12.0.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.
@@ -424,6 +424,70 @@ class KoolbaseDatabase {
424
424
  const { pending } = await (0, offline_state_js_1.readOfflineState)(userId);
425
425
  return pending.map(pending_write_js_1.toPendingWrite);
426
426
  }
427
+ /**
428
+ * Count and total over a whole collection, with the read rule applied
429
+ * inside the query.
430
+ *
431
+ * Not a paged query you add up yourself: the server aggregates the entire
432
+ * authorized set. And never a bare number — collections are schemaless, so
433
+ * a sum that quietly skipped three malformed rows would be a wrong number
434
+ * that looks right. Check `accounting` for what contributed.
435
+ *
436
+ * ```ts
437
+ * const r = await db.aggregate({
438
+ * collection: 'orders',
439
+ * groupBy: { field: 'created_at', bucket: 'month', timezone: 'Africa/Accra' },
440
+ * measures: [{ aggregate: 'sum', field: 'total', as: 'revenue' }],
441
+ * });
442
+ * ```
443
+ *
444
+ * A calendar bucket requires a timezone and the types enforce it: midnight
445
+ * means nothing without one, and Accra and UTC disagree about which day a
446
+ * 23:30 sale belongs to.
447
+ */
448
+ async aggregate(request) {
449
+ const raw = await this.request('POST', '/v1/sdk/db/aggregate', {
450
+ collection: request.collection,
451
+ ...(request.where?.length ? { where: request.where } : {}),
452
+ ...(request.groupBy
453
+ ? {
454
+ group_by: {
455
+ field: request.groupBy.field,
456
+ ...(request.groupBy.bucket
457
+ ? {
458
+ bucket: request.groupBy.bucket,
459
+ timezone: request.groupBy.timezone,
460
+ }
461
+ : {}),
462
+ },
463
+ }
464
+ : {}),
465
+ measures: request.measures.map((m) => ({
466
+ aggregate: m.aggregate,
467
+ ...(m.field ? { field: m.field } : {}),
468
+ as: m.as,
469
+ })),
470
+ });
471
+ const accounting = {};
472
+ for (const [name, a] of Object.entries(raw.accounting ?? {})) {
473
+ accounting[name] = {
474
+ counted: Number(a.counted ?? 0),
475
+ skipped: Number(a.skipped ?? 0),
476
+ skippedReason: a.skipped_reason,
477
+ };
478
+ }
479
+ return {
480
+ groups: (raw.groups ?? []).map((g) => ({
481
+ category: String(g.category ?? ''),
482
+ values: g.values ?? {},
483
+ })),
484
+ matched: Number(raw.matched ?? 0),
485
+ accounting,
486
+ // Explicitly boolean: a missing field must read as false, not
487
+ // undefined, or `if (!r.tooManyGroups)` passes on a refused request.
488
+ tooManyGroups: raw.too_many_groups === true,
489
+ };
490
+ }
427
491
  async conflicts() {
428
492
  const userId = this.requireUserId('the conflict list');
429
493
  const { conflicts } = await (0, offline_state_js_1.readOfflineState)(userId);
@@ -52,7 +52,13 @@ class DeviceMetadata {
52
52
  if (this.cached)
53
53
  return this.cached;
54
54
  const platform = (0, platform_js_1.getPlatform)().info.os;
55
- const platformVersion = (0, platform_js_1.getPlatform)().info.version;
55
+ // 'unknown' rather than empty, matching appVersion above. A browser
56
+ // outside the handful browserVersion() recognises — Brave, Opera,
57
+ // Samsung Internet, an in-app webview — reports nothing, and an empty
58
+ // header stored against a session is indistinguishable from an SDK that
59
+ // never reported itself at all. 'unknown' says the SDK spoke and could
60
+ // not identify the host, which is a different and more useful fact.
61
+ const platformVersion = (0, platform_js_1.getPlatform)().info.version || 'unknown';
56
62
  const deviceLabel = await this.getOrCreateDeviceLabel();
57
63
  // No User-Agent: a browser forbids setting it, and the same header set
58
64
  // now goes out from both hosts. The platform and version below carry
@@ -1,5 +1,6 @@
1
1
  export * from './types.js';
2
2
  export * from './errors.js';
3
+ export * from './aggregate.js';
3
4
  export * from './conflict.js';
4
5
  export * from './pending-write.js';
5
6
  export * from './function-errors.js';
package/dist/cjs/index.js CHANGED
@@ -22,6 +22,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
22
22
  exports.memoryPlatform = exports.getPlatform = exports.setPlatform = exports.RestoreResult = exports.koolbaseSdkVersion = exports.getOrCreateDeviceId = exports.KoolbaseMessaging = exports.KoolbaseAnalytics = exports.KoolbaseStorage = exports.KoolbaseRealtime = exports.KoolbaseFunctions = exports.KoolbaseFlags = exports.KoolbaseDatabase = exports.KoolbaseAuth = void 0;
23
23
  __exportStar(require("./types.js"), exports);
24
24
  __exportStar(require("./errors.js"), exports);
25
+ __exportStar(require("./aggregate.js"), exports);
25
26
  __exportStar(require("./conflict.js"), exports);
26
27
  __exportStar(require("./pending-write.js"), exports);
27
28
  __exportStar(require("./function-errors.js"), exports);
@@ -88,6 +88,64 @@ export type SignUpResult = {
88
88
  user: KoolbaseUser;
89
89
  session: null;
90
90
  };
91
+ /**
92
+ * One active session — somewhere the user is signed in.
93
+ *
94
+ * Token hashes are never included; the server excludes them deliberately.
95
+ */
96
+ export interface KoolbaseSessionInfo {
97
+ id: string;
98
+ /** Where it signed in from, when the server captured it. */
99
+ ip?: string;
100
+ /** What signed in. A browser sets this; native SDKs send their own. */
101
+ userAgent?: string;
102
+ /** A per-install label, where the SDK supplied one. */
103
+ deviceLabel?: string;
104
+ createdAt: string;
105
+ expiresAt: string;
106
+ /**
107
+ * True for the session making the request.
108
+ *
109
+ * A device list needs this: without it a user cannot tell which row is the
110
+ * phone in their hand, and "sign out everywhere else" has nothing to
111
+ * exclude.
112
+ */
113
+ isCurrent: boolean;
114
+ }
115
+ /**
116
+ * One thing that happened to this account.
117
+ *
118
+ * The server sanitizes these: `eventData` carries only the fields allowed
119
+ * for that event type, so a lockout can say which address was attempted
120
+ * while a successful login carries nothing extra.
121
+ */
122
+ export interface KoolbaseAuditEvent {
123
+ id: string;
124
+ /**
125
+ * What happened. One of: auth.login.success, auth.login.failed,
126
+ * auth.account.locked, auth.account.unlocked, auth.session.created,
127
+ * auth.session.refreshed, auth.session.revoked, auth.password.changed,
128
+ * auth.user.verified.
129
+ *
130
+ * A string rather than a union: the server may add one, and an app that
131
+ * stops compiling because it did would be worse than one that shows an
132
+ * event it does not recognise.
133
+ */
134
+ eventType: string;
135
+ occurredAt: string;
136
+ ip?: string;
137
+ userAgent?: string;
138
+ /** Whatever that event type is allowed to carry. Often empty. */
139
+ eventData: Record<string, unknown>;
140
+ }
141
+ /** A page of audit events, and how many there are in total. */
142
+ export interface KoolbaseAuditPage {
143
+ events: KoolbaseAuditEvent[];
144
+ /** Across all pages, so an app knows whether to offer "show more". */
145
+ total: number;
146
+ limit: number;
147
+ offset: number;
148
+ }
91
149
  export interface ResendVerificationResult {
92
150
  alreadyVerified: boolean;
93
151
  /** When the link in the email stops working. Null when nothing was sent. */
@@ -0,0 +1,115 @@
1
+ /**
2
+ * Asking "how many" and "how much" over a collection, correctly.
3
+ *
4
+ * Correct means the whole authorized set — never the first page — with the
5
+ * caller's read rule applied inside the query, and never a bare number:
6
+ * collections are schemaless, so a total that quietly excluded three
7
+ * malformed rows would be a wrong number that looks right. Every result
8
+ * carries its accounting.
9
+ *
10
+ * ```ts
11
+ * const r = await Koolbase.db.aggregate({
12
+ * collection: 'orders',
13
+ * groupBy: { field: 'created_at', bucket: 'month', timezone: 'Africa/Accra' },
14
+ * measures: [{ aggregate: 'sum', field: 'total', as: 'revenue' }],
15
+ * });
16
+ *
17
+ * for (const g of r.groups) {
18
+ * console.log(g.category, g.values.revenue);
19
+ * }
20
+ * if (r.accounting.revenue.skipped > 0) {
21
+ * // some orders had a total that was not a number — say so
22
+ * }
23
+ * ```
24
+ */
25
+ /** A record filter, AND-ed on top of the read rule. */
26
+ export interface KoolbaseAggregateFilter {
27
+ field: string;
28
+ op: 'eq' | 'neq' | 'in' | 'gt' | 'gte' | 'lt' | 'lte';
29
+ value: unknown;
30
+ }
31
+ /**
32
+ * The category axis: a field's value, or a calendar bucket of a timestamp
33
+ * field.
34
+ *
35
+ * A bucket REQUIRES a timezone, and the union makes omitting it a type
36
+ * error. "Midnight" means nothing without one — Africa/Accra and UTC
37
+ * disagree about which day a 23:30 sale belongs to — and the API refuses a
38
+ * bucket that arrives without one.
39
+ */
40
+ export type KoolbaseGroupBy = {
41
+ field: string;
42
+ bucket?: never;
43
+ timezone?: never;
44
+ } | {
45
+ field: string;
46
+ bucket: 'day' | 'week' | 'month' | 'year';
47
+ timezone: string;
48
+ };
49
+ /**
50
+ * One number per group.
51
+ *
52
+ * `count` counts RECORDS, so it takes no field and skips nothing. The others
53
+ * read a field, and a record whose value is missing or not a number is
54
+ * skipped and reported in the result's accounting.
55
+ */
56
+ export type KoolbaseMeasure = {
57
+ aggregate: 'count';
58
+ field?: never;
59
+ as: string;
60
+ } | {
61
+ aggregate: 'sum' | 'avg' | 'min' | 'max';
62
+ field: string;
63
+ as: string;
64
+ };
65
+ /** One aggregation request. */
66
+ export interface KoolbaseAggregateRequest {
67
+ collection: string;
68
+ /** AND-ed record filters on top of the read rule. */
69
+ where?: KoolbaseAggregateFilter[];
70
+ /** Absent means one group: the measures over everything. */
71
+ groupBy?: KoolbaseGroupBy;
72
+ /** At least one. */
73
+ measures: KoolbaseMeasure[];
74
+ }
75
+ /** One category and its numbers. */
76
+ export interface KoolbaseAggregateGroup {
77
+ /** Empty string for the single group when there was no groupBy. */
78
+ category: string;
79
+ /** Keyed by each measure's `as`. Null where a group had nothing to measure. */
80
+ values: Record<string, number | null>;
81
+ }
82
+ /**
83
+ * How a measure arrived at its number.
84
+ *
85
+ * `skipped` is not an error. It is what you need to decide whether the
86
+ * number means what it appears to mean — a revenue total over 900 of 903
87
+ * orders is a different fact from one over all of them, and only this says
88
+ * which you are looking at.
89
+ */
90
+ export interface KoolbaseAggregateAccounting {
91
+ counted: number;
92
+ skipped: number;
93
+ /** Present when skipped > 0. */
94
+ skippedReason?: 'missing' | 'not_numeric' | 'mixed';
95
+ }
96
+ /** The whole answer, including what could not be counted. */
97
+ export interface KoolbaseAggregateResult {
98
+ /**
99
+ * One per category, sorted — or exactly one with an empty category when
100
+ * there was no groupBy.
101
+ */
102
+ groups: KoolbaseAggregateGroup[];
103
+ /** Records that matched the read rule and filters: the denominator. */
104
+ matched: number;
105
+ /** Per measure name, keyed by its `as`. */
106
+ accounting: Record<string, KoolbaseAggregateAccounting>;
107
+ /**
108
+ * True when the groupBy produced more categories than allowed and the
109
+ * whole request was refused.
110
+ *
111
+ * `groups` is EMPTY when this is set: a partial set drawn as the whole
112
+ * would be exactly the wrong number this API exists to prevent.
113
+ */
114
+ tooManyGroups: boolean;
115
+ }
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Asking "how many" and "how much" over a collection, correctly.
3
+ *
4
+ * Correct means the whole authorized set — never the first page — with the
5
+ * caller's read rule applied inside the query, and never a bare number:
6
+ * collections are schemaless, so a total that quietly excluded three
7
+ * malformed rows would be a wrong number that looks right. Every result
8
+ * carries its accounting.
9
+ *
10
+ * ```ts
11
+ * const r = await Koolbase.db.aggregate({
12
+ * collection: 'orders',
13
+ * groupBy: { field: 'created_at', bucket: 'month', timezone: 'Africa/Accra' },
14
+ * measures: [{ aggregate: 'sum', field: 'total', as: 'revenue' }],
15
+ * });
16
+ *
17
+ * for (const g of r.groups) {
18
+ * console.log(g.category, g.values.revenue);
19
+ * }
20
+ * if (r.accounting.revenue.skipped > 0) {
21
+ * // some orders had a total that was not a number — say so
22
+ * }
23
+ * ```
24
+ */
25
+ export {};
@@ -21,14 +21,14 @@ export class InvalidCredentialsError extends KoolbaseAuthError {
21
21
  }
22
22
  export class EmailAlreadyInUseError extends KoolbaseAuthError {
23
23
  constructor() {
24
- super('Email is already in use', 'email_taken');
24
+ super('Email is already in use', 'email_in_use');
25
25
  this.name = 'EmailAlreadyInUseError';
26
26
  Object.setPrototypeOf(this, EmailAlreadyInUseError.prototype);
27
27
  }
28
28
  }
29
29
  export class UserDisabledError extends KoolbaseAuthError {
30
30
  constructor() {
31
- super('This account has been disabled', 'user_disabled');
31
+ super('This account has been disabled', 'account_disabled');
32
32
  this.name = 'UserDisabledError';
33
33
  Object.setPrototypeOf(this, UserDisabledError.prototype);
34
34
  }
@@ -49,7 +49,7 @@ export class WeakPasswordError extends KoolbaseAuthError {
49
49
  // ─── Session lifecycle ─────────────────────────────────────────────────────
50
50
  export class SessionExpiredError extends KoolbaseAuthError {
51
51
  constructor() {
52
- super('Session expired, please log in again', 'session_expired');
52
+ super('Session expired, please log in again', 'invalid_refresh_token');
53
53
  this.name = 'SessionExpiredError';
54
54
  Object.setPrototypeOf(this, SessionExpiredError.prototype);
55
55
  }
@@ -95,7 +95,7 @@ export class AccountLockedError extends KoolbaseAuthError {
95
95
  */
96
96
  export class UnlockTokenInvalidError extends KoolbaseAuthError {
97
97
  constructor() {
98
- super('Unlock link is invalid or has expired', 'unlock_token_invalid');
98
+ super('Unlock link is invalid or has expired', 'invalid_unlock_token');
99
99
  this.name = 'UnlockTokenInvalidError';
100
100
  Object.setPrototypeOf(this, UnlockTokenInvalidError.prototype);
101
101
  }
@@ -185,42 +185,42 @@ export class OtpMaxAttemptsError extends KoolbaseAuthError {
185
185
  }
186
186
  export class OtpRateLimitError extends KoolbaseAuthError {
187
187
  constructor() {
188
- super('Too many OTP requests, please wait before trying again', 'otp_rate_limit');
188
+ super('Too many OTP requests, please wait before trying again', 'rate_limit');
189
189
  this.name = 'OtpRateLimitError';
190
190
  Object.setPrototypeOf(this, OtpRateLimitError.prototype);
191
191
  }
192
192
  }
193
193
  export class PhoneAlreadyLinkedError extends KoolbaseAuthError {
194
194
  constructor() {
195
- super('Phone number is already associated with another account', 'phone_taken');
195
+ super('Phone number is already associated with another account', 'phone_in_use');
196
196
  this.name = 'PhoneAlreadyLinkedError';
197
197
  Object.setPrototypeOf(this, PhoneAlreadyLinkedError.prototype);
198
198
  }
199
199
  }
200
200
  export class SmsConfigMissingError extends KoolbaseAuthError {
201
201
  constructor() {
202
- super('SMS provider not configured for this project', 'sms_config_missing');
202
+ super('SMS provider not configured for this project', 'sms_not_configured');
203
203
  this.name = 'SmsConfigMissingError';
204
204
  Object.setPrototypeOf(this, SmsConfigMissingError.prototype);
205
205
  }
206
206
  }
207
207
  export class AppleSignInNotConfiguredError extends KoolbaseAuthError {
208
208
  constructor() {
209
- super('Apple Sign-In is not configured for this environment', 'apple_not_configured');
209
+ super('Apple Sign-In is not configured for this environment', 'oauth_not_configured');
210
210
  this.name = 'AppleSignInNotConfiguredError';
211
211
  Object.setPrototypeOf(this, AppleSignInNotConfiguredError.prototype);
212
212
  }
213
213
  }
214
214
  export class InvalidAppleTokenError extends KoolbaseAuthError {
215
215
  constructor() {
216
- super('Invalid Apple identity token', 'invalid_apple_token');
216
+ super('Invalid Apple identity token', 'invalid_oauth_token');
217
217
  this.name = 'InvalidAppleTokenError';
218
218
  Object.setPrototypeOf(this, InvalidAppleTokenError.prototype);
219
219
  }
220
220
  }
221
221
  export class AppleEmailRequiredError extends KoolbaseAuthError {
222
222
  constructor() {
223
- super('Apple did not return email for this sign-in. Revoke this app in iOS Settings → Apple ID and retry.', 'apple_email_required');
223
+ super('Apple did not return email for this sign-in. Revoke this app in iOS Settings → Apple ID and retry.', 'oauth_email_required');
224
224
  this.name = 'AppleEmailRequiredError';
225
225
  Object.setPrototypeOf(this, AppleEmailRequiredError.prototype);
226
226
  }
@@ -234,7 +234,7 @@ export class OAuthEmailConflictError extends KoolbaseAuthError {
234
234
  }
235
235
  export class GoogleSignInNotConfiguredError extends KoolbaseAuthError {
236
236
  constructor() {
237
- super('Google Sign-In is not configured for this environment', 'google_not_configured');
237
+ super('Google Sign-In is not configured for this environment', 'oauth_not_configured');
238
238
  this.name = 'GoogleSignInNotConfiguredError';
239
239
  Object.setPrototypeOf(this, GoogleSignInNotConfiguredError.prototype);
240
240
  }
@@ -414,14 +414,14 @@ export class MalformedSessionResponseError extends KoolbaseAuthError {
414
414
  }
415
415
  export class InvalidGoogleTokenError extends KoolbaseAuthError {
416
416
  constructor() {
417
- super('Invalid Google identity token', 'invalid_google_token');
417
+ super('Invalid Google identity token', 'invalid_oauth_token');
418
418
  this.name = 'InvalidGoogleTokenError';
419
419
  Object.setPrototypeOf(this, InvalidGoogleTokenError.prototype);
420
420
  }
421
421
  }
422
422
  export class GoogleEmailRequiredError extends KoolbaseAuthError {
423
423
  constructor() {
424
- super('Google did not return email for this sign-in. Ensure the email scope is requested in the native flow.', 'google_email_required');
424
+ super('Google did not return email for this sign-in. Ensure the email scope is requested in the native flow.', 'oauth_email_required');
425
425
  this.name = 'GoogleEmailRequiredError';
426
426
  Object.setPrototypeOf(this, GoogleEmailRequiredError.prototype);
427
427
  }
@@ -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;