@techfinityedge/koolbase-react-native 3.0.0 → 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -16,9 +16,13 @@ Auth, database, storage, realtime, functions, feature flags, remote config, vers
16
16
  3. Add the SDK:
17
17
 
18
18
  ```bash
19
- npm install @techfinityedge/koolbase-react-native@^3.0.0
19
+ npm install @techfinityedge/koolbase-react-native
20
20
  # or
21
- yarn add @techfinityedge/koolbase-react-native@^3.0.0
21
+ yarn add @techfinityedge/koolbase-react-native
22
+ # or
23
+ pnpm add @techfinityedge/koolbase-react-native
24
+ # or
25
+ bun add @techfinityedge/koolbase-react-native
22
26
  ```
23
27
 
24
28
  **4. Initialize at app startup:**
@@ -215,6 +219,8 @@ const deleted = await Koolbase.db.deleteWhere('sessions', {
215
219
  > A non-empty filter is required. The collection's delete rule applies; for
216
220
  > `owner`/`scoped` rules the delete is scoped to your own records. Online-only.
217
221
 
222
+ ---
223
+
218
224
  ### Offline-first
219
225
 
220
226
  ```typescript
@@ -224,6 +230,31 @@ if (isFromCache) console.log('Served from local cache');
224
230
  await Koolbase.db.syncPendingWrites();
225
231
  ```
226
232
 
233
+ ### Atomic batch writes
234
+
235
+ Run multiple writes in a single server-side transaction. All operations commit together or none are applied — any failure rolls back the entire batch.
236
+
237
+ ```ts
238
+ import { Koolbase, BatchOp } from '@techfinityedge/koolbase-react-native';
239
+
240
+ const results = await Koolbase.db.batch([
241
+ BatchOp.insert('orders', { total: 50, customer_id: customerId }),
242
+ BatchOp.update(inventoryId, { stock: 9 }),
243
+ BatchOp.upsert('counters', {
244
+ match: { name: 'orders' },
245
+ data: { value: 1 },
246
+ }),
247
+ BatchOp.delete(cartItemId),
248
+ ]);
249
+
250
+ // results[i] corresponds to operations[i]:
251
+ // - insert / update: { type, record }
252
+ // - upsert: { type, record, created } // created = true if inserted
253
+ // - delete: { type, deleted: true }
254
+ ```
255
+
256
+ **Online-only by design.** Atomicity needs the server's authoritative view, so `batch()` is never queued offline — it throws on network failure (like `upsert` and `deleteWhere`). A server-side rejection throws a `KoolbaseDataException` with the failing operation's details; nothing was persisted.
257
+
227
258
  ---
228
259
 
229
260
  ## Storage
@@ -1,4 +1,4 @@
1
- import { KoolbaseConfig, KoolbaseRecord, QueryOptions, QueryResult, UpsertResult } from './types';
1
+ import { KoolbaseConfig, KoolbaseRecord, QueryOptions, QueryResult, UpsertResult, BatchOp, BatchResult } from './types';
2
2
  export declare class KoolbaseDatabase {
3
3
  private config;
4
4
  private getUserId;
@@ -38,6 +38,30 @@ export declare class KoolbaseDatabase {
38
38
  * The collection cache is invalidated on success.
39
39
  */
40
40
  deleteWhere(collection: string, filters: Record<string, unknown>): Promise<number>;
41
+ /**
42
+ * Run multiple writes as a single atomic transaction.
43
+ *
44
+ * All `operations` commit together or none are applied — the server runs
45
+ * them in one database transaction and rolls back entirely on any failure.
46
+ * Operations apply in order and may span multiple collections.
47
+ *
48
+ * Online-only by design (like `upsert` and `deleteWhere`): atomicity needs
49
+ * the server's authoritative view, so a batch is never queued offline — it
50
+ * throws on network failure. A server-side rejection throws a
51
+ * `KoolbaseDataException` whose message identifies which operation failed;
52
+ * nothing was persisted.
53
+ *
54
+ * Returns one `BatchResult` per operation, in order.
55
+ *
56
+ * @example
57
+ * const results = await Koolbase.db.batch([
58
+ * BatchOp.insert('orders', { total: 50 }),
59
+ * BatchOp.update(inventoryId, { stock: 9 }),
60
+ * BatchOp.upsert('counters', { match: { name: 'orders' }, data: { value: 1 } }),
61
+ * BatchOp.delete(cartItemId),
62
+ * ]);
63
+ */
64
+ batch(operations: BatchOp[]): Promise<BatchResult[]>;
41
65
  get(recordId: string): Promise<KoolbaseRecord>;
42
66
  update(recordId: string, data: Record<string, unknown>): Promise<KoolbaseRecord>;
43
67
  delete(recordId: string): Promise<void>;
package/dist/database.js CHANGED
@@ -8,6 +8,23 @@ const database_errors_1 = require("./database-errors");
8
8
  function generateId() {
9
9
  return 'local_' + Math.random().toString(36).slice(2) + Date.now().toString(36);
10
10
  }
11
+ function batchOpToWire(op) {
12
+ switch (op.type) {
13
+ case 'insert':
14
+ return { type: 'insert', collection: op.collection, data: op.data };
15
+ case 'update':
16
+ return { type: 'update', record_id: op.recordId, data: op.data };
17
+ case 'delete':
18
+ return { type: 'delete', record_id: op.recordId };
19
+ case 'upsert':
20
+ return {
21
+ type: 'upsert',
22
+ collection: op.collection,
23
+ match: op.match,
24
+ data: op.data,
25
+ };
26
+ }
27
+ }
11
28
  class KoolbaseDatabase {
12
29
  constructor(config, getUserId, getToken) {
13
30
  this.config = config;
@@ -160,6 +177,70 @@ class KoolbaseDatabase {
160
177
  await (0, cache_store_1.invalidateCache)(userId, collection);
161
178
  return body.deleted ?? 0;
162
179
  }
180
+ // ─── Batch (atomic, online-only) ────────────────────────────────────────────
181
+ /**
182
+ * Run multiple writes as a single atomic transaction.
183
+ *
184
+ * All `operations` commit together or none are applied — the server runs
185
+ * them in one database transaction and rolls back entirely on any failure.
186
+ * Operations apply in order and may span multiple collections.
187
+ *
188
+ * Online-only by design (like `upsert` and `deleteWhere`): atomicity needs
189
+ * the server's authoritative view, so a batch is never queued offline — it
190
+ * throws on network failure. A server-side rejection throws a
191
+ * `KoolbaseDataException` whose message identifies which operation failed;
192
+ * nothing was persisted.
193
+ *
194
+ * Returns one `BatchResult` per operation, in order.
195
+ *
196
+ * @example
197
+ * const results = await Koolbase.db.batch([
198
+ * BatchOp.insert('orders', { total: 50 }),
199
+ * BatchOp.update(inventoryId, { stock: 9 }),
200
+ * BatchOp.upsert('counters', { match: { name: 'orders' }, data: { value: 1 } }),
201
+ * BatchOp.delete(cartItemId),
202
+ * ]);
203
+ */
204
+ async batch(operations) {
205
+ if (operations.length === 0) {
206
+ throw new Error('batch requires at least one operation');
207
+ }
208
+ const res = await fetch(`${this.config.baseUrl}/v1/sdk/db/batch`, {
209
+ method: 'POST',
210
+ headers: await this.buildHeaders(),
211
+ body: JSON.stringify({
212
+ operations: operations.map(batchOpToWire),
213
+ }),
214
+ });
215
+ const body = await res.json();
216
+ if (!res.ok) {
217
+ throw (0, database_errors_1.koolbaseDataError)(res.status, body, `Batch failed: ${res.status}`);
218
+ }
219
+ const results = (body.results ?? []).map(r => ({
220
+ type: r.type ?? '',
221
+ record: r.record
222
+ ? (0, record_1.recordFromWire)(r.record)
223
+ : undefined,
224
+ created: r.created,
225
+ deleted: r.deleted ?? false,
226
+ }));
227
+ // Keep the cache consistent with what committed. Insert/upsert carry the
228
+ // collection in the input op; update/delete address records by id, so we
229
+ // don't know the collection at this layer — those refresh naturally on
230
+ // the next query for the affected collection.
231
+ const userId = this.getUserId() ?? 'anonymous';
232
+ const touched = new Set();
233
+ for (const op of operations) {
234
+ if (op.type === 'insert' || op.type === 'upsert') {
235
+ touched.add(op.collection);
236
+ }
237
+ }
238
+ for (const col of touched) {
239
+ await (0, cache_store_1.invalidateCache)(userId, col);
240
+ }
241
+ return results;
242
+ }
243
+ // ─── Get single record ──────────────────────────────────────────────────────
163
244
  // ─── Get single record ──────────────────────────────────────────────────────
164
245
  async get(recordId) {
165
246
  const raw = await this.request('GET', `/v1/sdk/db/records/${recordId}`);
package/dist/types.d.ts CHANGED
@@ -243,3 +243,42 @@ export interface SignInWithGoogleParams {
243
243
  idToken: string;
244
244
  nonce?: string;
245
245
  }
246
+ export type BatchOp = {
247
+ type: 'insert';
248
+ collection: string;
249
+ data: Record<string, unknown>;
250
+ } | {
251
+ type: 'update';
252
+ recordId: string;
253
+ data: Record<string, unknown>;
254
+ } | {
255
+ type: 'delete';
256
+ recordId: string;
257
+ } | {
258
+ type: 'upsert';
259
+ collection: string;
260
+ match: Record<string, unknown>;
261
+ data: Record<string, unknown>;
262
+ };
263
+ /**
264
+ * Factory helpers for constructing batch operations. Same shape as
265
+ * Flutter's `KoolbaseBatchOp.insert(...)` etc., so the mental model
266
+ * transfers between platforms.
267
+ */
268
+ export declare const BatchOp: {
269
+ insert: (collection: string, data: Record<string, unknown>) => BatchOp;
270
+ update: (recordId: string, data: Record<string, unknown>) => BatchOp;
271
+ delete: (recordId: string) => BatchOp;
272
+ upsert: (collection: string, opts: {
273
+ match: Record<string, unknown>;
274
+ data: Record<string, unknown>;
275
+ }) => BatchOp;
276
+ };
277
+ export interface BatchResult {
278
+ type: string;
279
+ record?: KoolbaseRecord;
280
+ /** For upsert: true if a new record was inserted, false if one was updated. */
281
+ created?: boolean;
282
+ /** True for a successful delete. */
283
+ deleted?: boolean;
284
+ }
package/dist/types.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.FunctionRuntime = exports.RestoreResult = void 0;
3
+ exports.BatchOp = exports.FunctionRuntime = exports.RestoreResult = void 0;
4
4
  /**
5
5
  * Result of KoolbaseAuth.restoreSession(). Apps should branch on this:
6
6
  * - NoSession → show login screen
@@ -22,3 +22,19 @@ var FunctionRuntime;
22
22
  FunctionRuntime["Deno"] = "deno";
23
23
  FunctionRuntime["Dart"] = "dart";
24
24
  })(FunctionRuntime || (exports.FunctionRuntime = FunctionRuntime = {}));
25
+ /**
26
+ * Factory helpers for constructing batch operations. Same shape as
27
+ * Flutter's `KoolbaseBatchOp.insert(...)` etc., so the mental model
28
+ * transfers between platforms.
29
+ */
30
+ exports.BatchOp = {
31
+ insert: (collection, data) => ({ type: 'insert', collection, data }),
32
+ update: (recordId, data) => ({ type: 'update', recordId, data }),
33
+ delete: (recordId) => ({ type: 'delete', recordId }),
34
+ upsert: (collection, opts) => ({
35
+ type: 'upsert',
36
+ collection,
37
+ match: opts.match,
38
+ data: opts.data,
39
+ }),
40
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@techfinityedge/koolbase-react-native",
3
- "version": "3.0.0",
3
+ "version": "3.1.0",
4
4
  "description": "React Native SDK for Koolbase \u2014 auth, database, storage, realtime, feature flags, and functions in one package.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",