@oxog/synckit
v1.0.0
Published
Zero-dependency offline-first data sync toolkit with micro-kernel plugin architecture
Downloads
87
Maintainers
Readme
SyncKit
Zero-dependency offline-first data sync toolkit with micro-kernel plugin architecture
SyncKit is a comprehensive offline-first synchronization solution for web applications. It queues operations when offline, automatically syncs when back online, handles conflicts with multiple resolution strategies, supports optimistic updates, and persists the queue to IndexedDB—all without any runtime dependencies.
Features
✨ Offline-First: Queue operations when offline, auto-sync when back online 🔄 Conflict Resolution: Multiple strategies (last-write-wins, server-wins, client-wins, manual, custom) ⚡ Optimistic Updates: Apply changes immediately with rollback capability 🎯 Priority Queue: Critical operations processed first 🔁 Automatic Retry: Configurable backoff strategies (exponential, linear, fibonacci) 💾 Persistent Storage: IndexedDB for reliable queue persistence 🧩 Plugin Architecture: Micro-kernel design with powerful plugin system ⚛️ Framework Adapters: First-class support for React, Vue, and Svelte 📦 Zero Dependencies: Everything implemented from scratch 🔒 Type-Safe: Full TypeScript support with strict mode 🌳 Tree-Shakeable: Only bundle what you use
Installation
npm install @oxog/synckityarn add @oxog/synckitpnpm add @oxog/synckitQuick Start
import { createSyncKit } from '@oxog/synckit'
// Create instance
const sync = createSyncKit({
name: 'my-app',
storage: 'indexeddb',
executor: async (operation) => {
const response = await fetch(`/api/${operation.resource}`, {
method: operation.method,
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(operation.payload)
})
return response.json()
},
conflictStrategy: 'last-write-wins',
retry: {
maxAttempts: 5,
backoff: 'exponential'
}
})
// Initialize
await sync.init()
// Push operations
const { operationId, rollback } = sync.push({
resource: 'posts',
method: 'POST',
payload: { title: 'Hello', body: 'World' },
priority: 'high',
optimistic: true,
onSuccess: (result) => console.log('Synced!', result),
onError: (error) => {
console.error('Failed:', error)
rollback() // Revert optimistic update
}
})
// Listen to events
sync.on('sync-success', (event) => {
console.log('Operation synced:', event.operation)
})
sync.on('offline', () => {
console.log('Gone offline, operations will be queued')
})
sync.on('online', () => {
console.log('Back online, syncing queued operations')
})
// Check status
const status = sync.getStatus()
console.log(`${status.pending} pending, ${status.failed} failed`)Framework Integration
React
import { SyncKitProvider, useSync, useSyncStatus } from '@oxog/synckit/react'
function App() {
return (
<SyncKitProvider config={{ name: 'my-app', storage: 'indexeddb', executor: ... }}>
<MyApp />
</SyncKitProvider>
)
}
function CreatePost() {
const { push, isOnline } = useSync()
const { pending, failed } = useSyncStatus()
const handleSubmit = (data) => {
push({
resource: 'posts',
method: 'POST',
payload: data,
optimistic: true
})
}
return (
<div>
{!isOnline && <Badge>Offline</Badge>}
{pending > 0 && <Badge>{pending} pending</Badge>}
<form onSubmit={handleSubmit}>...</form>
</div>
)
}Vue
<script setup>
import { useSync, useSyncStatus } from '@oxog/synckit/vue'
const { push, isOnline } = useSync()
const { pending, failed } = useSyncStatus()
const handleSubmit = (data) => {
push({
resource: 'posts',
method: 'POST',
payload: data
})
}
</script>
<template>
<div>
<span v-if="!isOnline">Offline</span>
<span v-if="pending > 0">{{ pending }} pending</span>
<form @submit.prevent="handleSubmit">...</form>
</div>
</template>Svelte
<script>
import { syncStore, statusStore, onlineStore } from '@oxog/synckit/svelte'
function handleSubmit(data) {
$syncStore.push({
resource: 'posts',
method: 'POST',
payload: data
})
}
</script>
{#if !$onlineStore}
<span>Offline</span>
{/if}
{#if $statusStore.pending > 0}
<span>{$statusStore.pending} pending</span>
{/if}
<form on:submit|preventDefault={handleSubmit}>...</form>Core Concepts
Offline-First Architecture
SyncKit queues all operations and syncs them when online. The queue is persisted to IndexedDB, so operations survive page reloads and browser restarts.
Conflict Resolution
When the server rejects an operation due to a conflict (HTTP 409), SyncKit can resolve it automatically using one of these strategies:
- last-write-wins: Compare timestamps, newer wins
- server-wins: Always accept server data
- client-wins: Always keep client data
- manual: Wait for user decision
- custom: Use your own resolution logic
Optimistic Updates
Apply changes immediately for instant UI feedback, then rollback if sync fails:
const { operationId, rollback } = sync.push({
resource: 'posts/123',
method: 'PUT',
payload: updatedPost,
optimistic: true,
onError: () => rollback()
})Priority Queue
Operations with higher priority are synced first:
sync.push({
resource: 'critical-data',
method: 'POST',
payload: data,
priority: 'critical' // critical > high > normal > low
})Automatic Retry
Failed operations are retried automatically with configurable backoff:
createSyncKit({
retry: {
maxAttempts: 5,
backoff: 'exponential', // or 'linear', 'fibonacci', or custom function
baseDelay: 1000,
maxDelay: 30000,
jitter: true
}
})Plugins
SyncKit uses a micro-kernel architecture where all features are plugins.
Core Plugins (Always Loaded)
- queue-manager: Priority queue management
- network-monitor: Online/offline detection
- storage-indexeddb: IndexedDB persistence
- retry-engine: Automatic retry with backoff
- conflict-resolver: Conflict detection and resolution
Optional Plugins
import {
localStorageAdapter,
batching,
compression,
encryption,
backgroundSync,
syncUI,
analytics
} from '@oxog/synckit/plugins'
const sync = createSyncKit({
storage: localStorageAdapter({ key: 'my-queue' }),
plugins: [
batching({ maxBatchSize: 10 }),
compression({ threshold: 1024 }),
encryption({ key: 'secret' }),
backgroundSync({ tag: 'sync' }),
syncUI({ position: 'bottom-right' }),
analytics({ onReport: sendToAnalytics })
]
})API Reference
See the full API documentation.
Core Methods
init()- Initialize and load queue from storagedestroy()- Cleanup resourcespush(operation)- Add operation to queueremove(operationId)- Remove operationclear()- Clear entire queuesync()- Force sync immediatelyretry(operationId)- Retry specific operationretryAll()- Retry all failed operationspause()- Pause queue processingresume()- Resume queue processinggetQueue()- Get all operationsgetStatus()- Get queue statisticson(event, handler)- Subscribe to eventsoff(event, handler)- Unsubscribe from events
Events
online/offline- Network status changepush- Operation addedsync-start/sync-success/sync-error- Sync lifecycleconflict- Conflict detectedretry- Retry scheduledqueue-change- Queue modifiedstatus-change- Status updated
Examples
See the examples directory for complete working examples:
Documentation
Full documentation available at https://synckit.oxog.dev
Browser Support
- Chrome >= 87
- Firefox >= 78
- Safari >= 14
- Edge >= 87
Requires:
- IndexedDB (or localStorage fallback)
- Promises
- ES6+ features
Contributing
This is a zero-dependency project. All features must be implemented from scratch without adding runtime dependencies.
License
MIT © Ersin KOÇ
