@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 +13 -13
- package/dist/{dexie-DRLMKLl5.d.ts → dexie-BFPA0JU2.d.ts} +1 -1
- package/dist/{dexie-BqktVP7s.d.cts → dexie-T9m1mP1h.d.cts} +1 -1
- package/dist/dexie.d.cts +1 -1
- package/dist/dexie.d.ts +1 -1
- package/dist/index.cjs +44 -7
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +91 -3
- package/dist/index.d.ts +91 -3
- package/dist/index.js +3863 -10
- package/dist/index.js.map +1 -1
- package/dist/react/index.cjs +36 -1729
- package/dist/react/index.cjs.map +1 -1
- package/dist/react/index.d.cts +65 -31
- package/dist/react/index.d.ts +65 -31
- package/dist/react/index.js +31 -41
- package/dist/react/index.js.map +1 -1
- package/dist/{index.shared-D-fB8Odd.d.ts → types-BLw25Wlc.d.ts} +37 -86
- package/dist/{index.shared-DHuT0l_t.d.cts → types-yJuzCcux.d.cts} +37 -86
- package/package.json +1 -1
- package/src/index.native.ts +1 -0
- package/src/index.shared.ts +53 -40
- package/src/index.ts +1 -0
- package/src/react/index.ts +3 -2
- package/src/react/types.ts +6 -0
- package/src/react/useLiveQuery.ts +97 -0
- package/src/react/useSyncState.ts +45 -0
- package/src/types.ts +37 -3
- package/dist/chunk-QA2TX54K.js +0 -3835
- package/dist/chunk-QA2TX54K.js.map +0 -1
- package/src/react/useDync.ts +0 -156
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://www.npmjs.com/package/@anfenn/dync)
|
|
4
4
|
|
|
5
|
-
A complete
|
|
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 =
|
|
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 =
|
|
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
|
|
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
|
-
|
|
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,
|
|
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,
|
|
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
package/dist/dexie.d.ts
CHANGED
package/dist/index.cjs
CHANGED
|
@@ -1317,13 +1317,35 @@ var DyncBase = class {
|
|
|
1317
1317
|
mutationsDuringSync = false;
|
|
1318
1318
|
state;
|
|
1319
1319
|
name;
|
|
1320
|
-
|
|
1321
|
-
|
|
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 =
|
|
1345
|
+
this.batchSync = syncConfig;
|
|
1324
1346
|
this.syncedTables = new Set(this.batchSync.syncTables);
|
|
1325
1347
|
} else {
|
|
1326
|
-
this.syncApis =
|
|
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
|
-
|
|
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
|
|
1724
|
-
var Dync = DyncConstructor;
|
|
1761
|
+
var Dync = DyncBase;
|
|
1725
1762
|
|
|
1726
1763
|
// src/storage/memory/MemoryQueryContext.ts
|
|
1727
1764
|
var MemoryQueryContext = class {
|