@tanstack/db
Version:
A reactive client store for building super fast apps on sync
1 lines • 17.8 kB
Source Map (JSON)
{"version":3,"file":"ref-proxy.cjs","sources":["../../../../src/query/builder/ref-proxy.ts"],"sourcesContent":["import { PropRef, Value, isBasicOrAggregateExpression } from '../ir.js'\nimport { isRefProxy, registerRefProxy } from './ref-proxy-identity.js'\nimport { getWrapperExpressionName } from './wrapper-identity.js'\nimport type { BasicExpression } from '../ir.js'\nimport type { IsPlainObject, RefLeaf } from './types.js'\nimport type { VirtualRowProps } from '../../virtual-props.js'\n\nexport { isRefProxy } from './ref-proxy-identity.js'\n\nexport interface RefProxy<T = any> {\n /** @internal */\n readonly __refProxy: true\n /** @internal */\n readonly __path: Array<string>\n /** @internal */\n readonly __sourceAlias?: string\n /** @internal */\n readonly __type: T\n}\n\n/**\n * Virtual properties available on all row ref proxies.\n * These allow querying on sync status, origin, key, and collection ID.\n */\nexport type VirtualPropsRefProxy<\n TKey extends string | number = string | number,\n> = {\n readonly [K in keyof VirtualRowProps<TKey>]: RefLeaf<VirtualRowProps<TKey>[K]>\n}\n\n// Strip nullish members before deciding whether a schema field is traversable,\n// then restore them outside the proxy so the schema still requires a guard.\n// The tuple guard keeps exact null/undefined fields as leaves: `never` would\n// otherwise satisfy the plain-object check.\ntype SingleRowField<V, TKey extends string | number> = [\n NonNullable<V>,\n] extends [never]\n ? RefLeaf<V>\n : IsPlainObject<NonNullable<V>> extends true\n ?\n | SingleRowRefProxy<NonNullable<V>, TKey, false>\n | Extract<V, null | undefined>\n : RefLeaf<V>\n\n/**\n * Type for creating a RefProxy for a single row/type without namespacing\n * Used in collection indexes and where clauses\n *\n * Inferred row roots include virtual properties ($synced, $origin, $key,\n * $collectionId). The default exported shape is suitable for reusable helpers\n * that can accept either roots or recursively traversed user objects. Use the\n * third parameter as `true` when a helper specifically requires a row root.\n */\nexport type SingleRowRefProxy<\n T,\n TKey extends string | number = string | number,\n IncludeVirtualProps extends boolean = false,\n> =\n T extends Record<string, any>\n ? {\n [K in keyof T]: SingleRowField<T[K], TKey>\n } & RefProxy<T> &\n (IncludeVirtualProps extends true ? VirtualPropsRefProxy<TKey> : {})\n : RefProxy<T> &\n (IncludeVirtualProps extends true ? VirtualPropsRefProxy<TKey> : {})\n\n/**\n * Creates a proxy object that records property access paths for a single row\n * Used in collection indexes and where clauses\n */\nexport function createSingleRowRefProxy<\n T extends Record<string, any>,\n TKey extends string | number = string | number,\n>(): SingleRowRefProxy<T, TKey, true> {\n const cache = new Map<string, any>()\n\n function createProxy(path: Array<string>): any {\n const pathKey = JSON.stringify(path)\n if (cache.has(pathKey)) {\n return cache.get(pathKey)\n }\n\n const proxy = new Proxy({} as any, {\n get(target, prop, receiver) {\n if (prop === `__refProxy`) return true\n if (prop === `__path`) return path\n if (prop === `__sourceAlias`) return undefined\n if (prop === `__type`) return undefined // Type is only for TypeScript inference\n if (typeof prop === `symbol`) return Reflect.get(target, prop, receiver)\n\n const newPath = [...path, String(prop)]\n return createProxy(newPath)\n },\n\n has(target, prop) {\n if (\n prop === `__refProxy` ||\n prop === `__path` ||\n prop === `__sourceAlias` ||\n prop === `__type`\n )\n return true\n return Reflect.has(target, prop)\n },\n\n ownKeys(target) {\n return Reflect.ownKeys(target)\n },\n\n getOwnPropertyDescriptor(target, prop) {\n if (\n prop === `__refProxy` ||\n prop === `__path` ||\n prop === `__sourceAlias` ||\n prop === `__type`\n ) {\n return { enumerable: false, configurable: true }\n }\n return Reflect.getOwnPropertyDescriptor(target, prop)\n },\n })\n\n registerRefProxy(proxy)\n cache.set(pathKey, proxy)\n return proxy\n }\n\n // Return the root proxy that starts with an empty path\n return createProxy([]) as SingleRowRefProxy<T, TKey, true>\n}\n\n/**\n * Creates a proxy object that records property access paths\n * Used in callbacks like where, select, etc. to create type-safe references\n */\nexport function createRefProxy<T extends Record<string, any>>(\n aliases: Array<string>,\n): RefProxy<T> & T {\n const cache = new Map<string, any>()\n let accessId = 0 // Monotonic counter to record evaluation order\n\n function createProxy(path: Array<string>): any {\n const pathKey = JSON.stringify(path)\n if (cache.has(pathKey)) {\n return cache.get(pathKey)\n }\n\n const proxy = new Proxy({} as any, {\n get(target, prop, receiver) {\n if (prop === `__refProxy`) return true\n if (prop === `__path`) return path\n if (prop === `__sourceAlias`) return path[0]\n if (prop === `__type`) return undefined // Type is only for TypeScript inference\n if (typeof prop === `symbol`) return Reflect.get(target, prop, receiver)\n\n const newPath = [...path, String(prop)]\n return createProxy(newPath)\n },\n\n has(target, prop) {\n if (\n prop === `__refProxy` ||\n prop === `__path` ||\n prop === `__sourceAlias` ||\n prop === `__type`\n )\n return true\n return Reflect.has(target, prop)\n },\n\n ownKeys(target) {\n const id = ++accessId\n const sentinelKey = `__SPREAD_SENTINEL__${path.join(`.`)}__${id}`\n if (!Object.prototype.hasOwnProperty.call(target, sentinelKey)) {\n Object.defineProperty(target, sentinelKey, {\n enumerable: true,\n configurable: true,\n value: true,\n })\n }\n return Reflect.ownKeys(target)\n },\n\n getOwnPropertyDescriptor(target, prop) {\n if (\n prop === `__refProxy` ||\n prop === `__path` ||\n prop === `__sourceAlias` ||\n prop === `__type`\n ) {\n return { enumerable: false, configurable: true }\n }\n return Reflect.getOwnPropertyDescriptor(target, prop)\n },\n })\n\n registerRefProxy(proxy)\n cache.set(pathKey, proxy)\n return proxy\n }\n\n // Create the root proxy with all aliases as top-level properties\n const rootProxy = new Proxy({} as any, {\n get(target, prop, receiver) {\n if (prop === `__refProxy`) return true\n if (prop === `__path`) return []\n if (prop === `__sourceAlias`) return undefined\n if (prop === `__type`) return undefined // Type is only for TypeScript inference\n if (typeof prop === `symbol`) return Reflect.get(target, prop, receiver)\n\n const propStr = String(prop)\n if (aliases.includes(propStr) || aliases.includes(`*`)) {\n return createProxy([propStr])\n }\n\n return undefined\n },\n\n has(target, prop) {\n if (\n prop === `__refProxy` ||\n prop === `__path` ||\n prop === `__sourceAlias` ||\n prop === `__type`\n )\n return true\n if (typeof prop === `string` && aliases.includes(prop)) return true\n return Reflect.has(target, prop)\n },\n\n ownKeys(_target) {\n return [...aliases, `__refProxy`, `__path`, `__sourceAlias`, `__type`]\n },\n\n getOwnPropertyDescriptor(target, prop) {\n if (\n prop === `__refProxy` ||\n prop === `__path` ||\n prop === `__sourceAlias` ||\n prop === `__type`\n ) {\n return { enumerable: false, configurable: true }\n }\n if (typeof prop === `string` && aliases.includes(prop)) {\n return { enumerable: true, configurable: true }\n }\n return undefined\n },\n })\n\n registerRefProxy(rootProxy)\n return rootProxy\n}\n\n/**\n * Creates a ref proxy with $selected namespace for SELECT fields\n *\n * Adds a $selected property that allows accessing SELECT fields via $selected.fieldName syntax.\n * The $selected proxy creates paths like ['$selected', 'fieldName'] which directly reference\n * the $selected property on the namespaced row.\n *\n * @param aliases - Array of table aliases to create proxies for\n * @returns A ref proxy with table aliases and $selected namespace\n */\nexport function createRefProxyWithSelected<T extends Record<string, any>>(\n aliases: Array<string>,\n): RefProxy<T> &\n T & { $selected: SingleRowRefProxy<any, string | number, true> } {\n const baseProxy = createRefProxy(aliases)\n\n // Create a proxy for $selected that prefixes all paths with '$selected'\n const cache = new Map<string, any>()\n\n function createSelectedProxy(path: Array<string>): any {\n const pathKey = JSON.stringify(path)\n if (cache.has(pathKey)) {\n return cache.get(pathKey)\n }\n\n const proxy = new Proxy({} as any, {\n get(target, prop, receiver) {\n if (prop === `__refProxy`) return true\n if (prop === `__path`) return [`$selected`, ...path]\n if (prop === `__sourceAlias`) return `$selected`\n if (prop === `__type`) return undefined\n if (typeof prop === `symbol`) return Reflect.get(target, prop, receiver)\n\n const newPath = [...path, String(prop)]\n return createSelectedProxy(newPath)\n },\n\n has(target, prop) {\n if (\n prop === `__refProxy` ||\n prop === `__path` ||\n prop === `__sourceAlias` ||\n prop === `__type`\n )\n return true\n return Reflect.has(target, prop)\n },\n\n ownKeys(target) {\n return Reflect.ownKeys(target)\n },\n\n getOwnPropertyDescriptor(target, prop) {\n if (\n prop === `__refProxy` ||\n prop === `__path` ||\n prop === `__sourceAlias` ||\n prop === `__type`\n ) {\n return { enumerable: false, configurable: true }\n }\n return Reflect.getOwnPropertyDescriptor(target, prop)\n },\n })\n\n registerRefProxy(proxy)\n cache.set(pathKey, proxy)\n return proxy\n }\n\n const wrappedSelectedProxy = createSelectedProxy([])\n\n // Wrap the base proxy to also handle $selected access\n const selectedRootProxy = new Proxy(baseProxy, {\n get(target, prop, receiver) {\n if (prop === `$selected`) {\n return wrappedSelectedProxy\n }\n return Reflect.get(target, prop, receiver)\n },\n\n has(target, prop) {\n if (prop === `$selected`) return true\n return Reflect.has(target, prop)\n },\n\n ownKeys(target) {\n return [...Reflect.ownKeys(target), `$selected`]\n },\n\n getOwnPropertyDescriptor(target, prop) {\n if (prop === `$selected`) {\n return {\n enumerable: true,\n configurable: true,\n value: wrappedSelectedProxy,\n }\n }\n return Reflect.getOwnPropertyDescriptor(target, prop)\n },\n }) as RefProxy<T> &\n T & {\n $selected: SingleRowRefProxy<any, string | number, true>\n }\n registerRefProxy(selectedRootProxy)\n return selectedRootProxy\n}\n\n/**\n * Converts a value to an Expression.\n * If it's a RefProxy, creates a PropRef. Throws if the value is a\n * ToArrayWrapper, ConcatToArrayWrapper, CaseWhenWrapper, or MaterializeWrapper\n * (these must be used as direct select fields). Otherwise wraps it as a Value.\n */\nexport function toExpression<T = any>(value: T): BasicExpression<T>\nexport function toExpression(value: RefProxy<any>): BasicExpression<any>\nexport function toExpression(value: any): BasicExpression<any> {\n if (isRefProxy(value)) {\n return new PropRef(value.__path, value.__sourceAlias)\n }\n // toArray(), concat(toArray()), and materialize() must be used as direct\n // select fields, not inside expressions\n const name = getWrapperExpressionName(value)\n if (name) {\n throw new Error(\n `${name} cannot be used inside expressions (e.g., coalesce(), eq(), not()). ` +\n `Use ${name} directly as a select field value instead.`,\n )\n }\n // Only constructed expressions are IR; user values may have the same fields.\n if (isBasicOrAggregateExpression(value)) {\n return value as BasicExpression\n }\n return new Value(value)\n}\n\n/**\n * Helper to create a Value expression from a literal\n */\nexport function val<T>(value: T): BasicExpression<T> {\n return new Value(value)\n}\n"],"names":["registerRefProxy","isRefProxy","PropRef","getWrapperExpressionName","isBasicOrAggregateExpression","Value"],"mappings":"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"}