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.
156 lines (152 loc) • 4.63 kB
JavaScript
;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/index.ts
var src_exports = {};
__export(src_exports, {
createTriplitCollection: () => createTriplitCollection
});
module.exports = __toCommonJS(src_exports);
var import_db = require("@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 (0, import_db.createCollection)({
...restConfig,
// Spread passthrough options first (id, schema, rowUpdateMode, etc.)
...triplitAdapterOptions
// Then spread our adapter's core logic.
});
}
//# sourceMappingURL=index.js.map