@anfenn/dync 1.0.3 → 1.0.5

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.
Files changed (47) hide show
  1. package/README.md +32 -14
  2. package/dist/{chunk-66PSQW4D.js → chunk-YAAFAS64.js} +42 -42
  3. package/dist/chunk-YAAFAS64.js.map +1 -0
  4. package/dist/{dexie-Bv-fV10P.d.cts → dexie-1_xyU5MV.d.cts} +41 -38
  5. package/dist/{dexie-DJFApKsM.d.ts → dexie-ChZ0o0Sz.d.ts} +41 -38
  6. package/dist/dexie.cjs +40 -40
  7. package/dist/dexie.cjs.map +1 -1
  8. package/dist/dexie.d.cts +1 -1
  9. package/dist/dexie.d.ts +1 -1
  10. package/dist/dexie.js +40 -40
  11. package/dist/dexie.js.map +1 -1
  12. package/dist/index.cjs +41 -41
  13. package/dist/index.cjs.map +1 -1
  14. package/dist/index.d.cts +2 -2
  15. package/dist/index.d.ts +2 -2
  16. package/dist/index.js +1 -1
  17. package/dist/{index.shared-CkYsQYyn.d.ts → index.shared-3gbnIINY.d.ts} +4 -4
  18. package/dist/{index.shared-BGwvMH8f.d.cts → index.shared-XsB8HrvX.d.cts} +4 -4
  19. package/dist/react/index.cjs +26 -26
  20. package/dist/react/index.cjs.map +1 -1
  21. package/dist/react/index.d.cts +2 -2
  22. package/dist/react/index.d.ts +2 -2
  23. package/dist/react/index.js +1 -1
  24. package/dist/wa-sqlite.cjs +2 -2
  25. package/dist/wa-sqlite.cjs.map +1 -1
  26. package/dist/wa-sqlite.d.cts +2 -2
  27. package/dist/wa-sqlite.d.ts +2 -2
  28. package/dist/wa-sqlite.js +2 -2
  29. package/dist/wa-sqlite.js.map +1 -1
  30. package/package.json +1 -1
  31. package/src/core/StateManager.ts +4 -4
  32. package/src/core/pullOperations.ts +3 -3
  33. package/src/core/pushOperations.ts +13 -13
  34. package/src/core/tableEnhancers.ts +20 -20
  35. package/src/helpers.ts +1 -1
  36. package/src/storage/dexie/DexieAdapter.ts +2 -2
  37. package/src/storage/dexie/{DexieStorageCollection.ts → DexieCollection.ts} +12 -12
  38. package/src/storage/dexie/DexieTable.ts +123 -0
  39. package/src/storage/dexie/{DexieStorageWhereClause.ts → DexieWhereClause.ts} +21 -21
  40. package/src/storage/dexie/index.ts +3 -3
  41. package/src/storage/memory/MemoryTable.ts +40 -40
  42. package/src/storage/sqlite/SQLiteTable.ts +34 -36
  43. package/src/storage/sqlite/drivers/WaSqliteDriver.ts +6 -6
  44. package/src/storage/types.ts +22 -19
  45. package/src/types.ts +4 -4
  46. package/dist/chunk-66PSQW4D.js.map +0 -1
  47. package/src/storage/dexie/DexieStorageTable.ts +0 -123
