@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.
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.KoolbaseAnalytics = void 0;
4
+ const network_js_1 = require("./network.js");
4
5
  const platform_js_1 = require("./platform.js");
5
6
  const device_id_js_1 = require("./device-id.js");
6
7
  // ─── KoolbaseAnalytics ───────────────────────────────────────────────────────
@@ -102,7 +103,7 @@ class KoolbaseAnalytics {
102
103
  const batch = [...this.queue];
103
104
  this.queue = [];
104
105
  try {
105
- const response = await fetch(`${this.config.baseUrl}/v1/analytics/events`, {
106
+ const response = await (0, network_js_1.koolbaseFetch)(`${this.config.baseUrl}/v1/analytics/events`, {
106
107
  method: 'POST',
107
108
  headers: {
108
109
  '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
+ }
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.GoogleEmailRequiredError = exports.InvalidGoogleTokenError = exports.MalformedSessionResponseError = exports.ProviderIdentityAlreadyLinkedError = exports.IdentityNotFoundError = exports.InsufficientScopeError = exports.HideRequiresVerificationError = exports.LastCredentialError = exports.InsufficientAuthorityError = exports.SessionRequiredError = exports.UnsupportedOAuthProviderError = exports.OAuthOnlyAccountError = exports.AccountExistsError = exports.CurrentPasswordIncorrectError = exports.TokenAlreadyUsedError = exports.TokenExpiredError = exports.SignupsDisabledError = exports.ContactNotVerifiedError = exports.GoogleSignInNotConfiguredError = exports.OAuthEmailConflictError = exports.AppleEmailRequiredError = exports.InvalidAppleTokenError = exports.AppleSignInNotConfiguredError = exports.SmsConfigMissingError = exports.PhoneAlreadyLinkedError = exports.OtpRateLimitError = exports.OtpMaxAttemptsError = exports.OtpInvalidError = exports.OtpExpiredError = exports.EmailCodeDisabledError = exports.InvalidPhoneNumberError = exports.NetworkError = exports.VerificationResendDailyCapError = exports.VerificationResendCooldownError = exports.RateLimitError = exports.UnlockTokenInvalidError = exports.AccountLockedError = exports.TokenRevokedError = exports.SessionExpiredError = exports.WeakPasswordError = exports.UserDisabledError = exports.EmailAlreadyInUseError = exports.InvalidCredentialsError = exports.KoolbaseAuthError = void 0;
3
+ exports.MfaNotEnabledError = exports.MfaEnrollmentNotFoundError = exports.MfaAlreadyEnabledError = exports.RecentMfaRequiredError = exports.RecentAuthRequiredError = exports.MfaRequiredError = exports.GoogleEmailRequiredError = exports.InvalidGoogleTokenError = exports.MalformedSessionResponseError = exports.ProviderIdentityAlreadyLinkedError = exports.IdentityNotFoundError = exports.InsufficientScopeError = exports.HideRequiresVerificationError = exports.LastCredentialError = exports.InsufficientAuthorityError = exports.SessionRequiredError = exports.UnsupportedOAuthProviderError = exports.OAuthOnlyAccountError = exports.AccountExistsError = exports.CurrentPasswordIncorrectError = exports.TokenAlreadyUsedError = exports.TokenExpiredError = exports.SignupsDisabledError = exports.ContactNotVerifiedError = exports.GoogleSignInNotConfiguredError = exports.OAuthEmailConflictError = exports.AppleEmailRequiredError = exports.InvalidAppleTokenError = exports.AppleSignInNotConfiguredError = exports.SmsConfigMissingError = exports.PhoneAlreadyLinkedError = exports.OtpRateLimitError = exports.OtpMaxAttemptsError = exports.OtpInvalidError = exports.OtpExpiredError = exports.EmailCodeDisabledError = exports.InvalidPhoneNumberError = exports.NetworkError = exports.VerificationResendDailyCapError = exports.VerificationResendCooldownError = exports.RateLimitError = exports.UnlockTokenInvalidError = exports.AccountLockedError = exports.TokenRevokedError = exports.SessionExpiredError = exports.WeakPasswordError = exports.UserDisabledError = exports.EmailAlreadyInUseError = exports.InvalidCredentialsError = exports.KoolbaseAuthError = void 0;
4
4
  const errors_js_1 = require("./errors.js");
5
5
  /**
6
6
  * Base error type for all Koolbase auth errors. Catchable via
@@ -485,3 +485,60 @@ class GoogleEmailRequiredError extends KoolbaseAuthError {
485
485
  }
486
486
  }
487
487
  exports.GoogleEmailRequiredError = GoogleEmailRequiredError;
488
+ // ─── Two-step sign-in (MFA) ─────────────────────────────────────────────────
489
+ /**
490
+ * The first factor passed and this account needs a second: sign-in is not
491
+ * finished. Pass `challengeToken` to `verifyMfa` with a code from the
492
+ * person's authenticator app, or to `verifyRecoveryCode`. Apps that never
493
+ * enable MFA never see this.
494
+ */
495
+ class MfaRequiredError extends KoolbaseAuthError {
496
+ constructor(challengeToken, expiresAt) {
497
+ super('Two-step sign-in required', 'mfa_required');
498
+ this.name = 'MfaRequiredError';
499
+ this.challengeToken = challengeToken;
500
+ this.expiresAt = expiresAt;
501
+ Object.setPrototypeOf(this, MfaRequiredError.prototype);
502
+ }
503
+ }
504
+ exports.MfaRequiredError = MfaRequiredError;
505
+ class RecentAuthRequiredError extends KoolbaseAuthError {
506
+ constructor() {
507
+ super('Sign in again to change two-step sign-in', 'recent_auth_required');
508
+ this.name = 'RecentAuthRequiredError';
509
+ Object.setPrototypeOf(this, RecentAuthRequiredError.prototype);
510
+ }
511
+ }
512
+ exports.RecentAuthRequiredError = RecentAuthRequiredError;
513
+ class RecentMfaRequiredError extends KoolbaseAuthError {
514
+ constructor() {
515
+ super('Confirm with your authenticator first', 'recent_mfa_required');
516
+ this.name = 'RecentMfaRequiredError';
517
+ Object.setPrototypeOf(this, RecentMfaRequiredError.prototype);
518
+ }
519
+ }
520
+ exports.RecentMfaRequiredError = RecentMfaRequiredError;
521
+ class MfaAlreadyEnabledError extends KoolbaseAuthError {
522
+ constructor() {
523
+ super('Two-step sign-in is already on for this account', 'mfa_already_enabled');
524
+ this.name = 'MfaAlreadyEnabledError';
525
+ Object.setPrototypeOf(this, MfaAlreadyEnabledError.prototype);
526
+ }
527
+ }
528
+ exports.MfaAlreadyEnabledError = MfaAlreadyEnabledError;
529
+ class MfaEnrollmentNotFoundError extends KoolbaseAuthError {
530
+ constructor() {
531
+ super('No enrolment in progress; start again', 'mfa_enrollment_not_found');
532
+ this.name = 'MfaEnrollmentNotFoundError';
533
+ Object.setPrototypeOf(this, MfaEnrollmentNotFoundError.prototype);
534
+ }
535
+ }
536
+ exports.MfaEnrollmentNotFoundError = MfaEnrollmentNotFoundError;
537
+ class MfaNotEnabledError extends KoolbaseAuthError {
538
+ constructor() {
539
+ super('Two-step sign-in is not on for this account', 'mfa_not_enabled');
540
+ this.name = 'MfaNotEnabledError';
541
+ Object.setPrototypeOf(this, MfaNotEnabledError.prototype);
542
+ }
543
+ }
544
+ exports.MfaNotEnabledError = MfaNotEnabledError;
@@ -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/cjs/auth.js CHANGED
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.KoolbaseAuth = void 0;
4
+ const network_js_1 = require("./network.js");
4
5
  const types_js_1 = require("./types.js");
5
6
  const auth_errors_js_1 = require("./auth-errors.js");
6
7
  const platform_js_1 = require("./platform.js");
@@ -12,7 +13,7 @@ class KoolbaseAuth {
12
13
  this.listeners = new Set();
13
14
  this.config = config;
14
15
  this.metadata = new device_metadata_js_1.DeviceMetadata(config.appVersion);
15
- this.fetchFn = config.fetch ?? ((url, init) => fetch(url, init));
16
+ this.fetchFn = config.fetch ?? ((url, init) => (0, network_js_1.koolbaseFetch)(url, init));
16
17
  this.timeoutMs = config.authTimeout ?? 10000;
17
18
  if (config.authStorage) {
18
19
  this.storage = config.authStorage;
@@ -388,6 +389,8 @@ class KoolbaseAuth {
388
389
  const errorMessage = body?.error ?? '';
389
390
  // ─── code-first ───
390
391
  switch (code) {
392
+ case 'mfa_required':
393
+ throw this.mfaRequired(body);
391
394
  case 'oauth_not_configured':
392
395
  throw new auth_errors_js_1.GoogleSignInNotConfiguredError();
393
396
  case 'invalid_oauth_token':
@@ -442,6 +445,8 @@ class KoolbaseAuth {
442
445
  const errorMessage = body?.error ?? '';
443
446
  // ─── code-first ───
444
447
  switch (code) {
448
+ case 'mfa_required':
449
+ throw this.mfaRequired(body);
445
450
  case 'oauth_not_configured':
446
451
  throw new auth_errors_js_1.AppleSignInNotConfiguredError();
447
452
  case 'invalid_oauth_token':
@@ -873,6 +878,102 @@ class KoolbaseAuth {
873
878
  await this.setSessionInternal(session);
874
879
  return session;
875
880
  }
881
+ // ─── Two-step sign-in (MFA) ─────────────────────────────────────────────
882
+ //
883
+ // When an account has MFA on, every sign-in method throws MfaRequiredError.
884
+ // Finish with verifyMfa() or verifyRecoveryCode().
885
+ // One builder for every parser, so the four cannot drift apart in how the
886
+ // challenge reaches the app.
887
+ mfaRequired(body) {
888
+ const d = body?.details ?? {};
889
+ return new auth_errors_js_1.MfaRequiredError(String(d.challenge_token ?? ''), d.expires_at);
890
+ }
891
+ /** Finishes sign-in with a code from the person's authenticator app. */
892
+ async verifyMfa(params) {
893
+ const res = await this.authRequest('/v1/sdk/auth/mfa/verify', {
894
+ method: 'POST',
895
+ body: { challenge_token: params.challengeToken, code: params.code },
896
+ });
897
+ const session = await this.parseSessionResponse(res, false);
898
+ await this.setSessionInternal(session);
899
+ return session;
900
+ }
901
+ /**
902
+ * Finishes sign-in with a recovery code. Each works once; prompt the person
903
+ * to regenerate when recoveryCodesRemaining reaches zero.
904
+ */
905
+ async verifyRecoveryCode(params) {
906
+ const res = await this.authRequest('/v1/sdk/auth/mfa/verify-recovery', {
907
+ method: 'POST',
908
+ body: { challenge_token: params.challengeToken, code: params.code },
909
+ });
910
+ if (!res.ok)
911
+ await this.throwTypedError(res, false);
912
+ // Read once: the session and the remaining count come from one body.
913
+ const data = await res.json();
914
+ if (!data?.access_token)
915
+ throw new auth_errors_js_1.MalformedSessionResponseError('an access token');
916
+ if (!data?.refresh_token)
917
+ throw new auth_errors_js_1.MalformedSessionResponseError('a refresh token');
918
+ const session = {
919
+ accessToken: data.access_token,
920
+ refreshToken: data.refresh_token,
921
+ expiresAt: data.expires_at,
922
+ user: this.mapUser(data.user),
923
+ };
924
+ await this.setSessionInternal(session);
925
+ return { session, recoveryCodesRemaining: Number(data.recovery_codes_remaining ?? 0) };
926
+ }
927
+ /**
928
+ * Starts adding an authenticator; needs a sign-in within ten minutes. Show
929
+ * otpauthUri as a QR code, then call confirmMfaEnrollment with the first
930
+ * code the app shows.
931
+ */
932
+ async enrollMfa() {
933
+ const res = await this.authRequest('/v1/sdk/auth/mfa/enroll', { method: 'POST', includeAuth: true });
934
+ await this.checkResponse(res);
935
+ const d = await res.json();
936
+ return { otpauthUri: d.otpauth_uri, secret: d.secret };
937
+ }
938
+ /** Turns MFA on and returns ten recovery codes, shown this once only. Other devices are signed out. */
939
+ async confirmMfaEnrollment(code) {
940
+ const res = await this.authRequest('/v1/sdk/auth/mfa/enroll/confirm', {
941
+ method: 'POST',
942
+ body: { code },
943
+ includeAuth: true,
944
+ });
945
+ await this.checkResponse(res);
946
+ return (await res.json()).recovery_codes;
947
+ }
948
+ async mfaStatus() {
949
+ const res = await this.authRequest('/v1/sdk/auth/mfa', { method: 'GET', includeAuth: true });
950
+ await this.checkResponse(res);
951
+ const d = await res.json();
952
+ return { enabled: !!d.enabled, recoveryCodesRemaining: Number(d.recovery_codes_remaining ?? 0) };
953
+ }
954
+ /** Re-confirms the second factor, opening ten minutes to change MFA. */
955
+ async stepUpMfa(params) {
956
+ const res = await this.authRequest('/v1/sdk/auth/mfa/step-up', {
957
+ method: 'POST',
958
+ body: {
959
+ ...(params.code ? { code: params.code } : {}),
960
+ ...(params.recoveryCode ? { recovery_code: params.recoveryCode } : {}),
961
+ },
962
+ includeAuth: true,
963
+ });
964
+ await this.checkResponse(res);
965
+ }
966
+ /** Turns MFA off. Needs stepUpMfa() within the last ten minutes. */
967
+ async disableMfa() {
968
+ const res = await this.authRequest('/v1/sdk/auth/mfa/disable', { method: 'POST', includeAuth: true });
969
+ await this.checkResponse(res);
970
+ }
971
+ /** Replaces all recovery codes; the old ones stop working. Needs stepUpMfa() within ten minutes. */
972
+ async regenerateRecoveryCodes() {
973
+ const res = await this.authRequest('/v1/sdk/auth/mfa/recovery-codes', { method: 'POST', includeAuth: true });
974
+ await this.checkResponse(res);
975
+ return (await res.json()).recovery_codes;
976
+ }
876
977
  // ─── Phone OTP ──────────────────────────────────────────────────────────
877
978
  async sendOtp(params) {
878
979
  this.validatePhone(params.phoneNumber);
@@ -1040,6 +1141,18 @@ class KoolbaseAuth {
1040
1141
  const msg = body.error ?? '';
1041
1142
  // ─── code-first ───
1042
1143
  switch (code) {
1144
+ case 'mfa_required':
1145
+ throw this.mfaRequired(body);
1146
+ case 'recent_auth_required':
1147
+ throw new auth_errors_js_1.RecentAuthRequiredError();
1148
+ case 'recent_mfa_required':
1149
+ throw new auth_errors_js_1.RecentMfaRequiredError();
1150
+ case 'mfa_already_enabled':
1151
+ throw new auth_errors_js_1.MfaAlreadyEnabledError();
1152
+ case 'mfa_enrollment_not_found':
1153
+ throw new auth_errors_js_1.MfaEnrollmentNotFoundError();
1154
+ case 'mfa_not_enabled':
1155
+ throw new auth_errors_js_1.MfaNotEnabledError();
1043
1156
  case 'invalid_credentials':
1044
1157
  throw new auth_errors_js_1.InvalidCredentialsError();
1045
1158
  case 'email_in_use':
@@ -1160,6 +1273,8 @@ class KoolbaseAuth {
1160
1273
  const msg = body.error ?? '';
1161
1274
  // ─── code-first ───
1162
1275
  switch (code) {
1276
+ case 'mfa_required':
1277
+ throw this.mfaRequired(body);
1163
1278
  case 'invalid_phone':
1164
1279
  throw new auth_errors_js_1.InvalidPhoneNumberError();
1165
1280
  case 'otp_expired':
@@ -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
  *
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.KoolbaseDatabase = void 0;
4
+ const network_js_1 = require("./network.js");
4
5
  const errors_js_1 = require("./errors.js");
5
6
  const cache_store_js_1 = require("./cache-store.js");
6
7
  const offline_state_js_1 = require("./offline-state.js");
@@ -88,7 +89,7 @@ class KoolbaseDatabase {
88
89
  };
89
90
  }
90
91
  async request(method, path, body) {
91
- const res = await fetch(`${this.config.baseUrl}${path}`, {
92
+ const res = await (0, network_js_1.koolbaseFetch)(`${this.config.baseUrl}${path}`, {
92
93
  method,
93
94
  headers: await this.buildHeaders(),
94
95
  body: body ? JSON.stringify(body) : undefined,
@@ -126,7 +127,7 @@ class KoolbaseDatabase {
126
127
  * none of them clearing a rejected session. One path, two shapes of result.
127
128
  */
128
129
  async requestWithStatus(method, path, body) {
129
- const res = await fetch(`${this.config.baseUrl}${path}`, {
130
+ const res = await (0, network_js_1.koolbaseFetch)(`${this.config.baseUrl}${path}`, {
130
131
  method,
131
132
  headers: await this.buildHeaders(),
132
133
  body: body ? JSON.stringify(body) : undefined,
@@ -197,20 +198,31 @@ class KoolbaseDatabase {
197
198
  */
198
199
  async query(collection, options = {}) {
199
200
  const userId = this.getUserId() ?? 'anonymous';
200
- const queryHash = (0, cache_store_js_2.hashQuery)(collection, options);
201
- const cached = await (0, cache_store_js_2.getCached)(userId, collection, queryHash);
201
+ // The cache policy is how to read, not what to read: it stays out of the
202
+ // query's identity, so both policies share one cache entry.
203
+ const { cache: cachePolicy, ...queryOptions } = options;
204
+ const queryHash = (0, cache_store_js_2.hashQuery)(collection, queryOptions);
205
+ const cached = cachePolicy === 'network-only' ? null : await (0, cache_store_js_2.getCached)(userId, collection, queryHash);
202
206
  if (cached) {
203
- this.runQuery(collection, options)
207
+ this.runQuery(collection, queryOptions)
204
208
  .then(result => (0, cache_store_js_2.setCached)(userId, collection, queryHash, result))
205
209
  .catch(() => {
206
210
  // Network unavailable — cached data already returned
207
211
  });
208
212
  return { ...cached, isFromCache: true };
209
213
  }
210
- const result = await this.runQuery(collection, options);
214
+ const result = await this.runQuery(collection, queryOptions);
211
215
  await (0, cache_store_js_2.setCached)(userId, collection, queryHash, result);
212
216
  return { ...result, isFromCache: false };
213
217
  }
218
+ /**
219
+ * Forget this client's cached query results for a collection, so the next
220
+ * `query` waits for the server. For data changed elsewhere: by a Function,
221
+ * a server, or another user. The SDK's own writes already do this.
222
+ */
223
+ async invalidate(collection) {
224
+ await (0, cache_store_js_2.invalidateCache)(this.getUserId() ?? 'anonymous', collection);
225
+ }
214
226
  // ─── Insert (online-first with offline fallback) ───────────────────────────
215
227
  /**
216
228
  * Insert a new record into a collection.
@@ -4,6 +4,7 @@ exports.KoolbaseFiscal = exports.KoolbaseFiscalError = void 0;
4
4
  exports.isFiscalized = isFiscalized;
5
5
  exports.isPending = isPending;
6
6
  exports.decodeFiscalResponse = decodeFiscalResponse;
7
+ const network_js_1 = require("./network.js");
7
8
  const errors_js_1 = require("./errors.js");
8
9
  const KNOWN_STATUSES = [
9
10
  'created', 'blocked', 'queued', 'submitting',
@@ -112,7 +113,7 @@ class KoolbaseFiscal {
112
113
  const controller = new AbortController();
113
114
  const timer = setTimeout(() => controller.abort(), params.timeoutMs ?? 30000);
114
115
  try {
115
- const res = await fetch(`${this.config.baseUrl}/v1/sdk/fiscal/submit`, {
116
+ const res = await (0, network_js_1.koolbaseFetch)(`${this.config.baseUrl}/v1/sdk/fiscal/submit`, {
116
117
  method: 'POST',
117
118
  headers: await this.buildHeaders(),
118
119
  signal: controller.signal,
@@ -150,7 +151,7 @@ class KoolbaseFiscal {
150
151
  device_id: params.deviceId,
151
152
  client_ref: params.clientRef,
152
153
  });
153
- const res = await fetch(`${this.config.baseUrl}/v1/sdk/fiscal/status?${q.toString()}`, { method: 'GET', headers: await this.buildHeaders(), signal: controller.signal });
154
+ const res = await (0, network_js_1.koolbaseFetch)(`${this.config.baseUrl}/v1/sdk/fiscal/status?${q.toString()}`, { method: 'GET', headers: await this.buildHeaders(), signal: controller.signal });
154
155
  return await this.decode(res);
155
156
  }
156
157
  catch (e) {
@@ -4,7 +4,7 @@ export declare class KoolbaseFlags {
4
4
  private payload;
5
5
  private deviceId;
6
6
  constructor(config: KoolbaseConfig, deviceId: string);
7
- fetch(appVersion: string, platform: string): Promise<void>;
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/cjs/flags.js CHANGED
@@ -1,15 +1,16 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.KoolbaseFlags = void 0;
4
+ const network_js_1 = require("./network.js");
4
5
  class KoolbaseFlags {
5
6
  constructor(config, deviceId) {
6
7
  this.payload = null;
7
8
  this.config = config;
8
9
  this.deviceId = deviceId;
9
10
  }
10
- async fetch(appVersion, platform) {
11
+ async koolbaseFetch(appVersion, platform) {
11
12
  try {
12
- const res = await fetch(`${this.config.baseUrl}/v1/bootstrap?public_key=${this.config.publicKey}&device_id=${this.deviceId}&app_version=${appVersion}&platform=${platform}`);
13
+ const res = await (0, network_js_1.koolbaseFetch)(`${this.config.baseUrl}/v1/bootstrap?public_key=${this.config.publicKey}&device_id=${this.deviceId}&app_version=${appVersion}&platform=${platform}`);
13
14
  if (res.ok) {
14
15
  this.payload = await res.json();
15
16
  }
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.KoolbaseFunctions = void 0;
4
+ const network_js_1 = require("./network.js");
4
5
  const function_errors_js_1 = require("./function-errors.js");
5
6
  const errors_js_1 = require("./errors.js");
6
7
  const types_js_1 = require("./types.js");
@@ -13,7 +14,7 @@ class KoolbaseFunctions {
13
14
  // ─── Deploy ────────────────────────────────────────────────────────────────
14
15
  async deploy(options) {
15
16
  const runtime = options.runtime ?? types_js_1.FunctionRuntime.Deno;
16
- const res = await fetch(`${this.config.baseUrl}/v1/sdk/functions/deploy`, {
17
+ const res = await (0, network_js_1.koolbaseFetch)(`${this.config.baseUrl}/v1/sdk/functions/deploy`, {
17
18
  method: 'POST',
18
19
  headers: {
19
20
  'Content-Type': 'application/json',
@@ -57,7 +58,7 @@ class KoolbaseFunctions {
57
58
  if (userToken) {
58
59
  headers['Authorization'] = `Bearer ${userToken}`;
59
60
  }
60
- const res = await fetch(`${this.config.baseUrl}/v1/sdk/functions/${name}`, {
61
+ const res = await (0, network_js_1.koolbaseFetch)(`${this.config.baseUrl}/v1/sdk/functions/${name}`, {
61
62
  method: 'POST',
62
63
  headers,
63
64
  body: JSON.stringify({ body: body ?? {} }),
@@ -1,5 +1,6 @@
1
1
  export * from './types.js';
2
2
  export * from './errors.js';
3
+ export * from './network.js';
3
4
  export * from './aggregate.js';
4
5
  export * from './conflict.js';
5
6
  export * from './pending-write.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.isPending = exports.isFiscalized = exports.decodeFiscalResponse = exports.KoolbaseFiscalError = exports.KoolbaseFiscal = 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("./network.js"), exports);
25
26
  __exportStar(require("./aggregate.js"), exports);
26
27
  __exportStar(require("./conflict.js"), exports);
27
28
  __exportStar(require("./pending-write.js"), exports);
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.KoolbaseMessaging = void 0;
4
+ const network_js_1 = require("./network.js");
4
5
  // ─── KoolbaseMessaging ────────────────────────────────────────────────────────
5
6
  class KoolbaseMessaging {
6
7
  constructor(config) {
@@ -13,7 +14,7 @@ class KoolbaseMessaging {
13
14
  // ─── Register token ───────────────────────────────────────────────────────
14
15
  async registerToken(options) {
15
16
  try {
16
- const response = await fetch(`${this.config.baseUrl}/v1/messaging/register`, {
17
+ const response = await (0, network_js_1.koolbaseFetch)(`${this.config.baseUrl}/v1/messaging/register`, {
17
18
  method: 'POST',
18
19
  headers: {
19
20
  'Content-Type': 'application/json',
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Network failures, made actionable.
3
+ *
4
+ * A browser rejects a request it may not make cross-origin with the same bare
5
+ * TypeError ("Failed to fetch") as a dead network, and deliberately hides which
6
+ * it was. The SDK cannot tell them apart either, so it names the likely causes,
7
+ * starting with the one only the app's developer can fix: Trusted Origins.
8
+ *
9
+ * It stays a TypeError, so code that tells a network failure from a server
10
+ * rejection that way (the offline queue) behaves exactly as before.
11
+ */
12
+ export declare class KoolbaseNetworkError extends TypeError {
13
+ code: string;
14
+ readonly url: string;
15
+ readonly origin?: string;
16
+ constructor(url: string, cause: unknown);
17
+ }
18
+ /** fetch, with network failures turned into a KoolbaseNetworkError. */
19
+ export declare function koolbaseFetch(input: string, init?: RequestInit): Promise<Response>;
@@ -0,0 +1,52 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.KoolbaseNetworkError = void 0;
4
+ exports.koolbaseFetch = koolbaseFetch;
5
+ /**
6
+ * Network failures, made actionable.
7
+ *
8
+ * A browser rejects a request it may not make cross-origin with the same bare
9
+ * TypeError ("Failed to fetch") as a dead network, and deliberately hides which
10
+ * it was. The SDK cannot tell them apart either, so it names the likely causes,
11
+ * starting with the one only the app's developer can fix: Trusted Origins.
12
+ *
13
+ * It stays a TypeError, so code that tells a network failure from a server
14
+ * rejection that way (the offline queue) behaves exactly as before.
15
+ */
16
+ class KoolbaseNetworkError extends TypeError {
17
+ constructor(url, cause) {
18
+ const loc = globalThis.location;
19
+ const origin = loc?.origin && loc.origin !== 'null' ? loc.origin : undefined;
20
+ const reason = cause instanceof Error ? cause.message : String(cause);
21
+ let host = url;
22
+ try {
23
+ host = new URL(url).origin;
24
+ }
25
+ catch {
26
+ // keep the raw url
27
+ }
28
+ let message = `Could not reach Koolbase at ${host} (${reason}).`;
29
+ message += origin
30
+ ? ` If you are online, check that this site's address (${origin}) is in the project's Trusted Origins in the Koolbase dashboard.`
31
+ : ' Check the network connection and the configured base URL.';
32
+ super(message);
33
+ this.code = 'network_error';
34
+ this.name = 'KoolbaseNetworkError';
35
+ this.url = url;
36
+ this.origin = origin;
37
+ Object.setPrototypeOf(this, new.target.prototype);
38
+ }
39
+ }
40
+ exports.KoolbaseNetworkError = KoolbaseNetworkError;
41
+ /** fetch, with network failures turned into a KoolbaseNetworkError. */
42
+ async function koolbaseFetch(input, init) {
43
+ try {
44
+ return await fetch(input, init);
45
+ }
46
+ catch (e) {
47
+ // A request the caller cancelled keeps its meaning.
48
+ if (e?.name === 'AbortError')
49
+ throw e;
50
+ throw new KoolbaseNetworkError(input, e);
51
+ }
52
+ }