
Security News
Attackers Are Hunting High-Impact Node.js Maintainers in a Coordinated Social Engineering Campaign
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.
offlinesync
Advanced tools
Why OfflineSync.js? In today's connected world, users expect apps to work seamlessly, even without an internet connection. OfflineSync.js empowers developers to create offline-first applications, ensuring that data is queued and synced reliably when conne
OfflineSync is a lightweight, easy-to-use library for managing offline data synchronization in web and mobile applications. It allows actions to be queued while offline and automatically syncs them once the device is online. This ensures uninterrupted workflows for users even in the absence of network connectivity.
To install OfflineSync, use npm or yarn:
# Using npm
npm install offlinesync
# Using yarn
yarn add offlinesync
import OfflineSync from 'offlinesync';
const offlineSync = new OfflineSync({
maxQueueSize: 10, // Optional: Max number of actions in the queue
conflictStrategy: 'latest', // Optional: Conflict resolution strategy
persist: true, // Optional: Persist queue across sessions
});
Queue actions using the addAction method. Each action requires a type, data, id, and timestamp.
offlineSync.addAction({
type: 'ADD_TASK', // Action type
data: { id: 1, description: 'Complete the task' }, // Action data
id: 'task-1', // Unique action ID
timestamp: Date.now(), // Timestamp of when the action was queued
});
To sync queued actions, simply use the sync() method:
offlineSync.startSync();
This will sync the queued actions once the device is online, and successfully synced actions will be removed from the queue.
You can retrieve the current actions in the queue:
const queue = offlineSync.getQueuedActions()
console.log(queue); // Output: Array of actions in the queue
You can remove an individual action from the queue by its ID:
offlineSync.removeActionFromQueue('id'); // Remove specific action
To clear all actions from the queue:
offlineSync.clearQueuedAction() // Clear entire queue
OfflineSync offers several strategies to resolve conflicts when the same action is queued multiple times.
Example of a custom conflict strategy:
const offlineSync = new OfflineSync({
conflictStrategy: (action1, action2) => {
return action1.timestamp > action2.timestamp ? action1 : action2;
},
});
Goal: Enable users to add items to their shopping cart offline, queue the actions, and synchronize them with a server when back online.
1. Initialization OfflineSync is initialized with optional configurations, including queue size and conflict resolution strategies.
const syncInstance = new OfflineSync({
maxQueueSize: 10,
conflictStrategy: 'latest',
});
2. Adding Items to Cart When a user adds an item to the cart:
const addItemToCart = async (name: string, price: number) => {
const cartItem = {
id: Date.now(),
name,
quantity: 1,
price,
status: 'pending',
};
setCartItems((prev) => [...prev, cartItem]);
if (offlineSync?.onlineStatus) {
await processItem(cartItem);
} else {
queueItem(cartItem);
}
};
3. Queuing Offline Actions Queued actions are added to OfflineSync’s action queue. When the app detects network connectivity, these actions are automatically synced.
const queueItem = (cartItem: CartItemType) => {
offlineSync?.addAction({
type: 'ADD_ITEM',
data: cartItem,
id: String(cartItem.id),
timestamp: Date.now(),
});
updateQueue();
};
4. Syncing with the Server When the app comes online, queued items are sent to the server, and the queue is cleared for synced actions.
const sendQueuedItems = async () => {
if (!offlineSync) return;
const queuedActions = offlineSync.getQueuedActions()
for (const action of queuedActions) {
if (isCartItem(action.data)) {
await processItem(action.data);
offlineSync.removeActionFromQueue(action.id);
}
}
updateQueue();
};
5. Conflict Resolution OfflineSync resolves conflicts using predefined or custom strategies. For instance, keeping the latest version of conflicting actions:
const syncInstance = new OfflineSync({
conflictStrategy: 'latest',
});
ShoppingCart.tsx
// shoppingCart.tsx
'use client';
import React, { useState, useEffect } from 'react';
import CartItem from './component/shopping/CartItems';
import QueueList from './component/shopping/QueueList';
import AddToCartButton from './component/shopping/AddToCartButton';
import OfflineSync from 'offlinesync';
type CartItemType = {
id: number;
name: string;
quantity: number;
price: number;
status: 'pending' | 'sent';
};
const ShoppingCart = () => {
const [offlineSync, setOfflineSync] = useState<OfflineSync | null>(null);
const [status, setStatus] = useState<string>('Offline');
const [cartItems, setCartItems] = useState<CartItemType[]>([]);
const [queuedItems, setQueuedItems] = useState<CartItemType[]>([]);
useEffect(() => {
const syncInstance = new OfflineSync({ maxQueueSize: 10, conflictStrategy: 'latest' });
setOfflineSync(syncInstance);
const interval = setInterval(() => {
if (syncInstance) {
setStatus(syncInstance.onlineStatus ? 'Online' : 'Offline');
}
}, 1000);
return () => clearInterval(interval);
}, []);
const updateQueue = () => {
if (offlineSync) {
const queuedActions = offlineSync.getQueuedActions()
const queuedCartItems = queuedActions
.map((action) => action?.data)
.filter((data) => isCartItem(data));
setQueuedItems(queuedCartItems);
}
};
const isCartItem = (data: any): data is CartItemType => {
return data && typeof data.id === 'number' && typeof data.name === 'string';
};
const addItemToCart = async (name: string, price: number) => {
const cartItem: CartItemType = { id: Date.now(), name, quantity: 1, price, status: 'pending' };
setCartItems((prev) => [...prev, cartItem]);
if (offlineSync?.onlineStatus) {
await processItem(cartItem);
} else {
queueItem(cartItem);
}
};
const queueItem = (cartItem: CartItemType) => {
if (!offlineSync) return;
offlineSync?.addAction({
type: 'ADD_ITEM',
data: cartItem,
id: String(cartItem.id),
timestamp: Date.now(),
});
updateQueue();
};
const processItem = async (cartItem: CartItemType) => {
try {
const response = await fetch('https://jsonplaceholder.typicode.com/posts', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(cartItem),
});
const data = await response.json();
console.log('Item processed:', data);
} catch (error) {
console.error('Error processing item:', error);
}
};
const sendQueuedItems = async () => {
if (!offlineSync) return;
const queuedActions = offlineSync.getQueuedActions()
for (const action of queuedActions) {
if (isCartItem(action.data)) {
await processItem(action.data);
offlineSync.removeActionFromQueue(action.id);
}
}
updateQueue();
};
const clearQueue = () => {
if (offlineSync) {
offlineSync.clearQueuedAction()
setQueuedItems([]);
}
};
useEffect(() => {
if (status === 'Online') {
sendQueuedItems();
}
}, [status, offlineSync]);
useEffect(() => {
updateQueue();
}, [offlineSync]);
return (
<div>
<h1>Shopping Cart</h1>
<p>Status: {status}</p>
<AddToCartButton addItemToCart={addItemToCart} />
<div>
<h2>Your Cart</h2>
{cartItems.length > 0 ? (
cartItems.map((item) => (
<CartItem key={item.id} item={item} />
))
) : (
<p>Your cart is empty</p>
)}
</div>
<QueueList queuedItems={queuedItems} />
<div>
<button onClick={clearQueue}>Clear Queue</button>
</div>
</div>
);
};
export default ShoppingCart;
AddToCartButton.tsx: Button for adding items to the cart. CartItem.tsx: Displays individual cart items. QueueList.tsx: Displays queued items waiting to be synced
AddToCartButton.tsx
// AddToCartButton.tsx
import React, { useState } from 'react';
type AddToCartButtonProps = {
addItemToCart: (name: string, price: number) => void;
};
const AddToCartButton = ({ addItemToCart }: AddToCartButtonProps) => {
const [itemName, setItemName] = useState<string>('');
const [itemPrice, setItemPrice] = useState<number>(0);
const handleAddItem = () => {
if (itemName.trim() && itemPrice > 0) {
addItemToCart(itemName, itemPrice);
setItemName('');
setItemPrice(0);
}
};
return (
<div>
<input
type="text"
value={itemName}
onChange={(e) => setItemName(e.target.value)}
placeholder="Enter item name"
/>
<input
type="number"
value={itemPrice}
onChange={(e) => setItemPrice(Number(e.target.value))}
placeholder="Enter item price"
/>
<button onClick={handleAddItem}>Add Item to Cart</button>
</div>
);
};
export default AddToCartButton;
CartItems.tsx
// CartItem.tsx
import React from 'react';
type CartItemProps = {
item: {
id: number;
name: string;
quantity: number;
price: number;
status: 'pending' | 'sent';
};
};
const CartItem = ({ item }: CartItemProps) => {
return (
<div>
<h3>{item.name}</h3>
<p>Quantity: {item.quantity}</p>
<p>Price: ${item.price}</p>
<p>Status: {item.status}</p>
</div>
);
};
export default CartItem;
QueueList.tsx
// QueueList.tsx
import React from 'react';
type QueueListProps = {
queuedItems: {
id: number;
name: string;
quantity: number;
price: number;
status: 'pending' | 'sent';
}[];
};
const QueueList = ({ queuedItems }: QueueListProps) => {
return (
<div>
<h2 className="my-20 text-2xl">Queued Items</h2>
{queuedItems.length > 0 ? (
<ul>
{queuedItems.map((item) => (
<li key={item.id}>
{item.name} - {item.quantity} - ${item.price} - {item.status}
</li>
))}
</ul>
) : (
<p>No items in queue</p>
)}
</div>
);
};
export default QueueList;
1. Queue Management
Use 'latest' or custom strategies for resolving conflicts.
With OfflineSync, managing offline shopping cart operations becomes seamless, enhancing user experience even with intermittent network connectivity.
Feel free to copy and paste this markdown directly into your project's README.md file!
FAQs
Why OfflineSync.js? In today's connected world, users expect apps to work seamlessly, even without an internet connection. OfflineSync.js empowers developers to create offline-first applications, ensuring that data is queued and synced reliably when conne
We found that offlinesync demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 0 open source maintainers collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.

Security News
Axios compromise traced to social engineering, showing how attacks on maintainers can bypass controls and expose the broader software supply chain.

Security News
Node.js has paused its bug bounty program after funding ended, removing payouts for vulnerability reports but keeping its security process unchanged.