package/README.md CHANGED
@@ -52,10 +52,11 @@ And see how Dync compares to the alternatives [below](#hasnt-this-already-been-d
52
52
  ...,
53
53
  {
54
54
  // Only add an entry here for tables that should be synced
55
+ // Pseudocode here, see examples for working code
55
56
  items: {
56
57
  add: (item) => fetch('/api/items'),
57
58
  update: (id, changes) => fetch(`/api/items/${id}`),
58
- remove: (id) => fetch(`/api/items/${id}`),
59
+ delete: (id) => fetch(`/api/items/${id}`),
59
60
  list: (since) => fetch(`/api/items?since=${since}`),
60
61
  },
61
62
  },
@@ -69,8 +70,21 @@ And see how Dync compares to the alternatives [below](#hasnt-this-already-been-d
69
70
  ...,
70
71
  {
71
72
  syncTables: ['items'], // Only add tables to this array that should be synced
72
- push: (changes) => fetch('/api/sync/push'),
73
- pull: (since) => fetch(`/api/sync/pull?since=${since}`),
73
+ push: async (changes) => {
74
+ const res = await fetch('/api/sync/push', {
75
+ method: 'POST',
76
+ headers: { 'Content-Type': 'application/json' },
77
+ body: JSON.stringify(changes),
78
+ });
79
+ return res.json();
80
+ },
81
+ pull: async (since) => {
82
+ const params = new URLSearchParams(
83
+ Object.entries(since).map(([table, date]) => [table, date.toISOString()])
84
+ );
85
+ const res = await fetch(`/api/sync/pull?${params}`);
86
+ return res.json();
87
+ },
74
88
  },
75
89
  );
76
90
  ```
@@ -81,8 +95,8 @@ And see how Dync compares to the alternatives [below](#hasnt-this-already-been-d
81
95
 
82
96
  ```ts
83
97
  const { syncState, db } = useDync();
84
- syncState.conflicts; // Record<id, Conflict> Use to show user conflicts
85
- db.sync.resolveConflict(id, true);
98
+ syncState.conflicts; // Record<localId, Conflict>
99
+ db.sync.resolveConflict(localId, true);
86
100
  ```
87
101
 
88
102
  - Optional first load data download before periodic sync is enabled
@@ -90,10 +104,14 @@ And see how Dync compares to the alternatives [below](#hasnt-this-already-been-d
90
104
  - Reactive updates when data changes via `useLiveQuery()` React hook:
91
105
 
92
106
  ```ts
93
- useLiveQuery(async (db) => {
94
- const items = await db.items.toArray();
95
- setItems(items);
96
- }, []);
107
+ useLiveQuery(
108
+ async (db) => {
109
+ const items = await db.items.toArray(); // toArray() executes the query
110
+ setTodos(items);
111
+ },
112
+ [], // Re-run when variables change
113
+ ['items'], // Re-run when tables change
114
+ );
97
115
  ```
98
116
 
99
117
  - Sqlite schema migration
@@ -159,11 +177,11 @@ Your server records **must** have these fields. If it does but they're named dif
159
177
 
160
178
  Dync auto-injects these fields into your local table schema:
161
179
 
162
- | Field | Description |
163
- | ------------ | --------------------------------------------------------------------------------------------------------------------------------- |
164
- | `_localId` | Stable local identifier, never sent to the server. Ideal for React keys. Generate with `createLocalId()` or use any unique value. |
165
- | `id` | Unique identifier (any datatype). Can be assigned by client or server. |
166
- | `updated_at` | Assigned from the server's `updated_at` after sync. You may set it optimistically, but it's always overwritten on sync. |
180
+ | Field | Description |
181
+ | ------------ | -------------------------------------------------------------------------------------------------------------------------------------------- |
182
+ | `_localId` | Stable local identifier, never sent to the server. Ideal for React keys. Auto-generated UUID, but can be set manually with any unique value. |
183
+ | `id` | Unique identifier (any datatype). Can be assigned by client or server. |
184
+ | `updated_at` | Assigned from the server's `updated_at` after sync. You may set it optimistically, but it's always overwritten on sync. |
167
185
 
168
186
  Note: `deleted` doesn't exist on the client, as it's removed during sync.
169
187
 
@@ -24,7 +24,7 @@ var UPDATED_AT = "updated_at";
24
24
  var SyncAction = /* @__PURE__ */ ((SyncAction2) => {
25
25
  SyncAction2["Create"] = "create";
26
26
  SyncAction2["Update"] = "update";
27
- SyncAction2["Remove"] = "remove";
27
+ SyncAction2["Delete"] = "delete";
28
28
  return SyncAction2;
29
29
  })(SyncAction || {});
30
30
 
@@ -56,7 +56,7 @@ function orderFor(a) {
56
56
  return 1;
57
57
  case "update" /* Update */:
58
58
  return 2;
59
- case "remove" /* Remove */:
59
+ case "delete" /* Delete */:
60
60
  return 3;
61
61
  }
62
62
  }
@@ -159,17 +159,17 @@ var StateManager = class {
159
159
  const hasChanges = Object.keys(omittedChanges || {}).length > 0;
160
160
  const action = change.action;
161
161
  if (queueItem) {
162
- if (queueItem.action === "remove" /* Remove */) {
162
+ if (queueItem.action === "delete" /* Delete */) {
163
163
  return Promise.resolve();
164
164
  }
165
165
  queueItem.version += 1;
166
- if (action === "remove" /* Remove */) {
167
- queueItem.action = "remove" /* Remove */;
166
+ if (action === "delete" /* Delete */) {
167
+ queueItem.action = "delete" /* Delete */;
168
168
  } else if (hasChanges) {
169
169
  queueItem.changes = { ...queueItem.changes, ...omittedChanges };
170
170
  queueItem.after = { ...queueItem.after, ...omittedAfter };
171
171
  }
172
- } else if (action === "remove" /* Remove */ || hasChanges) {
172
+ } else if (action === "delete" /* Delete */ || hasChanges) {
173
173
  next.pendingChanges = [...next.pendingChanges];
174
174
  next.pendingChanges.push({
175
175
  action,
@@ -467,7 +467,7 @@ function enhanceSyncTable({ table, tableName, withTransaction, state, enhancedTa
467
467
  if (record) {
468
468
  deletedLocalId = record._localId;
469
469
  await state.addPendingChange({
470
- action: "remove" /* Remove */,
470
+ action: "delete" /* Delete */,
471
471
  tableName,
472
472
  localId: record._localId,
473
473
  id: record.id,
@@ -481,7 +481,7 @@ function enhanceSyncTable({ table, tableName, withTransaction, state, enhancedTa
481
481
  }
482
482
  };
483
483
  const wrappedBulkAdd = async (items) => {
484
- if (items.length === 0) return;
484
+ if (items.length === 0) return [];
485
485
  const now = (/* @__PURE__ */ new Date()).toISOString();
486
486
  const syncedItems = items.map((item) => {
487
487
  const localId = item._localId || createLocalId();
@@ -509,7 +509,7 @@ function enhanceSyncTable({ table, tableName, withTransaction, state, enhancedTa
509
509
  return result;
510
510
  };
511
511
  const wrappedBulkPut = async (items) => {
512
- if (items.length === 0) return;
512
+ if (items.length === 0) return [];
513
513
  const now = (/* @__PURE__ */ new Date()).toISOString();
514
514
  const syncedItems = items.map((item) => {
515
515
  const localId = item._localId || createLocalId();
@@ -602,7 +602,7 @@ function enhanceSyncTable({ table, tableName, withTransaction, state, enhancedTa
602
602
  if (record) {
603
603
  deletedLocalIds.push(record._localId);
604
604
  await state.addPendingChange({
605
- action: "remove" /* Remove */,
605
+ action: "delete" /* Delete */,
606
606
  tableName,
607
607
  localId: record._localId,
608
608
  id: record.id,
@@ -626,7 +626,7 @@ function enhanceSyncTable({ table, tableName, withTransaction, state, enhancedTa
626
626
  if (record._localId) {
627
627
  deletedLocalIds.push(record._localId);
628
628
  await state.addPendingChange({
629
- action: "remove" /* Remove */,
629
+ action: "delete" /* Delete */,
630
630
  tableName,
631
631
  localId: record._localId,
632
632
  id: record.id,
@@ -759,20 +759,20 @@ async function processPullData(tableName, serverData, since, ctx) {
759
759
  await ctx.withTransaction("rw", [tableName, DYNC_STATE_TABLE], async (tables) => {
760
760
  const txTable = tables[tableName];
761
761
  const pendingRemovalById = new Set(
762
- ctx.state.getState().pendingChanges.filter((p) => p.tableName === tableName && p.action === "remove" /* Remove */).map((p) => p.id)
762
+ ctx.state.getState().pendingChanges.filter((p) => p.tableName === tableName && p.action === "delete" /* Delete */).map((p) => p.id)
763
763
  );
764
764
  for (const remote of serverData) {
765
765
  const remoteUpdated = new Date(remote.updated_at);
766
766
  if (remoteUpdated > newest) newest = remoteUpdated;
767
767
  if (pendingRemovalById.has(remote.id)) {
768
- ctx.logger.debug(`[dync] pull:skip-pending-remove tableName=${tableName} id=${remote.id}`);
768
+ ctx.logger.debug(`[dync] pull:skip-pending-delete tableName=${tableName} id=${remote.id}`);
769
769
  continue;
770
770
  }
771
771
  const localItem = await txTable.where("id").equals(remote.id).first();
772
772
  if (remote.deleted) {
773
773
  if (localItem) {
774
774
  await txTable.raw.delete(localItem._localId);
775
- ctx.logger.debug(`[dync] pull:remove tableName=${tableName} id=${remote.id}`);
775
+ ctx.logger.debug(`[dync] pull:delete tableName=${tableName} id=${remote.id}`);
776
776
  hasChanges = true;
777
777
  }
778
778
  continue;
@@ -800,9 +800,9 @@ async function processPullData(tableName, serverData, since, ctx) {
800
800
  }
801
801
 
802
802
  // src/core/pushOperations.ts
803
- async function handleRemoveSuccess(change, ctx) {
803
+ async function handleDeleteSuccess(change, ctx) {
804
804
  const { tableName, localId, id } = change;
805
- ctx.logger.debug(`[dync] push:remove:success tableName=${tableName} localId=${localId} id=${id}`);
805
+ ctx.logger.debug(`[dync] push:delete:success tableName=${tableName} localId=${localId} id=${id}`);
806
806
  await ctx.state.removePendingChange(localId, tableName);
807
807
  }
808
808
  async function handleUpdateSuccess(change, ctx) {
@@ -823,9 +823,9 @@ async function handleCreateSuccess(change, serverResult, ctx) {
823
823
  if (wasChanged && ctx.state.samePendingVersion(tableName, localId, version)) {
824
824
  await ctx.state.removePendingChange(localId, tableName);
825
825
  } else {
826
- const nextAction = wasChanged ? "update" /* Update */ : "remove" /* Remove */;
826
+ const nextAction = wasChanged ? "update" /* Update */ : "delete" /* Delete */;
827
827
  await ctx.state.updatePendingChange(tableName, localId, nextAction, serverResult.id);
828
- if (nextAction === "remove" /* Remove */) return;
828
+ if (nextAction === "delete" /* Delete */) return;
829
829
  }
830
830
  });
831
831
  const finalItem = { ...changes, ...serverResult, _localId: localId };
@@ -850,14 +850,14 @@ async function pushOne(change, ctx) {
850
850
  ctx.logger.debug(`[dync] push:attempt action=${change.action} tableName=${change.tableName} localId=${change.localId}`);
851
851
  const { action, tableName, localId, id, changes, after } = change;
852
852
  switch (action) {
853
- case "remove" /* Remove */:
853
+ case "delete" /* Delete */:
854
854
  if (!id) {
855
- ctx.logger.warn(`[dync] push:remove:no-id tableName=${tableName} localId=${localId}`);
855
+ ctx.logger.warn(`[dync] push:delete:no-id tableName=${tableName} localId=${localId}`);
856
856
  await ctx.state.removePendingChange(localId, tableName);
857
857
  return;
858
858
  }
859
- await api.remove(id);
860
- await handleRemoveSuccess(change, ctx);
859
+ await api.delete(id);
860
+ await handleDeleteSuccess(change, ctx);
861
861
  break;
862
862
  case "update" /* Update */: {
863
863
  if (ctx.state.hasConflicts(localId)) {
@@ -953,10 +953,10 @@ async function pushAllBatch(ctx) {
953
953
  }
954
954
  const payloads = changesToPush.map((change) => ({
955
955
  table: change.tableName,
956
- action: change.action === "create" /* Create */ ? "add" : change.action === "update" /* Update */ ? "update" : "remove",
956
+ action: change.action === "create" /* Create */ ? "add" : change.action === "update" /* Update */ ? "update" : "delete",
957
957
  localId: change.localId,
958
958
  id: change.id,
959
- data: change.action === "remove" /* Remove */ ? void 0 : change.changes
959
+ data: change.action === "delete" /* Delete */ ? void 0 : change.changes
960
960
  }));
961
961
  ctx.logger.debug(`[dync] push:batch:start count=${payloads.length}`);
962
962
  const results = await ctx.batchSync.push(payloads);
@@ -994,11 +994,11 @@ async function processBatchPushResult(change, result, ctx) {
994
994
  return;
995
995
  }
996
996
  switch (action) {
997
- case "remove" /* Remove */:
998
- handleRemoveSuccess(change, ctx);
997
+ case "delete" /* Delete */:
998
+ await handleDeleteSuccess(change, ctx);
999
999
  break;
1000
1000
  case "update" /* Update */:
1001
- handleUpdateSuccess(change, ctx);
1001
+ await handleUpdateSuccess(change, ctx);
1002
1002
  break;
1003
1003
  case "create" /* Create */: {
1004
1004
  const serverResult = { id: result.id };
@@ -2015,23 +2015,23 @@ var MemoryTable = class {
2015
2015
  return this.baseBulkAdd(items);
2016
2016
  }
2017
2017
  baseBulkAdd(items) {
2018
- let lastKey = void 0;
2018
+ const keys = [];
2019
2019
  for (let index = 0; index < items.length; index += 1) {
2020
2020
  const item = items[index];
2021
- lastKey = this.baseAdd(item);
2021
+ keys.push(this.baseAdd(item));
2022
2022
  }
2023
- return lastKey;
2023
+ return keys;
2024
2024
  }
2025
2025
  async bulkPut(items) {
2026
2026
  return this.baseBulkPut(items);
2027
2027
  }
2028
2028
  baseBulkPut(items) {
2029
- let lastKey = void 0;
2029
+ const keys = [];
2030
2030
  for (let index = 0; index < items.length; index += 1) {
2031
2031
  const item = items[index];
2032
- lastKey = this.basePut(item);
2032
+ keys.push(this.basePut(item));
2033
2033
  }
2034
- return lastKey;
2034
+ return keys;
2035
2035
  }
2036
2036
  async bulkGet(keys) {
2037
2037
  return Promise.all(keys.map((key) => this.get(key)));
@@ -3251,12 +3251,12 @@ var SQLiteTable = class {
3251
3251
  return this.baseBulkAdd(items);
3252
3252
  }
3253
3253
  async baseBulkAdd(items) {
3254
- if (!items.length) return void 0;
3254
+ if (!items.length) return [];
3255
3255
  const columns = this.columnNames;
3256
3256
  const columnCount = columns.length;
3257
3257
  const maxParamsPerBatch = 500;
3258
3258
  const batchSize = Math.max(1, Math.floor(maxParamsPerBatch / columnCount));
3259
- let lastKey = void 0;
3259
+ const allKeys = [];
3260
3260
  for (let i = 0; i < items.length; i += batchSize) {
3261
3261
  const batch = items.slice(i, i + batchSize);
3262
3262
  const records = batch.map((item) => this.prepareRecordForWrite(item));
@@ -3265,25 +3265,25 @@ var SQLiteTable = class {
3265
3265
  const values = [];
3266
3266
  for (const record of records) {
3267
3267
  values.push(...this.extractColumnValues(record));
3268
+ allKeys.push(record[LOCAL_PK]);
3268
3269
  }
3269
3270
  await this.adapter.run(
3270
3271
  `INSERT INTO ${quoteIdentifier(this.name)} (${columns.map((c) => quoteIdentifier(c)).join(", ")}) VALUES ${placeholders}`,
3271
3272
  values
3272
3273
  );
3273
- lastKey = records[records.length - 1][LOCAL_PK];
3274
3274
  }
3275
- return lastKey;
3275
+ return allKeys;
3276
3276
  }
3277
3277
  async bulkPut(items) {
3278
3278
  return this.baseBulkPut(items);
3279
3279
  }
3280
3280
  async baseBulkPut(items) {
3281
- if (!items.length) return void 0;
3281
+ if (!items.length) return [];
3282
3282
  const columns = this.columnNames;
3283
3283
  const columnCount = columns.length;
3284
3284
  const maxParamsPerBatch = 500;
3285
3285
  const batchSize = Math.max(1, Math.floor(maxParamsPerBatch / columnCount));
3286
- let lastKey = void 0;
3286
+ const allKeys = [];
3287
3287
  for (let i = 0; i < items.length; i += batchSize) {
3288
3288
  const batch = items.slice(i, i + batchSize);
3289
3289
  const records = batch.map((item) => this.prepareRecordForWrite(item));
@@ -3292,14 +3292,14 @@ var SQLiteTable = class {
3292
3292
  const values = [];
3293
3293
  for (const record of records) {
3294
3294
  values.push(...this.extractColumnValues(record));
3295
+ allKeys.push(record[LOCAL_PK]);
3295
3296
  }
3296
3297
  await this.adapter.run(
3297
3298
  `INSERT OR REPLACE INTO ${quoteIdentifier(this.name)} (${columns.map((c) => quoteIdentifier(c)).join(", ")}) VALUES ${placeholders}`,
3298
3299
  values
3299
3300
  );
3300
- lastKey = records[records.length - 1][LOCAL_PK];
3301
3301
  }
3302
- return lastKey;
3302
+ return allKeys;
3303
3303
  }
3304
3304
  async bulkGet(keys) {
3305
3305
  if (!keys.length) return [];
@@ -3881,4 +3881,4 @@ export {
3881
3881
  SqliteQueryContext,
3882
3882
  SQLiteAdapter2 as SQLiteAdapter
3883
3883
  };
3884
- //# sourceMappingURL=chunk-66PSQW4D.js.map
3884
+ //# sourceMappingURL=chunk-YAAFAS64.js.map