@tanstack/db 0.0.7 → 0.0.9

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.
Files changed (43) hide show
  1. package/dist/cjs/collection.cjs +441 -284
  2. package/dist/cjs/collection.cjs.map +1 -1
  3. package/dist/cjs/collection.d.cts +103 -30
  4. package/dist/cjs/proxy.cjs +2 -2
  5. package/dist/cjs/proxy.cjs.map +1 -1
  6. package/dist/cjs/query/compiled-query.cjs +23 -37
  7. package/dist/cjs/query/compiled-query.cjs.map +1 -1
  8. package/dist/cjs/query/compiled-query.d.cts +2 -2
  9. package/dist/cjs/query/order-by.cjs +41 -38
  10. package/dist/cjs/query/order-by.cjs.map +1 -1
  11. package/dist/cjs/query/query-builder.cjs.map +1 -1
  12. package/dist/cjs/query/query-builder.d.cts +2 -2
  13. package/dist/cjs/query/schema.d.cts +5 -4
  14. package/dist/cjs/transactions.cjs +7 -6
  15. package/dist/cjs/transactions.cjs.map +1 -1
  16. package/dist/cjs/transactions.d.cts +9 -9
  17. package/dist/cjs/types.d.cts +28 -22
  18. package/dist/esm/collection.d.ts +103 -30
  19. package/dist/esm/collection.js +442 -285
  20. package/dist/esm/collection.js.map +1 -1
  21. package/dist/esm/proxy.js +2 -2
  22. package/dist/esm/proxy.js.map +1 -1
  23. package/dist/esm/query/compiled-query.d.ts +2 -2
  24. package/dist/esm/query/compiled-query.js +23 -37
  25. package/dist/esm/query/compiled-query.js.map +1 -1
  26. package/dist/esm/query/order-by.js +41 -38
  27. package/dist/esm/query/order-by.js.map +1 -1
  28. package/dist/esm/query/query-builder.d.ts +2 -2
  29. package/dist/esm/query/query-builder.js.map +1 -1
  30. package/dist/esm/query/schema.d.ts +5 -4
  31. package/dist/esm/transactions.d.ts +9 -9
  32. package/dist/esm/transactions.js +7 -6
  33. package/dist/esm/transactions.js.map +1 -1
  34. package/dist/esm/types.d.ts +28 -22
  35. package/package.json +2 -2
  36. package/src/collection.ts +624 -372
  37. package/src/proxy.ts +2 -2
  38. package/src/query/compiled-query.ts +26 -37
  39. package/src/query/order-by.ts +69 -67
  40. package/src/query/query-builder.ts +4 -3
  41. package/src/query/schema.ts +13 -3
  42. package/src/transactions.ts +24 -22
  43. package/src/types.ts +44 -22
