UNPKG

@tanstack/db

Version:

A reactive client store for building super fast apps on sync

1 lines • 18 kB
{"version":3,"file":"comparison.cjs","sources":["../../../src/utils/comparison.ts"],"sourcesContent":["import { durationFields, isTemporal } from '../utils'\nimport { getRuntimeReferenceIdentity } from '../query/runtime-reference-identity'\nimport { codedMessage, devBuild } from '../error-message'\nimport type { DurationFields } from '../utils'\nimport type { CompareOptions } from '../query/builder/types'\n\n/**\n * Whether a value has no IEEE-754 natural order: `NaN`, or an invalid Date\n * (whose timestamp is `NaN`). The query engine follows PostgreSQL float\n * semantics for these values — they are all equal to one another and greater\n * than every other (non-null) value — so the comparator and the WHERE\n * evaluator treat them explicitly instead of letting `NaN` compare unequal to\n * everything (which has no consistent order and cannot be indexed or sorted).\n */\nexport function isUnorderable(value: any): boolean {\n return (\n (typeof value === `number` && Number.isNaN(value)) ||\n (value instanceof Date && Number.isNaN(value.getTime()))\n )\n}\n\n/**\n * Universal comparison function for all data types\n * Handles null/undefined, strings, arrays, dates, objects, and primitives\n * Always sorts null/undefined values first\n */\nexport const ascComparator = (a: any, b: any, opts: CompareOptions): number => {\n const { nulls } = opts\n\n // Handle null/undefined\n if (a == null && b == null) return 0\n if (a == null) return nulls === `first` ? -1 : 1\n if (b == null) return nulls === `first` ? 1 : -1\n\n // Handle NaN / invalid Dates. Following PostgreSQL float semantics, they are\n // all equal and sort greater than every other non-null value. This keeps the\n // order total (NaN would otherwise compare equal to everything), so such\n // values can be sorted and stored in tree-based indexes.\n const aUnordered = isUnorderable(a)\n const bUnordered = isUnorderable(b)\n if (aUnordered && bUnordered) return 0\n if (aUnordered) return 1\n if (bUnordered) return -1\n\n // If both values are strings, apply the configured string collation.\n if (typeof a === `string` && typeof b === `string`) {\n if (opts.stringSort === `custom`) {\n return opts.compare(a, b)\n }\n if (opts.stringSort === `locale`) {\n return a.localeCompare(b, opts.locale, opts.localeOptions)\n }\n // For lexical sort we rely on direct comparison for primitive values\n }\n\n // if a and b are both arrays, compare them element by element\n if (Array.isArray(a) && Array.isArray(b)) {\n for (let i = 0; i < Math.min(a.length, b.length); i++) {\n const result = ascComparator(a[i], b[i], opts)\n if (result !== 0) {\n return result\n }\n }\n // All elements are equal up to the minimum length\n return a.length - b.length\n }\n\n // If both are dates, compare them\n if (a instanceof Date && b instanceof Date) {\n return a.getTime() - b.getTime()\n }\n\n // If both are Temporal objects, use compareTemporalValues for correct semantic ordering\n if (isTemporal(a) && isTemporal(b)) {\n return compareTemporalValues(a, b)\n }\n\n // Symbols have identity but no built-in order: relational comparison throws.\n // A stable runtime ID gives tree indexes a total order while preserving\n // equality only for the same symbol.\n const aIsSymbol = typeof a === `symbol`\n const bIsSymbol = typeof b === `symbol`\n if (aIsSymbol && bIsSymbol) {\n if (a === b) return 0\n return getRuntimeReferenceIdentity(a)[2] - getRuntimeReferenceIdentity(b)[2]\n }\n if (aIsSymbol) return 1\n if (bIsSymbol) return -1\n\n // If at least one of the values is an object, use stable IDs for comparison\n const aIsObject = typeof a === `object`\n const bIsObject = typeof b === `object`\n\n if (aIsObject || bIsObject) {\n // Symbols and objects share the runtime's reference-identity sequence.\n if (aIsObject && bIsObject) {\n const aId = getRuntimeReferenceIdentity(a)[2]\n const bId = getRuntimeReferenceIdentity(b)[2]\n return aId - bId\n }\n\n // If only one is an object, objects come after primitives\n if (aIsObject) return 1\n if (bIsObject) return -1\n }\n\n // For primitive values, use direct comparison\n if (a < b) return -1\n if (a > b) return 1\n return 0\n}\n\n/**\n * Descending comparator function for ordering values\n * Handles null/undefined as largest values (opposite of ascending)\n */\nexport const descComparator = (\n a: unknown,\n b: unknown,\n opts: CompareOptions,\n): number => {\n return a == null || b == null\n ? ascComparator(a, b, opts)\n : ascComparator(b, a, opts)\n}\n\nexport function indexObjectKind(value: any): string {\n if (isUint8Array(value)) return `Binary`\n if (isTemporal(value)) {\n const tag = value[Symbol.toStringTag]\n const fields = value as unknown as DurationFields\n return tag === `Temporal.Duration` &&\n (fields.years || fields.months || fields.weeks)\n ? `Temporal.Duration.calendar`\n : tag\n }\n if (Array.isArray(value)) return `Array`\n return `Object`\n}\n\nexport function makeComparator(\n opts: CompareOptions,\n scope: `values` | `index` = `values`,\n): (a: any, b: any) => number {\n const compare = opts.direction === `asc` ? ascComparator : descComparator\n if (scope !== `index`) return (a, b) => compare(a, b, opts)\n\n const direction = opts.direction === `asc` ? 1 : -1\n return (a, b) => {\n // An index needs one stable domain for each native comparison law. In\n // particular, Date order cannot be mixed with reference-object order.\n if (a != null && b != null) {\n if (a instanceof Date) a = a.getTime()\n if (b instanceof Date) b = b.getTime()\n const aType = typeof a\n const bType = typeof b\n if (aType !== bType) return (aType < bType ? -1 : 1) * direction\n if (aType === `object`) {\n const aKind = indexObjectKind(a)\n const bKind = indexObjectKind(b)\n if (aKind !== bKind) return (aKind < bKind ? -1 : 1) * direction\n // Binary content and unordered Temporal values use their exact keys\n // for stable storage order.\n if (\n aKind === `Binary` ||\n aKind === `Temporal.Duration.calendar` ||\n aKind === `Temporal.PlainMonthDay`\n ) {\n const left = normalizeValue(a)\n const right = normalizeValue(b)\n return (left < right ? -1 : left > right ? 1 : 0) * direction\n }\n }\n }\n return compare(a, b, opts)\n }\n}\n\n/**\n * Wraps a user comparator so an index crashes on a result that cannot order\n * its storage, instead of silently misplacing rows.\n */\nexport function makeCheckedComparator(\n compareFn: (a: any, b: any) => number,\n): (a: any, b: any) => number {\n return (a, b) => {\n const result: unknown = compareFn(a, b)\n if (typeof result !== `number` || Number.isNaN(result))\n throw new TypeError(\n devBuild() && process.env.NODE_ENV !== `production`\n ? `Index comparator must return a number, but returned ${String(result)}`\n : codedMessage(166, { result }),\n )\n return result\n }\n}\n\n/** Default comparator orders values in ascending order with nulls first and locale string comparison. */\nexport const defaultComparator = makeComparator({\n direction: `asc`,\n nulls: `first`,\n stringSort: `locale`,\n})\n\n/** Include host Buffers when the current realm has a different Uint8Array. */\nexport function isUint8Array(value: unknown): value is Uint8Array {\n return (\n value instanceof Uint8Array ||\n (typeof Buffer !== `undefined` && value instanceof Buffer)\n )\n}\n\n/** Compare two Uint8Arrays for content equality. */\nfunction areUint8ArraysEqual(a: Uint8Array, b: Uint8Array): boolean {\n if (a.byteLength !== b.byteLength) {\n return false\n }\n for (let i = 0; i < a.byteLength; i++) {\n if (a[i] !== b[i]) {\n return false\n }\n }\n return true\n}\n\nconst NORMALIZED_KEY_PREFIX = `\\u0000tanstack-db:`\nfunction normalizedKey(kind: string, value: string): string {\n return `${NORMALIZED_KEY_PREFIX}${kind}:${value}`\n}\n\nconst binaryKeys = new WeakMap<Uint8Array, string>()\n\nfunction normalizeBinary(value: Uint8Array): string {\n const cached = binaryKeys.get(value)\n if (cached) return cached\n let bytes: string\n if (value.byteLength <= 8192) {\n bytes = String.fromCharCode.apply(null, value as unknown as Array<number>)\n } else {\n const chunks: Array<string> = []\n for (let index = 0; index < value.byteLength; index += 8192) {\n chunks.push(\n String.fromCharCode.apply(\n null,\n value.subarray(index, index + 8192) as unknown as Array<number>,\n ),\n )\n }\n bytes = chunks.join(``)\n }\n const key = normalizedKey(`binary`, bytes)\n binaryKeys.set(value, key)\n return key\n}\n\n/**\n * Normalize a value for comparison and Map key usage\n * Converts values that can't be directly compared or used as Map keys\n * into comparable primitive representations\n */\nexport function normalizeValue(value: any): any {\n if (typeof value === `string`) {\n return value.startsWith(NORMALIZED_KEY_PREFIX)\n ? normalizedKey(`string`, value)\n : value\n }\n\n if (typeof value !== `object` || value === null) {\n return value\n }\n\n if (value instanceof Date) {\n return value.getTime()\n }\n\n if (isTemporal(value)) {\n const tag = value[Symbol.toStringTag]\n if (tag === 'Temporal.Duration') {\n const fields = value as unknown as DurationFields\n return normalizedKey(\n tag,\n durationFields.map((field) => fields[field]).join(','),\n )\n }\n return normalizedKey(tag, value.toString())\n }\n\n // Normalize Uint8Arrays/Buffers to a string representation for Map key usage\n // This enables content-based equality for binary data like ULIDs\n if (isUint8Array(value)) {\n return normalizeBinary(value)\n }\n\n return value\n}\n\n/**\n * Compare values using the equality semantics used by Map keys.\n */\nexport function areSameValueZeroEqual(a: unknown, b: unknown): boolean {\n return a === b || (Number.isNaN(a) && Number.isNaN(b))\n}\n\n/**\n * Compare two Temporal values of the same type, returning -1, 0, or 1.\n *\n * The tags check type agreement. Types without a static `.compare` (e.g.\n * `PlainMonthDay`) throw instead of using text order.\n *\n * Callers must ensure both arguments are Temporal objects; mixed types throw.\n */\nexport function compareTemporalValues(a: unknown, b: unknown): number {\n const aTag = (a as Record<symbol, unknown>)[Symbol.toStringTag]\n const bTag = (b as Record<symbol, unknown>)[Symbol.toStringTag]\n if (aTag !== bTag) {\n throw new TypeError(\n devBuild() && process.env.NODE_ENV !== `production`\n ? `Cannot order Temporal values of different types: ${aTag} vs ${bTag}`\n : codedMessage(167, { aTag, bTag }),\n )\n }\n const compare = (\n a as { constructor: { compare?: (x: unknown, y: unknown) => number } }\n ).constructor.compare\n if (typeof compare !== `function`) {\n throw new TypeError(\n devBuild() && process.env.NODE_ENV !== `production`\n ? `${aTag} has no defined ordering`\n : codedMessage(168, { aTag }),\n )\n }\n return compare(a, b)\n}\n\n/**\n * Order two non-null values, returning -1, 0, or 1.\n *\n * Temporal types intentionally throw from `valueOf` to prevent silent\n * miscomparison via the native relational operators — delegate to\n * `compareTemporalValues` for them. For everything else (numbers, strings,\n * Dates via `valueOf`, etc.) the native operators do the right thing.\n *\n * Callers must handle null/undefined themselves — this helper assumes both\n * arguments are non-null.\n */\nexport function compareValues(a: unknown, b: unknown): number {\n if (isTemporal(a) && isTemporal(b)) {\n return compareTemporalValues(a, b)\n }\n return (a as any) < (b as any) ? -1 : (a as any) > (b as any) ? 1 : 0\n}\n\n/**\n * Compare two values for equality, with special handling for Uint8Arrays and Buffers\n */\nexport function areValuesEqual(a: any, b: any): boolean {\n // Fast path for reference equality\n if (a === b) {\n return true\n }\n\n // Check for Uint8Array/Buffer comparison\n // If both are Uint8Arrays, compare by content\n if (isUint8Array(a) && isUint8Array(b)) {\n return areUint8ArraysEqual(a, b)\n }\n\n // Different types or not Uint8Arrays\n return false\n}\n"],"names":["isTemporal","getRuntimeReferenceIdentity","devBuild","codedMessage","durationFields"],"mappings":"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"}