@koolbase/core 12.2.0 → 12.4.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.
@@ -36,3 +36,9 @@ export declare class KoolbaseAnalytics {
36
36
  flush(): Promise<void>;
37
37
  dispose(): Promise<void>;
38
38
  }
39
+ /**
40
+ * What Koolbase.analytics is while analytics is off (the default since 12.4.0).
41
+ * Every call does nothing, and the first says once how to turn it on, so an app
42
+ * that tracks events without opting in keeps working instead of crashing.
43
+ */
44
+ export declare function disabledAnalytics(): KoolbaseAnalytics;
@@ -1,6 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.KoolbaseAnalytics = void 0;
4
+ exports.disabledAnalytics = disabledAnalytics;
5
+ const network_js_1 = require("./network.js");
4
6
  const platform_js_1 = require("./platform.js");
5
7
  const device_id_js_1 = require("./device-id.js");
6
8
  // ─── KoolbaseAnalytics ───────────────────────────────────────────────────────
@@ -102,7 +104,7 @@ class KoolbaseAnalytics {
102
104
  const batch = [...this.queue];
103
105
  this.queue = [];
104
106
  try {
105
- const response = await fetch(`${this.config.baseUrl}/v1/analytics/events`, {
107
+ const response = await (0, network_js_1.koolbaseFetch)(`${this.config.baseUrl}/v1/analytics/events`, {
106
108
  method: 'POST',
107
109
  headers: {
108
110
  'Content-Type': 'application/json',
@@ -128,3 +130,20 @@ class KoolbaseAnalytics {
128
130
  }
129
131
  }
130
132
  exports.KoolbaseAnalytics = KoolbaseAnalytics;
133
+ /**
134
+ * What Koolbase.analytics is while analytics is off (the default since 12.4.0).
135
+ * Every call does nothing, and the first says once how to turn it on, so an app
136
+ * that tracks events without opting in keeps working instead of crashing.
137
+ */
138
+ function disabledAnalytics() {
139
+ let told = false;
140
+ const noop = () => {
141
+ if (!told) {
142
+ told = true;
143
+ // eslint-disable-next-line no-console
144
+ console.info('[Koolbase] Analytics is off: it is opt-in since 12.4.0. Pass analyticsEnabled: true to Koolbase.initialize to send events.');
145
+ }
146
+ return Promise.resolve();
147
+ };
148
+ return new Proxy({}, { get: (_t, prop) => (prop === 'then' ? undefined : noop) });
149
+ }
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;
@@ -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';
@@ -13,7 +14,7 @@ export { KoolbaseFlags } from './flags.js';
13
14
  export { KoolbaseFunctions } from './functions.js';
14
15
  export { KoolbaseRealtime } from './realtime.js';
15
16
  export { KoolbaseStorage } from './storage.js';
16
- export { KoolbaseAnalytics } from './analytics.js';
17
+ export { KoolbaseAnalytics, disabledAnalytics } from './analytics.js';
17
18
  export { KoolbaseFiscal, KoolbaseFiscalError, decodeFiscalResponse, isFiscalized, isPending } from './fiscal.js';
18
19
  export type { FiscalIntentResult, FiscalStatus } from './fiscal.js';
19
20
  export { KoolbaseMessaging } from './messaging.js';
package/dist/cjs/index.js CHANGED
@@ -19,9 +19,10 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
19
19
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
20
20
  };
21
21
  Object.defineProperty(exports, "__esModule", { value: true });
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;
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.disabledAnalytics = 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);
@@ -43,6 +44,7 @@ var storage_js_1 = require("./storage.js");
43
44
  Object.defineProperty(exports, "KoolbaseStorage", { enumerable: true, get: function () { return storage_js_1.KoolbaseStorage; } });
44
45
  var analytics_js_1 = require("./analytics.js");
45
46
  Object.defineProperty(exports, "KoolbaseAnalytics", { enumerable: true, get: function () { return analytics_js_1.KoolbaseAnalytics; } });
47
+ Object.defineProperty(exports, "disabledAnalytics", { enumerable: true, get: function () { return analytics_js_1.disabledAnalytics; } });
46
48
  var fiscal_js_1 = require("./fiscal.js");
47
49
  Object.defineProperty(exports, "KoolbaseFiscal", { enumerable: true, get: function () { return fiscal_js_1.KoolbaseFiscal; } });
48
50
  Object.defineProperty(exports, "KoolbaseFiscalError", { enumerable: true, get: function () { return fiscal_js_1.KoolbaseFiscalError; } });
@@ -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,21 @@
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
+ /** A short message that is safe to show to people; `message` is for developers. */
15
+ readonly userMessage = "We can't connect right now. Check your connection and try again.";
16
+ readonly url: string;
17
+ readonly origin?: string;
18
+ constructor(url: string, cause: unknown);
19
+ }
20
+ /** fetch, with network failures turned into a KoolbaseNetworkError. */
21
+ export declare function koolbaseFetch(input: string, init?: RequestInit): Promise<Response>;
@@ -0,0 +1,54 @@
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
+ /** A short message that is safe to show to people; `message` is for developers. */
35
+ this.userMessage = "We can't connect right now. Check your connection and try again.";
36
+ this.name = 'KoolbaseNetworkError';
37
+ this.url = url;
38
+ this.origin = origin;
39
+ Object.setPrototypeOf(this, new.target.prototype);
40
+ }
41
+ }
42
+ exports.KoolbaseNetworkError = KoolbaseNetworkError;
43
+ /** fetch, with network failures turned into a KoolbaseNetworkError. */
44
+ async function koolbaseFetch(input, init) {
45
+ try {
46
+ return await fetch(input, init);
47
+ }
48
+ catch (e) {
49
+ // A request the caller cancelled keeps its meaning.
50
+ if (e?.name === 'AbortError')
51
+ throw e;
52
+ throw new KoolbaseNetworkError(input, e);
53
+ }
54
+ }
@@ -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 fetch(`${this.config.baseUrl}/v1/sdk/storage/upload-url`, {
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 fetch(options.file.uri)).blob();
127
+ : await (await (0, network_js_1.koolbaseFetch)(options.file.uri)).blob();
127
128
  const fileSize = fileBlob.size;
128
- const uploadRes = await fetch(upload_url, {
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 fetch(`${this.config.baseUrl}/v1/sdk/storage/confirm`, {
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 fetch(`${this.config.baseUrl}/v1/sdk/storage/objects/metadata`, {
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 fetch(url, { headers: await this.buildHeaders() });
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 fetch(url, {
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 fetch(url, { headers: await this.buildHeaders() });
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 fetch(url, { headers: await this.buildHeaders() });
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 fetch(url, {
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 fetch(url, {
405
+ const res = await (0, network_js_1.koolbaseFetch)(url, {
405
406
  method: 'DELETE',
406
407
  headers: await this.buildHeaders(),
407
408
  });
@@ -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 fetch(url, {
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 fetch(url, {
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 fetch(`${url}${q}`, { method: 'DELETE', headers });
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');
@@ -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[];
@@ -36,3 +36,9 @@ export declare class KoolbaseAnalytics {
36
36
  flush(): Promise<void>;
37
37
  dispose(): Promise<void>;
38
38
  }
39
+ /**
40
+ * What Koolbase.analytics is while analytics is off (the default since 12.4.0).
41
+ * Every call does nothing, and the first says once how to turn it on, so an app
42
+ * that tracks events without opting in keeps working instead of crashing.
43
+ */
44
+ export declare function disabledAnalytics(): KoolbaseAnalytics;
@@ -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 fetch(`${this.config.baseUrl}/v1/analytics/events`, {
103
+ const response = await koolbaseFetch(`${this.config.baseUrl}/v1/analytics/events`, {
103
104
  method: 'POST',
104
105
  headers: {
105
106
  'Content-Type': 'application/json',
@@ -124,3 +125,20 @@ export class KoolbaseAnalytics {
124
125
  await this.flush();
125
126
  }
126
127
  }
128
+ /**
129
+ * What Koolbase.analytics is while analytics is off (the default since 12.4.0).
130
+ * Every call does nothing, and the first says once how to turn it on, so an app
131
+ * that tracks events without opting in keeps working instead of crashing.
132
+ */
133
+ export function disabledAnalytics() {
134
+ let told = false;
135
+ const noop = () => {
136
+ if (!told) {
137
+ told = true;
138
+ // eslint-disable-next-line no-console
139
+ console.info('[Koolbase] Analytics is off: it is opt-in since 12.4.0. Pass analyticsEnabled: true to Koolbase.initialize to send events.');
140
+ }
141
+ return Promise.resolve();
142
+ };
143
+ return new Proxy({}, { get: (_t, prop) => (prop === 'then' ? undefined : noop) });
144
+ }
package/dist/esm/auth.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { koolbaseFetch } from './network.js';
1
2
  import { RestoreResult, } from './types.js';
2
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';
@@ -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) => 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;
@@ -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,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 fetch(`${this.config.baseUrl}${path}`, {
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 fetch(`${this.config.baseUrl}${path}`, {
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
- const queryHash = hashQuery(collection, options);
198
- const cached = await getCached(userId, collection, queryHash);
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, options)
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, options);
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.
@@ -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 fetch(`${this.config.baseUrl}/v1/sdk/fiscal/submit`, {
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 fetch(`${this.config.baseUrl}/v1/sdk/fiscal/status?${q.toString()}`, { method: 'GET', headers: await this.buildHeaders(), signal: controller.signal });
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) {
@@ -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/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 fetch(appVersion, platform) {
8
+ async koolbaseFetch(appVersion, platform) {
8
9
  try {
9
- const res = await fetch(`${this.config.baseUrl}/v1/bootstrap?public_key=${this.config.publicKey}&device_id=${this.deviceId}&app_version=${appVersion}&platform=${platform}`);
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
  }
@@ -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 fetch(`${this.config.baseUrl}/v1/sdk/functions/deploy`, {
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 fetch(`${this.config.baseUrl}/v1/sdk/functions/${name}`, {
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 ?? {} }),
@@ -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';
@@ -13,7 +14,7 @@ export { KoolbaseFlags } from './flags.js';
13
14
  export { KoolbaseFunctions } from './functions.js';
14
15
  export { KoolbaseRealtime } from './realtime.js';
15
16
  export { KoolbaseStorage } from './storage.js';
16
- export { KoolbaseAnalytics } from './analytics.js';
17
+ export { KoolbaseAnalytics, disabledAnalytics } from './analytics.js';
17
18
  export { KoolbaseFiscal, KoolbaseFiscalError, decodeFiscalResponse, isFiscalized, isPending } from './fiscal.js';
18
19
  export type { FiscalIntentResult, FiscalStatus } from './fiscal.js';
19
20
  export { KoolbaseMessaging } from './messaging.js';
package/dist/esm/index.js CHANGED
@@ -5,6 +5,7 @@
5
5
  // code installs the platform package, never this one directly.
6
6
  export * from './types.js';
7
7
  export * from './errors.js';
8
+ export * from './network.js';
8
9
  export * from './aggregate.js';
9
10
  export * from './conflict.js';
10
11
  export * from './pending-write.js';
@@ -18,7 +19,7 @@ export { KoolbaseFlags } from './flags.js';
18
19
  export { KoolbaseFunctions } from './functions.js';
19
20
  export { KoolbaseRealtime } from './realtime.js';
20
21
  export { KoolbaseStorage } from './storage.js';
21
- export { KoolbaseAnalytics } from './analytics.js';
22
+ export { KoolbaseAnalytics, disabledAnalytics } from './analytics.js';
22
23
  export { KoolbaseFiscal, KoolbaseFiscalError, decodeFiscalResponse, isFiscalized, isPending } from './fiscal.js';
23
24
  export { KoolbaseMessaging } from './messaging.js';
24
25
  export { getOrCreateDeviceId } from './device-id.js';
@@ -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 fetch(`${this.config.baseUrl}/v1/messaging/register`, {
14
+ const response = await koolbaseFetch(`${this.config.baseUrl}/v1/messaging/register`, {
14
15
  method: 'POST',
15
16
  headers: {
16
17
  'Content-Type': 'application/json',
@@ -0,0 +1,21 @@
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
+ /** A short message that is safe to show to people; `message` is for developers. */
15
+ readonly userMessage = "We can't connect right now. Check your connection and try again.";
16
+ readonly url: string;
17
+ readonly origin?: string;
18
+ constructor(url: string, cause: unknown);
19
+ }
20
+ /** fetch, with network failures turned into a KoolbaseNetworkError. */
21
+ export declare function koolbaseFetch(input: string, init?: RequestInit): Promise<Response>;
@@ -0,0 +1,49 @@
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 class KoolbaseNetworkError extends TypeError {
13
+ constructor(url, cause) {
14
+ const loc = globalThis.location;
15
+ const origin = loc?.origin && loc.origin !== 'null' ? loc.origin : undefined;
16
+ const reason = cause instanceof Error ? cause.message : String(cause);
17
+ let host = url;
18
+ try {
19
+ host = new URL(url).origin;
20
+ }
21
+ catch {
22
+ // keep the raw url
23
+ }
24
+ let message = `Could not reach Koolbase at ${host} (${reason}).`;
25
+ message += origin
26
+ ? ` If you are online, check that this site's address (${origin}) is in the project's Trusted Origins in the Koolbase dashboard.`
27
+ : ' Check the network connection and the configured base URL.';
28
+ super(message);
29
+ this.code = 'network_error';
30
+ /** A short message that is safe to show to people; `message` is for developers. */
31
+ this.userMessage = "We can't connect right now. Check your connection and try again.";
32
+ this.name = 'KoolbaseNetworkError';
33
+ this.url = url;
34
+ this.origin = origin;
35
+ Object.setPrototypeOf(this, new.target.prototype);
36
+ }
37
+ }
38
+ /** fetch, with network failures turned into a KoolbaseNetworkError. */
39
+ export async function koolbaseFetch(input, init) {
40
+ try {
41
+ return await fetch(input, init);
42
+ }
43
+ catch (e) {
44
+ // A request the caller cancelled keeps its meaning.
45
+ if (e?.name === 'AbortError')
46
+ throw e;
47
+ throw new KoolbaseNetworkError(input, e);
48
+ }
49
+ }
@@ -1,3 +1,4 @@
1
+ import { koolbaseFetch } from './network.js';
1
2
  import { KoolbaseUnauthenticatedError } from './errors.js';
2
3
  import { KoolbaseStorageError, koolbaseStorageErrorFromResponse, } from './storage-errors.js';
3
4
  // --- Cloudflare image-transform URL helpers -------------------------------
@@ -94,7 +95,7 @@ export class KoolbaseStorage {
94
95
  ? options.file.type || 'application/octet-stream'
95
96
  : options.file.type;
96
97
  // ─── Step 1: Get presigned upload URL ───
97
- const urlRes = await fetch(`${this.config.baseUrl}/v1/sdk/storage/upload-url`, {
98
+ const urlRes = await koolbaseFetch(`${this.config.baseUrl}/v1/sdk/storage/upload-url`, {
98
99
  method: 'POST',
99
100
  headers: {
100
101
  ...(await this.buildHeaders()),
@@ -120,9 +121,9 @@ export class KoolbaseStorage {
120
121
  // the two hosts differ here and nowhere else in this method.
121
122
  const fileBlob = isBlob
122
123
  ? options.file
123
- : await (await fetch(options.file.uri)).blob();
124
+ : await (await koolbaseFetch(options.file.uri)).blob();
124
125
  const fileSize = fileBlob.size;
125
- const uploadRes = await fetch(upload_url, {
126
+ const uploadRes = await koolbaseFetch(upload_url, {
126
127
  method: 'PUT',
127
128
  headers: { 'Content-Type': contentType },
128
129
  body: fileBlob,
@@ -149,7 +150,7 @@ export class KoolbaseStorage {
149
150
  if (options.metadata !== undefined) {
150
151
  confirmBody.metadata = options.metadata;
151
152
  }
152
- const confirmRes = await fetch(`${this.config.baseUrl}/v1/sdk/storage/confirm`, {
153
+ const confirmRes = await koolbaseFetch(`${this.config.baseUrl}/v1/sdk/storage/confirm`, {
153
154
  method: 'POST',
154
155
  headers: {
155
156
  ...(await this.buildHeaders()),
@@ -200,7 +201,7 @@ export class KoolbaseStorage {
200
201
  * // -> { category: 'landscape', tag: 'sunset' }
201
202
  */
202
203
  async updateMetadata(bucket, path, metadata) {
203
- const res = await fetch(`${this.config.baseUrl}/v1/sdk/storage/objects/metadata`, {
204
+ const res = await koolbaseFetch(`${this.config.baseUrl}/v1/sdk/storage/objects/metadata`, {
204
205
  method: 'PATCH',
205
206
  headers: {
206
207
  ...(await this.buildHeaders()),
@@ -223,7 +224,7 @@ export class KoolbaseStorage {
223
224
  if (versionId) {
224
225
  url += `&version_id=${encodeURIComponent(versionId)}`;
225
226
  }
226
- const res = await fetch(url, { headers: await this.buildHeaders() });
227
+ const res = await koolbaseFetch(url, { headers: await this.buildHeaders() });
227
228
  if (!res.ok) {
228
229
  throw await this.error(res, 'Failed to get download URL');
229
230
  }
@@ -318,7 +319,7 @@ export class KoolbaseStorage {
318
319
  const url = forcePurge
319
320
  ? `${this.config.baseUrl}/v1/sdk/storage/object?force_purge=true`
320
321
  : `${this.config.baseUrl}/v1/sdk/storage/object`;
321
- const res = await fetch(url, {
322
+ const res = await koolbaseFetch(url, {
322
323
  method: 'DELETE',
323
324
  headers: {
324
325
  ...(await this.buildHeaders()),
@@ -345,7 +346,7 @@ export class KoolbaseStorage {
345
346
  async listVersions(bucket, path) {
346
347
  const url = `${this.config.baseUrl}/v1/sdk/storage/object-versions` +
347
348
  `?bucket=${encodeURIComponent(bucket)}&path=${encodeURIComponent(path)}`;
348
- const res = await fetch(url, { headers: await this.buildHeaders() });
349
+ const res = await koolbaseFetch(url, { headers: await this.buildHeaders() });
349
350
  if (!res.ok) {
350
351
  throw await this.error(res, 'Failed to list versions');
351
352
  }
@@ -361,7 +362,7 @@ export class KoolbaseStorage {
361
362
  async getVersion(bucket, path, versionId) {
362
363
  const url = `${this.config.baseUrl}/v1/sdk/storage/object-versions/${encodeURIComponent(versionId)}` +
363
364
  `?bucket=${encodeURIComponent(bucket)}&path=${encodeURIComponent(path)}`;
364
- const res = await fetch(url, { headers: await this.buildHeaders() });
365
+ const res = await koolbaseFetch(url, { headers: await this.buildHeaders() });
365
366
  if (!res.ok) {
366
367
  throw await this.error(res, 'Failed to fetch version');
367
368
  }
@@ -380,7 +381,7 @@ export class KoolbaseStorage {
380
381
  async restoreVersion(bucket, path, versionId) {
381
382
  const url = `${this.config.baseUrl}/v1/sdk/storage/object-versions/${encodeURIComponent(versionId)}/restore` +
382
383
  `?bucket=${encodeURIComponent(bucket)}&path=${encodeURIComponent(path)}`;
383
- const res = await fetch(url, {
384
+ const res = await koolbaseFetch(url, {
384
385
  method: 'POST',
385
386
  headers: await this.buildHeaders(),
386
387
  });
@@ -398,7 +399,7 @@ export class KoolbaseStorage {
398
399
  async purgeVersion(bucket, path, versionId) {
399
400
  const url = `${this.config.baseUrl}/v1/sdk/storage/object-versions/${encodeURIComponent(versionId)}` +
400
401
  `?bucket=${encodeURIComponent(bucket)}&path=${encodeURIComponent(path)}`;
401
- const res = await fetch(url, {
402
+ const res = await koolbaseFetch(url, {
402
403
  method: 'DELETE',
403
404
  headers: await this.buildHeaders(),
404
405
  });
@@ -1,3 +1,4 @@
1
+ import { koolbaseFetch } from './network.js';
1
2
  import { readOfflineState, mutateOfflineState, migrateLegacyQueue, flushLockName, } from './offline-state.js';
2
3
  import { KoolbaseUnauthenticatedError } from './errors.js';
3
4
  import { getPlatform } from './platform.js';
@@ -242,7 +243,7 @@ export class SyncEngine {
242
243
  : `${this.config.baseUrl}/v1/sdk/db/records/${write.recordId}`;
243
244
  let res;
244
245
  if (write.operation === 'insert') {
245
- res = await fetch(url, {
246
+ res = await koolbaseFetch(url, {
246
247
  method: 'POST',
247
248
  headers,
248
249
  // The write's own id: generated at enqueue, identical on every retry.
@@ -256,7 +257,7 @@ export class SyncEngine {
256
257
  });
257
258
  }
258
259
  else if (write.operation === 'update') {
259
- res = await fetch(url, {
260
+ res = await koolbaseFetch(url, {
260
261
  method: 'PATCH',
261
262
  headers,
262
263
  // The revision the change was composed against. The server applies it
@@ -275,7 +276,7 @@ export class SyncEngine {
275
276
  const q = write.baseRevision !== undefined
276
277
  ? `?expected_revision=${write.baseRevision}`
277
278
  : '';
278
- res = await fetch(`${url}${q}`, { method: 'DELETE', headers });
279
+ res = await koolbaseFetch(`${url}${q}`, { method: 'DELETE', headers });
279
280
  }
280
281
  if (res.status === 401)
281
282
  throw new KoolbaseUnauthenticatedError('unauthorized');
@@ -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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@koolbase/core",
3
- "version": "12.2.0",
3
+ "version": "12.4.0",
4
4
  "description": "Koolbase SDK core \u2014 shared behaviour behind @koolbase/react-native and @koolbase/js. Install one of those, not this.",
5
5
  "main": "./dist/cjs/index.js",
6
6
  "types": "./dist/esm/index.d.ts",