@aegiondynamic/async-storage-sync
v1.0.13
Published
Offline-first data layer for React Native with local-first storage and automatic sync
Readme
@aegiondynamic/async-storage-sync
Offline-first queue for React Native with type-safe payloads and automatic sync.
Install
npm install @aegiondynamic/async-storage-sync @react-native-async-storage/async-storage @react-native-community/netinfoQuick Start
import { initSyncQueue, getSyncQueue } from '@aegiondynamic/async-storage-sync';
await initSyncQueue({
driver: 'asyncstorage',
serverUrl: 'https://api.example.com',
credentials: { Authorization: 'Token abc123' },
endpoint: '/submit',
idResolver: (item) => String(item.id ?? ''),
autoSync: true,
syncOnSave: true,
});
type Submission = {
id: string;
formId: string;
name: string;
};
const store = getSyncQueue().asType<Submission>();
const saved = await store.save('submissions', {
id: 'sub_1',
formId: 'form_a',
name: 'John',
});
console.log(saved.meta.id); // internal id used by queue
console.log(saved.data.name); // your payload
const result = await store.flushWithResult();
console.log(`Synced: ${result.synced}, Failed: ${result.failed}`);If queue access may happen during startup (for example from screens/services), use:
import { ensureInitialized } from '@aegiondynamic/async-storage-sync';
await ensureInitialized({
driver: 'asyncstorage',
serverUrl: 'https://api.example.com',
credentials: { Authorization: 'Token abc123' },
});What Changed
- Generic API:
save<T>(),getAll<T>(),getById<T>(),flushWithResult<T>(). - Metadata is separated from payload in storage and reads/writes.
- Identity is configurable with
idResolver. getSyncQueue()is non-generic and safe on a singleton; useasType<T>()for typed access.
Configuration
initSyncQueue({
driver: 'asyncstorage',
serverUrl: 'https://api.example.com',
credentials: {
Authorization: 'Token abc123',
'x-api-key': 'my-custom-key',
},
endpoint: '/submit',
autoSync: true,
syncOnSave: true,
autoSyncCollections: ['submissions'],
onSyncSuccess: 'delete',
ttl: 7 * 24 * 60 * 60 * 1000,
duplicateStrategy: 'append',
idResolver: (item) => String(item.id ?? ''),
payloadTransformer: (payload) => payload,
});API
Setup
initSyncQueue(config)
isInitialized()
ensureInitialized(config?)
getSyncQueue()
getTypedSyncQueue<T>()Typed access
type Invoice = { invoiceNo: string; amount: number };
const store = getSyncQueue().asType<Invoice>();
await store.save('invoices', { invoiceNo: 'INV-1', amount: 100 });
const all = await store.getAll('invoices');
const one = await store.getById('invoices', all[0].meta.id);Using the helper export:
import { getTypedSyncQueue } from '@aegiondynamic/async-storage-sync';
type Invoice = { invoiceNo: string; amount: number };
const store = getTypedSyncQueue<Invoice>();Record shape
type StoredRecord<T> = {
meta: {
id: string;
ts: number;
synced: 'pending' | 'synced' | 'failed';
type: string;
retries: number;
};
data: T;
};Sync
const store = getSyncQueue().asType<{ amount: number }>();
await store.flushWithResult();
await getSyncQueue().syncWithResult('invoices');
await getSyncQueue().syncManyWithResult(['invoices', 'receipts']);
await getSyncQueue().syncById('invoices', 'record-id');Events
const queue = getSyncQueue();
queue.onSynced((item) => {
console.log('Synced queue item:', item.id);
});
queue.onAuthError((statusCode, item) => {
console.log('Auth error:', statusCode, item.recordId);
});
queue.onStorageFull(() => {
console.log('Storage full');
});Notes
payloadTransformerreceives only your payload (record.data), not metadata.- Queue payload and collection records are persisted across app restarts.
- Max 5 retries per record with exponential backoff for 5xx errors.
getSyncQueue()throws if not initialized yet; useensureInitialized(config)for boot-safe access.ensureInitialized()without config works only after first init; otherwise pass config.
Sync Modes
autoSync: sync on connectivity/app-open signals (NetInfo-driven behavior).syncOnSave: schedule immediate debounced best-effort sync after eachsave().- Use both for most apps:
autoSynchandles reconnects,syncOnSavereduces delay when already online.
