@anfenn/dync 1.0.1 → 1.0.2

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.
@@ -1,4 +1,4 @@
1
- import { a as ApiFunctions, h as SyncOptions, B as BatchSync, D as Dync, i as SyncState } from '../index.shared-YSn6c01d.cjs';
1
+ import { a as ApiFunctions, h as SyncOptions, B as BatchSync, D as Dync, i as SyncState } from '../index.shared-BGwvMH8f.cjs';
2
2
  import { c as StorageAdapter } from '../dexie-Bv-fV10P.cjs';
3
3
  import '../types-CSbIAfu2.cjs';
4
4
  import 'dexie';
@@ -1,4 +1,4 @@
1
- import { a as ApiFunctions, h as SyncOptions, B as BatchSync, D as Dync, i as SyncState } from '../index.shared-CPIge2ZM.js';
1
+ import { a as ApiFunctions, h as SyncOptions, B as BatchSync, D as Dync, i as SyncState } from '../index.shared-CkYsQYyn.js';
2
2
  import { c as StorageAdapter } from '../dexie-DJFApKsM.js';
3
3
  import '../types-CSbIAfu2.js';
4
4
  import 'dexie';
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  Dync
3
- } from "../chunk-LGHOZECP.js";
3
+ } from "../chunk-66PSQW4D.js";
4
4
  import "../chunk-SQB6E7V2.js";
5
5
 
6
6
  // src/react/useDync.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@anfenn/dync",
3
- "version": "1.0.1",
3
+ "version": "1.0.2",
4
4
  "private": false,
5
5
  "description": "Write once, run IndexedDB & SQLite with sync anywhere - React, React Native, Expo, Capacitor, Electron & Node.js",
6
6
  "keywords": [],
@@ -96,9 +96,6 @@
96
96
  "build": "pnpm run format && pnpm run lint && pnpm exec tsc --noEmit && tsup",
97
97
  "build:all": "pnpm run build && pnpm --filter react-capacitor build",
98
98
  "dev": "tsup --watch",
99
- "dev:capacitor": "pnpm --filter react-capacitor run dev",
100
- "dev:expo:web": "pnpm --filter react-native-expo-sqlite web --clear",
101
- "dev:expo:start": "pnpm --filter react-native-expo-sqlite start --clear",
102
99
  "lint": "eslint . --fix \"src/**/*.{js,jsx,ts,tsx,json}\"",
103
100
  "format": "prettier --write \"**/*.{ts,tsx,md}\"",
104
101
  "size": "pnpm --filter size size",
