@tanstack/db 0.0.13 → 0.0.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/collection.cjs +117 -104
- package/dist/cjs/collection.cjs.map +1 -1
- package/dist/cjs/collection.d.cts +19 -22
- package/dist/cjs/index.cjs +35 -13
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs/index.d.cts +0 -1
- package/dist/cjs/query/builder/functions.cjs +107 -0
- package/dist/cjs/query/builder/functions.cjs.map +1 -0
- package/dist/cjs/query/builder/functions.d.cts +38 -0
- package/dist/cjs/query/builder/index.cjs +499 -0
- package/dist/cjs/query/builder/index.cjs.map +1 -0
- package/dist/cjs/query/builder/index.d.cts +324 -0
- package/dist/cjs/query/builder/ref-proxy.cjs +96 -0
- package/dist/cjs/query/builder/ref-proxy.cjs.map +1 -0
- package/dist/cjs/query/builder/ref-proxy.d.cts +28 -0
- package/dist/cjs/query/builder/types.d.cts +80 -0
- package/dist/cjs/query/compiler/evaluators.cjs +261 -0
- package/dist/cjs/query/compiler/evaluators.cjs.map +1 -0
- package/dist/cjs/query/compiler/evaluators.d.cts +11 -0
- package/dist/cjs/query/compiler/group-by.cjs +271 -0
- package/dist/cjs/query/compiler/group-by.cjs.map +1 -0
- package/dist/cjs/query/compiler/group-by.d.cts +7 -0
- package/dist/cjs/query/compiler/index.cjs +181 -0
- package/dist/cjs/query/compiler/index.cjs.map +1 -0
- package/dist/cjs/query/compiler/index.d.cts +15 -0
- package/dist/cjs/query/compiler/joins.cjs +116 -0
- package/dist/cjs/query/compiler/joins.cjs.map +1 -0
- package/dist/cjs/query/compiler/joins.d.cts +11 -0
- package/dist/cjs/query/compiler/order-by.cjs +89 -0
- package/dist/cjs/query/compiler/order-by.cjs.map +1 -0
- package/dist/cjs/query/compiler/order-by.d.cts +9 -0
- package/dist/cjs/query/compiler/select.cjs +57 -0
- package/dist/cjs/query/compiler/select.cjs.map +1 -0
- package/dist/cjs/query/compiler/select.d.cts +15 -0
- package/dist/cjs/query/index.d.cts +6 -5
- package/dist/cjs/query/ir.cjs +57 -0
- package/dist/cjs/query/ir.cjs.map +1 -0
- package/dist/cjs/query/ir.d.cts +81 -0
- package/dist/cjs/query/live-query-collection.cjs +224 -0
- package/dist/cjs/query/live-query-collection.cjs.map +1 -0
- package/dist/cjs/query/live-query-collection.d.cts +124 -0
- package/dist/cjs/transactions.cjs +20 -13
- package/dist/cjs/transactions.cjs.map +1 -1
- package/dist/cjs/transactions.d.cts +13 -4
- package/dist/cjs/types.d.cts +14 -1
- package/dist/esm/collection.d.ts +19 -22
- package/dist/esm/collection.js +118 -105
- package/dist/esm/collection.js.map +1 -1
- package/dist/esm/index.d.ts +0 -1
- package/dist/esm/index.js +34 -12
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/query/builder/functions.d.ts +38 -0
- package/dist/esm/query/builder/functions.js +107 -0
- package/dist/esm/query/builder/functions.js.map +1 -0
- package/dist/esm/query/builder/index.d.ts +324 -0
- package/dist/esm/query/builder/index.js +499 -0
- package/dist/esm/query/builder/index.js.map +1 -0
- package/dist/esm/query/builder/ref-proxy.d.ts +28 -0
- package/dist/esm/query/builder/ref-proxy.js +96 -0
- package/dist/esm/query/builder/ref-proxy.js.map +1 -0
- package/dist/esm/query/builder/types.d.ts +80 -0
- package/dist/esm/query/compiler/evaluators.d.ts +11 -0
- package/dist/esm/query/compiler/evaluators.js +261 -0
- package/dist/esm/query/compiler/evaluators.js.map +1 -0
- package/dist/esm/query/compiler/group-by.d.ts +7 -0
- package/dist/esm/query/compiler/group-by.js +271 -0
- package/dist/esm/query/compiler/group-by.js.map +1 -0
- package/dist/esm/query/compiler/index.d.ts +15 -0
- package/dist/esm/query/compiler/index.js +181 -0
- package/dist/esm/query/compiler/index.js.map +1 -0
- package/dist/esm/query/compiler/joins.d.ts +11 -0
- package/dist/esm/query/compiler/joins.js +116 -0
- package/dist/esm/query/compiler/joins.js.map +1 -0
- package/dist/esm/query/compiler/order-by.d.ts +9 -0
- package/dist/esm/query/compiler/order-by.js +89 -0
- package/dist/esm/query/compiler/order-by.js.map +1 -0
- package/dist/esm/query/compiler/select.d.ts +15 -0
- package/dist/esm/query/compiler/select.js +57 -0
- package/dist/esm/query/compiler/select.js.map +1 -0
- package/dist/esm/query/index.d.ts +6 -5
- package/dist/esm/query/ir.d.ts +81 -0
- package/dist/esm/query/ir.js +57 -0
- package/dist/esm/query/ir.js.map +1 -0
- package/dist/esm/query/live-query-collection.d.ts +124 -0
- package/dist/esm/query/live-query-collection.js +224 -0
- package/dist/esm/query/live-query-collection.js.map +1 -0
- package/dist/esm/transactions.d.ts +13 -4
- package/dist/esm/transactions.js +20 -13
- package/dist/esm/transactions.js.map +1 -1
- package/dist/esm/types.d.ts +14 -1
- package/package.json +3 -4
- package/src/collection.ts +152 -129
- package/src/index.ts +0 -1
- package/src/query/builder/functions.ts +267 -0
- package/src/query/builder/index.ts +648 -0
- package/src/query/builder/ref-proxy.ts +156 -0
- package/src/query/builder/types.ts +278 -0
- package/src/query/compiler/evaluators.ts +315 -0
- package/src/query/compiler/group-by.ts +428 -0
- package/src/query/compiler/index.ts +276 -0
- package/src/query/compiler/joins.ts +228 -0
- package/src/query/compiler/order-by.ts +139 -0
- package/src/query/compiler/select.ts +173 -0
- package/src/query/index.ts +64 -5
- package/src/query/ir.ts +128 -0
- package/src/query/live-query-collection.ts +509 -0
- package/src/transactions.ts +34 -19
- package/src/types.ts +16 -1
- package/dist/cjs/query/compiled-query.cjs +0 -160
- package/dist/cjs/query/compiled-query.cjs.map +0 -1
- package/dist/cjs/query/compiled-query.d.cts +0 -20
- package/dist/cjs/query/evaluators.cjs +0 -161
- package/dist/cjs/query/evaluators.cjs.map +0 -1
- package/dist/cjs/query/evaluators.d.cts +0 -14
- package/dist/cjs/query/extractors.cjs +0 -122
- package/dist/cjs/query/extractors.cjs.map +0 -1
- package/dist/cjs/query/extractors.d.cts +0 -22
- package/dist/cjs/query/functions.cjs +0 -152
- package/dist/cjs/query/functions.cjs.map +0 -1
- package/dist/cjs/query/functions.d.cts +0 -21
- package/dist/cjs/query/group-by.cjs +0 -88
- package/dist/cjs/query/group-by.cjs.map +0 -1
- package/dist/cjs/query/group-by.d.cts +0 -40
- package/dist/cjs/query/joins.cjs +0 -141
- package/dist/cjs/query/joins.cjs.map +0 -1
- package/dist/cjs/query/joins.d.cts +0 -14
- package/dist/cjs/query/order-by.cjs +0 -185
- package/dist/cjs/query/order-by.cjs.map +0 -1
- package/dist/cjs/query/order-by.d.cts +0 -3
- package/dist/cjs/query/pipeline-compiler.cjs +0 -89
- package/dist/cjs/query/pipeline-compiler.cjs.map +0 -1
- package/dist/cjs/query/pipeline-compiler.d.cts +0 -10
- package/dist/cjs/query/query-builder.cjs +0 -307
- package/dist/cjs/query/query-builder.cjs.map +0 -1
- package/dist/cjs/query/query-builder.d.cts +0 -225
- package/dist/cjs/query/schema.d.cts +0 -100
- package/dist/cjs/query/select.cjs +0 -130
- package/dist/cjs/query/select.cjs.map +0 -1
- package/dist/cjs/query/select.d.cts +0 -3
- package/dist/cjs/query/types.d.cts +0 -189
- package/dist/cjs/query/utils.cjs +0 -154
- package/dist/cjs/query/utils.cjs.map +0 -1
- package/dist/cjs/query/utils.d.cts +0 -37
- package/dist/cjs/utils.cjs +0 -17
- package/dist/cjs/utils.cjs.map +0 -1
- package/dist/cjs/utils.d.cts +0 -3
- package/dist/esm/query/compiled-query.d.ts +0 -20
- package/dist/esm/query/compiled-query.js +0 -160
- package/dist/esm/query/compiled-query.js.map +0 -1
- package/dist/esm/query/evaluators.d.ts +0 -14
- package/dist/esm/query/evaluators.js +0 -161
- package/dist/esm/query/evaluators.js.map +0 -1
- package/dist/esm/query/extractors.d.ts +0 -22
- package/dist/esm/query/extractors.js +0 -122
- package/dist/esm/query/extractors.js.map +0 -1
- package/dist/esm/query/functions.d.ts +0 -21
- package/dist/esm/query/functions.js +0 -152
- package/dist/esm/query/functions.js.map +0 -1
- package/dist/esm/query/group-by.d.ts +0 -40
- package/dist/esm/query/group-by.js +0 -88
- package/dist/esm/query/group-by.js.map +0 -1
- package/dist/esm/query/joins.d.ts +0 -14
- package/dist/esm/query/joins.js +0 -141
- package/dist/esm/query/joins.js.map +0 -1
- package/dist/esm/query/order-by.d.ts +0 -3
- package/dist/esm/query/order-by.js +0 -185
- package/dist/esm/query/order-by.js.map +0 -1
- package/dist/esm/query/pipeline-compiler.d.ts +0 -10
- package/dist/esm/query/pipeline-compiler.js +0 -89
- package/dist/esm/query/pipeline-compiler.js.map +0 -1
- package/dist/esm/query/query-builder.d.ts +0 -225
- package/dist/esm/query/query-builder.js +0 -307
- package/dist/esm/query/query-builder.js.map +0 -1
- package/dist/esm/query/schema.d.ts +0 -100
- package/dist/esm/query/select.d.ts +0 -3
- package/dist/esm/query/select.js +0 -130
- package/dist/esm/query/select.js.map +0 -1
- package/dist/esm/query/types.d.ts +0 -189
- package/dist/esm/query/utils.d.ts +0 -37
- package/dist/esm/query/utils.js +0 -154
- package/dist/esm/query/utils.js.map +0 -1
- package/dist/esm/utils.d.ts +0 -3
- package/dist/esm/utils.js +0 -17
- package/dist/esm/utils.js.map +0 -1
- package/src/query/compiled-query.ts +0 -234
- package/src/query/evaluators.ts +0 -250
- package/src/query/extractors.ts +0 -214
- package/src/query/functions.ts +0 -297
- package/src/query/group-by.ts +0 -139
- package/src/query/joins.ts +0 -260
- package/src/query/order-by.ts +0 -264
- package/src/query/pipeline-compiler.ts +0 -149
- package/src/query/query-builder.ts +0 -902
- package/src/query/schema.ts +0 -268
- package/src/query/select.ts +0 -208
- package/src/query/types.ts +0 -418
- package/src/query/utils.ts +0 -245
- package/src/utils.ts +0 -15
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.cjs","sources":["../../../../src/query/builder/index.ts"],"sourcesContent":["import { CollectionImpl } from \"../../collection.js\"\nimport { CollectionRef, QueryRef } from \"../ir.js\"\nimport { createRefProxy, isRefProxy, toExpression } from \"./ref-proxy.js\"\nimport type { NamespacedRow } from \"../../types.js\"\nimport type {\n Aggregate,\n BasicExpression,\n JoinClause,\n OrderBy,\n OrderByClause,\n OrderByDirection,\n QueryIR,\n} from \"../ir.js\"\nimport type {\n Context,\n GroupByCallback,\n JoinOnCallback,\n MergeContext,\n MergeContextWithJoinType,\n OrderByCallback,\n RefProxyForContext,\n ResultTypeFromSelect,\n SchemaFromSource,\n SelectObject,\n Source,\n WhereCallback,\n WithResult,\n} from \"./types.js\"\n\nexport class BaseQueryBuilder<TContext extends Context = Context> {\n private readonly query: Partial<QueryIR> = {}\n\n constructor(query: Partial<QueryIR> = {}) {\n this.query = { ...query }\n }\n\n /**\n * Creates a CollectionRef or QueryRef from a source object\n * @param source - An object with a single key-value pair\n * @param context - Context string for error messages (e.g., \"from clause\", \"join clause\")\n * @returns A tuple of [alias, ref] where alias is the source key and ref is the created reference\n */\n private _createRefForSource<TSource extends Source>(\n source: TSource,\n context: string\n ): [string, CollectionRef | QueryRef] {\n if (Object.keys(source).length !== 1) {\n throw new Error(`Only one source is allowed in the ${context}`)\n }\n\n const alias = Object.keys(source)[0]!\n const sourceValue = source[alias]\n\n let ref: CollectionRef | QueryRef\n\n if (sourceValue instanceof CollectionImpl) {\n ref = new CollectionRef(sourceValue, alias)\n } else if (sourceValue instanceof BaseQueryBuilder) {\n const subQuery = sourceValue._getQuery()\n if (!(subQuery as Partial<QueryIR>).from) {\n throw new Error(\n `A sub query passed to a ${context} must have a from clause itself`\n )\n }\n ref = new QueryRef(subQuery, alias)\n } else {\n throw new Error(`Invalid source`)\n }\n\n return [alias, ref]\n }\n\n /**\n * Specify the source table or subquery for the query\n *\n * @param source - An object with a single key-value pair where the key is the table alias and the value is a Collection or subquery\n * @returns A QueryBuilder with the specified source\n *\n * @example\n * ```ts\n * // Query from a collection\n * query.from({ users: usersCollection })\n *\n * // Query from a subquery\n * const activeUsers = query.from({ u: usersCollection }).where(({u}) => u.active)\n * query.from({ activeUsers })\n * ```\n */\n from<TSource extends Source>(\n source: TSource\n ): QueryBuilder<{\n baseSchema: SchemaFromSource<TSource>\n schema: SchemaFromSource<TSource>\n fromSourceName: keyof TSource & string\n hasJoins: false\n }> {\n const [, from] = this._createRefForSource(source, `from clause`)\n\n return new BaseQueryBuilder({\n ...this.query,\n from,\n }) as any\n }\n\n /**\n * Join another table or subquery to the current query\n *\n * @param source - An object with a single key-value pair where the key is the table alias and the value is a Collection or subquery\n * @param onCallback - A function that receives table references and returns the join condition\n * @param type - The type of join: 'inner', 'left', 'right', or 'full' (defaults to 'left')\n * @returns A QueryBuilder with the joined table available\n *\n * @example\n * ```ts\n * // Left join users with posts\n * query\n * .from({ users: usersCollection })\n * .join({ posts: postsCollection }, ({users, posts}) => eq(users.id, posts.userId))\n *\n * // Inner join with explicit type\n * query\n * .from({ u: usersCollection })\n * .join({ p: postsCollection }, ({u, p}) => eq(u.id, p.userId), 'inner')\n * ```\n *\n * // Join with a subquery\n * const activeUsers = query.from({ u: usersCollection }).where(({u}) => u.active)\n * query\n * .from({ activeUsers })\n * .join({ p: postsCollection }, ({u, p}) => eq(u.id, p.userId))\n */\n join<\n TSource extends Source,\n TJoinType extends `inner` | `left` | `right` | `full` = `left`,\n >(\n source: TSource,\n onCallback: JoinOnCallback<\n MergeContext<TContext, SchemaFromSource<TSource>>\n >,\n type: TJoinType = `left` as TJoinType\n ): QueryBuilder<\n MergeContextWithJoinType<TContext, SchemaFromSource<TSource>, TJoinType>\n > {\n const [alias, from] = this._createRefForSource(source, `join clause`)\n\n // Create a temporary context for the callback\n const currentAliases = this._getCurrentAliases()\n const newAliases = [...currentAliases, alias]\n const refProxy = createRefProxy(newAliases) as RefProxyForContext<\n MergeContext<TContext, SchemaFromSource<TSource>>\n >\n\n // Get the join condition expression\n const onExpression = onCallback(refProxy)\n\n // Extract left and right from the expression\n // For now, we'll assume it's an eq function with two arguments\n let left: BasicExpression\n let right: BasicExpression\n\n if (\n onExpression.type === `func` &&\n onExpression.name === `eq` &&\n onExpression.args.length === 2\n ) {\n left = onExpression.args[0]!\n right = onExpression.args[1]!\n } else {\n throw new Error(`Join condition must be an equality expression`)\n }\n\n const joinClause: JoinClause = {\n from,\n type,\n left,\n right,\n }\n\n const existingJoins = this.query.join || []\n\n return new BaseQueryBuilder({\n ...this.query,\n join: [...existingJoins, joinClause],\n }) as any\n }\n\n /**\n * Filter rows based on a condition\n *\n * @param callback - A function that receives table references and returns an expression\n * @returns A QueryBuilder with the where condition applied\n *\n * @example\n * ```ts\n * // Simple condition\n * query\n * .from({ users: usersCollection })\n * .where(({users}) => gt(users.age, 18))\n *\n * // Multiple conditions\n * query\n * .from({ users: usersCollection })\n * .where(({users}) => and(\n * gt(users.age, 18),\n * eq(users.active, true)\n * ))\n *\n * // Multiple where calls are ANDed together\n * query\n * .from({ users: usersCollection })\n * .where(({users}) => gt(users.age, 18))\n * .where(({users}) => eq(users.active, true))\n * ```\n */\n where(callback: WhereCallback<TContext>): QueryBuilder<TContext> {\n const aliases = this._getCurrentAliases()\n const refProxy = createRefProxy(aliases) as RefProxyForContext<TContext>\n const expression = callback(refProxy)\n\n const existingWhere = this.query.where || []\n\n return new BaseQueryBuilder({\n ...this.query,\n where: [...existingWhere, expression],\n }) as any\n }\n\n /**\n * Filter grouped rows based on aggregate conditions\n *\n * @param callback - A function that receives table references and returns an expression\n * @returns A QueryBuilder with the having condition applied\n *\n * @example\n * ```ts\n * // Filter groups by count\n * query\n * .from({ posts: postsCollection })\n * .groupBy(({posts}) => posts.userId)\n * .having(({posts}) => gt(count(posts.id), 5))\n *\n * // Filter by average\n * query\n * .from({ orders: ordersCollection })\n * .groupBy(({orders}) => orders.customerId)\n * .having(({orders}) => gt(avg(orders.total), 100))\n *\n * // Multiple having calls are ANDed together\n * query\n * .from({ orders: ordersCollection })\n * .groupBy(({orders}) => orders.customerId)\n * .having(({orders}) => gt(count(orders.id), 5))\n * .having(({orders}) => gt(avg(orders.total), 100))\n * ```\n */\n having(callback: WhereCallback<TContext>): QueryBuilder<TContext> {\n const aliases = this._getCurrentAliases()\n const refProxy = createRefProxy(aliases) as RefProxyForContext<TContext>\n const expression = callback(refProxy)\n\n const existingHaving = this.query.having || []\n\n return new BaseQueryBuilder({\n ...this.query,\n having: [...existingHaving, expression],\n }) as any\n }\n\n /**\n * Select specific columns or computed values from the query\n *\n * @param callback - A function that receives table references and returns an object with selected fields or expressions\n * @returns A QueryBuilder that returns only the selected fields\n *\n * @example\n * ```ts\n * // Select specific columns\n * query\n * .from({ users: usersCollection })\n * .select(({users}) => ({\n * name: users.name,\n * email: users.email\n * }))\n *\n * // Select with computed values\n * query\n * .from({ users: usersCollection })\n * .select(({users}) => ({\n * fullName: concat(users.firstName, ' ', users.lastName),\n * ageInMonths: mul(users.age, 12)\n * }))\n *\n * // Select with aggregates (requires GROUP BY)\n * query\n * .from({ posts: postsCollection })\n * .groupBy(({posts}) => posts.userId)\n * .select(({posts, count}) => ({\n * userId: posts.userId,\n * postCount: count(posts.id)\n * }))\n * ```\n */\n select<TSelectObject extends SelectObject>(\n callback: (refs: RefProxyForContext<TContext>) => TSelectObject\n ): QueryBuilder<WithResult<TContext, ResultTypeFromSelect<TSelectObject>>> {\n const aliases = this._getCurrentAliases()\n const refProxy = createRefProxy(aliases) as RefProxyForContext<TContext>\n const selectObject = callback(refProxy)\n\n // Check if any tables were spread during the callback\n const spreadSentinels = (refProxy as any).__spreadSentinels as Set<string>\n\n // Convert the select object to use expressions, including spread sentinels\n const select: Record<string, BasicExpression | Aggregate> = {}\n\n // First, add spread sentinels for any tables that were spread\n for (const spreadAlias of spreadSentinels) {\n const sentinelKey = `__SPREAD_SENTINEL__${spreadAlias}`\n select[sentinelKey] = toExpression(spreadAlias) // Use alias as a simple reference\n }\n\n // Then add the explicit select fields\n for (const [key, value] of Object.entries(selectObject)) {\n if (isRefProxy(value)) {\n select[key] = toExpression(value)\n } else if (\n typeof value === `object` &&\n `type` in value &&\n (value.type === `agg` || value.type === `func`)\n ) {\n select[key] = value as BasicExpression | Aggregate\n } else {\n select[key] = toExpression(value)\n }\n }\n\n return new BaseQueryBuilder({\n ...this.query,\n select,\n fnSelect: undefined, // remove the fnSelect clause if it exists\n }) as any\n }\n\n /**\n * Sort the query results by one or more columns\n *\n * @param callback - A function that receives table references and returns the field to sort by\n * @param direction - Sort direction: 'asc' for ascending, 'desc' for descending (defaults to 'asc')\n * @returns A QueryBuilder with the ordering applied\n *\n * @example\n * ```ts\n * // Sort by a single column\n * query\n * .from({ users: usersCollection })\n * .orderBy(({users}) => users.name)\n *\n * // Sort descending\n * query\n * .from({ users: usersCollection })\n * .orderBy(({users}) => users.createdAt, 'desc')\n *\n * // Multiple sorts (chain orderBy calls)\n * query\n * .from({ users: usersCollection })\n * .orderBy(({users}) => users.lastName)\n * .orderBy(({users}) => users.firstName)\n * ```\n */\n orderBy(\n callback: OrderByCallback<TContext>,\n direction: OrderByDirection = `asc`\n ): QueryBuilder<TContext> {\n const aliases = this._getCurrentAliases()\n const refProxy = createRefProxy(aliases) as RefProxyForContext<TContext>\n const result = callback(refProxy)\n\n // Create the new OrderBy structure with expression and direction\n const orderByClause: OrderByClause = {\n expression: toExpression(result),\n direction,\n }\n\n const existingOrderBy: OrderBy = this.query.orderBy || []\n\n return new BaseQueryBuilder({\n ...this.query,\n orderBy: [...existingOrderBy, orderByClause],\n }) as any\n }\n\n /**\n * Group rows by one or more columns for aggregation\n *\n * @param callback - A function that receives table references and returns the field(s) to group by\n * @returns A QueryBuilder with grouping applied (enables aggregate functions in SELECT and HAVING)\n *\n * @example\n * ```ts\n * // Group by a single column\n * query\n * .from({ posts: postsCollection })\n * .groupBy(({posts}) => posts.userId)\n * .select(({posts, count}) => ({\n * userId: posts.userId,\n * postCount: count()\n * }))\n *\n * // Group by multiple columns\n * query\n * .from({ sales: salesCollection })\n * .groupBy(({sales}) => [sales.region, sales.category])\n * .select(({sales, sum}) => ({\n * region: sales.region,\n * category: sales.category,\n * totalSales: sum(sales.amount)\n * }))\n * ```\n */\n groupBy(callback: GroupByCallback<TContext>): QueryBuilder<TContext> {\n const aliases = this._getCurrentAliases()\n const refProxy = createRefProxy(aliases) as RefProxyForContext<TContext>\n const result = callback(refProxy)\n\n const newExpressions = Array.isArray(result)\n ? result.map((r) => toExpression(r))\n : [toExpression(result)]\n\n // Replace existing groupBy expressions instead of extending them\n return new BaseQueryBuilder({\n ...this.query,\n groupBy: newExpressions,\n }) as any\n }\n\n /**\n * Limit the number of rows returned by the query\n * `orderBy` is required for `limit`\n *\n * @param count - Maximum number of rows to return\n * @returns A QueryBuilder with the limit applied\n *\n * @example\n * ```ts\n * // Get top 5 posts by likes\n * query\n * .from({ posts: postsCollection })\n * .orderBy(({posts}) => posts.likes, 'desc')\n * .limit(5)\n * ```\n */\n limit(count: number): QueryBuilder<TContext> {\n return new BaseQueryBuilder({\n ...this.query,\n limit: count,\n }) as any\n }\n\n /**\n * Skip a number of rows before returning results\n * `orderBy` is required for `offset`\n *\n * @param count - Number of rows to skip\n * @returns A QueryBuilder with the offset applied\n *\n * @example\n * ```ts\n * // Get second page of results\n * query\n * .from({ posts: postsCollection })\n * .orderBy(({posts}) => posts.createdAt, 'desc')\n * .offset(page * pageSize)\n * .limit(pageSize)\n * ```\n */\n offset(count: number): QueryBuilder<TContext> {\n return new BaseQueryBuilder({\n ...this.query,\n offset: count,\n }) as any\n }\n\n // Helper methods\n private _getCurrentAliases(): Array<string> {\n const aliases: Array<string> = []\n\n // Add the from alias\n if (this.query.from) {\n aliases.push(this.query.from.alias)\n }\n\n // Add join aliases\n if (this.query.join) {\n for (const join of this.query.join) {\n aliases.push(join.from.alias)\n }\n }\n\n return aliases\n }\n\n /**\n * Functional variants of the query builder\n * These are imperative function that are called for ery row.\n * Warning: that these cannot be optimized by the query compiler, and may prevent\n * some type of optimizations being possible.\n * @example\n * ```ts\n * q.fn.select((row) => ({\n * name: row.user.name.toUpperCase(),\n * age: row.user.age + 1,\n * }))\n * ```\n */\n get fn() {\n const builder = this\n return {\n /**\n * Select fields using a function that operates on each row\n * Warning: This cannot be optimized by the query compiler\n *\n * @param callback - A function that receives a row and returns the selected value\n * @returns A QueryBuilder with functional selection applied\n *\n * @example\n * ```ts\n * // Functional select (not optimized)\n * query\n * .from({ users: usersCollection })\n * .fn.select(row => ({\n * name: row.users.name.toUpperCase(),\n * age: row.users.age + 1,\n * }))\n * ```\n */\n select<TFuncSelectResult>(\n callback: (row: TContext[`schema`]) => TFuncSelectResult\n ): QueryBuilder<WithResult<TContext, TFuncSelectResult>> {\n return new BaseQueryBuilder({\n ...builder.query,\n select: undefined, // remove the select clause if it exists\n fnSelect: callback,\n })\n },\n /**\n * Filter rows using a function that operates on each row\n * Warning: This cannot be optimized by the query compiler\n *\n * @param callback - A function that receives a row and returns a boolean\n * @returns A QueryBuilder with functional filtering applied\n *\n * @example\n * ```ts\n * // Functional where (not optimized)\n * query\n * .from({ users: usersCollection })\n * .fn.where(row => row.users.name.startsWith('A'))\n * ```\n */\n where(\n callback: (row: TContext[`schema`]) => any\n ): QueryBuilder<TContext> {\n return new BaseQueryBuilder({\n ...builder.query,\n fnWhere: [\n ...(builder.query.fnWhere || []),\n callback as (row: NamespacedRow) => any,\n ],\n })\n },\n /**\n * Filter grouped rows using a function that operates on each aggregated row\n * Warning: This cannot be optimized by the query compiler\n *\n * @param callback - A function that receives an aggregated row and returns a boolean\n * @returns A QueryBuilder with functional having filter applied\n *\n * @example\n * ```ts\n * // Functional having (not optimized)\n * query\n * .from({ posts: postsCollection })\n * .groupBy(({posts}) => posts.userId)\n * .fn.having(row => row.count > 5)\n * ```\n */\n having(\n callback: (row: TContext[`schema`]) => any\n ): QueryBuilder<TContext> {\n return new BaseQueryBuilder({\n ...builder.query,\n fnHaving: [\n ...(builder.query.fnHaving || []),\n callback as (row: NamespacedRow) => any,\n ],\n })\n },\n }\n }\n\n _getQuery(): QueryIR {\n if (!this.query.from) {\n throw new Error(`Query must have a from clause`)\n }\n return this.query as QueryIR\n }\n}\n\n// Internal function to build a query from a callback\n// used by liveQueryCollectionOptions.query\nexport function buildQuery<TContext extends Context>(\n fn: (builder: InitialQueryBuilder) => QueryBuilder<TContext>\n): QueryIR {\n const result = fn(new BaseQueryBuilder())\n return getQueryIR(result)\n}\n\n// Internal function to get the QueryIR from a builder\nexport function getQueryIR(\n builder: BaseQueryBuilder | QueryBuilder<any> | InitialQueryBuilder\n): QueryIR {\n return (builder as unknown as BaseQueryBuilder)._getQuery()\n}\n\n// Type-only exports for the query builder\nexport type InitialQueryBuilder = Pick<BaseQueryBuilder<Context>, `from`>\n\nexport type InitialQueryBuilderConstructor = new () => InitialQueryBuilder\n\nexport type QueryBuilder<TContext extends Context> = Omit<\n BaseQueryBuilder<TContext>,\n `from` | `_getQuery`\n>\n\n// Main query builder class alias with the constructor type modified to hide all\n// but the from method on the initial instance\nexport const Query: InitialQueryBuilderConstructor = BaseQueryBuilder\n\n// Helper type to extract context from a QueryBuilder\nexport type ExtractContext<T> =\n T extends BaseQueryBuilder<infer TContext>\n ? TContext\n : T extends QueryBuilder<infer TContext>\n ? TContext\n : never\n\n// Export the types from types.ts for convenience\nexport type { Context, Source, GetResult } from \"./types.js\"\n"],"names":["CollectionImpl","CollectionRef","QueryRef","refProxy","createRefProxy","toExpression","isRefProxy"],"mappings":"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"}
|
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
import { OrderByDirection, QueryIR } from '../ir.js';
|
|
2
|
+
import { Context, GroupByCallback, JoinOnCallback, MergeContext, MergeContextWithJoinType, OrderByCallback, RefProxyForContext, ResultTypeFromSelect, SchemaFromSource, SelectObject, Source, WhereCallback, WithResult } from './types.js';
|
|
3
|
+
export declare class BaseQueryBuilder<TContext extends Context = Context> {
|
|
4
|
+
private readonly query;
|
|
5
|
+
constructor(query?: Partial<QueryIR>);
|
|
6
|
+
/**
|
|
7
|
+
* Creates a CollectionRef or QueryRef from a source object
|
|
8
|
+
* @param source - An object with a single key-value pair
|
|
9
|
+
* @param context - Context string for error messages (e.g., "from clause", "join clause")
|
|
10
|
+
* @returns A tuple of [alias, ref] where alias is the source key and ref is the created reference
|
|
11
|
+
*/
|
|
12
|
+
private _createRefForSource;
|
|
13
|
+
/**
|
|
14
|
+
* Specify the source table or subquery for the query
|
|
15
|
+
*
|
|
16
|
+
* @param source - An object with a single key-value pair where the key is the table alias and the value is a Collection or subquery
|
|
17
|
+
* @returns A QueryBuilder with the specified source
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```ts
|
|
21
|
+
* // Query from a collection
|
|
22
|
+
* query.from({ users: usersCollection })
|
|
23
|
+
*
|
|
24
|
+
* // Query from a subquery
|
|
25
|
+
* const activeUsers = query.from({ u: usersCollection }).where(({u}) => u.active)
|
|
26
|
+
* query.from({ activeUsers })
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
from<TSource extends Source>(source: TSource): QueryBuilder<{
|
|
30
|
+
baseSchema: SchemaFromSource<TSource>;
|
|
31
|
+
schema: SchemaFromSource<TSource>;
|
|
32
|
+
fromSourceName: keyof TSource & string;
|
|
33
|
+
hasJoins: false;
|
|
34
|
+
}>;
|
|
35
|
+
/**
|
|
36
|
+
* Join another table or subquery to the current query
|
|
37
|
+
*
|
|
38
|
+
* @param source - An object with a single key-value pair where the key is the table alias and the value is a Collection or subquery
|
|
39
|
+
* @param onCallback - A function that receives table references and returns the join condition
|
|
40
|
+
* @param type - The type of join: 'inner', 'left', 'right', or 'full' (defaults to 'left')
|
|
41
|
+
* @returns A QueryBuilder with the joined table available
|
|
42
|
+
*
|
|
43
|
+
* @example
|
|
44
|
+
* ```ts
|
|
45
|
+
* // Left join users with posts
|
|
46
|
+
* query
|
|
47
|
+
* .from({ users: usersCollection })
|
|
48
|
+
* .join({ posts: postsCollection }, ({users, posts}) => eq(users.id, posts.userId))
|
|
49
|
+
*
|
|
50
|
+
* // Inner join with explicit type
|
|
51
|
+
* query
|
|
52
|
+
* .from({ u: usersCollection })
|
|
53
|
+
* .join({ p: postsCollection }, ({u, p}) => eq(u.id, p.userId), 'inner')
|
|
54
|
+
* ```
|
|
55
|
+
*
|
|
56
|
+
* // Join with a subquery
|
|
57
|
+
* const activeUsers = query.from({ u: usersCollection }).where(({u}) => u.active)
|
|
58
|
+
* query
|
|
59
|
+
* .from({ activeUsers })
|
|
60
|
+
* .join({ p: postsCollection }, ({u, p}) => eq(u.id, p.userId))
|
|
61
|
+
*/
|
|
62
|
+
join<TSource extends Source, TJoinType extends `inner` | `left` | `right` | `full` = `left`>(source: TSource, onCallback: JoinOnCallback<MergeContext<TContext, SchemaFromSource<TSource>>>, type?: TJoinType): QueryBuilder<MergeContextWithJoinType<TContext, SchemaFromSource<TSource>, TJoinType>>;
|
|
63
|
+
/**
|
|
64
|
+
* Filter rows based on a condition
|
|
65
|
+
*
|
|
66
|
+
* @param callback - A function that receives table references and returns an expression
|
|
67
|
+
* @returns A QueryBuilder with the where condition applied
|
|
68
|
+
*
|
|
69
|
+
* @example
|
|
70
|
+
* ```ts
|
|
71
|
+
* // Simple condition
|
|
72
|
+
* query
|
|
73
|
+
* .from({ users: usersCollection })
|
|
74
|
+
* .where(({users}) => gt(users.age, 18))
|
|
75
|
+
*
|
|
76
|
+
* // Multiple conditions
|
|
77
|
+
* query
|
|
78
|
+
* .from({ users: usersCollection })
|
|
79
|
+
* .where(({users}) => and(
|
|
80
|
+
* gt(users.age, 18),
|
|
81
|
+
* eq(users.active, true)
|
|
82
|
+
* ))
|
|
83
|
+
*
|
|
84
|
+
* // Multiple where calls are ANDed together
|
|
85
|
+
* query
|
|
86
|
+
* .from({ users: usersCollection })
|
|
87
|
+
* .where(({users}) => gt(users.age, 18))
|
|
88
|
+
* .where(({users}) => eq(users.active, true))
|
|
89
|
+
* ```
|
|
90
|
+
*/
|
|
91
|
+
where(callback: WhereCallback<TContext>): QueryBuilder<TContext>;
|
|
92
|
+
/**
|
|
93
|
+
* Filter grouped rows based on aggregate conditions
|
|
94
|
+
*
|
|
95
|
+
* @param callback - A function that receives table references and returns an expression
|
|
96
|
+
* @returns A QueryBuilder with the having condition applied
|
|
97
|
+
*
|
|
98
|
+
* @example
|
|
99
|
+
* ```ts
|
|
100
|
+
* // Filter groups by count
|
|
101
|
+
* query
|
|
102
|
+
* .from({ posts: postsCollection })
|
|
103
|
+
* .groupBy(({posts}) => posts.userId)
|
|
104
|
+
* .having(({posts}) => gt(count(posts.id), 5))
|
|
105
|
+
*
|
|
106
|
+
* // Filter by average
|
|
107
|
+
* query
|
|
108
|
+
* .from({ orders: ordersCollection })
|
|
109
|
+
* .groupBy(({orders}) => orders.customerId)
|
|
110
|
+
* .having(({orders}) => gt(avg(orders.total), 100))
|
|
111
|
+
*
|
|
112
|
+
* // Multiple having calls are ANDed together
|
|
113
|
+
* query
|
|
114
|
+
* .from({ orders: ordersCollection })
|
|
115
|
+
* .groupBy(({orders}) => orders.customerId)
|
|
116
|
+
* .having(({orders}) => gt(count(orders.id), 5))
|
|
117
|
+
* .having(({orders}) => gt(avg(orders.total), 100))
|
|
118
|
+
* ```
|
|
119
|
+
*/
|
|
120
|
+
having(callback: WhereCallback<TContext>): QueryBuilder<TContext>;
|
|
121
|
+
/**
|
|
122
|
+
* Select specific columns or computed values from the query
|
|
123
|
+
*
|
|
124
|
+
* @param callback - A function that receives table references and returns an object with selected fields or expressions
|
|
125
|
+
* @returns A QueryBuilder that returns only the selected fields
|
|
126
|
+
*
|
|
127
|
+
* @example
|
|
128
|
+
* ```ts
|
|
129
|
+
* // Select specific columns
|
|
130
|
+
* query
|
|
131
|
+
* .from({ users: usersCollection })
|
|
132
|
+
* .select(({users}) => ({
|
|
133
|
+
* name: users.name,
|
|
134
|
+
* email: users.email
|
|
135
|
+
* }))
|
|
136
|
+
*
|
|
137
|
+
* // Select with computed values
|
|
138
|
+
* query
|
|
139
|
+
* .from({ users: usersCollection })
|
|
140
|
+
* .select(({users}) => ({
|
|
141
|
+
* fullName: concat(users.firstName, ' ', users.lastName),
|
|
142
|
+
* ageInMonths: mul(users.age, 12)
|
|
143
|
+
* }))
|
|
144
|
+
*
|
|
145
|
+
* // Select with aggregates (requires GROUP BY)
|
|
146
|
+
* query
|
|
147
|
+
* .from({ posts: postsCollection })
|
|
148
|
+
* .groupBy(({posts}) => posts.userId)
|
|
149
|
+
* .select(({posts, count}) => ({
|
|
150
|
+
* userId: posts.userId,
|
|
151
|
+
* postCount: count(posts.id)
|
|
152
|
+
* }))
|
|
153
|
+
* ```
|
|
154
|
+
*/
|
|
155
|
+
select<TSelectObject extends SelectObject>(callback: (refs: RefProxyForContext<TContext>) => TSelectObject): QueryBuilder<WithResult<TContext, ResultTypeFromSelect<TSelectObject>>>;
|
|
156
|
+
/**
|
|
157
|
+
* Sort the query results by one or more columns
|
|
158
|
+
*
|
|
159
|
+
* @param callback - A function that receives table references and returns the field to sort by
|
|
160
|
+
* @param direction - Sort direction: 'asc' for ascending, 'desc' for descending (defaults to 'asc')
|
|
161
|
+
* @returns A QueryBuilder with the ordering applied
|
|
162
|
+
*
|
|
163
|
+
* @example
|
|
164
|
+
* ```ts
|
|
165
|
+
* // Sort by a single column
|
|
166
|
+
* query
|
|
167
|
+
* .from({ users: usersCollection })
|
|
168
|
+
* .orderBy(({users}) => users.name)
|
|
169
|
+
*
|
|
170
|
+
* // Sort descending
|
|
171
|
+
* query
|
|
172
|
+
* .from({ users: usersCollection })
|
|
173
|
+
* .orderBy(({users}) => users.createdAt, 'desc')
|
|
174
|
+
*
|
|
175
|
+
* // Multiple sorts (chain orderBy calls)
|
|
176
|
+
* query
|
|
177
|
+
* .from({ users: usersCollection })
|
|
178
|
+
* .orderBy(({users}) => users.lastName)
|
|
179
|
+
* .orderBy(({users}) => users.firstName)
|
|
180
|
+
* ```
|
|
181
|
+
*/
|
|
182
|
+
orderBy(callback: OrderByCallback<TContext>, direction?: OrderByDirection): QueryBuilder<TContext>;
|
|
183
|
+
/**
|
|
184
|
+
* Group rows by one or more columns for aggregation
|
|
185
|
+
*
|
|
186
|
+
* @param callback - A function that receives table references and returns the field(s) to group by
|
|
187
|
+
* @returns A QueryBuilder with grouping applied (enables aggregate functions in SELECT and HAVING)
|
|
188
|
+
*
|
|
189
|
+
* @example
|
|
190
|
+
* ```ts
|
|
191
|
+
* // Group by a single column
|
|
192
|
+
* query
|
|
193
|
+
* .from({ posts: postsCollection })
|
|
194
|
+
* .groupBy(({posts}) => posts.userId)
|
|
195
|
+
* .select(({posts, count}) => ({
|
|
196
|
+
* userId: posts.userId,
|
|
197
|
+
* postCount: count()
|
|
198
|
+
* }))
|
|
199
|
+
*
|
|
200
|
+
* // Group by multiple columns
|
|
201
|
+
* query
|
|
202
|
+
* .from({ sales: salesCollection })
|
|
203
|
+
* .groupBy(({sales}) => [sales.region, sales.category])
|
|
204
|
+
* .select(({sales, sum}) => ({
|
|
205
|
+
* region: sales.region,
|
|
206
|
+
* category: sales.category,
|
|
207
|
+
* totalSales: sum(sales.amount)
|
|
208
|
+
* }))
|
|
209
|
+
* ```
|
|
210
|
+
*/
|
|
211
|
+
groupBy(callback: GroupByCallback<TContext>): QueryBuilder<TContext>;
|
|
212
|
+
/**
|
|
213
|
+
* Limit the number of rows returned by the query
|
|
214
|
+
* `orderBy` is required for `limit`
|
|
215
|
+
*
|
|
216
|
+
* @param count - Maximum number of rows to return
|
|
217
|
+
* @returns A QueryBuilder with the limit applied
|
|
218
|
+
*
|
|
219
|
+
* @example
|
|
220
|
+
* ```ts
|
|
221
|
+
* // Get top 5 posts by likes
|
|
222
|
+
* query
|
|
223
|
+
* .from({ posts: postsCollection })
|
|
224
|
+
* .orderBy(({posts}) => posts.likes, 'desc')
|
|
225
|
+
* .limit(5)
|
|
226
|
+
* ```
|
|
227
|
+
*/
|
|
228
|
+
limit(count: number): QueryBuilder<TContext>;
|
|
229
|
+
/**
|
|
230
|
+
* Skip a number of rows before returning results
|
|
231
|
+
* `orderBy` is required for `offset`
|
|
232
|
+
*
|
|
233
|
+
* @param count - Number of rows to skip
|
|
234
|
+
* @returns A QueryBuilder with the offset applied
|
|
235
|
+
*
|
|
236
|
+
* @example
|
|
237
|
+
* ```ts
|
|
238
|
+
* // Get second page of results
|
|
239
|
+
* query
|
|
240
|
+
* .from({ posts: postsCollection })
|
|
241
|
+
* .orderBy(({posts}) => posts.createdAt, 'desc')
|
|
242
|
+
* .offset(page * pageSize)
|
|
243
|
+
* .limit(pageSize)
|
|
244
|
+
* ```
|
|
245
|
+
*/
|
|
246
|
+
offset(count: number): QueryBuilder<TContext>;
|
|
247
|
+
private _getCurrentAliases;
|
|
248
|
+
/**
|
|
249
|
+
* Functional variants of the query builder
|
|
250
|
+
* These are imperative function that are called for ery row.
|
|
251
|
+
* Warning: that these cannot be optimized by the query compiler, and may prevent
|
|
252
|
+
* some type of optimizations being possible.
|
|
253
|
+
* @example
|
|
254
|
+
* ```ts
|
|
255
|
+
* q.fn.select((row) => ({
|
|
256
|
+
* name: row.user.name.toUpperCase(),
|
|
257
|
+
* age: row.user.age + 1,
|
|
258
|
+
* }))
|
|
259
|
+
* ```
|
|
260
|
+
*/
|
|
261
|
+
get fn(): {
|
|
262
|
+
/**
|
|
263
|
+
* Select fields using a function that operates on each row
|
|
264
|
+
* Warning: This cannot be optimized by the query compiler
|
|
265
|
+
*
|
|
266
|
+
* @param callback - A function that receives a row and returns the selected value
|
|
267
|
+
* @returns A QueryBuilder with functional selection applied
|
|
268
|
+
*
|
|
269
|
+
* @example
|
|
270
|
+
* ```ts
|
|
271
|
+
* // Functional select (not optimized)
|
|
272
|
+
* query
|
|
273
|
+
* .from({ users: usersCollection })
|
|
274
|
+
* .fn.select(row => ({
|
|
275
|
+
* name: row.users.name.toUpperCase(),
|
|
276
|
+
* age: row.users.age + 1,
|
|
277
|
+
* }))
|
|
278
|
+
* ```
|
|
279
|
+
*/
|
|
280
|
+
select<TFuncSelectResult>(callback: (row: TContext[`schema`]) => TFuncSelectResult): QueryBuilder<WithResult<TContext, TFuncSelectResult>>;
|
|
281
|
+
/**
|
|
282
|
+
* Filter rows using a function that operates on each row
|
|
283
|
+
* Warning: This cannot be optimized by the query compiler
|
|
284
|
+
*
|
|
285
|
+
* @param callback - A function that receives a row and returns a boolean
|
|
286
|
+
* @returns A QueryBuilder with functional filtering applied
|
|
287
|
+
*
|
|
288
|
+
* @example
|
|
289
|
+
* ```ts
|
|
290
|
+
* // Functional where (not optimized)
|
|
291
|
+
* query
|
|
292
|
+
* .from({ users: usersCollection })
|
|
293
|
+
* .fn.where(row => row.users.name.startsWith('A'))
|
|
294
|
+
* ```
|
|
295
|
+
*/
|
|
296
|
+
where(callback: (row: TContext[`schema`]) => any): QueryBuilder<TContext>;
|
|
297
|
+
/**
|
|
298
|
+
* Filter grouped rows using a function that operates on each aggregated row
|
|
299
|
+
* Warning: This cannot be optimized by the query compiler
|
|
300
|
+
*
|
|
301
|
+
* @param callback - A function that receives an aggregated row and returns a boolean
|
|
302
|
+
* @returns A QueryBuilder with functional having filter applied
|
|
303
|
+
*
|
|
304
|
+
* @example
|
|
305
|
+
* ```ts
|
|
306
|
+
* // Functional having (not optimized)
|
|
307
|
+
* query
|
|
308
|
+
* .from({ posts: postsCollection })
|
|
309
|
+
* .groupBy(({posts}) => posts.userId)
|
|
310
|
+
* .fn.having(row => row.count > 5)
|
|
311
|
+
* ```
|
|
312
|
+
*/
|
|
313
|
+
having(callback: (row: TContext[`schema`]) => any): QueryBuilder<TContext>;
|
|
314
|
+
};
|
|
315
|
+
_getQuery(): QueryIR;
|
|
316
|
+
}
|
|
317
|
+
export declare function buildQuery<TContext extends Context>(fn: (builder: InitialQueryBuilder) => QueryBuilder<TContext>): QueryIR;
|
|
318
|
+
export declare function getQueryIR(builder: BaseQueryBuilder | QueryBuilder<any> | InitialQueryBuilder): QueryIR;
|
|
319
|
+
export type InitialQueryBuilder = Pick<BaseQueryBuilder<Context>, `from`>;
|
|
320
|
+
export type InitialQueryBuilderConstructor = new () => InitialQueryBuilder;
|
|
321
|
+
export type QueryBuilder<TContext extends Context> = Omit<BaseQueryBuilder<TContext>, `from` | `_getQuery`>;
|
|
322
|
+
export declare const Query: InitialQueryBuilderConstructor;
|
|
323
|
+
export type ExtractContext<T> = T extends BaseQueryBuilder<infer TContext> ? TContext : T extends QueryBuilder<infer TContext> ? TContext : never;
|
|
324
|
+
export type { Context, Source, GetResult } from './types.js';
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
const ir = require("../ir.cjs");
|
|
4
|
+
function createRefProxy(aliases) {
|
|
5
|
+
const cache = /* @__PURE__ */ new Map();
|
|
6
|
+
const spreadSentinels = /* @__PURE__ */ new Set();
|
|
7
|
+
function createProxy(path) {
|
|
8
|
+
const pathKey = path.join(`.`);
|
|
9
|
+
if (cache.has(pathKey)) {
|
|
10
|
+
return cache.get(pathKey);
|
|
11
|
+
}
|
|
12
|
+
const proxy = new Proxy({}, {
|
|
13
|
+
get(target, prop, receiver) {
|
|
14
|
+
if (prop === `__refProxy`) return true;
|
|
15
|
+
if (prop === `__path`) return path;
|
|
16
|
+
if (prop === `__type`) return void 0;
|
|
17
|
+
if (typeof prop === `symbol`) return Reflect.get(target, prop, receiver);
|
|
18
|
+
const newPath = [...path, String(prop)];
|
|
19
|
+
return createProxy(newPath);
|
|
20
|
+
},
|
|
21
|
+
has(target, prop) {
|
|
22
|
+
if (prop === `__refProxy` || prop === `__path` || prop === `__type`)
|
|
23
|
+
return true;
|
|
24
|
+
return Reflect.has(target, prop);
|
|
25
|
+
},
|
|
26
|
+
ownKeys(target) {
|
|
27
|
+
if (path.length === 1) {
|
|
28
|
+
const aliasName = path[0];
|
|
29
|
+
spreadSentinels.add(aliasName);
|
|
30
|
+
}
|
|
31
|
+
return Reflect.ownKeys(target);
|
|
32
|
+
},
|
|
33
|
+
getOwnPropertyDescriptor(target, prop) {
|
|
34
|
+
if (prop === `__refProxy` || prop === `__path` || prop === `__type`) {
|
|
35
|
+
return { enumerable: false, configurable: true };
|
|
36
|
+
}
|
|
37
|
+
return Reflect.getOwnPropertyDescriptor(target, prop);
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
cache.set(pathKey, proxy);
|
|
41
|
+
return proxy;
|
|
42
|
+
}
|
|
43
|
+
const rootProxy = new Proxy({}, {
|
|
44
|
+
get(target, prop, receiver) {
|
|
45
|
+
if (prop === `__refProxy`) return true;
|
|
46
|
+
if (prop === `__path`) return [];
|
|
47
|
+
if (prop === `__type`) return void 0;
|
|
48
|
+
if (prop === `__spreadSentinels`) return spreadSentinels;
|
|
49
|
+
if (typeof prop === `symbol`) return Reflect.get(target, prop, receiver);
|
|
50
|
+
const propStr = String(prop);
|
|
51
|
+
if (aliases.includes(propStr)) {
|
|
52
|
+
return createProxy([propStr]);
|
|
53
|
+
}
|
|
54
|
+
return void 0;
|
|
55
|
+
},
|
|
56
|
+
has(target, prop) {
|
|
57
|
+
if (prop === `__refProxy` || prop === `__path` || prop === `__type` || prop === `__spreadSentinels`)
|
|
58
|
+
return true;
|
|
59
|
+
if (typeof prop === `string` && aliases.includes(prop)) return true;
|
|
60
|
+
return Reflect.has(target, prop);
|
|
61
|
+
},
|
|
62
|
+
ownKeys(_target) {
|
|
63
|
+
return [...aliases, `__refProxy`, `__path`, `__type`, `__spreadSentinels`];
|
|
64
|
+
},
|
|
65
|
+
getOwnPropertyDescriptor(target, prop) {
|
|
66
|
+
if (prop === `__refProxy` || prop === `__path` || prop === `__type` || prop === `__spreadSentinels`) {
|
|
67
|
+
return { enumerable: false, configurable: true };
|
|
68
|
+
}
|
|
69
|
+
if (typeof prop === `string` && aliases.includes(prop)) {
|
|
70
|
+
return { enumerable: true, configurable: true };
|
|
71
|
+
}
|
|
72
|
+
return void 0;
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
return rootProxy;
|
|
76
|
+
}
|
|
77
|
+
function toExpression(value) {
|
|
78
|
+
if (isRefProxy(value)) {
|
|
79
|
+
return new ir.Ref(value.__path);
|
|
80
|
+
}
|
|
81
|
+
if (value && typeof value === `object` && `type` in value && (value.type === `func` || value.type === `ref` || value.type === `val` || value.type === `agg`)) {
|
|
82
|
+
return value;
|
|
83
|
+
}
|
|
84
|
+
return new ir.Value(value);
|
|
85
|
+
}
|
|
86
|
+
function isRefProxy(value) {
|
|
87
|
+
return value && typeof value === `object` && value.__refProxy === true;
|
|
88
|
+
}
|
|
89
|
+
function val(value) {
|
|
90
|
+
return new ir.Value(value);
|
|
91
|
+
}
|
|
92
|
+
exports.createRefProxy = createRefProxy;
|
|
93
|
+
exports.isRefProxy = isRefProxy;
|
|
94
|
+
exports.toExpression = toExpression;
|
|
95
|
+
exports.val = val;
|
|
96
|
+
//# sourceMappingURL=ref-proxy.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ref-proxy.cjs","sources":["../../../../src/query/builder/ref-proxy.ts"],"sourcesContent":["import { Ref, Value } from \"../ir.js\"\nimport type { BasicExpression } from \"../ir.js\"\n\nexport interface RefProxy<T = any> {\n /** @internal */\n readonly __refProxy: true\n /** @internal */\n readonly __path: Array<string>\n /** @internal */\n readonly __type: T\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 const spreadSentinels = new Set<string>() // Track which aliases have been spread\n\n function createProxy(path: Array<string>): any {\n const pathKey = path.join(`.`)\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 === `__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 (prop === `__refProxy` || prop === `__path` || prop === `__type`)\n return true\n return Reflect.has(target, prop)\n },\n\n ownKeys(target) {\n // If this is a table-level proxy (path length 1), mark it as spread\n if (path.length === 1) {\n const aliasName = path[0]!\n spreadSentinels.add(aliasName)\n }\n return Reflect.ownKeys(target)\n },\n\n getOwnPropertyDescriptor(target, prop) {\n if (prop === `__refProxy` || prop === `__path` || prop === `__type`) {\n return { enumerable: false, configurable: true }\n }\n return Reflect.getOwnPropertyDescriptor(target, prop)\n },\n })\n\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 === `__type`) return undefined // Type is only for TypeScript inference\n if (prop === `__spreadSentinels`) return spreadSentinels // Expose spread sentinels\n if (typeof prop === `symbol`) return Reflect.get(target, prop, receiver)\n\n const propStr = String(prop)\n if (aliases.includes(propStr)) {\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 === `__type` ||\n prop === `__spreadSentinels`\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`, `__type`, `__spreadSentinels`]\n },\n\n getOwnPropertyDescriptor(target, prop) {\n if (\n prop === `__refProxy` ||\n prop === `__path` ||\n prop === `__type` ||\n prop === `__spreadSentinels`\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 return rootProxy\n}\n\n/**\n * Converts a value to an Expression\n * If it's a RefProxy, creates a Ref, otherwise creates 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 Ref(value.__path)\n }\n // If it's already an Expression (Func, Ref, Value) or Agg, return it directly\n if (\n value &&\n typeof value === `object` &&\n `type` in value &&\n (value.type === `func` ||\n value.type === `ref` ||\n value.type === `val` ||\n value.type === `agg`)\n ) {\n return value\n }\n return new Value(value)\n}\n\n/**\n * Type guard to check if a value is a RefProxy\n */\nexport function isRefProxy(value: any): value is RefProxy {\n return value && typeof value === `object` && value.__refProxy === true\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":["Ref","Value"],"mappings":";;;AAgBO,SAAS,eACd,SACiB;AACX,QAAA,4BAAY,IAAiB;AAC7B,QAAA,sCAAsB,IAAY;AAExC,WAAS,YAAY,MAA0B;AACvC,UAAA,UAAU,KAAK,KAAK,GAAG;AACzB,QAAA,MAAM,IAAI,OAAO,GAAG;AACf,aAAA,MAAM,IAAI,OAAO;AAAA,IAAA;AAG1B,UAAM,QAAQ,IAAI,MAAM,IAAW;AAAA,MACjC,IAAI,QAAQ,MAAM,UAAU;AACtB,YAAA,SAAS,aAAqB,QAAA;AAC9B,YAAA,SAAS,SAAiB,QAAA;AAC1B,YAAA,SAAS,SAAiB,QAAA;AAC1B,YAAA,OAAO,SAAS,SAAU,QAAO,QAAQ,IAAI,QAAQ,MAAM,QAAQ;AAEvE,cAAM,UAAU,CAAC,GAAG,MAAM,OAAO,IAAI,CAAC;AACtC,eAAO,YAAY,OAAO;AAAA,MAC5B;AAAA,MAEA,IAAI,QAAQ,MAAM;AAChB,YAAI,SAAS,gBAAgB,SAAS,YAAY,SAAS;AAClD,iBAAA;AACF,eAAA,QAAQ,IAAI,QAAQ,IAAI;AAAA,MACjC;AAAA,MAEA,QAAQ,QAAQ;AAEV,YAAA,KAAK,WAAW,GAAG;AACf,gBAAA,YAAY,KAAK,CAAC;AACxB,0BAAgB,IAAI,SAAS;AAAA,QAAA;AAExB,eAAA,QAAQ,QAAQ,MAAM;AAAA,MAC/B;AAAA,MAEA,yBAAyB,QAAQ,MAAM;AACrC,YAAI,SAAS,gBAAgB,SAAS,YAAY,SAAS,UAAU;AACnE,iBAAO,EAAE,YAAY,OAAO,cAAc,KAAK;AAAA,QAAA;AAE1C,eAAA,QAAQ,yBAAyB,QAAQ,IAAI;AAAA,MAAA;AAAA,IACtD,CACD;AAEK,UAAA,IAAI,SAAS,KAAK;AACjB,WAAA;AAAA,EAAA;AAIT,QAAM,YAAY,IAAI,MAAM,IAAW;AAAA,IACrC,IAAI,QAAQ,MAAM,UAAU;AACtB,UAAA,SAAS,aAAqB,QAAA;AAC9B,UAAA,SAAS,SAAU,QAAO,CAAC;AAC3B,UAAA,SAAS,SAAiB,QAAA;AAC1B,UAAA,SAAS,oBAA4B,QAAA;AACrC,UAAA,OAAO,SAAS,SAAU,QAAO,QAAQ,IAAI,QAAQ,MAAM,QAAQ;AAEjE,YAAA,UAAU,OAAO,IAAI;AACvB,UAAA,QAAQ,SAAS,OAAO,GAAG;AACtB,eAAA,YAAY,CAAC,OAAO,CAAC;AAAA,MAAA;AAGvB,aAAA;AAAA,IACT;AAAA,IAEA,IAAI,QAAQ,MAAM;AAChB,UACE,SAAS,gBACT,SAAS,YACT,SAAS,YACT,SAAS;AAEF,eAAA;AACT,UAAI,OAAO,SAAS,YAAY,QAAQ,SAAS,IAAI,EAAU,QAAA;AACxD,aAAA,QAAQ,IAAI,QAAQ,IAAI;AAAA,IACjC;AAAA,IAEA,QAAQ,SAAS;AACf,aAAO,CAAC,GAAG,SAAS,cAAc,UAAU,UAAU,mBAAmB;AAAA,IAC3E;AAAA,IAEA,yBAAyB,QAAQ,MAAM;AACrC,UACE,SAAS,gBACT,SAAS,YACT,SAAS,YACT,SAAS,qBACT;AACA,eAAO,EAAE,YAAY,OAAO,cAAc,KAAK;AAAA,MAAA;AAEjD,UAAI,OAAO,SAAS,YAAY,QAAQ,SAAS,IAAI,GAAG;AACtD,eAAO,EAAE,YAAY,MAAM,cAAc,KAAK;AAAA,MAAA;AAEzC,aAAA;AAAA,IAAA;AAAA,EACT,CACD;AAEM,SAAA;AACT;AAQO,SAAS,aAAa,OAAkC;AACzD,MAAA,WAAW,KAAK,GAAG;AACd,WAAA,IAAIA,GAAAA,IAAI,MAAM,MAAM;AAAA,EAAA;AAG7B,MACE,SACA,OAAO,UAAU,YACjB,UAAU,UACT,MAAM,SAAS,UACd,MAAM,SAAS,SACf,MAAM,SAAS,SACf,MAAM,SAAS,QACjB;AACO,WAAA;AAAA,EAAA;AAEF,SAAA,IAAIC,SAAM,KAAK;AACxB;AAKO,SAAS,WAAW,OAA+B;AACxD,SAAO,SAAS,OAAO,UAAU,YAAY,MAAM,eAAe;AACpE;AAKO,SAAS,IAAO,OAA8B;AAC5C,SAAA,IAAIA,SAAM,KAAK;AACxB;;;;;"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { BasicExpression } from '../ir.js';
|
|
2
|
+
export interface RefProxy<T = any> {
|
|
3
|
+
/** @internal */
|
|
4
|
+
readonly __refProxy: true;
|
|
5
|
+
/** @internal */
|
|
6
|
+
readonly __path: Array<string>;
|
|
7
|
+
/** @internal */
|
|
8
|
+
readonly __type: T;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Creates a proxy object that records property access paths
|
|
12
|
+
* Used in callbacks like where, select, etc. to create type-safe references
|
|
13
|
+
*/
|
|
14
|
+
export declare function createRefProxy<T extends Record<string, any>>(aliases: Array<string>): RefProxy<T> & T;
|
|
15
|
+
/**
|
|
16
|
+
* Converts a value to an Expression
|
|
17
|
+
* If it's a RefProxy, creates a Ref, otherwise creates a Value
|
|
18
|
+
*/
|
|
19
|
+
export declare function toExpression<T = any>(value: T): BasicExpression<T>;
|
|
20
|
+
export declare function toExpression(value: RefProxy<any>): BasicExpression<any>;
|
|
21
|
+
/**
|
|
22
|
+
* Type guard to check if a value is a RefProxy
|
|
23
|
+
*/
|
|
24
|
+
export declare function isRefProxy(value: any): value is RefProxy;
|
|
25
|
+
/**
|
|
26
|
+
* Helper to create a Value expression from a literal
|
|
27
|
+
*/
|
|
28
|
+
export declare function val<T>(value: T): BasicExpression<T>;
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { CollectionImpl } from '../../collection.js';
|
|
2
|
+
import { Aggregate, BasicExpression } from '../ir.js';
|
|
3
|
+
import { QueryBuilder } from './index.js';
|
|
4
|
+
export interface Context {
|
|
5
|
+
baseSchema: ContextSchema;
|
|
6
|
+
schema: ContextSchema;
|
|
7
|
+
fromSourceName: string;
|
|
8
|
+
hasJoins?: boolean;
|
|
9
|
+
joinTypes?: Record<string, `inner` | `left` | `right` | `full` | `outer` | `cross`>;
|
|
10
|
+
result?: any;
|
|
11
|
+
}
|
|
12
|
+
export type ContextSchema = Record<string, unknown>;
|
|
13
|
+
export type Source = {
|
|
14
|
+
[alias: string]: CollectionImpl<any, any> | QueryBuilder<Context>;
|
|
15
|
+
};
|
|
16
|
+
export type InferCollectionType<T> = T extends CollectionImpl<infer U> ? U : never;
|
|
17
|
+
export type SchemaFromSource<T extends Source> = Prettify<{
|
|
18
|
+
[K in keyof T]: T[K] extends CollectionImpl<infer U> ? U : T[K] extends QueryBuilder<infer TContext> ? GetResult<TContext> : never;
|
|
19
|
+
}>;
|
|
20
|
+
export type GetAliases<TContext extends Context> = keyof TContext[`schema`];
|
|
21
|
+
export type WhereCallback<TContext extends Context> = (refs: RefProxyForContext<TContext>) => any;
|
|
22
|
+
export type SelectObject<T extends Record<string, BasicExpression | Aggregate | RefProxy | RefProxyFor<any>> = Record<string, BasicExpression | Aggregate | RefProxy | RefProxyFor<any>>> = T;
|
|
23
|
+
export type ResultTypeFromSelect<TSelectObject> = {
|
|
24
|
+
[K in keyof TSelectObject]: TSelectObject[K] extends RefProxy<infer T> ? T : TSelectObject[K] extends BasicExpression<infer T> ? T : TSelectObject[K] extends Aggregate<infer T> ? T : TSelectObject[K] extends RefProxyFor<infer T> ? T : never;
|
|
25
|
+
};
|
|
26
|
+
export type OrderByCallback<TContext extends Context> = (refs: RefProxyForContext<TContext>) => any;
|
|
27
|
+
export type GroupByCallback<TContext extends Context> = (refs: RefProxyForContext<TContext>) => any;
|
|
28
|
+
export type JoinOnCallback<TContext extends Context> = (refs: RefProxyForContext<TContext>) => any;
|
|
29
|
+
export type RefProxyForContext<TContext extends Context> = {
|
|
30
|
+
[K in keyof TContext[`schema`]]: RefProxyFor<TContext[`schema`][K]>;
|
|
31
|
+
};
|
|
32
|
+
type IsExactlyUndefined<T> = [T] extends [undefined] ? true : false;
|
|
33
|
+
type IsOptional<T> = undefined extends T ? true : false;
|
|
34
|
+
type NonUndefined<T> = T extends undefined ? never : T;
|
|
35
|
+
export type RefProxyFor<T> = OmitRefProxy<IsExactlyUndefined<T> extends true ? RefProxy<T> : IsOptional<T> extends true ? NonUndefined<T> extends Record<string, any> ? {
|
|
36
|
+
[K in keyof NonUndefined<T>]: NonUndefined<T>[K] extends Record<string, any> ? RefProxyFor<NonUndefined<T>[K] | undefined> & RefProxy<NonUndefined<T>[K] | undefined> : RefProxy<NonUndefined<T>[K] | undefined>;
|
|
37
|
+
} & RefProxy<T> : RefProxy<T> : T extends Record<string, any> ? {
|
|
38
|
+
[K in keyof T]: T[K] extends Record<string, any> ? RefProxyFor<T[K]> & RefProxy<T[K]> : RefProxy<T[K]>;
|
|
39
|
+
} & RefProxy<T> : RefProxy<T>>;
|
|
40
|
+
type OmitRefProxy<T> = Omit<T, `__refProxy` | `__path` | `__type`>;
|
|
41
|
+
export interface RefProxy<T = any> {
|
|
42
|
+
/** @internal */
|
|
43
|
+
readonly __refProxy: true;
|
|
44
|
+
/** @internal */
|
|
45
|
+
readonly __path: Array<string>;
|
|
46
|
+
/** @internal */
|
|
47
|
+
readonly __type: T;
|
|
48
|
+
}
|
|
49
|
+
export type MergeContextWithJoinType<TContext extends Context, TNewSchema extends ContextSchema, TJoinType extends `inner` | `left` | `right` | `full` | `outer` | `cross`> = {
|
|
50
|
+
baseSchema: TContext[`baseSchema`];
|
|
51
|
+
schema: ApplyJoinOptionalityToMergedSchema<TContext[`schema`], TNewSchema, TJoinType, TContext[`fromSourceName`]>;
|
|
52
|
+
fromSourceName: TContext[`fromSourceName`];
|
|
53
|
+
hasJoins: true;
|
|
54
|
+
joinTypes: (TContext[`joinTypes`] extends Record<string, any> ? TContext[`joinTypes`] : {}) & {
|
|
55
|
+
[K in keyof TNewSchema & string]: TJoinType;
|
|
56
|
+
};
|
|
57
|
+
result: TContext[`result`];
|
|
58
|
+
};
|
|
59
|
+
export type ApplyJoinOptionalityToMergedSchema<TExistingSchema extends ContextSchema, TNewSchema extends ContextSchema, TJoinType extends `inner` | `left` | `right` | `full` | `outer` | `cross`, TFromSourceName extends string> = {
|
|
60
|
+
[K in keyof TExistingSchema]: K extends TFromSourceName ? TJoinType extends `right` | `full` ? TExistingSchema[K] | undefined : TExistingSchema[K] : TExistingSchema[K];
|
|
61
|
+
} & {
|
|
62
|
+
[K in keyof TNewSchema]: TJoinType extends `left` | `full` ? // New table becomes optional for left and full joins
|
|
63
|
+
TNewSchema[K] | undefined : TNewSchema[K];
|
|
64
|
+
};
|
|
65
|
+
export type GetResult<TContext extends Context> = Prettify<TContext[`result`] extends object ? TContext[`result`] : TContext[`hasJoins`] extends true ? TContext[`schema`] : TContext[`schema`][TContext[`fromSourceName`]]>;
|
|
66
|
+
export type ApplyJoinOptionalityToSchema<TSchema extends ContextSchema, TJoinTypes extends Record<string, string>, TFromSourceName extends string> = {
|
|
67
|
+
[K in keyof TSchema]: K extends TFromSourceName ? HasJoinType<TJoinTypes, `right` | `full`> extends true ? TSchema[K] | undefined : TSchema[K] : K extends keyof TJoinTypes ? TJoinTypes[K] extends `left` | `full` ? TSchema[K] | undefined : IsTableMadeOptionalBySubsequentJoins<K, TJoinTypes, TFromSourceName> extends true ? TSchema[K] | undefined : TSchema[K] : TSchema[K];
|
|
68
|
+
};
|
|
69
|
+
type IsTableMadeOptionalBySubsequentJoins<TTableAlias extends string | number | symbol, TJoinTypes extends Record<string, string>, TFromSourceName extends string> = TTableAlias extends TFromSourceName ? HasJoinType<TJoinTypes, `right` | `full`> : false;
|
|
70
|
+
export type HasJoinType<TJoinTypes extends Record<string, string>, TTargetTypes extends string> = true extends {
|
|
71
|
+
[K in keyof TJoinTypes]: TJoinTypes[K] extends TTargetTypes ? true : false;
|
|
72
|
+
}[keyof TJoinTypes] ? true : false;
|
|
73
|
+
export type MergeContext<TContext extends Context, TNewSchema extends ContextSchema> = MergeContextWithJoinType<TContext, TNewSchema, `left`>;
|
|
74
|
+
export type WithResult<TContext extends Context, TResult> = Prettify<Omit<TContext, `result`> & {
|
|
75
|
+
result: Prettify<TResult>;
|
|
76
|
+
}>;
|
|
77
|
+
export type Prettify<T> = {
|
|
78
|
+
[K in keyof T]: T[K];
|
|
79
|
+
} & {};
|
|
80
|
+
export {};
|