@anfenn/dync 1.0.25 → 1.0.27

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
@@ -2,7 +2,7 @@
2
2
 
3
3
  [![npm version](https://img.shields.io/npm/v/@anfenn/dync.svg)](https://www.npmjs.com/package/@anfenn/dync)
4
4
 
5
- A complete React offline-first data layer with sync engine for any local storage (IndexedDB, Sqlite, etc.), and any backend (Restful, GraphQL, Supabase, etc.) in a Website, PWA, CapacitorJs, React Native, or Electron app.
5
+ A complete Typescript offline-first data layer with sync engine for any local storage (IndexedDB, Sqlite, etc.), and any backend (Restful, GraphQL, Supabase, etc.) in a Website, PWA, CapacitorJs, React Native, or Electron app.
6
6
 
7
7
  Start with a Website or PWA using IndexedDB, sync with your existing REST API, and later ship native apps with encrypted SQLite - without changing a line of code.
8
8
 
@@ -48,9 +48,9 @@ And see how Dync compares to the alternatives [below](#hasnt-this-already-been-d
48
48
  - Option 1: Map remote api CRUD urls to a local collection:
49
49
 
50
50
  ```ts
51
- const db = makeDync(
51
+ const db = new Dync({
52
52
  ...,
53
- {
53
+ sync: {
54
54
  // Only add an entry here for tables that should be synced
55
55
  // Pseudocode here, see examples for working code
56
56
  items: {
@@ -60,15 +60,15 @@ And see how Dync compares to the alternatives [below](#hasnt-this-already-been-d
60
60
  list: (since) => fetch(`/api/items?since=${since}`),
61
61
  },
62
62
  },
63
- );
63
+ });
64
64
  ```
65
65
 
66
66
  - Option 2: Batch sync to remote /push & /pull endpoints:
67
67
 
68
68
  ```ts
69
- const db = makeDync(
69
+ const db = new Dync({
70
70
  ...,
71
- {
71
+ sync: {
72
72
  syncTables: ['items'], // Only add tables to this array that should be synced
73
73
  push: async (changes) => {
74
74
  const res = await fetch('/api/sync/push', {
@@ -79,14 +79,12 @@ And see how Dync compares to the alternatives [below](#hasnt-this-already-been-d
79
79
  return res.json();
80
80
  },
81
81
  pull: async (since) => {
82
- const params = new URLSearchParams(
83
- Object.entries(since).map(([table, date]) => [table, date.toISOString()])
84
- );
82
+ const params = new URLSearchParams(Object.entries(since).map(([table, date]) => [table, date.toISOString()]));
85
83
  const res = await fetch(`/api/sync/pull?${params}`);
86
84
  return res.json();
87
85
  },
88
86
  },
89
- );
87
+ });
90
88
  ```
91
89
 
92
90
  See [examples/shared/api.ts](examples/shared/api.ts) for a fully documented example of these two options.
@@ -94,7 +92,7 @@ And see how Dync compares to the alternatives [below](#hasnt-this-already-been-d
94
92
  - Full conflict resolution: `local-wins`, `remote-wins` or with `try-shallow-merge` the user can resolve with:
95
93
 
96
94
  ```ts
97
- const { syncState, db } = useDync();
95
+ const syncState = useSyncState(db);
98
96
  syncState.conflicts; // Record<localId, Conflict>
99
97
  db.sync.resolveConflict(localId, true);
100
98
  ```
@@ -107,11 +105,12 @@ And see how Dync compares to the alternatives [below](#hasnt-this-already-been-d
107
105
 
108
106
  ```ts
109
107
  useLiveQuery(
110
- async (db) => {
108
+ db,
109
+ async () => {
111
110
  const items = await db.items.toArray(); // toArray() executes the query
112
111
  setTodos(items);
113
112
  },
114
- [], // Re-run when variables change
113
+ [], // Re-run when variables change (None defined)
115
114
  ['items'], // Re-run when tables change
116
115
  );
117
116
  ```
@@ -124,6 +123,7 @@ And see how Dync compares to the alternatives [below](#hasnt-this-already-been-d
124
123
 
125
124
  - Full IndexedDB & SQL unified query language:
126
125
  - Using IndexedDB functions or raw SQL will always be more expressive independently
126
+ - When required, best performance will always come from native api
127
127
  - No need to learn another api when you might only need one storage type
128
128
  - Would greatly increase complexity of this library
129
129
 
@@ -440,4 +440,4 @@ declare class DexieAdapter implements StorageAdapter {
440
440
  transaction<T>(mode: TransactionMode, tableNames: string[], callback: (context: StorageTransactionContext) => Promise<T>): Promise<T>;
441
441
  }
442
442
 
443
- export { DexieQueryContext as D, MemoryAdapter as M, SQLiteAdapter as S, type TableSchemaDefinition as T, MemoryQueryContext as a, SqliteQueryContext as b, type StorageAdapter as c, type StorageTable as d, DexieAdapter as e };
443
+ export { DexieQueryContext as D, MemoryQueryContext as M, SqliteQueryContext as S, type TableSchemaDefinition as T, type StorageTable as a, MemoryAdapter as b, SQLiteAdapter as c, type StorageAdapter as d, DexieAdapter as e };
@@ -440,4 +440,4 @@ declare class DexieAdapter implements StorageAdapter {
440
440
  transaction<T>(mode: TransactionMode, tableNames: string[], callback: (context: StorageTransactionContext) => Promise<T>): Promise<T>;
441
441
  }
442
442
 
443
- export { DexieQueryContext as D, MemoryAdapter as M, SQLiteAdapter as S, type TableSchemaDefinition as T, MemoryQueryContext as a, SqliteQueryContext as b, type StorageAdapter as c, type StorageTable as d, DexieAdapter as e };
443
+ export { DexieQueryContext as D, MemoryQueryContext as M, SqliteQueryContext as S, type TableSchemaDefinition as T, type StorageTable as a, MemoryAdapter as b, SQLiteAdapter as c, type StorageAdapter as d, DexieAdapter as e };
package/dist/dexie.d.cts CHANGED
@@ -1,3 +1,3 @@
1
- export { e as DexieAdapter, D as DexieQueryContext, c as StorageAdapter } from './dexie-BqktVP7s.cjs';
1
+ export { e as DexieAdapter, D as DexieQueryContext, d as StorageAdapter } from './dexie-T9m1mP1h.cjs';
2
2
  import 'dexie';
3
3
  import './types-CSbIAfu2.cjs';
package/dist/dexie.d.ts CHANGED
@@ -1,3 +1,3 @@
1
- export { e as DexieAdapter, D as DexieQueryContext, c as StorageAdapter } from './dexie-DRLMKLl5.js';
1
+ export { e as DexieAdapter, D as DexieQueryContext, d as StorageAdapter } from './dexie-BFPA0JU2.js';
2
2
  import 'dexie';
3
3
  import './types-CSbIAfu2.js';
package/dist/index.cjs CHANGED
@@ -1317,13 +1317,35 @@ var DyncBase = class {
1317
1317
  mutationsDuringSync = false;
1318
1318
  state;
1319
1319
  name;
1320
- constructor(databaseName, syncApisOrBatchSync, storageAdapter, options) {
1321
- const isBatchMode = typeof syncApisOrBatchSync.push === "function";
1320
+ /**
1321
+ * Create a new Dync instance.
1322
+ *
1323
+ * @example Per-table sync mode
1324
+ * ```ts
1325
+ * const db = new Dync<Store>({
1326
+ * databaseName: 'my-app',
1327
+ * storageAdapter: new SQLiteAdapter(driver),
1328
+ * sync: { todos: todoSyncApi },
1329
+ * });
1330
+ * ```
1331
+ *
1332
+ * @example Batch sync mode
1333
+ * ```ts
1334
+ * const db = new Dync<Store>({
1335
+ * databaseName: 'my-app',
1336
+ * storageAdapter: new SQLiteAdapter(driver),
1337
+ * sync: { syncTables: ['todos'], push, pull },
1338
+ * });
1339
+ * ```
1340
+ */
1341
+ constructor(config) {
1342
+ const { databaseName, storageAdapter, sync: syncConfig, options } = config;
1343
+ const isBatchMode = typeof syncConfig.push === "function" && typeof syncConfig.pull === "function";
1322
1344
  if (isBatchMode) {
1323
- this.batchSync = syncApisOrBatchSync;
1345
+ this.batchSync = syncConfig;
1324
1346
  this.syncedTables = new Set(this.batchSync.syncTables);
1325
1347
  } else {
1326
- this.syncApis = syncApisOrBatchSync;
1348
+ this.syncApis = syncConfig;
1327
1349
  this.syncedTables = new Set(Object.keys(this.syncApis));
1328
1350
  }
1329
1351
  this.adapter = storageAdapter;
@@ -1340,6 +1362,10 @@ var DyncBase = class {
1340
1362
  this.state = new StateManager({
1341
1363
  storageAdapter: this.adapter
1342
1364
  });
1365
+ Object.defineProperty(this.sync, "state", {
1366
+ get: () => this.getSyncState(),
1367
+ enumerable: true
1368
+ });
1343
1369
  const driverInfo = "driverType" in this.adapter ? ` (Driver: ${this.adapter.driverType})` : "";
1344
1370
  this.logger.debug(`[dync] Initialized with ${this.adapter.type}${driverInfo}`);
1345
1371
  }
@@ -1383,6 +1409,17 @@ var DyncBase = class {
1383
1409
  storesDefined = true;
1384
1410
  self.adapter.defineSchema(versionNumber, fullSchema, schemaOptions);
1385
1411
  self.setupEnhancedTables(Object.keys(schema));
1412
+ for (const tableName of Object.keys(schema)) {
1413
+ if (!(tableName in self)) {
1414
+ Object.defineProperty(self, tableName, {
1415
+ get() {
1416
+ return self.table(tableName);
1417
+ },
1418
+ enumerable: true,
1419
+ configurable: false
1420
+ });
1421
+ }
1422
+ }
1386
1423
  return builder;
1387
1424
  },
1388
1425
  sqlite(configure) {
@@ -1714,14 +1751,14 @@ var DyncBase = class {
1714
1751
  sync = {
1715
1752
  enable: this.enableSync.bind(this),
1716
1753
  startFirstLoad: this.startFirstLoad.bind(this),
1717
- getState: this.getSyncState.bind(this),
1754
+ state: void 0,
1755
+ // getter in constructor
1718
1756
  resolveConflict: this.resolveConflict.bind(this),
1719
1757
  onStateChange: this.onSyncStateChange.bind(this),
1720
1758
  onMutation: this.onMutation.bind(this)
1721
1759
  };
1722
1760
  };
1723
- var DyncConstructor = DyncBase;
1724
- var Dync = DyncConstructor;
1761
+ var Dync = DyncBase;
1725
1762
 
1726
1763
  // src/storage/memory/MemoryQueryContext.ts
1727
1764
  var MemoryQueryContext = class {