@@ -30,10 +30,10 @@ export interface StateHelpers {
30
30
  setState(setterOrState: PersistedSyncState | ((state: PersistedSyncState) => Partial<PersistedSyncState>)): Promise<void>;
31
31
  setErrorInMemory(error: Error): void;
32
32
  addPendingChange(change: Omit<PendingChange, 'version'>): Promise<void>;
33
- samePendingVersion(stateKey: string, localId: string, version: number): boolean;
34
- removePendingChange(localId: string, stateKey: string): Promise<void>;
35
- updatePendingChange(stateKey: string, localId: string, action: SyncAction, id?: any): Promise<void>;
36
- setPendingChangeBefore(stateKey: string, localId: string, before: any): Promise<void>;
33
+ samePendingVersion(tableName: string, localId: string, version: number): boolean;
34
+ removePendingChange(localId: string, tableName: string): Promise<void>;
35
+ updatePendingChange(tableName: string, localId: string, action: SyncAction, id?: any): Promise<void>;
36
+ setPendingChangeBefore(tableName: string, localId: string, before: any): Promise<void>;
37
37
  hasConflicts(localId: string): boolean;
38
38
  getSyncStatus(): SyncStatus;
39
39
  setSyncStatus(status: SyncStatus): void;
@@ -111,7 +111,7 @@ export class StateManager implements StateHelpers {
111
111
 
112
112
  addPendingChange(change: Omit<PendingChange, 'version'>): Promise<void> {
113
113
  const next = clonePersistedState(this.persistedState);
114
- const queueItem = next.pendingChanges.find((p) => p.localId === change.localId && p.stateKey === change.stateKey);
114
+ const queueItem = next.pendingChanges.find((p) => p.localId === change.localId && p.tableName === change.tableName);
115
115
 
116
116
  const omittedChanges = omitFields(change.changes, LOCAL_ONLY_SYNC_FIELDS);
117
117
  const omittedBefore = omitFields(change.before, LOCAL_ONLY_SYNC_FIELDS);
@@ -136,7 +136,7 @@ export class StateManager implements StateHelpers {
136
136
  next.pendingChanges = [...next.pendingChanges];
137
137
  next.pendingChanges.push({
138
138
  action,
139
- stateKey: change.stateKey,
139
+ tableName: change.tableName,
140
140
  localId: change.localId,
141
141
  id: change.id,
142
142
  version: 1,
@@ -150,20 +150,20 @@ export class StateManager implements StateHelpers {
150
150
  return this.persist();
151
151
  }
152
152
 
153
- samePendingVersion(stateKey: string, localId: string, version: number): boolean {
154
- return this.persistedState.pendingChanges.find((p) => p.localId === localId && p.stateKey === stateKey)?.version === version;
153
+ samePendingVersion(tableName: string, localId: string, version: number): boolean {
154
+ return this.persistedState.pendingChanges.find((p) => p.localId === localId && p.tableName === tableName)?.version === version;
155
155
  }
156
156
 
157
- removePendingChange(localId: string, stateKey: string): Promise<void> {
157
+ removePendingChange(localId: string, tableName: string): Promise<void> {
158
158
  const next = clonePersistedState(this.persistedState);
159
- next.pendingChanges = next.pendingChanges.filter((p) => !(p.localId === localId && p.stateKey === stateKey));
159
+ next.pendingChanges = next.pendingChanges.filter((p) => !(p.localId === localId && p.tableName === tableName));
160
160
  this.persistedState = next;
161
161
  return this.persist();
162
162
  }
163
163
 
164
- updatePendingChange(stateKey: string, localId: string, action: SyncAction, id?: any): Promise<void> {
164
+ updatePendingChange(tableName: string, localId: string, action: SyncAction, id?: any): Promise<void> {
165
165
  const next = clonePersistedState(this.persistedState);
166
- const changeItem = next.pendingChanges.find((p) => p.stateKey === stateKey && p.localId === localId);
166
+ const changeItem = next.pendingChanges.find((p) => p.tableName === tableName && p.localId === localId);
167
167
  if (changeItem) {
168
168
  changeItem.action = action;
169
169
  if (id) changeItem.id = id;
@@ -173,9 +173,9 @@ export class StateManager implements StateHelpers {
173
173
  return Promise.resolve();
174
174
  }
175
175
 
176
- setPendingChangeBefore(stateKey: string, localId: string, before: any): Promise<void> {
176
+ setPendingChangeBefore(tableName: string, localId: string, before: any): Promise<void> {
177
177
  const next = clonePersistedState(this.persistedState);
178
- const changeItem = next.pendingChanges.find((p) => p.stateKey === stateKey && p.localId === localId);
178
+ const changeItem = next.pendingChanges.find((p) => p.tableName === tableName && p.localId === localId);
179
179
  if (changeItem) {
180
180
  changeItem.before = { ...(changeItem.before ?? {}), ...before };
181
181
  this.persistedState = next;
@@ -259,7 +259,7 @@ function cloneConflicts(conflicts: SyncState['conflicts'] | undefined): SyncStat
259
259
  const next: NonNullable<SyncState['conflicts']> = {};
260
260
  for (const [key, value] of Object.entries(conflicts)) {
261
261
  next[key] = {
262
- stateKey: value.stateKey,
262
+ tableName: value.tableName,
263
263
  fields: value.fields.map((field) => ({ ...field })),
264
264
  };
265
265
  }
@@ -44,14 +44,14 @@ export async function startFirstLoad(ctx: FirstLoadContext): Promise<void> {
44
44
 
45
45
  let error: Error | undefined;
46
46
 
47
- for (const [stateKey, api] of Object.entries(ctx.syncApis)) {
47
+ for (const [tableName, api] of Object.entries(ctx.syncApis)) {
48
48
  if (!api.firstLoad) {
49
- ctx.logger.error(`[dync] firstLoad:no-api-function stateKey=${stateKey}`);
49
+ ctx.logger.error(`[dync] firstLoad:no-api-function tableName=${tableName}`);
50
50
  continue;
51
51
  }
52
52
 
53
53
  try {
54
- ctx.logger.info(`[dync] firstLoad:start stateKey=${stateKey}`);
54
+ ctx.logger.info(`[dync] firstLoad:start tableName=${tableName}`);
55
55
 
56
56
  let lastId: unknown;
57
57
  let isEmptyTable = true;
@@ -66,14 +66,14 @@ export async function startFirstLoad(ctx: FirstLoadContext): Promise<void> {
66
66
  batchCount++;
67
67
 
68
68
  // Process batch in smaller chunks to manage memory and allow UI updates
69
- const { inserted, updated } = await processBatchInChunks(ctx, stateKey, batch, isEmptyTable, lastId === undefined);
69
+ const { inserted, updated } = await processBatchInChunks(ctx, tableName, batch, isEmptyTable, lastId === undefined);
70
70
  totalInserted += inserted;
71
71
  totalUpdated += updated;
72
72
 
73
73
  // Report progress if callback provided
74
74
  if (ctx.onProgress) {
75
75
  ctx.onProgress({
76
- table: stateKey,
76
+ table: tableName,
77
77
  inserted: totalInserted,
78
78
  updated: totalUpdated,
79
79
  total: totalInserted + totalUpdated,
@@ -82,7 +82,7 @@ export async function startFirstLoad(ctx: FirstLoadContext): Promise<void> {
82
82
 
83
83
  // After first batch, we know if table was empty
84
84
  if (lastId === undefined) {
85
- isEmptyTable = (await ctx.table(stateKey).count()) === batch.length;
85
+ isEmptyTable = (await ctx.table(tableName).count()) === batch.length;
86
86
  }
87
87
 
88
88
  if (lastId !== undefined && lastId === batch[batch.length - 1].id) {
@@ -97,10 +97,10 @@ export async function startFirstLoad(ctx: FirstLoadContext): Promise<void> {
97
97
  }
98
98
  }
99
99
 
100
- ctx.logger.info(`[dync] firstLoad:done stateKey=${stateKey} inserted=${totalInserted} updated=${totalUpdated}`);
100
+ ctx.logger.info(`[dync] firstLoad:done tableName=${tableName} inserted=${totalInserted} updated=${totalUpdated}`);
101
101
  } catch (err) {
102
102
  error = error ?? (err as Error);
103
- ctx.logger.error(`[dync] firstLoad:error stateKey=${stateKey}`, err);
103
+ ctx.logger.error(`[dync] firstLoad:error tableName=${tableName}`, err);
104
104
  }
105
105
  }
106
106
 
@@ -120,15 +120,15 @@ interface BatchResult {
120
120
 
121
121
  async function processBatchInChunks(
122
122
  ctx: FirstLoadBaseContext,
123
- stateKey: string,
123
+ tableName: string,
124
124
  batch: RemoteRecord[],
125
125
  isEmptyTable: boolean,
126
126
  isFirstBatch: boolean,
127
127
  ): Promise<BatchResult> {
128
- let newest = new Date(ctx.state.getState().lastPulled[stateKey] || 0);
128
+ let newest = new Date(ctx.state.getState().lastPulled[tableName] || 0);
129
129
 
130
- return ctx.withTransaction('rw', [stateKey, DYNC_STATE_TABLE], async (tables) => {
131
- const txTable = tables[stateKey]!;
130
+ return ctx.withTransaction('rw', [tableName, DYNC_STATE_TABLE], async (tables) => {
131
+ const txTable = tables[tableName]!;
132
132
 
133
133
  // Check if table is empty on first batch
134
134
  let tableIsEmpty = isEmptyTable;
@@ -176,7 +176,7 @@ async function processBatchInChunks(
176
176
  ...syncState,
177
177
  lastPulled: {
178
178
  ...syncState.lastPulled,
179
- [stateKey]: newest.toISOString(),
179
+ [tableName]: newest.toISOString(),
180
180
  },
181
181
  }));
182
182
 
@@ -274,28 +274,28 @@ export async function startFirstLoadBatch(ctx: FirstLoadBatchContext): Promise<v
274
274
  batchCount++;
275
275
 
276
276
  // Process each table's data
277
- for (const [stateKey, batch] of Object.entries(result.data)) {
278
- if (!ctx.batchSync.syncTables.includes(stateKey)) {
279
- ctx.logger.warn(`[dync] firstLoad:batch:unknown-table stateKey=${stateKey}`);
277
+ for (const [tableName, batch] of Object.entries(result.data)) {
278
+ if (!ctx.batchSync.syncTables.includes(tableName)) {
279
+ ctx.logger.warn(`[dync] firstLoad:batch:unknown-table tableName=${tableName}`);
280
280
  continue;
281
281
  }
282
282
 
283
283
  if (!batch?.length) continue;
284
284
 
285
- const isFirstBatch = progress[stateKey]!.inserted === 0 && progress[stateKey]!.updated === 0;
286
- const isEmptyTable = isFirstBatch && (await ctx.table(stateKey).count()) === 0;
285
+ const isFirstBatch = progress[tableName]!.inserted === 0 && progress[tableName]!.updated === 0;
286
+ const isEmptyTable = isFirstBatch && (await ctx.table(tableName).count()) === 0;
287
287
 
288
- const { inserted, updated } = await processBatchInChunks(ctx, stateKey, batch, isEmptyTable, isFirstBatch);
289
- progress[stateKey]!.inserted += inserted;
290
- progress[stateKey]!.updated += updated;
288
+ const { inserted, updated } = await processBatchInChunks(ctx, tableName, batch, isEmptyTable, isFirstBatch);
289
+ progress[tableName]!.inserted += inserted;
290
+ progress[tableName]!.updated += updated;
291
291
 
292
292
  // Report progress if callback provided
293
293
  if (ctx.onProgress) {
294
294
  ctx.onProgress({
295
- table: stateKey,
296
- inserted: progress[stateKey]!.inserted,
297
- updated: progress[stateKey]!.updated,
298
- total: progress[stateKey]!.inserted + progress[stateKey]!.updated,
295
+ table: tableName,
296
+ inserted: progress[tableName]!.inserted,
297
+ updated: progress[tableName]!.updated,
298
+ total: progress[tableName]!.inserted + progress[tableName]!.updated,
299
299
  });
300
300
  }
301
301
  }
@@ -314,8 +314,8 @@ export async function startFirstLoadBatch(ctx: FirstLoadBatchContext): Promise<v
314
314
  }
315
315
 
316
316
  // Log completion for each table
317
- for (const [stateKey, p] of Object.entries(progress)) {
318
- ctx.logger.info(`[dync] firstLoad:batch:done stateKey=${stateKey} inserted=${p.inserted} updated=${p.updated}`);
317
+ for (const [tableName, p] of Object.entries(progress)) {
318
+ ctx.logger.info(`[dync] firstLoad:batch:done tableName=${tableName} inserted=${p.inserted} updated=${p.updated}`);
319
319
  }
320
320
  } catch (err) {
321
321
  error = err as Error;
@@ -25,30 +25,30 @@ export interface PullAllBatchContext extends PullContext {
25
25
  export async function pullAll(ctx: PullAllContext): Promise<{ error?: Error; changedTables: string[] }> {
26
26
  let firstSyncError: Error | undefined;
27
27
  const changedTables: string[] = [];
28
- for (const [stateKey, api] of Object.entries(ctx.syncApis)) {
28
+ for (const [tableName, api] of Object.entries(ctx.syncApis)) {
29
29
  try {
30
- const lastPulled = ctx.state.getState().lastPulled[stateKey];
30
+ const lastPulled = ctx.state.getState().lastPulled[tableName];
31
31
  const since = lastPulled ? new Date(lastPulled) : new Date(0);
32
32
 
33
- ctx.logger.debug(`[dync] pull:start stateKey=${stateKey} since=${since.toISOString()}`);
33
+ ctx.logger.debug(`[dync] pull:start tableName=${tableName} since=${since.toISOString()}`);
34
34
 
35
35
  const serverData = (await api.list(since)) as SyncedRecord[];
36
- const changed = await processPullData(stateKey, serverData, since, ctx);
37
- if (changed) changedTables.push(stateKey);
36
+ const changed = await processPullData(tableName, serverData, since, ctx);
37
+ if (changed) changedTables.push(tableName);
38
38
  } catch (err) {
39
39
  firstSyncError = firstSyncError ?? (err as Error);
40
- ctx.logger.error(`[dync] pull:error stateKey=${stateKey}`, err);
40
+ ctx.logger.error(`[dync] pull:error tableName=${tableName}`, err);
41
41
  }
42
42
  }
43
43
  return { error: firstSyncError, changedTables };
44
44
  }
45
45
 
46
- async function handleRemoteItemUpdate(table: StorageTable<any>, stateKey: string, localItem: any, remote: any, ctx: PullContext): Promise<void> {
47
- const pendingChange = ctx.state.getState().pendingChanges.find((p) => p.stateKey === stateKey && p.localId === localItem._localId);
46
+ async function handleRemoteItemUpdate(table: StorageTable<any>, tableName: string, localItem: any, remote: any, ctx: PullContext): Promise<void> {
47
+ const pendingChange = ctx.state.getState().pendingChanges.find((p) => p.tableName === tableName && p.localId === localItem._localId);
48
48
  const conflictStrategy = ctx.conflictResolutionStrategy;
49
49
 
50
50
  if (pendingChange) {
51
- ctx.logger.debug(`[dync] pull:conflict-strategy:${conflictStrategy} stateKey=${stateKey} id=${remote.id}`);
51
+ ctx.logger.debug(`[dync] pull:conflict-strategy:${conflictStrategy} tableName=${tableName} id=${remote.id}`);
52
52
 
53
53
  switch (conflictStrategy) {
54
54
  case 'local-wins':
@@ -57,7 +57,7 @@ async function handleRemoteItemUpdate(table: StorageTable<any>, stateKey: string
57
57
  case 'remote-wins': {
58
58
  const merged = { ...remote, _localId: localItem._localId };
59
59
  await table.raw.update(localItem._localId, merged);
60
- await ctx.state.removePendingChange(localItem._localId, stateKey);
60
+ await ctx.state.removePendingChange(localItem._localId, tableName);
61
61
  break;
62
62
  }
63
63
 
@@ -75,7 +75,7 @@ async function handleRemoteItemUpdate(table: StorageTable<any>, stateKey: string
75
75
  ...syncState,
76
76
  conflicts: {
77
77
  ...(syncState.conflicts || {}),
78
- [localItem._localId]: { stateKey, fields },
78
+ [localItem._localId]: { tableName, fields },
79
79
  },
80
80
  }));
81
81
  } else {
@@ -101,7 +101,7 @@ async function handleRemoteItemUpdate(table: StorageTable<any>, stateKey: string
101
101
  } else {
102
102
  const merged = { ...localItem, ...remote };
103
103
  await table.raw.update(localItem._localId, merged);
104
- ctx.logger.debug(`[dync] pull:merge-remote stateKey=${stateKey} id=${remote.id}`);
104
+ ctx.logger.debug(`[dync] pull:merge-remote tableName=${tableName} id=${remote.id}`);
105
105
  }
106
106
  }
107
107
 
@@ -127,18 +127,18 @@ export async function pullAllBatch(ctx: PullAllBatchContext): Promise<{ error?:
127
127
  const serverDataByTable = await ctx.batchSync.pull(sinceMap);
128
128
 
129
129
  // Process each table's data
130
- for (const [stateKey, serverData] of Object.entries(serverDataByTable)) {
131
- if (!ctx.batchSync.syncTables.includes(stateKey)) {
132
- ctx.logger.warn(`[dync] pull:batch:unknown-table stateKey=${stateKey}`);
130
+ for (const [tableName, serverData] of Object.entries(serverDataByTable)) {
131
+ if (!ctx.batchSync.syncTables.includes(tableName)) {
132
+ ctx.logger.warn(`[dync] pull:batch:unknown-table tableName=${tableName}`);
133
133
  continue;
134
134
  }
135
135
 
136
136
  try {
137
- const changed = await processPullData(stateKey, serverData as SyncedRecord[], sinceMap[stateKey]!, ctx);
138
- if (changed) changedTables.push(stateKey);
137
+ const changed = await processPullData(tableName, serverData as SyncedRecord[], sinceMap[tableName]!, ctx);
138
+ if (changed) changedTables.push(tableName);
139
139
  } catch (err) {
140
140
  firstSyncError = firstSyncError ?? (err as Error);
141
- ctx.logger.error(`[dync] pull:batch:error stateKey=${stateKey}`, err);
141
+ ctx.logger.error(`[dync] pull:batch:error tableName=${tableName}`, err);
142
142
  }
143
143
  }
144
144
  } catch (err) {
@@ -149,20 +149,20 @@ export async function pullAllBatch(ctx: PullAllBatchContext): Promise<{ error?:
149
149
  return { error: firstSyncError, changedTables };
150
150
  }
151
151
 
152
- async function processPullData(stateKey: string, serverData: SyncedRecord[], since: Date, ctx: PullContext): Promise<boolean> {
152
+ async function processPullData(tableName: string, serverData: SyncedRecord[], since: Date, ctx: PullContext): Promise<boolean> {
153
153
  if (!serverData?.length) return false;
154
154
 
155
- ctx.logger.debug(`[dync] pull:process stateKey=${stateKey} count=${serverData.length}`);
155
+ ctx.logger.debug(`[dync] pull:process tableName=${tableName} count=${serverData.length}`);
156
156
 
157
157
  let newest = since;
158
158
  let hasChanges = false;
159
159
 
160
- await ctx.withTransaction('rw', [stateKey, DYNC_STATE_TABLE], async (tables) => {
161
- const txTable = tables[stateKey]!;
160
+ await ctx.withTransaction('rw', [tableName, DYNC_STATE_TABLE], async (tables) => {
161
+ const txTable = tables[tableName]!;
162
162
  const pendingRemovalById = new Set(
163
163
  ctx.state
164
164
  .getState()
165
- .pendingChanges.filter((p) => p.stateKey === stateKey && p.action === SyncAction.Remove)
165
+ .pendingChanges.filter((p) => p.tableName === tableName && p.action === SyncAction.Remove)
166
166
  .map((p) => p.id),
167
167
  );
168
168
 
@@ -171,7 +171,7 @@ async function processPullData(stateKey: string, serverData: SyncedRecord[], sin
171
171
  if (remoteUpdated > newest) newest = remoteUpdated;
172
172
 
173
173
  if (pendingRemovalById.has(remote.id)) {
174
- ctx.logger.debug(`[dync] pull:skip-pending-remove stateKey=${stateKey} id=${remote.id}`);
174
+ ctx.logger.debug(`[dync] pull:skip-pending-remove tableName=${tableName} id=${remote.id}`);
175
175
  continue;
176
176
  }
177
177
 
@@ -180,7 +180,7 @@ async function processPullData(stateKey: string, serverData: SyncedRecord[], sin
180
180
  if (remote.deleted) {
181
181
  if (localItem) {
182
182
  await txTable.raw.delete(localItem._localId);
183
- ctx.logger.debug(`[dync] pull:remove stateKey=${stateKey} id=${remote.id}`);
183
+ ctx.logger.debug(`[dync] pull:remove tableName=${tableName} id=${remote.id}`);
184
184
  hasChanges = true;
185
185
  }
186
186
  continue;
@@ -189,12 +189,12 @@ async function processPullData(stateKey: string, serverData: SyncedRecord[], sin
189
189
  delete remote.deleted;
190
190
 
191
191
  if (localItem) {
192
- await handleRemoteItemUpdate(txTable, stateKey, localItem, remote, ctx);
192
+ await handleRemoteItemUpdate(txTable, tableName, localItem, remote, ctx);
193
193
  hasChanges = true;
194
194
  } else {
195
195
  const newLocalItem = { ...remote, _localId: createLocalId() };
196
196
  await txTable.raw.add(newLocalItem);
197
- ctx.logger.debug(`[dync] pull:add stateKey=${stateKey} id=${remote.id}`);
197
+ ctx.logger.debug(`[dync] pull:add tableName=${tableName} id=${remote.id}`);
198
198
  hasChanges = true;
199
199
  }
200
200
  }
@@ -203,7 +203,7 @@ async function processPullData(stateKey: string, serverData: SyncedRecord[], sin
203
203
  ...syncState,
204
204
  lastPulled: {
205
205
  ...syncState.lastPulled,
206
- [stateKey]: newest.toISOString(),
206
+ [tableName]: newest.toISOString(),
207
207
  },
208
208
  }));
209
209
  });
@@ -23,40 +23,40 @@ export interface PushAllBatchContext extends PushContext {
23
23
  }
24
24
 
25
25
  async function handleRemoveSuccess(change: PendingChange, ctx: PushContext): Promise<void> {
26
- const { stateKey, localId, id } = change;
27
- ctx.logger.debug(`[dync] push:remove:success stateKey=${stateKey} localId=${localId} id=${id}`);
28
- await ctx.state.removePendingChange(localId, stateKey);
26
+ const { tableName, localId, id } = change;
27
+ ctx.logger.debug(`[dync] push:remove:success tableName=${tableName} localId=${localId} id=${id}`);
28
+ await ctx.state.removePendingChange(localId, tableName);
29
29
  }
30
30
 
31
31
  async function handleUpdateSuccess(change: PendingChange, ctx: PushContext): Promise<void> {
32
- const { stateKey, localId, version, changes } = change;
33
- ctx.logger.debug(`[dync] push:update:success stateKey=${stateKey} localId=${localId} id=${change.id}`);
34
- if (ctx.state.samePendingVersion(stateKey, localId, version)) {
35
- await ctx.state.removePendingChange(localId, stateKey);
32
+ const { tableName, localId, version, changes } = change;
33
+ ctx.logger.debug(`[dync] push:update:success tableName=${tableName} localId=${localId} id=${change.id}`);
34
+ if (ctx.state.samePendingVersion(tableName, localId, version)) {
35
+ await ctx.state.removePendingChange(localId, tableName);
36
36
  } else {
37
- await ctx.state.setPendingChangeBefore(stateKey, localId, changes);
37
+ await ctx.state.setPendingChangeBefore(tableName, localId, changes);
38
38
  }
39
39
  }
40
40
 
41
41
  async function handleCreateSuccess(change: PendingChange, serverResult: { id: unknown; updated_at?: string }, ctx: PushContext): Promise<void> {
42
- const { stateKey, localId, version, changes, id } = change;
43
- ctx.logger.debug(`[dync] push:create:success stateKey=${stateKey} localId=${localId} id=${id ?? serverResult.id}`);
42
+ const { tableName, localId, version, changes, id } = change;
43
+ ctx.logger.debug(`[dync] push:create:success tableName=${tableName} localId=${localId} id=${id ?? serverResult.id}`);
44
44
 
45
- await ctx.withTransaction('rw', [stateKey, DYNC_STATE_TABLE], async (tables) => {
46
- const txTable = tables[stateKey]!;
45
+ await ctx.withTransaction('rw', [tableName, DYNC_STATE_TABLE], async (tables) => {
46
+ const txTable = tables[tableName]!;
47
47
  const wasChanged = (await txTable.raw.update(localId, serverResult)) ?? 0;
48
48
 
49
- if (wasChanged && ctx.state.samePendingVersion(stateKey, localId, version)) {
50
- await ctx.state.removePendingChange(localId, stateKey);
49
+ if (wasChanged && ctx.state.samePendingVersion(tableName, localId, version)) {
50
+ await ctx.state.removePendingChange(localId, tableName);
51
51
  } else {
52
52
  const nextAction = wasChanged ? SyncAction.Update : SyncAction.Remove;
53
- await ctx.state.updatePendingChange(stateKey, localId, nextAction, serverResult.id);
53
+ await ctx.state.updatePendingChange(tableName, localId, nextAction, serverResult.id);
54
54
  if (nextAction === SyncAction.Remove) return;
55
55
  }
56
56
  });
57
57
 
58
58
  const finalItem = { ...changes, ...serverResult, _localId: localId };
59
- ctx.syncOptions.onAfterRemoteAdd?.(stateKey, finalItem);
59
+ ctx.syncOptions.onAfterRemoteAdd?.(tableName, finalItem);
60
60
  }
61
61
 
62
62
  export async function pushAll(ctx: PushAllContext): Promise<Error | undefined> {
@@ -75,18 +75,18 @@ export async function pushAll(ctx: PushAllContext): Promise<Error | undefined> {
75
75
  }
76
76
 
77
77
  async function pushOne(change: PendingChange, ctx: PushAllContext): Promise<void> {
78
- const api = ctx.syncApis[change.stateKey];
78
+ const api = ctx.syncApis[change.tableName];
79
79
  if (!api) return;
80
80
 
81
- ctx.logger.debug(`[dync] push:attempt action=${change.action} stateKey=${change.stateKey} localId=${change.localId}`);
81
+ ctx.logger.debug(`[dync] push:attempt action=${change.action} tableName=${change.tableName} localId=${change.localId}`);
82
82
 
83
- const { action, stateKey, localId, id, changes, after } = change;
83
+ const { action, tableName, localId, id, changes, after } = change;
84
84
 
85
85
  switch (action) {
86
86
  case SyncAction.Remove:
87
87
  if (!id) {
88
- ctx.logger.warn(`[dync] push:remove:no-id stateKey=${stateKey} localId=${localId}`);
89
- await ctx.state.removePendingChange(localId, stateKey);
88
+ ctx.logger.warn(`[dync] push:remove:no-id tableName=${tableName} localId=${localId}`);
89
+ await ctx.state.removePendingChange(localId, tableName);
90
90
  return;
91
91
  }
92
92
  await api.remove(id);
@@ -95,7 +95,7 @@ async function pushOne(change: PendingChange, ctx: PushAllContext): Promise<void
95
95
 
96
96
  case SyncAction.Update: {
97
97
  if (ctx.state.hasConflicts(localId)) {
98
- ctx.logger.warn(`[dync] push:update:skipping-with-conflicts stateKey=${stateKey} localId=${localId} id=${id}`);
98
+ ctx.logger.warn(`[dync] push:update:skipping-with-conflicts tableName=${tableName} localId=${localId} id=${id}`);
99
99
  return;
100
100
  }
101
101
 
@@ -113,9 +113,9 @@ async function pushOne(change: PendingChange, ctx: PushAllContext): Promise<void
113
113
  if (result) {
114
114
  await handleCreateSuccess(change, result, ctx);
115
115
  } else {
116
- ctx.logger.warn(`[dync] push:create:no-result stateKey=${stateKey} localId=${localId} id=${id}`);
117
- if (ctx.state.samePendingVersion(stateKey, localId, change.version)) {
118
- await ctx.state.removePendingChange(localId, stateKey);
116
+ ctx.logger.warn(`[dync] push:create:no-result tableName=${tableName} localId=${localId} id=${id}`);
117
+ if (ctx.state.samePendingVersion(tableName, localId, change.version)) {
118
+ await ctx.state.removePendingChange(localId, tableName);
119
119
  }
120
120
  }
121
121
  break;
@@ -124,25 +124,25 @@ async function pushOne(change: PendingChange, ctx: PushAllContext): Promise<void
124
124
  }
125
125
 
126
126
  async function handleMissingRemoteRecord(change: PendingChange, ctx: PushContext): Promise<void> {
127
- const { stateKey, localId } = change;
127
+ const { tableName, localId } = change;
128
128
  const strategy = ctx.syncOptions.missingRemoteRecordDuringUpdateStrategy!;
129
129
 
130
130
  let localItem: any;
131
131
 
132
- await ctx.withTransaction('rw', [stateKey, DYNC_STATE_TABLE], async (tables) => {
133
- const txTable = tables[stateKey]!;
132
+ await ctx.withTransaction('rw', [tableName, DYNC_STATE_TABLE], async (tables) => {
133
+ const txTable = tables[tableName]!;
134
134
  localItem = await txTable.get(localId);
135
135
 
136
136
  if (!localItem) {
137
- ctx.logger.warn(`[dync] push:missing-remote:no-local-item stateKey=${stateKey} localId=${localId}`);
138
- await ctx.state.removePendingChange(localId, stateKey);
137
+ ctx.logger.warn(`[dync] push:missing-remote:no-local-item tableName=${tableName} localId=${localId}`);
138
+ await ctx.state.removePendingChange(localId, tableName);
139
139
  return;
140
140
  }
141
141
 
142
142
  switch (strategy) {
143
143
  case 'delete-local-record':
144
144
  await txTable.raw.delete(localId);
145
- ctx.logger.debug(`[dync] push:missing-remote:${strategy} stateKey=${stateKey} id=${localItem.id}`);
145
+ ctx.logger.debug(`[dync] push:missing-remote:${strategy} tableName=${tableName} id=${localItem.id}`);
146
146
  break;
147
147
 
148
148
  case 'insert-remote-record': {
@@ -157,26 +157,26 @@ async function handleMissingRemoteRecord(change: PendingChange, ctx: PushContext
157
157
 
158
158
  await ctx.state.addPendingChange({
159
159
  action: SyncAction.Create,
160
- stateKey,
160
+ tableName,
161
161
  localId: newItem._localId,
162
162
  changes: newItem,
163
163
  before: null,
164
164
  });
165
165
 
166
- ctx.logger.debug(`[dync] push:missing-remote:${strategy} stateKey=${stateKey} id=${newItem.id}`);
166
+ ctx.logger.debug(`[dync] push:missing-remote:${strategy} tableName=${tableName} id=${newItem.id}`);
167
167
  break;
168
168
  }
169
169
 
170
170
  case 'ignore':
171
- ctx.logger.debug(`[dync] push:missing-remote:${strategy} stateKey=${stateKey} id=${localItem.id}`);
171
+ ctx.logger.debug(`[dync] push:missing-remote:${strategy} tableName=${tableName} id=${localItem.id}`);
172
172
  break;
173
173
 
174
174
  default:
175
- ctx.logger.error(`[dync] push:missing-remote:unknown-strategy stateKey=${stateKey} id=${localItem.id} strategy=${strategy}`);
175
+ ctx.logger.error(`[dync] push:missing-remote:unknown-strategy tableName=${tableName} id=${localItem.id} strategy=${strategy}`);
176
176
  break;
177
177
  }
178
178
 
179
- await ctx.state.removePendingChange(localId, stateKey);
179
+ await ctx.state.removePendingChange(localId, tableName);
180
180
  });
181
181
 
182
182
  ctx.syncOptions.onAfterMissingRemoteRecordDuringUpdate?.(strategy, localItem);
@@ -191,7 +191,7 @@ export async function pushAllBatch(ctx: PushAllBatchContext): Promise<Error | un
191
191
 
192
192
  try {
193
193
  const changesSnapshot = [...ctx.state.getState().pendingChanges]
194
- .filter((change) => ctx.batchSync.syncTables.includes(change.stateKey))
194
+ .filter((change) => ctx.batchSync.syncTables.includes(change.tableName))
195
195
  .sort((a, b) => orderFor(a.action) - orderFor(b.action));
196
196
 
197
197
  if (changesSnapshot.length === 0) {
@@ -202,7 +202,7 @@ export async function pushAllBatch(ctx: PushAllBatchContext): Promise<Error | un
202
202
  // Filter out changes with conflicts
203
203
  const changesToPush = changesSnapshot.filter((change) => {
204
204
  if (change.action === SyncAction.Update && ctx.state.hasConflicts(change.localId)) {
205
- ctx.logger.warn(`[dync] push:batch:skipping-with-conflicts stateKey=${change.stateKey} localId=${change.localId}`);
205
+ ctx.logger.warn(`[dync] push:batch:skipping-with-conflicts tableName=${change.tableName} localId=${change.localId}`);
206
206
  return false;
207
207
  }
208
208
  return true;
@@ -215,7 +215,7 @@ export async function pushAllBatch(ctx: PushAllBatchContext): Promise<Error | un
215
215
 
216
216
  // Build batch payload
217
217
  const payloads: BatchPushPayload[] = changesToPush.map((change) => ({
218
- table: change.stateKey,
218
+ table: change.tableName,
219
219
  action: change.action === SyncAction.Create ? 'add' : change.action === SyncAction.Update ? 'update' : 'remove',
220
220
  localId: change.localId,
221
221
  id: change.id,
@@ -257,14 +257,14 @@ export async function pushAllBatch(ctx: PushAllBatchContext): Promise<Error | un
257
257
  }
258
258
 
259
259
  async function processBatchPushResult(change: PendingChange, result: BatchPushResult, ctx: PushAllBatchContext): Promise<void> {
260
- const { action, stateKey, localId } = change;
260
+ const { action, tableName, localId } = change;
261
261
 
262
262
  if (!result.success) {
263
263
  if (action === SyncAction.Update) {
264
264
  // Update failed - might be missing remote record
265
265
  await handleMissingRemoteRecord(change, ctx);
266
266
  } else {
267
- ctx.logger.warn(`[dync] push:batch:failed stateKey=${stateKey} localId=${localId} error=${result.error}`);
267
+ ctx.logger.warn(`[dync] push:batch:failed tableName=${tableName} localId=${localId} error=${result.error}`);
268
268
  }
269
269
  return;
270
270
  }