@anfenn/dync 1.0.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/LICENSE +21 -0
- package/README.md +212 -0
- package/dist/capacitor.cjs +228 -0
- package/dist/capacitor.cjs.map +1 -0
- package/dist/capacitor.d.cts +62 -0
- package/dist/capacitor.d.ts +62 -0
- package/dist/capacitor.js +9 -0
- package/dist/capacitor.js.map +1 -0
- package/dist/chunk-LGHOZECP.js +3884 -0
- package/dist/chunk-LGHOZECP.js.map +1 -0
- package/dist/chunk-SQB6E7V2.js +191 -0
- package/dist/chunk-SQB6E7V2.js.map +1 -0
- package/dist/dexie-Bv-fV10P.d.cts +444 -0
- package/dist/dexie-DJFApKsM.d.ts +444 -0
- package/dist/dexie.cjs +381 -0
- package/dist/dexie.cjs.map +1 -0
- package/dist/dexie.d.cts +3 -0
- package/dist/dexie.d.ts +3 -0
- package/dist/dexie.js +343 -0
- package/dist/dexie.js.map +1 -0
- package/dist/expoSqlite.cjs +98 -0
- package/dist/expoSqlite.cjs.map +1 -0
- package/dist/expoSqlite.d.cts +17 -0
- package/dist/expoSqlite.d.ts +17 -0
- package/dist/expoSqlite.js +61 -0
- package/dist/expoSqlite.js.map +1 -0
- package/dist/index.cjs +3916 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +8 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +20 -0
- package/dist/index.js.map +1 -0
- package/dist/index.shared-CPIge2ZM.d.ts +234 -0
- package/dist/index.shared-YSn6c01d.d.cts +234 -0
- package/dist/node.cjs +126 -0
- package/dist/node.cjs.map +1 -0
- package/dist/node.d.cts +80 -0
- package/dist/node.d.ts +80 -0
- package/dist/node.js +89 -0
- package/dist/node.js.map +1 -0
- package/dist/react/index.cjs +1754 -0
- package/dist/react/index.cjs.map +1 -0
- package/dist/react/index.d.cts +40 -0
- package/dist/react/index.d.ts +40 -0
- package/dist/react/index.js +78 -0
- package/dist/react/index.js.map +1 -0
- package/dist/types-CSbIAfu2.d.cts +46 -0
- package/dist/types-CSbIAfu2.d.ts +46 -0
- package/dist/wa-sqlite.cjs +318 -0
- package/dist/wa-sqlite.cjs.map +1 -0
- package/dist/wa-sqlite.d.cts +175 -0
- package/dist/wa-sqlite.d.ts +175 -0
- package/dist/wa-sqlite.js +281 -0
- package/dist/wa-sqlite.js.map +1 -0
- package/package.json +171 -0
- package/src/addVisibilityChangeListener.native.ts +33 -0
- package/src/addVisibilityChangeListener.ts +24 -0
- package/src/capacitor.ts +4 -0
- package/src/core/StateManager.ts +272 -0
- package/src/core/firstLoad.ts +332 -0
- package/src/core/pullOperations.ts +212 -0
- package/src/core/pushOperations.ts +290 -0
- package/src/core/tableEnhancers.ts +457 -0
- package/src/core/types.ts +3 -0
- package/src/createLocalId.native.ts +8 -0
- package/src/createLocalId.ts +6 -0
- package/src/dexie.ts +2 -0
- package/src/expoSqlite.ts +2 -0
- package/src/helpers.ts +87 -0
- package/src/index.native.ts +28 -0
- package/src/index.shared.ts +613 -0
- package/src/index.ts +28 -0
- package/src/logger.ts +26 -0
- package/src/node.ts +4 -0
- package/src/react/index.ts +2 -0
- package/src/react/useDync.ts +156 -0
- package/src/storage/dexie/DexieAdapter.ts +72 -0
- package/src/storage/dexie/DexieQueryContext.ts +14 -0
- package/src/storage/dexie/DexieStorageCollection.ts +124 -0
- package/src/storage/dexie/DexieStorageTable.ts +123 -0
- package/src/storage/dexie/DexieStorageWhereClause.ts +103 -0
- package/src/storage/dexie/helpers.ts +1 -0
- package/src/storage/dexie/index.ts +7 -0
- package/src/storage/memory/MemoryAdapter.ts +55 -0
- package/src/storage/memory/MemoryCollection.ts +215 -0
- package/src/storage/memory/MemoryQueryContext.ts +14 -0
- package/src/storage/memory/MemoryTable.ts +336 -0
- package/src/storage/memory/MemoryWhereClause.ts +134 -0
- package/src/storage/memory/index.ts +7 -0
- package/src/storage/memory/types.ts +24 -0
- package/src/storage/sqlite/SQLiteAdapter.ts +564 -0
- package/src/storage/sqlite/SQLiteCollection.ts +294 -0
- package/src/storage/sqlite/SQLiteTable.ts +604 -0
- package/src/storage/sqlite/SQLiteWhereClause.ts +341 -0
- package/src/storage/sqlite/SqliteQueryContext.ts +30 -0
- package/src/storage/sqlite/drivers/BetterSqlite3Driver.ts +156 -0
- package/src/storage/sqlite/drivers/CapacitorFastSqlDriver.ts +114 -0
- package/src/storage/sqlite/drivers/CapacitorSQLiteDriver.ts +137 -0
- package/src/storage/sqlite/drivers/ExpoSQLiteDriver.native.ts +67 -0
- package/src/storage/sqlite/drivers/WaSqliteDriver.ts +537 -0
- package/src/storage/sqlite/drivers/wa-sqlite-vfs.d.ts +46 -0
- package/src/storage/sqlite/helpers.ts +144 -0
- package/src/storage/sqlite/index.ts +11 -0
- package/src/storage/sqlite/schema.ts +44 -0
- package/src/storage/sqlite/types.ts +164 -0
- package/src/storage/types.ts +112 -0
- package/src/types.ts +186 -0
- package/src/wa-sqlite.ts +4 -0
|
@@ -0,0 +1,457 @@
|
|
|
1
|
+
import { createLocalId } from '../helpers';
|
|
2
|
+
import { SyncAction, type MutationEvent, type SyncedRecord } from '../types';
|
|
3
|
+
import type { StorageTable } from '../storage/types';
|
|
4
|
+
import { DYNC_STATE_TABLE, type StateHelpers } from './StateManager';
|
|
5
|
+
import type { WithTransaction } from './types';
|
|
6
|
+
export type EmitMutation = (event: MutationEvent) => void;
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Wraps a table with mutation emission for reactive updates.
|
|
10
|
+
* This allows useLiveQuery to work with any table.
|
|
11
|
+
*/
|
|
12
|
+
export function wrapWithMutationEmitter<T>(table: StorageTable<T>, tableName: string, emitMutation: EmitMutation): void {
|
|
13
|
+
const rawAdd = table.raw.add;
|
|
14
|
+
const rawPut = table.raw.put;
|
|
15
|
+
const rawUpdate = table.raw.update;
|
|
16
|
+
const rawDelete = table.raw.delete;
|
|
17
|
+
const rawBulkAdd = table.raw.bulkAdd;
|
|
18
|
+
const rawBulkPut = table.raw.bulkPut;
|
|
19
|
+
const rawBulkUpdate = table.raw.bulkUpdate;
|
|
20
|
+
const rawBulkDelete = table.raw.bulkDelete;
|
|
21
|
+
const rawClear = table.raw.clear;
|
|
22
|
+
|
|
23
|
+
table.add = async (item: T) => {
|
|
24
|
+
const result = await rawAdd(item);
|
|
25
|
+
emitMutation({ type: 'add', tableName, keys: [result] });
|
|
26
|
+
return result;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
table.put = async (item: T) => {
|
|
30
|
+
const result = await rawPut(item);
|
|
31
|
+
emitMutation({ type: 'update', tableName, keys: [result] });
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
table.update = async (key: unknown, changes: Partial<T>) => {
|
|
36
|
+
const result = await rawUpdate(key, changes);
|
|
37
|
+
if (result > 0) {
|
|
38
|
+
emitMutation({ type: 'update', tableName, keys: [key] });
|
|
39
|
+
}
|
|
40
|
+
return result;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
table.delete = async (key: unknown) => {
|
|
44
|
+
await rawDelete(key);
|
|
45
|
+
emitMutation({ type: 'delete', tableName, keys: [key] });
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
table.bulkAdd = async (items: T[]) => {
|
|
49
|
+
const result = await rawBulkAdd(items);
|
|
50
|
+
if (items.length > 0) {
|
|
51
|
+
emitMutation({ type: 'add', tableName });
|
|
52
|
+
}
|
|
53
|
+
return result;
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
table.bulkPut = async (items: T[]) => {
|
|
57
|
+
const result = await rawBulkPut(items);
|
|
58
|
+
if (items.length > 0) {
|
|
59
|
+
emitMutation({ type: 'update', tableName });
|
|
60
|
+
}
|
|
61
|
+
return result;
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
table.bulkUpdate = async (keysAndChanges: Array<{ key: unknown; changes: Partial<T> }>) => {
|
|
65
|
+
const result = await rawBulkUpdate(keysAndChanges);
|
|
66
|
+
if (result > 0) {
|
|
67
|
+
emitMutation({ type: 'update', tableName, keys: keysAndChanges.map((kc) => kc.key) });
|
|
68
|
+
}
|
|
69
|
+
return result;
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
table.bulkDelete = async (keys: unknown[]) => {
|
|
73
|
+
await rawBulkDelete(keys);
|
|
74
|
+
if (keys.length > 0) {
|
|
75
|
+
emitMutation({ type: 'delete', tableName });
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
table.clear = async () => {
|
|
80
|
+
await rawClear();
|
|
81
|
+
emitMutation({ type: 'delete', tableName });
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
interface SetupOptions {
|
|
86
|
+
owner: object;
|
|
87
|
+
tableCache: Map<string, StorageTable<any>>;
|
|
88
|
+
enhancedTables: Set<string>;
|
|
89
|
+
getTable: (name: string) => StorageTable<any>;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export function setupEnhancedTables({ owner, tableCache, enhancedTables, getTable }: SetupOptions, tableNames: string[]): void {
|
|
93
|
+
for (const tableName of tableNames) {
|
|
94
|
+
tableCache.delete(tableName);
|
|
95
|
+
enhancedTables.delete(tableName);
|
|
96
|
+
|
|
97
|
+
if (!Object.prototype.hasOwnProperty.call(owner, tableName)) {
|
|
98
|
+
Object.defineProperty(owner, tableName, {
|
|
99
|
+
get: () => getTable(tableName),
|
|
100
|
+
enumerable: true,
|
|
101
|
+
configurable: true,
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
interface EnhanceOptions<T> {
|
|
108
|
+
table: StorageTable<T & SyncedRecord>;
|
|
109
|
+
tableName: string;
|
|
110
|
+
withTransaction: WithTransaction;
|
|
111
|
+
state: StateHelpers;
|
|
112
|
+
enhancedTables: Set<string>;
|
|
113
|
+
emitMutation: EmitMutation;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export function enhanceSyncTable<T>({ table, tableName, withTransaction, state, enhancedTables, emitMutation }: EnhanceOptions<T>): void {
|
|
117
|
+
const rawAdd = table.raw.add;
|
|
118
|
+
const rawPut = table.raw.put;
|
|
119
|
+
const rawUpdate = table.raw.update;
|
|
120
|
+
const rawDelete = table.raw.delete;
|
|
121
|
+
const rawBulkAdd = table.raw.bulkAdd;
|
|
122
|
+
const rawBulkPut = table.raw.bulkPut;
|
|
123
|
+
const rawBulkUpdate = table.raw.bulkUpdate;
|
|
124
|
+
const rawBulkDelete = table.raw.bulkDelete;
|
|
125
|
+
const rawClear = table.raw.clear;
|
|
126
|
+
|
|
127
|
+
const wrappedAdd = async (item: any) => {
|
|
128
|
+
let localId = item._localId;
|
|
129
|
+
if (!localId) localId = createLocalId();
|
|
130
|
+
|
|
131
|
+
const syncedItem = {
|
|
132
|
+
...item,
|
|
133
|
+
_localId: localId,
|
|
134
|
+
updated_at: new Date().toISOString(),
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
let result: any;
|
|
138
|
+
await withTransaction('rw', [tableName, DYNC_STATE_TABLE], async () => {
|
|
139
|
+
result = await rawAdd(syncedItem);
|
|
140
|
+
|
|
141
|
+
await state.addPendingChange({
|
|
142
|
+
action: SyncAction.Create,
|
|
143
|
+
stateKey: tableName,
|
|
144
|
+
localId,
|
|
145
|
+
changes: syncedItem,
|
|
146
|
+
before: null,
|
|
147
|
+
after: syncedItem,
|
|
148
|
+
});
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
emitMutation({ type: 'add', tableName, keys: [localId] });
|
|
152
|
+
return result;
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
const wrappedPut = async (item: any) => {
|
|
156
|
+
let localId = item._localId;
|
|
157
|
+
if (!localId) localId = createLocalId();
|
|
158
|
+
|
|
159
|
+
const syncedItem = {
|
|
160
|
+
...item,
|
|
161
|
+
_localId: localId,
|
|
162
|
+
updated_at: new Date().toISOString(),
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
let result: any;
|
|
166
|
+
let isUpdate = false;
|
|
167
|
+
let existingRecord: any;
|
|
168
|
+
|
|
169
|
+
await withTransaction('rw', [tableName, DYNC_STATE_TABLE], async (tables) => {
|
|
170
|
+
const txTable = tables[tableName]!;
|
|
171
|
+
existingRecord = await txTable.get(localId);
|
|
172
|
+
isUpdate = !!existingRecord;
|
|
173
|
+
|
|
174
|
+
result = await rawPut(syncedItem);
|
|
175
|
+
|
|
176
|
+
await state.addPendingChange({
|
|
177
|
+
action: isUpdate ? SyncAction.Update : SyncAction.Create,
|
|
178
|
+
stateKey: tableName,
|
|
179
|
+
localId,
|
|
180
|
+
id: existingRecord?.id,
|
|
181
|
+
changes: syncedItem,
|
|
182
|
+
before: existingRecord ?? null,
|
|
183
|
+
after: syncedItem,
|
|
184
|
+
});
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
emitMutation({ type: isUpdate ? 'update' : 'add', tableName, keys: [localId] });
|
|
188
|
+
return result;
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
const wrappedUpdate = async (key: any, changes: any) => {
|
|
192
|
+
const updatedChanges = {
|
|
193
|
+
...changes,
|
|
194
|
+
updated_at: new Date().toISOString(),
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
let result = 0;
|
|
198
|
+
await withTransaction('rw', [tableName, DYNC_STATE_TABLE], async (tables) => {
|
|
199
|
+
const txTable = tables[tableName]!;
|
|
200
|
+
const record = await txTable.get(key);
|
|
201
|
+
if (!record) {
|
|
202
|
+
throw new Error(`Record with key=${key} not found`);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
result = (await rawUpdate(key, updatedChanges as any)) ?? 0;
|
|
206
|
+
if (result > 0) {
|
|
207
|
+
await state.addPendingChange({
|
|
208
|
+
action: SyncAction.Update,
|
|
209
|
+
stateKey: tableName,
|
|
210
|
+
localId: key,
|
|
211
|
+
id: record.id,
|
|
212
|
+
changes: updatedChanges,
|
|
213
|
+
before: record,
|
|
214
|
+
after: { ...record, ...updatedChanges },
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
if (result > 0) {
|
|
220
|
+
emitMutation({ type: 'update', tableName, keys: [key] });
|
|
221
|
+
}
|
|
222
|
+
return result;
|
|
223
|
+
};
|
|
224
|
+
|
|
225
|
+
const wrappedDelete = async (key: any) => {
|
|
226
|
+
let deletedLocalId: string | undefined;
|
|
227
|
+
await withTransaction('rw', [tableName, DYNC_STATE_TABLE], async (tables) => {
|
|
228
|
+
const txTable = tables[tableName]!;
|
|
229
|
+
const record = await txTable.get(key);
|
|
230
|
+
|
|
231
|
+
await rawDelete(key);
|
|
232
|
+
|
|
233
|
+
if (record) {
|
|
234
|
+
deletedLocalId = record._localId;
|
|
235
|
+
await state.addPendingChange({
|
|
236
|
+
action: SyncAction.Remove,
|
|
237
|
+
stateKey: tableName,
|
|
238
|
+
localId: record._localId,
|
|
239
|
+
id: record.id,
|
|
240
|
+
changes: null,
|
|
241
|
+
before: record,
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
if (deletedLocalId) {
|
|
247
|
+
emitMutation({ type: 'delete', tableName, keys: [deletedLocalId] });
|
|
248
|
+
}
|
|
249
|
+
};
|
|
250
|
+
|
|
251
|
+
const wrappedBulkAdd = async (items: any[]) => {
|
|
252
|
+
if (items.length === 0) return;
|
|
253
|
+
|
|
254
|
+
const now = new Date().toISOString();
|
|
255
|
+
const syncedItems = items.map((item) => {
|
|
256
|
+
const localId = item._localId || createLocalId();
|
|
257
|
+
return {
|
|
258
|
+
...item,
|
|
259
|
+
_localId: localId,
|
|
260
|
+
updated_at: now,
|
|
261
|
+
};
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
let result: any;
|
|
265
|
+
await withTransaction('rw', [tableName, DYNC_STATE_TABLE], async () => {
|
|
266
|
+
result = await rawBulkAdd(syncedItems);
|
|
267
|
+
|
|
268
|
+
for (const syncedItem of syncedItems) {
|
|
269
|
+
await state.addPendingChange({
|
|
270
|
+
action: SyncAction.Create,
|
|
271
|
+
stateKey: tableName,
|
|
272
|
+
localId: syncedItem._localId,
|
|
273
|
+
changes: syncedItem,
|
|
274
|
+
before: null,
|
|
275
|
+
after: syncedItem,
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
emitMutation({ type: 'add', tableName, keys: syncedItems.map((i) => i._localId) });
|
|
281
|
+
return result;
|
|
282
|
+
};
|
|
283
|
+
|
|
284
|
+
const wrappedBulkPut = async (items: any[]) => {
|
|
285
|
+
if (items.length === 0) return;
|
|
286
|
+
|
|
287
|
+
const now = new Date().toISOString();
|
|
288
|
+
const syncedItems = items.map((item) => {
|
|
289
|
+
const localId = item._localId || createLocalId();
|
|
290
|
+
return {
|
|
291
|
+
...item,
|
|
292
|
+
_localId: localId,
|
|
293
|
+
updated_at: now,
|
|
294
|
+
};
|
|
295
|
+
});
|
|
296
|
+
const localIds = syncedItems.map((i) => i._localId);
|
|
297
|
+
|
|
298
|
+
let result: any;
|
|
299
|
+
await withTransaction('rw', [tableName, DYNC_STATE_TABLE], async (tables) => {
|
|
300
|
+
const txTable = tables[tableName]!;
|
|
301
|
+
|
|
302
|
+
// Check which items already exist
|
|
303
|
+
const existingRecords = await txTable.bulkGet(localIds);
|
|
304
|
+
const existingMap = new Map<string, any>();
|
|
305
|
+
for (let i = 0; i < localIds.length; i++) {
|
|
306
|
+
if (existingRecords[i]) {
|
|
307
|
+
existingMap.set(localIds[i], existingRecords[i]);
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
result = await rawBulkPut(syncedItems);
|
|
312
|
+
|
|
313
|
+
for (const syncedItem of syncedItems) {
|
|
314
|
+
const existing = existingMap.get(syncedItem._localId);
|
|
315
|
+
await state.addPendingChange({
|
|
316
|
+
action: existing ? SyncAction.Update : SyncAction.Create,
|
|
317
|
+
stateKey: tableName,
|
|
318
|
+
localId: syncedItem._localId,
|
|
319
|
+
id: existing?.id,
|
|
320
|
+
changes: syncedItem,
|
|
321
|
+
before: existing ?? null,
|
|
322
|
+
after: syncedItem,
|
|
323
|
+
});
|
|
324
|
+
}
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
emitMutation({ type: 'update', tableName, keys: localIds });
|
|
328
|
+
return result;
|
|
329
|
+
};
|
|
330
|
+
|
|
331
|
+
const wrappedBulkUpdate = async (keysAndChanges: Array<{ key: any; changes: any }>) => {
|
|
332
|
+
if (keysAndChanges.length === 0) return 0;
|
|
333
|
+
|
|
334
|
+
const now = new Date().toISOString();
|
|
335
|
+
const updatedKeysAndChanges = keysAndChanges.map(({ key, changes }) => ({
|
|
336
|
+
key,
|
|
337
|
+
changes: {
|
|
338
|
+
...changes,
|
|
339
|
+
updated_at: now,
|
|
340
|
+
},
|
|
341
|
+
}));
|
|
342
|
+
|
|
343
|
+
let result = 0;
|
|
344
|
+
const updatedKeys: string[] = [];
|
|
345
|
+
|
|
346
|
+
await withTransaction('rw', [tableName, DYNC_STATE_TABLE], async (tables) => {
|
|
347
|
+
const txTable = tables[tableName]!;
|
|
348
|
+
|
|
349
|
+
// Get all records before updating
|
|
350
|
+
const keys = updatedKeysAndChanges.map((kc) => kc.key);
|
|
351
|
+
const records = await txTable.bulkGet(keys);
|
|
352
|
+
const recordMap = new Map<string, any>();
|
|
353
|
+
for (let i = 0; i < keys.length; i++) {
|
|
354
|
+
if (records[i]) {
|
|
355
|
+
recordMap.set(String(keys[i]), records[i]);
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
result = await rawBulkUpdate(updatedKeysAndChanges);
|
|
360
|
+
|
|
361
|
+
for (const { key, changes } of updatedKeysAndChanges) {
|
|
362
|
+
const record = recordMap.get(String(key));
|
|
363
|
+
if (record) {
|
|
364
|
+
updatedKeys.push(record._localId);
|
|
365
|
+
await state.addPendingChange({
|
|
366
|
+
action: SyncAction.Update,
|
|
367
|
+
stateKey: tableName,
|
|
368
|
+
localId: record._localId,
|
|
369
|
+
id: record.id,
|
|
370
|
+
changes,
|
|
371
|
+
before: record,
|
|
372
|
+
after: { ...record, ...changes },
|
|
373
|
+
});
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
});
|
|
377
|
+
|
|
378
|
+
if (updatedKeys.length > 0) {
|
|
379
|
+
emitMutation({ type: 'update', tableName, keys: updatedKeys });
|
|
380
|
+
}
|
|
381
|
+
return result;
|
|
382
|
+
};
|
|
383
|
+
|
|
384
|
+
const wrappedBulkDelete = async (keys: any[]) => {
|
|
385
|
+
if (keys.length === 0) return;
|
|
386
|
+
|
|
387
|
+
const deletedLocalIds: string[] = [];
|
|
388
|
+
await withTransaction('rw', [tableName, DYNC_STATE_TABLE], async (tables) => {
|
|
389
|
+
const txTable = tables[tableName]!;
|
|
390
|
+
|
|
391
|
+
// Get all records before deleting
|
|
392
|
+
const records = await txTable.bulkGet(keys);
|
|
393
|
+
|
|
394
|
+
await rawBulkDelete(keys);
|
|
395
|
+
|
|
396
|
+
for (const record of records) {
|
|
397
|
+
if (record) {
|
|
398
|
+
deletedLocalIds.push(record._localId);
|
|
399
|
+
await state.addPendingChange({
|
|
400
|
+
action: SyncAction.Remove,
|
|
401
|
+
stateKey: tableName,
|
|
402
|
+
localId: record._localId,
|
|
403
|
+
id: record.id,
|
|
404
|
+
changes: null,
|
|
405
|
+
before: record,
|
|
406
|
+
});
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
});
|
|
410
|
+
|
|
411
|
+
if (deletedLocalIds.length > 0) {
|
|
412
|
+
emitMutation({ type: 'delete', tableName, keys: deletedLocalIds });
|
|
413
|
+
}
|
|
414
|
+
};
|
|
415
|
+
|
|
416
|
+
const wrappedClear = async () => {
|
|
417
|
+
const deletedLocalIds: string[] = [];
|
|
418
|
+
await withTransaction('rw', [tableName, DYNC_STATE_TABLE], async (tables) => {
|
|
419
|
+
const txTable = tables[tableName]!;
|
|
420
|
+
|
|
421
|
+
// Get all records before clearing
|
|
422
|
+
const allRecords = await txTable.toArray();
|
|
423
|
+
|
|
424
|
+
await rawClear();
|
|
425
|
+
|
|
426
|
+
for (const record of allRecords) {
|
|
427
|
+
if (record._localId) {
|
|
428
|
+
deletedLocalIds.push(record._localId);
|
|
429
|
+
await state.addPendingChange({
|
|
430
|
+
action: SyncAction.Remove,
|
|
431
|
+
stateKey: tableName,
|
|
432
|
+
localId: record._localId,
|
|
433
|
+
id: record.id,
|
|
434
|
+
changes: null,
|
|
435
|
+
before: record,
|
|
436
|
+
});
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
});
|
|
440
|
+
|
|
441
|
+
if (deletedLocalIds.length > 0) {
|
|
442
|
+
emitMutation({ type: 'delete', tableName, keys: deletedLocalIds });
|
|
443
|
+
}
|
|
444
|
+
};
|
|
445
|
+
|
|
446
|
+
table.add = wrappedAdd;
|
|
447
|
+
table.put = wrappedPut;
|
|
448
|
+
table.update = wrappedUpdate;
|
|
449
|
+
table.delete = wrappedDelete;
|
|
450
|
+
table.bulkAdd = wrappedBulkAdd;
|
|
451
|
+
table.bulkPut = wrappedBulkPut;
|
|
452
|
+
table.bulkUpdate = wrappedBulkUpdate;
|
|
453
|
+
table.bulkDelete = wrappedBulkDelete;
|
|
454
|
+
table.clear = wrappedClear;
|
|
455
|
+
|
|
456
|
+
enhancedTables.add(tableName);
|
|
457
|
+
}
|
package/src/dexie.ts
ADDED
package/src/helpers.ts
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { SyncAction } from './types';
|
|
2
|
+
import { createLocalId } from './createLocalId';
|
|
3
|
+
|
|
4
|
+
export { createLocalId };
|
|
5
|
+
|
|
6
|
+
export function sleep(ms: number, signal?: AbortSignal): Promise<void> {
|
|
7
|
+
return new Promise((resolve) => {
|
|
8
|
+
if (signal?.aborted) {
|
|
9
|
+
resolve();
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
const timer = setTimeout(resolve, ms);
|
|
13
|
+
signal?.addEventListener('abort', () => {
|
|
14
|
+
clearTimeout(timer);
|
|
15
|
+
resolve();
|
|
16
|
+
});
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function changeKeysTo(input: any | any[], toIdKey: string, toUpdatedAtKey: string, toDeletedKey: string) {
|
|
21
|
+
if (!input) return input;
|
|
22
|
+
const isArray = Array.isArray(input);
|
|
23
|
+
const result = (isArray ? input : [input]).map((item) => {
|
|
24
|
+
const { id, updated_at, deleted, ...rest } = item;
|
|
25
|
+
return {
|
|
26
|
+
[toIdKey]: id,
|
|
27
|
+
[toUpdatedAtKey]: updated_at,
|
|
28
|
+
[toDeletedKey]: deleted,
|
|
29
|
+
...rest,
|
|
30
|
+
};
|
|
31
|
+
});
|
|
32
|
+
return isArray ? result : result[0];
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function changeKeysFrom(input: any | any[], fromIdKey: string, fromUpdatedAtKey: string, fromDeletedKey: string) {
|
|
36
|
+
if (!input) return input;
|
|
37
|
+
const isArray = Array.isArray(input);
|
|
38
|
+
const result = (isArray ? input : [input]).map((item) => {
|
|
39
|
+
const { [fromIdKey]: id, [fromUpdatedAtKey]: updated_at, [fromDeletedKey]: deleted, ...rest } = item;
|
|
40
|
+
return {
|
|
41
|
+
id,
|
|
42
|
+
updated_at,
|
|
43
|
+
deleted,
|
|
44
|
+
...rest,
|
|
45
|
+
};
|
|
46
|
+
});
|
|
47
|
+
return isArray ? result : result[0];
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function orderFor(a: SyncAction): number {
|
|
51
|
+
switch (a) {
|
|
52
|
+
case SyncAction.Create:
|
|
53
|
+
return 1;
|
|
54
|
+
case SyncAction.Update:
|
|
55
|
+
return 2;
|
|
56
|
+
case SyncAction.Remove:
|
|
57
|
+
return 3;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function omitFields(item: any, fields: string[]) {
|
|
62
|
+
const result = { ...item };
|
|
63
|
+
for (const k of fields) delete result[k];
|
|
64
|
+
return result;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export function addOnSetDo(obj: any, key: string, fn: (v: any) => void): void {
|
|
68
|
+
let value = obj[key];
|
|
69
|
+
|
|
70
|
+
Object.defineProperty(obj, key, {
|
|
71
|
+
get() {
|
|
72
|
+
return value;
|
|
73
|
+
},
|
|
74
|
+
set(newVal) {
|
|
75
|
+
value = newVal;
|
|
76
|
+
fn(value);
|
|
77
|
+
},
|
|
78
|
+
enumerable: true,
|
|
79
|
+
configurable: false,
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export function deleteKeyIfEmptyObject(obj: any, key: string) {
|
|
84
|
+
if (obj[key] && Object.keys(obj[key]).length === 0) {
|
|
85
|
+
delete obj[key];
|
|
86
|
+
}
|
|
87
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export * from './index.shared';
|
|
2
|
+
export { MemoryAdapter } from './storage/memory';
|
|
3
|
+
export { SQLiteAdapter } from './storage/sqlite';
|
|
4
|
+
export { MemoryQueryContext } from './storage/memory';
|
|
5
|
+
export { SqliteQueryContext } from './storage/sqlite';
|
|
6
|
+
export type { SQLiteDatabaseDriver, SQLiteQueryResult, SQLiteRunResult } from './storage/sqlite/types';
|
|
7
|
+
export type { StorageAdapter } from './storage/types';
|
|
8
|
+
export { SyncAction } from './types';
|
|
9
|
+
export { createLocalId } from './helpers';
|
|
10
|
+
|
|
11
|
+
export type {
|
|
12
|
+
AfterRemoteAddCallback,
|
|
13
|
+
ApiFunctions,
|
|
14
|
+
BatchSync,
|
|
15
|
+
BatchPushPayload,
|
|
16
|
+
BatchPushResult,
|
|
17
|
+
BatchFirstLoadResult,
|
|
18
|
+
ConflictResolutionStrategy,
|
|
19
|
+
FirstLoadProgress,
|
|
20
|
+
FirstLoadProgressCallback,
|
|
21
|
+
MissingRemoteRecordStrategy,
|
|
22
|
+
MissingRemoteRecordDuringUpdateCallback,
|
|
23
|
+
MutationEvent,
|
|
24
|
+
SyncOptions,
|
|
25
|
+
SyncState,
|
|
26
|
+
SyncedRecord,
|
|
27
|
+
TableMap,
|
|
28
|
+
} from './types';
|