@tanstack/offline-transactions
Version:
Offline-first transaction capabilities for TanStack DB
115 lines (114 loc) • 3.78 kB
JavaScript
;
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
const tracer = require("../telemetry/tracer.cjs");
const TransactionSerializer = require("./TransactionSerializer.cjs");
class OutboxManager {
constructor(storage, collections) {
this.keyPrefix = `tx:`;
this.storage = storage;
this.serializer = new TransactionSerializer.TransactionSerializer(collections);
}
getStorageKey(id) {
return `${this.keyPrefix}${id}`;
}
async add(transaction) {
return tracer.withSpan(
`outbox.add`,
{
"transaction.id": transaction.id,
"transaction.mutationFnName": transaction.mutationFnName,
"transaction.keyCount": transaction.keys.length
},
async () => {
const key = this.getStorageKey(transaction.id);
const serialized = this.serializer.serialize(transaction);
await this.storage.set(key, serialized);
}
);
}
async get(id) {
return tracer.withSpan(`outbox.get`, {}, async (span) => {
const key = this.getStorageKey(id);
const data = await this.storage.get(key);
if (!data) {
span.setAttribute(`result`, `not_found`);
return null;
}
try {
const transaction = this.serializer.deserialize(data);
span.setAttribute(`result`, `found`);
return transaction;
} catch (error) {
console.warn(`Failed to deserialize transaction ${id}:`, error);
span.setAttribute(`result`, `deserialize_error`);
return null;
}
});
}
async getAll() {
return tracer.withSpan(`outbox.getAll`, {}, async (span) => {
const keys = await this.storage.keys();
const transactionKeys = keys.filter(
(key) => key.startsWith(this.keyPrefix)
);
span.setAttribute(`transactionCount`, transactionKeys.length);
const transactions = [];
for (const key of transactionKeys) {
const data = await this.storage.get(key);
if (data) {
try {
const transaction = this.serializer.deserialize(data);
transactions.push(transaction);
} catch (error) {
console.warn(
`Failed to deserialize transaction from key ${key}:`,
error
);
}
}
}
return transactions.sort(
(a, b) => a.createdAt.getTime() - b.createdAt.getTime()
);
});
}
async getByKeys(keys) {
const allTransactions = await this.getAll();
const keySet = new Set(keys);
return allTransactions.filter(
(transaction) => transaction.keys.some((key) => keySet.has(key))
);
}
async update(id, updates) {
return tracer.withSpan(`outbox.update`, {}, async () => {
const existing = await this.get(id);
if (!existing) {
throw new Error(`Transaction ${id} not found`);
}
const updated = { ...existing, ...updates };
await this.add(updated);
});
}
async remove(id) {
return tracer.withSpan(`outbox.remove`, {}, async () => {
const key = this.getStorageKey(id);
await this.storage.delete(key);
});
}
async removeMany(ids) {
return tracer.withSpan(`outbox.removeMany`, { count: ids.length }, async () => {
await Promise.all(ids.map((id) => this.remove(id)));
});
}
async clear() {
const keys = await this.storage.keys();
const transactionKeys = keys.filter((key) => key.startsWith(this.keyPrefix));
await Promise.all(transactionKeys.map((key) => this.storage.delete(key)));
}
async count() {
const keys = await this.storage.keys();
return keys.filter((key) => key.startsWith(this.keyPrefix)).length;
}
}
exports.OutboxManager = OutboxManager;
//# sourceMappingURL=OutboxManager.cjs.map