@tanstack/db
Version:
A reactive client store for building super fast apps on sync
280 lines (279 loc) • 8.59 kB
JavaScript
"use strict";
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
const dbIvm = require("@tanstack/db-ivm");
const comparison = require("../utils/comparison.cjs");
const errorMessage = require("../error-message.cjs");
const arrayUtils = require("../utils/array-utils.cjs");
const baseIndex = require("./base-index.cjs");
function orderValue(value) {
return value instanceof Date ? value.getTime() : value;
}
class BasicIndex extends baseIndex.BaseIndex {
constructor() {
super(...arguments);
this.ax = /* @__PURE__ */ new Map();
this.J = [];
this.gG = [];
this.aK = /* @__PURE__ */ new Set();
}
/**
* Adds a value to the index
*/
add(key, item) {
const indexedValue = this.evaluateAddedValue(key, item);
const normalizedValue = comparison.normalizeValue(indexedValue);
this.dF(key, normalizedValue, indexedValue);
this.addRangeValue(indexedValue);
this.aK.add(key);
}
dF(key, normalizedValue, value) {
const keySet = this.ax.get(normalizedValue);
if (keySet) {
keySet.add(key);
} else {
const orderedValue = orderValue(value);
const insertIdx = arrayUtils.findInsertPositionInArray(
this.J,
orderedValue,
this.compareFn
);
this.ax.set(normalizedValue, /* @__PURE__ */ new Set([key]));
this.J.splice(insertIdx, 0, orderedValue);
this.gG.splice(insertIdx, 0, normalizedValue);
}
}
/**
* Removes a value from the index
*/
remove(key, item) {
let indexedValue;
try {
indexedValue = this.evaluateIndexExpression(item);
} catch (error) {
baseIndex.warnRemovalFailure(key, error);
this.aK.delete(key);
return;
}
const normalizedValue = comparison.normalizeValue(indexedValue);
this.cf(key, normalizedValue, indexedValue);
this.removeRangeValue(indexedValue);
this.aK.delete(key);
}
cf(key, normalizedValue, value) {
const keySet = this.ax.get(normalizedValue);
if (keySet?.has(key)) {
if (keySet.size === 1) {
const orderedValue = orderValue(value);
let sortedIndex = arrayUtils.findInsertPositionInArray(
this.J,
orderedValue,
this.compareFn
);
while (sortedIndex < this.J.length && this.compareFn(this.J[sortedIndex], orderedValue) === 0) {
if (comparison.areSameValueZeroEqual(
this.gG[sortedIndex],
normalizedValue
)) {
this.J.splice(sortedIndex, 1);
this.gG.splice(sortedIndex, 1);
break;
}
sortedIndex++;
}
this.ax.delete(normalizedValue);
}
keySet.delete(key);
}
}
/**
* Updates a value in the index
*/
update(key, oldItem, newItem) {
let oldIndexedValue;
let newIndexedValue;
try {
oldIndexedValue = this.evaluateIndexExpression(oldItem);
newIndexedValue = this.evaluateIndexExpression(newItem);
} catch {
this.remove(key, oldItem);
this.add(key, newItem);
return;
}
const oldValue = comparison.normalizeValue(oldIndexedValue);
const newValue = comparison.normalizeValue(newIndexedValue);
if (comparison.areSameValueZeroEqual(oldValue, newValue) && this.ax.get(newValue)?.has(key) && this.aK.has(key)) {
this.removeRangeValue(oldIndexedValue);
this.addRangeValue(newIndexedValue);
return;
}
this.cf(key, oldValue, oldIndexedValue);
this.removeRangeValue(oldIndexedValue);
this.dF(key, newValue, newIndexedValue);
this.addRangeValue(newIndexedValue);
this.aK.add(key);
}
/**
* Builds the index from a collection of entries
*/
build(entries) {
this.clear();
for (const [key, item] of entries) {
const indexedValue = this.evaluateAddedValue(key, item);
const value = comparison.normalizeValue(indexedValue);
const keys = this.ax.get(value);
if (keys) keys.add(key);
else {
this.ax.set(value, /* @__PURE__ */ new Set([key]));
this.J.push(orderValue(indexedValue));
this.gG.push(value);
}
this.addRangeValue(indexedValue);
this.aK.add(key);
}
const sortable = this.J.map((value, index) => ({
value,
exact: this.gG[index]
}));
sortable.sort((left, right) => this.compareFn(left.value, right.value));
this.J = sortable.map(({ value }) => value);
this.gG = sortable.map(({ exact }) => exact);
}
/**
* Clears all data from the index
*/
clear() {
this.ax.clear();
this.J = [];
this.gG = [];
this.aK.clear();
this.clearRangeValues();
}
lookup(operation, value) {
const result = this.lookupSupportedOperation(operation, value);
if (result !== void 0) return result;
throw new Error(
errorMessage.devBuild() && process.env.NODE_ENV !== `production` ? `Operation ${operation} not supported by BasicIndex` : errorMessage.codedMessage(100, { operation })
);
}
/**
* Gets the number of indexed keys
*/
get keyCount() {
return this.aK.size;
}
/**
* Performs an equality lookup - O(1)
*/
equalityLookup(value) {
const normalizedValue = comparison.normalizeValue(value);
return this.ax.get(normalizedValue) ?? /* @__PURE__ */ new Set();
}
/**
* Performs a range query using binary search - O(log n + m)
*/
rangeQuery(options = {}) {
const { from, to, fromInclusive = true, toInclusive = true } = options;
const result = /* @__PURE__ */ new Set();
if (this.J.length === 0) {
return result;
}
const orderedFrom = orderValue(from);
const orderedTo = orderValue(to);
const hasFrom = `from` in options;
const hasTo = `to` in options;
let startIdx = 0;
if (hasFrom) {
startIdx = arrayUtils.findInsertPositionInArray(
this.J,
orderedFrom,
this.compareFn
);
while (!fromInclusive && startIdx < this.J.length && this.compareFn(this.J[startIdx], orderedFrom) === 0) {
startIdx++;
}
}
let endIdx = this.J.length;
if (hasTo) {
endIdx = arrayUtils.findInsertPositionInArray(
this.J,
orderedTo,
this.compareFn
);
while (toInclusive && endIdx < this.J.length && this.compareFn(this.J[endIdx], orderedTo) === 0) {
endIdx++;
}
}
for (let i = startIdx; i < endIdx; i++) {
const keys = this.ax.get(this.gG[i]);
if (keys) {
keys.forEach((key) => result.add(key));
}
}
return result;
}
/**
* Returns the next n items in sorted order
*/
take(n, from, filterFn) {
const orderedFrom = orderValue(from);
let startIdx = arrayUtils.findInsertPositionInArray(
this.J,
orderedFrom,
this.compareFn
);
while (startIdx < this.J.length && this.compareFn(this.J[startIdx], orderedFrom) <= 0) {
startIdx++;
}
return this.dM(n, startIdx, 1, filterFn);
}
/**
* Returns the next n items in reverse sorted order
*/
takeReversed(n, from, filterFn) {
const orderedFrom = orderValue(from);
let startIdx = arrayUtils.findInsertPositionInArray(
this.J,
orderedFrom,
this.compareFn
) - 1;
while (startIdx >= 0 && this.compareFn(this.J[startIdx], orderedFrom) >= 0) {
startIdx--;
}
return this.dM(n, startIdx, -1, filterFn);
}
/**
* Returns the first n items in sorted order (from the start)
*/
takeFromStart(n, filterFn) {
return this.dM(n, 0, 1, filterFn);
}
/**
* Returns the first n items in reverse sorted order (from the end)
*/
takeReversedFromEnd(n, filterFn) {
return this.dM(n, this.J.length - 1, -1, filterFn);
}
dM(n, startIndex, step, filterFn) {
const result = [];
let index = startIndex;
while (index >= 0 && index < this.J.length && result.length < n) {
const groupValue = this.J[index];
const groupKeys = [];
do {
for (const key of this.ax.get(this.gG[index]) ?? []) {
groupKeys.push(key);
}
index += step;
} while (index >= 0 && index < this.J.length && this.compareFn(this.J[index], groupValue) === 0);
groupKeys.sort(step === 1 ? dbIvm.compareKeys : arrayUtils.compareKeysReversed);
for (const key of groupKeys) {
if (filterFn?.(key) ?? true) result.push(key);
if (result.length >= n) break;
}
}
return result;
}
}
baseIndex.builtInIndexResolverNames.set(BasicIndex, `BasicIndex`);
exports.BasicIndex = BasicIndex;
//# sourceMappingURL=basic-index.cjs.map