@anfenn/zync 0.3.4 → 0.4.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/README.md +25 -20
- package/dist/index.cjs +178 -76
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +19 -6
- package/dist/index.d.ts +19 -6
- package/dist/index.js +175 -75
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/logger.ts","../src/helpers.ts","../src/pull.ts","../src/push.ts","../src/firstLoad.ts","../src/indexedDBStorage.ts"],"sourcesContent":["import { create, type StateCreator } from 'zustand';\nimport { persist } from 'zustand/middleware';\nimport { newLogger, type Logger, type LogLevel } from './logger';\nimport { orderFor, findApi, findChanges, tryAddToPendingChanges } from './helpers';\nimport type {\n ApiFunctions,\n SyncOptions,\n SyncState,\n SyncedStateCreator,\n PendingChange,\n UseStoreWithSync,\n MissingRemoteRecordStrategy,\n ConflictResolutionStrategy,\n} from './types';\nimport { pull } from './pull';\nimport { pushOne } from './push';\nimport { startFirstLoad } from './firstLoad';\n\nexport { createIndexedDBStorage } from './indexedDBStorage';\nexport { nextLocalId } from './helpers';\nexport type { ApiFunctions, UseStoreWithSync, SyncState } from './types';\n\nexport enum SyncAction {\n Create = 'create',\n Update = 'update',\n Remove = 'remove',\n}\n\nconst DEFAULT_SYNC_INTERVAL_MILLIS = 5000;\nconst DEFAULT_LOGGER: Logger = console;\nconst DEFAULT_MIN_LOG_LEVEL: LogLevel = 'debug';\nconst DEFAULT_MISSING_REMOTE_RECORD_STRATEGY: MissingRemoteRecordStrategy = 'ignore';\nconst DEFAULT_CONFLICT_RESOLUTION_STRATEGY: ConflictResolutionStrategy = 'local-wins';\n\nexport function createWithSync<TStore extends object>(\n stateCreator: SyncedStateCreator<TStore>,\n persistOptions: any,\n syncApi: Record<string, ApiFunctions>,\n syncOptions: SyncOptions = {},\n): Promise<UseStoreWithSync<TStore>> {\n const store = create(persistWithSync(stateCreator, persistOptions, syncApi, syncOptions)) as UseStoreWithSync<TStore>;\n\n return new Promise<UseStoreWithSync<TStore>>((resolve) => {\n store.persist.onFinishHydration((_state) => {\n resolve(store);\n });\n });\n}\n\nexport function persistWithSync<TStore extends object>(\n stateCreator: SyncedStateCreator<TStore>,\n persistOptions: any,\n syncApi: Record<string, ApiFunctions>,\n syncOptions: SyncOptions = {},\n) {\n const syncInterval = syncOptions.syncInterval ?? DEFAULT_SYNC_INTERVAL_MILLIS;\n const missingStrategy = syncOptions.missingRemoteRecordDuringUpdateStrategy ?? DEFAULT_MISSING_REMOTE_RECORD_STRATEGY;\n const conflictResolutionStrategy = syncOptions.conflictResolutionStrategy ?? DEFAULT_CONFLICT_RESOLUTION_STRATEGY;\n const logger = newLogger(syncOptions.logger ?? DEFAULT_LOGGER, syncOptions.minLogLevel ?? DEFAULT_MIN_LOG_LEVEL);\n\n const baseOnRehydrate = persistOptions?.onRehydrateStorage;\n const basePartialize = persistOptions?.partialize;\n\n const wrappedPersistOptions = {\n ...persistOptions,\n onRehydrateStorage: () => {\n logger.debug('[zync] rehydration:start');\n\n return (state: any, error: any) => {\n if (error) {\n logger.error('[zync] rehydration:failed', error);\n } else {\n baseOnRehydrate?.(state, error);\n logger.debug('[zync] rehydration:complete', state);\n }\n };\n },\n partialize: (s: any) => {\n // Select state to be persisted\n\n const base = basePartialize ? basePartialize(s) : s;\n const { syncState, ...rest } = base || {};\n return {\n ...rest,\n syncState: {\n firstLoadDone: syncState.firstLoadDone,\n pendingChanges: syncState.pendingChanges,\n lastPulled: syncState.lastPulled,\n },\n };\n },\n merge: (persisted: any, current: any) => {\n // Here after hydration.\n // `persisted` is state from storage that's just loaded (possibly asynchronously e.g. IndexedDB)\n // `current` is what the user has defined (they may have added or removed state keys)\n // Zync is designed to not be used until hydration is complete, so we don't expect to have to\n // merge user mutated state (i.e. current) into persisted. So we do the Zustand recommended pattern of\n // shallow copy where persisted keys win:\n const state = { ...current, ...persisted };\n\n return {\n ...state,\n syncState: {\n ...state.syncState,\n status: 'idle', // this confirms 'hydrating' is done\n },\n };\n },\n };\n\n const creator: StateCreator<TStore & SyncState, [], []> = (set: any, get: any, storeApi: any) => {\n let syncIntervalId: any;\n\n async function syncOnce() {\n const state: SyncState = get();\n if (!state.syncState.enabled || state.syncState.status !== 'idle') return;\n\n set((state: any) => ({\n syncState: {\n ...(state.syncState || {}),\n status: 'syncing',\n },\n }));\n\n let firstSyncError: Error | undefined;\n\n // 1) PULL for each stateKey\n for (const stateKey of Object.keys(syncApi)) {\n try {\n const api = findApi(stateKey, syncApi);\n await pull(set, get, stateKey, api, logger, conflictResolutionStrategy);\n } catch (err) {\n firstSyncError = firstSyncError ?? (err as Error);\n logger.error(`[zync] pull:error stateKey=${stateKey}`, err);\n }\n }\n\n // 2) PUSH queued changes\n const changesSnapshot: PendingChange[] = [...(get().syncState.pendingChanges || [])];\n\n // Deterministic ordering: Create -> Update -> Remove so dependencies (e.g. id assignment) happen first\n changesSnapshot.sort((a, b) => orderFor(a.action) - orderFor(b.action));\n\n for (const change of changesSnapshot) {\n try {\n const api = findApi(change.stateKey, syncApi);\n await pushOne(\n set,\n get,\n change,\n api,\n logger,\n setAndQueueToSync,\n missingStrategy,\n syncOptions.onMissingRemoteRecordDuringUpdate,\n syncOptions.onAfterRemoteAdd,\n );\n } catch (err) {\n firstSyncError = firstSyncError ?? (err as Error);\n logger.error(`[zync] push:error change=${change}`, err);\n }\n }\n\n set((state: any) => ({\n syncState: {\n ...(state.syncState || {}),\n status: 'idle',\n error: firstSyncError,\n },\n }));\n\n if (get().syncState.pendingChanges.length > 0 && !firstSyncError) {\n // If there are pending changes and no sync error, we can sync again\n await syncOnce();\n }\n }\n\n function setAndSyncOnce(partial: any) {\n if (typeof partial === 'function') {\n set((state: any) => ({ ...partial(state) }));\n } else {\n set(partial);\n }\n syncOnce();\n }\n\n function setAndQueueToSync(partial: any) {\n if (typeof partial === 'function') {\n set((state: any) => newSyncState(state, partial(state)));\n } else {\n set((state: any) => newSyncState(state, partial));\n }\n syncOnce();\n }\n\n function newSyncState(state: any, partial: any) {\n const pendingChanges: PendingChange[] = state.syncState.pendingChanges || [];\n\n Object.keys(partial).map((stateKey) => {\n const current = state[stateKey];\n const updated = partial[stateKey];\n const changes = findChanges(current, updated); // find additions, deletions & updates\n tryAddToPendingChanges(pendingChanges, stateKey, changes);\n });\n\n return {\n ...partial,\n syncState: {\n ...(state.syncState || {}),\n pendingChanges,\n },\n };\n }\n\n function enable(enabled: boolean) {\n set((state: any) => ({\n syncState: {\n ...(state.syncState || {}),\n enabled,\n },\n }));\n\n enableSyncTimer(enabled);\n addVisibilityChangeListener(enabled);\n }\n\n function enableSyncTimer(enabled: boolean) {\n clearInterval(syncIntervalId);\n syncIntervalId = undefined;\n if (enabled) {\n syncIntervalId = setInterval(syncOnce, syncInterval);\n syncOnce();\n }\n }\n\n function addVisibilityChangeListener(add: boolean) {\n if (add) {\n document.addEventListener('visibilitychange', onVisibilityChange);\n } else {\n document.removeEventListener('visibilitychange', onVisibilityChange);\n }\n }\n\n function onVisibilityChange() {\n if (document.visibilityState === 'visible') {\n logger.debug('[zync] sync:start-in-foreground');\n enableSyncTimer(true);\n } else {\n logger.debug('[zync] sync:pause-in-background');\n enableSyncTimer(false);\n }\n }\n\n // public useStore.sync api, similar in principle to useStore.persist\n storeApi.sync = {\n enable,\n startFirstLoad: () => startFirstLoad(set, syncApi, logger),\n };\n\n const userState = stateCreator(setAndSyncOnce, get, setAndQueueToSync) as TStore;\n\n return {\n ...userState,\n syncState: {\n // set defaults\n status: 'hydrating',\n error: undefined,\n enabled: false,\n firstLoadDone: false,\n pendingChanges: [],\n lastPulled: {},\n },\n } as TStore & SyncState;\n };\n\n return persist(creator, wrappedPersistOptions);\n}\n","export type LogLevel = 'debug' | 'info' | 'warn' | 'error' | 'none';\n\nexport interface Logger {\n debug: (...args: any[]) => void;\n info: (...args: any[]) => void;\n warn: (...args: any[]) => void;\n error: (...args: any[]) => void;\n}\n\nexport function newLogger(base: Logger, min: LogLevel): Logger {\n const order: Record<LogLevel, number> = {\n debug: 10,\n info: 20,\n warn: 30,\n error: 40,\n none: 100,\n };\n const threshold = order[min];\n const enabled = (lvl: LogLevel) => order[lvl] >= threshold;\n return {\n debug: (...a: any[]) => enabled('debug') && base.debug?.(...a),\n info: (...a: any[]) => enabled('info') && base.info?.(...a),\n warn: (...a: any[]) => enabled('warn') && base.warn?.(...a),\n error: (...a: any[]) => enabled('error') && base.error?.(...a),\n };\n}\n","import { SyncAction } from './index';\nimport type { ApiFunctions, PendingChange } from './types';\n\nconst SYNC_FIELDS = ['_localId', 'updated_at', 'deleted'] as const;\n\nexport function nextLocalId(): string {\n return crypto.randomUUID();\n}\n\nexport function orderFor(a: SyncAction): number {\n switch (a) {\n case SyncAction.Create:\n return 1;\n case SyncAction.Update:\n return 2;\n case SyncAction.Remove:\n return 3;\n }\n}\n\nexport function omitSyncFields(item: any) {\n const result = { ...item };\n for (const k of SYNC_FIELDS) delete result[k];\n return result;\n}\n\nexport function samePendingVersion(get: any, stateKey: string, localId: string, version: number): boolean {\n const q: PendingChange[] = get().syncState.pendingChanges || [];\n const curChange = q.find((p) => p.localId === localId && p.stateKey === stateKey);\n return curChange?.version === version;\n}\n\nexport function removeFromPendingChanges(set: any, localId: string, stateKey: string) {\n set((s: any) => {\n const queue: PendingChange[] = (s.syncState.pendingChanges || []).filter((p: PendingChange) => !(p.localId === localId && p.stateKey === stateKey));\n return {\n syncState: {\n ...(s.syncState || {}),\n pendingChanges: queue,\n },\n };\n });\n}\n\nexport function tryAddToPendingChanges(pendingChanges: PendingChange[], stateKey: string, changes: Map<string, ChangeRecord>) {\n for (const [localId, change] of changes) {\n let omittedItem = omitSyncFields(change.changes);\n const queueItem = pendingChanges.find((p) => p.localId === localId && p.stateKey === stateKey);\n const hasChanges = Object.keys(omittedItem).length > 0;\n const action = change.updatedItem === null ? SyncAction.Remove : change.currentItem === null ? SyncAction.Create : SyncAction.Update;\n\n if (action === SyncAction.Update && change.updatedItem && change.currentItem && change.currentItem._localId !== change.updatedItem._localId) {\n // Here when insert-remote-record swaps local remotely deleted item with a fresh copy to push up\n omittedItem = omitSyncFields(change.updatedItem);\n }\n\n if (queueItem) {\n if (queueItem.action === SyncAction.Remove) {\n // Once a Remove is queued, it stays a Remove\n continue;\n }\n\n queueItem.version += 1;\n\n if (action === SyncAction.Remove) {\n queueItem.action = SyncAction.Remove;\n } else if (hasChanges) {\n // Never change the action here, it stays Create or Update and is removed when synced\n queueItem.changes = { ...queueItem.changes, ...omittedItem };\n }\n } else if (action === SyncAction.Remove || hasChanges) {\n pendingChanges.push({ action, stateKey, localId, id: change.id, version: 1, changes: omittedItem });\n }\n }\n}\n\nexport function setPendingChangeToUpdate(get: any, stateKey: string, localId: string, id?: any) {\n // id is optional as the user may client assign the id, but not return it from the api\n const pendingChanges: PendingChange[] = get().syncState.pendingChanges || [];\n const change = pendingChanges.find((p) => p.stateKey === stateKey && p.localId === localId);\n if (change) {\n change.action = SyncAction.Update;\n if (id) change.id = id;\n }\n}\n\nexport function findApi(stateKey: string, syncApi: Record<string, ApiFunctions>) {\n const api = syncApi[stateKey];\n if (!api || !api.add || !api.update || !api.remove || !api.list || !api.firstLoad) {\n throw new Error(`Missing API function(s) for state key: ${stateKey}.`);\n }\n return api;\n}\n\ntype ChangeRecord = {\n currentItem?: any;\n updatedItem?: any;\n changes: any;\n id?: any;\n};\n\n/**\n * Compares the top-level keys of items in `current` and `updated` arrays (assumed to have `_localId`).\n * Returns a Map where the key is `_localId` and the value is an object with:\n * - `currentItem`: The item from `current` (or `null` for additions).\n * - `updatedItem`: The item from `updated` (or `null` for deletions).\n * - `changes`: An object with differing top-level keys and their values (or the full item for additions, or `null` for deletions).\n */\nexport function findChanges(current: any[], updated: any[]): Map<string, ChangeRecord> {\n const currentMap = new Map<string, any>();\n for (const item of current) {\n if (item && item._localId) {\n currentMap.set(item._localId, { ...item });\n }\n }\n\n const changesMap = new Map<string, ChangeRecord>();\n\n // Check for changes and additions\n for (const update of updated) {\n const item = { ...update };\n if (item && item._localId) {\n const curr = currentMap.get(item._localId);\n if (curr) {\n const diff: any = {};\n for (const key in curr) {\n if (key !== '_localId' && curr[key] !== item[key]) {\n diff[key] = item[key];\n }\n }\n if (Object.keys(diff).length > 0) {\n // Changes\n changesMap.set(item._localId, { currentItem: curr, updatedItem: item, changes: diff, id: curr.id ?? item.id });\n }\n } else {\n // Addition\n changesMap.set(item._localId, { currentItem: null, updatedItem: item, changes: item, id: item.id });\n }\n }\n }\n\n // Check for deletions\n for (const [localId, curr] of currentMap) {\n if (!updated.some((u) => u && u._localId === localId)) {\n changesMap.set(localId, { currentItem: curr, updatedItem: null, changes: null, id: curr.id });\n }\n }\n\n return changesMap;\n}\n","import type { ApiFunctions, ConflictResolutionStrategy, PendingChange, SyncedRecord } from './types';\nimport { SyncAction } from './index';\nimport { nextLocalId } from './helpers';\nimport type { Logger } from './logger';\n\nexport async function pull(set: any, get: any, stateKey: string, api: ApiFunctions, logger: Logger, conflictResolutionStrategy: ConflictResolutionStrategy) {\n const lastPulled: Record<string, string> = get().syncState.lastPulled || {};\n const lastPulledAt = new Date(lastPulled[stateKey] || new Date(0));\n\n logger.debug(`[zync] pull:start stateKey=${stateKey} since=${lastPulledAt.toISOString()}`);\n\n const serverData = (await api.list(lastPulledAt)) as SyncedRecord[];\n if (!serverData?.length) return;\n\n let newest = lastPulledAt;\n set((state: any) => {\n const pendingChanges: PendingChange[] = state.syncState.pendingChanges || [];\n const localItems: any[] = state[stateKey] || [];\n let nextItems = [...localItems];\n const localById = new Map<any, any>(localItems.filter((l) => l.id).map((l) => [l.id, l]));\n // prevent resurrecting deleted items by pulling them again\n const pendingRemovalById = new Set(pendingChanges.filter((p) => p.stateKey === stateKey && p.action === SyncAction.Remove).map((p) => p.id));\n\n for (const remote of serverData) {\n const remoteUpdated = new Date(remote.updated_at);\n if (remoteUpdated > newest) newest = remoteUpdated;\n\n // If a Remove is pending for this localId, skip merging/adding to avoid briefly resurrecting the item\n if (pendingRemovalById.has(remote.id)) {\n logger.debug(`[zync] pull:skip-pending-remove stateKey=${stateKey} id=${remote.id}`);\n continue;\n }\n\n const localItem = localById.get(remote.id);\n if (remote.deleted) {\n if (localItem) {\n nextItems = nextItems.filter((i: any) => i.id !== remote.id);\n logger.debug(`[zync] pull:remove stateKey=${stateKey} id=${remote.id}`);\n }\n continue;\n }\n\n delete remote.deleted;\n\n if (localItem) {\n const pendingChange = pendingChanges.find((p: any) => p.stateKey === stateKey && p.localId === localItem._localId);\n if (pendingChange) {\n // TODO: Conflict resolution required\n switch (conflictResolutionStrategy) {\n case 'local-wins':\n // Ignore remote changes, keep local\n logger.debug(`[zync] pull:conflict-strategy:${conflictResolutionStrategy} stateKey=${stateKey} id=${remote.id}`);\n break;\n\n case 'remote-wins': {\n // Ignore local changes, keep remote\n const merged = {\n ...remote,\n _localId: localItem._localId,\n };\n nextItems = nextItems.map((i: any) => (i._localId === localItem._localId ? merged : i));\n logger.debug(`[zync] pull:conflict-strategy:${conflictResolutionStrategy} stateKey=${stateKey} id=${remote.id}`);\n break;\n }\n\n // case 'try-shallow-merge':\n // // Try and merge all fields, fail if not possible due to conflicts\n // // throw new ConflictError('Details...');\n // break;\n\n // case 'custom':\n // // Hook to allow custom userland logic\n // // const error = onConflict(localItem, remote, stateKey, pending);\n // // logger.debug(`[zync] pull:conflict-strategy:${conflictResolutionStrategy} stateKey=${stateKey} id=${remote.id} error=${error}`);\n // // if (error) throw new ConflictError(error);\n // break;\n\n default:\n logger.error(`[zync] pull:conflict-strategy:unknown stateKey=${stateKey} id=${remote.id} strategy=${conflictResolutionStrategy}`);\n break;\n }\n } else {\n // No pending changes, merge remote into local\n const merged = {\n ...localItem,\n ...remote,\n };\n nextItems = nextItems.map((i: any) => (i._localId === localItem._localId ? merged : i));\n logger.debug(`[zync] pull:merge-remote stateKey=${stateKey} id=${remote.id}`);\n }\n } else {\n // Add remote item (no local item)\n nextItems = [...nextItems, { ...remote, _localId: nextLocalId() }];\n logger.debug(`[zync] pull:add stateKey=${stateKey} id=${remote.id}`);\n }\n }\n\n return {\n [stateKey]: nextItems,\n syncState: {\n ...(state.syncState || {}),\n lastPulled: {\n ...(state.syncState.lastPulled || {}),\n [stateKey]: newest.toISOString(),\n },\n },\n };\n });\n}\n","import { removeFromPendingChanges, samePendingVersion, setPendingChangeToUpdate } from './helpers';\nimport { nextLocalId, SyncAction } from './index';\nimport type { Logger } from './logger';\nimport type {\n AfterRemoteAddCallback,\n ApiFunctions,\n MissingRemoteRecordStrategy,\n MissingRemoteRecordDuringUpdateCallback,\n PendingChange,\n SetAndSyncCallback,\n} from './types';\n\nexport async function pushOne(\n set: any,\n get: any,\n change: PendingChange,\n api: ApiFunctions,\n logger: Logger,\n setAndQueueToSync: SetAndSyncCallback,\n missingStrategy: MissingRemoteRecordStrategy,\n onMissingRemoteRecordDuringUpdate?: MissingRemoteRecordDuringUpdateCallback,\n onAfterRemoteAdd?: AfterRemoteAddCallback,\n) {\n logger.debug(`[zync] push:attempt action=${change.action} stateKey=${change.stateKey} localId=${change.localId}`);\n\n const { action, stateKey, localId, id, version, changes } = change;\n const changesClone = { ...changes }; // TODO: Test if needed\n\n switch (action) {\n case SyncAction.Remove:\n if (!id) {\n logger.warn(`[zync] push:remove:no-id stateKey=${stateKey} localId=${localId}`);\n removeFromPendingChanges(set, localId, stateKey);\n return;\n }\n\n await api.remove(id);\n logger.debug(`[zync] push:remove:success stateKey=${stateKey} localId=${localId} id=${id}`);\n removeFromPendingChanges(set, localId, stateKey);\n break;\n\n case SyncAction.Update: {\n const exists = await api.update(id, changesClone);\n if (exists) {\n logger.debug(`[zync] push:update:success stateKey=${stateKey} localId=${localId} id=${id}`);\n if (samePendingVersion(get, stateKey, localId, version)) {\n removeFromPendingChanges(set, localId, stateKey);\n }\n return;\n } else {\n const state = get();\n const items: any[] = state[stateKey] || [];\n const item = items.find((i) => i._localId === localId);\n if (!item) {\n logger.warn(`[zync] push:missing-remote:no-local-item stateKey=${stateKey} localId=${localId}`);\n removeFromPendingChanges(set, localId, stateKey);\n return;\n }\n\n switch (missingStrategy) {\n case 'delete-local-record':\n set((s: any) => ({\n [stateKey]: (s[stateKey] || []).filter((i: any) => i._localId !== localId),\n }));\n logger.debug(`[zync] push:missing-remote:${missingStrategy} stateKey=${stateKey} id=${item.id}`);\n break;\n\n case 'insert-remote-record': {\n const newItem = {\n ...item,\n _localId: nextLocalId(),\n updated_at: new Date().toISOString(),\n };\n\n // replace old with modified and queue Create\n setAndQueueToSync((s: any) => ({\n [stateKey]: (s[stateKey] || []).map((i: any) => (i._localId === localId ? newItem : i)),\n }));\n\n logger.debug(`[zync] push:missing-remote:${missingStrategy} stateKey=${stateKey} id=${newItem.id}`);\n break;\n }\n\n case 'ignore':\n logger.debug(`[zync] push:missing-remote:${missingStrategy} stateKey=${stateKey} id=${item.id}`);\n break;\n\n default:\n logger.error(`[zync] push:missing-remote:unknown-strategy stateKey=${stateKey} id=${item.id} strategy=${missingStrategy}`);\n break;\n }\n\n removeFromPendingChanges(set, localId, stateKey);\n // Call hook so userland can alert the user etc.\n onMissingRemoteRecordDuringUpdate?.(missingStrategy, item);\n }\n break;\n }\n\n case SyncAction.Create: {\n const result = await api.add(changesClone);\n if (result) {\n logger.debug(`[zync] push:create:success stateKey=${stateKey} localId=${localId} id=${id}`);\n\n // Merge server-assigned fields (id, updated_at, etc) directly into local entity\n set((s: any) => ({\n [stateKey]: (s[stateKey] || []).map((i: any) => (i._localId === localId ? { ...i, ...result } : i)),\n }));\n\n if (samePendingVersion(get, stateKey, localId, version)) {\n removeFromPendingChanges(set, localId, stateKey);\n } else {\n // Item changed during request, ensure any pendingChanges entry has id and is now an Update\n setPendingChangeToUpdate(get, stateKey, localId, result.id);\n }\n\n const finalItem = { ...changes, ...result, _localId: localId };\n\n // Call hook so userland can perform any cascading adjustments\n onAfterRemoteAdd?.(set, get, setAndQueueToSync, stateKey, finalItem);\n } else {\n logger.warn(`[zync] push:create:no-result stateKey=${stateKey} localId=${localId} id=${id}`);\n if (samePendingVersion(get, stateKey, localId, version)) {\n removeFromPendingChanges(set, localId, stateKey);\n }\n }\n break;\n }\n }\n}\n","import { findApi, nextLocalId } from './helpers';\nimport type { Logger } from './logger';\nimport type { ApiFunctions } from './types';\n\nexport async function startFirstLoad(set: any, syncApi: Record<string, ApiFunctions>, logger: Logger) {\n let syncError: Error | undefined;\n\n for (const stateKey of Object.keys(syncApi)) {\n try {\n logger.info(`[zync] firstLoad:start stateKey=${stateKey}`);\n\n const api = findApi(stateKey, syncApi);\n let lastId; // Start as undefined to allow the userland api code to set the initial value+type\n\n // Batch until empty\n while (true) {\n const batch = await api.firstLoad(lastId);\n if (!batch?.length) break;\n\n // Merge batch\n set((state: any) => {\n const local: any[] = state[stateKey] || [];\n const localById = new Map<any, any>(local.filter((l) => l.id).map((l) => [l.id, l]));\n\n let newest = new Date(state.syncState.lastPulled[stateKey] || 0);\n const next = [...local];\n for (const remote of batch) {\n const remoteUpdated = new Date(remote.updated_at || 0);\n if (remoteUpdated > newest) newest = remoteUpdated;\n\n if (remote.deleted) continue;\n\n delete remote.deleted;\n\n const localItem = remote.id ? localById.get(remote.id) : undefined;\n if (localItem) {\n const merged = {\n ...localItem,\n ...remote,\n _localId: localItem._localId,\n };\n const idx = next.findIndex((i) => i._localId === localItem._localId);\n if (idx >= 0) next[idx] = merged;\n } else {\n next.push({\n ...remote,\n _localId: nextLocalId(),\n });\n }\n }\n\n return {\n [stateKey]: next,\n syncState: {\n ...(state.syncState || {}),\n lastPulled: {\n ...(state.syncState.lastPulled || {}),\n [stateKey]: newest.toISOString(),\n },\n },\n };\n });\n\n if (lastId !== undefined && lastId === batch[batch.length - 1].id) {\n throw new Error(`Duplicate records downloaded, stopping to prevent infinite loop`);\n }\n\n lastId = batch[batch.length - 1].id;\n }\n\n logger.info(`[zync] firstLoad:done stateKey=${stateKey}`);\n } catch (err) {\n syncError = syncError ?? (err as Error);\n logger.error(`[zync] firstLoad:error stateKey=${stateKey}`, err);\n }\n }\n\n set((state: any) => ({\n syncState: {\n ...(state.syncState || {}),\n firstLoadDone: true,\n error: syncError,\n },\n }));\n}\n","import type { IDBPDatabase } from 'idb';\n\nexport function createIndexedDBStorage(options: { dbName: string; storeName: string }) {\n const dbName = options.dbName;\n const storeName = options.storeName;\n\n // dbPromise is created lazily by initDB() to avoid pulling `idb` into bundles\n let dbPromise: Promise<IDBPDatabase<any>> | null = null;\n\n async function initDB(): Promise<IDBPDatabase<any>> {\n if (dbPromise) return dbPromise;\n try {\n const idb = await import(/* webpackChunkName: \"idb\" */ 'idb');\n dbPromise = idb.openDB(dbName, 1, {\n upgrade(db: IDBPDatabase<any>) {\n if (!db.objectStoreNames.contains(storeName)) {\n db.createObjectStore(storeName);\n }\n },\n });\n return dbPromise;\n } catch (_e) {\n throw new Error('Missing optional dependency \"idb\". Install it to use IndexedDB storage: npm install idb');\n }\n }\n\n async function ensureStore(): Promise<void> {\n const db = await initDB();\n if (db.objectStoreNames.contains(storeName)) return;\n const nextVersion = (db.version || 0) + 1;\n try {\n db.close();\n } catch (_e) {\n // ignore\n }\n const idb = await import(/* webpackChunkName: \"idb\" */ 'idb');\n dbPromise = idb.openDB(dbName, nextVersion, {\n upgrade(upg: IDBPDatabase<any>) {\n if (!upg.objectStoreNames.contains(storeName)) upg.createObjectStore(storeName);\n },\n });\n await dbPromise;\n }\n\n async function withRetry<T>(fn: (db: IDBPDatabase<any>) => Promise<T>): Promise<T> {\n try {\n const db = await initDB();\n return await fn(db);\n } catch (err: any) {\n const msg = String(err && err.message ? err.message : err);\n if (err && (err.name === 'NotFoundError' || /objectStore/i.test(msg))) {\n await ensureStore();\n const db2 = await initDB();\n return await fn(db2);\n }\n throw err;\n }\n }\n\n return {\n getItem: async (name: string): Promise<string | null> => {\n return withRetry(async (db) => {\n let v = await db.get(storeName, name);\n v = v ?? null; // Zustand expects null for missing keys\n return v;\n });\n },\n setItem: async (name: string, value: string): Promise<void> => {\n return withRetry(async (db) => {\n await db.put(storeName, value, name);\n });\n },\n removeItem: async (name: string): Promise<void> => {\n return withRetry(async (db) => {\n await db.delete(storeName, name);\n });\n },\n };\n}\n"],"mappings":"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","names":["SyncAction","state"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/logger.ts","../src/helpers.ts","../src/pull.ts","../src/push.ts","../src/firstLoad.ts","../src/indexedDBStorage.ts"],"sourcesContent":["import { create, type StateCreator } from 'zustand';\nimport { persist } from 'zustand/middleware';\nimport { newLogger, type Logger, type LogLevel } from './logger';\nimport { orderFor, findApi, findChanges, tryAddToPendingChanges, tryUpdateConflicts, resolveConflict, sleep } from './helpers';\nimport {\n type ApiFunctions,\n type SyncOptions,\n type SyncState,\n type SyncedStateCreator,\n type PendingChange,\n type UseStoreWithSync,\n type MissingRemoteRecordStrategy,\n type ConflictResolutionStrategy,\n} from './types';\nimport { pull } from './pull';\nimport { pushOne } from './push';\nimport { startFirstLoad } from './firstLoad';\n\nexport { createIndexedDBStorage } from './indexedDBStorage';\nexport { createLocalId, changeKeysTo, changeKeysFrom } from './helpers';\nexport type { ApiFunctions, UseStoreWithSync, SyncState } from './types';\n\nexport enum SyncAction {\n Create = 'create',\n Update = 'update',\n Remove = 'remove',\n}\n\nconst DEFAULT_SYNC_INTERVAL_MILLIS = 2000;\nconst DEFAULT_LOGGER: Logger = console;\nconst DEFAULT_MIN_LOG_LEVEL: LogLevel = 'debug';\nconst DEFAULT_MISSING_REMOTE_RECORD_STRATEGY: MissingRemoteRecordStrategy = 'ignore';\nconst DEFAULT_CONFLICT_RESOLUTION_STRATEGY: ConflictResolutionStrategy = 'client-wins';\n\nexport function createWithSync<TStore extends object>(\n stateCreator: SyncedStateCreator<TStore>,\n persistOptions: any,\n syncApi: Record<string, ApiFunctions>,\n syncOptions: SyncOptions = {},\n): Promise<UseStoreWithSync<TStore>> {\n const store = create(persistWithSync(stateCreator, persistOptions, syncApi, syncOptions)) as UseStoreWithSync<TStore>;\n\n return new Promise<UseStoreWithSync<TStore>>((resolve) => {\n store.persist.onFinishHydration((_state) => {\n resolve(store);\n });\n });\n}\n\nexport function persistWithSync<TStore extends object>(\n stateCreator: SyncedStateCreator<TStore>,\n persistOptions: any,\n syncApi: Record<string, ApiFunctions>,\n syncOptions: SyncOptions = {},\n) {\n const syncInterval = syncOptions.syncInterval ?? DEFAULT_SYNC_INTERVAL_MILLIS;\n const missingStrategy = syncOptions.missingRemoteRecordDuringUpdateStrategy ?? DEFAULT_MISSING_REMOTE_RECORD_STRATEGY;\n const conflictResolutionStrategy = syncOptions.conflictResolutionStrategy ?? DEFAULT_CONFLICT_RESOLUTION_STRATEGY;\n const logger = newLogger(syncOptions.logger ?? DEFAULT_LOGGER, syncOptions.minLogLevel ?? DEFAULT_MIN_LOG_LEVEL);\n\n const baseOnRehydrate = persistOptions?.onRehydrateStorage;\n const basePartialize = persistOptions?.partialize;\n\n const wrappedPersistOptions = {\n ...persistOptions,\n onRehydrateStorage: () => {\n logger.debug('[zync] rehydration:start');\n\n return (state: any, error: any) => {\n if (error) {\n logger.error('[zync] rehydration:failed', error);\n } else {\n baseOnRehydrate?.(state, error);\n logger.debug('[zync] rehydration:complete', state);\n }\n };\n },\n partialize: (s: any) => {\n // Select state to be persisted\n\n const base = basePartialize ? basePartialize(s) : s;\n const { syncState, ...rest } = base || {};\n return {\n ...rest,\n syncState: {\n firstLoadDone: syncState.firstLoadDone,\n pendingChanges: syncState.pendingChanges,\n lastPulled: syncState.lastPulled,\n conflicts: syncState.conflicts,\n },\n };\n },\n merge: (persisted: any, current: any) => {\n // Here after hydration.\n // `persisted` is state from storage that's just loaded (possibly asynchronously e.g. IndexedDB)\n // `current` is what the user has defined (they may have added or removed state keys)\n // Zync is designed to not be used until hydration is complete, so we don't expect to have to\n // merge user mutated state (i.e. current) into persisted. So we do the Zustand recommended pattern of\n // shallow copy where persisted keys win:\n const state = { ...current, ...persisted };\n\n return {\n ...state,\n syncState: {\n ...(state.syncState || {}),\n status: 'idle', // this confirms 'hydrating' is done\n },\n };\n },\n };\n\n const creator: StateCreator<TStore & SyncState, [], []> = (set: any, get: any, storeApi: any) => {\n let syncTimerStarted = false;\n\n async function syncOnce() {\n if (get().syncState.status !== 'idle') return;\n\n set((state: any) => ({\n syncState: {\n ...(state.syncState || {}),\n status: 'syncing',\n },\n }));\n\n let firstSyncError: Error | undefined;\n\n // 1) PULL for each stateKey\n for (const stateKey of Object.keys(syncApi)) {\n try {\n const api = findApi(stateKey, syncApi);\n await pull(set, get, stateKey, api, logger, conflictResolutionStrategy);\n } catch (err) {\n firstSyncError = firstSyncError ?? (err as Error);\n logger.error(`[zync] pull:error stateKey=${stateKey}`, err);\n }\n }\n\n // 2) PUSH queued changes\n const changesSnapshot: PendingChange[] = [...(get().syncState.pendingChanges || [])];\n\n // Deterministic ordering: Create -> Update -> Remove so dependencies (e.g. id assignment) happen first\n changesSnapshot.sort((a, b) => orderFor(a.action) - orderFor(b.action));\n\n for (const change of changesSnapshot) {\n try {\n const api = findApi(change.stateKey, syncApi);\n await pushOne(\n set,\n get,\n change,\n api,\n logger,\n setAndQueueToSync,\n missingStrategy,\n syncOptions.onMissingRemoteRecordDuringUpdate,\n syncOptions.onAfterRemoteAdd,\n );\n } catch (err) {\n firstSyncError = firstSyncError ?? (err as Error);\n logger.error(`[zync] push:error change=${change}`, err);\n }\n }\n\n set((state: any) => ({\n syncState: {\n ...(state.syncState || {}),\n status: 'idle',\n error: firstSyncError,\n } as SyncState['syncState'],\n }));\n }\n\n function setAndQueueToSync(partial: any) {\n if (typeof partial === 'function') {\n set((state: any) => newSyncState(state, partial(state)));\n } else {\n set((state: any) => newSyncState(state, partial));\n }\n }\n\n function newSyncState(state: any, partial: any) {\n const pendingChanges: PendingChange[] = state.syncState.pendingChanges || [];\n\n Object.keys(partial).map((stateKey) => {\n const current = state[stateKey];\n const updated = partial[stateKey];\n const changes = findChanges(current, updated); // find additions, deletions & updates\n tryAddToPendingChanges(pendingChanges, stateKey, changes);\n });\n\n // Prevent stale conflicts reporting old values to user\n const conflicts = tryUpdateConflicts(pendingChanges, state.syncState.conflicts);\n\n return {\n ...partial,\n syncState: {\n ...(state.syncState || {}),\n pendingChanges,\n conflicts,\n } as SyncState['syncState'],\n };\n }\n\n function enable(enabled: boolean) {\n set((state: any) => ({\n syncState: {\n ...(state.syncState || {}),\n status: enabled ? 'idle' : 'disabled',\n },\n }));\n\n startSyncTimer(enabled);\n addVisibilityChangeListener(enabled);\n }\n\n function startSyncTimer(start: boolean) {\n if (start) {\n tryStart(); // Unawaited async\n } else {\n syncTimerStarted = false;\n }\n }\n\n async function tryStart() {\n if (syncTimerStarted) return;\n syncTimerStarted = true;\n\n while (true) {\n if (!syncTimerStarted) break;\n await syncOnce();\n await sleep(syncInterval);\n }\n }\n\n function addVisibilityChangeListener(add: boolean) {\n if (add) {\n document.addEventListener('visibilitychange', onVisibilityChange);\n } else {\n document.removeEventListener('visibilitychange', onVisibilityChange);\n }\n }\n\n function onVisibilityChange() {\n if (document.visibilityState === 'visible') {\n logger.debug('[zync] sync:start-in-foreground');\n startSyncTimer(true);\n } else {\n logger.debug('[zync] sync:pause-in-background');\n startSyncTimer(false);\n }\n }\n\n // public useStore.sync api, similar in principle to useStore.persist\n storeApi.sync = {\n enable,\n startFirstLoad: () => startFirstLoad(set, syncApi, logger),\n resolveConflict: (localId: string, keepLocal: boolean) => resolveConflict(set, localId, keepLocal),\n };\n\n const userState = stateCreator(set, get, setAndQueueToSync) as TStore;\n\n return {\n ...userState,\n syncState: {\n // set defaults\n status: 'hydrating',\n error: undefined,\n conflicts: undefined,\n firstLoadDone: false,\n pendingChanges: [],\n lastPulled: {},\n },\n } as TStore & SyncState;\n };\n\n return persist(creator, wrappedPersistOptions);\n}\n","export type LogLevel = 'debug' | 'info' | 'warn' | 'error' | 'none';\n\nexport interface Logger {\n debug: (...args: any[]) => void;\n info: (...args: any[]) => void;\n warn: (...args: any[]) => void;\n error: (...args: any[]) => void;\n}\n\nexport function newLogger(base: Logger, min: LogLevel): Logger {\n const order: Record<LogLevel, number> = {\n debug: 10,\n info: 20,\n warn: 30,\n error: 40,\n none: 100,\n };\n const threshold = order[min];\n const enabled = (lvl: LogLevel) => order[lvl] >= threshold;\n return {\n debug: (...a: any[]) => enabled('debug') && base.debug?.(...a),\n info: (...a: any[]) => enabled('info') && base.info?.(...a),\n warn: (...a: any[]) => enabled('warn') && base.warn?.(...a),\n error: (...a: any[]) => enabled('error') && base.error?.(...a),\n };\n}\n","import { SyncAction } from './index';\nimport type { ApiFunctions, Conflict, FieldConflict, PendingChange, SyncState } from './types';\n\nconst SYNC_FIELDS = ['_localId', 'updated_at', 'deleted'] as const;\n\nexport function createLocalId(): string {\n return crypto.randomUUID();\n}\n\nexport function sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nexport function changeKeysTo(input: any | any[], toIdKey: string, toUpdatedAtKey: string, toDeletedKey: string) {\n if (!input) return input;\n const isArray = Array.isArray(input);\n const result = (isArray ? input : [input]).map((item) => {\n const { id, updated_at, deleted, ...rest } = item;\n return {\n [toIdKey]: id,\n [toUpdatedAtKey]: updated_at,\n [toDeletedKey]: deleted,\n ...rest,\n };\n });\n return isArray ? result : result[0];\n}\n\nexport function changeKeysFrom(input: any | any[], fromIdKey: string, fromUpdatedAtKey: string, fromDeletedKey: string) {\n if (!input) return input;\n const isArray = Array.isArray(input);\n const result = (isArray ? input : [input]).map((item) => {\n const { [fromIdKey]: id, [fromUpdatedAtKey]: updated_at, [fromDeletedKey]: deleted, ...rest } = item;\n return {\n id,\n updated_at,\n deleted,\n ...rest,\n };\n });\n return isArray ? result : result[0];\n}\n\nexport function orderFor(a: SyncAction): number {\n switch (a) {\n case SyncAction.Create:\n return 1;\n case SyncAction.Update:\n return 2;\n case SyncAction.Remove:\n return 3;\n }\n}\n\nexport function omitSyncFields(item: any) {\n const result = { ...item };\n for (const k of SYNC_FIELDS) delete result[k];\n return result;\n}\n\nexport function samePendingVersion(get: any, stateKey: string, localId: string, version: number): boolean {\n const pending: PendingChange[] = get().syncState.pendingChanges || [];\n const curChange = pending.find((p) => p.localId === localId && p.stateKey === stateKey);\n return curChange?.version === version;\n}\n\nexport function removeFromPendingChanges(set: any, localId: string, stateKey: string) {\n set((s: any) => {\n const queue: PendingChange[] = (s.syncState.pendingChanges || []).filter((p: PendingChange) => !(p.localId === localId && p.stateKey === stateKey));\n return {\n syncState: {\n ...(s.syncState || {}),\n pendingChanges: queue,\n },\n };\n });\n}\n\nexport function tryAddToPendingChanges(pendingChanges: PendingChange[], stateKey: string, changes: Map<string, ChangeRecord>) {\n for (const [localId, change] of changes) {\n let omittedItem = omitSyncFields(change.changes);\n const queueItem = pendingChanges.find((p) => p.localId === localId && p.stateKey === stateKey);\n const hasChanges = Object.keys(omittedItem).length > 0;\n const action = change.updatedItem === null ? SyncAction.Remove : change.currentItem === null ? SyncAction.Create : SyncAction.Update;\n\n if (action === SyncAction.Update && change.updatedItem && change.currentItem && change.currentItem._localId !== change.updatedItem._localId) {\n // Here when insert-remote-record swaps local remotely deleted item with a fresh copy to push up\n omittedItem = omitSyncFields(change.updatedItem);\n }\n\n if (queueItem) {\n if (queueItem.action === SyncAction.Remove) {\n // Once a Remove is queued, it stays a Remove\n continue;\n }\n\n queueItem.version += 1;\n\n if (action === SyncAction.Remove) {\n queueItem.action = SyncAction.Remove;\n } else if (hasChanges) {\n // Never change the action here, it stays Create or Update and is removed when synced\n queueItem.changes = { ...queueItem.changes, ...omittedItem };\n }\n } else if (action === SyncAction.Remove || hasChanges) {\n pendingChanges.push({ action, stateKey, localId, id: change.id, version: 1, changes: omittedItem, current: omitSyncFields(change.currentItem) });\n }\n }\n}\n\nexport function setPendingChangeToUpdate(get: any, stateKey: string, localId: string, id?: any) {\n // id is optional as the user may client assign the id, but not return it from the api\n const pendingChanges: PendingChange[] = get().syncState.pendingChanges || [];\n const change = pendingChanges.find((p) => p.stateKey === stateKey && p.localId === localId);\n if (change) {\n change.action = SyncAction.Update;\n if (id) change.id = id;\n }\n}\n\nexport function tryUpdateConflicts(pendingChanges: PendingChange[], conflicts?: Record<string, Conflict>): Record<string, Conflict> | undefined {\n if (!conflicts) return conflicts;\n\n const newConflicts = { ...conflicts };\n\n for (const change of pendingChanges) {\n const conflict = newConflicts[change.localId];\n if (conflict && change.changes) {\n // Loop changed fields and update their old possibly stale value to the current local value\n const newFields = conflict.fields.map((f) => {\n if (f.key in change.changes) {\n return { ...f, localValue: change.changes[f.key] } as FieldConflict;\n }\n return f;\n });\n\n newConflicts[change.localId] = { stateKey: conflict.stateKey, fields: newFields };\n }\n }\n\n return newConflicts;\n}\n\nexport function findApi(stateKey: string, syncApi: Record<string, ApiFunctions>) {\n const api = syncApi[stateKey];\n if (!api || !api.add || !api.update || !api.remove || !api.list || !api.firstLoad) {\n throw new Error(`Missing API function(s) for state key: ${stateKey}.`);\n }\n return api;\n}\n\ntype ChangeRecord = {\n currentItem?: any;\n updatedItem?: any;\n changes: any;\n id?: any;\n};\n\n/**\n * Compares the top-level keys of items in `current` and `updated` arrays (assumed to have `_localId`).\n * Returns a Map where the key is `_localId` and the value is an object with:\n * - `currentItem`: The item from `current` (or `null` for additions).\n * - `updatedItem`: The item from `updated` (or `null` for deletions).\n * - `changes`: An object with differing top-level keys and their values (or the full item for additions, or `null` for deletions).\n */\nexport function findChanges(current: any[], updated: any[]): Map<string, ChangeRecord> {\n const currentMap = new Map<string, any>();\n for (const item of current) {\n if (item && item._localId) {\n currentMap.set(item._localId, { ...item });\n }\n }\n\n const changesMap = new Map<string, ChangeRecord>();\n\n // Check for changes and additions\n for (const update of updated) {\n const item = { ...update };\n if (item && item._localId) {\n const curr = currentMap.get(item._localId);\n if (curr) {\n const diff: any = {};\n for (const key in curr) {\n if (key !== '_localId' && curr[key] !== item[key]) {\n diff[key] = item[key];\n }\n }\n if (Object.keys(diff).length > 0) {\n // Changes\n changesMap.set(item._localId, { currentItem: curr, updatedItem: item, changes: diff, id: curr.id ?? item.id });\n }\n } else {\n // Addition\n changesMap.set(item._localId, { currentItem: null, updatedItem: item, changes: item, id: item.id });\n }\n }\n }\n\n // Check for deletions\n for (const [localId, curr] of currentMap) {\n if (!updated.some((u) => u && u._localId === localId)) {\n changesMap.set(localId, { currentItem: curr, updatedItem: null, changes: null, id: curr.id });\n }\n }\n\n return changesMap;\n}\n\nexport function hasKeysOrUndefined(obj: any): any {\n return Object.keys(obj).length === 0 ? undefined : obj;\n}\n\nexport function hasConflicts(get: any, localId: string): boolean {\n const state = get() as SyncState;\n if (state.syncState.conflicts) {\n return !!state.syncState.conflicts[localId];\n }\n return false;\n}\n\nexport function resolveConflict(set: any, localId: string, keepLocalFields: boolean) {\n set((state: any) => {\n const syncState: SyncState['syncState'] = state.syncState || {};\n const conflicts: Record<string, Conflict> = syncState.conflicts || {};\n const conflict = conflicts[localId];\n if (conflict) {\n const items = state[conflict.stateKey];\n const item = items.find((i: any) => i._localId === localId);\n if (!item) {\n return state;\n }\n\n const resolved: any = { ...item };\n let pendingChanges = [...syncState.pendingChanges];\n\n if (!keepLocalFields) {\n // Use remote value(s)\n for (const field of conflict.fields) {\n resolved[field.key] = field.remoteValue;\n }\n\n // Remove resolved pending change\n pendingChanges = pendingChanges.filter((p) => !(p.stateKey === conflict.stateKey && p.localId === localId));\n }\n\n // Replace with resolved item\n const nextItems = items.map((i: any) => (i._localId === localId ? resolved : i));\n const nextConflicts = { ...conflicts };\n delete nextConflicts[localId];\n\n return {\n [conflict.stateKey]: nextItems,\n syncState: {\n ...syncState,\n pendingChanges,\n conflicts: hasKeysOrUndefined(nextConflicts),\n },\n };\n }\n return state;\n });\n}\n","import { type ApiFunctions, type FieldConflict, type ConflictResolutionStrategy, type SyncedRecord, type PendingChange } from './types';\nimport { SyncAction } from './index';\nimport { createLocalId, hasKeysOrUndefined } from './helpers';\nimport type { Logger } from './logger';\n\nexport async function pull(set: any, get: any, stateKey: string, api: ApiFunctions, logger: Logger, conflictResolutionStrategy: ConflictResolutionStrategy) {\n const lastPulled: Record<string, string> = get().syncState.lastPulled || {};\n const lastPulledAt = new Date(lastPulled[stateKey] || new Date(0));\n\n logger.debug(`[zync] pull:start stateKey=${stateKey} since=${lastPulledAt.toISOString()}`);\n\n const serverData = (await api.list(lastPulledAt)) as SyncedRecord[];\n if (!serverData?.length) return;\n\n let newest = lastPulledAt;\n\n set((state: any) => {\n let pendingChanges = [...(state.syncState.pendingChanges as PendingChange[])];\n const conflicts = { ...state.syncState.conflicts };\n const localItems: any[] = state[stateKey] || [];\n let nextItems = [...localItems];\n\n const localById = new Map<any, any>(localItems.filter((l) => l.id).map((l) => [l.id, l]));\n // prevent resurrecting deleted items when pulling them again\n const pendingRemovalById = new Set(pendingChanges.filter((p) => p.stateKey === stateKey && p.action === SyncAction.Remove).map((p) => p.id));\n\n for (const remote of serverData) {\n const remoteUpdated = new Date(remote.updated_at);\n if (remoteUpdated > newest) newest = remoteUpdated;\n\n // If a Remove is pending for this localId, skip merging/adding to avoid briefly resurrecting the item\n if (pendingRemovalById.has(remote.id)) {\n logger.debug(`[zync] pull:skip-pending-remove stateKey=${stateKey} id=${remote.id}`);\n continue;\n }\n\n const localItem = localById.get(remote.id);\n if (remote.deleted) {\n if (localItem) {\n nextItems = nextItems.filter((i: any) => i.id !== remote.id);\n logger.debug(`[zync] pull:remove stateKey=${stateKey} id=${remote.id}`);\n }\n continue;\n }\n\n delete remote.deleted;\n\n if (localItem) {\n const pendingChange = pendingChanges.find((p: any) => p.stateKey === stateKey && p.localId === localItem._localId);\n if (pendingChange) {\n logger.debug(`[zync] pull:conflict-strategy:${conflictResolutionStrategy} stateKey=${stateKey} id=${remote.id}`);\n\n switch (conflictResolutionStrategy) {\n case 'client-wins':\n // Ignore remote changes, keep local\n break;\n\n case 'server-wins': {\n // Ignore local changes, keep remote\n const merged = { ...remote, _localId: localItem._localId };\n nextItems = nextItems.map((i: any) => (i._localId === localItem._localId ? merged : i));\n // Remove pending change so it isn't pushed after pull\n pendingChanges = pendingChanges.filter((p) => !(p.stateKey === stateKey && p.localId === localItem._localId));\n break;\n }\n\n case 'try-shallow-merge': {\n // List fields that local and remote have changed\n const changes = pendingChange.changes || {};\n const current = pendingChange.current || {};\n const fields: FieldConflict[] = Object.entries(changes)\n .filter(([k, localValue]) => k in current && k in remote && current[k] !== remote[k] && localValue !== remote[k])\n .map(([key, localValue]) => ({ key, localValue, remoteValue: remote[key] }));\n\n if (fields.length > 0) {\n logger.warn(`[zync] pull:${conflictResolutionStrategy}:conflicts-found`, JSON.stringify(fields, null, 4));\n conflicts[localItem._localId] = { stateKey, fields };\n } else {\n // No conflicts, merge remote into local but only preserve fields that were\n // actually changed locally\n const localChangedKeys = Object.keys(changes);\n const preservedLocal: any = { _localId: localItem._localId };\n for (const k of localChangedKeys) {\n if (k in localItem) preservedLocal[k] = localItem[k];\n }\n\n const merged = { ...remote, ...preservedLocal };\n nextItems = nextItems.map((i: any) => (i._localId === localItem._localId ? merged : i));\n // Merge now resolved, drop pending and conflict\n delete conflicts[localItem._localId];\n }\n break;\n }\n }\n } else {\n // No pending changes, merge remote into local\n const merged = { ...localItem, ...remote };\n nextItems = nextItems.map((i: any) => (i._localId === localItem._localId ? merged : i));\n logger.debug(`[zync] pull:merge-remote stateKey=${stateKey} id=${remote.id}`);\n }\n } else {\n // Add remote item (no local item)\n nextItems = [...nextItems, { ...remote, _localId: createLocalId() }];\n logger.debug(`[zync] pull:add stateKey=${stateKey} id=${remote.id}`);\n }\n }\n\n return {\n [stateKey]: nextItems,\n syncState: {\n ...(state.syncState || {}),\n pendingChanges,\n conflicts: hasKeysOrUndefined(conflicts),\n lastPulled: {\n ...(state.syncState.lastPulled || {}),\n [stateKey]: newest.toISOString(),\n },\n },\n };\n });\n}\n","import { hasConflicts, removeFromPendingChanges, samePendingVersion, setPendingChangeToUpdate } from './helpers';\nimport { createLocalId, SyncAction } from './index';\nimport type { Logger } from './logger';\nimport type {\n AfterRemoteAddCallback,\n ApiFunctions,\n MissingRemoteRecordStrategy,\n MissingRemoteRecordDuringUpdateCallback,\n PendingChange,\n SetAndSyncCallback,\n} from './types';\n\nexport async function pushOne(\n set: any,\n get: any,\n change: PendingChange,\n api: ApiFunctions,\n logger: Logger,\n setAndQueueToSync: SetAndSyncCallback,\n missingStrategy: MissingRemoteRecordStrategy,\n onMissingRemoteRecordDuringUpdate?: MissingRemoteRecordDuringUpdateCallback,\n onAfterRemoteAdd?: AfterRemoteAddCallback,\n) {\n logger.debug(`[zync] push:attempt action=${change.action} stateKey=${change.stateKey} localId=${change.localId}`);\n\n const { action, stateKey, localId, id, version, changes } = change;\n const changesClone = { ...changes }; // TODO: Test if needed\n\n switch (action) {\n case SyncAction.Remove:\n if (!id) {\n logger.warn(`[zync] push:remove:no-id stateKey=${stateKey} localId=${localId}`);\n removeFromPendingChanges(set, localId, stateKey);\n return;\n }\n\n await api.remove(id);\n logger.debug(`[zync] push:remove:success stateKey=${stateKey} localId=${localId} id=${id}`);\n removeFromPendingChanges(set, localId, stateKey);\n break;\n\n case SyncAction.Update: {\n if (hasConflicts(get, change.localId)) {\n logger.warn(`[zync] push:update:skipping-with-conflicts stateKey=${stateKey} localId=${localId} id=${id}`);\n return;\n }\n\n const exists = await api.update(id, changesClone);\n if (exists) {\n logger.debug(`[zync] push:update:success stateKey=${stateKey} localId=${localId} id=${id}`);\n if (samePendingVersion(get, stateKey, localId, version)) {\n removeFromPendingChanges(set, localId, stateKey);\n }\n return;\n } else {\n const state = get();\n const items: any[] = state[stateKey] || [];\n const item = items.find((i) => i._localId === localId);\n if (!item) {\n logger.warn(`[zync] push:missing-remote:no-local-item stateKey=${stateKey} localId=${localId}`);\n removeFromPendingChanges(set, localId, stateKey);\n return;\n }\n\n switch (missingStrategy) {\n case 'delete-local-record':\n set((s: any) => ({\n [stateKey]: (s[stateKey] || []).filter((i: any) => i._localId !== localId),\n }));\n logger.debug(`[zync] push:missing-remote:${missingStrategy} stateKey=${stateKey} id=${item.id}`);\n break;\n\n case 'insert-remote-record': {\n const newItem = {\n ...item,\n _localId: createLocalId(),\n updated_at: new Date().toISOString(),\n };\n\n // replace old with modified and queue Create\n setAndQueueToSync((s: any) => ({\n [stateKey]: (s[stateKey] || []).map((i: any) => (i._localId === localId ? newItem : i)),\n }));\n\n logger.debug(`[zync] push:missing-remote:${missingStrategy} stateKey=${stateKey} id=${newItem.id}`);\n break;\n }\n\n case 'ignore':\n logger.debug(`[zync] push:missing-remote:${missingStrategy} stateKey=${stateKey} id=${item.id}`);\n break;\n\n default:\n logger.error(`[zync] push:missing-remote:unknown-strategy stateKey=${stateKey} id=${item.id} strategy=${missingStrategy}`);\n break;\n }\n\n removeFromPendingChanges(set, localId, stateKey);\n // Call hook so userland can alert the user etc.\n onMissingRemoteRecordDuringUpdate?.(missingStrategy, item);\n }\n break;\n }\n\n case SyncAction.Create: {\n const result = await api.add(changesClone);\n if (result) {\n logger.debug(`[zync] push:create:success stateKey=${stateKey} localId=${localId} id=${id}`);\n\n // Merge server-assigned fields (id, updated_at, etc) directly into local entity\n set((s: any) => ({\n [stateKey]: (s[stateKey] || []).map((i: any) => (i._localId === localId ? { ...i, ...result } : i)),\n }));\n\n if (samePendingVersion(get, stateKey, localId, version)) {\n removeFromPendingChanges(set, localId, stateKey);\n } else {\n // Item changed during request, ensure any pendingChanges entry has id and is now an Update\n setPendingChangeToUpdate(get, stateKey, localId, result.id);\n }\n\n const finalItem = { ...changes, ...result, _localId: localId };\n\n // Call hook so userland can perform any cascading adjustments\n onAfterRemoteAdd?.(set, get, setAndQueueToSync, stateKey, finalItem);\n } else {\n logger.warn(`[zync] push:create:no-result stateKey=${stateKey} localId=${localId} id=${id}`);\n if (samePendingVersion(get, stateKey, localId, version)) {\n removeFromPendingChanges(set, localId, stateKey);\n }\n }\n break;\n }\n }\n}\n","import { findApi, createLocalId } from './helpers';\nimport type { Logger } from './logger';\nimport type { ApiFunctions } from './types';\n\nexport async function startFirstLoad(set: any, syncApi: Record<string, ApiFunctions>, logger: Logger) {\n let syncError: Error | undefined;\n\n for (const stateKey of Object.keys(syncApi)) {\n try {\n logger.info(`[zync] firstLoad:start stateKey=${stateKey}`);\n\n const api = findApi(stateKey, syncApi);\n let lastId; // Start as undefined to allow the userland api code to set the initial value+type\n\n // Batch until empty\n while (true) {\n const batch = await api.firstLoad(lastId);\n if (!batch?.length) break;\n\n // Merge batch\n set((state: any) => {\n const local: any[] = state[stateKey] || [];\n const localById = new Map<any, any>(local.filter((l) => l.id).map((l) => [l.id, l]));\n\n let newest = new Date(state.syncState.lastPulled[stateKey] || 0);\n const next = [...local];\n for (const remote of batch) {\n const remoteUpdated = new Date(remote.updated_at || 0);\n if (remoteUpdated > newest) newest = remoteUpdated;\n\n if (remote.deleted) continue;\n\n delete remote.deleted;\n\n const localItem = remote.id ? localById.get(remote.id) : undefined;\n if (localItem) {\n const merged = {\n ...localItem,\n ...remote,\n _localId: localItem._localId,\n };\n const idx = next.findIndex((i) => i._localId === localItem._localId);\n if (idx >= 0) next[idx] = merged;\n } else {\n next.push({\n ...remote,\n _localId: createLocalId(),\n });\n }\n }\n\n return {\n [stateKey]: next,\n syncState: {\n ...(state.syncState || {}),\n lastPulled: {\n ...(state.syncState.lastPulled || {}),\n [stateKey]: newest.toISOString(),\n },\n },\n };\n });\n\n if (lastId !== undefined && lastId === batch[batch.length - 1].id) {\n throw new Error(`Duplicate records downloaded, stopping to prevent infinite loop`);\n }\n\n lastId = batch[batch.length - 1].id;\n }\n\n logger.info(`[zync] firstLoad:done stateKey=${stateKey}`);\n } catch (err) {\n syncError = syncError ?? (err as Error);\n logger.error(`[zync] firstLoad:error stateKey=${stateKey}`, err);\n }\n }\n\n set((state: any) => ({\n syncState: {\n ...(state.syncState || {}),\n firstLoadDone: true,\n error: syncError,\n },\n }));\n}\n","import type { IDBPDatabase } from 'idb';\n\nexport function createIndexedDBStorage(options: { dbName: string; storeName: string }) {\n const dbName = options.dbName;\n const storeName = options.storeName;\n\n // dbPromise is created lazily by initDB() to avoid pulling `idb` into bundles\n let dbPromise: Promise<IDBPDatabase<any>> | null = null;\n\n async function initDB(): Promise<IDBPDatabase<any>> {\n if (dbPromise) return dbPromise;\n try {\n const idb = await import(/* webpackChunkName: \"idb\" */ 'idb');\n dbPromise = idb.openDB(dbName, 1, {\n upgrade(db: IDBPDatabase<any>) {\n if (!db.objectStoreNames.contains(storeName)) {\n db.createObjectStore(storeName);\n }\n },\n });\n return dbPromise;\n } catch (_e) {\n throw new Error('Missing optional dependency \"idb\". Install it to use IndexedDB storage: npm install idb');\n }\n }\n\n async function ensureStore(): Promise<void> {\n const db = await initDB();\n if (db.objectStoreNames.contains(storeName)) return;\n const nextVersion = (db.version || 0) + 1;\n try {\n db.close();\n } catch (_e) {\n // ignore\n }\n const idb = await import(/* webpackChunkName: \"idb\" */ 'idb');\n dbPromise = idb.openDB(dbName, nextVersion, {\n upgrade(upg: IDBPDatabase<any>) {\n if (!upg.objectStoreNames.contains(storeName)) upg.createObjectStore(storeName);\n },\n });\n await dbPromise;\n }\n\n async function withRetry<T>(fn: (db: IDBPDatabase<any>) => Promise<T>): Promise<T> {\n try {\n const db = await initDB();\n return await fn(db);\n } catch (err: any) {\n const msg = String(err && err.message ? err.message : err);\n if (err && (err.name === 'NotFoundError' || /objectStore/i.test(msg))) {\n await ensureStore();\n const db2 = await initDB();\n return await fn(db2);\n }\n throw err;\n }\n }\n\n return {\n getItem: async (name: string): Promise<string | null> => {\n return withRetry(async (db) => {\n let v = await db.get(storeName, name);\n v = v ?? null; // Zustand expects null for missing keys\n return v;\n });\n },\n setItem: async (name: string, value: string): Promise<void> => {\n return withRetry(async (db) => {\n await db.put(storeName, value, name);\n });\n },\n removeItem: async (name: string): Promise<void> => {\n return withRetry(async (db) => {\n await db.delete(storeName, name);\n });\n },\n };\n}\n"],"mappings":"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","names":["SyncAction"]}
|