@tanstack/offline-transactions
Version:
Offline-first transaction capabilities for TanStack DB
140 lines (139 loc) • 4.16 kB
JavaScript
class TransactionSerializer {
constructor(collections) {
this.collections = collections;
this.collectionIdToKey = /* @__PURE__ */ new Map();
for (const [key, collection] of Object.entries(collections)) {
this.collectionIdToKey.set(collection.id, key);
}
}
serialize(transaction) {
const serialized = {
...transaction,
createdAt: transaction.createdAt.toISOString(),
mutations: transaction.mutations.map(
(mutation) => this.serializeMutation(mutation)
)
};
return JSON.stringify(serialized);
}
deserialize(data) {
const parsed = JSON.parse(data);
const createdAt = new Date(parsed.createdAt);
if (isNaN(createdAt.getTime())) {
throw new Error(
`Failed to deserialize transaction: invalid createdAt value "${parsed.createdAt}"`
);
}
return {
...parsed,
createdAt,
mutations: parsed.mutations.map(
(mutationData) => this.deserializeMutation(mutationData)
)
};
}
serializeMutation(mutation) {
const registryKey = this.collectionIdToKey.get(mutation.collection.id);
if (!registryKey) {
throw new Error(
`Collection with id ${mutation.collection.id} not found in registry`
);
}
return {
globalKey: mutation.globalKey,
type: mutation.type,
modified: this.serializeValue(mutation.modified),
original: this.serializeValue(mutation.original),
changes: this.serializeValue(mutation.changes),
collectionId: registryKey
// Store registry key instead of collection.id
};
}
deserializeMutation(data) {
const collection = this.collections[data.collectionId];
if (!collection) {
throw new Error(`Collection with id ${data.collectionId} not found`);
}
const modified = this.deserializeValue(data.modified);
const key = modified ? collection.getKeyFromItem(modified) : null;
return {
globalKey: data.globalKey,
type: data.type,
modified,
original: this.deserializeValue(data.original),
changes: this.deserializeValue(data.changes) ?? {},
collection,
// These fields would need to be reconstructed by the executor
mutationId: ``,
// Will be regenerated
key,
metadata: void 0,
syncMetadata: {},
optimistic: true,
createdAt: /* @__PURE__ */ new Date(),
updatedAt: /* @__PURE__ */ new Date()
};
}
serializeValue(value) {
if (value === null || value === void 0) {
return value;
}
if (value instanceof Date) {
return { __type: `Date`, value: value.toISOString() };
}
if (typeof value === `object`) {
const result = Array.isArray(value) ? [] : {};
for (const key in value) {
if (Object.prototype.hasOwnProperty.call(value, key)) {
result[key] = this.serializeValue(value[key]);
}
}
return result;
}
return value;
}
deserializeValue(value) {
if (value === null || value === void 0) {
return value;
}
if (typeof value === `object` && value.__type === `Date`) {
if (value.value === void 0 || value.value === null) {
throw new Error(`Corrupted Date marker: missing value field`);
}
const date = new Date(value.value);
if (isNaN(date.getTime())) {
throw new Error(
`Failed to deserialize Date marker: invalid date value "${value.value}"`
);
}
return date;
}
if (typeof value === `object`) {
const result = Array.isArray(value) ? [] : {};
for (const key in value) {
if (Object.prototype.hasOwnProperty.call(value, key)) {
result[key] = this.deserializeValue(value[key]);
}
}
return result;
}
return value;
}
serializeError(error) {
return {
name: error.name,
message: error.message,
stack: error.stack
};
}
deserializeError(data) {
const error = new Error(data.message);
error.name = data.name;
error.stack = data.stack;
return error;
}
}
export {
TransactionSerializer
};
//# sourceMappingURL=TransactionSerializer.js.map