UNPKG

@tanstack/db

Version:

A reactive client store for building super fast apps on sync

180 lines (179 loc) 5.59 kB
import { MultiSet, serializeValue } from "@tanstack/db-ivm"; import { normalizeOrderByPaths } from "../compiler/expressions.js"; import { buildQuery, getQueryIR } from "../builder/index.js"; function extractCollectionsFromQuery(query) { const collections = {}; function extractFromSource(source) { if (source.type === `collectionRef`) { collections[source.collection.id] = source.collection; } else if (source.type === `queryRef`) { extractFromQuery(source.query); } } function extractFromQuery(q) { if (q.from) { extractFromSource(q.from); } if (q.join && Array.isArray(q.join)) { for (const joinClause of q.join) { if (joinClause.from) { extractFromSource(joinClause.from); } } } } extractFromQuery(query); return collections; } function extractCollectionFromSource(query) { const from = query.from; if (from.type === `collectionRef`) { return from.collection; } else if (from.type === `queryRef`) { return extractCollectionFromSource(from.query); } throw new Error( `Failed to extract collection. Invalid FROM clause: ${JSON.stringify(query)}` ); } function extractCollectionAliases(query) { const aliasesById = /* @__PURE__ */ new Map(); function recordAlias(source) { if (!source) return; if (source.type === `collectionRef`) { const { id } = source.collection; const existing = aliasesById.get(id); if (existing) { existing.add(source.alias); } else { aliasesById.set(id, /* @__PURE__ */ new Set([source.alias])); } } else if (source.type === `queryRef`) { traverse(source.query); } } function traverse(q) { if (!q) return; recordAlias(q.from); if (q.join) { for (const joinClause of q.join) { recordAlias(joinClause.from); } } } traverse(query); return aliasesById; } function buildQueryFromConfig(config) { if (typeof config.query === `function`) { return buildQuery(config.query); } return getQueryIR(config.query); } function sendChangesToInput(input, changes, getKey) { const multiSetArray = []; for (const change of changes) { const key = getKey(change.value); if (change.type === `insert`) { multiSetArray.push([[key, change.value], 1]); } else if (change.type === `update`) { multiSetArray.push([[key, change.previousValue], -1]); multiSetArray.push([[key, change.value], 1]); } else { multiSetArray.push([[key, change.value], -1]); } } if (multiSetArray.length !== 0) { input.sendData(new MultiSet(multiSetArray)); } return multiSetArray.length; } function* splitUpdates(changes) { for (const change of changes) { if (change.type === `update`) { yield { type: `delete`, key: change.key, value: change.previousValue }; yield { type: `insert`, key: change.key, value: change.value }; } else { yield change; } } } function filterDuplicateInserts(changes, sentKeys) { const filtered = []; for (const change of changes) { if (change.type === `insert`) { if (sentKeys.has(change.key)) { continue; } sentKeys.add(change.key); } else if (change.type === `delete`) { sentKeys.delete(change.key); } filtered.push(change); } return filtered; } function trackBiggestSentValue(changes, current, sentKeys, comparator) { let biggest = current; let shouldResetLoadKey = false; for (const change of changes) { if (change.type === `delete`) continue; const isNewKey = !sentKeys.has(change.key); if (biggest === void 0) { biggest = change.value; shouldResetLoadKey = true; } else if (comparator(biggest, change.value) < 0) { biggest = change.value; shouldResetLoadKey = true; } else if (isNewKey) { shouldResetLoadKey = true; } } return { biggest, shouldResetLoadKey }; } function computeSubscriptionOrderByHints(query, alias) { const { orderBy, limit, offset } = query; const effectiveLimit = limit !== void 0 && offset !== void 0 ? limit + offset : limit; const normalizedOrderBy = orderBy ? normalizeOrderByPaths(orderBy, alias) : void 0; const canPassOrderBy = normalizedOrderBy?.every((clause) => { const exp = clause.expression; if (exp.type !== `ref`) return false; const path = exp.path; return Array.isArray(path) && path.length === 1; }) ?? false; return { orderBy: canPassOrderBy ? normalizedOrderBy : void 0, limit: canPassOrderBy ? effectiveLimit : void 0 }; } function computeOrderedLoadCursor(orderByInfo, biggestSentRow, lastLoadRequestKey, alias, limit) { const { orderBy, valueExtractorForRawRow, offset } = orderByInfo; const extractedValues = biggestSentRow ? valueExtractorForRawRow(biggestSentRow) : void 0; let minValues; if (extractedValues !== void 0) { minValues = Array.isArray(extractedValues) ? extractedValues : [extractedValues]; } const loadRequestKey = serializeValue({ minValues: minValues ?? null, offset, limit }); if (lastLoadRequestKey === loadRequestKey) { return void 0; } const normalizedOrderBy = normalizeOrderByPaths(orderBy, alias); return { minValues, normalizedOrderBy, loadRequestKey }; } export { buildQueryFromConfig, computeOrderedLoadCursor, computeSubscriptionOrderByHints, extractCollectionAliases, extractCollectionFromSource, extractCollectionsFromQuery, filterDuplicateInserts, sendChangesToInput, splitUpdates, trackBiggestSentValue }; //# sourceMappingURL=utils.js.map