triplit-tanstackdb
Version:
TanStack DB collection adapter for the Triplit real-time, offline-first sync engine. Enables powerful optimistic updates and reactive, cross-source queries.
137 lines (134 loc) • 3.71 kB
JavaScript
// src/index.ts
import {
createCollection
} from "@tanstack/db";
// src/options.ts
function createTriplitCollectionOptions(options) {
const { client, query, getKey, onError } = options;
const collectionName = query.collectionName;
const syncFn = (params) => {
const { begin, write, commit, markReady, collection } = params;
let isReady = false;
let unsubscribeFromTriplit;
const reconcileSnapshot = (results) => {
begin();
const localItems = Array.from(collection.state.values());
const localKeys = new Set(localItems.map(getKey));
const remoteItems = results ? Array.from(results.values()) : [];
const remoteKeys = new Set(remoteItems.map(getKey));
for (const key of localKeys) {
if (!remoteKeys.has(key)) {
const originalItem = localItems.find((item) => getKey(item) === key);
if (originalItem) {
write({ type: "delete", value: originalItem });
}
}
}
for (const typedItem of remoteItems) {
const key = getKey(typedItem);
if (localKeys.has(key)) {
write({ type: "update", value: typedItem });
} else {
write({ type: "insert", value: typedItem });
}
}
commit();
};
client.fetch(query).then((initialResults) => {
if (isReady) return;
reconcileSnapshot(initialResults);
markReady();
isReady = true;
}).catch((err) => {
console.error("[Triplit Adapter] Initial fetch failed, waiting for subscription.", err);
onError == null ? void 0 : onError(err);
});
unsubscribeFromTriplit = client.subscribe(
query,
(results) => {
reconcileSnapshot(results);
if (!isReady) {
markReady();
isReady = true;
}
},
(error) => {
console.error("[Triplit Adapter] Subscription error:", error);
onError == null ? void 0 : onError(error);
}
);
return () => {
unsubscribeFromTriplit == null ? void 0 : unsubscribeFromTriplit();
};
};
const onInsert = async ({ transaction }) => {
try {
for (const mutation of transaction.mutations) {
await client.insert(collectionName, mutation.modified);
}
} catch (error) {
onError == null ? void 0 : onError(error);
throw error;
}
};
const onUpdate = async ({ transaction }) => {
try {
for (const mutation of transaction.mutations) {
await client.update(
collectionName,
String(mutation.key),
mutation.changes
);
}
} catch (error) {
onError == null ? void 0 : onError(error);
throw error;
}
};
const onDelete = async ({ transaction }) => {
try {
for (const mutation of transaction.mutations) {
await client.delete(collectionName, String(mutation.key));
}
} catch (error) {
onError == null ? void 0 : onError(error);
throw error;
}
};
return {
getKey,
sync: { sync: syncFn },
onInsert,
onUpdate,
onDelete
};
}
// src/index.ts
function createTriplitCollection(options) {
const {
client,
query,
getKey,
onError,
// Capture all other standard options into a 'rest' object.
...restConfig
} = options;
const triplitAdapterOptions = createTriplitCollectionOptions(
{
client,
query,
getKey,
onError
}
);
return createCollection({
...restConfig,
// Spread passthrough options first (id, schema, rowUpdateMode, etc.)
...triplitAdapterOptions
// Then spread our adapter's core logic.
});
}
export {
createTriplitCollection
};
//# sourceMappingURL=index.js.map