@koolbase/core 12.1.0 → 12.3.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/analytics.js +2 -1
- package/dist/cjs/auth-errors.d.ts +26 -0
- package/dist/cjs/auth-errors.js +58 -1
- package/dist/cjs/auth.d.ts +41 -0
- package/dist/cjs/auth.js +116 -1
- package/dist/cjs/database.d.ts +6 -0
- package/dist/cjs/database.js +18 -6
- package/dist/cjs/fiscal.js +3 -2
- package/dist/cjs/flags.d.ts +1 -1
- package/dist/cjs/flags.js +3 -2
- package/dist/cjs/functions.js +3 -2
- package/dist/cjs/index.d.ts +1 -0
- package/dist/cjs/index.js +1 -0
- package/dist/cjs/messaging.js +2 -1
- package/dist/cjs/network.d.ts +19 -0
- package/dist/cjs/network.js +52 -0
- package/dist/cjs/storage.js +12 -11
- package/dist/cjs/sync-engine.js +4 -3
- package/dist/cjs/types.d.ts +7 -0
- package/dist/esm/analytics.js +2 -1
- package/dist/esm/auth-errors.d.ts +26 -0
- package/dist/esm/auth-errors.js +51 -0
- package/dist/esm/auth.d.ts +41 -0
- package/dist/esm/auth.js +117 -2
- package/dist/esm/database.d.ts +6 -0
- package/dist/esm/database.js +18 -6
- package/dist/esm/fiscal.js +3 -2
- package/dist/esm/flags.d.ts +1 -1
- package/dist/esm/flags.js +3 -2
- package/dist/esm/functions.js +3 -2
- package/dist/esm/index.d.ts +1 -0
- package/dist/esm/index.js +1 -0
- package/dist/esm/messaging.js +2 -1
- package/dist/esm/network.d.ts +19 -0
- package/dist/esm/network.js +47 -0
- package/dist/esm/storage.js +12 -11
- package/dist/esm/sync-engine.js +4 -3
- package/dist/esm/types.d.ts +7 -0
- package/package.json +2 -2
package/dist/cjs/storage.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.KoolbaseStorage = void 0;
|
|
4
|
+
const network_js_1 = require("./network.js");
|
|
4
5
|
const errors_js_1 = require("./errors.js");
|
|
5
6
|
const storage_errors_js_1 = require("./storage-errors.js");
|
|
6
7
|
// --- Cloudflare image-transform URL helpers -------------------------------
|
|
@@ -97,7 +98,7 @@ class KoolbaseStorage {
|
|
|
97
98
|
? options.file.type || 'application/octet-stream'
|
|
98
99
|
: options.file.type;
|
|
99
100
|
// ─── Step 1: Get presigned upload URL ───
|
|
100
|
-
const urlRes = await
|
|
101
|
+
const urlRes = await (0, network_js_1.koolbaseFetch)(`${this.config.baseUrl}/v1/sdk/storage/upload-url`, {
|
|
101
102
|
method: 'POST',
|
|
102
103
|
headers: {
|
|
103
104
|
...(await this.buildHeaders()),
|
|
@@ -123,9 +124,9 @@ class KoolbaseStorage {
|
|
|
123
124
|
// the two hosts differ here and nowhere else in this method.
|
|
124
125
|
const fileBlob = isBlob
|
|
125
126
|
? options.file
|
|
126
|
-
: await (await
|
|
127
|
+
: await (await (0, network_js_1.koolbaseFetch)(options.file.uri)).blob();
|
|
127
128
|
const fileSize = fileBlob.size;
|
|
128
|
-
const uploadRes = await
|
|
129
|
+
const uploadRes = await (0, network_js_1.koolbaseFetch)(upload_url, {
|
|
129
130
|
method: 'PUT',
|
|
130
131
|
headers: { 'Content-Type': contentType },
|
|
131
132
|
body: fileBlob,
|
|
@@ -152,7 +153,7 @@ class KoolbaseStorage {
|
|
|
152
153
|
if (options.metadata !== undefined) {
|
|
153
154
|
confirmBody.metadata = options.metadata;
|
|
154
155
|
}
|
|
155
|
-
const confirmRes = await
|
|
156
|
+
const confirmRes = await (0, network_js_1.koolbaseFetch)(`${this.config.baseUrl}/v1/sdk/storage/confirm`, {
|
|
156
157
|
method: 'POST',
|
|
157
158
|
headers: {
|
|
158
159
|
...(await this.buildHeaders()),
|
|
@@ -203,7 +204,7 @@ class KoolbaseStorage {
|
|
|
203
204
|
* // -> { category: 'landscape', tag: 'sunset' }
|
|
204
205
|
*/
|
|
205
206
|
async updateMetadata(bucket, path, metadata) {
|
|
206
|
-
const res = await
|
|
207
|
+
const res = await (0, network_js_1.koolbaseFetch)(`${this.config.baseUrl}/v1/sdk/storage/objects/metadata`, {
|
|
207
208
|
method: 'PATCH',
|
|
208
209
|
headers: {
|
|
209
210
|
...(await this.buildHeaders()),
|
|
@@ -226,7 +227,7 @@ class KoolbaseStorage {
|
|
|
226
227
|
if (versionId) {
|
|
227
228
|
url += `&version_id=${encodeURIComponent(versionId)}`;
|
|
228
229
|
}
|
|
229
|
-
const res = await
|
|
230
|
+
const res = await (0, network_js_1.koolbaseFetch)(url, { headers: await this.buildHeaders() });
|
|
230
231
|
if (!res.ok) {
|
|
231
232
|
throw await this.error(res, 'Failed to get download URL');
|
|
232
233
|
}
|
|
@@ -321,7 +322,7 @@ class KoolbaseStorage {
|
|
|
321
322
|
const url = forcePurge
|
|
322
323
|
? `${this.config.baseUrl}/v1/sdk/storage/object?force_purge=true`
|
|
323
324
|
: `${this.config.baseUrl}/v1/sdk/storage/object`;
|
|
324
|
-
const res = await
|
|
325
|
+
const res = await (0, network_js_1.koolbaseFetch)(url, {
|
|
325
326
|
method: 'DELETE',
|
|
326
327
|
headers: {
|
|
327
328
|
...(await this.buildHeaders()),
|
|
@@ -348,7 +349,7 @@ class KoolbaseStorage {
|
|
|
348
349
|
async listVersions(bucket, path) {
|
|
349
350
|
const url = `${this.config.baseUrl}/v1/sdk/storage/object-versions` +
|
|
350
351
|
`?bucket=${encodeURIComponent(bucket)}&path=${encodeURIComponent(path)}`;
|
|
351
|
-
const res = await
|
|
352
|
+
const res = await (0, network_js_1.koolbaseFetch)(url, { headers: await this.buildHeaders() });
|
|
352
353
|
if (!res.ok) {
|
|
353
354
|
throw await this.error(res, 'Failed to list versions');
|
|
354
355
|
}
|
|
@@ -364,7 +365,7 @@ class KoolbaseStorage {
|
|
|
364
365
|
async getVersion(bucket, path, versionId) {
|
|
365
366
|
const url = `${this.config.baseUrl}/v1/sdk/storage/object-versions/${encodeURIComponent(versionId)}` +
|
|
366
367
|
`?bucket=${encodeURIComponent(bucket)}&path=${encodeURIComponent(path)}`;
|
|
367
|
-
const res = await
|
|
368
|
+
const res = await (0, network_js_1.koolbaseFetch)(url, { headers: await this.buildHeaders() });
|
|
368
369
|
if (!res.ok) {
|
|
369
370
|
throw await this.error(res, 'Failed to fetch version');
|
|
370
371
|
}
|
|
@@ -383,7 +384,7 @@ class KoolbaseStorage {
|
|
|
383
384
|
async restoreVersion(bucket, path, versionId) {
|
|
384
385
|
const url = `${this.config.baseUrl}/v1/sdk/storage/object-versions/${encodeURIComponent(versionId)}/restore` +
|
|
385
386
|
`?bucket=${encodeURIComponent(bucket)}&path=${encodeURIComponent(path)}`;
|
|
386
|
-
const res = await
|
|
387
|
+
const res = await (0, network_js_1.koolbaseFetch)(url, {
|
|
387
388
|
method: 'POST',
|
|
388
389
|
headers: await this.buildHeaders(),
|
|
389
390
|
});
|
|
@@ -401,7 +402,7 @@ class KoolbaseStorage {
|
|
|
401
402
|
async purgeVersion(bucket, path, versionId) {
|
|
402
403
|
const url = `${this.config.baseUrl}/v1/sdk/storage/object-versions/${encodeURIComponent(versionId)}` +
|
|
403
404
|
`?bucket=${encodeURIComponent(bucket)}&path=${encodeURIComponent(path)}`;
|
|
404
|
-
const res = await
|
|
405
|
+
const res = await (0, network_js_1.koolbaseFetch)(url, {
|
|
405
406
|
method: 'DELETE',
|
|
406
407
|
headers: await this.buildHeaders(),
|
|
407
408
|
});
|
package/dist/cjs/sync-engine.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.SyncEngine = void 0;
|
|
4
|
+
const network_js_1 = require("./network.js");
|
|
4
5
|
const offline_state_js_1 = require("./offline-state.js");
|
|
5
6
|
const errors_js_1 = require("./errors.js");
|
|
6
7
|
const platform_js_1 = require("./platform.js");
|
|
@@ -245,7 +246,7 @@ class SyncEngine {
|
|
|
245
246
|
: `${this.config.baseUrl}/v1/sdk/db/records/${write.recordId}`;
|
|
246
247
|
let res;
|
|
247
248
|
if (write.operation === 'insert') {
|
|
248
|
-
res = await
|
|
249
|
+
res = await (0, network_js_1.koolbaseFetch)(url, {
|
|
249
250
|
method: 'POST',
|
|
250
251
|
headers,
|
|
251
252
|
// The write's own id: generated at enqueue, identical on every retry.
|
|
@@ -259,7 +260,7 @@ class SyncEngine {
|
|
|
259
260
|
});
|
|
260
261
|
}
|
|
261
262
|
else if (write.operation === 'update') {
|
|
262
|
-
res = await
|
|
263
|
+
res = await (0, network_js_1.koolbaseFetch)(url, {
|
|
263
264
|
method: 'PATCH',
|
|
264
265
|
headers,
|
|
265
266
|
// The revision the change was composed against. The server applies it
|
|
@@ -278,7 +279,7 @@ class SyncEngine {
|
|
|
278
279
|
const q = write.baseRevision !== undefined
|
|
279
280
|
? `?expected_revision=${write.baseRevision}`
|
|
280
281
|
: '';
|
|
281
|
-
res = await
|
|
282
|
+
res = await (0, network_js_1.koolbaseFetch)(`${url}${q}`, { method: 'DELETE', headers });
|
|
282
283
|
}
|
|
283
284
|
if (res.status === 401)
|
|
284
285
|
throw new errors_js_1.KoolbaseUnauthenticatedError('unauthorized');
|
package/dist/cjs/types.d.ts
CHANGED
|
@@ -239,6 +239,13 @@ export interface QueryOptions {
|
|
|
239
239
|
orderBy?: string;
|
|
240
240
|
orderDesc?: boolean;
|
|
241
241
|
populate?: string[];
|
|
242
|
+
/**
|
|
243
|
+
* How to read: 'default' is cache-first, refreshed in the background;
|
|
244
|
+
* 'network-only' skips the cache and waits for the server (the result still
|
|
245
|
+
* refreshes the cache). Use 'network-only' after a write this client did not
|
|
246
|
+
* make, by a Function or another user, when you need the server's state now.
|
|
247
|
+
*/
|
|
248
|
+
cache?: 'default' | 'network-only';
|
|
242
249
|
}
|
|
243
250
|
export interface QueryResult {
|
|
244
251
|
records: KoolbaseRecord[];
|
package/dist/esm/analytics.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { koolbaseFetch } from './network.js';
|
|
1
2
|
import { getPlatform } from './platform.js';
|
|
2
3
|
import { getOrCreateDeviceId } from './device-id.js';
|
|
3
4
|
// ─── KoolbaseAnalytics ───────────────────────────────────────────────────────
|
|
@@ -99,7 +100,7 @@ export class KoolbaseAnalytics {
|
|
|
99
100
|
const batch = [...this.queue];
|
|
100
101
|
this.queue = [];
|
|
101
102
|
try {
|
|
102
|
-
const response = await
|
|
103
|
+
const response = await koolbaseFetch(`${this.config.baseUrl}/v1/analytics/events`, {
|
|
103
104
|
method: 'POST',
|
|
104
105
|
headers: {
|
|
105
106
|
'Content-Type': 'application/json',
|
|
@@ -257,3 +257,29 @@ export declare class InvalidGoogleTokenError extends KoolbaseAuthError {
|
|
|
257
257
|
export declare class GoogleEmailRequiredError extends KoolbaseAuthError {
|
|
258
258
|
constructor();
|
|
259
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
|
+
}
|
package/dist/esm/auth-errors.js
CHANGED
|
@@ -438,3 +438,54 @@ export class GoogleEmailRequiredError extends KoolbaseAuthError {
|
|
|
438
438
|
Object.setPrototypeOf(this, GoogleEmailRequiredError.prototype);
|
|
439
439
|
}
|
|
440
440
|
}
|
|
441
|
+
// ─── Two-step sign-in (MFA) ─────────────────────────────────────────────────
|
|
442
|
+
/**
|
|
443
|
+
* The first factor passed and this account needs a second: sign-in is not
|
|
444
|
+
* finished. Pass `challengeToken` to `verifyMfa` with a code from the
|
|
445
|
+
* person's authenticator app, or to `verifyRecoveryCode`. Apps that never
|
|
446
|
+
* enable MFA never see this.
|
|
447
|
+
*/
|
|
448
|
+
export class MfaRequiredError extends KoolbaseAuthError {
|
|
449
|
+
constructor(challengeToken, expiresAt) {
|
|
450
|
+
super('Two-step sign-in required', 'mfa_required');
|
|
451
|
+
this.name = 'MfaRequiredError';
|
|
452
|
+
this.challengeToken = challengeToken;
|
|
453
|
+
this.expiresAt = expiresAt;
|
|
454
|
+
Object.setPrototypeOf(this, MfaRequiredError.prototype);
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
export class RecentAuthRequiredError extends KoolbaseAuthError {
|
|
458
|
+
constructor() {
|
|
459
|
+
super('Sign in again to change two-step sign-in', 'recent_auth_required');
|
|
460
|
+
this.name = 'RecentAuthRequiredError';
|
|
461
|
+
Object.setPrototypeOf(this, RecentAuthRequiredError.prototype);
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
export class RecentMfaRequiredError extends KoolbaseAuthError {
|
|
465
|
+
constructor() {
|
|
466
|
+
super('Confirm with your authenticator first', 'recent_mfa_required');
|
|
467
|
+
this.name = 'RecentMfaRequiredError';
|
|
468
|
+
Object.setPrototypeOf(this, RecentMfaRequiredError.prototype);
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
export class MfaAlreadyEnabledError extends KoolbaseAuthError {
|
|
472
|
+
constructor() {
|
|
473
|
+
super('Two-step sign-in is already on for this account', 'mfa_already_enabled');
|
|
474
|
+
this.name = 'MfaAlreadyEnabledError';
|
|
475
|
+
Object.setPrototypeOf(this, MfaAlreadyEnabledError.prototype);
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
export class MfaEnrollmentNotFoundError extends KoolbaseAuthError {
|
|
479
|
+
constructor() {
|
|
480
|
+
super('No enrolment in progress; start again', 'mfa_enrollment_not_found');
|
|
481
|
+
this.name = 'MfaEnrollmentNotFoundError';
|
|
482
|
+
Object.setPrototypeOf(this, MfaEnrollmentNotFoundError.prototype);
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
export class MfaNotEnabledError extends KoolbaseAuthError {
|
|
486
|
+
constructor() {
|
|
487
|
+
super('Two-step sign-in is not on for this account', 'mfa_not_enabled');
|
|
488
|
+
this.name = 'MfaNotEnabledError';
|
|
489
|
+
Object.setPrototypeOf(this, MfaNotEnabledError.prototype);
|
|
490
|
+
}
|
|
491
|
+
}
|
package/dist/esm/auth.d.ts
CHANGED
|
@@ -343,6 +343,47 @@ export declare class KoolbaseAuth {
|
|
|
343
343
|
email: string;
|
|
344
344
|
code: string;
|
|
345
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[]>;
|
|
346
387
|
sendOtp(params: SendOtpParams): Promise<OtpSendResult>;
|
|
347
388
|
verifyOtp(params: VerifyOtpParams): Promise<PhoneVerifyResult>;
|
|
348
389
|
linkPhone(params: LinkPhoneParams): Promise<void>;
|
package/dist/esm/auth.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
import { koolbaseFetch } from './network.js';
|
|
1
2
|
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, EmailCodeDisabledError, } from './auth-errors.js';
|
|
3
|
+
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, MfaRequiredError, RecentAuthRequiredError, RecentMfaRequiredError, MfaAlreadyEnabledError, MfaEnrollmentNotFoundError, MfaNotEnabledError, } from './auth-errors.js';
|
|
3
4
|
import { getPlatform } from './platform.js';
|
|
4
5
|
import { DeviceMetadata } from './device-metadata.js';
|
|
5
6
|
export class KoolbaseAuth {
|
|
@@ -9,7 +10,7 @@ export class KoolbaseAuth {
|
|
|
9
10
|
this.listeners = new Set();
|
|
10
11
|
this.config = config;
|
|
11
12
|
this.metadata = new DeviceMetadata(config.appVersion);
|
|
12
|
-
this.fetchFn = config.fetch ?? ((url, init) =>
|
|
13
|
+
this.fetchFn = config.fetch ?? ((url, init) => koolbaseFetch(url, init));
|
|
13
14
|
this.timeoutMs = config.authTimeout ?? 10000;
|
|
14
15
|
if (config.authStorage) {
|
|
15
16
|
this.storage = config.authStorage;
|
|
@@ -385,6 +386,8 @@ export class KoolbaseAuth {
|
|
|
385
386
|
const errorMessage = body?.error ?? '';
|
|
386
387
|
// ─── code-first ───
|
|
387
388
|
switch (code) {
|
|
389
|
+
case 'mfa_required':
|
|
390
|
+
throw this.mfaRequired(body);
|
|
388
391
|
case 'oauth_not_configured':
|
|
389
392
|
throw new GoogleSignInNotConfiguredError();
|
|
390
393
|
case 'invalid_oauth_token':
|
|
@@ -439,6 +442,8 @@ export class KoolbaseAuth {
|
|
|
439
442
|
const errorMessage = body?.error ?? '';
|
|
440
443
|
// ─── code-first ───
|
|
441
444
|
switch (code) {
|
|
445
|
+
case 'mfa_required':
|
|
446
|
+
throw this.mfaRequired(body);
|
|
442
447
|
case 'oauth_not_configured':
|
|
443
448
|
throw new AppleSignInNotConfiguredError();
|
|
444
449
|
case 'invalid_oauth_token':
|
|
@@ -870,6 +875,102 @@ export class KoolbaseAuth {
|
|
|
870
875
|
await this.setSessionInternal(session);
|
|
871
876
|
return session;
|
|
872
877
|
}
|
|
878
|
+
// ─── Two-step sign-in (MFA) ─────────────────────────────────────────────
|
|
879
|
+
//
|
|
880
|
+
// When an account has MFA on, every sign-in method throws MfaRequiredError.
|
|
881
|
+
// Finish with verifyMfa() or verifyRecoveryCode().
|
|
882
|
+
// One builder for every parser, so the four cannot drift apart in how the
|
|
883
|
+
// challenge reaches the app.
|
|
884
|
+
mfaRequired(body) {
|
|
885
|
+
const d = body?.details ?? {};
|
|
886
|
+
return new MfaRequiredError(String(d.challenge_token ?? ''), d.expires_at);
|
|
887
|
+
}
|
|
888
|
+
/** Finishes sign-in with a code from the person's authenticator app. */
|
|
889
|
+
async verifyMfa(params) {
|
|
890
|
+
const res = await this.authRequest('/v1/sdk/auth/mfa/verify', {
|
|
891
|
+
method: 'POST',
|
|
892
|
+
body: { challenge_token: params.challengeToken, code: params.code },
|
|
893
|
+
});
|
|
894
|
+
const session = await this.parseSessionResponse(res, false);
|
|
895
|
+
await this.setSessionInternal(session);
|
|
896
|
+
return session;
|
|
897
|
+
}
|
|
898
|
+
/**
|
|
899
|
+
* Finishes sign-in with a recovery code. Each works once; prompt the person
|
|
900
|
+
* to regenerate when recoveryCodesRemaining reaches zero.
|
|
901
|
+
*/
|
|
902
|
+
async verifyRecoveryCode(params) {
|
|
903
|
+
const res = await this.authRequest('/v1/sdk/auth/mfa/verify-recovery', {
|
|
904
|
+
method: 'POST',
|
|
905
|
+
body: { challenge_token: params.challengeToken, code: params.code },
|
|
906
|
+
});
|
|
907
|
+
if (!res.ok)
|
|
908
|
+
await this.throwTypedError(res, false);
|
|
909
|
+
// Read once: the session and the remaining count come from one body.
|
|
910
|
+
const data = await res.json();
|
|
911
|
+
if (!data?.access_token)
|
|
912
|
+
throw new MalformedSessionResponseError('an access token');
|
|
913
|
+
if (!data?.refresh_token)
|
|
914
|
+
throw new MalformedSessionResponseError('a refresh token');
|
|
915
|
+
const session = {
|
|
916
|
+
accessToken: data.access_token,
|
|
917
|
+
refreshToken: data.refresh_token,
|
|
918
|
+
expiresAt: data.expires_at,
|
|
919
|
+
user: this.mapUser(data.user),
|
|
920
|
+
};
|
|
921
|
+
await this.setSessionInternal(session);
|
|
922
|
+
return { session, recoveryCodesRemaining: Number(data.recovery_codes_remaining ?? 0) };
|
|
923
|
+
}
|
|
924
|
+
/**
|
|
925
|
+
* Starts adding an authenticator; needs a sign-in within ten minutes. Show
|
|
926
|
+
* otpauthUri as a QR code, then call confirmMfaEnrollment with the first
|
|
927
|
+
* code the app shows.
|
|
928
|
+
*/
|
|
929
|
+
async enrollMfa() {
|
|
930
|
+
const res = await this.authRequest('/v1/sdk/auth/mfa/enroll', { method: 'POST', includeAuth: true });
|
|
931
|
+
await this.checkResponse(res);
|
|
932
|
+
const d = await res.json();
|
|
933
|
+
return { otpauthUri: d.otpauth_uri, secret: d.secret };
|
|
934
|
+
}
|
|
935
|
+
/** Turns MFA on and returns ten recovery codes, shown this once only. Other devices are signed out. */
|
|
936
|
+
async confirmMfaEnrollment(code) {
|
|
937
|
+
const res = await this.authRequest('/v1/sdk/auth/mfa/enroll/confirm', {
|
|
938
|
+
method: 'POST',
|
|
939
|
+
body: { code },
|
|
940
|
+
includeAuth: true,
|
|
941
|
+
});
|
|
942
|
+
await this.checkResponse(res);
|
|
943
|
+
return (await res.json()).recovery_codes;
|
|
944
|
+
}
|
|
945
|
+
async mfaStatus() {
|
|
946
|
+
const res = await this.authRequest('/v1/sdk/auth/mfa', { method: 'GET', includeAuth: true });
|
|
947
|
+
await this.checkResponse(res);
|
|
948
|
+
const d = await res.json();
|
|
949
|
+
return { enabled: !!d.enabled, recoveryCodesRemaining: Number(d.recovery_codes_remaining ?? 0) };
|
|
950
|
+
}
|
|
951
|
+
/** Re-confirms the second factor, opening ten minutes to change MFA. */
|
|
952
|
+
async stepUpMfa(params) {
|
|
953
|
+
const res = await this.authRequest('/v1/sdk/auth/mfa/step-up', {
|
|
954
|
+
method: 'POST',
|
|
955
|
+
body: {
|
|
956
|
+
...(params.code ? { code: params.code } : {}),
|
|
957
|
+
...(params.recoveryCode ? { recovery_code: params.recoveryCode } : {}),
|
|
958
|
+
},
|
|
959
|
+
includeAuth: true,
|
|
960
|
+
});
|
|
961
|
+
await this.checkResponse(res);
|
|
962
|
+
}
|
|
963
|
+
/** Turns MFA off. Needs stepUpMfa() within the last ten minutes. */
|
|
964
|
+
async disableMfa() {
|
|
965
|
+
const res = await this.authRequest('/v1/sdk/auth/mfa/disable', { method: 'POST', includeAuth: true });
|
|
966
|
+
await this.checkResponse(res);
|
|
967
|
+
}
|
|
968
|
+
/** Replaces all recovery codes; the old ones stop working. Needs stepUpMfa() within ten minutes. */
|
|
969
|
+
async regenerateRecoveryCodes() {
|
|
970
|
+
const res = await this.authRequest('/v1/sdk/auth/mfa/recovery-codes', { method: 'POST', includeAuth: true });
|
|
971
|
+
await this.checkResponse(res);
|
|
972
|
+
return (await res.json()).recovery_codes;
|
|
973
|
+
}
|
|
873
974
|
// ─── Phone OTP ──────────────────────────────────────────────────────────
|
|
874
975
|
async sendOtp(params) {
|
|
875
976
|
this.validatePhone(params.phoneNumber);
|
|
@@ -1037,6 +1138,18 @@ export class KoolbaseAuth {
|
|
|
1037
1138
|
const msg = body.error ?? '';
|
|
1038
1139
|
// ─── code-first ───
|
|
1039
1140
|
switch (code) {
|
|
1141
|
+
case 'mfa_required':
|
|
1142
|
+
throw this.mfaRequired(body);
|
|
1143
|
+
case 'recent_auth_required':
|
|
1144
|
+
throw new RecentAuthRequiredError();
|
|
1145
|
+
case 'recent_mfa_required':
|
|
1146
|
+
throw new RecentMfaRequiredError();
|
|
1147
|
+
case 'mfa_already_enabled':
|
|
1148
|
+
throw new MfaAlreadyEnabledError();
|
|
1149
|
+
case 'mfa_enrollment_not_found':
|
|
1150
|
+
throw new MfaEnrollmentNotFoundError();
|
|
1151
|
+
case 'mfa_not_enabled':
|
|
1152
|
+
throw new MfaNotEnabledError();
|
|
1040
1153
|
case 'invalid_credentials':
|
|
1041
1154
|
throw new InvalidCredentialsError();
|
|
1042
1155
|
case 'email_in_use':
|
|
@@ -1157,6 +1270,8 @@ export class KoolbaseAuth {
|
|
|
1157
1270
|
const msg = body.error ?? '';
|
|
1158
1271
|
// ─── code-first ───
|
|
1159
1272
|
switch (code) {
|
|
1273
|
+
case 'mfa_required':
|
|
1274
|
+
throw this.mfaRequired(body);
|
|
1160
1275
|
case 'invalid_phone':
|
|
1161
1276
|
throw new InvalidPhoneNumberError();
|
|
1162
1277
|
case 'otp_expired':
|
package/dist/esm/database.d.ts
CHANGED
|
@@ -47,6 +47,12 @@ export declare class KoolbaseDatabase {
|
|
|
47
47
|
* store for `update`/`delete`.
|
|
48
48
|
*/
|
|
49
49
|
query(collection: string, options?: QueryOptions): Promise<QueryResult>;
|
|
50
|
+
/**
|
|
51
|
+
* Forget this client's cached query results for a collection, so the next
|
|
52
|
+
* `query` waits for the server. For data changed elsewhere: by a Function,
|
|
53
|
+
* a server, or another user. The SDK's own writes already do this.
|
|
54
|
+
*/
|
|
55
|
+
invalidate(collection: string): Promise<void>;
|
|
50
56
|
/**
|
|
51
57
|
* Insert a new record into a collection.
|
|
52
58
|
*
|
package/dist/esm/database.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { koolbaseFetch } from './network.js';
|
|
1
2
|
import { KoolbaseError, KoolbaseOfflineBaselineUnavailableError, KoolbaseUnauthenticatedError, } from './errors.js';
|
|
2
3
|
import { cacheRecord, getCachedRecord, removeCachedRecord } from './cache-store.js';
|
|
3
4
|
import { readOfflineState, mutateOfflineState, queueWrite, } from './offline-state.js';
|
|
@@ -85,7 +86,7 @@ export class KoolbaseDatabase {
|
|
|
85
86
|
};
|
|
86
87
|
}
|
|
87
88
|
async request(method, path, body) {
|
|
88
|
-
const res = await
|
|
89
|
+
const res = await koolbaseFetch(`${this.config.baseUrl}${path}`, {
|
|
89
90
|
method,
|
|
90
91
|
headers: await this.buildHeaders(),
|
|
91
92
|
body: body ? JSON.stringify(body) : undefined,
|
|
@@ -123,7 +124,7 @@ export class KoolbaseDatabase {
|
|
|
123
124
|
* none of them clearing a rejected session. One path, two shapes of result.
|
|
124
125
|
*/
|
|
125
126
|
async requestWithStatus(method, path, body) {
|
|
126
|
-
const res = await
|
|
127
|
+
const res = await koolbaseFetch(`${this.config.baseUrl}${path}`, {
|
|
127
128
|
method,
|
|
128
129
|
headers: await this.buildHeaders(),
|
|
129
130
|
body: body ? JSON.stringify(body) : undefined,
|
|
@@ -194,20 +195,31 @@ export class KoolbaseDatabase {
|
|
|
194
195
|
*/
|
|
195
196
|
async query(collection, options = {}) {
|
|
196
197
|
const userId = this.getUserId() ?? 'anonymous';
|
|
197
|
-
|
|
198
|
-
|
|
198
|
+
// The cache policy is how to read, not what to read: it stays out of the
|
|
199
|
+
// query's identity, so both policies share one cache entry.
|
|
200
|
+
const { cache: cachePolicy, ...queryOptions } = options;
|
|
201
|
+
const queryHash = hashQuery(collection, queryOptions);
|
|
202
|
+
const cached = cachePolicy === 'network-only' ? null : await getCached(userId, collection, queryHash);
|
|
199
203
|
if (cached) {
|
|
200
|
-
this.runQuery(collection,
|
|
204
|
+
this.runQuery(collection, queryOptions)
|
|
201
205
|
.then(result => setCached(userId, collection, queryHash, result))
|
|
202
206
|
.catch(() => {
|
|
203
207
|
// Network unavailable — cached data already returned
|
|
204
208
|
});
|
|
205
209
|
return { ...cached, isFromCache: true };
|
|
206
210
|
}
|
|
207
|
-
const result = await this.runQuery(collection,
|
|
211
|
+
const result = await this.runQuery(collection, queryOptions);
|
|
208
212
|
await setCached(userId, collection, queryHash, result);
|
|
209
213
|
return { ...result, isFromCache: false };
|
|
210
214
|
}
|
|
215
|
+
/**
|
|
216
|
+
* Forget this client's cached query results for a collection, so the next
|
|
217
|
+
* `query` waits for the server. For data changed elsewhere: by a Function,
|
|
218
|
+
* a server, or another user. The SDK's own writes already do this.
|
|
219
|
+
*/
|
|
220
|
+
async invalidate(collection) {
|
|
221
|
+
await invalidateCache(this.getUserId() ?? 'anonymous', collection);
|
|
222
|
+
}
|
|
211
223
|
// ─── Insert (online-first with offline fallback) ───────────────────────────
|
|
212
224
|
/**
|
|
213
225
|
* Insert a new record into a collection.
|
package/dist/esm/fiscal.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { koolbaseFetch } from './network.js';
|
|
1
2
|
import { KoolbaseError, KoolbaseUnauthenticatedError } from './errors.js';
|
|
2
3
|
const KNOWN_STATUSES = [
|
|
3
4
|
'created', 'blocked', 'queued', 'submitting',
|
|
@@ -105,7 +106,7 @@ export class KoolbaseFiscal {
|
|
|
105
106
|
const controller = new AbortController();
|
|
106
107
|
const timer = setTimeout(() => controller.abort(), params.timeoutMs ?? 30000);
|
|
107
108
|
try {
|
|
108
|
-
const res = await
|
|
109
|
+
const res = await koolbaseFetch(`${this.config.baseUrl}/v1/sdk/fiscal/submit`, {
|
|
109
110
|
method: 'POST',
|
|
110
111
|
headers: await this.buildHeaders(),
|
|
111
112
|
signal: controller.signal,
|
|
@@ -143,7 +144,7 @@ export class KoolbaseFiscal {
|
|
|
143
144
|
device_id: params.deviceId,
|
|
144
145
|
client_ref: params.clientRef,
|
|
145
146
|
});
|
|
146
|
-
const res = await
|
|
147
|
+
const res = await koolbaseFetch(`${this.config.baseUrl}/v1/sdk/fiscal/status?${q.toString()}`, { method: 'GET', headers: await this.buildHeaders(), signal: controller.signal });
|
|
147
148
|
return await this.decode(res);
|
|
148
149
|
}
|
|
149
150
|
catch (e) {
|
package/dist/esm/flags.d.ts
CHANGED
|
@@ -4,7 +4,7 @@ export declare class KoolbaseFlags {
|
|
|
4
4
|
private payload;
|
|
5
5
|
private deviceId;
|
|
6
6
|
constructor(config: KoolbaseConfig, deviceId: string);
|
|
7
|
-
|
|
7
|
+
koolbaseFetch(appVersion: string, platform: string): Promise<void>;
|
|
8
8
|
isEnabled(key: string): boolean;
|
|
9
9
|
getString(key: string, fallback?: string): string;
|
|
10
10
|
getNumber(key: string, fallback?: number): number;
|
package/dist/esm/flags.js
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
|
+
import { koolbaseFetch } from './network.js';
|
|
1
2
|
export class KoolbaseFlags {
|
|
2
3
|
constructor(config, deviceId) {
|
|
3
4
|
this.payload = null;
|
|
4
5
|
this.config = config;
|
|
5
6
|
this.deviceId = deviceId;
|
|
6
7
|
}
|
|
7
|
-
async
|
|
8
|
+
async koolbaseFetch(appVersion, platform) {
|
|
8
9
|
try {
|
|
9
|
-
const res = await
|
|
10
|
+
const res = await koolbaseFetch(`${this.config.baseUrl}/v1/bootstrap?public_key=${this.config.publicKey}&device_id=${this.deviceId}&app_version=${appVersion}&platform=${platform}`);
|
|
10
11
|
if (res.ok) {
|
|
11
12
|
this.payload = await res.json();
|
|
12
13
|
}
|
package/dist/esm/functions.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { koolbaseFetch } from './network.js';
|
|
1
2
|
import { functionInvokeError } from './function-errors.js';
|
|
2
3
|
import { KoolbaseUnauthenticatedError } from './errors.js';
|
|
3
4
|
import { FunctionRuntime, } from './types.js';
|
|
@@ -10,7 +11,7 @@ export class KoolbaseFunctions {
|
|
|
10
11
|
// ─── Deploy ────────────────────────────────────────────────────────────────
|
|
11
12
|
async deploy(options) {
|
|
12
13
|
const runtime = options.runtime ?? FunctionRuntime.Deno;
|
|
13
|
-
const res = await
|
|
14
|
+
const res = await koolbaseFetch(`${this.config.baseUrl}/v1/sdk/functions/deploy`, {
|
|
14
15
|
method: 'POST',
|
|
15
16
|
headers: {
|
|
16
17
|
'Content-Type': 'application/json',
|
|
@@ -54,7 +55,7 @@ export class KoolbaseFunctions {
|
|
|
54
55
|
if (userToken) {
|
|
55
56
|
headers['Authorization'] = `Bearer ${userToken}`;
|
|
56
57
|
}
|
|
57
|
-
const res = await
|
|
58
|
+
const res = await koolbaseFetch(`${this.config.baseUrl}/v1/sdk/functions/${name}`, {
|
|
58
59
|
method: 'POST',
|
|
59
60
|
headers,
|
|
60
61
|
body: JSON.stringify({ body: body ?? {} }),
|
package/dist/esm/index.d.ts
CHANGED
package/dist/esm/index.js
CHANGED
package/dist/esm/messaging.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { koolbaseFetch } from './network.js';
|
|
1
2
|
// ─── KoolbaseMessaging ────────────────────────────────────────────────────────
|
|
2
3
|
export class KoolbaseMessaging {
|
|
3
4
|
constructor(config) {
|
|
@@ -10,7 +11,7 @@ export class KoolbaseMessaging {
|
|
|
10
11
|
// ─── Register token ───────────────────────────────────────────────────────
|
|
11
12
|
async registerToken(options) {
|
|
12
13
|
try {
|
|
13
|
-
const response = await
|
|
14
|
+
const response = await koolbaseFetch(`${this.config.baseUrl}/v1/messaging/register`, {
|
|
14
15
|
method: 'POST',
|
|
15
16
|
headers: {
|
|
16
17
|
'Content-Type': 'application/json',
|