@warlock.js/cascade 5.12.0 → 5.13.0
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/CHANGELOG.md +31 -0
- package/cjs/index.cjs +685 -128
- package/cjs/index.cjs.map +1 -1
- package/esm/contracts/database-driver.contract.d.mts +44 -7
- package/esm/contracts/database-driver.contract.d.mts.map +1 -1
- package/esm/contracts/index.d.mts +2 -2
- package/esm/contracts/query-builder.contract.d.mts +61 -1
- package/esm/contracts/query-builder.contract.d.mts.map +1 -1
- package/esm/drivers/mongodb/mongodb-driver.d.mts +8 -4
- package/esm/drivers/mongodb/mongodb-driver.d.mts.map +1 -1
- package/esm/drivers/mongodb/mongodb-driver.mjs +16 -6
- package/esm/drivers/mongodb/mongodb-driver.mjs.map +1 -1
- package/esm/drivers/mongodb/mongodb-query-builder.d.mts +32 -7
- package/esm/drivers/mongodb/mongodb-query-builder.d.mts.map +1 -1
- package/esm/drivers/mongodb/mongodb-query-builder.mjs +56 -8
- package/esm/drivers/mongodb/mongodb-query-builder.mjs.map +1 -1
- package/esm/drivers/mongodb/mongodb-query-parser.d.mts +39 -12
- package/esm/drivers/mongodb/mongodb-query-parser.d.mts.map +1 -1
- package/esm/drivers/mongodb/mongodb-query-parser.mjs +143 -55
- package/esm/drivers/mongodb/mongodb-query-parser.mjs.map +1 -1
- package/esm/drivers/mongodb/mongodb-update-translator.mjs +25 -0
- package/esm/drivers/mongodb/mongodb-update-translator.mjs.map +1 -0
- package/esm/drivers/mongodb/pipeline-stage-object.mjs +24 -0
- package/esm/drivers/mongodb/pipeline-stage-object.mjs.map +1 -0
- package/esm/drivers/mongodb/types.d.mts +7 -1
- package/esm/drivers/mongodb/types.d.mts.map +1 -1
- package/esm/drivers/postgres/postgres-driver.d.mts +52 -9
- package/esm/drivers/postgres/postgres-driver.d.mts.map +1 -1
- package/esm/drivers/postgres/postgres-driver.mjs +170 -38
- package/esm/drivers/postgres/postgres-driver.mjs.map +1 -1
- package/esm/drivers/postgres/postgres-query-builder.d.mts +17 -1
- package/esm/drivers/postgres/postgres-query-builder.d.mts.map +1 -1
- package/esm/drivers/postgres/postgres-query-builder.mjs +31 -0
- package/esm/drivers/postgres/postgres-query-builder.mjs.map +1 -1
- package/esm/drivers/postgres/postgres-update-validator.mjs +36 -0
- package/esm/drivers/postgres/postgres-update-validator.mjs.map +1 -0
- package/esm/errors/unsupported-lean-operation.error.d.mts +25 -0
- package/esm/errors/unsupported-lean-operation.error.d.mts.map +1 -0
- package/esm/errors/unsupported-lean-operation.error.mjs +31 -0
- package/esm/errors/unsupported-lean-operation.error.mjs.map +1 -0
- package/esm/errors/unsupported-query-operation.error.d.mts +30 -0
- package/esm/errors/unsupported-query-operation.error.d.mts.map +1 -0
- package/esm/errors/unsupported-query-operation.error.mjs +37 -0
- package/esm/errors/unsupported-query-operation.error.mjs.map +1 -0
- package/esm/errors/unsupported-update-operation.error.d.mts +30 -0
- package/esm/errors/unsupported-update-operation.error.d.mts.map +1 -0
- package/esm/errors/unsupported-update-operation.error.mjs +37 -0
- package/esm/errors/unsupported-update-operation.error.mjs.map +1 -0
- package/esm/index.d.mts +6 -3
- package/esm/index.mjs +4 -1
- package/esm/model/methods/query-methods.mjs +22 -6
- package/esm/model/methods/query-methods.mjs.map +1 -1
- package/esm/model/model.d.mts +28 -11
- package/esm/model/model.d.mts.map +1 -1
- package/esm/model/model.mjs +30 -13
- package/esm/model/model.mjs.map +1 -1
- package/esm/query-builder/lean-records.mjs +34 -0
- package/esm/query-builder/lean-records.mjs.map +1 -0
- package/esm/query-builder/query-builder.d.mts +13 -1
- package/esm/query-builder/query-builder.d.mts.map +1 -1
- package/esm/query-builder/query-builder.mjs +16 -0
- package/esm/query-builder/query-builder.mjs.map +1 -1
- package/llms-full.txt +72 -5
- package/llms.txt +3 -3
- package/package.json +4 -4
- package/skills/aggregate-data/SKILL.md +24 -1
- package/skills/perform-atomic-ops/SKILL.md +38 -3
- package/skills/query-data/SKILL.md +10 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"query-builder.mjs","names":[],"sources":["../../../../../../../cascade/src/query-builder/query-builder.ts"],"sourcesContent":["/**\r\n * Pure Query Builder Base Class\r\n *\r\n * Driver-agnostic operation recorder. All fluent methods push typed entries into\r\n * `operations[]`. No SQL, no driver references, no table property, no execution.\r\n *\r\n * ┌─────────────────────────────────────────────────┐\r\n * │ Usage contexts │\r\n * │ (a) Subclassed — PG / Mongo / MySQL / … │\r\n * │ (b) Instantiated directly (new QueryBuilder()) │\r\n * │ inside callbacks for: │\r\n * │ • nested where groups │\r\n * │ • joinWith constraints │\r\n * │ • whereExists / whereHas subqueries │\r\n * └─────────────────────────────────────────────────┘\r\n *\r\n * Design rules:\r\n * - `table` / alias are NOT here — the parser gets them from the executor.\r\n * - `opIndex` is protected so subclasses can rebuild after direct mutation.\r\n * - Op type names are stable — parsers switch on them; no renaming without\r\n * a parser update.\r\n * - OR-variants keep distinct op types (orWhere, orWhereColumn, …) so existing\r\n * parsers that switch on type need no changes.\r\n * - `joinWith` eagerly resolves callbacks → subOps at record time so the\r\n * driver executor receives a plain data structure, not a live function.\r\n *\r\n * @module cascade/query-builder\r\n */\r\n\r\nimport type {\r\n GroupByInput,\r\n HavingInput,\r\n JoinOptions,\r\n LockForUpdateOptions,\r\n OrderDirection,\r\n RawExpression,\r\n WhereCallback,\r\n WhereObject,\r\n WhereOperator,\r\n} from \"../contracts/query-builder.contract\";\r\nimport { sanitizeFilter, sanitizeFilterValue } from \"../utils/sanitize-filter\";\r\n\r\n// ============================================================================\r\n// TYPES\r\n// ============================================================================\r\n\r\n/**\r\n * A single recorded query operation.\r\n * `type` is the discriminator; `data` carries all parameters.\r\n */\r\nexport type Op = {\r\n readonly type: string;\r\n readonly data: Record<string, unknown>;\r\n};\r\n\r\n/**\r\n * Constraint value accepted by `joinWith()`.\r\n *\r\n * - `string` → comma-separated column shorthand: `\"id,name,createdAt\"`\r\n * - `fn` → callback receives a bare QueryBuilder to record sub-ops\r\n *\r\n * @example\r\n * joinWith({ actions: \"id,status\" })\r\n * joinWith({ actions: q => q.where(\"status\", \"pending\").limit(5) })\r\n */\r\nexport type JoinWithConstraint = string | ((q: QueryBuilder) => void);\r\n\r\n// ============================================================================\r\n// QUERY BUILDER — CONCRETE, DIRECTLY INSTANTIABLE\r\n// ============================================================================\r\n\r\n/**\r\n * Pure, driver-agnostic query builder.\r\n *\r\n * Records operations in `operations[]`. Subclasses own execution, parsing, and\r\n * driver-specific clause generation. Safe to instantiate directly inside\r\n * callbacks where only operation recording is needed.\r\n *\r\n * @example\r\n * ```ts\r\n * // Driver subclass usage:\r\n * const users = await User.query()\r\n * .select([\"id\", \"name\"])\r\n * .where(\"status\", \"active\")\r\n * .where(q => q.where(\"role\", \"admin\").orWhere(\"role\", \"mod\"))\r\n * .orderBy(\"createdAt\", \"desc\")\r\n * .limit(10)\r\n * .get();\r\n *\r\n * // Direct instantiation (callback context — no driver needed):\r\n * joinWith({ actions: q => q.where(\"status\", \"pending\").limit(5) });\r\n * // The sub-QB's operations[] are captured and stored in the joinWith op data.\r\n * ```\r\n */\r\nexport class QueryBuilder<T = unknown> {\r\n // ════════════════════════════════════════════════════════\r\n // OPERATION STORE\r\n // ════════════════════════════════════════════════════════\r\n\r\n /** Flat, ordered list of recorded operations. Public for parser access. */\r\n public operations: Op[] = [];\r\n\r\n /**\r\n * type → ordered list of indices into `operations[]`.\r\n *\r\n * Protected (not private) so:\r\n * - `rebuildIndex()` can reset it after direct `operations[]` mutation.\r\n * - Subclasses can inspect it without unsafe casts.\r\n *\r\n * External consumers should use `getOps(type)` instead.\r\n */\r\n protected opIndex: Map<string, number[]> = new Map();\r\n\r\n // ════════════════════════════════════════════════════════\r\n // SCOPE STATE (injected by Model.query(), consumed before execution)\r\n // ════════════════════════════════════════════════════════\r\n\r\n /** Global scope definitions injected by Model.query(). Keyed by scope name. */\r\n public pendingGlobalScopes?: Map<string, any>;\r\n /** Local scope callbacks injected by Model.query(). Applied on demand via scope(). */\r\n public availableLocalScopes?: Map<string, (...args: any[]) => void>;\r\n /** Names of global scopes that have been intentionally disabled. */\r\n public disabledGlobalScopes: Set<string> = new Set();\r\n /** True once the driver subclass has applied pending scopes. */\r\n public scopesApplied = false;\r\n\r\n // ════════════════════════════════════════════════════════\r\n // RELATION STATE (consumed by driver subclass at execute time)\r\n // ════════════════════════════════════════════════════════\r\n\r\n /** Relations to eager-load via separate queries. */\r\n public eagerLoadRelations: Map<string, boolean | ((query: any) => void)> = new Map();\r\n /** Count expressions to emit per result row, keyed by output column alias. */\r\n public countRelations: Map<string, { relation: string; constraintOps?: Op[] }> = new Map();\r\n /** Relation definition map injected from the owning Model. */\r\n public relationDefinitions?: Record<string, any>;\r\n /** The Model class reference, required for relation resolution. */\r\n public modelClass?: any;\r\n\r\n // ════════════════════════════════════════════════════════\r\n // CORE INTERNALS\r\n // ════════════════════════════════════════════════════════\r\n\r\n /**\r\n * Append an operation to `operations[]` and update `opIndex`.\r\n * Every fluent method calls this.\r\n */\r\n protected addOperation(type: string, data: Record<string, unknown>): void {\r\n const idx = this.operations.length;\r\n this.operations.push({ type, data });\r\n const list = this.opIndex.get(type);\r\n if (list) {\r\n list.push(idx);\r\n } else {\r\n this.opIndex.set(type, [idx]);\r\n }\r\n }\r\n\r\n /**\r\n * Return all recorded operations of the specified types in original\r\n * insertion order.\r\n *\r\n * @example\r\n * builder.getOps(\"where\", \"orWhere\", \"whereIn\")\r\n */\r\n public getOps(...types: string[]): Op[] {\r\n if (types.length === 1) {\n const type = types[0];\n if (type === undefined) {\n return [];\n }\n\n return (this.opIndex.get(type) ?? []).flatMap((index) => {\n const operation = this.operations[index];\n return operation === undefined ? [] : [operation];\n });\n }\r\n const result: Array<{ idx: number; op: Op }> = [];\r\n for (const type of types) {\r\n for (const idx of this.opIndex.get(type) ?? []) {\r\n const operation = this.operations[idx];\n if (operation !== undefined) {\n result.push({ idx, op: operation });\n }\n }\r\n }\r\n return result.sort((a, b) => a.idx - b.idx).map((r) => r.op);\r\n }\r\n\r\n /**\r\n * Rebuild `opIndex` from scratch.\r\n *\r\n * Call this after any direct mutation of `this.operations[]` (e.g. scope\r\n * injection, joinWith consumption in the executor, clone post-processing).\r\n */\r\n public rebuildIndex(): void {\r\n this.opIndex = new Map();\r\n for (let i = 0; i < this.operations.length; i++) {\r\n const operation = this.operations[i];\n if (operation === undefined) {\n continue;\n }\n\n const type = operation.type;\n const list = this.opIndex.get(type);\r\n if (list) {\r\n list.push(i);\r\n } else {\r\n this.opIndex.set(type, [i]);\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * Factory for sub-QueryBuilders used inside callbacks.\r\n *\r\n * Override in driver subclasses to return a driver-typed instance, so that\r\n * driver-specific methods (e.g. `whereArrayContains`) are available inside\r\n * nested `where(q => ...)` / `whereHas` / `joinWith` callbacks.\r\n *\r\n * @example\r\n * // In PostgresQueryBuilder:\r\n * protected override subQuery(): QueryBuilder {\r\n * return new PostgresQueryBuilder(\"__sub__\", this.dataSource);\r\n * }\r\n */\r\n protected subQuery(): QueryBuilder {\r\n return new QueryBuilder();\r\n }\r\n\r\n /**\r\n * Shallow-clone this builder — copies operations, opIndex, and all shared state.\r\n *\r\n * Subclasses MUST call `super.clone()` and then copy their own fields\r\n * (dataSource, joinRelations, …).\r\n */\r\n public clone(): this {\r\n const cloned = Object.create(Object.getPrototypeOf(this)) as this;\r\n cloned.operations = [...this.operations];\r\n cloned.opIndex = new Map(Array.from(this.opIndex.entries()).map(([k, v]) => [k, [...v]]));\r\n cloned.pendingGlobalScopes = this.pendingGlobalScopes;\r\n cloned.availableLocalScopes = this.availableLocalScopes;\r\n cloned.disabledGlobalScopes = new Set(this.disabledGlobalScopes);\r\n cloned.scopesApplied = this.scopesApplied;\r\n cloned.eagerLoadRelations = new Map(this.eagerLoadRelations);\r\n cloned.countRelations = new Map(this.countRelations);\r\n cloned.relationDefinitions = this.relationDefinitions;\r\n cloned.modelClass = this.modelClass;\r\n return cloned;\r\n }\r\n\r\n // ════════════════════════════════════════════════════════\r\n // SCOPES\r\n // ════════════════════════════════════════════════════════\r\n\r\n /** Disable one or more named global scopes for this query. */\r\n public withoutGlobalScope(...scopeNames: string[]): this {\r\n scopeNames.forEach((name) => this.disabledGlobalScopes.add(name));\r\n return this;\r\n }\r\n\r\n /** Disable ALL pending global scopes for this query. */\r\n public withoutGlobalScopes(): this {\r\n this.pendingGlobalScopes?.forEach((_, name) => this.disabledGlobalScopes.add(name));\r\n return this;\r\n }\r\n\r\n /**\r\n * Apply a registered local scope by name.\r\n * @throws if no local scopes are available or the named scope is not found\r\n */\r\n public scope(scopeName: string, ...args: unknown[]): this {\r\n if (!this.availableLocalScopes) {\r\n throw new Error(\"No local scopes available on this query builder.\");\r\n }\r\n const cb = this.availableLocalScopes.get(scopeName);\r\n if (!cb) throw new Error(`Local scope \"${scopeName}\" not found.`);\r\n cb(this, ...args);\r\n return this;\r\n }\r\n\r\n // ════════════════════════════════════════════════════════\r\n // WHERE CLAUSES — CORE\r\n // ════════════════════════════════════════════════════════\r\n\r\n /**\r\n * Add a WHERE clause (AND).\r\n *\r\n * @example\r\n * q.where(\"status\", \"active\")\r\n * q.where(\"age\", \">\", 18)\r\n * q.where({ role: \"admin\", active: true })\r\n * q.where(q => q.where(\"a\", 1).orWhere(\"b\", 2))\r\n */\r\n public where(field: string, value: unknown): this;\r\n public where(field: string, operator: WhereOperator, value: unknown): this;\r\n public where(conditions: WhereObject): this;\r\n public where(callback: WhereCallback<T>): this;\r\n public where(...args: unknown[]): this {\r\n if (args.length === 1 && typeof args[0] === \"function\") {\r\n const sub = this.subQuery();\r\n (args[0] as (q: QueryBuilder) => void)(sub);\r\n this.addOperation(\"where\", { nested: sub.operations });\r\n } else if (args.length === 1 && typeof args[0] === \"object\" && args[0] !== null) {\r\n for (const [key, value] of Object.entries(sanitizeFilter(args[0] as WhereObject))) {\r\n this.addOperation(\"where\", { field: key, operator: \"=\", value });\r\n }\r\n } else if (args.length === 2) {\r\n this.addOperation(\"where\", {\r\n field: args[0],\r\n operator: \"=\",\r\n value: sanitizeFilterValue(args[1], String(args[0])),\r\n });\r\n } else {\r\n // \"=\" is still an equality position — sanitize like the 2-arg form.\r\n this.addOperation(\"where\", {\r\n field: args[0],\r\n operator: args[1],\r\n value: args[1] === \"=\" ? sanitizeFilterValue(args[2], String(args[0])) : args[2],\r\n });\r\n }\r\n return this;\r\n }\r\n\r\n /**\r\n * Add an OR WHERE clause.\r\n *\r\n * @example\r\n * q.where(\"role\", \"admin\").orWhere(\"role\", \"mod\")\r\n */\r\n public orWhere(field: string, value: unknown): this;\r\n public orWhere(field: string, operator: WhereOperator, value: unknown): this;\r\n public orWhere(conditions: WhereObject): this;\r\n public orWhere(callback: WhereCallback<T>): this;\r\n public orWhere(...args: unknown[]): this {\r\n if (args.length === 1 && typeof args[0] === \"function\") {\r\n const sub = this.subQuery();\r\n (args[0] as (q: QueryBuilder) => void)(sub);\r\n this.addOperation(\"orWhere\", { nested: sub.operations });\r\n } else if (args.length === 1 && typeof args[0] === \"object\" && args[0] !== null) {\r\n for (const [key, value] of Object.entries(sanitizeFilter(args[0] as WhereObject))) {\r\n this.addOperation(\"orWhere\", { field: key, operator: \"=\", value });\r\n }\r\n } else if (args.length === 2) {\r\n this.addOperation(\"orWhere\", {\r\n field: args[0],\r\n operator: \"=\",\r\n value: sanitizeFilterValue(args[1], String(args[0])),\r\n });\r\n } else {\r\n // \"=\" is still an equality position — sanitize like the 2-arg form.\r\n this.addOperation(\"orWhere\", {\r\n field: args[0],\r\n operator: args[1],\r\n value: args[1] === \"=\" ? sanitizeFilterValue(args[2], String(args[0])) : args[2],\r\n });\r\n }\r\n return this;\r\n }\r\n\r\n /**\r\n * Raw WHERE expression in the target dialect (AND).\r\n *\r\n * @example\r\n * q.whereRaw(\"age > ? AND role = ?\", [18, \"admin\"]) // SQL\r\n * q.whereRaw({ $expr: { $gt: [\"$stock\", \"$reserved\"] } }) // MongoDB\r\n */\r\n public whereRaw(expression: RawExpression, bindings?: unknown[]): this {\r\n this.addOperation(\"whereRaw\", { expression, bindings: bindings ?? [] });\r\n return this;\r\n }\r\n\r\n /** Raw OR WHERE expression. */\r\n public orWhereRaw(expression: RawExpression, bindings?: unknown[]): this {\r\n this.addOperation(\"orWhereRaw\", { expression, bindings: bindings ?? [] });\r\n return this;\r\n }\r\n\r\n // ════════════════════════════════════════════════════════\r\n // WHERE CLAUSES — COLUMN COMPARISONS\r\n // ════════════════════════════════════════════════════════\r\n\r\n /**\r\n * Compare two columns directly (AND).\r\n * @example q.whereColumn(\"stock\", \">\", \"reserved\")\r\n */\r\n public whereColumn(first: string, operator: WhereOperator, second: string): this {\r\n this.addOperation(\"whereColumn\", { first, operator, second });\r\n return this;\r\n }\r\n\r\n /** Compare two columns directly (OR). */\r\n public orWhereColumn(first: string, operator: WhereOperator, second: string): this {\r\n this.addOperation(\"orWhereColumn\", { first, operator, second });\r\n return this;\r\n }\r\n\r\n /** Compare multiple column pairs in one call. */\r\n public whereColumns(\r\n comparisons: Array<[left: string, operator: WhereOperator, right: string]>,\r\n ): this {\r\n for (const [left, operator, right] of comparisons) {\r\n this.whereColumn(left, operator, right);\r\n }\r\n return this;\r\n }\r\n\r\n /**\r\n * Field value must fall between two other column values.\r\n * Stored as a `whereBetween` op with `useColumns: true` so the SQL parser\r\n * knows to quote the values as identifiers rather than bind them.\r\n */\r\n public whereBetweenColumns(field: string, lowerColumn: string, upperColumn: string): this {\r\n this.addOperation(\"whereBetween\", { field, lowerColumn, upperColumn, useColumns: true });\r\n return this;\r\n }\r\n\r\n // ════════════════════════════════════════════════════════\r\n // WHERE CLAUSES — STANDARD COMPARISON OPERATORS\r\n // ════════════════════════════════════════════════════════\r\n\r\n /** WHERE field IN values. */\r\n public whereIn(field: string, values: unknown[]): this {\r\n this.addOperation(\"whereIn\", { field, values });\r\n return this;\r\n }\r\n\r\n /** WHERE field NOT IN values. */\r\n public whereNotIn(field: string, values: unknown[]): this {\r\n this.addOperation(\"whereNotIn\", { field, values });\r\n return this;\r\n }\r\n\r\n /** WHERE field IS NULL. */\r\n public whereNull(field: string): this {\r\n this.addOperation(\"whereNull\", { field });\r\n return this;\r\n }\r\n\r\n /** WHERE field IS NOT NULL. */\r\n public whereNotNull(field: string): this {\r\n this.addOperation(\"whereNotNull\", { field });\r\n return this;\r\n }\r\n\r\n /** WHERE field BETWEEN low AND high. */\r\n public whereBetween(field: string, range: [unknown, unknown]): this {\r\n this.addOperation(\"whereBetween\", { field, range });\r\n return this;\r\n }\r\n\r\n /** WHERE field NOT BETWEEN low AND high. */\r\n public whereNotBetween(field: string, range: [unknown, unknown]): this {\r\n this.addOperation(\"whereNotBetween\", { field, range });\r\n return this;\r\n }\r\n\r\n // ════════════════════════════════════════════════════════\r\n // WHERE CLAUSES — PATTERN MATCHING\r\n // ════════════════════════════════════════════════════════\r\n\r\n /**\r\n * LIKE pattern match (AND).\r\n *\r\n * A string is a LIKE pattern (`%` wildcard) and is matched literally\r\n * otherwise — regex metacharacters in it are escaped by the driver, so\r\n * search input cannot alter the query. Pass an explicit `RegExp` to opt into\r\n * raw pattern semantics; never build that `RegExp` from user input.\r\n *\r\n * @example q.whereLike(\"email\", \"%@gmail.com\")\r\n */\r\n public whereLike(field: string, pattern: RegExp | string): this {\r\n this.addOperation(\"whereLike\", { field, ...this.likePatternData(pattern) });\r\n return this;\r\n }\r\n\r\n /** NOT LIKE pattern match. @see whereLike for the escaping rules. */\r\n public whereNotLike(field: string, pattern: RegExp | string): this {\r\n this.addOperation(\"whereNotLike\", { field, ...this.likePatternData(pattern) });\r\n return this;\r\n }\r\n\r\n /**\r\n * Flatten a LIKE argument into operation data, keeping the distinction the\r\n * drivers need: an explicit `RegExp` (developer-authored) stays a pattern,\r\n * a string (potentially request input) is a literal.\r\n */\r\n private likePatternData(pattern: RegExp | string): {\r\n pattern: string;\r\n isRegExp?: true;\r\n } {\r\n return pattern instanceof RegExp\r\n ? { pattern: pattern.source, isRegExp: true }\r\n : { pattern };\r\n }\r\n\r\n /** Starts with a prefix. */\r\n public whereStartsWith(field: string, value: string | number): this {\r\n return this.whereLike(field, `${value}%`);\r\n }\r\n\r\n /** Does NOT start with a prefix. */\r\n public whereNotStartsWith(field: string, value: string | number): this {\r\n return this.whereNotLike(field, `${value}%`);\r\n }\r\n\r\n /** Ends with a suffix. */\r\n public whereEndsWith(field: string, value: string | number): this {\r\n return this.whereLike(field, `%${value}`);\r\n }\r\n\r\n /** Does NOT end with a suffix. */\r\n public whereNotEndsWith(field: string, value: string | number): this {\r\n return this.whereNotLike(field, `%${value}`);\r\n }\r\n\r\n // ════════════════════════════════════════════════════════\r\n // WHERE CLAUSES — DATE/TIME PARTIALS\r\n // ════════════════════════════════════════════════════════\r\n\r\n /**\r\n * Match on date portion only (time ignored).\r\n * @example q.whereDate(\"createdAt\", \"2024-05-01\")\r\n */\r\n public whereDate(field: string, value: Date | string): this {\r\n this.addOperation(\"whereDate\", { field, value });\r\n return this;\r\n }\r\n\r\n /** Alias for whereDate. */\r\n public whereDateEquals(field: string, value: Date | string): this {\r\n return this.whereDate(field, value);\r\n }\r\n\r\n /** Field date is before value. */\r\n public whereDateBefore(field: string, value: Date | string): this {\r\n this.addOperation(\"whereDateBefore\", { field, value });\r\n return this;\r\n }\r\n\r\n /** Field date is after value. */\r\n public whereDateAfter(field: string, value: Date | string): this {\r\n this.addOperation(\"whereDateAfter\", { field, value });\r\n return this;\r\n }\r\n\r\n /** Field date is within a range [from, to]. */\r\n public whereDateBetween(field: string, range: [Date | string, Date | string]): this {\r\n this.addOperation(\"whereDateBetween\", { field, range });\r\n return this;\r\n }\r\n\r\n /** Field date is NOT within a range. */\r\n public whereDateNotBetween(field: string, range: [Date | string, Date | string]): this {\r\n this.addOperation(\"whereNotBetween\", { field, range });\r\n return this;\r\n }\r\n\r\n /**\r\n * Match on the time portion of a datetime field.\r\n * Emits a `whereRaw` op with a driver-agnostic marker; the driver parser\r\n * rewrites it to the appropriate SQL (`TIME(field) = ?`) or Mongo expression.\r\n */\r\n public whereTime(field: string, value: string): this {\r\n this.addOperation(\"whereRaw\", {\r\n expression: `TIME(${field}) = ?`,\r\n bindings: [value],\r\n });\r\n return this;\r\n }\r\n\r\n /**\r\n * Day-of-month from a date field (1–31).\r\n * Uses a `whereRaw` op so SQL parsers get the `EXTRACT` expression directly.\r\n * MongoDB drivers override to emit `$dayOfMonth`.\r\n */\r\n public whereDay(field: string, value: number): this {\r\n this.addOperation(\"whereRaw\", {\r\n expression: `EXTRACT(DAY FROM ${field}) = ?`,\r\n bindings: [value],\r\n });\r\n return this;\r\n }\r\n\r\n /** Month extracted from a date field (1–12). */\r\n public whereMonth(field: string, value: number): this {\r\n this.addOperation(\"whereRaw\", {\r\n expression: `EXTRACT(MONTH FROM ${field}) = ?`,\r\n bindings: [value],\r\n });\r\n return this;\r\n }\r\n\r\n /** Year extracted from a date field. */\r\n public whereYear(field: string, value: number): this {\r\n this.addOperation(\"whereRaw\", {\r\n expression: `EXTRACT(YEAR FROM ${field}) = ?`,\r\n bindings: [value],\r\n });\r\n return this;\r\n }\r\n\r\n // ════════════════════════════════════════════════════════\r\n // WHERE CLAUSES — JSON / STRUCTURED DATA\r\n // ════════════════════════════════════════════════════════\r\n\r\n /**\r\n * JSON/array path contains the given value.\r\n * @example q.whereJsonContains(\"tags\", \"typescript\")\r\n */\r\n public whereJsonContains(path: string, value: unknown): this {\r\n this.addOperation(\"whereJsonContains\", { path, value });\r\n return this;\r\n }\r\n\r\n /** JSON/array path does NOT contain the value. */\r\n public whereJsonDoesntContain(path: string, value: unknown): this {\r\n this.addOperation(\"whereJsonDoesntContain\", { path, value });\r\n return this;\r\n }\r\n\r\n /**\r\n * JSON path key exists.\r\n * Uses a `whereRaw` so existing SQL parsers get `IS NOT NULL` immediately.\r\n */\r\n public whereJsonContainsKey(path: string): this {\r\n this.addOperation(\"whereRaw\", { expression: `${path} IS NOT NULL`, bindings: [] });\r\n return this;\r\n }\r\n\r\n /**\r\n * Constrain the length of a JSON array at a path.\r\n * @example q.whereJsonLength(\"tags\", \">\", 3)\r\n */\r\n public whereJsonLength(path: string, operator: WhereOperator, value: number): this {\r\n this.addOperation(\"whereRaw\", {\r\n expression: `jsonb_array_length(${path}) ${operator} ?`,\r\n bindings: [value],\r\n });\r\n return this;\r\n }\r\n\r\n /** JSON path must resolve to an array. */\r\n public whereJsonIsArray(path: string): this {\r\n this.addOperation(\"whereRaw\", {\r\n expression: `jsonb_typeof(${path}) = 'array'`,\r\n bindings: [],\r\n });\r\n return this;\r\n }\r\n\r\n /** JSON path must resolve to an object. */\r\n public whereJsonIsObject(path: string): this {\r\n this.addOperation(\"whereRaw\", {\r\n expression: `jsonb_typeof(${path}) = 'object'`,\r\n bindings: [],\r\n });\r\n return this;\r\n }\r\n\r\n /**\r\n * Constrain the number of elements in an array field.\r\n * @example q.whereArrayLength(\"roles\", \">=\", 2)\r\n */\r\n public whereArrayLength(field: string, operator: WhereOperator, value: number): this {\r\n this.addOperation(\"whereRaw\", {\r\n expression: `array_length(${field}, 1) ${operator} ?`,\r\n bindings: [value],\r\n });\r\n return this;\r\n }\r\n\r\n // ════════════════════════════════════════════════════════\r\n // WHERE CLAUSES — CONVENIENCE SHORTCUTS\r\n // ════════════════════════════════════════════════════════\r\n\r\n /** WHERE id = value. */\r\n public whereId(value: string | number): this {\r\n return this.where(\"id\", value);\r\n }\r\n\r\n /** WHERE id IN values. */\r\n public whereIds(values: Array<string | number>): this {\r\n return this.whereIn(\"id\", values);\r\n }\r\n\r\n /** WHERE uuid = value. */\r\n public whereUuid(value: string): this {\r\n return this.where(\"uuid\", value);\r\n }\r\n\r\n /** WHERE ulid = value. */\r\n public whereUlid(value: string): this {\r\n return this.where(\"ulid\", value);\r\n }\r\n\r\n /**\r\n * Full-text search across one or more fields.\r\n * @example q.whereFullText([\"title\", \"body\"], \"typescript\")\r\n */\r\n public whereFullText(fields: string | string[], query: string): this {\r\n this.addOperation(\"whereFullText\", {\r\n fields: Array.isArray(fields) ? fields : [fields],\r\n query,\r\n });\r\n return this;\r\n }\r\n\r\n /** Full-text search (OR). */\r\n public orWhereFullText(fields: string | string[], query: string): this {\r\n return this.whereFullText(fields, query);\r\n }\r\n\r\n /** Alias for whereFullText with a single field. */\r\n public whereSearch(field: string, query: string): this {\r\n return this.whereFullText([field], query);\r\n }\r\n\r\n /**\r\n * Text search with optional extra equality filters.\r\n * MongoDB-style convenience shorthand.\r\n */\r\n public textSearch(query: string, filters?: WhereObject): this {\r\n if (filters) {\r\n for (const [key, value] of Object.entries(filters)) this.where(key, value as never);\r\n }\r\n return this;\r\n }\r\n\r\n // ════════════════════════════════════════════════════════\r\n // WHERE CLAUSES — EXISTENCE / SUBQUERIES\r\n // ════════════════════════════════════════════════════════\r\n\r\n /**\r\n * WHERE EXISTS (subquery callback) or field IS NOT NULL (string).\r\n *\r\n * @example\r\n * q.whereExists(sub => sub.where(\"userId\", \"users.id\"))\r\n * q.whereExists(\"optionalField\")\r\n */\r\n public whereExists(field: string): this;\r\n public whereExists(callback: WhereCallback<T>): this;\r\n public whereExists(param: string | WhereCallback<T>): this {\r\n if (typeof param === \"function\") {\r\n const sub = this.subQuery();\r\n param(sub as any);\r\n this.addOperation(\"whereExists\", { subquery: sub.operations });\r\n } else {\r\n this.addOperation(\"whereNotNull\", { field: param });\r\n }\r\n return this;\r\n }\r\n\r\n /**\r\n * WHERE NOT EXISTS (subquery callback) or field IS NULL (string).\r\n */\r\n public whereNotExists(field: string): this;\r\n public whereNotExists(callback: WhereCallback<T>): this;\r\n public whereNotExists(param: string | WhereCallback<T>): this {\r\n if (typeof param === \"function\") {\r\n const sub = this.subQuery();\r\n param(sub as any);\r\n this.addOperation(\"whereNotExists\", { subquery: sub.operations });\r\n } else {\r\n this.addOperation(\"whereNull\", { field: param });\r\n }\r\n return this;\r\n }\r\n\r\n /**\r\n * Constrain an array/collection field by element count.\r\n *\r\n * @example\r\n * q.whereSize(\"tags\", 3) // exactly 3\r\n * q.whereSize(\"tags\", \">=\", 1) // at least 1\r\n */\r\n public whereSize(field: string, size: number): this;\r\n public whereSize(field: string, operator: WhereOperator, size: number): this;\r\n public whereSize(field: string, ...args: unknown[]): this {\r\n const operator = args.length === 2 ? (args[0] as WhereOperator) : \"=\";\r\n const size = (args.length === 2 ? args[1] : args[0]) as number;\r\n return this.whereArrayLength(field, operator, size);\r\n }\r\n\r\n /**\r\n * AND NOT wrapper — negate a nested group.\r\n * @example q.whereNot(q => q.where(\"status\", \"banned\").where(\"role\", \"user\"))\r\n */\r\n public whereNot(callback: WhereCallback<T>): this {\r\n const sub = this.subQuery();\r\n callback(sub as any);\r\n this.addOperation(\"whereNot\", { nested: sub.operations });\r\n return this;\r\n }\r\n\r\n /** OR NOT wrapper. */\r\n public orWhereNot(callback: WhereCallback<T>): this {\r\n const sub = this.subQuery();\r\n callback(sub as any);\r\n this.addOperation(\"orWhereNot\", { nested: sub.operations });\r\n return this;\r\n }\r\n\r\n // ════════════════════════════════════════════════════════\r\n // JOINS — STANDARD SQL-STYLE\r\n // Note: Op type names match parser switch cases exactly.\r\n // join / innerJoin → INNER JOIN\r\n // leftJoin → LEFT JOIN\r\n // rightJoin → RIGHT JOIN\r\n // fullJoin → FULL OUTER JOIN\r\n // crossJoin → CROSS JOIN\r\n // joinRaw → raw expression\r\n // ════════════════════════════════════════════════════════\r\n\r\n /**\r\n * INNER JOIN.\r\n * @example q.join(\"categories\", \"posts.categoryId\", \"categories.id\")\r\n */\r\n public join(table: string, localField: string, foreignField: string): this;\r\n public join(options: JoinOptions): this;\r\n public join(...args: unknown[]): this {\r\n if (args.length === 3) {\r\n this.addOperation(\"join\", { table: args[0], localField: args[1], foreignField: args[2] });\r\n } else {\r\n this.addOperation(\"join\", args[0] as Record<string, unknown>);\r\n }\r\n return this;\r\n }\r\n\r\n /** LEFT JOIN. */\r\n public leftJoin(table: string, localField: string, foreignField: string): this;\r\n public leftJoin(options: JoinOptions): this;\r\n public leftJoin(...args: unknown[]): this {\r\n if (args.length === 3) {\r\n this.addOperation(\"leftJoin\", { table: args[0], localField: args[1], foreignField: args[2] });\r\n } else {\r\n this.addOperation(\"leftJoin\", args[0] as Record<string, unknown>);\r\n }\r\n return this;\r\n }\r\n\r\n /** RIGHT JOIN. */\r\n public rightJoin(table: string, localField: string, foreignField: string): this;\r\n public rightJoin(options: JoinOptions): this;\r\n public rightJoin(...args: unknown[]): this {\r\n if (args.length === 3) {\r\n this.addOperation(\"rightJoin\", {\r\n table: args[0],\r\n localField: args[1],\r\n foreignField: args[2],\r\n });\r\n } else {\r\n this.addOperation(\"rightJoin\", args[0] as Record<string, unknown>);\r\n }\r\n return this;\r\n }\r\n\r\n /** INNER JOIN (alias for join). */\r\n public innerJoin(table: string, localField: string, foreignField: string): this;\r\n public innerJoin(options: JoinOptions): this;\r\n public innerJoin(...args: unknown[]): this {\r\n if (args.length === 3) {\r\n this.addOperation(\"innerJoin\", {\r\n table: args[0],\r\n localField: args[1],\r\n foreignField: args[2],\r\n });\r\n } else {\r\n this.addOperation(\"innerJoin\", args[0] as Record<string, unknown>);\r\n }\r\n return this;\r\n }\r\n\r\n /** FULL OUTER JOIN. */\r\n public fullJoin(table: string, localField: string, foreignField: string): this;\r\n public fullJoin(options: JoinOptions): this;\r\n public fullJoin(...args: unknown[]): this {\r\n if (args.length === 3) {\r\n this.addOperation(\"fullJoin\", { table: args[0], localField: args[1], foreignField: args[2] });\r\n } else {\r\n this.addOperation(\"fullJoin\", args[0] as Record<string, unknown>);\r\n }\r\n return this;\r\n }\r\n\r\n /** CROSS JOIN. */\r\n public crossJoin(table: string): this {\r\n this.addOperation(\"crossJoin\", { table });\r\n return this;\r\n }\r\n\r\n /** Raw JOIN expression. Driver responsible for handling. */\r\n public joinRaw(expression: RawExpression, bindings?: unknown[]): this {\r\n this.addOperation(\"joinRaw\", { expression, bindings: bindings ?? [] });\r\n return this;\r\n }\r\n\r\n // ════════════════════════════════════════════════════════\r\n // RELATION EAGER LOADING — JOIN-BASED (joinWith)\r\n // ════════════════════════════════════════════════════════\r\n\r\n /**\r\n * Eager-load named relations via a single JOIN / $lookup query.\r\n *\r\n * Constraints are eagerly resolved at call time:\r\n * - Callbacks are invoked immediately → `subOps` stored in op data.\r\n * - Column shorthands are parsed into a `columns[]` array.\r\n *\r\n * The driver executor reads the `joinWith` op and uses the resolved data\r\n * alongside its own relation definition map to emit the appropriate SQL JOIN\r\n * or MongoDB $lookup stage.\r\n *\r\n * Supported arg forms (may be mixed):\r\n * - `\"author\"` / `[\"author\", \"category\"]` — no constraint\r\n * - `{ author: \"id,name\" }` — column shorthand\r\n * - `{ actions: q => q.where(\"status\",\"pending\").limit(5) }` — callback\r\n *\r\n * @example\r\n * Post.joinWith(\"author\", \"category\")\r\n * ChatMessage.joinWith({ actions: q => q.where(\"status\", \"pending\").limit(5) })\r\n * ChatMessage.joinWith({ org: \"id,name\", actions: q => q.orderBy(\"sort_order\") })\r\n */\r\n public joinWith(...args: unknown[]): this {\r\n const resolved: Record<string, { columns?: string[]; subOps?: Op[] }> = {};\r\n\r\n for (const arg of args) {\r\n if (typeof arg === \"string\") {\r\n resolved[arg] = {};\r\n } else if (Array.isArray(arg)) {\r\n for (const rel of arg as string[]) resolved[rel] = {};\r\n } else if (typeof arg === \"object\" && arg !== null) {\r\n for (const [rel, constraint] of Object.entries(arg as Record<string, JoinWithConstraint>)) {\r\n if (typeof constraint === \"function\") {\r\n const sub = this.subQuery();\r\n constraint(sub);\r\n resolved[rel] = { subOps: sub.operations };\r\n } else if (typeof constraint === \"string\" && constraint !== \"\") {\r\n resolved[rel] = {\r\n columns: constraint\r\n .split(\",\")\r\n .map((s) => s.trim())\r\n .filter(Boolean),\r\n };\r\n } else {\r\n resolved[rel] = {};\r\n }\r\n }\r\n }\r\n }\r\n\r\n this.addOperation(\"joinWith\", { resolved });\r\n return this;\r\n }\r\n\r\n // ════════════════════════════════════════════════════════\r\n // RELATION EAGER LOADING — SEPARATE QUERIES (with)\r\n // ════════════════════════════════════════════════════════\r\n\r\n /**\r\n * Eager-load relations via separate queries (N+1 avoided by batching).\r\n *\r\n * @example\r\n * q.with(\"posts\")\r\n * q.with(\"posts\", q => q.where(\"published\", true))\r\n * q.with({ posts: true, comments: q => q.limit(5) })\r\n */\r\n public with(\r\n ...args: (string | Record<string, boolean | ((q: any) => void)> | ((q: any) => void))[]\r\n ): this {\r\n for (let i = 0; i < args.length; i++) {\r\n const arg = args[i];\r\n if (typeof arg === \"string\") {\r\n const next = args[i + 1];\r\n if (typeof next === \"function\") {\r\n this.eagerLoadRelations.set(arg, next as (q: any) => void);\r\n i++;\r\n } else {\r\n this.eagerLoadRelations.set(arg, true);\r\n }\r\n } else if (typeof arg === \"object\" && arg !== null) {\r\n for (const [key, value] of Object.entries(\r\n arg as Record<string, boolean | ((q: any) => void)>,\r\n )) {\r\n this.eagerLoadRelations.set(key, value);\r\n }\r\n }\r\n }\r\n return this;\r\n }\r\n\r\n /**\r\n * Register one or more relation counts to emit alongside each result row.\r\n *\r\n * Accepts:\r\n * - Bare relation names (variadic strings or array): `withCount(\"posts\", \"comments\")`\r\n * - Alias shorthand: `withCount(\"posts as totalPosts\")`\r\n * - Object form for per-relation constraints / aliases:\r\n * `withCount({ posts: true, \"posts as approved\": (q) => q.where(\"approved\", true) })`\r\n *\r\n * Each entry is stored in `countRelations` keyed by its output column alias\r\n * (default `${relationName}Count`). The driver subclass consumes the map at\r\n * execute time to emit count expressions.\r\n *\r\n * @example\r\n * ```typescript\r\n * await User.query().withCount(\"posts\").get(); // postsCount\r\n * await User.query().withCount(\"posts as totalPosts\").get(); // totalPosts\r\n * await User.query()\r\n * .withCount({\r\n * posts: true,\r\n * \"posts as published\": (q) => q.where(\"isPublished\", true),\r\n * comments: \"commentTotal\",\r\n * })\r\n * .get();\r\n * ```\r\n */\r\n public withCount(...args: unknown[]): this {\r\n for (const arg of args) {\r\n if (typeof arg === \"string\") {\r\n this.recordCountEntry(arg);\r\n continue;\r\n }\r\n\r\n if (Array.isArray(arg)) {\r\n for (const spec of arg as string[]) {\r\n this.recordCountEntry(spec);\r\n }\r\n continue;\r\n }\r\n\r\n if (typeof arg === \"object\" && arg !== null) {\r\n const entries = Object.entries(\r\n arg as Record<string, true | string | ((query: any) => void)>,\r\n );\r\n\r\n for (const [key, value] of entries) {\r\n if (value === true) {\r\n this.recordCountEntry(key);\r\n } else if (typeof value === \"string\") {\r\n this.recordCountEntry(`${key} as ${value}`);\r\n } else if (typeof value === \"function\") {\r\n this.recordCountEntry(key, value);\r\n }\r\n }\r\n }\r\n }\r\n\r\n return this;\r\n }\r\n\r\n /**\r\n * Parse a count spec (\"relation\" or \"relation as alias\") into its relation\r\n * name and output alias, optionally capturing a constraint callback's\r\n * operations via a sub-builder. Stored in `countRelations` keyed by alias.\r\n */\r\n protected recordCountEntry(spec: string, constraint?: (query: any) => void): void {\r\n const { relation, alias } = this.parseCountSpec(spec);\r\n\r\n let constraintOps: Op[] | undefined;\r\n\r\n if (constraint) {\r\n const sub = this.subQuery();\r\n constraint(sub);\r\n constraintOps = sub.operations;\r\n }\r\n\r\n this.countRelations.set(alias, { relation, constraintOps });\r\n }\r\n\r\n /**\r\n * Split a `\"<relation>\"` or `\"<relation> as <alias>\"` spec. Returns the\r\n * resolved relation name and the output column alias (defaulting to\r\n * `${relation}Count` when no `as` is present).\r\n */\r\n protected parseCountSpec(spec: string): { relation: string; alias: string } {\r\n const trimmed = spec.trim();\r\n const match = /^(.+?)\\s+as\\s+(.+)$/i.exec(trimmed);\r\n\r\n if (!match) {\r\n return { relation: trimmed, alias: `${trimmed}Count` };\r\n }\r\n\r\n const relation = match[1];\n const alias = match[2];\n\n if (relation === undefined || alias === undefined) {\n return { relation: trimmed, alias: `${trimmed}Count` };\n }\n\n return { relation: relation.trim(), alias: alias.trim() };\n }\r\n\r\n /**\r\n * Filter to rows that have at least one related record.\r\n * @example q.has(\"comments\")\r\n * @example q.has(\"comments\", \">=\", 3)\r\n */\r\n public has(relation: string, operator?: WhereOperator, count?: number): this {\r\n this.addOperation(\"has\", { relation, operator: operator ?? \">=\", count: count ?? 1 });\r\n return this;\r\n }\r\n\r\n /**\r\n * Filter to rows with related records matching a sub-query (AND).\r\n * @example q.whereHas(\"comments\", q => q.where(\"approved\", true))\r\n */\r\n public whereHas(relation: string, callback: (q: any) => void): this {\r\n const sub = this.subQuery();\r\n callback(sub);\r\n this.addOperation(\"whereHas\", { relation, subquery: sub.operations });\r\n return this;\r\n }\r\n\r\n /** Same as whereHas but OR-joined. */\r\n public orWhereHas(relation: string, callback: (q: any) => void): this {\r\n const sub = this.subQuery();\r\n callback(sub);\r\n this.addOperation(\"orWhereHas\", { relation, subquery: sub.operations });\r\n return this;\r\n }\r\n\r\n /** Filter to rows with NO related records. */\r\n public doesntHave(relation: string): this {\r\n this.addOperation(\"doesntHave\", { relation });\r\n return this;\r\n }\r\n\r\n /** Filter to rows with NO related records matching conditions. */\r\n public whereDoesntHave(relation: string, callback: (q: any) => void): this {\r\n const sub = this.subQuery();\r\n callback(sub);\r\n this.addOperation(\"whereDoesntHave\", { relation, subquery: sub.operations });\r\n return this;\r\n }\r\n\r\n // ════════════════════════════════════════════════════════\r\n // SELECT / PROJECTION\r\n // ════════════════════════════════════════════════════════\r\n\r\n /**\r\n * Select specific columns.\r\n *\r\n * @example\r\n * q.select([\"id\", \"name\"])\r\n * q.select(\"id\", \"name\")\r\n * q.select({ name: 1, password: 0 }) // MongoDB-style projection\r\n */\r\n public select(fields: string[]): this;\r\n public select(fields: Record<string, 0 | 1 | boolean>): this;\r\n public select(...fields: Array<string | string[]>): this;\r\n public select(...args: unknown[]): this {\r\n if (args.length === 1 && Array.isArray(args[0])) {\r\n this.addOperation(\"select\", { fields: args[0] });\r\n } else if (args.length === 1 && typeof args[0] === \"object\" && !Array.isArray(args[0])) {\r\n this.addOperation(\"select\", { fields: args[0] as Record<string, unknown> });\r\n } else {\r\n this.addOperation(\"select\", { fields: (args as Array<string | string[]>).flat() });\r\n }\r\n return this;\r\n }\r\n\r\n /** Select a field under an alias. @example q.selectAs(\"fullName\", \"name\") */\r\n public selectAs(field: string, alias: string): this {\r\n this.addOperation(\"select\", { fields: { [field]: alias } });\r\n return this;\r\n }\r\n\r\n /**\r\n * Raw SELECT expression.\r\n * @example q.selectRaw(\"COUNT(*) AS total\")\r\n */\r\n public selectRaw(expression: RawExpression, bindings?: unknown[]): this {\r\n this.addOperation(\"selectRaw\", { expression, bindings: bindings ?? [] });\r\n return this;\r\n }\r\n\r\n /** Multiple raw SELECT expressions in one call. */\r\n public selectRawMany(\r\n definitions: Array<{ alias: string; expression: RawExpression; bindings?: unknown[] }>,\r\n ): this {\r\n for (const def of definitions) {\r\n this.selectRaw({ [def.alias]: def.expression }, def.bindings);\r\n }\r\n return this;\r\n }\r\n\r\n /** Subquery as a named projected field. */\r\n public selectSub(expression: RawExpression, alias: string): this {\r\n this.addOperation(\"selectRaw\", { expression: { [alias]: expression } });\r\n return this;\r\n }\r\n\r\n /** Alias for selectSub. */\r\n public addSelectSub(expression: RawExpression, alias: string): this {\r\n return this.selectSub(expression, alias);\r\n }\r\n\r\n /**\r\n * Aggregate function as a projected field.\r\n * @example q.selectAggregate(\"price\", \"sum\", \"totalRevenue\")\r\n */\r\n public selectAggregate(\r\n field: string,\r\n aggregate: \"sum\" | \"avg\" | \"min\" | \"max\" | \"count\" | \"first\" | \"last\",\r\n alias: string,\r\n ): this {\r\n return this.selectRaw({ [alias]: `${aggregate.toUpperCase()}(${field})` });\r\n }\r\n\r\n /** Existence check as a projected boolean field. */\r\n public selectExists(field: string, alias: string): this {\r\n return this.selectRaw({ [alias]: `${field} IS NOT NULL` });\r\n }\r\n\r\n /** COUNT as a projected field. */\r\n public selectCount(field: string, alias: string): this {\r\n return this.selectAggregate(field, \"count\", alias);\r\n }\r\n\r\n /**\r\n * CASE / switch expression.\r\n * @example q.selectCase([{ when: \"status = 1\", then: \"'active'\" }], \"'inactive'\", \"statusLabel\")\r\n */\r\n public selectCase(\r\n cases: Array<{ when: RawExpression; then: RawExpression | unknown }>,\r\n otherwise: RawExpression | unknown,\r\n alias: string,\r\n ): this {\r\n const caseExpr = cases.map((c) => `WHEN ${c.when} THEN ${c.then}`).join(\" \");\r\n return this.selectRaw({ [alias]: `CASE ${caseExpr} ELSE ${otherwise} END` });\r\n }\r\n\r\n /** IF/ELSE conditional field. */\r\n public selectWhen(\r\n condition: RawExpression,\r\n thenValue: RawExpression | unknown,\r\n elseValue: RawExpression | unknown,\r\n alias: string,\r\n ): this {\r\n return this.selectRaw({\r\n [alias]: `CASE WHEN ${condition} THEN ${thenValue} ELSE ${elseValue} END`,\r\n });\r\n }\r\n\r\n /**\r\n * Driver-native projection manipulation.\r\n * No-op in base — override in driver subclasses.\r\n */\r\n public selectDriverProjection(_callback: (projection: Record<string, unknown>) => void): this {\r\n return this;\r\n }\r\n\r\n /** JSON path extraction as a projected field. */\r\n public selectJson(path: string, alias?: string): this {\r\n const parts = path.split(\"->\");\r\n const column = parts[0] ?? \"\";\n const jsonPath = parts.slice(1).join(\"->\");\r\n const expr = jsonPath ? `${column}->>'${jsonPath}'` : column;\r\n return alias ? this.selectAs(expr, alias) : this.selectRaw(expr);\r\n }\r\n\r\n /** JSON extraction via raw expression. */\r\n public selectJsonRaw(_path: string, expression: RawExpression, alias: string): this {\r\n return this.selectRaw({ [alias]: expression });\r\n }\r\n\r\n /** Exclude a JSON path from projection. */\r\n public deselectJson(path: string): this {\r\n return this.deselect([path]);\r\n }\r\n\r\n /** String concatenation as a projected field. */\r\n public selectConcat(fields: Array<string | RawExpression>, alias: string): this {\r\n return this.selectRaw({ [alias]: fields.join(\" || \") });\r\n }\r\n\r\n /** COALESCE (first non-null) as a projected field. */\r\n public selectCoalesce(fields: Array<string | RawExpression>, alias: string): this {\r\n return this.selectRaw({ [alias]: `COALESCE(${fields.join(\", \")})` });\r\n }\r\n\r\n /** Window function expression. */\r\n public selectWindow(spec: RawExpression): this {\r\n this.addOperation(\"selectRaw\", { expression: spec });\r\n return this;\r\n }\r\n\r\n /** Exclude specific columns from results. */\r\n public deselect(fields: string[]): this {\r\n this.addOperation(\"deselect\", { fields });\r\n return this;\r\n }\r\n\r\n /**\r\n * Remove all select operations (resets to wildcard).\r\n * Uses `rebuildIndex()` — no unsafe casts.\r\n */\r\n public clearSelect(): this {\r\n this.operations = this.operations.filter(\r\n (op) => !op.type.startsWith(\"select\") && op.type !== \"deselect\",\r\n );\r\n this.rebuildIndex();\r\n return this;\r\n }\r\n\r\n /** Alias for clearSelect. */\r\n public selectAll(): this {\r\n return this.clearSelect();\r\n }\r\n\r\n /** Alias for clearSelect. */\r\n public selectDefault(): this {\r\n return this.clearSelect();\r\n }\r\n\r\n /** Append additional fields to existing selection. */\r\n public addSelect(fields: string[]): this {\r\n this.addOperation(\"select\", { fields, add: true });\r\n return this;\r\n }\r\n\r\n /**\r\n * Record a DISTINCT flag (fluent — does not execute).\r\n * Subclasses expose a separate async `distinct(field)` execution method.\r\n */\r\n public distinctValues(fields?: string | string[]): this {\r\n const fieldList = fields ? (Array.isArray(fields) ? fields : [fields]) : [];\r\n this.addOperation(\"distinct\", { fields: fieldList });\r\n return this;\r\n }\r\n\r\n // ════════════════════════════════════════════════════════\r\n // ORDERING\r\n // ════════════════════════════════════════════════════════\r\n\r\n /**\r\n * ORDER BY a column.\r\n *\r\n * @example\r\n * q.orderBy(\"createdAt\", \"desc\")\r\n * q.orderBy({ name: \"asc\", age: \"desc\" })\r\n */\r\n public orderBy(field: string, direction?: OrderDirection): this;\r\n public orderBy(fields: Record<string, OrderDirection>): this;\r\n public orderBy(...args: unknown[]): this {\r\n if (typeof args[0] === \"string\") {\r\n this.addOperation(\"orderBy\", {\r\n field: args[0],\r\n direction: (args[1] as OrderDirection) ?? \"asc\",\r\n });\r\n } else {\r\n for (const [field, direction] of Object.entries(args[0] as Record<string, OrderDirection>)) {\r\n this.addOperation(\"orderBy\", { field, direction });\r\n }\r\n }\r\n return this;\r\n }\r\n\r\n /** ORDER BY descending shorthand. */\r\n public orderByDesc(field: string): this {\r\n return this.orderBy(field, \"desc\");\r\n }\r\n\r\n /**\r\n * Raw ORDER BY expression.\r\n * @example q.orderByRaw(\"RANDOM()\")\r\n * @example q.orderByRaw({ $meta: \"textScore\" })\r\n */\r\n public orderByRaw(expression: RawExpression, bindings?: unknown[]): this {\r\n this.addOperation(\"orderByRaw\", { expression, bindings: bindings ?? [] });\r\n return this;\r\n }\r\n\r\n /**\r\n * Random order. Maps to `RANDOM()` in SQL or `$sample` in MongoDB.\r\n * @param limit - Optional limit (required for MongoDB $sample)\r\n */\r\n public orderByRandom(limit?: number): this {\r\n this.addOperation(\"orderByRaw\", { expression: \"RANDOM()\" });\r\n if (limit !== undefined) this.limit(limit);\r\n return this;\r\n }\r\n\r\n /** Order ascending by a date column (oldest first). */\r\n public oldest(column = \"createdAt\"): this {\r\n return this.orderBy(column, \"asc\");\r\n }\r\n\r\n // ════════════════════════════════════════════════════════\r\n // LIMIT / OFFSET\r\n // ════════════════════════════════════════════════════════\r\n\r\n /** Limit number of results. */\r\n public limit(value: number): this {\r\n this.addOperation(\"limit\", { value });\r\n return this;\r\n }\r\n\r\n /** Skip N results (OFFSET). */\r\n public skip(value: number): this {\r\n this.addOperation(\"offset\", { value });\r\n return this;\r\n }\r\n\r\n /** Alias for skip. */\r\n public offset(value: number): this {\r\n return this.skip(value);\r\n }\r\n\r\n /** Alias for limit. */\r\n public take(value: number): this {\r\n return this.limit(value);\r\n }\r\n\r\n // ════════════════════════════════════════════════════════\r\n // ROW LOCKING\r\n // ════════════════════════════════════════════════════════\r\n\r\n /**\r\n * Lock the selected rows for update (`SELECT ... FOR UPDATE`).\r\n *\r\n * `skipLocked` skips rows other transactions hold locks on (concurrent\r\n * queue-claim shape); `noWait` errors immediately instead of waiting. The\r\n * two are mutually exclusive. Only meaningful inside a transaction.\r\n *\r\n * SQL drivers emit the locking clause; drivers without row locking\r\n * (MongoDB) override this to throw.\r\n */\r\n public lockForUpdate(options?: LockForUpdateOptions): this {\r\n if (options?.skipLocked && options?.noWait) {\r\n throw new Error(\"lockForUpdate: `skipLocked` and `noWait` are mutually exclusive.\");\r\n }\r\n\r\n this.addOperation(\"lock\", {\r\n mode: \"update\",\r\n skipLocked: options?.skipLocked ?? false,\r\n noWait: options?.noWait ?? false,\r\n });\r\n return this;\r\n }\r\n\r\n // ════════════════════════════════════════════════════════\r\n // GROUPING / AGGREGATION\r\n // ════════════════════════════════════════════════════════\r\n\r\n /**\r\n * GROUP BY clause.\r\n * @example q.groupBy(\"status\")\r\n * @example q.groupBy([\"year\", \"month\"])\r\n */\r\n public groupBy(input: GroupByInput): this {\r\n const fields = Array.isArray(input) ? input : [input];\r\n this.addOperation(\"groupBy\", { fields });\r\n return this;\r\n }\r\n\r\n /** Raw GROUP BY expression. */\r\n public groupByRaw(expression: RawExpression, bindings?: unknown[]): this {\r\n this.addOperation(\"groupBy\", { expression, bindings: bindings ?? [] });\r\n return this;\r\n }\r\n\r\n /**\r\n * HAVING clause (post-group filter).\r\n *\r\n * @example\r\n * q.having(\"total\", \">\", 100)\r\n * q.having([\"total\", \">\", 100])\r\n * q.having({ total: 100 })\r\n */\r\n public having(field: string, value: unknown): this;\r\n public having(field: string, operator: WhereOperator, value: unknown): this;\r\n public having(condition: HavingInput): this;\r\n public having(...args: unknown[]): this {\r\n if (args.length === 1) {\r\n const input = args[0] as HavingInput;\r\n if (Array.isArray(input)) {\r\n if (input.length === 2) {\r\n this.addOperation(\"having\", { field: input[0], operator: \"=\", value: input[1] });\r\n } else {\r\n this.addOperation(\"having\", { field: input[0], operator: input[1], value: input[2] });\r\n }\r\n } else {\r\n for (const [key, value] of Object.entries(input as Record<string, unknown>)) {\r\n this.addOperation(\"having\", { field: key, operator: \"=\", value });\r\n }\r\n }\r\n } else if (args.length === 2) {\r\n this.addOperation(\"having\", { field: args[0], operator: \"=\", value: args[1] });\r\n } else {\r\n this.addOperation(\"having\", { field: args[0], operator: args[1], value: args[2] });\r\n }\r\n return this;\r\n }\r\n\r\n /** Raw HAVING expression. */\r\n public havingRaw(expression: RawExpression, bindings?: unknown[]): this {\r\n this.addOperation(\"havingRaw\", { expression, bindings: bindings ?? [] });\r\n return this;\r\n }\r\n\r\n // ════════════════════════════════════════════════════════\r\n // UTILITY / CONTROL FLOW\r\n // ════════════════════════════════════════════════════════\r\n\r\n /**\r\n * Side-effect tap — executes callback synchronously and returns `this`.\r\n * @example q.where(...).tap(q => console.log(q.operations.length)).limit(10)\r\n */\r\n public tap(callback: (builder: this) => void): this {\r\n callback(this);\r\n return this;\r\n }\r\n\r\n /**\r\n * Conditionally apply query modifications.\r\n *\r\n * @example\r\n * q.when(userId, (q, id) => q.where(\"userId\", id))\r\n * q.when(isAdmin, q => q.withoutGlobalScopes(), q => q.scope(\"active\"))\r\n */\r\n public when<V>(\r\n condition: V | boolean,\r\n callback: (builder: this, value: V) => void,\r\n otherwise?: (builder: this) => void,\r\n ): this {\r\n if (condition) {\r\n callback(this, condition as V);\r\n } else if (otherwise) {\r\n otherwise(this);\r\n }\r\n return this;\r\n }\r\n}\r\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"query-builder.mjs","names":[],"sources":["../../../../../../../cascade/src/query-builder/query-builder.ts"],"sourcesContent":["/**\r\n * Pure Query Builder Base Class\r\n *\r\n * Driver-agnostic operation recorder. All fluent methods push typed entries into\r\n * `operations[]`. No SQL, no driver references, no table property, no execution.\r\n *\r\n * ┌─────────────────────────────────────────────────┐\r\n * │ Usage contexts │\r\n * │ (a) Subclassed — PG / Mongo / MySQL / … │\r\n * │ (b) Instantiated directly (new QueryBuilder()) │\r\n * │ inside callbacks for: │\r\n * │ • nested where groups │\r\n * │ • joinWith constraints │\r\n * │ • whereExists / whereHas subqueries │\r\n * └─────────────────────────────────────────────────┘\r\n *\r\n * Design rules:\r\n * - `table` / alias are NOT here — the parser gets them from the executor.\r\n * - `opIndex` is protected so subclasses can rebuild after direct mutation.\r\n * - Op type names are stable — parsers switch on them; no renaming without\r\n * a parser update.\r\n * - OR-variants keep distinct op types (orWhere, orWhereColumn, …) so existing\r\n * parsers that switch on type need no changes.\r\n * - `joinWith` eagerly resolves callbacks → subOps at record time so the\r\n * driver executor receives a plain data structure, not a live function.\r\n *\r\n * @module cascade/query-builder\r\n */\r\n\r\nimport type {\r\n GroupByInput,\r\n HavingInput,\r\n JoinOptions,\r\n LeanDocument,\r\n LockForUpdateOptions,\r\n OrderDirection,\r\n RawExpression,\r\n WhereCallback,\r\n WhereObject,\r\n WhereOperator,\r\n} from \"../contracts/query-builder.contract\";\r\nimport { sanitizeFilter, sanitizeFilterValue } from \"../utils/sanitize-filter\";\r\n\r\n// ============================================================================\r\n// TYPES\r\n// ============================================================================\r\n\r\n/**\r\n * A single recorded query operation.\r\n * `type` is the discriminator; `data` carries all parameters.\r\n */\r\nexport type Op = {\r\n readonly type: string;\r\n readonly data: Record<string, unknown>;\r\n};\r\n\r\n/**\r\n * Constraint value accepted by `joinWith()`.\r\n *\r\n * - `string` → comma-separated column shorthand: `\"id,name,createdAt\"`\r\n * - `fn` → callback receives a bare QueryBuilder to record sub-ops\r\n *\r\n * @example\r\n * joinWith({ actions: \"id,status\" })\r\n * joinWith({ actions: q => q.where(\"status\", \"pending\").limit(5) })\r\n */\r\nexport type JoinWithConstraint = string | ((q: QueryBuilder) => void);\r\n\r\n// ============================================================================\r\n// QUERY BUILDER — CONCRETE, DIRECTLY INSTANTIABLE\r\n// ============================================================================\r\n\r\n/**\r\n * Pure, driver-agnostic query builder.\r\n *\r\n * Records operations in `operations[]`. Subclasses own execution, parsing, and\r\n * driver-specific clause generation. Safe to instantiate directly inside\r\n * callbacks where only operation recording is needed.\r\n *\r\n * @example\r\n * ```ts\r\n * // Driver subclass usage:\r\n * const users = await User.query()\r\n * .select([\"id\", \"name\"])\r\n * .where(\"status\", \"active\")\r\n * .where(q => q.where(\"role\", \"admin\").orWhere(\"role\", \"mod\"))\r\n * .orderBy(\"createdAt\", \"desc\")\r\n * .limit(10)\r\n * .get();\r\n *\r\n * // Direct instantiation (callback context — no driver needed):\r\n * joinWith({ actions: q => q.where(\"status\", \"pending\").limit(5) });\r\n * // The sub-QB's operations[] are captured and stored in the joinWith op data.\r\n * ```\r\n */\r\nexport class QueryBuilder<T = unknown> {\r\n // ════════════════════════════════════════════════════════\r\n // OPERATION STORE\r\n // ════════════════════════════════════════════════════════\r\n\r\n /** Flat, ordered list of recorded operations. Public for parser access. */\r\n public operations: Op[] = [];\r\n\r\n /**\r\n * type → ordered list of indices into `operations[]`.\r\n *\r\n * Protected (not private) so:\r\n * - `rebuildIndex()` can reset it after direct `operations[]` mutation.\r\n * - Subclasses can inspect it without unsafe casts.\r\n *\r\n * External consumers should use `getOps(type)` instead.\r\n */\r\n protected opIndex: Map<string, number[]> = new Map();\r\n\r\n // ════════════════════════════════════════════════════════\r\n // SCOPE STATE (injected by Model.query(), consumed before execution)\r\n // ════════════════════════════════════════════════════════\r\n\r\n /** Global scope definitions injected by Model.query(). Keyed by scope name. */\r\n public pendingGlobalScopes?: Map<string, any>;\r\n /** Local scope callbacks injected by Model.query(). Applied on demand via scope(). */\r\n public availableLocalScopes?: Map<string, (...args: any[]) => void>;\r\n /** Names of global scopes that have been intentionally disabled. */\r\n public disabledGlobalScopes: Set<string> = new Set();\r\n /** True once the driver subclass has applied pending scopes. */\r\n public scopesApplied = false;\r\n\r\n // ════════════════════════════════════════════════════════\r\n // RELATION STATE (consumed by driver subclass at execute time)\r\n // ════════════════════════════════════════════════════════\r\n\r\n /** Relations to eager-load via separate queries. */\r\n public eagerLoadRelations: Map<string, boolean | ((query: any) => void)> = new Map();\r\n /** Count expressions to emit per result row, keyed by output column alias. */\r\n public countRelations: Map<string, { relation: string; constraintOps?: Op[] }> = new Map();\r\n /** Relation definition map injected from the owning Model. */\r\n public relationDefinitions?: Record<string, any>;\r\n /** The Model class reference, required for relation resolution. */\r\n public modelClass?: any;\r\n\r\n // ════════════════════════════════════════════════════════\r\n // READ MODE\r\n // ════════════════════════════════════════════════════════\r\n\r\n /** True once `lean()` was called — execution skips Model hydration. */\r\n public isLean = false;\r\n\r\n // ════════════════════════════════════════════════════════\r\n // CORE INTERNALS\r\n // ════════════════════════════════════════════════════════\r\n\r\n /**\r\n * Append an operation to `operations[]` and update `opIndex`.\r\n * Every fluent method calls this.\r\n */\r\n protected addOperation(type: string, data: Record<string, unknown>): void {\r\n const idx = this.operations.length;\r\n this.operations.push({ type, data });\r\n const list = this.opIndex.get(type);\r\n if (list) {\r\n list.push(idx);\r\n } else {\r\n this.opIndex.set(type, [idx]);\r\n }\r\n }\r\n\r\n /**\r\n * Return all recorded operations of the specified types in original\r\n * insertion order.\r\n *\r\n * @example\r\n * builder.getOps(\"where\", \"orWhere\", \"whereIn\")\r\n */\r\n public getOps(...types: string[]): Op[] {\r\n if (types.length === 1) {\r\n const type = types[0];\r\n if (type === undefined) {\r\n return [];\r\n }\r\n\r\n return (this.opIndex.get(type) ?? []).flatMap((index) => {\r\n const operation = this.operations[index];\r\n return operation === undefined ? [] : [operation];\r\n });\r\n }\r\n const result: Array<{ idx: number; op: Op }> = [];\r\n for (const type of types) {\r\n for (const idx of this.opIndex.get(type) ?? []) {\r\n const operation = this.operations[idx];\r\n if (operation !== undefined) {\r\n result.push({ idx, op: operation });\r\n }\r\n }\r\n }\r\n return result.sort((a, b) => a.idx - b.idx).map((r) => r.op);\r\n }\r\n\r\n /**\r\n * Rebuild `opIndex` from scratch.\r\n *\r\n * Call this after any direct mutation of `this.operations[]` (e.g. scope\r\n * injection, joinWith consumption in the executor, clone post-processing).\r\n */\r\n public rebuildIndex(): void {\r\n this.opIndex = new Map();\r\n for (let i = 0; i < this.operations.length; i++) {\r\n const operation = this.operations[i];\r\n if (operation === undefined) {\r\n continue;\r\n }\r\n\r\n const type = operation.type;\r\n const list = this.opIndex.get(type);\r\n if (list) {\r\n list.push(i);\r\n } else {\r\n this.opIndex.set(type, [i]);\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * Factory for sub-QueryBuilders used inside callbacks.\r\n *\r\n * Override in driver subclasses to return a driver-typed instance, so that\r\n * driver-specific methods (e.g. `whereArrayContains`) are available inside\r\n * nested `where(q => ...)` / `whereHas` / `joinWith` callbacks.\r\n *\r\n * @example\r\n * // In PostgresQueryBuilder:\r\n * protected override subQuery(): QueryBuilder {\r\n * return new PostgresQueryBuilder(\"__sub__\", this.dataSource);\r\n * }\r\n */\r\n protected subQuery(): QueryBuilder {\r\n return new QueryBuilder();\r\n }\r\n\r\n /**\r\n * Shallow-clone this builder — copies operations, opIndex, and all shared state.\r\n *\r\n * Subclasses MUST call `super.clone()` and then copy their own fields\r\n * (dataSource, joinRelations, …).\r\n */\r\n public clone(): this {\r\n const cloned = Object.create(Object.getPrototypeOf(this)) as this;\r\n cloned.operations = [...this.operations];\r\n cloned.opIndex = new Map(Array.from(this.opIndex.entries()).map(([k, v]) => [k, [...v]]));\r\n cloned.pendingGlobalScopes = this.pendingGlobalScopes;\r\n cloned.availableLocalScopes = this.availableLocalScopes;\r\n cloned.disabledGlobalScopes = new Set(this.disabledGlobalScopes);\r\n cloned.scopesApplied = this.scopesApplied;\r\n cloned.eagerLoadRelations = new Map(this.eagerLoadRelations);\r\n cloned.countRelations = new Map(this.countRelations);\r\n cloned.relationDefinitions = this.relationDefinitions;\r\n cloned.modelClass = this.modelClass;\r\n cloned.isLean = this.isLean;\r\n return cloned;\r\n }\r\n\r\n /**\r\n * Switch to lean read mode: execution returns the plain driver rows — no\r\n * Model hydration, no driver deserialization, no hydrating/fetched\r\n * callbacks. The model's `static hidden` fields are still stripped.\r\n * Eager-loading relations in lean mode throws `UnsupportedLeanOperationError`.\r\n *\r\n * @example\r\n * const rows = await User.query().where(\"isActive\", true).lean().get();\r\n */\r\n public lean(): QueryBuilder<LeanDocument<T>> {\r\n this.isLean = true;\r\n return this as unknown as QueryBuilder<LeanDocument<T>>;\r\n }\r\n\r\n // ════════════════════════════════════════════════════════\r\n // SCOPES\r\n // ════════════════════════════════════════════════════════\r\n\r\n /** Disable one or more named global scopes for this query. */\r\n public withoutGlobalScope(...scopeNames: string[]): this {\r\n scopeNames.forEach((name) => this.disabledGlobalScopes.add(name));\r\n return this;\r\n }\r\n\r\n /** Disable ALL pending global scopes for this query. */\r\n public withoutGlobalScopes(): this {\r\n this.pendingGlobalScopes?.forEach((_, name) => this.disabledGlobalScopes.add(name));\r\n return this;\r\n }\r\n\r\n /**\r\n * Apply a registered local scope by name.\r\n * @throws if no local scopes are available or the named scope is not found\r\n */\r\n public scope(scopeName: string, ...args: unknown[]): this {\r\n if (!this.availableLocalScopes) {\r\n throw new Error(\"No local scopes available on this query builder.\");\r\n }\r\n const cb = this.availableLocalScopes.get(scopeName);\r\n if (!cb) throw new Error(`Local scope \"${scopeName}\" not found.`);\r\n cb(this, ...args);\r\n return this;\r\n }\r\n\r\n // ════════════════════════════════════════════════════════\r\n // WHERE CLAUSES — CORE\r\n // ════════════════════════════════════════════════════════\r\n\r\n /**\r\n * Add a WHERE clause (AND).\r\n *\r\n * @example\r\n * q.where(\"status\", \"active\")\r\n * q.where(\"age\", \">\", 18)\r\n * q.where({ role: \"admin\", active: true })\r\n * q.where(q => q.where(\"a\", 1).orWhere(\"b\", 2))\r\n */\r\n public where(field: string, value: unknown): this;\r\n public where(field: string, operator: WhereOperator, value: unknown): this;\r\n public where(conditions: WhereObject): this;\r\n public where(callback: WhereCallback<T>): this;\r\n public where(...args: unknown[]): this {\r\n if (args.length === 1 && typeof args[0] === \"function\") {\r\n const sub = this.subQuery();\r\n (args[0] as (q: QueryBuilder) => void)(sub);\r\n this.addOperation(\"where\", { nested: sub.operations });\r\n } else if (args.length === 1 && typeof args[0] === \"object\" && args[0] !== null) {\r\n for (const [key, value] of Object.entries(sanitizeFilter(args[0] as WhereObject))) {\r\n this.addOperation(\"where\", { field: key, operator: \"=\", value });\r\n }\r\n } else if (args.length === 2) {\r\n this.addOperation(\"where\", {\r\n field: args[0],\r\n operator: \"=\",\r\n value: sanitizeFilterValue(args[1], String(args[0])),\r\n });\r\n } else {\r\n // \"=\" is still an equality position — sanitize like the 2-arg form.\r\n this.addOperation(\"where\", {\r\n field: args[0],\r\n operator: args[1],\r\n value: args[1] === \"=\" ? sanitizeFilterValue(args[2], String(args[0])) : args[2],\r\n });\r\n }\r\n return this;\r\n }\r\n\r\n /**\r\n * Add an OR WHERE clause.\r\n *\r\n * @example\r\n * q.where(\"role\", \"admin\").orWhere(\"role\", \"mod\")\r\n */\r\n public orWhere(field: string, value: unknown): this;\r\n public orWhere(field: string, operator: WhereOperator, value: unknown): this;\r\n public orWhere(conditions: WhereObject): this;\r\n public orWhere(callback: WhereCallback<T>): this;\r\n public orWhere(...args: unknown[]): this {\r\n if (args.length === 1 && typeof args[0] === \"function\") {\r\n const sub = this.subQuery();\r\n (args[0] as (q: QueryBuilder) => void)(sub);\r\n this.addOperation(\"orWhere\", { nested: sub.operations });\r\n } else if (args.length === 1 && typeof args[0] === \"object\" && args[0] !== null) {\r\n for (const [key, value] of Object.entries(sanitizeFilter(args[0] as WhereObject))) {\r\n this.addOperation(\"orWhere\", { field: key, operator: \"=\", value });\r\n }\r\n } else if (args.length === 2) {\r\n this.addOperation(\"orWhere\", {\r\n field: args[0],\r\n operator: \"=\",\r\n value: sanitizeFilterValue(args[1], String(args[0])),\r\n });\r\n } else {\r\n // \"=\" is still an equality position — sanitize like the 2-arg form.\r\n this.addOperation(\"orWhere\", {\r\n field: args[0],\r\n operator: args[1],\r\n value: args[1] === \"=\" ? sanitizeFilterValue(args[2], String(args[0])) : args[2],\r\n });\r\n }\r\n return this;\r\n }\r\n\r\n /**\r\n * Raw WHERE expression in the target dialect (AND).\r\n *\r\n * @example\r\n * q.whereRaw(\"age > ? AND role = ?\", [18, \"admin\"]) // SQL\r\n * q.whereRaw({ $expr: { $gt: [\"$stock\", \"$reserved\"] } }) // MongoDB\r\n */\r\n public whereRaw(expression: RawExpression, bindings?: unknown[]): this {\r\n this.addOperation(\"whereRaw\", { expression, bindings: bindings ?? [] });\r\n return this;\r\n }\r\n\r\n /** Raw OR WHERE expression. */\r\n public orWhereRaw(expression: RawExpression, bindings?: unknown[]): this {\r\n this.addOperation(\"orWhereRaw\", { expression, bindings: bindings ?? [] });\r\n return this;\r\n }\r\n\r\n // ════════════════════════════════════════════════════════\r\n // WHERE CLAUSES — COLUMN COMPARISONS\r\n // ════════════════════════════════════════════════════════\r\n\r\n /**\r\n * Compare two columns directly (AND).\r\n * @example q.whereColumn(\"stock\", \">\", \"reserved\")\r\n */\r\n public whereColumn(first: string, operator: WhereOperator, second: string): this {\r\n this.addOperation(\"whereColumn\", { first, operator, second });\r\n return this;\r\n }\r\n\r\n /** Compare two columns directly (OR). */\r\n public orWhereColumn(first: string, operator: WhereOperator, second: string): this {\r\n this.addOperation(\"orWhereColumn\", { first, operator, second });\r\n return this;\r\n }\r\n\r\n /** Compare multiple column pairs in one call. */\r\n public whereColumns(\r\n comparisons: Array<[left: string, operator: WhereOperator, right: string]>,\r\n ): this {\r\n for (const [left, operator, right] of comparisons) {\r\n this.whereColumn(left, operator, right);\r\n }\r\n return this;\r\n }\r\n\r\n /**\r\n * Field value must fall between two other column values.\r\n * Stored as a `whereBetween` op with `useColumns: true` so the SQL parser\r\n * knows to quote the values as identifiers rather than bind them.\r\n */\r\n public whereBetweenColumns(field: string, lowerColumn: string, upperColumn: string): this {\r\n this.addOperation(\"whereBetween\", { field, lowerColumn, upperColumn, useColumns: true });\r\n return this;\r\n }\r\n\r\n // ════════════════════════════════════════════════════════\r\n // WHERE CLAUSES — STANDARD COMPARISON OPERATORS\r\n // ════════════════════════════════════════════════════════\r\n\r\n /** WHERE field IN values. */\r\n public whereIn(field: string, values: unknown[]): this {\r\n this.addOperation(\"whereIn\", { field, values });\r\n return this;\r\n }\r\n\r\n /** WHERE field NOT IN values. */\r\n public whereNotIn(field: string, values: unknown[]): this {\r\n this.addOperation(\"whereNotIn\", { field, values });\r\n return this;\r\n }\r\n\r\n /** WHERE field IS NULL. */\r\n public whereNull(field: string): this {\r\n this.addOperation(\"whereNull\", { field });\r\n return this;\r\n }\r\n\r\n /** WHERE field IS NOT NULL. */\r\n public whereNotNull(field: string): this {\r\n this.addOperation(\"whereNotNull\", { field });\r\n return this;\r\n }\r\n\r\n /** WHERE field BETWEEN low AND high. */\r\n public whereBetween(field: string, range: [unknown, unknown]): this {\r\n this.addOperation(\"whereBetween\", { field, range });\r\n return this;\r\n }\r\n\r\n /** WHERE field NOT BETWEEN low AND high. */\r\n public whereNotBetween(field: string, range: [unknown, unknown]): this {\r\n this.addOperation(\"whereNotBetween\", { field, range });\r\n return this;\r\n }\r\n\r\n // ════════════════════════════════════════════════════════\r\n // WHERE CLAUSES — PATTERN MATCHING\r\n // ════════════════════════════════════════════════════════\r\n\r\n /**\r\n * LIKE pattern match (AND).\r\n *\r\n * A string is a LIKE pattern (`%` wildcard) and is matched literally\r\n * otherwise — regex metacharacters in it are escaped by the driver, so\r\n * search input cannot alter the query. Pass an explicit `RegExp` to opt into\r\n * raw pattern semantics; never build that `RegExp` from user input.\r\n *\r\n * @example q.whereLike(\"email\", \"%@gmail.com\")\r\n */\r\n public whereLike(field: string, pattern: RegExp | string): this {\r\n this.addOperation(\"whereLike\", { field, ...this.likePatternData(pattern) });\r\n return this;\r\n }\r\n\r\n /** NOT LIKE pattern match. @see whereLike for the escaping rules. */\r\n public whereNotLike(field: string, pattern: RegExp | string): this {\r\n this.addOperation(\"whereNotLike\", { field, ...this.likePatternData(pattern) });\r\n return this;\r\n }\r\n\r\n /**\r\n * Flatten a LIKE argument into operation data, keeping the distinction the\r\n * drivers need: an explicit `RegExp` (developer-authored) stays a pattern,\r\n * a string (potentially request input) is a literal.\r\n */\r\n private likePatternData(pattern: RegExp | string): {\r\n pattern: string;\r\n isRegExp?: true;\r\n } {\r\n return pattern instanceof RegExp\r\n ? { pattern: pattern.source, isRegExp: true }\r\n : { pattern };\r\n }\r\n\r\n /** Starts with a prefix. */\r\n public whereStartsWith(field: string, value: string | number): this {\r\n return this.whereLike(field, `${value}%`);\r\n }\r\n\r\n /** Does NOT start with a prefix. */\r\n public whereNotStartsWith(field: string, value: string | number): this {\r\n return this.whereNotLike(field, `${value}%`);\r\n }\r\n\r\n /** Ends with a suffix. */\r\n public whereEndsWith(field: string, value: string | number): this {\r\n return this.whereLike(field, `%${value}`);\r\n }\r\n\r\n /** Does NOT end with a suffix. */\r\n public whereNotEndsWith(field: string, value: string | number): this {\r\n return this.whereNotLike(field, `%${value}`);\r\n }\r\n\r\n // ════════════════════════════════════════════════════════\r\n // WHERE CLAUSES — DATE/TIME PARTIALS\r\n // ════════════════════════════════════════════════════════\r\n\r\n /**\r\n * Match on date portion only (time ignored).\r\n * @example q.whereDate(\"createdAt\", \"2024-05-01\")\r\n */\r\n public whereDate(field: string, value: Date | string): this {\r\n this.addOperation(\"whereDate\", { field, value });\r\n return this;\r\n }\r\n\r\n /** Alias for whereDate. */\r\n public whereDateEquals(field: string, value: Date | string): this {\r\n return this.whereDate(field, value);\r\n }\r\n\r\n /** Field date is before value. */\r\n public whereDateBefore(field: string, value: Date | string): this {\r\n this.addOperation(\"whereDateBefore\", { field, value });\r\n return this;\r\n }\r\n\r\n /** Field date is after value. */\r\n public whereDateAfter(field: string, value: Date | string): this {\r\n this.addOperation(\"whereDateAfter\", { field, value });\r\n return this;\r\n }\r\n\r\n /** Field date is within a range [from, to]. */\r\n public whereDateBetween(field: string, range: [Date | string, Date | string]): this {\r\n this.addOperation(\"whereDateBetween\", { field, range });\r\n return this;\r\n }\r\n\r\n /** Field date is NOT within a range. */\r\n public whereDateNotBetween(field: string, range: [Date | string, Date | string]): this {\r\n this.addOperation(\"whereNotBetween\", { field, range });\r\n return this;\r\n }\r\n\r\n /**\r\n * Match on the time portion of a datetime field.\r\n * Emits a `whereRaw` op with a driver-agnostic marker; the driver parser\r\n * rewrites it to the appropriate SQL (`TIME(field) = ?`) or Mongo expression.\r\n */\r\n public whereTime(field: string, value: string): this {\r\n this.addOperation(\"whereRaw\", {\r\n expression: `TIME(${field}) = ?`,\r\n bindings: [value],\r\n });\r\n return this;\r\n }\r\n\r\n /**\r\n * Day-of-month from a date field (1–31).\r\n * Uses a `whereRaw` op so SQL parsers get the `EXTRACT` expression directly.\r\n * MongoDB drivers override to emit `$dayOfMonth`.\r\n */\r\n public whereDay(field: string, value: number): this {\r\n this.addOperation(\"whereRaw\", {\r\n expression: `EXTRACT(DAY FROM ${field}) = ?`,\r\n bindings: [value],\r\n });\r\n return this;\r\n }\r\n\r\n /** Month extracted from a date field (1–12). */\r\n public whereMonth(field: string, value: number): this {\r\n this.addOperation(\"whereRaw\", {\r\n expression: `EXTRACT(MONTH FROM ${field}) = ?`,\r\n bindings: [value],\r\n });\r\n return this;\r\n }\r\n\r\n /** Year extracted from a date field. */\r\n public whereYear(field: string, value: number): this {\r\n this.addOperation(\"whereRaw\", {\r\n expression: `EXTRACT(YEAR FROM ${field}) = ?`,\r\n bindings: [value],\r\n });\r\n return this;\r\n }\r\n\r\n // ════════════════════════════════════════════════════════\r\n // WHERE CLAUSES — JSON / STRUCTURED DATA\r\n // ════════════════════════════════════════════════════════\r\n\r\n /**\r\n * JSON/array path contains the given value.\r\n * @example q.whereJsonContains(\"tags\", \"typescript\")\r\n */\r\n public whereJsonContains(path: string, value: unknown): this {\r\n this.addOperation(\"whereJsonContains\", { path, value });\r\n return this;\r\n }\r\n\r\n /** JSON/array path does NOT contain the value. */\r\n public whereJsonDoesntContain(path: string, value: unknown): this {\r\n this.addOperation(\"whereJsonDoesntContain\", { path, value });\r\n return this;\r\n }\r\n\r\n /**\r\n * JSON path key exists.\r\n * Uses a `whereRaw` so existing SQL parsers get `IS NOT NULL` immediately.\r\n */\r\n public whereJsonContainsKey(path: string): this {\r\n this.addOperation(\"whereRaw\", { expression: `${path} IS NOT NULL`, bindings: [] });\r\n return this;\r\n }\r\n\r\n /**\r\n * Constrain the length of a JSON array at a path.\r\n * @example q.whereJsonLength(\"tags\", \">\", 3)\r\n */\r\n public whereJsonLength(path: string, operator: WhereOperator, value: number): this {\r\n this.addOperation(\"whereRaw\", {\r\n expression: `jsonb_array_length(${path}) ${operator} ?`,\r\n bindings: [value],\r\n });\r\n return this;\r\n }\r\n\r\n /** JSON path must resolve to an array. */\r\n public whereJsonIsArray(path: string): this {\r\n this.addOperation(\"whereRaw\", {\r\n expression: `jsonb_typeof(${path}) = 'array'`,\r\n bindings: [],\r\n });\r\n return this;\r\n }\r\n\r\n /** JSON path must resolve to an object. */\r\n public whereJsonIsObject(path: string): this {\r\n this.addOperation(\"whereRaw\", {\r\n expression: `jsonb_typeof(${path}) = 'object'`,\r\n bindings: [],\r\n });\r\n return this;\r\n }\r\n\r\n /**\r\n * Constrain the number of elements in an array field.\r\n * @example q.whereArrayLength(\"roles\", \">=\", 2)\r\n */\r\n public whereArrayLength(field: string, operator: WhereOperator, value: number): this {\r\n this.addOperation(\"whereRaw\", {\r\n expression: `array_length(${field}, 1) ${operator} ?`,\r\n bindings: [value],\r\n });\r\n return this;\r\n }\r\n\r\n // ════════════════════════════════════════════════════════\r\n // WHERE CLAUSES — CONVENIENCE SHORTCUTS\r\n // ════════════════════════════════════════════════════════\r\n\r\n /** WHERE id = value. */\r\n public whereId(value: string | number): this {\r\n return this.where(\"id\", value);\r\n }\r\n\r\n /** WHERE id IN values. */\r\n public whereIds(values: Array<string | number>): this {\r\n return this.whereIn(\"id\", values);\r\n }\r\n\r\n /** WHERE uuid = value. */\r\n public whereUuid(value: string): this {\r\n return this.where(\"uuid\", value);\r\n }\r\n\r\n /** WHERE ulid = value. */\r\n public whereUlid(value: string): this {\r\n return this.where(\"ulid\", value);\r\n }\r\n\r\n /**\r\n * Full-text search across one or more fields.\r\n * @example q.whereFullText([\"title\", \"body\"], \"typescript\")\r\n */\r\n public whereFullText(fields: string | string[], query: string): this {\r\n this.addOperation(\"whereFullText\", {\r\n fields: Array.isArray(fields) ? fields : [fields],\r\n query,\r\n });\r\n return this;\r\n }\r\n\r\n /** Full-text search (OR). */\r\n public orWhereFullText(fields: string | string[], query: string): this {\r\n return this.whereFullText(fields, query);\r\n }\r\n\r\n /** Alias for whereFullText with a single field. */\r\n public whereSearch(field: string, query: string): this {\r\n return this.whereFullText([field], query);\r\n }\r\n\r\n /**\r\n * Text search with optional extra equality filters.\r\n * MongoDB-style convenience shorthand.\r\n */\r\n public textSearch(query: string, filters?: WhereObject): this {\r\n if (filters) {\r\n for (const [key, value] of Object.entries(filters)) this.where(key, value as never);\r\n }\r\n return this;\r\n }\r\n\r\n // ════════════════════════════════════════════════════════\r\n // WHERE CLAUSES — EXISTENCE / SUBQUERIES\r\n // ════════════════════════════════════════════════════════\r\n\r\n /**\r\n * WHERE EXISTS (subquery callback) or field IS NOT NULL (string).\r\n *\r\n * @example\r\n * q.whereExists(sub => sub.where(\"userId\", \"users.id\"))\r\n * q.whereExists(\"optionalField\")\r\n */\r\n public whereExists(field: string): this;\r\n public whereExists(callback: WhereCallback<T>): this;\r\n public whereExists(param: string | WhereCallback<T>): this {\r\n if (typeof param === \"function\") {\r\n const sub = this.subQuery();\r\n param(sub as any);\r\n this.addOperation(\"whereExists\", { subquery: sub.operations });\r\n } else {\r\n this.addOperation(\"whereNotNull\", { field: param });\r\n }\r\n return this;\r\n }\r\n\r\n /**\r\n * WHERE NOT EXISTS (subquery callback) or field IS NULL (string).\r\n */\r\n public whereNotExists(field: string): this;\r\n public whereNotExists(callback: WhereCallback<T>): this;\r\n public whereNotExists(param: string | WhereCallback<T>): this {\r\n if (typeof param === \"function\") {\r\n const sub = this.subQuery();\r\n param(sub as any);\r\n this.addOperation(\"whereNotExists\", { subquery: sub.operations });\r\n } else {\r\n this.addOperation(\"whereNull\", { field: param });\r\n }\r\n return this;\r\n }\r\n\r\n /**\r\n * Constrain an array/collection field by element count.\r\n *\r\n * @example\r\n * q.whereSize(\"tags\", 3) // exactly 3\r\n * q.whereSize(\"tags\", \">=\", 1) // at least 1\r\n */\r\n public whereSize(field: string, size: number): this;\r\n public whereSize(field: string, operator: WhereOperator, size: number): this;\r\n public whereSize(field: string, ...args: unknown[]): this {\r\n const operator = args.length === 2 ? (args[0] as WhereOperator) : \"=\";\r\n const size = (args.length === 2 ? args[1] : args[0]) as number;\r\n return this.whereArrayLength(field, operator, size);\r\n }\r\n\r\n /**\r\n * AND NOT wrapper — negate a nested group.\r\n * @example q.whereNot(q => q.where(\"status\", \"banned\").where(\"role\", \"user\"))\r\n */\r\n public whereNot(callback: WhereCallback<T>): this {\r\n const sub = this.subQuery();\r\n callback(sub as any);\r\n this.addOperation(\"whereNot\", { nested: sub.operations });\r\n return this;\r\n }\r\n\r\n /** OR NOT wrapper. */\r\n public orWhereNot(callback: WhereCallback<T>): this {\r\n const sub = this.subQuery();\r\n callback(sub as any);\r\n this.addOperation(\"orWhereNot\", { nested: sub.operations });\r\n return this;\r\n }\r\n\r\n // ════════════════════════════════════════════════════════\r\n // JOINS — STANDARD SQL-STYLE\r\n // Note: Op type names match parser switch cases exactly.\r\n // join / innerJoin → INNER JOIN\r\n // leftJoin → LEFT JOIN\r\n // rightJoin → RIGHT JOIN\r\n // fullJoin → FULL OUTER JOIN\r\n // crossJoin → CROSS JOIN\r\n // joinRaw → raw expression\r\n // ════════════════════════════════════════════════════════\r\n\r\n /**\r\n * INNER JOIN.\r\n * @example q.join(\"categories\", \"posts.categoryId\", \"categories.id\")\r\n */\r\n public join(table: string, localField: string, foreignField: string): this;\r\n public join(options: JoinOptions): this;\r\n public join(...args: unknown[]): this {\r\n if (args.length === 3) {\r\n this.addOperation(\"join\", { table: args[0], localField: args[1], foreignField: args[2] });\r\n } else {\r\n this.addOperation(\"join\", args[0] as Record<string, unknown>);\r\n }\r\n return this;\r\n }\r\n\r\n /** LEFT JOIN. */\r\n public leftJoin(table: string, localField: string, foreignField: string): this;\r\n public leftJoin(options: JoinOptions): this;\r\n public leftJoin(...args: unknown[]): this {\r\n if (args.length === 3) {\r\n this.addOperation(\"leftJoin\", { table: args[0], localField: args[1], foreignField: args[2] });\r\n } else {\r\n this.addOperation(\"leftJoin\", args[0] as Record<string, unknown>);\r\n }\r\n return this;\r\n }\r\n\r\n /** RIGHT JOIN. */\r\n public rightJoin(table: string, localField: string, foreignField: string): this;\r\n public rightJoin(options: JoinOptions): this;\r\n public rightJoin(...args: unknown[]): this {\r\n if (args.length === 3) {\r\n this.addOperation(\"rightJoin\", {\r\n table: args[0],\r\n localField: args[1],\r\n foreignField: args[2],\r\n });\r\n } else {\r\n this.addOperation(\"rightJoin\", args[0] as Record<string, unknown>);\r\n }\r\n return this;\r\n }\r\n\r\n /** INNER JOIN (alias for join). */\r\n public innerJoin(table: string, localField: string, foreignField: string): this;\r\n public innerJoin(options: JoinOptions): this;\r\n public innerJoin(...args: unknown[]): this {\r\n if (args.length === 3) {\r\n this.addOperation(\"innerJoin\", {\r\n table: args[0],\r\n localField: args[1],\r\n foreignField: args[2],\r\n });\r\n } else {\r\n this.addOperation(\"innerJoin\", args[0] as Record<string, unknown>);\r\n }\r\n return this;\r\n }\r\n\r\n /** FULL OUTER JOIN. */\r\n public fullJoin(table: string, localField: string, foreignField: string): this;\r\n public fullJoin(options: JoinOptions): this;\r\n public fullJoin(...args: unknown[]): this {\r\n if (args.length === 3) {\r\n this.addOperation(\"fullJoin\", { table: args[0], localField: args[1], foreignField: args[2] });\r\n } else {\r\n this.addOperation(\"fullJoin\", args[0] as Record<string, unknown>);\r\n }\r\n return this;\r\n }\r\n\r\n /** CROSS JOIN. */\r\n public crossJoin(table: string): this {\r\n this.addOperation(\"crossJoin\", { table });\r\n return this;\r\n }\r\n\r\n /** Raw JOIN expression. Driver responsible for handling. */\r\n public joinRaw(expression: RawExpression, bindings?: unknown[]): this {\r\n this.addOperation(\"joinRaw\", { expression, bindings: bindings ?? [] });\r\n return this;\r\n }\r\n\r\n // ════════════════════════════════════════════════════════\r\n // RELATION EAGER LOADING — JOIN-BASED (joinWith)\r\n // ════════════════════════════════════════════════════════\r\n\r\n /**\r\n * Eager-load named relations via a single JOIN / $lookup query.\r\n *\r\n * Constraints are eagerly resolved at call time:\r\n * - Callbacks are invoked immediately → `subOps` stored in op data.\r\n * - Column shorthands are parsed into a `columns[]` array.\r\n *\r\n * The driver executor reads the `joinWith` op and uses the resolved data\r\n * alongside its own relation definition map to emit the appropriate SQL JOIN\r\n * or MongoDB $lookup stage.\r\n *\r\n * Supported arg forms (may be mixed):\r\n * - `\"author\"` / `[\"author\", \"category\"]` — no constraint\r\n * - `{ author: \"id,name\" }` — column shorthand\r\n * - `{ actions: q => q.where(\"status\",\"pending\").limit(5) }` — callback\r\n *\r\n * @example\r\n * Post.joinWith(\"author\", \"category\")\r\n * ChatMessage.joinWith({ actions: q => q.where(\"status\", \"pending\").limit(5) })\r\n * ChatMessage.joinWith({ org: \"id,name\", actions: q => q.orderBy(\"sort_order\") })\r\n */\r\n public joinWith(...args: unknown[]): this {\r\n const resolved: Record<string, { columns?: string[]; subOps?: Op[] }> = {};\r\n\r\n for (const arg of args) {\r\n if (typeof arg === \"string\") {\r\n resolved[arg] = {};\r\n } else if (Array.isArray(arg)) {\r\n for (const rel of arg as string[]) resolved[rel] = {};\r\n } else if (typeof arg === \"object\" && arg !== null) {\r\n for (const [rel, constraint] of Object.entries(arg as Record<string, JoinWithConstraint>)) {\r\n if (typeof constraint === \"function\") {\r\n const sub = this.subQuery();\r\n constraint(sub);\r\n resolved[rel] = { subOps: sub.operations };\r\n } else if (typeof constraint === \"string\" && constraint !== \"\") {\r\n resolved[rel] = {\r\n columns: constraint\r\n .split(\",\")\r\n .map((s) => s.trim())\r\n .filter(Boolean),\r\n };\r\n } else {\r\n resolved[rel] = {};\r\n }\r\n }\r\n }\r\n }\r\n\r\n this.addOperation(\"joinWith\", { resolved });\r\n return this;\r\n }\r\n\r\n // ════════════════════════════════════════════════════════\r\n // RELATION EAGER LOADING — SEPARATE QUERIES (with)\r\n // ════════════════════════════════════════════════════════\r\n\r\n /**\r\n * Eager-load relations via separate queries (N+1 avoided by batching).\r\n *\r\n * @example\r\n * q.with(\"posts\")\r\n * q.with(\"posts\", q => q.where(\"published\", true))\r\n * q.with({ posts: true, comments: q => q.limit(5) })\r\n */\r\n public with(\r\n ...args: (string | Record<string, boolean | ((q: any) => void)> | ((q: any) => void))[]\r\n ): this {\r\n for (let i = 0; i < args.length; i++) {\r\n const arg = args[i];\r\n if (typeof arg === \"string\") {\r\n const next = args[i + 1];\r\n if (typeof next === \"function\") {\r\n this.eagerLoadRelations.set(arg, next as (q: any) => void);\r\n i++;\r\n } else {\r\n this.eagerLoadRelations.set(arg, true);\r\n }\r\n } else if (typeof arg === \"object\" && arg !== null) {\r\n for (const [key, value] of Object.entries(\r\n arg as Record<string, boolean | ((q: any) => void)>,\r\n )) {\r\n this.eagerLoadRelations.set(key, value);\r\n }\r\n }\r\n }\r\n return this;\r\n }\r\n\r\n /**\r\n * Register one or more relation counts to emit alongside each result row.\r\n *\r\n * Accepts:\r\n * - Bare relation names (variadic strings or array): `withCount(\"posts\", \"comments\")`\r\n * - Alias shorthand: `withCount(\"posts as totalPosts\")`\r\n * - Object form for per-relation constraints / aliases:\r\n * `withCount({ posts: true, \"posts as approved\": (q) => q.where(\"approved\", true) })`\r\n *\r\n * Each entry is stored in `countRelations` keyed by its output column alias\r\n * (default `${relationName}Count`). The driver subclass consumes the map at\r\n * execute time to emit count expressions.\r\n *\r\n * @example\r\n * ```typescript\r\n * await User.query().withCount(\"posts\").get(); // postsCount\r\n * await User.query().withCount(\"posts as totalPosts\").get(); // totalPosts\r\n * await User.query()\r\n * .withCount({\r\n * posts: true,\r\n * \"posts as published\": (q) => q.where(\"isPublished\", true),\r\n * comments: \"commentTotal\",\r\n * })\r\n * .get();\r\n * ```\r\n */\r\n public withCount(...args: unknown[]): this {\r\n for (const arg of args) {\r\n if (typeof arg === \"string\") {\r\n this.recordCountEntry(arg);\r\n continue;\r\n }\r\n\r\n if (Array.isArray(arg)) {\r\n for (const spec of arg as string[]) {\r\n this.recordCountEntry(spec);\r\n }\r\n continue;\r\n }\r\n\r\n if (typeof arg === \"object\" && arg !== null) {\r\n const entries = Object.entries(\r\n arg as Record<string, true | string | ((query: any) => void)>,\r\n );\r\n\r\n for (const [key, value] of entries) {\r\n if (value === true) {\r\n this.recordCountEntry(key);\r\n } else if (typeof value === \"string\") {\r\n this.recordCountEntry(`${key} as ${value}`);\r\n } else if (typeof value === \"function\") {\r\n this.recordCountEntry(key, value);\r\n }\r\n }\r\n }\r\n }\r\n\r\n return this;\r\n }\r\n\r\n /**\r\n * Parse a count spec (\"relation\" or \"relation as alias\") into its relation\r\n * name and output alias, optionally capturing a constraint callback's\r\n * operations via a sub-builder. Stored in `countRelations` keyed by alias.\r\n */\r\n protected recordCountEntry(spec: string, constraint?: (query: any) => void): void {\r\n const { relation, alias } = this.parseCountSpec(spec);\r\n\r\n let constraintOps: Op[] | undefined;\r\n\r\n if (constraint) {\r\n const sub = this.subQuery();\r\n constraint(sub);\r\n constraintOps = sub.operations;\r\n }\r\n\r\n this.countRelations.set(alias, { relation, constraintOps });\r\n }\r\n\r\n /**\r\n * Split a `\"<relation>\"` or `\"<relation> as <alias>\"` spec. Returns the\r\n * resolved relation name and the output column alias (defaulting to\r\n * `${relation}Count` when no `as` is present).\r\n */\r\n protected parseCountSpec(spec: string): { relation: string; alias: string } {\r\n const trimmed = spec.trim();\r\n const match = /^(.+?)\\s+as\\s+(.+)$/i.exec(trimmed);\r\n\r\n if (!match) {\r\n return { relation: trimmed, alias: `${trimmed}Count` };\r\n }\r\n\r\n const relation = match[1];\r\n const alias = match[2];\r\n\r\n if (relation === undefined || alias === undefined) {\r\n return { relation: trimmed, alias: `${trimmed}Count` };\r\n }\r\n\r\n return { relation: relation.trim(), alias: alias.trim() };\r\n }\r\n\r\n /**\r\n * Filter to rows that have at least one related record.\r\n * @example q.has(\"comments\")\r\n * @example q.has(\"comments\", \">=\", 3)\r\n */\r\n public has(relation: string, operator?: WhereOperator, count?: number): this {\r\n this.addOperation(\"has\", { relation, operator: operator ?? \">=\", count: count ?? 1 });\r\n return this;\r\n }\r\n\r\n /**\r\n * Filter to rows with related records matching a sub-query (AND).\r\n * @example q.whereHas(\"comments\", q => q.where(\"approved\", true))\r\n */\r\n public whereHas(relation: string, callback: (q: any) => void): this {\r\n const sub = this.subQuery();\r\n callback(sub);\r\n this.addOperation(\"whereHas\", { relation, subquery: sub.operations });\r\n return this;\r\n }\r\n\r\n /** Same as whereHas but OR-joined. */\r\n public orWhereHas(relation: string, callback: (q: any) => void): this {\r\n const sub = this.subQuery();\r\n callback(sub);\r\n this.addOperation(\"orWhereHas\", { relation, subquery: sub.operations });\r\n return this;\r\n }\r\n\r\n /** Filter to rows with NO related records. */\r\n public doesntHave(relation: string): this {\r\n this.addOperation(\"doesntHave\", { relation });\r\n return this;\r\n }\r\n\r\n /** Filter to rows with NO related records matching conditions. */\r\n public whereDoesntHave(relation: string, callback: (q: any) => void): this {\r\n const sub = this.subQuery();\r\n callback(sub);\r\n this.addOperation(\"whereDoesntHave\", { relation, subquery: sub.operations });\r\n return this;\r\n }\r\n\r\n // ════════════════════════════════════════════════════════\r\n // SELECT / PROJECTION\r\n // ════════════════════════════════════════════════════════\r\n\r\n /**\r\n * Select specific columns.\r\n *\r\n * @example\r\n * q.select([\"id\", \"name\"])\r\n * q.select(\"id\", \"name\")\r\n * q.select({ name: 1, password: 0 }) // MongoDB-style projection\r\n */\r\n public select(fields: string[]): this;\r\n public select(fields: Record<string, 0 | 1 | boolean>): this;\r\n public select(...fields: Array<string | string[]>): this;\r\n public select(...args: unknown[]): this {\r\n if (args.length === 1 && Array.isArray(args[0])) {\r\n this.addOperation(\"select\", { fields: args[0] });\r\n } else if (args.length === 1 && typeof args[0] === \"object\" && !Array.isArray(args[0])) {\r\n this.addOperation(\"select\", { fields: args[0] as Record<string, unknown> });\r\n } else {\r\n this.addOperation(\"select\", { fields: (args as Array<string | string[]>).flat() });\r\n }\r\n return this;\r\n }\r\n\r\n /** Select a field under an alias. @example q.selectAs(\"fullName\", \"name\") */\r\n public selectAs(field: string, alias: string): this {\r\n this.addOperation(\"select\", { fields: { [field]: alias } });\r\n return this;\r\n }\r\n\r\n /**\r\n * Raw SELECT expression.\r\n * @example q.selectRaw(\"COUNT(*) AS total\")\r\n */\r\n public selectRaw(expression: RawExpression, bindings?: unknown[]): this {\r\n this.addOperation(\"selectRaw\", { expression, bindings: bindings ?? [] });\r\n return this;\r\n }\r\n\r\n /** Multiple raw SELECT expressions in one call. */\r\n public selectRawMany(\r\n definitions: Array<{ alias: string; expression: RawExpression; bindings?: unknown[] }>,\r\n ): this {\r\n for (const def of definitions) {\r\n this.selectRaw({ [def.alias]: def.expression }, def.bindings);\r\n }\r\n return this;\r\n }\r\n\r\n /** Subquery as a named projected field. */\r\n public selectSub(expression: RawExpression, alias: string): this {\r\n this.addOperation(\"selectRaw\", { expression: { [alias]: expression } });\r\n return this;\r\n }\r\n\r\n /** Alias for selectSub. */\r\n public addSelectSub(expression: RawExpression, alias: string): this {\r\n return this.selectSub(expression, alias);\r\n }\r\n\r\n /**\r\n * Aggregate function as a projected field.\r\n * @example q.selectAggregate(\"price\", \"sum\", \"totalRevenue\")\r\n */\r\n public selectAggregate(\r\n field: string,\r\n aggregate: \"sum\" | \"avg\" | \"min\" | \"max\" | \"count\" | \"first\" | \"last\",\r\n alias: string,\r\n ): this {\r\n return this.selectRaw({ [alias]: `${aggregate.toUpperCase()}(${field})` });\r\n }\r\n\r\n /** Existence check as a projected boolean field. */\r\n public selectExists(field: string, alias: string): this {\r\n return this.selectRaw({ [alias]: `${field} IS NOT NULL` });\r\n }\r\n\r\n /** COUNT as a projected field. */\r\n public selectCount(field: string, alias: string): this {\r\n return this.selectAggregate(field, \"count\", alias);\r\n }\r\n\r\n /**\r\n * CASE / switch expression.\r\n * @example q.selectCase([{ when: \"status = 1\", then: \"'active'\" }], \"'inactive'\", \"statusLabel\")\r\n */\r\n public selectCase(\r\n cases: Array<{ when: RawExpression; then: RawExpression | unknown }>,\r\n otherwise: RawExpression | unknown,\r\n alias: string,\r\n ): this {\r\n const caseExpr = cases.map((c) => `WHEN ${c.when} THEN ${c.then}`).join(\" \");\r\n return this.selectRaw({ [alias]: `CASE ${caseExpr} ELSE ${otherwise} END` });\r\n }\r\n\r\n /** IF/ELSE conditional field. */\r\n public selectWhen(\r\n condition: RawExpression,\r\n thenValue: RawExpression | unknown,\r\n elseValue: RawExpression | unknown,\r\n alias: string,\r\n ): this {\r\n return this.selectRaw({\r\n [alias]: `CASE WHEN ${condition} THEN ${thenValue} ELSE ${elseValue} END`,\r\n });\r\n }\r\n\r\n /**\r\n * Driver-native projection manipulation.\r\n * No-op in base — override in driver subclasses.\r\n */\r\n public selectDriverProjection(_callback: (projection: Record<string, unknown>) => void): this {\r\n return this;\r\n }\r\n\r\n /** JSON path extraction as a projected field. */\r\n public selectJson(path: string, alias?: string): this {\r\n const parts = path.split(\"->\");\r\n const column = parts[0] ?? \"\";\r\n const jsonPath = parts.slice(1).join(\"->\");\r\n const expr = jsonPath ? `${column}->>'${jsonPath}'` : column;\r\n return alias ? this.selectAs(expr, alias) : this.selectRaw(expr);\r\n }\r\n\r\n /** JSON extraction via raw expression. */\r\n public selectJsonRaw(_path: string, expression: RawExpression, alias: string): this {\r\n return this.selectRaw({ [alias]: expression });\r\n }\r\n\r\n /** Exclude a JSON path from projection. */\r\n public deselectJson(path: string): this {\r\n return this.deselect([path]);\r\n }\r\n\r\n /** String concatenation as a projected field. */\r\n public selectConcat(fields: Array<string | RawExpression>, alias: string): this {\r\n return this.selectRaw({ [alias]: fields.join(\" || \") });\r\n }\r\n\r\n /** COALESCE (first non-null) as a projected field. */\r\n public selectCoalesce(fields: Array<string | RawExpression>, alias: string): this {\r\n return this.selectRaw({ [alias]: `COALESCE(${fields.join(\", \")})` });\r\n }\r\n\r\n /** Window function expression. */\r\n public selectWindow(spec: RawExpression): this {\r\n this.addOperation(\"selectRaw\", { expression: spec });\r\n return this;\r\n }\r\n\r\n /** Exclude specific columns from results. */\r\n public deselect(fields: string[]): this {\r\n this.addOperation(\"deselect\", { fields });\r\n return this;\r\n }\r\n\r\n /**\r\n * Remove all select operations (resets to wildcard).\r\n * Uses `rebuildIndex()` — no unsafe casts.\r\n */\r\n public clearSelect(): this {\r\n this.operations = this.operations.filter(\r\n (op) => !op.type.startsWith(\"select\") && op.type !== \"deselect\",\r\n );\r\n this.rebuildIndex();\r\n return this;\r\n }\r\n\r\n /** Alias for clearSelect. */\r\n public selectAll(): this {\r\n return this.clearSelect();\r\n }\r\n\r\n /** Alias for clearSelect. */\r\n public selectDefault(): this {\r\n return this.clearSelect();\r\n }\r\n\r\n /** Append additional fields to existing selection. */\r\n public addSelect(fields: string[]): this {\r\n this.addOperation(\"select\", { fields, add: true });\r\n return this;\r\n }\r\n\r\n /**\r\n * Record a DISTINCT flag (fluent — does not execute).\r\n * Subclasses expose a separate async `distinct(field)` execution method.\r\n */\r\n public distinctValues(fields?: string | string[]): this {\r\n const fieldList = fields ? (Array.isArray(fields) ? fields : [fields]) : [];\r\n this.addOperation(\"distinct\", { fields: fieldList });\r\n return this;\r\n }\r\n\r\n // ════════════════════════════════════════════════════════\r\n // ORDERING\r\n // ════════════════════════════════════════════════════════\r\n\r\n /**\r\n * ORDER BY a column.\r\n *\r\n * @example\r\n * q.orderBy(\"createdAt\", \"desc\")\r\n * q.orderBy({ name: \"asc\", age: \"desc\" })\r\n */\r\n public orderBy(field: string, direction?: OrderDirection): this;\r\n public orderBy(fields: Record<string, OrderDirection>): this;\r\n public orderBy(...args: unknown[]): this {\r\n if (typeof args[0] === \"string\") {\r\n this.addOperation(\"orderBy\", {\r\n field: args[0],\r\n direction: (args[1] as OrderDirection) ?? \"asc\",\r\n });\r\n } else {\r\n for (const [field, direction] of Object.entries(args[0] as Record<string, OrderDirection>)) {\r\n this.addOperation(\"orderBy\", { field, direction });\r\n }\r\n }\r\n return this;\r\n }\r\n\r\n /** ORDER BY descending shorthand. */\r\n public orderByDesc(field: string): this {\r\n return this.orderBy(field, \"desc\");\r\n }\r\n\r\n /**\r\n * Raw ORDER BY expression.\r\n * @example q.orderByRaw(\"RANDOM()\")\r\n * @example q.orderByRaw({ $meta: \"textScore\" })\r\n */\r\n public orderByRaw(expression: RawExpression, bindings?: unknown[]): this {\r\n this.addOperation(\"orderByRaw\", { expression, bindings: bindings ?? [] });\r\n return this;\r\n }\r\n\r\n /**\r\n * Random order. Maps to `RANDOM()` in SQL or `$sample` in MongoDB.\r\n * @param limit - Optional limit (required for MongoDB $sample)\r\n */\r\n public orderByRandom(limit?: number): this {\r\n this.addOperation(\"orderByRaw\", { expression: \"RANDOM()\" });\r\n if (limit !== undefined) this.limit(limit);\r\n return this;\r\n }\r\n\r\n /** Order ascending by a date column (oldest first). */\r\n public oldest(column = \"createdAt\"): this {\r\n return this.orderBy(column, \"asc\");\r\n }\r\n\r\n // ════════════════════════════════════════════════════════\r\n // LIMIT / OFFSET\r\n // ════════════════════════════════════════════════════════\r\n\r\n /** Limit number of results. */\r\n public limit(value: number): this {\r\n this.addOperation(\"limit\", { value });\r\n return this;\r\n }\r\n\r\n /** Skip N results (OFFSET). */\r\n public skip(value: number): this {\r\n this.addOperation(\"offset\", { value });\r\n return this;\r\n }\r\n\r\n /** Alias for skip. */\r\n public offset(value: number): this {\r\n return this.skip(value);\r\n }\r\n\r\n /** Alias for limit. */\r\n public take(value: number): this {\r\n return this.limit(value);\r\n }\r\n\r\n // ════════════════════════════════════════════════════════\r\n // ROW LOCKING\r\n // ════════════════════════════════════════════════════════\r\n\r\n /**\r\n * Lock the selected rows for update (`SELECT ... FOR UPDATE`).\r\n *\r\n * `skipLocked` skips rows other transactions hold locks on (concurrent\r\n * queue-claim shape); `noWait` errors immediately instead of waiting. The\r\n * two are mutually exclusive. Only meaningful inside a transaction.\r\n *\r\n * SQL drivers emit the locking clause; drivers without row locking\r\n * (MongoDB) override this to throw.\r\n */\r\n public lockForUpdate(options?: LockForUpdateOptions): this {\r\n if (options?.skipLocked && options?.noWait) {\r\n throw new Error(\"lockForUpdate: `skipLocked` and `noWait` are mutually exclusive.\");\r\n }\r\n\r\n this.addOperation(\"lock\", {\r\n mode: \"update\",\r\n skipLocked: options?.skipLocked ?? false,\r\n noWait: options?.noWait ?? false,\r\n });\r\n return this;\r\n }\r\n\r\n // ════════════════════════════════════════════════════════\r\n // GROUPING / AGGREGATION\r\n // ════════════════════════════════════════════════════════\r\n\r\n /**\r\n * GROUP BY clause.\r\n * @example q.groupBy(\"status\")\r\n * @example q.groupBy([\"year\", \"month\"])\r\n */\r\n public groupBy(input: GroupByInput): this {\r\n const fields = Array.isArray(input) ? input : [input];\r\n this.addOperation(\"groupBy\", { fields });\r\n return this;\r\n }\r\n\r\n /** Raw GROUP BY expression. */\r\n public groupByRaw(expression: RawExpression, bindings?: unknown[]): this {\r\n this.addOperation(\"groupBy\", { expression, bindings: bindings ?? [] });\r\n return this;\r\n }\r\n\r\n /**\r\n * HAVING clause (post-group filter).\r\n *\r\n * @example\r\n * q.having(\"total\", \">\", 100)\r\n * q.having([\"total\", \">\", 100])\r\n * q.having({ total: 100 })\r\n */\r\n public having(field: string, value: unknown): this;\r\n public having(field: string, operator: WhereOperator, value: unknown): this;\r\n public having(condition: HavingInput): this;\r\n public having(...args: unknown[]): this {\r\n if (args.length === 1) {\r\n const input = args[0] as HavingInput;\r\n if (Array.isArray(input)) {\r\n if (input.length === 2) {\r\n this.addOperation(\"having\", { field: input[0], operator: \"=\", value: input[1] });\r\n } else {\r\n this.addOperation(\"having\", { field: input[0], operator: input[1], value: input[2] });\r\n }\r\n } else {\r\n for (const [key, value] of Object.entries(input as Record<string, unknown>)) {\r\n this.addOperation(\"having\", { field: key, operator: \"=\", value });\r\n }\r\n }\r\n } else if (args.length === 2) {\r\n this.addOperation(\"having\", { field: args[0], operator: \"=\", value: args[1] });\r\n } else {\r\n this.addOperation(\"having\", { field: args[0], operator: args[1], value: args[2] });\r\n }\r\n return this;\r\n }\r\n\r\n /** Raw HAVING expression. */\r\n public havingRaw(expression: RawExpression, bindings?: unknown[]): this {\r\n this.addOperation(\"havingRaw\", { expression, bindings: bindings ?? [] });\r\n return this;\r\n }\r\n\r\n // ════════════════════════════════════════════════════════\r\n // UTILITY / CONTROL FLOW\r\n // ════════════════════════════════════════════════════════\r\n\r\n /**\r\n * Side-effect tap — executes callback synchronously and returns `this`.\r\n * @example q.where(...).tap(q => console.log(q.operations.length)).limit(10)\r\n */\r\n public tap(callback: (builder: this) => void): this {\r\n callback(this);\r\n return this;\r\n }\r\n\r\n /**\r\n * Conditionally apply query modifications.\r\n *\r\n * @example\r\n * q.when(userId, (q, id) => q.where(\"userId\", id))\r\n * q.when(isAdmin, q => q.withoutGlobalScopes(), q => q.scope(\"active\"))\r\n */\r\n public when<V>(\r\n condition: V | boolean,\r\n callback: (builder: this, value: V) => void,\r\n otherwise?: (builder: this) => void,\r\n ): this {\r\n if (condition) {\r\n callback(this, condition as V);\r\n } else if (otherwise) {\r\n otherwise(this);\r\n }\r\n return this;\r\n }\r\n}\r\n"],"mappings":"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"}
|
package/llms-full.txt
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
---
|
|
10
10
|
name: aggregate-data
|
|
11
|
-
description: 'Compute aggregates over a query — scalar `.count()` / `.sum(field)` / `.avg` / `.min` / `.max`, plus grouped rollups via the two-arg `.groupBy(fields, { alias: $agg.* })`, portable date-bucketing via `.groupByDate(col, unit, aggregates?)`, the `$agg` helpers (including expression-aware `$agg.sum($expr.mul("price","quantity"))` / `$agg.sumRaw`), and `.having(alias, op, value)` on computed aggregates. Triggers: `.count`, `.sum`, `.avg`, `.min`, `.max`, `.groupBy`, `.groupByDate`, `.having`, `$agg`, `$agg.sum`, `$agg.sumRaw`, `$agg.count`, `$expr`, `$expr.mul`, `$expr.col`, `$expr.lit`; "monthly revenue report", "revenue per month", "X per category", "group by status", "sum price times quantity", "dashboard rollup"; typical import `import { Model, $agg, $expr } from "@warlock.js/cascade"`. Skip: row queries — `@warlock.js/cascade/query-data/SKILL.md`; cached aggregates — `@warlock.js/cache/use-cached-hof/SKILL.md`; competing tools raw SQL `GROUP BY`, `mongoose aggregate`, `prisma` `groupBy`.'
|
|
11
|
+
description: 'Compute aggregates over a query — scalar `.count()` / `.sum(field)` / `.avg` / `.min` / `.max`, plus grouped rollups via the two-arg `.groupBy(fields, { alias: $agg.* })`, portable date-bucketing via `.groupByDate(col, unit, aggregates?)`, the `$agg` helpers (including expression-aware `$agg.sum($expr.mul("price","quantity"))` / `$agg.sumRaw`), and `.having(alias, op, value)` on computed aggregates, plus MongoDB pipeline stages `.unwind(field, options?)` / `.addFields(fields)` / pipeline-form `join()` (`UnsupportedQueryOperationError` on Postgres). Triggers: `.unwind`, `.addFields`, `.joinRaw`, `.raw`, `$unwind`, `$lookup`, `.count`, `.sum`, `.avg`, `.min`, `.max`, `.groupBy`, `.groupByDate`, `.having`, `$agg`, `$agg.sum`, `$agg.sumRaw`, `$agg.count`, `$expr`, `$expr.mul`, `$expr.col`, `$expr.lit`; "monthly revenue report", "revenue per month", "X per category", "group by status", "sum price times quantity", "dashboard rollup"; typical import `import { Model, $agg, $expr } from "@warlock.js/cascade"`. Skip: row queries — `@warlock.js/cascade/query-data/SKILL.md`; cached aggregates — `@warlock.js/cache/use-cached-hof/SKILL.md`; competing tools raw SQL `GROUP BY`, `mongoose aggregate`, `prisma` `groupBy`.'
|
|
12
12
|
---
|
|
13
13
|
|
|
14
14
|
# Use aggregates and groupBy
|
|
@@ -138,6 +138,29 @@ await Order.query()
|
|
|
138
138
|
|
|
139
139
|
The `orderBy` reference matches the alias from the aggregates object.
|
|
140
140
|
|
|
141
|
+
## Pipeline stages — `.unwind()` / `.addFields()` / pipeline `join()` (MongoDB)
|
|
142
|
+
|
|
143
|
+
```ts
|
|
144
|
+
// One row per tag, then filter the tags (stages run in call order)
|
|
145
|
+
const rows = await Post.query().unwind("tags").where("tags", "news").lean().get();
|
|
146
|
+
|
|
147
|
+
// Keep posts with no tags; record each tag's position
|
|
148
|
+
await Post.query().unwind("tags", { preserveNullAndEmptyArrays: true, includeArrayIndex: "position" }).get();
|
|
149
|
+
|
|
150
|
+
// Computed field, existing fields kept
|
|
151
|
+
await Post.query().addFields({ score: { $add: ["$likes", "$shares"] } }).orderBy("score", "desc").get();
|
|
152
|
+
|
|
153
|
+
// Pipeline $lookup
|
|
154
|
+
await Post.query().join({ table: "comments", localField: "id", foreignField: "postId",
|
|
155
|
+
alias: "approved", pipeline: [{ $match: { approved: true } }] }).lean().get();
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
`unwind()` returns several rows for one stored document. Use `.lean()` when you only read them. `addFields` expressions are raw aggregation expressions: write them in code, never from request data. Computed columns in the output also work with `selectRaw({ alias: expr })` (`$project`). There is no `$facet` method yet.
|
|
159
|
+
|
|
160
|
+
Raw stages: `joinRaw({ $lookup: { from, let, pipeline, as } })` (or an array of stages) is emitted verbatim in call order. `raw((pipeline) => [...pipeline, { $sample: { size: 5 } }])` gets the pipeline built so far; return a new array or mutate it and return nothing. Anything else (a SQL string, a non-stage object) throws `UnsupportedQueryOperationError`. Build these in code, never from request data.
|
|
161
|
+
|
|
162
|
+
**Postgres:** `unwind()` and `addFields()` throw `UnsupportedQueryOperationError` (`operation`, `driver`) when you call them. They are never dropped without an error. Use `selectRaw` with `jsonb_array_elements()` / `unnest()`, or a related table, instead.
|
|
163
|
+
|
|
141
164
|
## Gotchas
|
|
142
165
|
|
|
143
166
|
- **`where` vs `having`.** Row filters go in `.where()` (before grouping, index-friendly); aggregate filters go in `.having()`.
|
|
@@ -1773,7 +1796,7 @@ The total count requires an extra query. On very large filtered tables, this can
|
|
|
1773
1796
|
|
|
1774
1797
|
---
|
|
1775
1798
|
name: perform-atomic-ops
|
|
1776
|
-
description: 'Avoid races on concurrent writes — `Model.increase(filter, field, n)` / `Model.decrease` for atomic counters, `Model.atomic(filter, ops)` for arbitrary mutations (`$set` / `$inc` / `$push` / `$pull`), `Model.createMany` / `Model.findAndUpdate` / `Model.delete` for bulk. `atomic()` / `findAndUpdate()` / `findOneAndUpdate()` / `findAndReplace()` / `findOneAndDelete()` sanitize their `filter` argument — `$`-prefixed keys throw `UnsafeFilterError`, same check as `where()`. Triggers: `Model.increase`, `Model.decrease`, `Model.atomic`, `Model.createMany`, `createMany bulk`, `batchSize`, `Model.findAndUpdate`, `Model.findOneAndUpdate`, `Model.findAndReplace`, `Model.findOneAndDelete`, `Model.delete`, `$inc`, `$set`, `UnsafeFilterError`; "increment counter under concurrency", "bulk insert without N+1", "fast bulk insert", "insert thousands of rows", "atomic update without loading", "is atomic() safe with a request body filter"; typical import `import { Model } from "@warlock.js/cascade"`. Skip: multi-row atomicity — `@warlock.js/cascade/manage-transactions/SKILL.md`; competing patterns `mongoose findOneAndUpdate`, `pg` `UPDATE ... SET x = x + 1`.'
|
|
1799
|
+
description: 'Avoid races on concurrent writes — `Model.increase(filter, field, n)` / `Model.decrease` for atomic counters, `Model.atomic(filter, ops, options?)` for arbitrary mutations (`$set` / `$inc` / `$push` / `$pull` / `$addToSet` / `$setOnInsert`, pipeline updates, `upsert`, `returnDocument`, `arrayFilters`, `trustedFilter`), `Model.createMany` / `Model.findAndUpdate` / `Model.delete` for bulk. `atomic()` / `findAndUpdate()` / `findOneAndUpdate()` / `findAndReplace()` / `findOneAndDelete()` sanitize their `filter` argument — `$`-prefixed keys throw `UnsafeFilterError`, same check as `where()`. Triggers: `Model.increase`, `Model.decrease`, `Model.atomic`, `Model.createMany`, `createMany bulk`, `batchSize`, `Model.findAndUpdate`, `Model.findOneAndUpdate`, `Model.findAndReplace`, `Model.findOneAndDelete`, `Model.delete`, `$inc`, `$set`, `UnsafeFilterError`, `UnsupportedUpdateOperationError`, `upsert`, `returnDocument`, `$setOnInsert`; "upsert a counter", "reserve quota atomically", "increment counter under concurrency", "bulk insert without N+1", "fast bulk insert", "insert thousands of rows", "atomic update without loading", "is atomic() safe with a request body filter"; typical import `import { Model } from "@warlock.js/cascade"`. Skip: multi-row atomicity — `@warlock.js/cascade/manage-transactions/SKILL.md`; competing patterns `mongoose findOneAndUpdate`, `pg` `UPDATE ... SET x = x + 1`.'
|
|
1777
1800
|
---
|
|
1778
1801
|
|
|
1779
1802
|
# Use atomic operations
|
|
@@ -1798,9 +1821,44 @@ await User.atomic({ id: userId }, {
|
|
|
1798
1821
|
});
|
|
1799
1822
|
```
|
|
1800
1823
|
|
|
1801
|
-
`Model.atomic(filter, operations)` → `Promise<number>`. Driver-flavored atomic mutation — MongoDB has `$set` / `$inc` / `$push` / `$pull
|
|
1824
|
+
`Model.atomic(filter, operations)` → `Promise<number>`. Driver-flavored atomic mutation — MongoDB has `$set` / `$inc` / `$push` / `$pull` natively; the Postgres driver supports `$set` / `$unset` / `$inc` / `$dec` (and `$setOnInsert` on upsert) and throws `UnsupportedUpdateOperationError` for the rest. Use when you need to combine multiple field changes atomically without loading the model first.
|
|
1802
1825
|
|
|
1803
|
-
**`filter` is sanitized like `where()`.** `atomic()`, `findAndUpdate()`, `findOneAndUpdate()`, `findAndReplace()` and `findOneAndDelete()` all run their `filter` argument through the same `$`-prefixed-key check as `Model.where()` (see [`query-data`](@warlock.js/cascade/query-data/SKILL.md)) and throw `UnsafeFilterError` on a key like `$ne`. Before this, these five bypassed `where()` entirely, so a filter forwarded straight from a request body — `User.atomic(req.body.filter, { $set: { role: "admin" } })` — could still smuggle an operator like `{ role: { $ne: "admin" } }` through as a live query even though `where()` itself was already guarded. Only the FILTER is checked — the update-operator object (`$set`/`$inc`/`$unset`/…) is untouched, since that's meant to carry `$` keys. If you legitimately need operator conditions in the filter, express them through `Model.query().where(...)`
|
|
1826
|
+
**`filter` is sanitized like `where()`.** `atomic()`, `findAndUpdate()`, `findOneAndUpdate()`, `findAndReplace()` and `findOneAndDelete()` all run their `filter` argument through the same `$`-prefixed-key check as `Model.where()` (see [`query-data`](@warlock.js/cascade/query-data/SKILL.md)) and throw `UnsafeFilterError` on a key like `$ne`. Before this, these five bypassed `where()` entirely, so a filter forwarded straight from a request body — `User.atomic(req.body.filter, { $set: { role: "admin" } })` — could still smuggle an operator like `{ role: { $ne: "admin" } }` through as a live query even though `where()` itself was already guarded. Only the FILTER is checked — the update-operator object (`$set`/`$inc`/`$unset`/…) is untouched, since that's meant to carry `$` keys. If you legitimately need operator conditions in the filter, express them through `Model.query().where(...)`, or pass `{ trustedFilter: true }` for a code-authored filter (see below).
|
|
1827
|
+
|
|
1828
|
+
## Upsert, returnDocument, pipelines — the options argument
|
|
1829
|
+
|
|
1830
|
+
```ts
|
|
1831
|
+
// Counter: filter + $inc + $setOnInsert + upsert → the new document, one call
|
|
1832
|
+
const counter = await Counter.findOneAndUpdate(
|
|
1833
|
+
{ key: "signups" },
|
|
1834
|
+
{ $inc: { count: 1 }, $setOnInsert: { startedAt: new Date() } },
|
|
1835
|
+
{ upsert: true }, // returnDocument defaults to "after"
|
|
1836
|
+
);
|
|
1837
|
+
|
|
1838
|
+
// Quota reservation: the conditional filter needs trustedFilter (code-authored only!)
|
|
1839
|
+
const granted = await Quota.atomic(
|
|
1840
|
+
{ id: quotaId, used: { $lt: 10 } },
|
|
1841
|
+
{ $inc: { used: 1 } },
|
|
1842
|
+
{ trustedFilter: true },
|
|
1843
|
+
); // 1 = reserved, 0 = quota full. 50 parallel calls → exactly 10 succeed
|
|
1844
|
+
|
|
1845
|
+
// MongoDB only: pipeline update, $addToSet, arrayFilters
|
|
1846
|
+
await Post.findOneAndUpdate({ slug }, [{ $set: { score: { $add: ["$likes", "$shares"] } } }]);
|
|
1847
|
+
await Post.atomic({ slug }, { $set: { "grades.$[low].score": 50 } }, { arrayFilters: [{ "low.score": { $lt: 50 } }] });
|
|
1848
|
+
```
|
|
1849
|
+
|
|
1850
|
+
- `atomic(filter, update, { upsert?, arrayFilters?, trustedFilter? })` → modified + upserted count.
|
|
1851
|
+
- `findOneAndUpdate(filter, update, { ...same, returnDocument?: "before" | "after" })`.
|
|
1852
|
+
- `findAndUpdate(filter, update, { upsert?, arrayFilters? })`. No `trustedFilter`, because it reads the rows back with `where(filter)`.
|
|
1853
|
+
|
|
1854
|
+
**Postgres.** An upsert runs as `INSERT … ON CONFLICT (target) DO UPDATE … RETURNING *`. The target is the primary key, or a unique index, whose columns are ALL equality keys of the filter. Other filter conditions become `DO UPDATE … WHERE`. If the row exists and fails them, nothing is written and `findOneAndUpdate` returns `null`. The driver throws `UnsupportedUpdateOperationError` (`.operation`, `.driver`) for:
|
|
1855
|
+
- pipeline updates
|
|
1856
|
+
- `arrayFilters`
|
|
1857
|
+
- `$push`, `$pull`, `$addToSet` and unknown operators
|
|
1858
|
+
- `returnDocument: "before"` together with `upsert`
|
|
1859
|
+
- an upsert with no unique target it can find
|
|
1860
|
+
|
|
1861
|
+
It never ignores one of these without throwing.
|
|
1804
1862
|
|
|
1805
1863
|
## Bulk insert — `Model.createMany`
|
|
1806
1864
|
|
|
@@ -1898,7 +1956,7 @@ for (const user of targets) {
|
|
|
1898
1956
|
|
|
1899
1957
|
---
|
|
1900
1958
|
name: query-data
|
|
1901
|
-
description: 'Query records via the model — `.where(field, value)` / `.where(field, op, value)`, `.find(id)` / `.first` / `.all`, `.orderBy`, `.count` / `.exists`, plus `.whereIn` / `.whereBetween` / `.whereLike` / `.pluck` / `.firstOrFail` / scopes via `addScope`. Covers filter safety: `where()` rejects `$`-prefixed keys (`UnsafeFilterError`), `whereRaw()` string form rejects `$where`/`$function`/`$accumulator` (`UnsafeRawExpressionError`), and `whereLike`/`whereStartsWith`/`whereEndsWith`/`whereSearch` match string arguments literally (pass a `RegExp` for pattern semantics). Triggers: `.where`, `.find`, `.first`, `.firstOrFail`, `.all`, `.get`, `.orderBy`, `.exists`, `.whereIn`, `.whereBetween`, `.whereLike`, `.whereRaw`, `addScope`, `escapeRegex`, `likePatternToRegexSource`, `UnsafeFilterError`, `UnsafeRawExpressionError`; "filter by status", "find by id", "fetch active users", "check existence", "search box query", "is where() safe from injection"; typical import `import { Model } from "@warlock.js/cascade"`. Skip: pagination — `@warlock.js/cascade/paginate-results/SKILL.md`; aggregates — `@warlock.js/cascade/aggregate-data/SKILL.md`.'
|
|
1959
|
+
description: 'Query records via the model — `.where(field, value)` / `.where(field, op, value)`, `.find(id)` / `.first` / `.all`, `.orderBy`, `.count` / `.exists`, `.lean()` plain-object reads, plus `.whereIn` / `.whereBetween` / `.whereLike` / `.pluck` / `.firstOrFail` / scopes via `addScope`. Covers filter safety: `where()` rejects `$`-prefixed keys (`UnsafeFilterError`), `whereRaw()` string form rejects `$where`/`$function`/`$accumulator` (`UnsafeRawExpressionError`), and `whereLike`/`whereStartsWith`/`whereEndsWith`/`whereSearch` match string arguments literally (pass a `RegExp` for pattern semantics). Triggers: `.where`, `.find`, `.first`, `.firstOrFail`, `.all`, `.get`, `.orderBy`, `.exists`, `.whereIn`, `.whereBetween`, `.whereLike`, `.whereRaw`, `.lean`, `UnsupportedLeanOperationError`, `addScope`, `escapeRegex`, `likePatternToRegexSource`, `UnsafeFilterError`, `UnsafeRawExpressionError`; "filter by status", "find by id", "fetch active users", "check existence", "search box query", "is where() safe from injection"; typical import `import { Model } from "@warlock.js/cascade"`. Skip: pagination — `@warlock.js/cascade/paginate-results/SKILL.md`; aggregates — `@warlock.js/cascade/aggregate-data/SKILL.md`.'
|
|
1902
1960
|
---
|
|
1903
1961
|
|
|
1904
1962
|
# Query data
|
|
@@ -2002,6 +2060,15 @@ const newest = await User
|
|
|
2002
2060
|
|
|
2003
2061
|
For pagination see [`@warlock.js/cascade/paginate-results/SKILL.md`](@warlock.js/cascade/paginate-results/SKILL.md).
|
|
2004
2062
|
|
|
2063
|
+
## Plain objects — `.lean()`
|
|
2064
|
+
|
|
2065
|
+
```ts
|
|
2066
|
+
const rows = await User.query().where("status", "active").lean().orderBy("id").get();
|
|
2067
|
+
rows[0].name; // typed as UserSchema, not a User instance
|
|
2068
|
+
```
|
|
2069
|
+
|
|
2070
|
+
`.lean()` (anywhere before the terminator) returns the rows as the driver sent them: no `User` instances, no driver casting (a date string stays a string, Mongo `_id` stays an `ObjectId`), no `onFetched` / model `fetched` event. Use it for read-only lists and exports. It still strips `static hidden` fields. `where` / `select` / `orderBy` / `limit` / `first` / `paginate` behave as usual. Adding `.with()` or `.joinWith()` throws `UnsupportedLeanOperationError`: load relations with a second lean query, or drop `.lean()`. About 1.9x faster than a hydrated read for 10k Mongo documents (one run on a busy machine).
|
|
2071
|
+
|
|
2005
2072
|
## Count and existence
|
|
2006
2073
|
|
|
2007
2074
|
```ts
|
package/llms.txt
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
## Skills
|
|
8
8
|
|
|
9
|
-
- [aggregate-data](@warlock.js/cascade/aggregate-data/SKILL.md): Compute aggregates over a query — scalar `.count()` / `.sum(field)` / `.avg` / `.min` / `.max`, plus grouped rollups via the two-arg `.groupBy(fields, { alias: $agg.* })`, portable date-bucketing via `.groupByDate(col, unit, aggregates?)`, the `$agg` helpers (including expression-aware `$agg.sum($expr.mul("price","quantity"))` / `$agg.sumRaw`), and `.having(alias, op, value)` on computed aggregates. Triggers: `.count`, `.sum`, `.avg`, `.min`, `.max`, `.groupBy`, `.groupByDate`, `.having`, `$agg`, `$agg.sum`, `$agg.sumRaw`, `$agg.count`, `$expr`, `$expr.mul`, `$expr.col`, `$expr.lit`; "monthly revenue report", "revenue per month", "X per category", "group by status", "sum price times quantity", "dashboard rollup"; typical import `import { Model, $agg, $expr } from "@warlock.js/cascade"`. Skip: row queries — `@warlock.js/cascade/query-data/SKILL.md`; cached aggregates — `@warlock.js/cache/use-cached-hof/SKILL.md`; competing tools raw SQL `GROUP BY`, `mongoose aggregate`, `prisma` `groupBy`.
|
|
9
|
+
- [aggregate-data](@warlock.js/cascade/aggregate-data/SKILL.md): Compute aggregates over a query — scalar `.count()` / `.sum(field)` / `.avg` / `.min` / `.max`, plus grouped rollups via the two-arg `.groupBy(fields, { alias: $agg.* })`, portable date-bucketing via `.groupByDate(col, unit, aggregates?)`, the `$agg` helpers (including expression-aware `$agg.sum($expr.mul("price","quantity"))` / `$agg.sumRaw`), and `.having(alias, op, value)` on computed aggregates, plus MongoDB pipeline stages `.unwind(field, options?)` / `.addFields(fields)` / pipeline-form `join()` (`UnsupportedQueryOperationError` on Postgres). Triggers: `.unwind`, `.addFields`, `.joinRaw`, `.raw`, `$unwind`, `$lookup`, `.count`, `.sum`, `.avg`, `.min`, `.max`, `.groupBy`, `.groupByDate`, `.having`, `$agg`, `$agg.sum`, `$agg.sumRaw`, `$agg.count`, `$expr`, `$expr.mul`, `$expr.col`, `$expr.lit`; "monthly revenue report", "revenue per month", "X per category", "group by status", "sum price times quantity", "dashboard rollup"; typical import `import { Model, $agg, $expr } from "@warlock.js/cascade"`. Skip: row queries — `@warlock.js/cascade/query-data/SKILL.md`; cached aggregates — `@warlock.js/cache/use-cached-hof/SKILL.md`; competing tools raw SQL `GROUP BY`, `mongoose aggregate`, `prisma` `groupBy`.
|
|
10
10
|
- [alter-migration](@warlock.js/cascade/alter-migration/SKILL.md): Evolve an existing table with `Migration.alter(Model, schema, options?)` — add/drop/rename/modify columns; add/drop regular, unique, expression, full-text, geo, vector, and TTL indexes; add/drop foreign keys and CHECK constraints; write rollbacks with class-form methods in `down()`. Triggers: "alter a table", "add a column to existing table", "drop a column", "rename a column", "add an index", "drop a unique constraint", "change a column type", `Migration.alter`, `dropUnique`, `addIndex`, `addForeign`. Skip: creating a brand-new table — `@warlock.js/cascade/write-migration/SKILL.md`.
|
|
11
11
|
- [cascade-basics](@warlock.js/cascade/cascade-basics/SKILL.md): Start with @warlock.js/cascade ORM — model-first for MongoDB and Postgres, one schema (seal) does triple duty (type / validator / DB shape), model is the query entry point. Triggers: `Model`, `RegisterModel`, `connectToDatabase`, `Infer`, `v.object`; "which cascade skill do I need", "set up the ORM", "define my first model", "model-first ORM"; typical import `import { Model, RegisterModel } from "@warlock.js/cascade"`. Skip: schema vocabulary — `@warlock.js/seal/seal-basics/SKILL.md`; competing libs `mongoose`, `prisma`, `typeorm`, `drizzle`, `sequelize`, `mongodb` driver, `knex`.
|
|
12
12
|
- [configure-delete-strategy](@warlock.js/cascade/configure-delete-strategy/SKILL.md): Pick the delete behavior — `permanent` (hard delete), `soft` (set `deletedAt`, keep the row), `trash` (move to a separate table). Configure via `static deleteStrategy` or `.destroy({ strategy })`; restore via static `Model.restore(id)` / `Model.restoreAll()`. Triggers: `static deleteStrategy`, `.destroy`, `Model.restore`, `Model.restoreAll`, `deletedAtColumn`, `trashTable`; "soft delete users", "restore a deleted record", "GDPR hard delete"; typical import `import { Model } from "@warlock.js/cascade"`. Skip: lifecycle events — `@warlock.js/cascade/subscribe-to-model-events/SKILL.md`; competing libs `mongoose-delete`, `typeorm softRemove`, `sequelize` paranoid.
|
|
@@ -15,8 +15,8 @@
|
|
|
15
15
|
- [manage-data-sources](@warlock.js/cascade/manage-data-sources/SKILL.md): Configure multiple databases — register each via `connectToDatabase({ name, driver, database, isDefault })`, assign a model with `static dataSource = "name"`, route a migration with `dataSource` on the migration class, inspect via `dataSourceRegistry.get(name)` / `getAllDataSources()`. The first (or `isDefault: true`) source is the default. Triggers: `connectToDatabase`, `dataSourceRegistry`, `dataSourceRegistry.get`, `getAllDataSources`, `static dataSource`; "multi-database app", "per-tenant DB", "analytics on separate DB"; typical import `import { connectToDatabase, dataSourceRegistry } from "@warlock.js/cascade"`. Skip: per-source migrations — `@warlock.js/cascade/write-migration/SKILL.md`; transaction scope — `@warlock.js/cascade/manage-transactions/SKILL.md`; competing patterns `mongoose.createConnection`, `typeorm` `DataSource`, `prisma` multi-schema.
|
|
16
16
|
- [manage-transactions](@warlock.js/cascade/manage-transactions/SKILL.md): Wrap multi-statement work in `transaction(async () => {...})` — rollback on throw, commit on resolve, optional `isolation` level (Postgres), per-`dataSource` scope. Also the home for row locking (`lockForUpdate({ skipLocked })` → `SELECT ... FOR UPDATE [SKIP LOCKED | NOWAIT]`, Postgres-only), transaction-aware raw SQL (`Model.raw` / `DataSource.raw` → `RawQueryResult`) and Postgres native-array column handling (`JSONB[]` / `TEXT[]` / `INTEGER[]` auto-detected via schema introspection on connect; `nativeArrayColumns` is an optional override). Postgres native; MongoDB requires replica set. Triggers: `transaction`, `isolation`, `SERIALIZABLE`, `READ COMMITTED`, nested transaction, flat nesting, nested savepoints, `lockForUpdate`, `skipLocked`, `FOR UPDATE`, `SKIP LOCKED`, `NOWAIT`, row lock, pessimistic lock, queue claim, `Model.raw`, `DataSource.raw`, raw SQL, `RawQueryResult`, `nativeArrayColumns`, `JSONB[]`, `TEXT[]`; "wrap two writes atomically", "transfer balance between accounts", "rollback on error", "MongoDB replica set transactions", "lock rows so workers don't double-process", "claim jobs from a table", "run raw SQL", "native array column", "malformed array literal", "array column not saving", "nested transaction not visible", "foreign key violation on insert inside transaction", "service transaction inside seeder"; typical import `import { transaction } from "@warlock.js/cascade"`. Skip: single-row atomic ops without a transaction — `@warlock.js/cascade/perform-atomic-ops/SKILL.md`; per-source scope — `@warlock.js/cascade/manage-data-sources/SKILL.md`; competing patterns `mongoose.startSession`, `pg` `BEGIN` manually, `prisma.$transaction`, `typeorm` `QueryRunner`.
|
|
17
17
|
- [paginate-results](@warlock.js/cascade/paginate-results/SKILL.md): Paginate query results — `.paginate({page, limit, filter?})` for offset (returns `data` + `pagination` total/page/limit/pages), `.cursorPaginate({limit, cursor})` for very large datasets, `.chunk(size, callback)` for streaming. Triggers: `.paginate`, `.cursorPaginate`, `.chunk`, `nextCursor`, `hasMore`, `pagination.total`; "paginate the list", "infinite scroll / load more", "stream a large table", "page 2 of users"; typical import `import { Model } from "@warlock.js/cascade"`. Skip: filter chain — `@warlock.js/cascade/query-data/SKILL.md`; eager loading on pages — `@warlock.js/cascade/define-relations/SKILL.md`; competing libs `mongoose-paginate-v2`, `prisma` cursor, `typeorm-pagination`.
|
|
18
|
-
- [perform-atomic-ops](@warlock.js/cascade/perform-atomic-ops/SKILL.md): Avoid races on concurrent writes — `Model.increase(filter, field, n)` / `Model.decrease` for atomic counters, `Model.atomic(filter, ops)` for arbitrary mutations (`$set` / `$inc` / `$push` / `$pull`), `Model.createMany` / `Model.findAndUpdate` / `Model.delete` for bulk. `atomic()` / `findAndUpdate()` / `findOneAndUpdate()` / `findAndReplace()` / `findOneAndDelete()` sanitize their `filter` argument — `$`-prefixed keys throw `UnsafeFilterError`, same check as `where()`. Triggers: `Model.increase`, `Model.decrease`, `Model.atomic`, `Model.createMany`, `createMany bulk`, `batchSize`, `Model.findAndUpdate`, `Model.findOneAndUpdate`, `Model.findAndReplace`, `Model.findOneAndDelete`, `Model.delete`, `$inc`, `$set`, `UnsafeFilterError`; "increment counter under concurrency", "bulk insert without N+1", "fast bulk insert", "insert thousands of rows", "atomic update without loading", "is atomic() safe with a request body filter"; typical import `import { Model } from "@warlock.js/cascade"`. Skip: multi-row atomicity — `@warlock.js/cascade/manage-transactions/SKILL.md`; competing patterns `mongoose findOneAndUpdate`, `pg` `UPDATE ... SET x = x + 1`.
|
|
19
|
-
- [query-data](@warlock.js/cascade/query-data/SKILL.md): Query records via the model — `.where(field, value)` / `.where(field, op, value)`, `.find(id)` / `.first` / `.all`, `.orderBy`, `.count` / `.exists`, plus `.whereIn` / `.whereBetween` / `.whereLike` / `.pluck` / `.firstOrFail` / scopes via `addScope`. Covers filter safety: `where()` rejects `$`-prefixed keys (`UnsafeFilterError`), `whereRaw()` string form rejects `$where`/`$function`/`$accumulator` (`UnsafeRawExpressionError`), and `whereLike`/`whereStartsWith`/`whereEndsWith`/`whereSearch` match string arguments literally (pass a `RegExp` for pattern semantics). Triggers: `.where`, `.find`, `.first`, `.firstOrFail`, `.all`, `.get`, `.orderBy`, `.exists`, `.whereIn`, `.whereBetween`, `.whereLike`, `.whereRaw`, `addScope`, `escapeRegex`, `likePatternToRegexSource`, `UnsafeFilterError`, `UnsafeRawExpressionError`; "filter by status", "find by id", "fetch active users", "check existence", "search box query", "is where() safe from injection"; typical import `import { Model } from "@warlock.js/cascade"`. Skip: pagination — `@warlock.js/cascade/paginate-results/SKILL.md`; aggregates — `@warlock.js/cascade/aggregate-data/SKILL.md`.
|
|
18
|
+
- [perform-atomic-ops](@warlock.js/cascade/perform-atomic-ops/SKILL.md): Avoid races on concurrent writes — `Model.increase(filter, field, n)` / `Model.decrease` for atomic counters, `Model.atomic(filter, ops, options?)` for arbitrary mutations (`$set` / `$inc` / `$push` / `$pull` / `$addToSet` / `$setOnInsert`, pipeline updates, `upsert`, `returnDocument`, `arrayFilters`, `trustedFilter`), `Model.createMany` / `Model.findAndUpdate` / `Model.delete` for bulk. `atomic()` / `findAndUpdate()` / `findOneAndUpdate()` / `findAndReplace()` / `findOneAndDelete()` sanitize their `filter` argument — `$`-prefixed keys throw `UnsafeFilterError`, same check as `where()`. Triggers: `Model.increase`, `Model.decrease`, `Model.atomic`, `Model.createMany`, `createMany bulk`, `batchSize`, `Model.findAndUpdate`, `Model.findOneAndUpdate`, `Model.findAndReplace`, `Model.findOneAndDelete`, `Model.delete`, `$inc`, `$set`, `UnsafeFilterError`, `UnsupportedUpdateOperationError`, `upsert`, `returnDocument`, `$setOnInsert`; "upsert a counter", "reserve quota atomically", "increment counter under concurrency", "bulk insert without N+1", "fast bulk insert", "insert thousands of rows", "atomic update without loading", "is atomic() safe with a request body filter"; typical import `import { Model } from "@warlock.js/cascade"`. Skip: multi-row atomicity — `@warlock.js/cascade/manage-transactions/SKILL.md`; competing patterns `mongoose findOneAndUpdate`, `pg` `UPDATE ... SET x = x + 1`.
|
|
19
|
+
- [query-data](@warlock.js/cascade/query-data/SKILL.md): Query records via the model — `.where(field, value)` / `.where(field, op, value)`, `.find(id)` / `.first` / `.all`, `.orderBy`, `.count` / `.exists`, `.lean()` plain-object reads, plus `.whereIn` / `.whereBetween` / `.whereLike` / `.pluck` / `.firstOrFail` / scopes via `addScope`. Covers filter safety: `where()` rejects `$`-prefixed keys (`UnsafeFilterError`), `whereRaw()` string form rejects `$where`/`$function`/`$accumulator` (`UnsafeRawExpressionError`), and `whereLike`/`whereStartsWith`/`whereEndsWith`/`whereSearch` match string arguments literally (pass a `RegExp` for pattern semantics). Triggers: `.where`, `.find`, `.first`, `.firstOrFail`, `.all`, `.get`, `.orderBy`, `.exists`, `.whereIn`, `.whereBetween`, `.whereLike`, `.whereRaw`, `.lean`, `UnsupportedLeanOperationError`, `addScope`, `escapeRegex`, `likePatternToRegexSource`, `UnsafeFilterError`, `UnsafeRawExpressionError`; "filter by status", "find by id", "fetch active users", "check existence", "search box query", "is where() safe from injection"; typical import `import { Model } from "@warlock.js/cascade"`. Skip: pagination — `@warlock.js/cascade/paginate-results/SKILL.md`; aggregates — `@warlock.js/cascade/aggregate-data/SKILL.md`.
|
|
20
20
|
- [run-cascade-cli](@warlock.js/cascade/run-cascade-cli/SKILL.md): Cascade's standalone `cascade` binary + the Operations API it wraps — `cascade migrate` / `migrate:list` / `migrate:rollback` / `migrate:export-sql`, and `runMigrations` / `rollbackMigrations` / `freshMigrate` / `exportMigrationsSQL` / `listExecutedMigrations` / `createDatabase` / `dropAllTables` / `migrationRunner`. Triggers: `cascade migrate`, `migrate:list`, `migrate:rollback`, `migrate:export-sql`, `runMigrations`, `rollbackMigrations`, `freshMigrate`, `exportMigrationsSQL`, `listExecutedMigrations`, `migrationRunner`; "run migrations in deploy/CI", "reset DB for tests", "programmatic migration", "foreign key constraint cannot be implemented", `CASCADE_PRIMARY_KEY`; typical import `import { runMigrations, migrationRunner } from "@warlock.js/cascade"`. Skip: writing migration files — `@warlock.js/cascade/write-migration/SKILL.md`; competing tools `knex migrate:latest`, `prisma migrate deploy`, `typeorm migration:run`.
|
|
21
21
|
- [search-by-vector](@warlock.js/cascade/search-by-vector/SKILL.md): Vector similarity search via `.similarTo(column, embedding, alias?)` — adds a similarity `score` column and orders by vector distance so the index is used; cap results with `.limit()`. Postgres uses pgvector (IVFFlat index via `this.vectorIndex`); MongoDB needs Atlas. Schema: `this.vector(column, dimensions)` + `this.vectorIndex(column, { dimensions, similarity })`. Triggers: `.similarTo`, `this.vector`, `this.vectorIndex`, `.whereFullText`, pgvector; "semantic search", "RAG retrieval", "find similar articles", "hybrid vector + full-text"; typical import `import { Model } from "@warlock.js/cascade"`. Skip: query basics — `@warlock.js/cascade/query-data/SKILL.md`; semantic cache — `@warlock.js/cache/use-cache-similarity/SKILL.md`; competing libs `pgvector` directly, `chromadb`, `pinecone`, `weaviate`, `qdrant`.
|
|
22
22
|
- [subscribe-to-model-events](@warlock.js/cascade/subscribe-to-model-events/SKILL.md): Hook into model lifecycle events — `saving` / `saved`, `creating` / `created`, `updating` / `updated`, `validating` / `validated`, `deleting` / `deleted`, `restoring` / `restored`, `fetching` / `fetched`. Per-model `Model.on(event, fn)` or global via `Model.globalEvents()`. Triggers: `Model.on`, `Model.off`, `saving`, `saved`, `created`, `updated`, `deleting`, `deleted`, `restored`; "audit log on save", "notify on change", "denormalize into search index"; typical import `import { Model } from "@warlock.js/cascade"`. Skip: dirty tracking — `@warlock.js/cascade/track-changes/SKILL.md`; competing libs `mongoose` middleware, `typeorm` subscribers, `prisma` extensions.
|