@@ -1 +1 @@
1
- {"version":3,"file":"query-builder.cjs","sources":["../../../src/query/query-builder.ts"],"sourcesContent":["import type { Collection } from \"../collection\"\nimport type {\n Comparator,\n Condition,\n From,\n JoinClause,\n Limit,\n LiteralValue,\n Offset,\n OrderBy,\n Query,\n Select,\n WhereCallback,\n WithQuery,\n} from \"./schema.js\"\nimport type {\n Context,\n Flatten,\n InferResultTypeFromSelectTuple,\n Input,\n InputReference,\n PropertyReference,\n PropertyReferenceString,\n RemoveIndexSignature,\n Schema,\n} from \"./types.js\"\n\ntype CollectionRef = { [K: string]: Collection<any> }\n\nexport class BaseQueryBuilder<TContext extends Context<Schema>> {\n private readonly query: Partial<Query<TContext>> = {}\n\n /**\n * Create a new QueryBuilder instance.\n */\n constructor(query: Partial<Query<TContext>> = {}) {\n this.query = query\n }\n\n from<TCollectionRef extends CollectionRef>(\n collectionRef: TCollectionRef\n ): QueryBuilder<{\n baseSchema: Flatten<\n TContext[`baseSchema`] & {\n [K in keyof TCollectionRef & string]: RemoveIndexSignature<\n (TCollectionRef[keyof TCollectionRef] extends Collection<infer T>\n ? T\n : never) &\n Input\n >\n }\n >\n schema: Flatten<{\n [K in keyof TCollectionRef & string]: RemoveIndexSignature<\n (TCollectionRef[keyof TCollectionRef] extends Collection<infer T>\n ? T\n : never) &\n Input\n >\n }>\n default: keyof TCollectionRef & string\n }>\n\n from<\n T extends InputReference<{\n baseSchema: TContext[`baseSchema`]\n schema: TContext[`baseSchema`]\n }>,\n >(\n collection: T\n ): QueryBuilder<{\n baseSchema: TContext[`baseSchema`]\n schema: {\n [K in T]: RemoveIndexSignature<TContext[`baseSchema`][T]>\n }\n default: T\n }>\n\n from<\n T extends InputReference<{\n baseSchema: TContext[`baseSchema`]\n schema: TContext[`baseSchema`]\n }>,\n TAs extends string,\n >(\n collection: T,\n as: TAs\n ): QueryBuilder<{\n baseSchema: TContext[`baseSchema`]\n schema: {\n [K in TAs]: RemoveIndexSignature<TContext[`baseSchema`][T]>\n }\n default: TAs\n }>\n\n /**\n * Specify the collection to query from.\n * This is the first method that must be called in the chain.\n *\n * @param collection The collection name to query from\n * @param as Optional alias for the collection\n * @returns A new QueryBuilder with the from clause set\n */\n from<\n T extends\n | InputReference<{\n baseSchema: TContext[`baseSchema`]\n schema: TContext[`baseSchema`]\n }>\n | CollectionRef,\n TAs extends string | undefined,\n >(collection: T, as?: TAs) {\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n if (typeof collection === `object` && collection !== null) {\n return this.fromCollectionRef(collection)\n } else if (typeof collection === `string`) {\n return this.fromInputReference(\n collection as InputReference<{\n baseSchema: TContext[`baseSchema`]\n schema: TContext[`baseSchema`]\n }>,\n as\n )\n } else {\n throw new Error(`Invalid collection type`)\n }\n }\n\n private fromCollectionRef<TCollectionRef extends CollectionRef>(\n collectionRef: TCollectionRef\n ) {\n const keys = Object.keys(collectionRef)\n if (keys.length !== 1) {\n throw new Error(`Expected exactly one key`)\n }\n\n const key = keys[0]!\n const collection = collectionRef[key]!\n\n const newBuilder = new BaseQueryBuilder()\n Object.assign(newBuilder.query, this.query)\n newBuilder.query.from = key as From<TContext>\n newBuilder.query.collections ??= {}\n newBuilder.query.collections[key] = collection\n\n return newBuilder as unknown as QueryBuilder<{\n baseSchema: TContext[`baseSchema`] & {\n [K in keyof TCollectionRef &\n string]: (TCollectionRef[keyof TCollectionRef] extends Collection<\n infer T\n >\n ? T\n : never) &\n Input\n }\n schema: {\n [K in keyof TCollectionRef &\n string]: (TCollectionRef[keyof TCollectionRef] extends Collection<\n infer T\n >\n ? T\n : never) &\n Input\n }\n default: keyof TCollectionRef & string\n }>\n }\n\n private fromInputReference<\n T extends InputReference<{\n baseSchema: TContext[`baseSchema`]\n schema: TContext[`baseSchema`]\n }>,\n TAs extends string | undefined,\n >(collection: T, as?: TAs) {\n const newBuilder = new BaseQueryBuilder()\n Object.assign(newBuilder.query, this.query)\n newBuilder.query.from = collection as From<TContext>\n if (as) {\n newBuilder.query.as = as\n }\n\n // Calculate the result type without deep nesting\n type ResultSchema = TAs extends undefined\n ? { [K in T]: TContext[`baseSchema`][T] }\n : { [K in string & TAs]: TContext[`baseSchema`][T] }\n\n type ResultDefault = TAs extends undefined ? T : string & TAs\n\n // Use simpler type assertion to avoid excessive depth\n return newBuilder as unknown as QueryBuilder<{\n baseSchema: TContext[`baseSchema`]\n schema: ResultSchema\n default: ResultDefault\n }>\n }\n\n /**\n * Specify what columns to select.\n * Overwrites any previous select clause.\n * Also supports callback functions that receive the row context and return selected data.\n *\n * @param selects The columns to select (can include callbacks)\n * @returns A new QueryBuilder with the select clause set\n */\n select<TSelects extends Array<Select<TContext>>>(\n this: QueryBuilder<TContext>,\n ...selects: TSelects\n ) {\n // Validate function calls in the selects\n // Need to use a type assertion to bypass deep recursive type checking\n const validatedSelects = selects.map((select) => {\n // If the select is an object with aliases, validate each value\n if (\n typeof select === `object` &&\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n select !== null &&\n !Array.isArray(select)\n ) {\n const result: Record<string, any> = {}\n\n for (const [key, value] of Object.entries(select)) {\n // If it's a function call (object with a single key that is an allowed function name)\n if (\n typeof value === `object` &&\n value !== null &&\n !Array.isArray(value)\n ) {\n const keys = Object.keys(value)\n if (keys.length === 1) {\n const funcName = keys[0]!\n // List of allowed function names from AllowedFunctionName\n const allowedFunctions = [\n `SUM`,\n `COUNT`,\n `AVG`,\n `MIN`,\n `MAX`,\n `DATE`,\n `JSON_EXTRACT`,\n `JSON_EXTRACT_PATH`,\n `UPPER`,\n `LOWER`,\n `COALESCE`,\n `CONCAT`,\n `LENGTH`,\n `ORDER_INDEX`,\n ]\n\n if (!allowedFunctions.includes(funcName)) {\n console.warn(\n `Unsupported function: ${funcName}. Expected one of: ${allowedFunctions.join(`, `)}`\n )\n }\n }\n }\n\n result[key] = value\n }\n\n return result\n }\n\n return select\n })\n\n // Ensure we have an orderByIndex in the select if we have an orderBy\n // This is required if select is called after orderBy\n if (this._query.orderBy) {\n validatedSelects.push({ _orderByIndex: { ORDER_INDEX: `numeric` } })\n }\n\n const newBuilder = new BaseQueryBuilder<TContext>(\n (this as BaseQueryBuilder<TContext>).query\n )\n newBuilder.query.select = validatedSelects as Array<Select<TContext>>\n\n return newBuilder as QueryBuilder<\n Flatten<\n Omit<TContext, `result`> & {\n result: InferResultTypeFromSelectTuple<TContext, TSelects>\n }\n >\n >\n }\n\n /**\n * Add a where clause comparing two values.\n */\n where(\n left: PropertyReferenceString<TContext> | LiteralValue,\n operator: Comparator,\n right: PropertyReferenceString<TContext> | LiteralValue\n ): QueryBuilder<TContext>\n\n /**\n * Add a where clause with a complete condition object.\n */\n where(condition: Condition<TContext>): QueryBuilder<TContext>\n\n /**\n * Add a where clause with a callback function.\n */\n where(callback: WhereCallback<TContext>): QueryBuilder<TContext>\n\n /**\n * Add a where clause to filter the results.\n * Can be called multiple times to add AND conditions.\n * Also supports callback functions that receive the row context.\n *\n * @param leftOrConditionOrCallback The left operand, complete condition, or callback function\n * @param operator Optional comparison operator\n * @param right Optional right operand\n * @returns A new QueryBuilder with the where clause added\n */\n where(\n leftOrConditionOrCallback: any,\n operator?: any,\n right?: any\n ): QueryBuilder<TContext> {\n // Create a new builder with a copy of the current query\n // Use simplistic approach to avoid deep type errors\n const newBuilder = new BaseQueryBuilder<TContext>()\n Object.assign(newBuilder.query, this.query)\n\n let condition: any\n\n // Determine if this is a callback, complete condition, or individual parts\n if (typeof leftOrConditionOrCallback === `function`) {\n // It's a callback function\n condition = leftOrConditionOrCallback\n } else if (operator !== undefined && right !== undefined) {\n // Create a condition from parts\n condition = [leftOrConditionOrCallback, operator, right]\n } else {\n // Use the provided condition directly\n condition = leftOrConditionOrCallback\n }\n\n // Where is always an array, so initialize or append\n if (!newBuilder.query.where) {\n newBuilder.query.where = [condition]\n } else {\n newBuilder.query.where = [...newBuilder.query.where, condition]\n }\n\n return newBuilder as unknown as QueryBuilder<TContext>\n }\n\n /**\n * Add a having clause comparing two values.\n * For filtering results after they have been grouped.\n */\n having(\n left: PropertyReferenceString<TContext> | LiteralValue,\n operator: Comparator,\n right: PropertyReferenceString<TContext> | LiteralValue\n ): QueryBuilder<TContext>\n\n /**\n * Add a having clause with a complete condition object.\n * For filtering results after they have been grouped.\n */\n having(condition: Condition<TContext>): QueryBuilder<TContext>\n\n /**\n * Add a having clause with a callback function.\n * For filtering results after they have been grouped.\n */\n having(callback: WhereCallback<TContext>): QueryBuilder<TContext>\n\n /**\n * Add a having clause to filter the grouped results.\n * Can be called multiple times to add AND conditions.\n * Also supports callback functions that receive the row context.\n *\n * @param leftOrConditionOrCallback The left operand, complete condition, or callback function\n * @param operator Optional comparison operator\n * @param right Optional right operand\n * @returns A new QueryBuilder with the having clause added\n */\n having(\n leftOrConditionOrCallback: any,\n operator?: any,\n right?: any\n ): QueryBuilder<TContext> {\n // Create a new builder with a copy of the current query\n const newBuilder = new BaseQueryBuilder<TContext>()\n Object.assign(newBuilder.query, this.query)\n\n let condition: any\n\n // Determine if this is a callback, complete condition, or individual parts\n if (typeof leftOrConditionOrCallback === `function`) {\n // It's a callback function\n condition = leftOrConditionOrCallback\n } else if (operator !== undefined && right !== undefined) {\n // Create a condition from parts\n condition = [leftOrConditionOrCallback, operator, right]\n } else {\n // Use the provided condition directly\n condition = leftOrConditionOrCallback\n }\n\n // Having is always an array, so initialize or append\n if (!newBuilder.query.having) {\n newBuilder.query.having = [condition]\n } else {\n newBuilder.query.having = [...newBuilder.query.having, condition]\n }\n\n return newBuilder as QueryBuilder<TContext>\n }\n\n /**\n * Add a join clause to the query using a CollectionRef.\n */\n join<TCollectionRef extends CollectionRef>(joinClause: {\n type: `inner` | `left` | `right` | `full` | `cross`\n from: TCollectionRef\n on: Condition<\n Flatten<{\n baseSchema: TContext[`baseSchema`]\n schema: TContext[`schema`] & {\n [K in keyof TCollectionRef & string]: RemoveIndexSignature<\n (TCollectionRef[keyof TCollectionRef] extends Collection<infer T>\n ? T\n : never) &\n Input\n >\n }\n }>\n >\n where?: Condition<\n Flatten<{\n baseSchema: TContext[`baseSchema`]\n schema: {\n [K in keyof TCollectionRef & string]: RemoveIndexSignature<\n (TCollectionRef[keyof TCollectionRef] extends Collection<infer T>\n ? T\n : never) &\n Input\n >\n }\n }>\n >\n }): QueryBuilder<\n Flatten<\n Omit<TContext, `schema`> & {\n schema: TContext[`schema`] & {\n [K in keyof TCollectionRef & string]: RemoveIndexSignature<\n (TCollectionRef[keyof TCollectionRef] extends Collection<infer T>\n ? T\n : never) &\n Input\n >\n }\n hasJoin: true\n }\n >\n >\n\n /**\n * Add a join clause to the query without specifying an alias.\n * The collection name will be used as the default alias.\n */\n join<\n T extends InputReference<{\n baseSchema: TContext[`baseSchema`]\n schema: TContext[`baseSchema`]\n }>,\n >(joinClause: {\n type: `inner` | `left` | `right` | `full` | `cross`\n from: T\n on: Condition<\n Flatten<{\n baseSchema: TContext[`baseSchema`]\n schema: TContext[`schema`] & {\n [K in T]: RemoveIndexSignature<TContext[`baseSchema`][T]>\n }\n }>\n >\n where?: Condition<\n Flatten<{\n baseSchema: TContext[`baseSchema`]\n schema: { [K in T]: RemoveIndexSignature<TContext[`baseSchema`][T]> }\n }>\n >\n }): QueryBuilder<\n Flatten<\n Omit<TContext, `schema`> & {\n schema: TContext[`schema`] & {\n [K in T]: RemoveIndexSignature<TContext[`baseSchema`][T]>\n }\n hasJoin: true\n }\n >\n >\n\n /**\n * Add a join clause to the query with a specified alias.\n */\n join<\n TFrom extends InputReference<{\n baseSchema: TContext[`baseSchema`]\n schema: TContext[`baseSchema`]\n }>,\n TAs extends string,\n >(joinClause: {\n type: `inner` | `left` | `right` | `full` | `cross`\n from: TFrom\n as: TAs\n on: Condition<\n Flatten<{\n baseSchema: TContext[`baseSchema`]\n schema: TContext[`schema`] & {\n [K in TAs]: RemoveIndexSignature<TContext[`baseSchema`][TFrom]>\n }\n }>\n >\n where?: Condition<\n Flatten<{\n baseSchema: TContext[`baseSchema`]\n schema: {\n [K in TAs]: RemoveIndexSignature<TContext[`baseSchema`][TFrom]>\n }\n }>\n >\n }): QueryBuilder<\n Flatten<\n Omit<TContext, `schema`> & {\n schema: TContext[`schema`] & {\n [K in TAs]: RemoveIndexSignature<TContext[`baseSchema`][TFrom]>\n }\n hasJoin: true\n }\n >\n >\n\n join<\n TFrom extends\n | InputReference<{\n baseSchema: TContext[`baseSchema`]\n schema: TContext[`baseSchema`]\n }>\n | CollectionRef,\n TAs extends string | undefined = undefined,\n >(joinClause: {\n type: `inner` | `left` | `right` | `full` | `cross`\n from: TFrom\n as?: TAs\n on: Condition<\n Flatten<{\n baseSchema: TContext[`baseSchema`]\n schema: TContext[`schema`] &\n (TFrom extends CollectionRef\n ? {\n [K in keyof TFrom & string]: RemoveIndexSignature<\n (TFrom[keyof TFrom] extends Collection<infer T> ? T : never) &\n Input\n >\n }\n : TFrom extends InputReference<infer TRef>\n ? {\n [K in keyof TRef & string]: RemoveIndexSignature<\n TRef[keyof TRef]\n >\n }\n : never)\n }>\n >\n where?: Condition<\n Flatten<{\n baseSchema: TContext[`baseSchema`]\n schema: TContext[`schema`] &\n (TFrom extends CollectionRef\n ? {\n [K in keyof TFrom & string]: RemoveIndexSignature<\n (TFrom[keyof TFrom] extends Collection<infer T> ? T : never) &\n Input\n >\n }\n : TFrom extends InputReference<infer TRef>\n ? {\n [K in keyof TRef & string]: RemoveIndexSignature<\n TRef[keyof TRef]\n >\n }\n : never)\n }>\n >\n }): QueryBuilder<any> {\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n if (typeof joinClause.from === `object` && joinClause.from !== null) {\n return this.joinCollectionRef(\n joinClause as {\n type: `inner` | `left` | `right` | `full` | `cross`\n from: CollectionRef\n on: Condition<any>\n where?: Condition<any>\n }\n )\n } else {\n return this.joinInputReference(\n joinClause as {\n type: `inner` | `left` | `right` | `full` | `cross`\n from: InputReference<{\n baseSchema: TContext[`baseSchema`]\n schema: TContext[`baseSchema`]\n }>\n as?: TAs\n on: Condition<any>\n where?: Condition<any>\n }\n )\n }\n }\n\n private joinCollectionRef<TCollectionRef extends CollectionRef>(joinClause: {\n type: `inner` | `left` | `right` | `full` | `cross`\n from: TCollectionRef\n on: Condition<any>\n where?: Condition<any>\n }): QueryBuilder<any> {\n // Create a new builder with a copy of the current query\n const newBuilder = new BaseQueryBuilder<TContext>()\n Object.assign(newBuilder.query, this.query)\n\n // Get the collection key\n const keys = Object.keys(joinClause.from)\n if (keys.length !== 1) {\n throw new Error(`Expected exactly one key in CollectionRef`)\n }\n const key = keys[0]!\n const collection = joinClause.from[key]\n if (!collection) {\n throw new Error(`Collection not found for key: ${key}`)\n }\n\n // Create a copy of the join clause for the query\n const joinClauseCopy = {\n type: joinClause.type,\n from: key,\n on: joinClause.on,\n where: joinClause.where,\n } as JoinClause<TContext>\n\n // Add the join clause to the query\n if (!newBuilder.query.join) {\n newBuilder.query.join = [joinClauseCopy]\n } else {\n newBuilder.query.join = [...newBuilder.query.join, joinClauseCopy]\n }\n\n // Add the collection to the collections map\n newBuilder.query.collections ??= {}\n newBuilder.query.collections[key] = collection\n\n // Return the new builder with updated schema type\n return newBuilder as QueryBuilder<\n Flatten<\n Omit<TContext, `schema`> & {\n schema: TContext[`schema`] & {\n [K in keyof TCollectionRef & string]: RemoveIndexSignature<\n (TCollectionRef[keyof TCollectionRef] extends Collection<infer T>\n ? T\n : never) &\n Input\n >\n }\n }\n >\n >\n }\n\n private joinInputReference<\n TFrom extends InputReference<{\n baseSchema: TContext[`baseSchema`]\n schema: TContext[`baseSchema`]\n }>,\n TAs extends string | undefined = undefined,\n >(joinClause: {\n type: `inner` | `left` | `right` | `full` | `cross`\n from: TFrom\n as?: TAs\n on: Condition<any>\n where?: Condition<any>\n }): QueryBuilder<any> {\n // Create a new builder with a copy of the current query\n const newBuilder = new BaseQueryBuilder<TContext>()\n Object.assign(newBuilder.query, this.query)\n\n // Create a copy of the join clause for the query\n const joinClauseCopy = { ...joinClause } as JoinClause<TContext>\n\n // Add the join clause to the query\n if (!newBuilder.query.join) {\n newBuilder.query.join = [joinClauseCopy]\n } else {\n newBuilder.query.join = [...newBuilder.query.join, joinClauseCopy]\n }\n\n // Determine the alias or use the collection name as default\n const _effectiveAlias = joinClause.as ?? joinClause.from\n\n // Return the new builder with updated schema type\n return newBuilder as QueryBuilder<\n Flatten<\n Omit<TContext, `schema`> & {\n schema: TContext[`schema`] & {\n [K in typeof _effectiveAlias]: TContext[`baseSchema`][TFrom]\n }\n }\n >\n >\n }\n\n /**\n * Add an orderBy clause to sort the results.\n * Overwrites any previous orderBy clause.\n *\n * @param orderBy The order specification\n * @returns A new QueryBuilder with the orderBy clause set\n */\n orderBy(orderBy: OrderBy<TContext>): QueryBuilder<TContext> {\n // Create a new builder with a copy of the current query\n const newBuilder = new BaseQueryBuilder<TContext>()\n Object.assign(newBuilder.query, this.query)\n\n // Set the orderBy clause\n newBuilder.query.orderBy = orderBy\n\n // Ensure we have an orderByIndex in the select if we have an orderBy\n // This is required if select is called before orderBy\n newBuilder.query.select = [\n ...(newBuilder.query.select ?? []),\n { _orderByIndex: { ORDER_INDEX: `numeric` } },\n ]\n\n return newBuilder as QueryBuilder<TContext>\n }\n\n /**\n * Set a limit on the number of results returned.\n *\n * @param limit Maximum number of results to return\n * @returns A new QueryBuilder with the limit set\n */\n limit(limit: Limit<TContext>): QueryBuilder<TContext> {\n // Create a new builder with a copy of the current query\n const newBuilder = new BaseQueryBuilder<TContext>()\n Object.assign(newBuilder.query, this.query)\n\n // Set the limit\n newBuilder.query.limit = limit\n\n return newBuilder as QueryBuilder<TContext>\n }\n\n /**\n * Set an offset to skip a number of results.\n *\n * @param offset Number of results to skip\n * @returns A new QueryBuilder with the offset set\n */\n offset(offset: Offset<TContext>): QueryBuilder<TContext> {\n // Create a new builder with a copy of the current query\n const newBuilder = new BaseQueryBuilder<TContext>()\n Object.assign(newBuilder.query, this.query)\n\n // Set the offset\n newBuilder.query.offset = offset\n\n return newBuilder as QueryBuilder<TContext>\n }\n\n /**\n * Add a groupBy clause to group the results by one or more columns.\n *\n * @param groupBy The column(s) to group by\n * @returns A new QueryBuilder with the groupBy clause set\n */\n groupBy(\n groupBy: PropertyReference<TContext> | Array<PropertyReference<TContext>>\n ): QueryBuilder<TContext> {\n // Create a new builder with a copy of the current query\n const newBuilder = new BaseQueryBuilder<TContext>()\n Object.assign(newBuilder.query, this.query)\n\n // Set the groupBy clause\n newBuilder.query.groupBy = groupBy\n\n return newBuilder as QueryBuilder<TContext>\n }\n\n /**\n * Define a Common Table Expression (CTE) that can be referenced in the main query.\n * This allows referencing the CTE by name in subsequent from/join clauses.\n *\n * @param name The name of the CTE\n * @param queryBuilderCallback A function that builds the CTE query\n * @returns A new QueryBuilder with the CTE added\n */\n with<TName extends string, TResult = Record<string, unknown>>(\n name: TName,\n queryBuilderCallback: (\n builder: InitialQueryBuilder<{\n baseSchema: TContext[`baseSchema`]\n schema: {}\n }>\n ) => QueryBuilder<any>\n ): InitialQueryBuilder<{\n baseSchema: TContext[`baseSchema`] & { [K in TName]: TResult }\n schema: TContext[`schema`]\n }> {\n // Create a new builder with a copy of the current query\n const newBuilder = new BaseQueryBuilder<TContext>()\n Object.assign(newBuilder.query, this.query)\n\n // Create a new builder for the CTE\n const cteBuilder = new BaseQueryBuilder<{\n baseSchema: TContext[`baseSchema`]\n schema: {}\n }>()\n\n // Get the CTE query from the callback\n const cteQueryBuilder = queryBuilderCallback(\n cteBuilder as InitialQueryBuilder<{\n baseSchema: TContext[`baseSchema`]\n schema: {}\n }>\n )\n\n // Get the query from the builder\n const cteQuery = cteQueryBuilder._query\n\n // Add an 'as' property to the CTE\n const withQuery: WithQuery<any> = {\n ...cteQuery,\n as: name,\n }\n\n // Add the CTE to the with array\n if (!newBuilder.query.with) {\n newBuilder.query.with = [withQuery]\n } else {\n newBuilder.query.with = [...newBuilder.query.with, withQuery]\n }\n\n // Use a type cast that simplifies the type structure to avoid recursion\n return newBuilder as unknown as InitialQueryBuilder<{\n baseSchema: TContext[`baseSchema`] & { [K in TName]: TResult }\n schema: TContext[`schema`]\n }>\n }\n\n get _query(): Query<TContext> {\n return this.query as Query<TContext>\n }\n}\n\nexport type InitialQueryBuilder<TContext extends Context<Schema>> = Pick<\n BaseQueryBuilder<TContext>,\n `from` | `with`\n>\n\nexport type QueryBuilder<TContext extends Context<Schema>> = Omit<\n BaseQueryBuilder<TContext>,\n `from`\n>\n\n/**\n * Create a new query builder with the given schema\n */\nexport function queryBuilder<TBaseSchema extends Schema = {}>() {\n return new BaseQueryBuilder<{\n baseSchema: TBaseSchema\n schema: {}\n }>() as InitialQueryBuilder<{\n baseSchema: TBaseSchema\n schema: {}\n }>\n}\n\nexport type ResultsFromContext<TContext extends Context<Schema>> = Flatten<\n TContext[`result`] extends object\n ? TContext[`result`] // If there is a select we will have a result type\n : TContext[`hasJoin`] extends true\n ? TContext[`schema`] // If there is a join, the query returns the namespaced schema\n : TContext[`default`] extends keyof TContext[`schema`]\n ? TContext[`schema`][TContext[`default`]] // If there is no join we return the flat default schema\n : never // Should never happen\n>\n\nexport type ResultFromQueryBuilder<TQueryBuilder> = Flatten<\n TQueryBuilder extends QueryBuilder<infer C>\n ? C extends { result: infer R }\n ? R\n : never\n : never\n>\n"],"names":[],"mappings":";;AA6BO,MAAM,iBAAmD;AAAA;AAAA;AAAA;AAAA,EAM9D,YAAY,QAAkC,IAAI;AALlD,SAAiB,QAAkC,CAAC;AAMlD,SAAK,QAAQ;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAmEf,KAQE,YAAe,IAAU;AAEzB,QAAI,OAAO,eAAe,YAAY,eAAe,MAAM;AAClD,aAAA,KAAK,kBAAkB,UAAU;AAAA,IAC1C,WAAW,OAAO,eAAe,UAAU;AACzC,aAAO,KAAK;AAAA,QACV;AAAA,QAIA;AAAA,MACF;AAAA,IAAA,OACK;AACC,YAAA,IAAI,MAAM,yBAAyB;AAAA,IAAA;AAAA,EAC3C;AAAA,EAGM,kBACN,eACA;;AACM,UAAA,OAAO,OAAO,KAAK,aAAa;AAClC,QAAA,KAAK,WAAW,GAAG;AACf,YAAA,IAAI,MAAM,0BAA0B;AAAA,IAAA;AAGtC,UAAA,MAAM,KAAK,CAAC;AACZ,UAAA,aAAa,cAAc,GAAG;AAE9B,UAAA,aAAa,IAAI,iBAAiB;AACxC,WAAO,OAAO,WAAW,OAAO,KAAK,KAAK;AAC1C,eAAW,MAAM,OAAO;AACb,qBAAA,OAAM,gBAAN,GAAM,cAAgB,CAAC;AACvB,eAAA,MAAM,YAAY,GAAG,IAAI;AAE7B,WAAA;AAAA,EAAA;AAAA,EAuBD,mBAMN,YAAe,IAAU;AACnB,UAAA,aAAa,IAAI,iBAAiB;AACxC,WAAO,OAAO,WAAW,OAAO,KAAK,KAAK;AAC1C,eAAW,MAAM,OAAO;AACxB,QAAI,IAAI;AACN,iBAAW,MAAM,KAAK;AAAA,IAAA;AAWjB,WAAA;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAeT,UAEK,SACH;AAGA,UAAM,mBAAmB,QAAQ,IAAI,CAAC,WAAW;AAE/C,UACE,OAAO,WAAW;AAAA,MAElB,WAAW,QACX,CAAC,MAAM,QAAQ,MAAM,GACrB;AACA,cAAM,SAA8B,CAAC;AAErC,mBAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,MAAM,GAAG;AAG/C,cAAA,OAAO,UAAU,YACjB,UAAU,QACV,CAAC,MAAM,QAAQ,KAAK,GACpB;AACM,kBAAA,OAAO,OAAO,KAAK,KAAK;AAC1B,gBAAA,KAAK,WAAW,GAAG;AACf,oBAAA,WAAW,KAAK,CAAC;AAEvB,oBAAM,mBAAmB;AAAA,gBACvB;AAAA,gBACA;AAAA,gBACA;AAAA,gBACA;AAAA,gBACA;AAAA,gBACA;AAAA,gBACA;AAAA,gBACA;AAAA,gBACA;AAAA,gBACA;AAAA,gBACA;AAAA,gBACA;AAAA,gBACA;AAAA,gBACA;AAAA,cACF;AAEA,kBAAI,CAAC,iBAAiB,SAAS,QAAQ,GAAG;AAChC,wBAAA;AAAA,kBACN,yBAAyB,QAAQ,sBAAsB,iBAAiB,KAAK,IAAI,CAAC;AAAA,gBACpF;AAAA,cAAA;AAAA,YACF;AAAA,UACF;AAGF,iBAAO,GAAG,IAAI;AAAA,QAAA;AAGT,eAAA;AAAA,MAAA;AAGF,aAAA;AAAA,IAAA,CACR;AAIG,QAAA,KAAK,OAAO,SAAS;AACvB,uBAAiB,KAAK,EAAE,eAAe,EAAE,aAAa,UAAA,GAAa;AAAA,IAAA;AAGrE,UAAM,aAAa,IAAI;AAAA,MACpB,KAAoC;AAAA,IACvC;AACA,eAAW,MAAM,SAAS;AAEnB,WAAA;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAsCT,MACE,2BACA,UACA,OACwB;AAGlB,UAAA,aAAa,IAAI,iBAA2B;AAClD,WAAO,OAAO,WAAW,OAAO,KAAK,KAAK;AAEtC,QAAA;AAGA,QAAA,OAAO,8BAA8B,YAAY;AAEvC,kBAAA;AAAA,IACH,WAAA,aAAa,UAAa,UAAU,QAAW;AAE5C,kBAAA,CAAC,2BAA2B,UAAU,KAAK;AAAA,IAAA,OAClD;AAEO,kBAAA;AAAA,IAAA;AAIV,QAAA,CAAC,WAAW,MAAM,OAAO;AAChB,iBAAA,MAAM,QAAQ,CAAC,SAAS;AAAA,IAAA,OAC9B;AACL,iBAAW,MAAM,QAAQ,CAAC,GAAG,WAAW,MAAM,OAAO,SAAS;AAAA,IAAA;AAGzD,WAAA;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAmCT,OACE,2BACA,UACA,OACwB;AAElB,UAAA,aAAa,IAAI,iBAA2B;AAClD,WAAO,OAAO,WAAW,OAAO,KAAK,KAAK;AAEtC,QAAA;AAGA,QAAA,OAAO,8BAA8B,YAAY;AAEvC,kBAAA;AAAA,IACH,WAAA,aAAa,UAAa,UAAU,QAAW;AAE5C,kBAAA,CAAC,2BAA2B,UAAU,KAAK;AAAA,IAAA,OAClD;AAEO,kBAAA;AAAA,IAAA;AAIV,QAAA,CAAC,WAAW,MAAM,QAAQ;AACjB,iBAAA,MAAM,SAAS,CAAC,SAAS;AAAA,IAAA,OAC/B;AACL,iBAAW,MAAM,SAAS,CAAC,GAAG,WAAW,MAAM,QAAQ,SAAS;AAAA,IAAA;AAG3D,WAAA;AAAA,EAAA;AAAA,EAgIT,KAQE,YA4CoB;AAEpB,QAAI,OAAO,WAAW,SAAS,YAAY,WAAW,SAAS,MAAM;AACnE,aAAO,KAAK;AAAA,QACV;AAAA,MAMF;AAAA,IAAA,OACK;AACL,aAAO,KAAK;AAAA,QACV;AAAA,MAUF;AAAA,IAAA;AAAA,EACF;AAAA,EAGM,kBAAwD,YAK1C;;AAEd,UAAA,aAAa,IAAI,iBAA2B;AAClD,WAAO,OAAO,WAAW,OAAO,KAAK,KAAK;AAG1C,UAAM,OAAO,OAAO,KAAK,WAAW,IAAI;AACpC,QAAA,KAAK,WAAW,GAAG;AACf,YAAA,IAAI,MAAM,2CAA2C;AAAA,IAAA;AAEvD,UAAA,MAAM,KAAK,CAAC;AACZ,UAAA,aAAa,WAAW,KAAK,GAAG;AACtC,QAAI,CAAC,YAAY;AACf,YAAM,IAAI,MAAM,iCAAiC,GAAG,EAAE;AAAA,IAAA;AAIxD,UAAM,iBAAiB;AAAA,MACrB,MAAM,WAAW;AAAA,MACjB,MAAM;AAAA,MACN,IAAI,WAAW;AAAA,MACf,OAAO,WAAW;AAAA,IACpB;AAGI,QAAA,CAAC,WAAW,MAAM,MAAM;AACf,iBAAA,MAAM,OAAO,CAAC,cAAc;AAAA,IAAA,OAClC;AACL,iBAAW,MAAM,OAAO,CAAC,GAAG,WAAW,MAAM,MAAM,cAAc;AAAA,IAAA;AAIxD,qBAAA,OAAM,gBAAN,GAAM,cAAgB,CAAC;AACvB,eAAA,MAAM,YAAY,GAAG,IAAI;AAG7B,WAAA;AAAA,EAAA;AAAA,EAgBD,mBAMN,YAMoB;AAEd,UAAA,aAAa,IAAI,iBAA2B;AAClD,WAAO,OAAO,WAAW,OAAO,KAAK,KAAK;AAGpC,UAAA,iBAAiB,EAAE,GAAG,WAAW;AAGnC,QAAA,CAAC,WAAW,MAAM,MAAM;AACf,iBAAA,MAAM,OAAO,CAAC,cAAc;AAAA,IAAA,OAClC;AACL,iBAAW,MAAM,OAAO,CAAC,GAAG,WAAW,MAAM,MAAM,cAAc;AAAA,IAAA;AAI3C,eAAW,MAAM,WAAW;AAG7C,WAAA;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAkBT,QAAQ,SAAoD;AAEpD,UAAA,aAAa,IAAI,iBAA2B;AAClD,WAAO,OAAO,WAAW,OAAO,KAAK,KAAK;AAG1C,eAAW,MAAM,UAAU;AAI3B,eAAW,MAAM,SAAS;AAAA,MACxB,GAAI,WAAW,MAAM,UAAU,CAAC;AAAA,MAChC,EAAE,eAAe,EAAE,aAAa,UAAY,EAAA;AAAA,IAC9C;AAEO,WAAA;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAST,MAAM,OAAgD;AAE9C,UAAA,aAAa,IAAI,iBAA2B;AAClD,WAAO,OAAO,WAAW,OAAO,KAAK,KAAK;AAG1C,eAAW,MAAM,QAAQ;AAElB,WAAA;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAST,OAAO,QAAkD;AAEjD,UAAA,aAAa,IAAI,iBAA2B;AAClD,WAAO,OAAO,WAAW,OAAO,KAAK,KAAK;AAG1C,eAAW,MAAM,SAAS;AAEnB,WAAA;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAST,QACE,SACwB;AAElB,UAAA,aAAa,IAAI,iBAA2B;AAClD,WAAO,OAAO,WAAW,OAAO,KAAK,KAAK;AAG1C,eAAW,MAAM,UAAU;AAEpB,WAAA;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWT,KACE,MACA,sBASC;AAEK,UAAA,aAAa,IAAI,iBAA2B;AAClD,WAAO,OAAO,WAAW,OAAO,KAAK,KAAK;AAGpC,UAAA,aAAa,IAAI,iBAGpB;AAGH,UAAM,kBAAkB;AAAA,MACtB;AAAA,IAIF;AAGA,UAAM,WAAW,gBAAgB;AAGjC,UAAM,YAA4B;AAAA,MAChC,GAAG;AAAA,MACH,IAAI;AAAA,IACN;AAGI,QAAA,CAAC,WAAW,MAAM,MAAM;AACf,iBAAA,MAAM,OAAO,CAAC,SAAS;AAAA,IAAA,OAC7B;AACL,iBAAW,MAAM,OAAO,CAAC,GAAG,WAAW,MAAM,MAAM,SAAS;AAAA,IAAA;AAIvD,WAAA;AAAA,EAAA;AAAA,EAMT,IAAI,SAA0B;AAC5B,WAAO,KAAK;AAAA,EAAA;AAEhB;AAeO,SAAS,eAAgD;AAC9D,SAAO,IAAI,iBAGR;AAIL;;;"}
1
+ {"version":3,"file":"query-builder.cjs","sources":["../../../src/query/query-builder.ts"],"sourcesContent":["import type { Collection } from \"../collection\"\nimport type {\n Comparator,\n ComparatorValue,\n Condition,\n From,\n JoinClause,\n Limit,\n LiteralValue,\n Offset,\n OrderBy,\n Query,\n Select,\n WhereCallback,\n WithQuery,\n} from \"./schema.js\"\nimport type {\n Context,\n Flatten,\n InferResultTypeFromSelectTuple,\n Input,\n InputReference,\n PropertyReference,\n PropertyReferenceString,\n RemoveIndexSignature,\n Schema,\n} from \"./types.js\"\n\ntype CollectionRef = { [K: string]: Collection<any> }\n\nexport class BaseQueryBuilder<TContext extends Context<Schema>> {\n private readonly query: Partial<Query<TContext>> = {}\n\n /**\n * Create a new QueryBuilder instance.\n */\n constructor(query: Partial<Query<TContext>> = {}) {\n this.query = query\n }\n\n from<TCollectionRef extends CollectionRef>(\n collectionRef: TCollectionRef\n ): QueryBuilder<{\n baseSchema: Flatten<\n TContext[`baseSchema`] & {\n [K in keyof TCollectionRef & string]: RemoveIndexSignature<\n (TCollectionRef[keyof TCollectionRef] extends Collection<infer T>\n ? T\n : never) &\n Input\n >\n }\n >\n schema: Flatten<{\n [K in keyof TCollectionRef & string]: RemoveIndexSignature<\n (TCollectionRef[keyof TCollectionRef] extends Collection<infer T>\n ? T\n : never) &\n Input\n >\n }>\n default: keyof TCollectionRef & string\n }>\n\n from<\n T extends InputReference<{\n baseSchema: TContext[`baseSchema`]\n schema: TContext[`baseSchema`]\n }>,\n >(\n collection: T\n ): QueryBuilder<{\n baseSchema: TContext[`baseSchema`]\n schema: {\n [K in T]: RemoveIndexSignature<TContext[`baseSchema`][T]>\n }\n default: T\n }>\n\n from<\n T extends InputReference<{\n baseSchema: TContext[`baseSchema`]\n schema: TContext[`baseSchema`]\n }>,\n TAs extends string,\n >(\n collection: T,\n as: TAs\n ): QueryBuilder<{\n baseSchema: TContext[`baseSchema`]\n schema: {\n [K in TAs]: RemoveIndexSignature<TContext[`baseSchema`][T]>\n }\n default: TAs\n }>\n\n /**\n * Specify the collection to query from.\n * This is the first method that must be called in the chain.\n *\n * @param collection The collection name to query from\n * @param as Optional alias for the collection\n * @returns A new QueryBuilder with the from clause set\n */\n from<\n T extends\n | InputReference<{\n baseSchema: TContext[`baseSchema`]\n schema: TContext[`baseSchema`]\n }>\n | CollectionRef,\n TAs extends string | undefined,\n >(collection: T, as?: TAs) {\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n if (typeof collection === `object` && collection !== null) {\n return this.fromCollectionRef(collection)\n } else if (typeof collection === `string`) {\n return this.fromInputReference(\n collection as InputReference<{\n baseSchema: TContext[`baseSchema`]\n schema: TContext[`baseSchema`]\n }>,\n as\n )\n } else {\n throw new Error(`Invalid collection type`)\n }\n }\n\n private fromCollectionRef<TCollectionRef extends CollectionRef>(\n collectionRef: TCollectionRef\n ) {\n const keys = Object.keys(collectionRef)\n if (keys.length !== 1) {\n throw new Error(`Expected exactly one key`)\n }\n\n const key = keys[0]!\n const collection = collectionRef[key]!\n\n const newBuilder = new BaseQueryBuilder()\n Object.assign(newBuilder.query, this.query)\n newBuilder.query.from = key as From<TContext>\n newBuilder.query.collections ??= {}\n newBuilder.query.collections[key] = collection\n\n return newBuilder as unknown as QueryBuilder<{\n baseSchema: TContext[`baseSchema`] & {\n [K in keyof TCollectionRef &\n string]: (TCollectionRef[keyof TCollectionRef] extends Collection<\n infer T\n >\n ? T\n : never) &\n Input\n }\n schema: {\n [K in keyof TCollectionRef &\n string]: (TCollectionRef[keyof TCollectionRef] extends Collection<\n infer T\n >\n ? T\n : never) &\n Input\n }\n default: keyof TCollectionRef & string\n }>\n }\n\n private fromInputReference<\n T extends InputReference<{\n baseSchema: TContext[`baseSchema`]\n schema: TContext[`baseSchema`]\n }>,\n TAs extends string | undefined,\n >(collection: T, as?: TAs) {\n const newBuilder = new BaseQueryBuilder()\n Object.assign(newBuilder.query, this.query)\n newBuilder.query.from = collection as From<TContext>\n if (as) {\n newBuilder.query.as = as\n }\n\n // Calculate the result type without deep nesting\n type ResultSchema = TAs extends undefined\n ? { [K in T]: TContext[`baseSchema`][T] }\n : { [K in string & TAs]: TContext[`baseSchema`][T] }\n\n type ResultDefault = TAs extends undefined ? T : string & TAs\n\n // Use simpler type assertion to avoid excessive depth\n return newBuilder as unknown as QueryBuilder<{\n baseSchema: TContext[`baseSchema`]\n schema: ResultSchema\n default: ResultDefault\n }>\n }\n\n /**\n * Specify what columns to select.\n * Overwrites any previous select clause.\n * Also supports callback functions that receive the row context and return selected data.\n *\n * @param selects The columns to select (can include callbacks)\n * @returns A new QueryBuilder with the select clause set\n */\n select<TSelects extends Array<Select<TContext>>>(\n this: QueryBuilder<TContext>,\n ...selects: TSelects\n ) {\n // Validate function calls in the selects\n // Need to use a type assertion to bypass deep recursive type checking\n const validatedSelects = selects.map((select) => {\n // If the select is an object with aliases, validate each value\n if (\n typeof select === `object` &&\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n select !== null &&\n !Array.isArray(select)\n ) {\n const result: Record<string, any> = {}\n\n for (const [key, value] of Object.entries(select)) {\n // If it's a function call (object with a single key that is an allowed function name)\n if (\n typeof value === `object` &&\n value !== null &&\n !Array.isArray(value)\n ) {\n const keys = Object.keys(value)\n if (keys.length === 1) {\n const funcName = keys[0]!\n // List of allowed function names from AllowedFunctionName\n const allowedFunctions = [\n `SUM`,\n `COUNT`,\n `AVG`,\n `MIN`,\n `MAX`,\n `DATE`,\n `JSON_EXTRACT`,\n `JSON_EXTRACT_PATH`,\n `UPPER`,\n `LOWER`,\n `COALESCE`,\n `CONCAT`,\n `LENGTH`,\n `ORDER_INDEX`,\n ]\n\n if (!allowedFunctions.includes(funcName)) {\n console.warn(\n `Unsupported function: ${funcName}. Expected one of: ${allowedFunctions.join(`, `)}`\n )\n }\n }\n }\n\n result[key] = value\n }\n\n return result\n }\n\n return select\n })\n\n // Ensure we have an orderByIndex in the select if we have an orderBy\n // This is required if select is called after orderBy\n if (this._query.orderBy) {\n validatedSelects.push({ _orderByIndex: { ORDER_INDEX: `numeric` } })\n }\n\n const newBuilder = new BaseQueryBuilder<TContext>(\n (this as BaseQueryBuilder<TContext>).query\n )\n newBuilder.query.select = validatedSelects as Array<Select<TContext>>\n\n return newBuilder as QueryBuilder<\n Flatten<\n Omit<TContext, `result`> & {\n result: InferResultTypeFromSelectTuple<TContext, TSelects>\n }\n >\n >\n }\n\n /**\n * Add a where clause comparing two values.\n */\n where<T extends Comparator>(\n left: PropertyReferenceString<TContext> | LiteralValue,\n operator: T,\n right: ComparatorValue<T, TContext>\n ): QueryBuilder<TContext>\n\n /**\n * Add a where clause with a complete condition object.\n */\n where(condition: Condition<TContext>): QueryBuilder<TContext>\n\n /**\n * Add a where clause with a callback function.\n */\n where(callback: WhereCallback<TContext>): QueryBuilder<TContext>\n\n /**\n * Add a where clause to filter the results.\n * Can be called multiple times to add AND conditions.\n * Also supports callback functions that receive the row context.\n *\n * @param leftOrConditionOrCallback The left operand, complete condition, or callback function\n * @param operator Optional comparison operator\n * @param right Optional right operand\n * @returns A new QueryBuilder with the where clause added\n */\n where(\n leftOrConditionOrCallback: any,\n operator?: any,\n right?: any\n ): QueryBuilder<TContext> {\n // Create a new builder with a copy of the current query\n // Use simplistic approach to avoid deep type errors\n const newBuilder = new BaseQueryBuilder<TContext>()\n Object.assign(newBuilder.query, this.query)\n\n let condition: any\n\n // Determine if this is a callback, complete condition, or individual parts\n if (typeof leftOrConditionOrCallback === `function`) {\n // It's a callback function\n condition = leftOrConditionOrCallback\n } else if (operator !== undefined && right !== undefined) {\n // Create a condition from parts\n condition = [leftOrConditionOrCallback, operator, right]\n } else {\n // Use the provided condition directly\n condition = leftOrConditionOrCallback\n }\n\n // Where is always an array, so initialize or append\n if (!newBuilder.query.where) {\n newBuilder.query.where = [condition]\n } else {\n newBuilder.query.where = [...newBuilder.query.where, condition]\n }\n\n return newBuilder as unknown as QueryBuilder<TContext>\n }\n\n /**\n * Add a having clause comparing two values.\n * For filtering results after they have been grouped.\n */\n having(\n left: PropertyReferenceString<TContext> | LiteralValue,\n operator: Comparator,\n right: PropertyReferenceString<TContext> | LiteralValue\n ): QueryBuilder<TContext>\n\n /**\n * Add a having clause with a complete condition object.\n * For filtering results after they have been grouped.\n */\n having(condition: Condition<TContext>): QueryBuilder<TContext>\n\n /**\n * Add a having clause with a callback function.\n * For filtering results after they have been grouped.\n */\n having(callback: WhereCallback<TContext>): QueryBuilder<TContext>\n\n /**\n * Add a having clause to filter the grouped results.\n * Can be called multiple times to add AND conditions.\n * Also supports callback functions that receive the row context.\n *\n * @param leftOrConditionOrCallback The left operand, complete condition, or callback function\n * @param operator Optional comparison operator\n * @param right Optional right operand\n * @returns A new QueryBuilder with the having clause added\n */\n having(\n leftOrConditionOrCallback: any,\n operator?: any,\n right?: any\n ): QueryBuilder<TContext> {\n // Create a new builder with a copy of the current query\n const newBuilder = new BaseQueryBuilder<TContext>()\n Object.assign(newBuilder.query, this.query)\n\n let condition: any\n\n // Determine if this is a callback, complete condition, or individual parts\n if (typeof leftOrConditionOrCallback === `function`) {\n // It's a callback function\n condition = leftOrConditionOrCallback\n } else if (operator !== undefined && right !== undefined) {\n // Create a condition from parts\n condition = [leftOrConditionOrCallback, operator, right]\n } else {\n // Use the provided condition directly\n condition = leftOrConditionOrCallback\n }\n\n // Having is always an array, so initialize or append\n if (!newBuilder.query.having) {\n newBuilder.query.having = [condition]\n } else {\n newBuilder.query.having = [...newBuilder.query.having, condition]\n }\n\n return newBuilder as QueryBuilder<TContext>\n }\n\n /**\n * Add a join clause to the query using a CollectionRef.\n */\n join<TCollectionRef extends CollectionRef>(joinClause: {\n type: `inner` | `left` | `right` | `full` | `cross`\n from: TCollectionRef\n on: Condition<\n Flatten<{\n baseSchema: TContext[`baseSchema`]\n schema: TContext[`schema`] & {\n [K in keyof TCollectionRef & string]: RemoveIndexSignature<\n (TCollectionRef[keyof TCollectionRef] extends Collection<infer T>\n ? T\n : never) &\n Input\n >\n }\n }>\n >\n where?: Condition<\n Flatten<{\n baseSchema: TContext[`baseSchema`]\n schema: {\n [K in keyof TCollectionRef & string]: RemoveIndexSignature<\n (TCollectionRef[keyof TCollectionRef] extends Collection<infer T>\n ? T\n : never) &\n Input\n >\n }\n }>\n >\n }): QueryBuilder<\n Flatten<\n Omit<TContext, `schema`> & {\n schema: TContext[`schema`] & {\n [K in keyof TCollectionRef & string]: RemoveIndexSignature<\n (TCollectionRef[keyof TCollectionRef] extends Collection<infer T>\n ? T\n : never) &\n Input\n >\n }\n hasJoin: true\n }\n >\n >\n\n /**\n * Add a join clause to the query without specifying an alias.\n * The collection name will be used as the default alias.\n */\n join<\n T extends InputReference<{\n baseSchema: TContext[`baseSchema`]\n schema: TContext[`baseSchema`]\n }>,\n >(joinClause: {\n type: `inner` | `left` | `right` | `full` | `cross`\n from: T\n on: Condition<\n Flatten<{\n baseSchema: TContext[`baseSchema`]\n schema: TContext[`schema`] & {\n [K in T]: RemoveIndexSignature<TContext[`baseSchema`][T]>\n }\n }>\n >\n where?: Condition<\n Flatten<{\n baseSchema: TContext[`baseSchema`]\n schema: { [K in T]: RemoveIndexSignature<TContext[`baseSchema`][T]> }\n }>\n >\n }): QueryBuilder<\n Flatten<\n Omit<TContext, `schema`> & {\n schema: TContext[`schema`] & {\n [K in T]: RemoveIndexSignature<TContext[`baseSchema`][T]>\n }\n hasJoin: true\n }\n >\n >\n\n /**\n * Add a join clause to the query with a specified alias.\n */\n join<\n TFrom extends InputReference<{\n baseSchema: TContext[`baseSchema`]\n schema: TContext[`baseSchema`]\n }>,\n TAs extends string,\n >(joinClause: {\n type: `inner` | `left` | `right` | `full` | `cross`\n from: TFrom\n as: TAs\n on: Condition<\n Flatten<{\n baseSchema: TContext[`baseSchema`]\n schema: TContext[`schema`] & {\n [K in TAs]: RemoveIndexSignature<TContext[`baseSchema`][TFrom]>\n }\n }>\n >\n where?: Condition<\n Flatten<{\n baseSchema: TContext[`baseSchema`]\n schema: {\n [K in TAs]: RemoveIndexSignature<TContext[`baseSchema`][TFrom]>\n }\n }>\n >\n }): QueryBuilder<\n Flatten<\n Omit<TContext, `schema`> & {\n schema: TContext[`schema`] & {\n [K in TAs]: RemoveIndexSignature<TContext[`baseSchema`][TFrom]>\n }\n hasJoin: true\n }\n >\n >\n\n join<\n TFrom extends\n | InputReference<{\n baseSchema: TContext[`baseSchema`]\n schema: TContext[`baseSchema`]\n }>\n | CollectionRef,\n TAs extends string | undefined = undefined,\n >(joinClause: {\n type: `inner` | `left` | `right` | `full` | `cross`\n from: TFrom\n as?: TAs\n on: Condition<\n Flatten<{\n baseSchema: TContext[`baseSchema`]\n schema: TContext[`schema`] &\n (TFrom extends CollectionRef\n ? {\n [K in keyof TFrom & string]: RemoveIndexSignature<\n (TFrom[keyof TFrom] extends Collection<infer T> ? T : never) &\n Input\n >\n }\n : TFrom extends InputReference<infer TRef>\n ? {\n [K in keyof TRef & string]: RemoveIndexSignature<\n TRef[keyof TRef]\n >\n }\n : never)\n }>\n >\n where?: Condition<\n Flatten<{\n baseSchema: TContext[`baseSchema`]\n schema: TContext[`schema`] &\n (TFrom extends CollectionRef\n ? {\n [K in keyof TFrom & string]: RemoveIndexSignature<\n (TFrom[keyof TFrom] extends Collection<infer T> ? T : never) &\n Input\n >\n }\n : TFrom extends InputReference<infer TRef>\n ? {\n [K in keyof TRef & string]: RemoveIndexSignature<\n TRef[keyof TRef]\n >\n }\n : never)\n }>\n >\n }): QueryBuilder<any> {\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n if (typeof joinClause.from === `object` && joinClause.from !== null) {\n return this.joinCollectionRef(\n joinClause as {\n type: `inner` | `left` | `right` | `full` | `cross`\n from: CollectionRef\n on: Condition<any>\n where?: Condition<any>\n }\n )\n } else {\n return this.joinInputReference(\n joinClause as {\n type: `inner` | `left` | `right` | `full` | `cross`\n from: InputReference<{\n baseSchema: TContext[`baseSchema`]\n schema: TContext[`baseSchema`]\n }>\n as?: TAs\n on: Condition<any>\n where?: Condition<any>\n }\n )\n }\n }\n\n private joinCollectionRef<TCollectionRef extends CollectionRef>(joinClause: {\n type: `inner` | `left` | `right` | `full` | `cross`\n from: TCollectionRef\n on: Condition<any>\n where?: Condition<any>\n }): QueryBuilder<any> {\n // Create a new builder with a copy of the current query\n const newBuilder = new BaseQueryBuilder<TContext>()\n Object.assign(newBuilder.query, this.query)\n\n // Get the collection key\n const keys = Object.keys(joinClause.from)\n if (keys.length !== 1) {\n throw new Error(`Expected exactly one key in CollectionRef`)\n }\n const key = keys[0]!\n const collection = joinClause.from[key]\n if (!collection) {\n throw new Error(`Collection not found for key: ${key}`)\n }\n\n // Create a copy of the join clause for the query\n const joinClauseCopy = {\n type: joinClause.type,\n from: key,\n on: joinClause.on,\n where: joinClause.where,\n } as JoinClause<TContext>\n\n // Add the join clause to the query\n if (!newBuilder.query.join) {\n newBuilder.query.join = [joinClauseCopy]\n } else {\n newBuilder.query.join = [...newBuilder.query.join, joinClauseCopy]\n }\n\n // Add the collection to the collections map\n newBuilder.query.collections ??= {}\n newBuilder.query.collections[key] = collection\n\n // Return the new builder with updated schema type\n return newBuilder as QueryBuilder<\n Flatten<\n Omit<TContext, `schema`> & {\n schema: TContext[`schema`] & {\n [K in keyof TCollectionRef & string]: RemoveIndexSignature<\n (TCollectionRef[keyof TCollectionRef] extends Collection<infer T>\n ? T\n : never) &\n Input\n >\n }\n }\n >\n >\n }\n\n private joinInputReference<\n TFrom extends InputReference<{\n baseSchema: TContext[`baseSchema`]\n schema: TContext[`baseSchema`]\n }>,\n TAs extends string | undefined = undefined,\n >(joinClause: {\n type: `inner` | `left` | `right` | `full` | `cross`\n from: TFrom\n as?: TAs\n on: Condition<any>\n where?: Condition<any>\n }): QueryBuilder<any> {\n // Create a new builder with a copy of the current query\n const newBuilder = new BaseQueryBuilder<TContext>()\n Object.assign(newBuilder.query, this.query)\n\n // Create a copy of the join clause for the query\n const joinClauseCopy = { ...joinClause } as JoinClause<TContext>\n\n // Add the join clause to the query\n if (!newBuilder.query.join) {\n newBuilder.query.join = [joinClauseCopy]\n } else {\n newBuilder.query.join = [...newBuilder.query.join, joinClauseCopy]\n }\n\n // Determine the alias or use the collection name as default\n const _effectiveAlias = joinClause.as ?? joinClause.from\n\n // Return the new builder with updated schema type\n return newBuilder as QueryBuilder<\n Flatten<\n Omit<TContext, `schema`> & {\n schema: TContext[`schema`] & {\n [K in typeof _effectiveAlias]: TContext[`baseSchema`][TFrom]\n }\n }\n >\n >\n }\n\n /**\n * Add an orderBy clause to sort the results.\n * Overwrites any previous orderBy clause.\n *\n * @param orderBy The order specification\n * @returns A new QueryBuilder with the orderBy clause set\n */\n orderBy(orderBy: OrderBy<TContext>): QueryBuilder<TContext> {\n // Create a new builder with a copy of the current query\n const newBuilder = new BaseQueryBuilder<TContext>()\n Object.assign(newBuilder.query, this.query)\n\n // Set the orderBy clause\n newBuilder.query.orderBy = orderBy\n\n // Ensure we have an orderByIndex in the select if we have an orderBy\n // This is required if select is called before orderBy\n newBuilder.query.select = [\n ...(newBuilder.query.select ?? []),\n { _orderByIndex: { ORDER_INDEX: `numeric` } },\n ]\n\n return newBuilder as QueryBuilder<TContext>\n }\n\n /**\n * Set a limit on the number of results returned.\n *\n * @param limit Maximum number of results to return\n * @returns A new QueryBuilder with the limit set\n */\n limit(limit: Limit<TContext>): QueryBuilder<TContext> {\n // Create a new builder with a copy of the current query\n const newBuilder = new BaseQueryBuilder<TContext>()\n Object.assign(newBuilder.query, this.query)\n\n // Set the limit\n newBuilder.query.limit = limit\n\n return newBuilder as QueryBuilder<TContext>\n }\n\n /**\n * Set an offset to skip a number of results.\n *\n * @param offset Number of results to skip\n * @returns A new QueryBuilder with the offset set\n */\n offset(offset: Offset<TContext>): QueryBuilder<TContext> {\n // Create a new builder with a copy of the current query\n const newBuilder = new BaseQueryBuilder<TContext>()\n Object.assign(newBuilder.query, this.query)\n\n // Set the offset\n newBuilder.query.offset = offset\n\n return newBuilder as QueryBuilder<TContext>\n }\n\n /**\n * Add a groupBy clause to group the results by one or more columns.\n *\n * @param groupBy The column(s) to group by\n * @returns A new QueryBuilder with the groupBy clause set\n */\n groupBy(\n groupBy: PropertyReference<TContext> | Array<PropertyReference<TContext>>\n ): QueryBuilder<TContext> {\n // Create a new builder with a copy of the current query\n const newBuilder = new BaseQueryBuilder<TContext>()\n Object.assign(newBuilder.query, this.query)\n\n // Set the groupBy clause\n newBuilder.query.groupBy = groupBy\n\n return newBuilder as QueryBuilder<TContext>\n }\n\n /**\n * Define a Common Table Expression (CTE) that can be referenced in the main query.\n * This allows referencing the CTE by name in subsequent from/join clauses.\n *\n * @param name The name of the CTE\n * @param queryBuilderCallback A function that builds the CTE query\n * @returns A new QueryBuilder with the CTE added\n */\n with<TName extends string, TResult = Record<string, unknown>>(\n name: TName,\n queryBuilderCallback: (\n builder: InitialQueryBuilder<{\n baseSchema: TContext[`baseSchema`]\n schema: {}\n }>\n ) => QueryBuilder<any>\n ): InitialQueryBuilder<{\n baseSchema: TContext[`baseSchema`] & { [K in TName]: TResult }\n schema: TContext[`schema`]\n }> {\n // Create a new builder with a copy of the current query\n const newBuilder = new BaseQueryBuilder<TContext>()\n Object.assign(newBuilder.query, this.query)\n\n // Create a new builder for the CTE\n const cteBuilder = new BaseQueryBuilder<{\n baseSchema: TContext[`baseSchema`]\n schema: {}\n }>()\n\n // Get the CTE query from the callback\n const cteQueryBuilder = queryBuilderCallback(\n cteBuilder as InitialQueryBuilder<{\n baseSchema: TContext[`baseSchema`]\n schema: {}\n }>\n )\n\n // Get the query from the builder\n const cteQuery = cteQueryBuilder._query\n\n // Add an 'as' property to the CTE\n const withQuery: WithQuery<any> = {\n ...cteQuery,\n as: name,\n }\n\n // Add the CTE to the with array\n if (!newBuilder.query.with) {\n newBuilder.query.with = [withQuery]\n } else {\n newBuilder.query.with = [...newBuilder.query.with, withQuery]\n }\n\n // Use a type cast that simplifies the type structure to avoid recursion\n return newBuilder as unknown as InitialQueryBuilder<{\n baseSchema: TContext[`baseSchema`] & { [K in TName]: TResult }\n schema: TContext[`schema`]\n }>\n }\n\n get _query(): Query<TContext> {\n return this.query as Query<TContext>\n }\n}\n\nexport type InitialQueryBuilder<TContext extends Context<Schema>> = Pick<\n BaseQueryBuilder<TContext>,\n `from` | `with`\n>\n\nexport type QueryBuilder<TContext extends Context<Schema>> = Omit<\n BaseQueryBuilder<TContext>,\n `from`\n>\n\n/**\n * Create a new query builder with the given schema\n */\nexport function queryBuilder<TBaseSchema extends Schema = {}>() {\n return new BaseQueryBuilder<{\n baseSchema: TBaseSchema\n schema: {}\n }>() as InitialQueryBuilder<{\n baseSchema: TBaseSchema\n schema: {}\n }>\n}\n\nexport type ResultsFromContext<TContext extends Context<Schema>> = Flatten<\n TContext[`result`] extends object\n ? TContext[`result`] // If there is a select we will have a result type\n : TContext[`hasJoin`] extends true\n ? TContext[`schema`] // If there is a join, the query returns the namespaced schema\n : TContext[`default`] extends keyof TContext[`schema`]\n ? TContext[`schema`][TContext[`default`]] // If there is no join we return the flat default schema\n : never // Should never happen\n>\n\nexport type ResultFromQueryBuilder<TQueryBuilder> = Flatten<\n TQueryBuilder extends QueryBuilder<infer C>\n ? C extends { result: infer R }\n ? R\n : never\n : never\n>\n"],"names":[],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  import { Collection } from '../collection.cjs';
2
- import { Comparator, Condition, Limit, LiteralValue, Offset, OrderBy, Query, Select, WhereCallback } from './schema.js';
2
+ import { Comparator, ComparatorValue, Condition, Limit, LiteralValue, Offset, OrderBy, Query, Select, WhereCallback } from './schema.js';
3
3
  import { Context, Flatten, InferResultTypeFromSelectTuple, Input, InputReference, PropertyReference, PropertyReferenceString, RemoveIndexSignature, Schema } from './types.js';
4
4
  type CollectionRef = {
5
5
  [K: string]: Collection<any>;
@@ -55,7 +55,7 @@ export declare class BaseQueryBuilder<TContext extends Context<Schema>> {
55
55
  /**
56
56
  * Add a where clause comparing two values.
57
57
  */
58
- where(left: PropertyReferenceString<TContext> | LiteralValue, operator: Comparator, right: PropertyReferenceString<TContext> | LiteralValue): QueryBuilder<TContext>;
58
+ where<T extends Comparator>(left: PropertyReferenceString<TContext> | LiteralValue, operator: T, right: ComparatorValue<T, TContext>): QueryBuilder<TContext>;
59
59
  /**
60
60
  * Add a where clause with a complete condition object.
61
61
  */
@@ -1,10 +1,11 @@
1
- import { Context, InputReference, PropertyReference, PropertyReferenceString, WildcardReferenceString } from './types.js';
1
+ import { Context, InputReference, PropertyReference, PropertyReferenceString, Schema, WildcardReferenceString } from './types.js';
2
2
  import { Collection } from '../collection.cjs';
3
3
  export type ColumnName<TColumnNames extends string> = TColumnNames;
4
4
  export type JSONLike = string | number | boolean | Date | null | Array<JSONLike> | {
5
5
  [key: string]: JSONLike;
6
6
  };
7
7
  export type LiteralValue = (string & {}) | number | boolean | Date | null | undefined;
8
+ export type ComparatorValue<T extends Comparator, TContext extends Context<Schema>> = T extends `in` | `not in` ? Array<LiteralValue> : PropertyReferenceString<TContext> | LiteralValue;
8
9
  export type SafeString<T extends string> = T extends `@${string}` ? never : T;
9
10
  export type OptionalSafeString<T extends any> = T extends string ? SafeString<T> : never;
10
11
  export type LiteralValueWithSafeString<T extends any> = (OptionalSafeString<T> & {}) | number | boolean | Date | null | undefined;
@@ -63,7 +64,7 @@ export type Select<TContext extends Context = Context> = PropertyReferenceString
63
64
  export type SelectCallback<TContext extends Context = Context> = (context: TContext extends {
64
65
  schema: infer S;
65
66
  } ? S : any) => any;
66
- export type As<TContext extends Context = Context> = string;
67
+ export type As<_TContext extends Context = Context> = string;
67
68
  export type From<TContext extends Context = Context> = InputReference<{
68
69
  baseSchema: TContext[`baseSchema`];
69
70
  schema: TContext[`baseSchema`];
@@ -74,8 +75,8 @@ export type WhereCallback<TContext extends Context = Context> = (context: TConte
74
75
  export type Where<TContext extends Context = Context> = Array<Condition<TContext> | WhereCallback<TContext>>;
75
76
  export type Having<TContext extends Context = Context> = Where<TContext>;
76
77
  export type GroupBy<TContext extends Context = Context> = PropertyReference<TContext> | Array<PropertyReference<TContext>>;
77
- export type Limit<TContext extends Context = Context> = number;
78
- export type Offset<TContext extends Context = Context> = number;
78
+ export type Limit<_TContext extends Context = Context> = number;
79
+ export type Offset<_TContext extends Context = Context> = number;
79
80
  export interface BaseQuery<TContext extends Context = Context> {
80
81
  select?: Array<Select<TContext>>;
81
82
  as?: As<TContext>;
@@ -79,7 +79,7 @@ class Transaction {
79
79
  applyMutations(mutations) {
80
80
  for (const newMutation of mutations) {
81
81
  const existingIndex = this.mutations.findIndex(
82
- (m) => m.key === newMutation.key
82
+ (m) => m.globalKey === newMutation.globalKey
83
83
  );
84
84
  if (existingIndex >= 0) {
85
85
  this.mutations[existingIndex] = newMutation;
@@ -97,9 +97,9 @@ class Transaction {
97
97
  this.setState(`failed`);
98
98
  if (!isSecondaryRollback) {
99
99
  const mutationIds = /* @__PURE__ */ new Set();
100
- this.mutations.forEach((m) => mutationIds.add(m.key));
100
+ this.mutations.forEach((m) => mutationIds.add(m.globalKey));
101
101
  for (const t of transactions) {
102
- t.state === `pending` && t.mutations.some((m) => mutationIds.has(m.key)) && t.rollback({ isSecondaryRollback: true });
102
+ t.state === `pending` && t.mutations.some((m) => mutationIds.has(m.globalKey)) && t.rollback({ isSecondaryRollback: true });
103
103
  }
104
104
  }
105
105
  this.isPersisted.reject((_a = this.error) == null ? void 0 : _a.error);
@@ -111,7 +111,7 @@ class Transaction {
111
111
  const hasCalled = /* @__PURE__ */ new Set();
112
112
  for (const mutation of this.mutations) {
113
113
  if (!hasCalled.has(mutation.collection.id)) {
114
- mutation.collection.transactions.setState((state) => state);
114
+ mutation.collection.onTransactionStateChange();
115
115
  mutation.collection.commitPendingTransactions();
116
116
  hasCalled.add(mutation.collection.id);
117
117
  }
@@ -127,8 +127,9 @@ class Transaction {
127
127
  return this;
128
128
  }
129
129
  try {
130
- const transactionWithMutations = this;
131
- await this.mutationFn({ transaction: transactionWithMutations });
130
+ await this.mutationFn({
131
+ transaction: this
132
+ });
132
133
  this.setState(`completed`);
133
134
  this.touchCollection();
134
135
  this.isPersisted.resolve(this);
@@ -1 +1 @@
1
- {"version":3,"file":"transactions.cjs","sources":["../../src/transactions.ts"],"sourcesContent":["import { createDeferred } from \"./deferred\"\nimport type { Deferred } from \"./deferred\"\nimport type {\n PendingMutation,\n TransactionConfig,\n TransactionState,\n TransactionWithMutations,\n} from \"./types\"\n\nfunction generateUUID() {\n // Check if crypto.randomUUID is available (modern browsers and Node.js 15+)\n if (\n typeof crypto !== `undefined` &&\n typeof crypto.randomUUID === `function`\n ) {\n return crypto.randomUUID()\n }\n\n // Fallback implementation for older environments\n return `xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx`.replace(/[xy]/g, function (c) {\n const r = (Math.random() * 16) | 0\n const v = c === `x` ? r : (r & 0x3) | 0x8\n return v.toString(16)\n })\n}\n\nconst transactions: Array<Transaction> = []\nlet transactionStack: Array<Transaction> = []\n\nexport function createTransaction(config: TransactionConfig): Transaction {\n if (typeof config.mutationFn === `undefined`) {\n throw `mutationFn is required when creating a transaction`\n }\n\n let transactionId = config.id\n if (!transactionId) {\n transactionId = generateUUID()\n }\n const newTransaction = new Transaction({ ...config, id: transactionId })\n\n transactions.push(newTransaction)\n\n return newTransaction\n}\n\nexport function getActiveTransaction(): Transaction | undefined {\n if (transactionStack.length > 0) {\n return transactionStack.slice(-1)[0]\n } else {\n return undefined\n }\n}\n\nfunction registerTransaction(tx: Transaction) {\n transactionStack.push(tx)\n}\n\nfunction unregisterTransaction(tx: Transaction) {\n transactionStack = transactionStack.filter((t) => t.id !== tx.id)\n}\n\nfunction removeFromPendingList(tx: Transaction) {\n const index = transactions.findIndex((t) => t.id === tx.id)\n if (index !== -1) {\n transactions.splice(index, 1)\n }\n}\n\nexport class Transaction {\n public id: string\n public state: TransactionState\n public mutationFn\n public mutations: Array<PendingMutation<any>>\n public isPersisted: Deferred<Transaction>\n public autoCommit: boolean\n public createdAt: Date\n public metadata: Record<string, unknown>\n public error?: {\n message: string\n error: Error\n }\n\n constructor(config: TransactionConfig) {\n this.id = config.id!\n this.mutationFn = config.mutationFn\n this.state = `pending`\n this.mutations = []\n this.isPersisted = createDeferred()\n this.autoCommit = config.autoCommit ?? true\n this.createdAt = new Date()\n this.metadata = config.metadata ?? {}\n }\n\n setState(newState: TransactionState) {\n this.state = newState\n\n if (newState === `completed` || newState === `failed`) {\n removeFromPendingList(this)\n }\n }\n\n mutate(callback: () => void): Transaction {\n if (this.state !== `pending`) {\n throw `You can no longer call .mutate() as the transaction is no longer pending`\n }\n\n registerTransaction(this)\n try {\n callback()\n } finally {\n unregisterTransaction(this)\n }\n\n if (this.autoCommit) {\n this.commit()\n }\n\n return this\n }\n\n applyMutations(mutations: Array<PendingMutation<any>>): void {\n for (const newMutation of mutations) {\n const existingIndex = this.mutations.findIndex(\n (m) => m.key === newMutation.key\n )\n\n if (existingIndex >= 0) {\n // Replace existing mutation\n this.mutations[existingIndex] = newMutation\n } else {\n // Insert new mutation\n this.mutations.push(newMutation)\n }\n }\n }\n\n rollback(config?: { isSecondaryRollback?: boolean }): Transaction {\n const isSecondaryRollback = config?.isSecondaryRollback ?? false\n if (this.state === `completed`) {\n throw `You can no longer call .rollback() as the transaction is already completed`\n }\n\n this.setState(`failed`)\n\n // See if there's any other transactions w/ mutations on the same ids\n // and roll them back as well.\n if (!isSecondaryRollback) {\n const mutationIds = new Set()\n this.mutations.forEach((m) => mutationIds.add(m.key))\n for (const t of transactions) {\n t.state === `pending` &&\n t.mutations.some((m) => mutationIds.has(m.key)) &&\n t.rollback({ isSecondaryRollback: true })\n }\n }\n\n // Reject the promise\n this.isPersisted.reject(this.error?.error)\n this.touchCollection()\n\n return this\n }\n\n // Tell collection that something has changed with the transaction\n touchCollection(): void {\n const hasCalled = new Set()\n for (const mutation of this.mutations) {\n if (!hasCalled.has(mutation.collection.id)) {\n mutation.collection.transactions.setState((state) => state)\n mutation.collection.commitPendingTransactions()\n hasCalled.add(mutation.collection.id)\n }\n }\n }\n\n async commit(): Promise<Transaction> {\n if (this.state !== `pending`) {\n throw `You can no longer call .commit() as the transaction is no longer pending`\n }\n\n this.setState(`persisting`)\n\n if (this.mutations.length === 0) {\n this.setState(`completed`)\n\n return this\n }\n\n // Run mutationFn\n try {\n // At this point we know there's at least one mutation\n // Use type assertion to tell TypeScript about this guarantee\n const transactionWithMutations =\n this as unknown as TransactionWithMutations\n await this.mutationFn({ transaction: transactionWithMutations })\n\n this.setState(`completed`)\n this.touchCollection()\n\n this.isPersisted.resolve(this)\n } catch (error) {\n // Update transaction with error information\n this.error = {\n message: error instanceof Error ? error.message : String(error),\n error: error instanceof Error ? error : new Error(String(error)),\n }\n\n // rollback the transaction\n return this.rollback()\n }\n\n return this\n }\n}\n"],"names":["createDeferred"],"mappings":"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"}
1
+ {"version":3,"file":"transactions.cjs","sources":["../../src/transactions.ts"],"sourcesContent":["import { createDeferred } from \"./deferred\"\nimport type { Deferred } from \"./deferred\"\nimport type {\n MutationFn,\n PendingMutation,\n TransactionConfig,\n TransactionState,\n TransactionWithMutations,\n} from \"./types\"\n\nfunction generateUUID() {\n // Check if crypto.randomUUID is available (modern browsers and Node.js 15+)\n if (\n typeof crypto !== `undefined` &&\n typeof crypto.randomUUID === `function`\n ) {\n return crypto.randomUUID()\n }\n\n // Fallback implementation for older environments\n return `xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx`.replace(/[xy]/g, function (c) {\n const r = (Math.random() * 16) | 0\n const v = c === `x` ? r : (r & 0x3) | 0x8\n return v.toString(16)\n })\n}\n\nconst transactions: Array<Transaction<any>> = []\nlet transactionStack: Array<Transaction<any>> = []\n\nexport function createTransaction(config: TransactionConfig): Transaction {\n if (typeof config.mutationFn === `undefined`) {\n throw `mutationFn is required when creating a transaction`\n }\n\n let transactionId = config.id\n if (!transactionId) {\n transactionId = generateUUID()\n }\n const newTransaction = new Transaction({ ...config, id: transactionId })\n\n transactions.push(newTransaction)\n\n return newTransaction\n}\n\nexport function getActiveTransaction(): Transaction | undefined {\n if (transactionStack.length > 0) {\n return transactionStack.slice(-1)[0]\n } else {\n return undefined\n }\n}\n\nfunction registerTransaction(tx: Transaction<any>) {\n transactionStack.push(tx)\n}\n\nfunction unregisterTransaction(tx: Transaction<any>) {\n transactionStack = transactionStack.filter((t) => t.id !== tx.id)\n}\n\nfunction removeFromPendingList(tx: Transaction<any>) {\n const index = transactions.findIndex((t) => t.id === tx.id)\n if (index !== -1) {\n transactions.splice(index, 1)\n }\n}\n\nexport class Transaction<T extends object = Record<string, unknown>> {\n public id: string\n public state: TransactionState\n public mutationFn: MutationFn<T>\n public mutations: Array<PendingMutation<T>>\n public isPersisted: Deferred<Transaction<T>>\n public autoCommit: boolean\n public createdAt: Date\n public metadata: Record<string, unknown>\n public error?: {\n message: string\n error: Error\n }\n\n constructor(config: TransactionConfig<T>) {\n this.id = config.id!\n this.mutationFn = config.mutationFn\n this.state = `pending`\n this.mutations = []\n this.isPersisted = createDeferred<Transaction<T>>()\n this.autoCommit = config.autoCommit ?? true\n this.createdAt = new Date()\n this.metadata = config.metadata ?? {}\n }\n\n setState(newState: TransactionState) {\n this.state = newState\n\n if (newState === `completed` || newState === `failed`) {\n removeFromPendingList(this)\n }\n }\n\n mutate(callback: () => void): Transaction<T> {\n if (this.state !== `pending`) {\n throw `You can no longer call .mutate() as the transaction is no longer pending`\n }\n\n registerTransaction(this)\n try {\n callback()\n } finally {\n unregisterTransaction(this)\n }\n\n if (this.autoCommit) {\n this.commit()\n }\n\n return this\n }\n\n applyMutations(mutations: Array<PendingMutation<any>>): void {\n for (const newMutation of mutations) {\n const existingIndex = this.mutations.findIndex(\n (m) => m.globalKey === newMutation.globalKey\n )\n\n if (existingIndex >= 0) {\n // Replace existing mutation\n this.mutations[existingIndex] = newMutation\n } else {\n // Insert new mutation\n this.mutations.push(newMutation)\n }\n }\n }\n\n rollback(config?: { isSecondaryRollback?: boolean }): Transaction<T> {\n const isSecondaryRollback = config?.isSecondaryRollback ?? false\n if (this.state === `completed`) {\n throw `You can no longer call .rollback() as the transaction is already completed`\n }\n\n this.setState(`failed`)\n\n // See if there's any other transactions w/ mutations on the same ids\n // and roll them back as well.\n if (!isSecondaryRollback) {\n const mutationIds = new Set()\n this.mutations.forEach((m) => mutationIds.add(m.globalKey))\n for (const t of transactions) {\n t.state === `pending` &&\n t.mutations.some((m) => mutationIds.has(m.globalKey)) &&\n t.rollback({ isSecondaryRollback: true })\n }\n }\n\n // Reject the promise\n this.isPersisted.reject(this.error?.error)\n this.touchCollection()\n\n return this\n }\n\n // Tell collection that something has changed with the transaction\n touchCollection(): void {\n const hasCalled = new Set()\n for (const mutation of this.mutations) {\n if (!hasCalled.has(mutation.collection.id)) {\n mutation.collection.onTransactionStateChange()\n mutation.collection.commitPendingTransactions()\n hasCalled.add(mutation.collection.id)\n }\n }\n }\n\n async commit(): Promise<Transaction<T>> {\n if (this.state !== `pending`) {\n throw `You can no longer call .commit() as the transaction is no longer pending`\n }\n\n this.setState(`persisting`)\n\n if (this.mutations.length === 0) {\n this.setState(`completed`)\n\n return this\n }\n\n // Run mutationFn\n try {\n // At this point we know there's at least one mutation\n // We've already verified mutations is non-empty, so this cast is safe\n // Use a direct type assertion instead of object spreading to preserve the original type\n await this.mutationFn({\n transaction: this as unknown as TransactionWithMutations<T>,\n })\n\n this.setState(`completed`)\n this.touchCollection()\n\n this.isPersisted.resolve(this)\n } catch (error) {\n // Update transaction with error information\n this.error = {\n message: error instanceof Error ? error.message : String(error),\n error: error instanceof Error ? error : new Error(String(error)),\n }\n\n // rollback the transaction\n return this.rollback()\n }\n\n return this\n }\n}\n"],"names":["createDeferred"],"mappings":"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"}
@@ -1,13 +1,13 @@
1
1
  import { Deferred } from './deferred.cjs';
2
- import { PendingMutation, TransactionConfig, TransactionState } from './types.cjs';
2
+ import { MutationFn, PendingMutation, TransactionConfig, TransactionState } from './types.cjs';
3
3
  export declare function createTransaction(config: TransactionConfig): Transaction;
4
4
  export declare function getActiveTransaction(): Transaction | undefined;
5
- export declare class Transaction {
5
+ export declare class Transaction<T extends object = Record<string, unknown>> {
6
6
  id: string;
7
7
  state: TransactionState;
8
- mutationFn: import('./types.cjs').MutationFn;
9
- mutations: Array<PendingMutation<any>>;
10
- isPersisted: Deferred<Transaction>;
8
+ mutationFn: MutationFn<T>;
9
+ mutations: Array<PendingMutation<T>>;
10
+ isPersisted: Deferred<Transaction<T>>;
11
11
  autoCommit: boolean;
12
12
  createdAt: Date;
13
13
  metadata: Record<string, unknown>;
@@ -15,13 +15,13 @@ export declare class Transaction {
15
15
  message: string;
16
16
  error: Error;
17
17
  };
18
- constructor(config: TransactionConfig);
18
+ constructor(config: TransactionConfig<T>);
19
19
  setState(newState: TransactionState): void;
20
- mutate(callback: () => void): Transaction;
20
+ mutate(callback: () => void): Transaction<T>;
21
21
  applyMutations(mutations: Array<PendingMutation<any>>): void;
22
22
  rollback(config?: {
23
23
  isSecondaryRollback?: boolean;
24
- }): Transaction;
24
+ }): Transaction<T>;
25
25
  touchCollection(): void;
26
- commit(): Promise<Transaction>;
26
+ commit(): Promise<Transaction<T>>;
27
27
  }
@@ -17,36 +17,41 @@ export type UtilsRecord = Record<string, Fn>;
17
17
  */
18
18
  export interface PendingMutation<T extends object = Record<string, unknown>> {
19
19
  mutationId: string;
20
- original: Record<string, unknown>;
21
- modified: Record<string, unknown>;
22
- changes: Record<string, unknown>;
20
+ original: Partial<T>;
21
+ modified: T;
22
+ changes: Partial<T>;
23
+ globalKey: string;
23
24
  key: any;
24
25
  type: OperationType;
25
26
  metadata: unknown;
26
27
  syncMetadata: Record<string, unknown>;
27
28
  createdAt: Date;
28
29
  updatedAt: Date;
29
- collection: Collection<T>;
30
+ collection: Collection<T, any>;
30
31
  }
31
32
  /**
32
33
  * Configuration options for creating a new transaction
33
34
  */
34
- export type MutationFnParams = {
35
- transaction: Transaction;
35
+ export type MutationFnParams<T extends object = Record<string, unknown>> = {
36
+ transaction: TransactionWithMutations<T>;
36
37
  };
37
- export type MutationFn = (params: MutationFnParams) => Promise<any>;
38
+ export type MutationFn<T extends object = Record<string, unknown>> = (params: MutationFnParams<T>) => Promise<any>;
39
+ /**
40
+ * Represents a non-empty array (at least one element)
41
+ */
42
+ export type NonEmptyArray<T> = [T, ...Array<T>];
38
43
  /**
39
44
  * Utility type for a Transaction with at least one mutation
40
45
  * This is used internally by the Transaction.commit method
41
46
  */
42
- export type TransactionWithMutations<T extends object = Record<string, unknown>> = Transaction & {
43
- mutations: [PendingMutation<T>, ...Array<PendingMutation<T>>];
47
+ export type TransactionWithMutations<T extends object = Record<string, unknown>> = Transaction<T> & {
48
+ mutations: NonEmptyArray<PendingMutation<T>>;
44
49
  };
45
- export interface TransactionConfig {
50
+ export interface TransactionConfig<T extends object = Record<string, unknown>> {
46
51
  /** Unique identifier for the transaction */
47
52
  id?: string;
48
53
  autoCommit?: boolean;
49
- mutationFn: MutationFn;
54
+ mutationFn: MutationFn<T>;
50
55
  /** Custom metadata to associate with the transaction */
51
56
  metadata?: Record<string, unknown>;
52
57
  }
@@ -56,9 +61,9 @@ type Value<TExtensions = never> = string | number | boolean | bigint | null | TE
56
61
  };
57
62
  export type Row<TExtensions = never> = Record<string, Value<TExtensions>>;
58
63
  export type OperationType = `insert` | `update` | `delete`;
59
- export interface SyncConfig<T extends object = Record<string, unknown>> {
64
+ export interface SyncConfig<T extends object = Record<string, unknown>, TKey extends string | number = string | number> {
60
65
  sync: (params: {
61
- collection: Collection<T>;
66
+ collection: Collection<T, TKey>;
62
67
  begin: () => void;
63
68
  write: (message: Omit<ChangeMessage<T>, `key`>) => void;
64
69
  commit: () => void;
@@ -69,8 +74,8 @@ export interface SyncConfig<T extends object = Record<string, unknown>> {
69
74
  */
70
75
  getSyncMetadata?: () => Record<string, unknown>;
71
76
  }
72
- export interface ChangeMessage<T extends object = Record<string, unknown>> {
73
- key: any;
77
+ export interface ChangeMessage<T extends object = Record<string, unknown>, TKey extends string | number = string | number> {
78
+ key: TKey;
74
79
  value: T;
75
80
  previousValue?: T;
76
81
  type: OperationType;
@@ -101,9 +106,9 @@ export interface OperationConfig {
101
106
  export interface InsertConfig {
102
107
  metadata?: Record<string, unknown>;
103
108
  }
104
- export interface CollectionConfig<T extends object = Record<string, unknown>> {
109
+ export interface CollectionConfig<T extends object = Record<string, unknown>, TKey extends string | number = string | number> {
105
110
  id?: string;
106
- sync: SyncConfig<T>;
111
+ sync: SyncConfig<T, TKey>;
107
112
  schema?: StandardSchema<T>;
108
113
  /**
109
114
  * Function to extract the ID from an object
@@ -112,27 +117,27 @@ export interface CollectionConfig<T extends object = Record<string, unknown>> {
112
117
  * @returns The ID string for the item
113
118
  * @example
114
119
  * // For a collection with a 'uuid' field as the primary key
115
- * getId: (item) => item.uuid
120
+ * getKey: (item) => item.uuid
116
121
  */
117
- getId: (item: T) => any;
122
+ getKey: (item: T) => TKey;
118
123
  /**
119
124
  * Optional asynchronous handler function called before an insert operation
120
125
  * @param params Object containing transaction and mutation information
121
126
  * @returns Promise resolving to any value
122
127
  */
123
- onInsert?: MutationFn;
128
+ onInsert?: MutationFn<T>;
124
129
  /**
125
130
  * Optional asynchronous handler function called before an update operation
126
131
  * @param params Object containing transaction and mutation information
127
132
  * @returns Promise resolving to any value
128
133
  */
129
- onUpdate?: MutationFn;
134
+ onUpdate?: MutationFn<T>;
130
135
  /**
131
136
  * Optional asynchronous handler function called before a delete operation
132
137
  * @param params Object containing transaction and mutation information
133
138
  * @returns Promise resolving to any value
134
139
  */
135
- onDelete?: MutationFn;
140
+ onDelete?: MutationFn<T>;
136
141
  }
137
142
  export type ChangesPayload<T extends object = Record<string, unknown>> = Array<ChangeMessage<T>>;
138
143
  /**
@@ -159,3 +164,4 @@ export type KeyedNamespacedRow = [unknown, NamespacedRow];
159
164
  * a `select` clause.
160
165
  */
161
166
  export type NamespacedAndKeyedStream = IStreamBuilder<KeyedNamespacedRow>;
167
+ export type ChangeListener<T extends object = Record<string, unknown>, TKey extends string | number = string | number> = (changes: Array<ChangeMessage<T, TKey>>) => void;