@warlock.js/cascade 4.6.1 → 4.7.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 +21 -0
- package/cjs/index.cjs +277 -26
- package/cjs/index.cjs.map +1 -1
- package/esm/contracts/database-driver.contract.d.mts +8 -0
- package/esm/contracts/database-driver.contract.d.mts.map +1 -1
- package/esm/contracts/index.d.mts +1 -1
- package/esm/contracts/query-builder.contract.d.mts +36 -1
- package/esm/contracts/query-builder.contract.d.mts.map +1 -1
- package/esm/drivers/mongodb/mongodb-driver.d.mts +5 -0
- package/esm/drivers/mongodb/mongodb-driver.d.mts.map +1 -1
- package/esm/drivers/mongodb/mongodb-driver.mjs +5 -0
- package/esm/drivers/mongodb/mongodb-driver.mjs.map +1 -1
- package/esm/drivers/mongodb/mongodb-migration-driver.d.mts +4 -0
- package/esm/drivers/mongodb/mongodb-migration-driver.d.mts.map +1 -1
- package/esm/drivers/mongodb/mongodb-migration-driver.mjs +5 -2
- package/esm/drivers/mongodb/mongodb-migration-driver.mjs.map +1 -1
- package/esm/drivers/mongodb/mongodb-query-builder.d.mts +15 -0
- package/esm/drivers/mongodb/mongodb-query-builder.d.mts.map +1 -1
- package/esm/drivers/mongodb/mongodb-query-builder.mjs +24 -0
- package/esm/drivers/mongodb/mongodb-query-builder.mjs.map +1 -1
- package/esm/drivers/mongodb/mongodb-query-parser.d.mts +16 -0
- package/esm/drivers/mongodb/mongodb-query-parser.d.mts.map +1 -1
- package/esm/drivers/mongodb/mongodb-query-parser.mjs +33 -1
- package/esm/drivers/mongodb/mongodb-query-parser.mjs.map +1 -1
- package/esm/drivers/postgres/postgres-driver.d.mts +16 -0
- package/esm/drivers/postgres/postgres-driver.d.mts.map +1 -1
- package/esm/drivers/postgres/postgres-driver.mjs +65 -1
- package/esm/drivers/postgres/postgres-driver.mjs.map +1 -1
- package/esm/drivers/postgres/postgres-query-builder.d.mts +14 -3
- package/esm/drivers/postgres/postgres-query-builder.d.mts.map +1 -1
- package/esm/drivers/postgres/postgres-query-builder.mjs +44 -8
- package/esm/drivers/postgres/postgres-query-builder.mjs.map +1 -1
- package/esm/drivers/postgres/postgres-query-parser.d.mts +6 -1
- package/esm/drivers/postgres/postgres-query-parser.d.mts.map +1 -1
- package/esm/drivers/postgres/postgres-query-parser.mjs +13 -0
- package/esm/drivers/postgres/postgres-query-parser.mjs.map +1 -1
- package/esm/drivers/postgres/postgres-sql-serializer.mjs +15 -4
- package/esm/drivers/postgres/postgres-sql-serializer.mjs.map +1 -1
- package/esm/index.d.mts +2 -2
- package/esm/migration/migration-runner.d.mts.map +1 -1
- package/esm/migration/migration-runner.mjs +25 -3
- package/esm/migration/migration-runner.mjs.map +1 -1
- package/esm/migration/migration.d.mts +6 -3
- package/esm/migration/migration.d.mts.map +1 -1
- package/esm/migration/migration.mjs +6 -3
- package/esm/migration/migration.mjs.map +1 -1
- package/esm/model/methods/scope-methods.mjs +18 -4
- package/esm/model/methods/scope-methods.mjs.map +1 -1
- package/esm/model/model.d.mts +6 -0
- package/esm/model/model.d.mts.map +1 -1
- package/esm/model/model.mjs +6 -0
- package/esm/model/model.mjs.map +1 -1
- package/esm/query-builder/query-builder.d.mts +12 -1
- package/esm/query-builder/query-builder.d.mts.map +1 -1
- package/esm/query-builder/query-builder.mjs +19 -0
- package/esm/query-builder/query-builder.mjs.map +1 -1
- package/llms-full.txt +31 -1
- package/llms.txt +1 -1
- package/package.json +4 -4
- package/skills/README.md +1 -1
- package/skills/manage-transactions/SKILL.md +31 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"query-builder.d.mts","names":[],"sources":["../../../../../../../@warlock.js/cascade/src/query-builder/query-builder.ts"],"mappings":";;;;;;;
|
|
1
|
+
{"version":3,"file":"query-builder.d.mts","names":[],"sources":["../../../../../../../@warlock.js/cascade/src/query-builder/query-builder.ts"],"mappings":"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"}
|
|
@@ -1015,6 +1015,25 @@ var QueryBuilder = class QueryBuilder {
|
|
|
1015
1015
|
return this.limit(value);
|
|
1016
1016
|
}
|
|
1017
1017
|
/**
|
|
1018
|
+
* Lock the selected rows for update (`SELECT ... FOR UPDATE`).
|
|
1019
|
+
*
|
|
1020
|
+
* `skipLocked` skips rows other transactions hold locks on (concurrent
|
|
1021
|
+
* queue-claim shape); `noWait` errors immediately instead of waiting. The
|
|
1022
|
+
* two are mutually exclusive. Only meaningful inside a transaction.
|
|
1023
|
+
*
|
|
1024
|
+
* SQL drivers emit the locking clause; drivers without row locking
|
|
1025
|
+
* (MongoDB) override this to throw.
|
|
1026
|
+
*/
|
|
1027
|
+
lockForUpdate(options) {
|
|
1028
|
+
if (options?.skipLocked && options?.noWait) throw new Error("lockForUpdate: `skipLocked` and `noWait` are mutually exclusive.");
|
|
1029
|
+
this.addOperation("lock", {
|
|
1030
|
+
mode: "update",
|
|
1031
|
+
skipLocked: options?.skipLocked ?? false,
|
|
1032
|
+
noWait: options?.noWait ?? false
|
|
1033
|
+
});
|
|
1034
|
+
return this;
|
|
1035
|
+
}
|
|
1036
|
+
/**
|
|
1018
1037
|
* GROUP BY clause.
|
|
1019
1038
|
* @example q.groupBy("status")
|
|
1020
1039
|
* @example q.groupBy(["year", "month"])
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"query-builder.mjs","names":[],"sources":["../../../../../../../@warlock.js/cascade/src/query-builder/query-builder.ts"],"sourcesContent":["/**\n * Pure Query Builder Base Class\n *\n * Driver-agnostic operation recorder. All fluent methods push typed entries into\n * `operations[]`. No SQL, no driver references, no table property, no execution.\n *\n * ┌─────────────────────────────────────────────────┐\n * │ Usage contexts │\n * │ (a) Subclassed — PG / Mongo / MySQL / … │\n * │ (b) Instantiated directly (new QueryBuilder()) │\n * │ inside callbacks for: │\n * │ • nested where groups │\n * │ • joinWith constraints │\n * │ • whereExists / whereHas subqueries │\n * └─────────────────────────────────────────────────┘\n *\n * Design rules:\n * - `table` / alias are NOT here — the parser gets them from the executor.\n * - `opIndex` is protected so subclasses can rebuild after direct mutation.\n * - Op type names are stable — parsers switch on them; no renaming without\n * a parser update.\n * - OR-variants keep distinct op types (orWhere, orWhereColumn, …) so existing\n * parsers that switch on type need no changes.\n * - `joinWith` eagerly resolves callbacks → subOps at record time so the\n * driver executor receives a plain data structure, not a live function.\n *\n * @module cascade/query-builder\n */\n\nimport type {\n GroupByInput,\n HavingInput,\n JoinOptions,\n OrderDirection,\n RawExpression,\n WhereCallback,\n WhereObject,\n WhereOperator,\n} from \"../contracts/query-builder.contract\";\n\n// ============================================================================\n// TYPES\n// ============================================================================\n\n/**\n * A single recorded query operation.\n * `type` is the discriminator; `data` carries all parameters.\n */\nexport type Op = {\n readonly type: string;\n readonly data: Record<string, unknown>;\n};\n\n/**\n * Constraint value accepted by `joinWith()`.\n *\n * - `string` → comma-separated column shorthand: `\"id,name,createdAt\"`\n * - `fn` → callback receives a bare QueryBuilder to record sub-ops\n *\n * @example\n * joinWith({ actions: \"id,status\" })\n * joinWith({ actions: q => q.where(\"status\", \"pending\").limit(5) })\n */\nexport type JoinWithConstraint = string | ((q: QueryBuilder) => void);\n\n// ============================================================================\n// QUERY BUILDER — CONCRETE, DIRECTLY INSTANTIABLE\n// ============================================================================\n\n/**\n * Pure, driver-agnostic query builder.\n *\n * Records operations in `operations[]`. Subclasses own execution, parsing, and\n * driver-specific clause generation. Safe to instantiate directly inside\n * callbacks where only operation recording is needed.\n *\n * @example\n * ```ts\n * // Driver subclass usage:\n * const users = await User.query()\n * .select([\"id\", \"name\"])\n * .where(\"status\", \"active\")\n * .where(q => q.where(\"role\", \"admin\").orWhere(\"role\", \"mod\"))\n * .orderBy(\"createdAt\", \"desc\")\n * .limit(10)\n * .get();\n *\n * // Direct instantiation (callback context — no driver needed):\n * joinWith({ actions: q => q.where(\"status\", \"pending\").limit(5) });\n * // The sub-QB's operations[] are captured and stored in the joinWith op data.\n * ```\n */\nexport class QueryBuilder<T = unknown> {\n // ════════════════════════════════════════════════════════\n // OPERATION STORE\n // ════════════════════════════════════════════════════════\n\n /** Flat, ordered list of recorded operations. Public for parser access. */\n public operations: Op[] = [];\n\n /**\n * type → ordered list of indices into `operations[]`.\n *\n * Protected (not private) so:\n * - `rebuildIndex()` can reset it after direct `operations[]` mutation.\n * - Subclasses can inspect it without unsafe casts.\n *\n * External consumers should use `getOps(type)` instead.\n */\n protected opIndex: Map<string, number[]> = new Map();\n\n // ════════════════════════════════════════════════════════\n // SCOPE STATE (injected by Model.query(), consumed before execution)\n // ════════════════════════════════════════════════════════\n\n /** Global scope definitions injected by Model.query(). Keyed by scope name. */\n public pendingGlobalScopes?: Map<string, any>;\n /** Local scope callbacks injected by Model.query(). Applied on demand via scope(). */\n public availableLocalScopes?: Map<string, (...args: any[]) => void>;\n /** Names of global scopes that have been intentionally disabled. */\n public disabledGlobalScopes: Set<string> = new Set();\n /** True once the driver subclass has applied pending scopes. */\n public scopesApplied = false;\n\n // ════════════════════════════════════════════════════════\n // RELATION STATE (consumed by driver subclass at execute time)\n // ════════════════════════════════════════════════════════\n\n /** Relations to eager-load via separate queries. */\n public eagerLoadRelations: Map<string, boolean | ((query: any) => void)> = new Map();\n /** Count expressions to emit per result row, keyed by output column alias. */\n public countRelations: Map<string, { relation: string; constraintOps?: Op[] }> = new Map();\n /** Relation definition map injected from the owning Model. */\n public relationDefinitions?: Record<string, any>;\n /** The Model class reference, required for relation resolution. */\n public modelClass?: any;\n\n // ════════════════════════════════════════════════════════\n // CORE INTERNALS\n // ════════════════════════════════════════════════════════\n\n /**\n * Append an operation to `operations[]` and update `opIndex`.\n * Every fluent method calls this.\n */\n protected addOperation(type: string, data: Record<string, unknown>): void {\n const idx = this.operations.length;\n this.operations.push({ type, data });\n const list = this.opIndex.get(type);\n if (list) {\n list.push(idx);\n } else {\n this.opIndex.set(type, [idx]);\n }\n }\n\n /**\n * Return all recorded operations of the specified types in original\n * insertion order.\n *\n * @example\n * builder.getOps(\"where\", \"orWhere\", \"whereIn\")\n */\n public getOps(...types: string[]): Op[] {\n if (types.length === 1) {\n return (this.opIndex.get(types[0]) ?? []).map((i) => this.operations[i]);\n }\n const result: Array<{ idx: number; op: Op }> = [];\n for (const type of types) {\n for (const idx of this.opIndex.get(type) ?? []) {\n result.push({ idx, op: this.operations[idx] });\n }\n }\n return result.sort((a, b) => a.idx - b.idx).map((r) => r.op);\n }\n\n /**\n * Rebuild `opIndex` from scratch.\n *\n * Call this after any direct mutation of `this.operations[]` (e.g. scope\n * injection, joinWith consumption in the executor, clone post-processing).\n */\n public rebuildIndex(): void {\n this.opIndex = new Map();\n for (let i = 0; i < this.operations.length; i++) {\n const type = this.operations[i].type;\n const list = this.opIndex.get(type);\n if (list) {\n list.push(i);\n } else {\n this.opIndex.set(type, [i]);\n }\n }\n }\n\n /**\n * Factory for sub-QueryBuilders used inside callbacks.\n *\n * Override in driver subclasses to return a driver-typed instance, so that\n * driver-specific methods (e.g. `whereArrayContains`) are available inside\n * nested `where(q => ...)` / `whereHas` / `joinWith` callbacks.\n *\n * @example\n * // In PostgresQueryBuilder:\n * protected override subQuery(): QueryBuilder {\n * return new PostgresQueryBuilder(\"__sub__\", this.dataSource);\n * }\n */\n protected subQuery(): QueryBuilder {\n return new QueryBuilder();\n }\n\n /**\n * Shallow-clone this builder — copies operations, opIndex, and all shared state.\n *\n * Subclasses MUST call `super.clone()` and then copy their own fields\n * (dataSource, joinRelations, …).\n */\n public clone(): this {\n const cloned = Object.create(Object.getPrototypeOf(this)) as this;\n cloned.operations = [...this.operations];\n cloned.opIndex = new Map(Array.from(this.opIndex.entries()).map(([k, v]) => [k, [...v]]));\n cloned.pendingGlobalScopes = this.pendingGlobalScopes;\n cloned.availableLocalScopes = this.availableLocalScopes;\n cloned.disabledGlobalScopes = new Set(this.disabledGlobalScopes);\n cloned.scopesApplied = this.scopesApplied;\n cloned.eagerLoadRelations = new Map(this.eagerLoadRelations);\n cloned.countRelations = new Map(this.countRelations);\n cloned.relationDefinitions = this.relationDefinitions;\n cloned.modelClass = this.modelClass;\n return cloned;\n }\n\n // ════════════════════════════════════════════════════════\n // SCOPES\n // ════════════════════════════════════════════════════════\n\n /** Disable one or more named global scopes for this query. */\n public withoutGlobalScope(...scopeNames: string[]): this {\n scopeNames.forEach((name) => this.disabledGlobalScopes.add(name));\n return this;\n }\n\n /** Disable ALL pending global scopes for this query. */\n public withoutGlobalScopes(): this {\n this.pendingGlobalScopes?.forEach((_, name) => this.disabledGlobalScopes.add(name));\n return this;\n }\n\n /**\n * Apply a registered local scope by name.\n * @throws if no local scopes are available or the named scope is not found\n */\n public scope(scopeName: string, ...args: unknown[]): this {\n if (!this.availableLocalScopes) {\n throw new Error(\"No local scopes available on this query builder.\");\n }\n const cb = this.availableLocalScopes.get(scopeName);\n if (!cb) throw new Error(`Local scope \"${scopeName}\" not found.`);\n cb(this, ...args);\n return this;\n }\n\n // ════════════════════════════════════════════════════════\n // WHERE CLAUSES — CORE\n // ════════════════════════════════════════════════════════\n\n /**\n * Add a WHERE clause (AND).\n *\n * @example\n * q.where(\"status\", \"active\")\n * q.where(\"age\", \">\", 18)\n * q.where({ role: \"admin\", active: true })\n * q.where(q => q.where(\"a\", 1).orWhere(\"b\", 2))\n */\n public where(field: string, value: unknown): this;\n public where(field: string, operator: WhereOperator, value: unknown): this;\n public where(conditions: WhereObject): this;\n public where(callback: WhereCallback<T>): this;\n public where(...args: unknown[]): this {\n if (args.length === 1 && typeof args[0] === \"function\") {\n const sub = this.subQuery();\n (args[0] as (q: QueryBuilder) => void)(sub);\n this.addOperation(\"where\", { nested: sub.operations });\n } else if (args.length === 1 && typeof args[0] === \"object\" && args[0] !== null) {\n for (const [key, value] of Object.entries(args[0] as WhereObject)) {\n this.addOperation(\"where\", { field: key, operator: \"=\", value });\n }\n } else if (args.length === 2) {\n this.addOperation(\"where\", { field: args[0], operator: \"=\", value: args[1] });\n } else {\n this.addOperation(\"where\", { field: args[0], operator: args[1], value: args[2] });\n }\n return this;\n }\n\n /**\n * Add an OR WHERE clause.\n *\n * @example\n * q.where(\"role\", \"admin\").orWhere(\"role\", \"mod\")\n */\n public orWhere(field: string, value: unknown): this;\n public orWhere(field: string, operator: WhereOperator, value: unknown): this;\n public orWhere(conditions: WhereObject): this;\n public orWhere(callback: WhereCallback<T>): this;\n public orWhere(...args: unknown[]): this {\n if (args.length === 1 && typeof args[0] === \"function\") {\n const sub = this.subQuery();\n (args[0] as (q: QueryBuilder) => void)(sub);\n this.addOperation(\"orWhere\", { nested: sub.operations });\n } else if (args.length === 1 && typeof args[0] === \"object\" && args[0] !== null) {\n for (const [key, value] of Object.entries(args[0] as WhereObject)) {\n this.addOperation(\"orWhere\", { field: key, operator: \"=\", value });\n }\n } else if (args.length === 2) {\n this.addOperation(\"orWhere\", { field: args[0], operator: \"=\", value: args[1] });\n } else {\n this.addOperation(\"orWhere\", { field: args[0], operator: args[1], value: args[2] });\n }\n return this;\n }\n\n /**\n * Raw WHERE expression in the target dialect (AND).\n *\n * @example\n * q.whereRaw(\"age > ? AND role = ?\", [18, \"admin\"]) // SQL\n * q.whereRaw({ $expr: { $gt: [\"$stock\", \"$reserved\"] } }) // MongoDB\n */\n public whereRaw(expression: RawExpression, bindings?: unknown[]): this {\n this.addOperation(\"whereRaw\", { expression, bindings: bindings ?? [] });\n return this;\n }\n\n /** Raw OR WHERE expression. */\n public orWhereRaw(expression: RawExpression, bindings?: unknown[]): this {\n this.addOperation(\"orWhereRaw\", { expression, bindings: bindings ?? [] });\n return this;\n }\n\n // ════════════════════════════════════════════════════════\n // WHERE CLAUSES — COLUMN COMPARISONS\n // ════════════════════════════════════════════════════════\n\n /**\n * Compare two columns directly (AND).\n * @example q.whereColumn(\"stock\", \">\", \"reserved\")\n */\n public whereColumn(first: string, operator: WhereOperator, second: string): this {\n this.addOperation(\"whereColumn\", { first, operator, second });\n return this;\n }\n\n /** Compare two columns directly (OR). */\n public orWhereColumn(first: string, operator: WhereOperator, second: string): this {\n this.addOperation(\"orWhereColumn\", { first, operator, second });\n return this;\n }\n\n /** Compare multiple column pairs in one call. */\n public whereColumns(\n comparisons: Array<[left: string, operator: WhereOperator, right: string]>,\n ): this {\n for (const [left, operator, right] of comparisons) {\n this.whereColumn(left, operator, right);\n }\n return this;\n }\n\n /**\n * Field value must fall between two other column values.\n * Stored as a `whereBetween` op with `useColumns: true` so the SQL parser\n * knows to quote the values as identifiers rather than bind them.\n */\n public whereBetweenColumns(field: string, lowerColumn: string, upperColumn: string): this {\n this.addOperation(\"whereBetween\", { field, lowerColumn, upperColumn, useColumns: true });\n return this;\n }\n\n // ════════════════════════════════════════════════════════\n // WHERE CLAUSES — STANDARD COMPARISON OPERATORS\n // ════════════════════════════════════════════════════════\n\n /** WHERE field IN values. */\n public whereIn(field: string, values: unknown[]): this {\n this.addOperation(\"whereIn\", { field, values });\n return this;\n }\n\n /** WHERE field NOT IN values. */\n public whereNotIn(field: string, values: unknown[]): this {\n this.addOperation(\"whereNotIn\", { field, values });\n return this;\n }\n\n /** WHERE field IS NULL. */\n public whereNull(field: string): this {\n this.addOperation(\"whereNull\", { field });\n return this;\n }\n\n /** WHERE field IS NOT NULL. */\n public whereNotNull(field: string): this {\n this.addOperation(\"whereNotNull\", { field });\n return this;\n }\n\n /** WHERE field BETWEEN low AND high. */\n public whereBetween(field: string, range: [unknown, unknown]): this {\n this.addOperation(\"whereBetween\", { field, range });\n return this;\n }\n\n /** WHERE field NOT BETWEEN low AND high. */\n public whereNotBetween(field: string, range: [unknown, unknown]): this {\n this.addOperation(\"whereNotBetween\", { field, range });\n return this;\n }\n\n // ════════════════════════════════════════════════════════\n // WHERE CLAUSES — PATTERN MATCHING\n // ════════════════════════════════════════════════════════\n\n /**\n * LIKE pattern match (AND).\n * @example q.whereLike(\"email\", \"%@gmail.com\")\n */\n public whereLike(field: string, pattern: RegExp | string): this {\n const patternStr = pattern instanceof RegExp ? pattern.source : pattern;\n this.addOperation(\"whereLike\", { field, pattern: patternStr });\n return this;\n }\n\n /** NOT LIKE pattern match. */\n public whereNotLike(field: string, pattern: RegExp | string): this {\n const patternStr = pattern instanceof RegExp ? pattern.source : pattern;\n this.addOperation(\"whereNotLike\", { field, pattern: patternStr });\n return this;\n }\n\n /** Starts with a prefix. */\n public whereStartsWith(field: string, value: string | number): this {\n return this.whereLike(field, `${value}%`);\n }\n\n /** Does NOT start with a prefix. */\n public whereNotStartsWith(field: string, value: string | number): this {\n return this.whereNotLike(field, `${value}%`);\n }\n\n /** Ends with a suffix. */\n public whereEndsWith(field: string, value: string | number): this {\n return this.whereLike(field, `%${value}`);\n }\n\n /** Does NOT end with a suffix. */\n public whereNotEndsWith(field: string, value: string | number): this {\n return this.whereNotLike(field, `%${value}`);\n }\n\n // ════════════════════════════════════════════════════════\n // WHERE CLAUSES — DATE/TIME PARTIALS\n // ════════════════════════════════════════════════════════\n\n /**\n * Match on date portion only (time ignored).\n * @example q.whereDate(\"createdAt\", \"2024-05-01\")\n */\n public whereDate(field: string, value: Date | string): this {\n this.addOperation(\"whereDate\", { field, value });\n return this;\n }\n\n /** Alias for whereDate. */\n public whereDateEquals(field: string, value: Date | string): this {\n return this.whereDate(field, value);\n }\n\n /** Field date is before value. */\n public whereDateBefore(field: string, value: Date | string): this {\n this.addOperation(\"whereDateBefore\", { field, value });\n return this;\n }\n\n /** Field date is after value. */\n public whereDateAfter(field: string, value: Date | string): this {\n this.addOperation(\"whereDateAfter\", { field, value });\n return this;\n }\n\n /** Field date is within a range [from, to]. */\n public whereDateBetween(field: string, range: [Date | string, Date | string]): this {\n this.addOperation(\"whereDateBetween\", { field, range });\n return this;\n }\n\n /** Field date is NOT within a range. */\n public whereDateNotBetween(field: string, range: [Date | string, Date | string]): this {\n this.addOperation(\"whereNotBetween\", { field, range });\n return this;\n }\n\n /**\n * Match on the time portion of a datetime field.\n * Emits a `whereRaw` op with a driver-agnostic marker; the driver parser\n * rewrites it to the appropriate SQL (`TIME(field) = ?`) or Mongo expression.\n */\n public whereTime(field: string, value: string): this {\n this.addOperation(\"whereRaw\", {\n expression: `TIME(${field}) = ?`,\n bindings: [value],\n });\n return this;\n }\n\n /**\n * Day-of-month from a date field (1–31).\n * Uses a `whereRaw` op so SQL parsers get the `EXTRACT` expression directly.\n * MongoDB drivers override to emit `$dayOfMonth`.\n */\n public whereDay(field: string, value: number): this {\n this.addOperation(\"whereRaw\", {\n expression: `EXTRACT(DAY FROM ${field}) = ?`,\n bindings: [value],\n });\n return this;\n }\n\n /** Month extracted from a date field (1–12). */\n public whereMonth(field: string, value: number): this {\n this.addOperation(\"whereRaw\", {\n expression: `EXTRACT(MONTH FROM ${field}) = ?`,\n bindings: [value],\n });\n return this;\n }\n\n /** Year extracted from a date field. */\n public whereYear(field: string, value: number): this {\n this.addOperation(\"whereRaw\", {\n expression: `EXTRACT(YEAR FROM ${field}) = ?`,\n bindings: [value],\n });\n return this;\n }\n\n // ════════════════════════════════════════════════════════\n // WHERE CLAUSES — JSON / STRUCTURED DATA\n // ════════════════════════════════════════════════════════\n\n /**\n * JSON/array path contains the given value.\n * @example q.whereJsonContains(\"tags\", \"typescript\")\n */\n public whereJsonContains(path: string, value: unknown): this {\n this.addOperation(\"whereJsonContains\", { path, value });\n return this;\n }\n\n /** JSON/array path does NOT contain the value. */\n public whereJsonDoesntContain(path: string, value: unknown): this {\n this.addOperation(\"whereJsonDoesntContain\", { path, value });\n return this;\n }\n\n /**\n * JSON path key exists.\n * Uses a `whereRaw` so existing SQL parsers get `IS NOT NULL` immediately.\n */\n public whereJsonContainsKey(path: string): this {\n this.addOperation(\"whereRaw\", { expression: `${path} IS NOT NULL`, bindings: [] });\n return this;\n }\n\n /**\n * Constrain the length of a JSON array at a path.\n * @example q.whereJsonLength(\"tags\", \">\", 3)\n */\n public whereJsonLength(path: string, operator: WhereOperator, value: number): this {\n this.addOperation(\"whereRaw\", {\n expression: `jsonb_array_length(${path}) ${operator} ?`,\n bindings: [value],\n });\n return this;\n }\n\n /** JSON path must resolve to an array. */\n public whereJsonIsArray(path: string): this {\n this.addOperation(\"whereRaw\", {\n expression: `jsonb_typeof(${path}) = 'array'`,\n bindings: [],\n });\n return this;\n }\n\n /** JSON path must resolve to an object. */\n public whereJsonIsObject(path: string): this {\n this.addOperation(\"whereRaw\", {\n expression: `jsonb_typeof(${path}) = 'object'`,\n bindings: [],\n });\n return this;\n }\n\n /**\n * Constrain the number of elements in an array field.\n * @example q.whereArrayLength(\"roles\", \">=\", 2)\n */\n public whereArrayLength(field: string, operator: WhereOperator, value: number): this {\n this.addOperation(\"whereRaw\", {\n expression: `array_length(${field}, 1) ${operator} ?`,\n bindings: [value],\n });\n return this;\n }\n\n // ════════════════════════════════════════════════════════\n // WHERE CLAUSES — CONVENIENCE SHORTCUTS\n // ════════════════════════════════════════════════════════\n\n /** WHERE id = value. */\n public whereId(value: string | number): this {\n return this.where(\"id\", value);\n }\n\n /** WHERE id IN values. */\n public whereIds(values: Array<string | number>): this {\n return this.whereIn(\"id\", values);\n }\n\n /** WHERE uuid = value. */\n public whereUuid(value: string): this {\n return this.where(\"uuid\", value);\n }\n\n /** WHERE ulid = value. */\n public whereUlid(value: string): this {\n return this.where(\"ulid\", value);\n }\n\n /**\n * Full-text search across one or more fields.\n * @example q.whereFullText([\"title\", \"body\"], \"typescript\")\n */\n public whereFullText(fields: string | string[], query: string): this {\n this.addOperation(\"whereFullText\", {\n fields: Array.isArray(fields) ? fields : [fields],\n query,\n });\n return this;\n }\n\n /** Full-text search (OR). */\n public orWhereFullText(fields: string | string[], query: string): this {\n return this.whereFullText(fields, query);\n }\n\n /** Alias for whereFullText with a single field. */\n public whereSearch(field: string, query: string): this {\n return this.whereFullText([field], query);\n }\n\n /**\n * Text search with optional extra equality filters.\n * MongoDB-style convenience shorthand.\n */\n public textSearch(query: string, filters?: WhereObject): this {\n if (filters) {\n for (const [key, value] of Object.entries(filters)) this.where(key, value as never);\n }\n return this;\n }\n\n // ════════════════════════════════════════════════════════\n // WHERE CLAUSES — EXISTENCE / SUBQUERIES\n // ════════════════════════════════════════════════════════\n\n /**\n * WHERE EXISTS (subquery callback) or field IS NOT NULL (string).\n *\n * @example\n * q.whereExists(sub => sub.where(\"userId\", \"users.id\"))\n * q.whereExists(\"optionalField\")\n */\n public whereExists(field: string): this;\n public whereExists(callback: WhereCallback<T>): this;\n public whereExists(param: string | WhereCallback<T>): this {\n if (typeof param === \"function\") {\n const sub = this.subQuery();\n param(sub as any);\n this.addOperation(\"whereExists\", { subquery: sub.operations });\n } else {\n this.addOperation(\"whereNotNull\", { field: param });\n }\n return this;\n }\n\n /**\n * WHERE NOT EXISTS (subquery callback) or field IS NULL (string).\n */\n public whereNotExists(field: string): this;\n public whereNotExists(callback: WhereCallback<T>): this;\n public whereNotExists(param: string | WhereCallback<T>): this {\n if (typeof param === \"function\") {\n const sub = this.subQuery();\n param(sub as any);\n this.addOperation(\"whereNotExists\", { subquery: sub.operations });\n } else {\n this.addOperation(\"whereNull\", { field: param });\n }\n return this;\n }\n\n /**\n * Constrain an array/collection field by element count.\n *\n * @example\n * q.whereSize(\"tags\", 3) // exactly 3\n * q.whereSize(\"tags\", \">=\", 1) // at least 1\n */\n public whereSize(field: string, size: number): this;\n public whereSize(field: string, operator: WhereOperator, size: number): this;\n public whereSize(field: string, ...args: unknown[]): this {\n const operator = args.length === 2 ? (args[0] as WhereOperator) : \"=\";\n const size = (args.length === 2 ? args[1] : args[0]) as number;\n return this.whereArrayLength(field, operator, size);\n }\n\n /**\n * AND NOT wrapper — negate a nested group.\n * @example q.whereNot(q => q.where(\"status\", \"banned\").where(\"role\", \"user\"))\n */\n public whereNot(callback: WhereCallback<T>): this {\n const sub = this.subQuery();\n callback(sub as any);\n this.addOperation(\"whereNot\", { nested: sub.operations });\n return this;\n }\n\n /** OR NOT wrapper. */\n public orWhereNot(callback: WhereCallback<T>): this {\n const sub = this.subQuery();\n callback(sub as any);\n this.addOperation(\"orWhereNot\", { nested: sub.operations });\n return this;\n }\n\n // ════════════════════════════════════════════════════════\n // JOINS — STANDARD SQL-STYLE\n // Note: Op type names match parser switch cases exactly.\n // join / innerJoin → INNER JOIN\n // leftJoin → LEFT JOIN\n // rightJoin → RIGHT JOIN\n // fullJoin → FULL OUTER JOIN\n // crossJoin → CROSS JOIN\n // joinRaw → raw expression\n // ════════════════════════════════════════════════════════\n\n /**\n * INNER JOIN.\n * @example q.join(\"categories\", \"posts.categoryId\", \"categories.id\")\n */\n public join(table: string, localField: string, foreignField: string): this;\n public join(options: JoinOptions): this;\n public join(...args: unknown[]): this {\n if (args.length === 3) {\n this.addOperation(\"join\", { table: args[0], localField: args[1], foreignField: args[2] });\n } else {\n this.addOperation(\"join\", args[0] as Record<string, unknown>);\n }\n return this;\n }\n\n /** LEFT JOIN. */\n public leftJoin(table: string, localField: string, foreignField: string): this;\n public leftJoin(options: JoinOptions): this;\n public leftJoin(...args: unknown[]): this {\n if (args.length === 3) {\n this.addOperation(\"leftJoin\", { table: args[0], localField: args[1], foreignField: args[2] });\n } else {\n this.addOperation(\"leftJoin\", args[0] as Record<string, unknown>);\n }\n return this;\n }\n\n /** RIGHT JOIN. */\n public rightJoin(table: string, localField: string, foreignField: string): this;\n public rightJoin(options: JoinOptions): this;\n public rightJoin(...args: unknown[]): this {\n if (args.length === 3) {\n this.addOperation(\"rightJoin\", {\n table: args[0],\n localField: args[1],\n foreignField: args[2],\n });\n } else {\n this.addOperation(\"rightJoin\", args[0] as Record<string, unknown>);\n }\n return this;\n }\n\n /** INNER JOIN (alias for join). */\n public innerJoin(table: string, localField: string, foreignField: string): this;\n public innerJoin(options: JoinOptions): this;\n public innerJoin(...args: unknown[]): this {\n if (args.length === 3) {\n this.addOperation(\"innerJoin\", {\n table: args[0],\n localField: args[1],\n foreignField: args[2],\n });\n } else {\n this.addOperation(\"innerJoin\", args[0] as Record<string, unknown>);\n }\n return this;\n }\n\n /** FULL OUTER JOIN. */\n public fullJoin(table: string, localField: string, foreignField: string): this;\n public fullJoin(options: JoinOptions): this;\n public fullJoin(...args: unknown[]): this {\n if (args.length === 3) {\n this.addOperation(\"fullJoin\", { table: args[0], localField: args[1], foreignField: args[2] });\n } else {\n this.addOperation(\"fullJoin\", args[0] as Record<string, unknown>);\n }\n return this;\n }\n\n /** CROSS JOIN. */\n public crossJoin(table: string): this {\n this.addOperation(\"crossJoin\", { table });\n return this;\n }\n\n /** Raw JOIN expression. Driver responsible for handling. */\n public joinRaw(expression: RawExpression, bindings?: unknown[]): this {\n this.addOperation(\"joinRaw\", { expression, bindings: bindings ?? [] });\n return this;\n }\n\n // ════════════════════════════════════════════════════════\n // RELATION EAGER LOADING — JOIN-BASED (joinWith)\n // ════════════════════════════════════════════════════════\n\n /**\n * Eager-load named relations via a single JOIN / $lookup query.\n *\n * Constraints are eagerly resolved at call time:\n * - Callbacks are invoked immediately → `subOps` stored in op data.\n * - Column shorthands are parsed into a `columns[]` array.\n *\n * The driver executor reads the `joinWith` op and uses the resolved data\n * alongside its own relation definition map to emit the appropriate SQL JOIN\n * or MongoDB $lookup stage.\n *\n * Supported arg forms (may be mixed):\n * - `\"author\"` / `[\"author\", \"category\"]` — no constraint\n * - `{ author: \"id,name\" }` — column shorthand\n * - `{ actions: q => q.where(\"status\",\"pending\").limit(5) }` — callback\n *\n * @example\n * Post.joinWith(\"author\", \"category\")\n * ChatMessage.joinWith({ actions: q => q.where(\"status\", \"pending\").limit(5) })\n * ChatMessage.joinWith({ org: \"id,name\", actions: q => q.orderBy(\"sort_order\") })\n */\n public joinWith(...args: unknown[]): this {\n const resolved: Record<string, { columns?: string[]; subOps?: Op[] }> = {};\n\n for (const arg of args) {\n if (typeof arg === \"string\") {\n resolved[arg] = {};\n } else if (Array.isArray(arg)) {\n for (const rel of arg as string[]) resolved[rel] = {};\n } else if (typeof arg === \"object\" && arg !== null) {\n for (const [rel, constraint] of Object.entries(arg as Record<string, JoinWithConstraint>)) {\n if (typeof constraint === \"function\") {\n const sub = this.subQuery();\n constraint(sub);\n resolved[rel] = { subOps: sub.operations };\n } else if (typeof constraint === \"string\" && constraint !== \"\") {\n resolved[rel] = {\n columns: constraint\n .split(\",\")\n .map((s) => s.trim())\n .filter(Boolean),\n };\n } else {\n resolved[rel] = {};\n }\n }\n }\n }\n\n this.addOperation(\"joinWith\", { resolved });\n return this;\n }\n\n // ════════════════════════════════════════════════════════\n // RELATION EAGER LOADING — SEPARATE QUERIES (with)\n // ════════════════════════════════════════════════════════\n\n /**\n * Eager-load relations via separate queries (N+1 avoided by batching).\n *\n * @example\n * q.with(\"posts\")\n * q.with(\"posts\", q => q.where(\"published\", true))\n * q.with({ posts: true, comments: q => q.limit(5) })\n */\n public with(\n ...args: (string | Record<string, boolean | ((q: any) => void)> | ((q: any) => void))[]\n ): this {\n for (let i = 0; i < args.length; i++) {\n const arg = args[i];\n if (typeof arg === \"string\") {\n const next = args[i + 1];\n if (typeof next === \"function\") {\n this.eagerLoadRelations.set(arg, next as (q: any) => void);\n i++;\n } else {\n this.eagerLoadRelations.set(arg, true);\n }\n } else if (typeof arg === \"object\" && arg !== null) {\n for (const [key, value] of Object.entries(\n arg as Record<string, boolean | ((q: any) => void)>,\n )) {\n this.eagerLoadRelations.set(key, value);\n }\n }\n }\n return this;\n }\n\n /**\n * Register one or more relation counts to emit alongside each result row.\n *\n * Accepts:\n * - Bare relation names (variadic strings or array): `withCount(\"posts\", \"comments\")`\n * - Alias shorthand: `withCount(\"posts as totalPosts\")`\n * - Object form for per-relation constraints / aliases:\n * `withCount({ posts: true, \"posts as approved\": (q) => q.where(\"approved\", true) })`\n *\n * Each entry is stored in `countRelations` keyed by its output column alias\n * (default `${relationName}Count`). The driver subclass consumes the map at\n * execute time to emit count expressions.\n *\n * @example\n * ```typescript\n * await User.query().withCount(\"posts\").get(); // postsCount\n * await User.query().withCount(\"posts as totalPosts\").get(); // totalPosts\n * await User.query()\n * .withCount({\n * posts: true,\n * \"posts as published\": (q) => q.where(\"isPublished\", true),\n * comments: \"commentTotal\",\n * })\n * .get();\n * ```\n */\n public withCount(...args: unknown[]): this {\n for (const arg of args) {\n if (typeof arg === \"string\") {\n this.recordCountEntry(arg);\n continue;\n }\n\n if (Array.isArray(arg)) {\n for (const spec of arg as string[]) {\n this.recordCountEntry(spec);\n }\n continue;\n }\n\n if (typeof arg === \"object\" && arg !== null) {\n const entries = Object.entries(\n arg as Record<string, true | string | ((query: any) => void)>,\n );\n\n for (const [key, value] of entries) {\n if (value === true) {\n this.recordCountEntry(key);\n } else if (typeof value === \"string\") {\n this.recordCountEntry(`${key} as ${value}`);\n } else if (typeof value === \"function\") {\n this.recordCountEntry(key, value);\n }\n }\n }\n }\n\n return this;\n }\n\n /**\n * Parse a count spec (\"relation\" or \"relation as alias\") into its relation\n * name and output alias, optionally capturing a constraint callback's\n * operations via a sub-builder. Stored in `countRelations` keyed by alias.\n */\n protected recordCountEntry(spec: string, constraint?: (query: any) => void): void {\n const { relation, alias } = this.parseCountSpec(spec);\n\n let constraintOps: Op[] | undefined;\n\n if (constraint) {\n const sub = this.subQuery();\n constraint(sub);\n constraintOps = sub.operations;\n }\n\n this.countRelations.set(alias, { relation, constraintOps });\n }\n\n /**\n * Split a `\"<relation>\"` or `\"<relation> as <alias>\"` spec. Returns the\n * resolved relation name and the output column alias (defaulting to\n * `${relation}Count` when no `as` is present).\n */\n protected parseCountSpec(spec: string): { relation: string; alias: string } {\n const trimmed = spec.trim();\n const match = /^(.+?)\\s+as\\s+(.+)$/i.exec(trimmed);\n\n if (!match) {\n return { relation: trimmed, alias: `${trimmed}Count` };\n }\n\n return { relation: match[1].trim(), alias: match[2].trim() };\n }\n\n /**\n * Filter to rows that have at least one related record.\n * @example q.has(\"comments\")\n * @example q.has(\"comments\", \">=\", 3)\n */\n public has(relation: string, operator?: WhereOperator, count?: number): this {\n this.addOperation(\"has\", { relation, operator: operator ?? \">=\", count: count ?? 1 });\n return this;\n }\n\n /**\n * Filter to rows with related records matching a sub-query (AND).\n * @example q.whereHas(\"comments\", q => q.where(\"approved\", true))\n */\n public whereHas(relation: string, callback: (q: any) => void): this {\n const sub = this.subQuery();\n callback(sub);\n this.addOperation(\"whereHas\", { relation, subquery: sub.operations });\n return this;\n }\n\n /** Same as whereHas but OR-joined. */\n public orWhereHas(relation: string, callback: (q: any) => void): this {\n const sub = this.subQuery();\n callback(sub);\n this.addOperation(\"orWhereHas\", { relation, subquery: sub.operations });\n return this;\n }\n\n /** Filter to rows with NO related records. */\n public doesntHave(relation: string): this {\n this.addOperation(\"doesntHave\", { relation });\n return this;\n }\n\n /** Filter to rows with NO related records matching conditions. */\n public whereDoesntHave(relation: string, callback: (q: any) => void): this {\n const sub = this.subQuery();\n callback(sub);\n this.addOperation(\"whereDoesntHave\", { relation, subquery: sub.operations });\n return this;\n }\n\n // ════════════════════════════════════════════════════════\n // SELECT / PROJECTION\n // ════════════════════════════════════════════════════════\n\n /**\n * Select specific columns.\n *\n * @example\n * q.select([\"id\", \"name\"])\n * q.select(\"id\", \"name\")\n * q.select({ name: 1, password: 0 }) // MongoDB-style projection\n */\n public select(fields: string[]): this;\n public select(fields: Record<string, 0 | 1 | boolean>): this;\n public select(...fields: Array<string | string[]>): this;\n public select(...args: unknown[]): this {\n if (args.length === 1 && Array.isArray(args[0])) {\n this.addOperation(\"select\", { fields: args[0] });\n } else if (args.length === 1 && typeof args[0] === \"object\" && !Array.isArray(args[0])) {\n this.addOperation(\"select\", { fields: args[0] as Record<string, unknown> });\n } else {\n this.addOperation(\"select\", { fields: (args as Array<string | string[]>).flat() });\n }\n return this;\n }\n\n /** Select a field under an alias. @example q.selectAs(\"fullName\", \"name\") */\n public selectAs(field: string, alias: string): this {\n this.addOperation(\"select\", { fields: { [field]: alias } });\n return this;\n }\n\n /**\n * Raw SELECT expression.\n * @example q.selectRaw(\"COUNT(*) AS total\")\n */\n public selectRaw(expression: RawExpression, bindings?: unknown[]): this {\n this.addOperation(\"selectRaw\", { expression, bindings: bindings ?? [] });\n return this;\n }\n\n /** Multiple raw SELECT expressions in one call. */\n public selectRawMany(\n definitions: Array<{ alias: string; expression: RawExpression; bindings?: unknown[] }>,\n ): this {\n for (const def of definitions) {\n this.selectRaw({ [def.alias]: def.expression }, def.bindings);\n }\n return this;\n }\n\n /** Subquery as a named projected field. */\n public selectSub(expression: RawExpression, alias: string): this {\n this.addOperation(\"selectRaw\", { expression: { [alias]: expression } });\n return this;\n }\n\n /** Alias for selectSub. */\n public addSelectSub(expression: RawExpression, alias: string): this {\n return this.selectSub(expression, alias);\n }\n\n /**\n * Aggregate function as a projected field.\n * @example q.selectAggregate(\"price\", \"sum\", \"totalRevenue\")\n */\n public selectAggregate(\n field: string,\n aggregate: \"sum\" | \"avg\" | \"min\" | \"max\" | \"count\" | \"first\" | \"last\",\n alias: string,\n ): this {\n return this.selectRaw({ [alias]: `${aggregate.toUpperCase()}(${field})` });\n }\n\n /** Existence check as a projected boolean field. */\n public selectExists(field: string, alias: string): this {\n return this.selectRaw({ [alias]: `${field} IS NOT NULL` });\n }\n\n /** COUNT as a projected field. */\n public selectCount(field: string, alias: string): this {\n return this.selectAggregate(field, \"count\", alias);\n }\n\n /**\n * CASE / switch expression.\n * @example q.selectCase([{ when: \"status = 1\", then: \"'active'\" }], \"'inactive'\", \"statusLabel\")\n */\n public selectCase(\n cases: Array<{ when: RawExpression; then: RawExpression | unknown }>,\n otherwise: RawExpression | unknown,\n alias: string,\n ): this {\n const caseExpr = cases.map((c) => `WHEN ${c.when} THEN ${c.then}`).join(\" \");\n return this.selectRaw({ [alias]: `CASE ${caseExpr} ELSE ${otherwise} END` });\n }\n\n /** IF/ELSE conditional field. */\n public selectWhen(\n condition: RawExpression,\n thenValue: RawExpression | unknown,\n elseValue: RawExpression | unknown,\n alias: string,\n ): this {\n return this.selectRaw({\n [alias]: `CASE WHEN ${condition} THEN ${thenValue} ELSE ${elseValue} END`,\n });\n }\n\n /**\n * Driver-native projection manipulation.\n * No-op in base — override in driver subclasses.\n */\n public selectDriverProjection(_callback: (projection: Record<string, unknown>) => void): this {\n return this;\n }\n\n /** JSON path extraction as a projected field. */\n public selectJson(path: string, alias?: string): this {\n const parts = path.split(\"->\");\n const column = parts[0];\n const jsonPath = parts.slice(1).join(\"->\");\n const expr = jsonPath ? `${column}->>'${jsonPath}'` : column;\n return alias ? this.selectAs(expr, alias) : this.selectRaw(expr);\n }\n\n /** JSON extraction via raw expression. */\n public selectJsonRaw(_path: string, expression: RawExpression, alias: string): this {\n return this.selectRaw({ [alias]: expression });\n }\n\n /** Exclude a JSON path from projection. */\n public deselectJson(path: string): this {\n return this.deselect([path]);\n }\n\n /** String concatenation as a projected field. */\n public selectConcat(fields: Array<string | RawExpression>, alias: string): this {\n return this.selectRaw({ [alias]: fields.join(\" || \") });\n }\n\n /** COALESCE (first non-null) as a projected field. */\n public selectCoalesce(fields: Array<string | RawExpression>, alias: string): this {\n return this.selectRaw({ [alias]: `COALESCE(${fields.join(\", \")})` });\n }\n\n /** Window function expression. */\n public selectWindow(spec: RawExpression): this {\n this.addOperation(\"selectRaw\", { expression: spec });\n return this;\n }\n\n /** Exclude specific columns from results. */\n public deselect(fields: string[]): this {\n this.addOperation(\"deselect\", { fields });\n return this;\n }\n\n /**\n * Remove all select operations (resets to wildcard).\n * Uses `rebuildIndex()` — no unsafe casts.\n */\n public clearSelect(): this {\n this.operations = this.operations.filter(\n (op) => !op.type.startsWith(\"select\") && op.type !== \"deselect\",\n );\n this.rebuildIndex();\n return this;\n }\n\n /** Alias for clearSelect. */\n public selectAll(): this {\n return this.clearSelect();\n }\n\n /** Alias for clearSelect. */\n public selectDefault(): this {\n return this.clearSelect();\n }\n\n /** Append additional fields to existing selection. */\n public addSelect(fields: string[]): this {\n this.addOperation(\"select\", { fields, add: true });\n return this;\n }\n\n /**\n * Record a DISTINCT flag (fluent — does not execute).\n * Subclasses expose a separate async `distinct(field)` execution method.\n */\n public distinctValues(fields?: string | string[]): this {\n const fieldList = fields ? (Array.isArray(fields) ? fields : [fields]) : [];\n this.addOperation(\"distinct\", { fields: fieldList });\n return this;\n }\n\n // ════════════════════════════════════════════════════════\n // ORDERING\n // ════════════════════════════════════════════════════════\n\n /**\n * ORDER BY a column.\n *\n * @example\n * q.orderBy(\"createdAt\", \"desc\")\n * q.orderBy({ name: \"asc\", age: \"desc\" })\n */\n public orderBy(field: string, direction?: OrderDirection): this;\n public orderBy(fields: Record<string, OrderDirection>): this;\n public orderBy(...args: unknown[]): this {\n if (typeof args[0] === \"string\") {\n this.addOperation(\"orderBy\", {\n field: args[0],\n direction: (args[1] as OrderDirection) ?? \"asc\",\n });\n } else {\n for (const [field, direction] of Object.entries(args[0] as Record<string, OrderDirection>)) {\n this.addOperation(\"orderBy\", { field, direction });\n }\n }\n return this;\n }\n\n /** ORDER BY descending shorthand. */\n public orderByDesc(field: string): this {\n return this.orderBy(field, \"desc\");\n }\n\n /**\n * Raw ORDER BY expression.\n * @example q.orderByRaw(\"RANDOM()\")\n * @example q.orderByRaw({ $meta: \"textScore\" })\n */\n public orderByRaw(expression: RawExpression, bindings?: unknown[]): this {\n this.addOperation(\"orderByRaw\", { expression, bindings: bindings ?? [] });\n return this;\n }\n\n /**\n * Random order. Maps to `RANDOM()` in SQL or `$sample` in MongoDB.\n * @param limit - Optional limit (required for MongoDB $sample)\n */\n public orderByRandom(limit?: number): this {\n this.addOperation(\"orderByRaw\", { expression: \"RANDOM()\" });\n if (limit !== undefined) this.limit(limit);\n return this;\n }\n\n /** Order ascending by a date column (oldest first). */\n public oldest(column = \"createdAt\"): this {\n return this.orderBy(column, \"asc\");\n }\n\n // ════════════════════════════════════════════════════════\n // LIMIT / OFFSET\n // ════════════════════════════════════════════════════════\n\n /** Limit number of results. */\n public limit(value: number): this {\n this.addOperation(\"limit\", { value });\n return this;\n }\n\n /** Skip N results (OFFSET). */\n public skip(value: number): this {\n this.addOperation(\"offset\", { value });\n return this;\n }\n\n /** Alias for skip. */\n public offset(value: number): this {\n return this.skip(value);\n }\n\n /** Alias for limit. */\n public take(value: number): this {\n return this.limit(value);\n }\n\n // ════════════════════════════════════════════════════════\n // GROUPING / AGGREGATION\n // ════════════════════════════════════════════════════════\n\n /**\n * GROUP BY clause.\n * @example q.groupBy(\"status\")\n * @example q.groupBy([\"year\", \"month\"])\n */\n public groupBy(input: GroupByInput): this {\n const fields = Array.isArray(input) ? input : [input];\n this.addOperation(\"groupBy\", { fields });\n return this;\n }\n\n /** Raw GROUP BY expression. */\n public groupByRaw(expression: RawExpression, bindings?: unknown[]): this {\n this.addOperation(\"groupBy\", { expression, bindings: bindings ?? [] });\n return this;\n }\n\n /**\n * HAVING clause (post-group filter).\n *\n * @example\n * q.having(\"total\", \">\", 100)\n * q.having([\"total\", \">\", 100])\n * q.having({ total: 100 })\n */\n public having(field: string, value: unknown): this;\n public having(field: string, operator: WhereOperator, value: unknown): this;\n public having(condition: HavingInput): this;\n public having(...args: unknown[]): this {\n if (args.length === 1) {\n const input = args[0] as HavingInput;\n if (Array.isArray(input)) {\n if (input.length === 2) {\n this.addOperation(\"having\", { field: input[0], operator: \"=\", value: input[1] });\n } else {\n this.addOperation(\"having\", { field: input[0], operator: input[1], value: input[2] });\n }\n } else {\n for (const [key, value] of Object.entries(input as Record<string, unknown>)) {\n this.addOperation(\"having\", { field: key, operator: \"=\", value });\n }\n }\n } else if (args.length === 2) {\n this.addOperation(\"having\", { field: args[0], operator: \"=\", value: args[1] });\n } else {\n this.addOperation(\"having\", { field: args[0], operator: args[1], value: args[2] });\n }\n return this;\n }\n\n /** Raw HAVING expression. */\n public havingRaw(expression: RawExpression, bindings?: unknown[]): this {\n this.addOperation(\"havingRaw\", { expression, bindings: bindings ?? [] });\n return this;\n }\n\n // ════════════════════════════════════════════════════════\n // UTILITY / CONTROL FLOW\n // ════════════════════════════════════════════════════════\n\n /**\n * Side-effect tap — executes callback synchronously and returns `this`.\n * @example q.where(...).tap(q => console.log(q.operations.length)).limit(10)\n */\n public tap(callback: (builder: this) => void): this {\n callback(this);\n return this;\n }\n\n /**\n * Conditionally apply query modifications.\n *\n * @example\n * q.when(userId, (q, id) => q.where(\"userId\", id))\n * q.when(isAdmin, q => q.withoutGlobalScopes(), q => q.scope(\"active\"))\n */\n public when<V>(\n condition: V | boolean,\n callback: (builder: this, value: V) => void,\n otherwise?: (builder: this) => void,\n ): this {\n if (condition) {\n callback(this, condition as V);\n } else if (otherwise) {\n otherwise(this);\n }\n return this;\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"query-builder.mjs","names":[],"sources":["../../../../../../../@warlock.js/cascade/src/query-builder/query-builder.ts"],"sourcesContent":["/**\n * Pure Query Builder Base Class\n *\n * Driver-agnostic operation recorder. All fluent methods push typed entries into\n * `operations[]`. No SQL, no driver references, no table property, no execution.\n *\n * ┌─────────────────────────────────────────────────┐\n * │ Usage contexts │\n * │ (a) Subclassed — PG / Mongo / MySQL / … │\n * │ (b) Instantiated directly (new QueryBuilder()) │\n * │ inside callbacks for: │\n * │ • nested where groups │\n * │ • joinWith constraints │\n * │ • whereExists / whereHas subqueries │\n * └─────────────────────────────────────────────────┘\n *\n * Design rules:\n * - `table` / alias are NOT here — the parser gets them from the executor.\n * - `opIndex` is protected so subclasses can rebuild after direct mutation.\n * - Op type names are stable — parsers switch on them; no renaming without\n * a parser update.\n * - OR-variants keep distinct op types (orWhere, orWhereColumn, …) so existing\n * parsers that switch on type need no changes.\n * - `joinWith` eagerly resolves callbacks → subOps at record time so the\n * driver executor receives a plain data structure, not a live function.\n *\n * @module cascade/query-builder\n */\n\nimport type {\n GroupByInput,\n HavingInput,\n JoinOptions,\n LockForUpdateOptions,\n OrderDirection,\n RawExpression,\n WhereCallback,\n WhereObject,\n WhereOperator,\n} from \"../contracts/query-builder.contract\";\n\n// ============================================================================\n// TYPES\n// ============================================================================\n\n/**\n * A single recorded query operation.\n * `type` is the discriminator; `data` carries all parameters.\n */\nexport type Op = {\n readonly type: string;\n readonly data: Record<string, unknown>;\n};\n\n/**\n * Constraint value accepted by `joinWith()`.\n *\n * - `string` → comma-separated column shorthand: `\"id,name,createdAt\"`\n * - `fn` → callback receives a bare QueryBuilder to record sub-ops\n *\n * @example\n * joinWith({ actions: \"id,status\" })\n * joinWith({ actions: q => q.where(\"status\", \"pending\").limit(5) })\n */\nexport type JoinWithConstraint = string | ((q: QueryBuilder) => void);\n\n// ============================================================================\n// QUERY BUILDER — CONCRETE, DIRECTLY INSTANTIABLE\n// ============================================================================\n\n/**\n * Pure, driver-agnostic query builder.\n *\n * Records operations in `operations[]`. Subclasses own execution, parsing, and\n * driver-specific clause generation. Safe to instantiate directly inside\n * callbacks where only operation recording is needed.\n *\n * @example\n * ```ts\n * // Driver subclass usage:\n * const users = await User.query()\n * .select([\"id\", \"name\"])\n * .where(\"status\", \"active\")\n * .where(q => q.where(\"role\", \"admin\").orWhere(\"role\", \"mod\"))\n * .orderBy(\"createdAt\", \"desc\")\n * .limit(10)\n * .get();\n *\n * // Direct instantiation (callback context — no driver needed):\n * joinWith({ actions: q => q.where(\"status\", \"pending\").limit(5) });\n * // The sub-QB's operations[] are captured and stored in the joinWith op data.\n * ```\n */\nexport class QueryBuilder<T = unknown> {\n // ════════════════════════════════════════════════════════\n // OPERATION STORE\n // ════════════════════════════════════════════════════════\n\n /** Flat, ordered list of recorded operations. Public for parser access. */\n public operations: Op[] = [];\n\n /**\n * type → ordered list of indices into `operations[]`.\n *\n * Protected (not private) so:\n * - `rebuildIndex()` can reset it after direct `operations[]` mutation.\n * - Subclasses can inspect it without unsafe casts.\n *\n * External consumers should use `getOps(type)` instead.\n */\n protected opIndex: Map<string, number[]> = new Map();\n\n // ════════════════════════════════════════════════════════\n // SCOPE STATE (injected by Model.query(), consumed before execution)\n // ════════════════════════════════════════════════════════\n\n /** Global scope definitions injected by Model.query(). Keyed by scope name. */\n public pendingGlobalScopes?: Map<string, any>;\n /** Local scope callbacks injected by Model.query(). Applied on demand via scope(). */\n public availableLocalScopes?: Map<string, (...args: any[]) => void>;\n /** Names of global scopes that have been intentionally disabled. */\n public disabledGlobalScopes: Set<string> = new Set();\n /** True once the driver subclass has applied pending scopes. */\n public scopesApplied = false;\n\n // ════════════════════════════════════════════════════════\n // RELATION STATE (consumed by driver subclass at execute time)\n // ════════════════════════════════════════════════════════\n\n /** Relations to eager-load via separate queries. */\n public eagerLoadRelations: Map<string, boolean | ((query: any) => void)> = new Map();\n /** Count expressions to emit per result row, keyed by output column alias. */\n public countRelations: Map<string, { relation: string; constraintOps?: Op[] }> = new Map();\n /** Relation definition map injected from the owning Model. */\n public relationDefinitions?: Record<string, any>;\n /** The Model class reference, required for relation resolution. */\n public modelClass?: any;\n\n // ════════════════════════════════════════════════════════\n // CORE INTERNALS\n // ════════════════════════════════════════════════════════\n\n /**\n * Append an operation to `operations[]` and update `opIndex`.\n * Every fluent method calls this.\n */\n protected addOperation(type: string, data: Record<string, unknown>): void {\n const idx = this.operations.length;\n this.operations.push({ type, data });\n const list = this.opIndex.get(type);\n if (list) {\n list.push(idx);\n } else {\n this.opIndex.set(type, [idx]);\n }\n }\n\n /**\n * Return all recorded operations of the specified types in original\n * insertion order.\n *\n * @example\n * builder.getOps(\"where\", \"orWhere\", \"whereIn\")\n */\n public getOps(...types: string[]): Op[] {\n if (types.length === 1) {\n return (this.opIndex.get(types[0]) ?? []).map((i) => this.operations[i]);\n }\n const result: Array<{ idx: number; op: Op }> = [];\n for (const type of types) {\n for (const idx of this.opIndex.get(type) ?? []) {\n result.push({ idx, op: this.operations[idx] });\n }\n }\n return result.sort((a, b) => a.idx - b.idx).map((r) => r.op);\n }\n\n /**\n * Rebuild `opIndex` from scratch.\n *\n * Call this after any direct mutation of `this.operations[]` (e.g. scope\n * injection, joinWith consumption in the executor, clone post-processing).\n */\n public rebuildIndex(): void {\n this.opIndex = new Map();\n for (let i = 0; i < this.operations.length; i++) {\n const type = this.operations[i].type;\n const list = this.opIndex.get(type);\n if (list) {\n list.push(i);\n } else {\n this.opIndex.set(type, [i]);\n }\n }\n }\n\n /**\n * Factory for sub-QueryBuilders used inside callbacks.\n *\n * Override in driver subclasses to return a driver-typed instance, so that\n * driver-specific methods (e.g. `whereArrayContains`) are available inside\n * nested `where(q => ...)` / `whereHas` / `joinWith` callbacks.\n *\n * @example\n * // In PostgresQueryBuilder:\n * protected override subQuery(): QueryBuilder {\n * return new PostgresQueryBuilder(\"__sub__\", this.dataSource);\n * }\n */\n protected subQuery(): QueryBuilder {\n return new QueryBuilder();\n }\n\n /**\n * Shallow-clone this builder — copies operations, opIndex, and all shared state.\n *\n * Subclasses MUST call `super.clone()` and then copy their own fields\n * (dataSource, joinRelations, …).\n */\n public clone(): this {\n const cloned = Object.create(Object.getPrototypeOf(this)) as this;\n cloned.operations = [...this.operations];\n cloned.opIndex = new Map(Array.from(this.opIndex.entries()).map(([k, v]) => [k, [...v]]));\n cloned.pendingGlobalScopes = this.pendingGlobalScopes;\n cloned.availableLocalScopes = this.availableLocalScopes;\n cloned.disabledGlobalScopes = new Set(this.disabledGlobalScopes);\n cloned.scopesApplied = this.scopesApplied;\n cloned.eagerLoadRelations = new Map(this.eagerLoadRelations);\n cloned.countRelations = new Map(this.countRelations);\n cloned.relationDefinitions = this.relationDefinitions;\n cloned.modelClass = this.modelClass;\n return cloned;\n }\n\n // ════════════════════════════════════════════════════════\n // SCOPES\n // ════════════════════════════════════════════════════════\n\n /** Disable one or more named global scopes for this query. */\n public withoutGlobalScope(...scopeNames: string[]): this {\n scopeNames.forEach((name) => this.disabledGlobalScopes.add(name));\n return this;\n }\n\n /** Disable ALL pending global scopes for this query. */\n public withoutGlobalScopes(): this {\n this.pendingGlobalScopes?.forEach((_, name) => this.disabledGlobalScopes.add(name));\n return this;\n }\n\n /**\n * Apply a registered local scope by name.\n * @throws if no local scopes are available or the named scope is not found\n */\n public scope(scopeName: string, ...args: unknown[]): this {\n if (!this.availableLocalScopes) {\n throw new Error(\"No local scopes available on this query builder.\");\n }\n const cb = this.availableLocalScopes.get(scopeName);\n if (!cb) throw new Error(`Local scope \"${scopeName}\" not found.`);\n cb(this, ...args);\n return this;\n }\n\n // ════════════════════════════════════════════════════════\n // WHERE CLAUSES — CORE\n // ════════════════════════════════════════════════════════\n\n /**\n * Add a WHERE clause (AND).\n *\n * @example\n * q.where(\"status\", \"active\")\n * q.where(\"age\", \">\", 18)\n * q.where({ role: \"admin\", active: true })\n * q.where(q => q.where(\"a\", 1).orWhere(\"b\", 2))\n */\n public where(field: string, value: unknown): this;\n public where(field: string, operator: WhereOperator, value: unknown): this;\n public where(conditions: WhereObject): this;\n public where(callback: WhereCallback<T>): this;\n public where(...args: unknown[]): this {\n if (args.length === 1 && typeof args[0] === \"function\") {\n const sub = this.subQuery();\n (args[0] as (q: QueryBuilder) => void)(sub);\n this.addOperation(\"where\", { nested: sub.operations });\n } else if (args.length === 1 && typeof args[0] === \"object\" && args[0] !== null) {\n for (const [key, value] of Object.entries(args[0] as WhereObject)) {\n this.addOperation(\"where\", { field: key, operator: \"=\", value });\n }\n } else if (args.length === 2) {\n this.addOperation(\"where\", { field: args[0], operator: \"=\", value: args[1] });\n } else {\n this.addOperation(\"where\", { field: args[0], operator: args[1], value: args[2] });\n }\n return this;\n }\n\n /**\n * Add an OR WHERE clause.\n *\n * @example\n * q.where(\"role\", \"admin\").orWhere(\"role\", \"mod\")\n */\n public orWhere(field: string, value: unknown): this;\n public orWhere(field: string, operator: WhereOperator, value: unknown): this;\n public orWhere(conditions: WhereObject): this;\n public orWhere(callback: WhereCallback<T>): this;\n public orWhere(...args: unknown[]): this {\n if (args.length === 1 && typeof args[0] === \"function\") {\n const sub = this.subQuery();\n (args[0] as (q: QueryBuilder) => void)(sub);\n this.addOperation(\"orWhere\", { nested: sub.operations });\n } else if (args.length === 1 && typeof args[0] === \"object\" && args[0] !== null) {\n for (const [key, value] of Object.entries(args[0] as WhereObject)) {\n this.addOperation(\"orWhere\", { field: key, operator: \"=\", value });\n }\n } else if (args.length === 2) {\n this.addOperation(\"orWhere\", { field: args[0], operator: \"=\", value: args[1] });\n } else {\n this.addOperation(\"orWhere\", { field: args[0], operator: args[1], value: args[2] });\n }\n return this;\n }\n\n /**\n * Raw WHERE expression in the target dialect (AND).\n *\n * @example\n * q.whereRaw(\"age > ? AND role = ?\", [18, \"admin\"]) // SQL\n * q.whereRaw({ $expr: { $gt: [\"$stock\", \"$reserved\"] } }) // MongoDB\n */\n public whereRaw(expression: RawExpression, bindings?: unknown[]): this {\n this.addOperation(\"whereRaw\", { expression, bindings: bindings ?? [] });\n return this;\n }\n\n /** Raw OR WHERE expression. */\n public orWhereRaw(expression: RawExpression, bindings?: unknown[]): this {\n this.addOperation(\"orWhereRaw\", { expression, bindings: bindings ?? [] });\n return this;\n }\n\n // ════════════════════════════════════════════════════════\n // WHERE CLAUSES — COLUMN COMPARISONS\n // ════════════════════════════════════════════════════════\n\n /**\n * Compare two columns directly (AND).\n * @example q.whereColumn(\"stock\", \">\", \"reserved\")\n */\n public whereColumn(first: string, operator: WhereOperator, second: string): this {\n this.addOperation(\"whereColumn\", { first, operator, second });\n return this;\n }\n\n /** Compare two columns directly (OR). */\n public orWhereColumn(first: string, operator: WhereOperator, second: string): this {\n this.addOperation(\"orWhereColumn\", { first, operator, second });\n return this;\n }\n\n /** Compare multiple column pairs in one call. */\n public whereColumns(\n comparisons: Array<[left: string, operator: WhereOperator, right: string]>,\n ): this {\n for (const [left, operator, right] of comparisons) {\n this.whereColumn(left, operator, right);\n }\n return this;\n }\n\n /**\n * Field value must fall between two other column values.\n * Stored as a `whereBetween` op with `useColumns: true` so the SQL parser\n * knows to quote the values as identifiers rather than bind them.\n */\n public whereBetweenColumns(field: string, lowerColumn: string, upperColumn: string): this {\n this.addOperation(\"whereBetween\", { field, lowerColumn, upperColumn, useColumns: true });\n return this;\n }\n\n // ════════════════════════════════════════════════════════\n // WHERE CLAUSES — STANDARD COMPARISON OPERATORS\n // ════════════════════════════════════════════════════════\n\n /** WHERE field IN values. */\n public whereIn(field: string, values: unknown[]): this {\n this.addOperation(\"whereIn\", { field, values });\n return this;\n }\n\n /** WHERE field NOT IN values. */\n public whereNotIn(field: string, values: unknown[]): this {\n this.addOperation(\"whereNotIn\", { field, values });\n return this;\n }\n\n /** WHERE field IS NULL. */\n public whereNull(field: string): this {\n this.addOperation(\"whereNull\", { field });\n return this;\n }\n\n /** WHERE field IS NOT NULL. */\n public whereNotNull(field: string): this {\n this.addOperation(\"whereNotNull\", { field });\n return this;\n }\n\n /** WHERE field BETWEEN low AND high. */\n public whereBetween(field: string, range: [unknown, unknown]): this {\n this.addOperation(\"whereBetween\", { field, range });\n return this;\n }\n\n /** WHERE field NOT BETWEEN low AND high. */\n public whereNotBetween(field: string, range: [unknown, unknown]): this {\n this.addOperation(\"whereNotBetween\", { field, range });\n return this;\n }\n\n // ════════════════════════════════════════════════════════\n // WHERE CLAUSES — PATTERN MATCHING\n // ════════════════════════════════════════════════════════\n\n /**\n * LIKE pattern match (AND).\n * @example q.whereLike(\"email\", \"%@gmail.com\")\n */\n public whereLike(field: string, pattern: RegExp | string): this {\n const patternStr = pattern instanceof RegExp ? pattern.source : pattern;\n this.addOperation(\"whereLike\", { field, pattern: patternStr });\n return this;\n }\n\n /** NOT LIKE pattern match. */\n public whereNotLike(field: string, pattern: RegExp | string): this {\n const patternStr = pattern instanceof RegExp ? pattern.source : pattern;\n this.addOperation(\"whereNotLike\", { field, pattern: patternStr });\n return this;\n }\n\n /** Starts with a prefix. */\n public whereStartsWith(field: string, value: string | number): this {\n return this.whereLike(field, `${value}%`);\n }\n\n /** Does NOT start with a prefix. */\n public whereNotStartsWith(field: string, value: string | number): this {\n return this.whereNotLike(field, `${value}%`);\n }\n\n /** Ends with a suffix. */\n public whereEndsWith(field: string, value: string | number): this {\n return this.whereLike(field, `%${value}`);\n }\n\n /** Does NOT end with a suffix. */\n public whereNotEndsWith(field: string, value: string | number): this {\n return this.whereNotLike(field, `%${value}`);\n }\n\n // ════════════════════════════════════════════════════════\n // WHERE CLAUSES — DATE/TIME PARTIALS\n // ════════════════════════════════════════════════════════\n\n /**\n * Match on date portion only (time ignored).\n * @example q.whereDate(\"createdAt\", \"2024-05-01\")\n */\n public whereDate(field: string, value: Date | string): this {\n this.addOperation(\"whereDate\", { field, value });\n return this;\n }\n\n /** Alias for whereDate. */\n public whereDateEquals(field: string, value: Date | string): this {\n return this.whereDate(field, value);\n }\n\n /** Field date is before value. */\n public whereDateBefore(field: string, value: Date | string): this {\n this.addOperation(\"whereDateBefore\", { field, value });\n return this;\n }\n\n /** Field date is after value. */\n public whereDateAfter(field: string, value: Date | string): this {\n this.addOperation(\"whereDateAfter\", { field, value });\n return this;\n }\n\n /** Field date is within a range [from, to]. */\n public whereDateBetween(field: string, range: [Date | string, Date | string]): this {\n this.addOperation(\"whereDateBetween\", { field, range });\n return this;\n }\n\n /** Field date is NOT within a range. */\n public whereDateNotBetween(field: string, range: [Date | string, Date | string]): this {\n this.addOperation(\"whereNotBetween\", { field, range });\n return this;\n }\n\n /**\n * Match on the time portion of a datetime field.\n * Emits a `whereRaw` op with a driver-agnostic marker; the driver parser\n * rewrites it to the appropriate SQL (`TIME(field) = ?`) or Mongo expression.\n */\n public whereTime(field: string, value: string): this {\n this.addOperation(\"whereRaw\", {\n expression: `TIME(${field}) = ?`,\n bindings: [value],\n });\n return this;\n }\n\n /**\n * Day-of-month from a date field (1–31).\n * Uses a `whereRaw` op so SQL parsers get the `EXTRACT` expression directly.\n * MongoDB drivers override to emit `$dayOfMonth`.\n */\n public whereDay(field: string, value: number): this {\n this.addOperation(\"whereRaw\", {\n expression: `EXTRACT(DAY FROM ${field}) = ?`,\n bindings: [value],\n });\n return this;\n }\n\n /** Month extracted from a date field (1–12). */\n public whereMonth(field: string, value: number): this {\n this.addOperation(\"whereRaw\", {\n expression: `EXTRACT(MONTH FROM ${field}) = ?`,\n bindings: [value],\n });\n return this;\n }\n\n /** Year extracted from a date field. */\n public whereYear(field: string, value: number): this {\n this.addOperation(\"whereRaw\", {\n expression: `EXTRACT(YEAR FROM ${field}) = ?`,\n bindings: [value],\n });\n return this;\n }\n\n // ════════════════════════════════════════════════════════\n // WHERE CLAUSES — JSON / STRUCTURED DATA\n // ════════════════════════════════════════════════════════\n\n /**\n * JSON/array path contains the given value.\n * @example q.whereJsonContains(\"tags\", \"typescript\")\n */\n public whereJsonContains(path: string, value: unknown): this {\n this.addOperation(\"whereJsonContains\", { path, value });\n return this;\n }\n\n /** JSON/array path does NOT contain the value. */\n public whereJsonDoesntContain(path: string, value: unknown): this {\n this.addOperation(\"whereJsonDoesntContain\", { path, value });\n return this;\n }\n\n /**\n * JSON path key exists.\n * Uses a `whereRaw` so existing SQL parsers get `IS NOT NULL` immediately.\n */\n public whereJsonContainsKey(path: string): this {\n this.addOperation(\"whereRaw\", { expression: `${path} IS NOT NULL`, bindings: [] });\n return this;\n }\n\n /**\n * Constrain the length of a JSON array at a path.\n * @example q.whereJsonLength(\"tags\", \">\", 3)\n */\n public whereJsonLength(path: string, operator: WhereOperator, value: number): this {\n this.addOperation(\"whereRaw\", {\n expression: `jsonb_array_length(${path}) ${operator} ?`,\n bindings: [value],\n });\n return this;\n }\n\n /** JSON path must resolve to an array. */\n public whereJsonIsArray(path: string): this {\n this.addOperation(\"whereRaw\", {\n expression: `jsonb_typeof(${path}) = 'array'`,\n bindings: [],\n });\n return this;\n }\n\n /** JSON path must resolve to an object. */\n public whereJsonIsObject(path: string): this {\n this.addOperation(\"whereRaw\", {\n expression: `jsonb_typeof(${path}) = 'object'`,\n bindings: [],\n });\n return this;\n }\n\n /**\n * Constrain the number of elements in an array field.\n * @example q.whereArrayLength(\"roles\", \">=\", 2)\n */\n public whereArrayLength(field: string, operator: WhereOperator, value: number): this {\n this.addOperation(\"whereRaw\", {\n expression: `array_length(${field}, 1) ${operator} ?`,\n bindings: [value],\n });\n return this;\n }\n\n // ════════════════════════════════════════════════════════\n // WHERE CLAUSES — CONVENIENCE SHORTCUTS\n // ════════════════════════════════════════════════════════\n\n /** WHERE id = value. */\n public whereId(value: string | number): this {\n return this.where(\"id\", value);\n }\n\n /** WHERE id IN values. */\n public whereIds(values: Array<string | number>): this {\n return this.whereIn(\"id\", values);\n }\n\n /** WHERE uuid = value. */\n public whereUuid(value: string): this {\n return this.where(\"uuid\", value);\n }\n\n /** WHERE ulid = value. */\n public whereUlid(value: string): this {\n return this.where(\"ulid\", value);\n }\n\n /**\n * Full-text search across one or more fields.\n * @example q.whereFullText([\"title\", \"body\"], \"typescript\")\n */\n public whereFullText(fields: string | string[], query: string): this {\n this.addOperation(\"whereFullText\", {\n fields: Array.isArray(fields) ? fields : [fields],\n query,\n });\n return this;\n }\n\n /** Full-text search (OR). */\n public orWhereFullText(fields: string | string[], query: string): this {\n return this.whereFullText(fields, query);\n }\n\n /** Alias for whereFullText with a single field. */\n public whereSearch(field: string, query: string): this {\n return this.whereFullText([field], query);\n }\n\n /**\n * Text search with optional extra equality filters.\n * MongoDB-style convenience shorthand.\n */\n public textSearch(query: string, filters?: WhereObject): this {\n if (filters) {\n for (const [key, value] of Object.entries(filters)) this.where(key, value as never);\n }\n return this;\n }\n\n // ════════════════════════════════════════════════════════\n // WHERE CLAUSES — EXISTENCE / SUBQUERIES\n // ════════════════════════════════════════════════════════\n\n /**\n * WHERE EXISTS (subquery callback) or field IS NOT NULL (string).\n *\n * @example\n * q.whereExists(sub => sub.where(\"userId\", \"users.id\"))\n * q.whereExists(\"optionalField\")\n */\n public whereExists(field: string): this;\n public whereExists(callback: WhereCallback<T>): this;\n public whereExists(param: string | WhereCallback<T>): this {\n if (typeof param === \"function\") {\n const sub = this.subQuery();\n param(sub as any);\n this.addOperation(\"whereExists\", { subquery: sub.operations });\n } else {\n this.addOperation(\"whereNotNull\", { field: param });\n }\n return this;\n }\n\n /**\n * WHERE NOT EXISTS (subquery callback) or field IS NULL (string).\n */\n public whereNotExists(field: string): this;\n public whereNotExists(callback: WhereCallback<T>): this;\n public whereNotExists(param: string | WhereCallback<T>): this {\n if (typeof param === \"function\") {\n const sub = this.subQuery();\n param(sub as any);\n this.addOperation(\"whereNotExists\", { subquery: sub.operations });\n } else {\n this.addOperation(\"whereNull\", { field: param });\n }\n return this;\n }\n\n /**\n * Constrain an array/collection field by element count.\n *\n * @example\n * q.whereSize(\"tags\", 3) // exactly 3\n * q.whereSize(\"tags\", \">=\", 1) // at least 1\n */\n public whereSize(field: string, size: number): this;\n public whereSize(field: string, operator: WhereOperator, size: number): this;\n public whereSize(field: string, ...args: unknown[]): this {\n const operator = args.length === 2 ? (args[0] as WhereOperator) : \"=\";\n const size = (args.length === 2 ? args[1] : args[0]) as number;\n return this.whereArrayLength(field, operator, size);\n }\n\n /**\n * AND NOT wrapper — negate a nested group.\n * @example q.whereNot(q => q.where(\"status\", \"banned\").where(\"role\", \"user\"))\n */\n public whereNot(callback: WhereCallback<T>): this {\n const sub = this.subQuery();\n callback(sub as any);\n this.addOperation(\"whereNot\", { nested: sub.operations });\n return this;\n }\n\n /** OR NOT wrapper. */\n public orWhereNot(callback: WhereCallback<T>): this {\n const sub = this.subQuery();\n callback(sub as any);\n this.addOperation(\"orWhereNot\", { nested: sub.operations });\n return this;\n }\n\n // ════════════════════════════════════════════════════════\n // JOINS — STANDARD SQL-STYLE\n // Note: Op type names match parser switch cases exactly.\n // join / innerJoin → INNER JOIN\n // leftJoin → LEFT JOIN\n // rightJoin → RIGHT JOIN\n // fullJoin → FULL OUTER JOIN\n // crossJoin → CROSS JOIN\n // joinRaw → raw expression\n // ════════════════════════════════════════════════════════\n\n /**\n * INNER JOIN.\n * @example q.join(\"categories\", \"posts.categoryId\", \"categories.id\")\n */\n public join(table: string, localField: string, foreignField: string): this;\n public join(options: JoinOptions): this;\n public join(...args: unknown[]): this {\n if (args.length === 3) {\n this.addOperation(\"join\", { table: args[0], localField: args[1], foreignField: args[2] });\n } else {\n this.addOperation(\"join\", args[0] as Record<string, unknown>);\n }\n return this;\n }\n\n /** LEFT JOIN. */\n public leftJoin(table: string, localField: string, foreignField: string): this;\n public leftJoin(options: JoinOptions): this;\n public leftJoin(...args: unknown[]): this {\n if (args.length === 3) {\n this.addOperation(\"leftJoin\", { table: args[0], localField: args[1], foreignField: args[2] });\n } else {\n this.addOperation(\"leftJoin\", args[0] as Record<string, unknown>);\n }\n return this;\n }\n\n /** RIGHT JOIN. */\n public rightJoin(table: string, localField: string, foreignField: string): this;\n public rightJoin(options: JoinOptions): this;\n public rightJoin(...args: unknown[]): this {\n if (args.length === 3) {\n this.addOperation(\"rightJoin\", {\n table: args[0],\n localField: args[1],\n foreignField: args[2],\n });\n } else {\n this.addOperation(\"rightJoin\", args[0] as Record<string, unknown>);\n }\n return this;\n }\n\n /** INNER JOIN (alias for join). */\n public innerJoin(table: string, localField: string, foreignField: string): this;\n public innerJoin(options: JoinOptions): this;\n public innerJoin(...args: unknown[]): this {\n if (args.length === 3) {\n this.addOperation(\"innerJoin\", {\n table: args[0],\n localField: args[1],\n foreignField: args[2],\n });\n } else {\n this.addOperation(\"innerJoin\", args[0] as Record<string, unknown>);\n }\n return this;\n }\n\n /** FULL OUTER JOIN. */\n public fullJoin(table: string, localField: string, foreignField: string): this;\n public fullJoin(options: JoinOptions): this;\n public fullJoin(...args: unknown[]): this {\n if (args.length === 3) {\n this.addOperation(\"fullJoin\", { table: args[0], localField: args[1], foreignField: args[2] });\n } else {\n this.addOperation(\"fullJoin\", args[0] as Record<string, unknown>);\n }\n return this;\n }\n\n /** CROSS JOIN. */\n public crossJoin(table: string): this {\n this.addOperation(\"crossJoin\", { table });\n return this;\n }\n\n /** Raw JOIN expression. Driver responsible for handling. */\n public joinRaw(expression: RawExpression, bindings?: unknown[]): this {\n this.addOperation(\"joinRaw\", { expression, bindings: bindings ?? [] });\n return this;\n }\n\n // ════════════════════════════════════════════════════════\n // RELATION EAGER LOADING — JOIN-BASED (joinWith)\n // ════════════════════════════════════════════════════════\n\n /**\n * Eager-load named relations via a single JOIN / $lookup query.\n *\n * Constraints are eagerly resolved at call time:\n * - Callbacks are invoked immediately → `subOps` stored in op data.\n * - Column shorthands are parsed into a `columns[]` array.\n *\n * The driver executor reads the `joinWith` op and uses the resolved data\n * alongside its own relation definition map to emit the appropriate SQL JOIN\n * or MongoDB $lookup stage.\n *\n * Supported arg forms (may be mixed):\n * - `\"author\"` / `[\"author\", \"category\"]` — no constraint\n * - `{ author: \"id,name\" }` — column shorthand\n * - `{ actions: q => q.where(\"status\",\"pending\").limit(5) }` — callback\n *\n * @example\n * Post.joinWith(\"author\", \"category\")\n * ChatMessage.joinWith({ actions: q => q.where(\"status\", \"pending\").limit(5) })\n * ChatMessage.joinWith({ org: \"id,name\", actions: q => q.orderBy(\"sort_order\") })\n */\n public joinWith(...args: unknown[]): this {\n const resolved: Record<string, { columns?: string[]; subOps?: Op[] }> = {};\n\n for (const arg of args) {\n if (typeof arg === \"string\") {\n resolved[arg] = {};\n } else if (Array.isArray(arg)) {\n for (const rel of arg as string[]) resolved[rel] = {};\n } else if (typeof arg === \"object\" && arg !== null) {\n for (const [rel, constraint] of Object.entries(arg as Record<string, JoinWithConstraint>)) {\n if (typeof constraint === \"function\") {\n const sub = this.subQuery();\n constraint(sub);\n resolved[rel] = { subOps: sub.operations };\n } else if (typeof constraint === \"string\" && constraint !== \"\") {\n resolved[rel] = {\n columns: constraint\n .split(\",\")\n .map((s) => s.trim())\n .filter(Boolean),\n };\n } else {\n resolved[rel] = {};\n }\n }\n }\n }\n\n this.addOperation(\"joinWith\", { resolved });\n return this;\n }\n\n // ════════════════════════════════════════════════════════\n // RELATION EAGER LOADING — SEPARATE QUERIES (with)\n // ════════════════════════════════════════════════════════\n\n /**\n * Eager-load relations via separate queries (N+1 avoided by batching).\n *\n * @example\n * q.with(\"posts\")\n * q.with(\"posts\", q => q.where(\"published\", true))\n * q.with({ posts: true, comments: q => q.limit(5) })\n */\n public with(\n ...args: (string | Record<string, boolean | ((q: any) => void)> | ((q: any) => void))[]\n ): this {\n for (let i = 0; i < args.length; i++) {\n const arg = args[i];\n if (typeof arg === \"string\") {\n const next = args[i + 1];\n if (typeof next === \"function\") {\n this.eagerLoadRelations.set(arg, next as (q: any) => void);\n i++;\n } else {\n this.eagerLoadRelations.set(arg, true);\n }\n } else if (typeof arg === \"object\" && arg !== null) {\n for (const [key, value] of Object.entries(\n arg as Record<string, boolean | ((q: any) => void)>,\n )) {\n this.eagerLoadRelations.set(key, value);\n }\n }\n }\n return this;\n }\n\n /**\n * Register one or more relation counts to emit alongside each result row.\n *\n * Accepts:\n * - Bare relation names (variadic strings or array): `withCount(\"posts\", \"comments\")`\n * - Alias shorthand: `withCount(\"posts as totalPosts\")`\n * - Object form for per-relation constraints / aliases:\n * `withCount({ posts: true, \"posts as approved\": (q) => q.where(\"approved\", true) })`\n *\n * Each entry is stored in `countRelations` keyed by its output column alias\n * (default `${relationName}Count`). The driver subclass consumes the map at\n * execute time to emit count expressions.\n *\n * @example\n * ```typescript\n * await User.query().withCount(\"posts\").get(); // postsCount\n * await User.query().withCount(\"posts as totalPosts\").get(); // totalPosts\n * await User.query()\n * .withCount({\n * posts: true,\n * \"posts as published\": (q) => q.where(\"isPublished\", true),\n * comments: \"commentTotal\",\n * })\n * .get();\n * ```\n */\n public withCount(...args: unknown[]): this {\n for (const arg of args) {\n if (typeof arg === \"string\") {\n this.recordCountEntry(arg);\n continue;\n }\n\n if (Array.isArray(arg)) {\n for (const spec of arg as string[]) {\n this.recordCountEntry(spec);\n }\n continue;\n }\n\n if (typeof arg === \"object\" && arg !== null) {\n const entries = Object.entries(\n arg as Record<string, true | string | ((query: any) => void)>,\n );\n\n for (const [key, value] of entries) {\n if (value === true) {\n this.recordCountEntry(key);\n } else if (typeof value === \"string\") {\n this.recordCountEntry(`${key} as ${value}`);\n } else if (typeof value === \"function\") {\n this.recordCountEntry(key, value);\n }\n }\n }\n }\n\n return this;\n }\n\n /**\n * Parse a count spec (\"relation\" or \"relation as alias\") into its relation\n * name and output alias, optionally capturing a constraint callback's\n * operations via a sub-builder. Stored in `countRelations` keyed by alias.\n */\n protected recordCountEntry(spec: string, constraint?: (query: any) => void): void {\n const { relation, alias } = this.parseCountSpec(spec);\n\n let constraintOps: Op[] | undefined;\n\n if (constraint) {\n const sub = this.subQuery();\n constraint(sub);\n constraintOps = sub.operations;\n }\n\n this.countRelations.set(alias, { relation, constraintOps });\n }\n\n /**\n * Split a `\"<relation>\"` or `\"<relation> as <alias>\"` spec. Returns the\n * resolved relation name and the output column alias (defaulting to\n * `${relation}Count` when no `as` is present).\n */\n protected parseCountSpec(spec: string): { relation: string; alias: string } {\n const trimmed = spec.trim();\n const match = /^(.+?)\\s+as\\s+(.+)$/i.exec(trimmed);\n\n if (!match) {\n return { relation: trimmed, alias: `${trimmed}Count` };\n }\n\n return { relation: match[1].trim(), alias: match[2].trim() };\n }\n\n /**\n * Filter to rows that have at least one related record.\n * @example q.has(\"comments\")\n * @example q.has(\"comments\", \">=\", 3)\n */\n public has(relation: string, operator?: WhereOperator, count?: number): this {\n this.addOperation(\"has\", { relation, operator: operator ?? \">=\", count: count ?? 1 });\n return this;\n }\n\n /**\n * Filter to rows with related records matching a sub-query (AND).\n * @example q.whereHas(\"comments\", q => q.where(\"approved\", true))\n */\n public whereHas(relation: string, callback: (q: any) => void): this {\n const sub = this.subQuery();\n callback(sub);\n this.addOperation(\"whereHas\", { relation, subquery: sub.operations });\n return this;\n }\n\n /** Same as whereHas but OR-joined. */\n public orWhereHas(relation: string, callback: (q: any) => void): this {\n const sub = this.subQuery();\n callback(sub);\n this.addOperation(\"orWhereHas\", { relation, subquery: sub.operations });\n return this;\n }\n\n /** Filter to rows with NO related records. */\n public doesntHave(relation: string): this {\n this.addOperation(\"doesntHave\", { relation });\n return this;\n }\n\n /** Filter to rows with NO related records matching conditions. */\n public whereDoesntHave(relation: string, callback: (q: any) => void): this {\n const sub = this.subQuery();\n callback(sub);\n this.addOperation(\"whereDoesntHave\", { relation, subquery: sub.operations });\n return this;\n }\n\n // ════════════════════════════════════════════════════════\n // SELECT / PROJECTION\n // ════════════════════════════════════════════════════════\n\n /**\n * Select specific columns.\n *\n * @example\n * q.select([\"id\", \"name\"])\n * q.select(\"id\", \"name\")\n * q.select({ name: 1, password: 0 }) // MongoDB-style projection\n */\n public select(fields: string[]): this;\n public select(fields: Record<string, 0 | 1 | boolean>): this;\n public select(...fields: Array<string | string[]>): this;\n public select(...args: unknown[]): this {\n if (args.length === 1 && Array.isArray(args[0])) {\n this.addOperation(\"select\", { fields: args[0] });\n } else if (args.length === 1 && typeof args[0] === \"object\" && !Array.isArray(args[0])) {\n this.addOperation(\"select\", { fields: args[0] as Record<string, unknown> });\n } else {\n this.addOperation(\"select\", { fields: (args as Array<string | string[]>).flat() });\n }\n return this;\n }\n\n /** Select a field under an alias. @example q.selectAs(\"fullName\", \"name\") */\n public selectAs(field: string, alias: string): this {\n this.addOperation(\"select\", { fields: { [field]: alias } });\n return this;\n }\n\n /**\n * Raw SELECT expression.\n * @example q.selectRaw(\"COUNT(*) AS total\")\n */\n public selectRaw(expression: RawExpression, bindings?: unknown[]): this {\n this.addOperation(\"selectRaw\", { expression, bindings: bindings ?? [] });\n return this;\n }\n\n /** Multiple raw SELECT expressions in one call. */\n public selectRawMany(\n definitions: Array<{ alias: string; expression: RawExpression; bindings?: unknown[] }>,\n ): this {\n for (const def of definitions) {\n this.selectRaw({ [def.alias]: def.expression }, def.bindings);\n }\n return this;\n }\n\n /** Subquery as a named projected field. */\n public selectSub(expression: RawExpression, alias: string): this {\n this.addOperation(\"selectRaw\", { expression: { [alias]: expression } });\n return this;\n }\n\n /** Alias for selectSub. */\n public addSelectSub(expression: RawExpression, alias: string): this {\n return this.selectSub(expression, alias);\n }\n\n /**\n * Aggregate function as a projected field.\n * @example q.selectAggregate(\"price\", \"sum\", \"totalRevenue\")\n */\n public selectAggregate(\n field: string,\n aggregate: \"sum\" | \"avg\" | \"min\" | \"max\" | \"count\" | \"first\" | \"last\",\n alias: string,\n ): this {\n return this.selectRaw({ [alias]: `${aggregate.toUpperCase()}(${field})` });\n }\n\n /** Existence check as a projected boolean field. */\n public selectExists(field: string, alias: string): this {\n return this.selectRaw({ [alias]: `${field} IS NOT NULL` });\n }\n\n /** COUNT as a projected field. */\n public selectCount(field: string, alias: string): this {\n return this.selectAggregate(field, \"count\", alias);\n }\n\n /**\n * CASE / switch expression.\n * @example q.selectCase([{ when: \"status = 1\", then: \"'active'\" }], \"'inactive'\", \"statusLabel\")\n */\n public selectCase(\n cases: Array<{ when: RawExpression; then: RawExpression | unknown }>,\n otherwise: RawExpression | unknown,\n alias: string,\n ): this {\n const caseExpr = cases.map((c) => `WHEN ${c.when} THEN ${c.then}`).join(\" \");\n return this.selectRaw({ [alias]: `CASE ${caseExpr} ELSE ${otherwise} END` });\n }\n\n /** IF/ELSE conditional field. */\n public selectWhen(\n condition: RawExpression,\n thenValue: RawExpression | unknown,\n elseValue: RawExpression | unknown,\n alias: string,\n ): this {\n return this.selectRaw({\n [alias]: `CASE WHEN ${condition} THEN ${thenValue} ELSE ${elseValue} END`,\n });\n }\n\n /**\n * Driver-native projection manipulation.\n * No-op in base — override in driver subclasses.\n */\n public selectDriverProjection(_callback: (projection: Record<string, unknown>) => void): this {\n return this;\n }\n\n /** JSON path extraction as a projected field. */\n public selectJson(path: string, alias?: string): this {\n const parts = path.split(\"->\");\n const column = parts[0];\n const jsonPath = parts.slice(1).join(\"->\");\n const expr = jsonPath ? `${column}->>'${jsonPath}'` : column;\n return alias ? this.selectAs(expr, alias) : this.selectRaw(expr);\n }\n\n /** JSON extraction via raw expression. */\n public selectJsonRaw(_path: string, expression: RawExpression, alias: string): this {\n return this.selectRaw({ [alias]: expression });\n }\n\n /** Exclude a JSON path from projection. */\n public deselectJson(path: string): this {\n return this.deselect([path]);\n }\n\n /** String concatenation as a projected field. */\n public selectConcat(fields: Array<string | RawExpression>, alias: string): this {\n return this.selectRaw({ [alias]: fields.join(\" || \") });\n }\n\n /** COALESCE (first non-null) as a projected field. */\n public selectCoalesce(fields: Array<string | RawExpression>, alias: string): this {\n return this.selectRaw({ [alias]: `COALESCE(${fields.join(\", \")})` });\n }\n\n /** Window function expression. */\n public selectWindow(spec: RawExpression): this {\n this.addOperation(\"selectRaw\", { expression: spec });\n return this;\n }\n\n /** Exclude specific columns from results. */\n public deselect(fields: string[]): this {\n this.addOperation(\"deselect\", { fields });\n return this;\n }\n\n /**\n * Remove all select operations (resets to wildcard).\n * Uses `rebuildIndex()` — no unsafe casts.\n */\n public clearSelect(): this {\n this.operations = this.operations.filter(\n (op) => !op.type.startsWith(\"select\") && op.type !== \"deselect\",\n );\n this.rebuildIndex();\n return this;\n }\n\n /** Alias for clearSelect. */\n public selectAll(): this {\n return this.clearSelect();\n }\n\n /** Alias for clearSelect. */\n public selectDefault(): this {\n return this.clearSelect();\n }\n\n /** Append additional fields to existing selection. */\n public addSelect(fields: string[]): this {\n this.addOperation(\"select\", { fields, add: true });\n return this;\n }\n\n /**\n * Record a DISTINCT flag (fluent — does not execute).\n * Subclasses expose a separate async `distinct(field)` execution method.\n */\n public distinctValues(fields?: string | string[]): this {\n const fieldList = fields ? (Array.isArray(fields) ? fields : [fields]) : [];\n this.addOperation(\"distinct\", { fields: fieldList });\n return this;\n }\n\n // ════════════════════════════════════════════════════════\n // ORDERING\n // ════════════════════════════════════════════════════════\n\n /**\n * ORDER BY a column.\n *\n * @example\n * q.orderBy(\"createdAt\", \"desc\")\n * q.orderBy({ name: \"asc\", age: \"desc\" })\n */\n public orderBy(field: string, direction?: OrderDirection): this;\n public orderBy(fields: Record<string, OrderDirection>): this;\n public orderBy(...args: unknown[]): this {\n if (typeof args[0] === \"string\") {\n this.addOperation(\"orderBy\", {\n field: args[0],\n direction: (args[1] as OrderDirection) ?? \"asc\",\n });\n } else {\n for (const [field, direction] of Object.entries(args[0] as Record<string, OrderDirection>)) {\n this.addOperation(\"orderBy\", { field, direction });\n }\n }\n return this;\n }\n\n /** ORDER BY descending shorthand. */\n public orderByDesc(field: string): this {\n return this.orderBy(field, \"desc\");\n }\n\n /**\n * Raw ORDER BY expression.\n * @example q.orderByRaw(\"RANDOM()\")\n * @example q.orderByRaw({ $meta: \"textScore\" })\n */\n public orderByRaw(expression: RawExpression, bindings?: unknown[]): this {\n this.addOperation(\"orderByRaw\", { expression, bindings: bindings ?? [] });\n return this;\n }\n\n /**\n * Random order. Maps to `RANDOM()` in SQL or `$sample` in MongoDB.\n * @param limit - Optional limit (required for MongoDB $sample)\n */\n public orderByRandom(limit?: number): this {\n this.addOperation(\"orderByRaw\", { expression: \"RANDOM()\" });\n if (limit !== undefined) this.limit(limit);\n return this;\n }\n\n /** Order ascending by a date column (oldest first). */\n public oldest(column = \"createdAt\"): this {\n return this.orderBy(column, \"asc\");\n }\n\n // ════════════════════════════════════════════════════════\n // LIMIT / OFFSET\n // ════════════════════════════════════════════════════════\n\n /** Limit number of results. */\n public limit(value: number): this {\n this.addOperation(\"limit\", { value });\n return this;\n }\n\n /** Skip N results (OFFSET). */\n public skip(value: number): this {\n this.addOperation(\"offset\", { value });\n return this;\n }\n\n /** Alias for skip. */\n public offset(value: number): this {\n return this.skip(value);\n }\n\n /** Alias for limit. */\n public take(value: number): this {\n return this.limit(value);\n }\n\n // ════════════════════════════════════════════════════════\n // ROW LOCKING\n // ════════════════════════════════════════════════════════\n\n /**\n * Lock the selected rows for update (`SELECT ... FOR UPDATE`).\n *\n * `skipLocked` skips rows other transactions hold locks on (concurrent\n * queue-claim shape); `noWait` errors immediately instead of waiting. The\n * two are mutually exclusive. Only meaningful inside a transaction.\n *\n * SQL drivers emit the locking clause; drivers without row locking\n * (MongoDB) override this to throw.\n */\n public lockForUpdate(options?: LockForUpdateOptions): this {\n if (options?.skipLocked && options?.noWait) {\n throw new Error(\"lockForUpdate: `skipLocked` and `noWait` are mutually exclusive.\");\n }\n\n this.addOperation(\"lock\", {\n mode: \"update\",\n skipLocked: options?.skipLocked ?? false,\n noWait: options?.noWait ?? false,\n });\n return this;\n }\n\n // ════════════════════════════════════════════════════════\n // GROUPING / AGGREGATION\n // ════════════════════════════════════════════════════════\n\n /**\n * GROUP BY clause.\n * @example q.groupBy(\"status\")\n * @example q.groupBy([\"year\", \"month\"])\n */\n public groupBy(input: GroupByInput): this {\n const fields = Array.isArray(input) ? input : [input];\n this.addOperation(\"groupBy\", { fields });\n return this;\n }\n\n /** Raw GROUP BY expression. */\n public groupByRaw(expression: RawExpression, bindings?: unknown[]): this {\n this.addOperation(\"groupBy\", { expression, bindings: bindings ?? [] });\n return this;\n }\n\n /**\n * HAVING clause (post-group filter).\n *\n * @example\n * q.having(\"total\", \">\", 100)\n * q.having([\"total\", \">\", 100])\n * q.having({ total: 100 })\n */\n public having(field: string, value: unknown): this;\n public having(field: string, operator: WhereOperator, value: unknown): this;\n public having(condition: HavingInput): this;\n public having(...args: unknown[]): this {\n if (args.length === 1) {\n const input = args[0] as HavingInput;\n if (Array.isArray(input)) {\n if (input.length === 2) {\n this.addOperation(\"having\", { field: input[0], operator: \"=\", value: input[1] });\n } else {\n this.addOperation(\"having\", { field: input[0], operator: input[1], value: input[2] });\n }\n } else {\n for (const [key, value] of Object.entries(input as Record<string, unknown>)) {\n this.addOperation(\"having\", { field: key, operator: \"=\", value });\n }\n }\n } else if (args.length === 2) {\n this.addOperation(\"having\", { field: args[0], operator: \"=\", value: args[1] });\n } else {\n this.addOperation(\"having\", { field: args[0], operator: args[1], value: args[2] });\n }\n return this;\n }\n\n /** Raw HAVING expression. */\n public havingRaw(expression: RawExpression, bindings?: unknown[]): this {\n this.addOperation(\"havingRaw\", { expression, bindings: bindings ?? [] });\n return this;\n }\n\n // ════════════════════════════════════════════════════════\n // UTILITY / CONTROL FLOW\n // ════════════════════════════════════════════════════════\n\n /**\n * Side-effect tap — executes callback synchronously and returns `this`.\n * @example q.where(...).tap(q => console.log(q.operations.length)).limit(10)\n */\n public tap(callback: (builder: this) => void): this {\n callback(this);\n return this;\n }\n\n /**\n * Conditionally apply query modifications.\n *\n * @example\n * q.when(userId, (q, id) => q.where(\"userId\", id))\n * q.when(isAdmin, q => q.withoutGlobalScopes(), q => q.scope(\"active\"))\n */\n public when<V>(\n condition: V | boolean,\n callback: (builder: this, value: V) => void,\n otherwise?: (builder: this) => void,\n ): this {\n if (condition) {\n callback(this, condition as V);\n } else if (otherwise) {\n otherwise(this);\n }\n return this;\n }\n}\n"],"mappings":"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"}
|
package/llms-full.txt
CHANGED
|
@@ -1432,7 +1432,7 @@ There is no `has(name)` / `list()` / `setDefault()` — guard with a `try/catch`
|
|
|
1432
1432
|
|
|
1433
1433
|
---
|
|
1434
1434
|
name: manage-transactions
|
|
1435
|
-
description: 'Wrap multi-statement work in `transaction(async () => {...})` — rollback on throw, commit on resolve, optional `isolation` level (Postgres), per-`dataSource` scope. Also the home for 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, `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", "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`.'
|
|
1435
|
+
description: '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`.'
|
|
1436
1436
|
---
|
|
1437
1437
|
|
|
1438
1438
|
# Use transactions
|
|
@@ -1531,6 +1531,36 @@ await transaction(async () => {
|
|
|
1531
1531
|
|
|
1532
1532
|
On `SERIALIZABLE`, Postgres may abort with a serialization failure when concurrent transactions conflict — wrap in retry logic at the caller.
|
|
1533
1533
|
|
|
1534
|
+
## Row locking — `lockForUpdate({ skipLocked })` (Postgres)
|
|
1535
|
+
|
|
1536
|
+
Chain `lockForUpdate()` on any query to emit `SELECT ... FOR UPDATE`: the matched rows are locked until the transaction ends, so no other transaction can modify or lock them. Only meaningful **inside** a transaction — outside one the lock releases immediately.
|
|
1537
|
+
|
|
1538
|
+
`{ skipLocked: true }` adds `SKIP LOCKED` — rows another transaction already holds are silently skipped instead of waited on. That is the standard concurrent queue-claim shape: N workers each grab *different* pending rows with no double-processing and no blocking.
|
|
1539
|
+
|
|
1540
|
+
```ts
|
|
1541
|
+
import { transaction } from "@warlock.js/cascade";
|
|
1542
|
+
|
|
1543
|
+
// Worker loop — each concurrent worker claims a disjoint batch:
|
|
1544
|
+
const jobs = await transaction(async () => {
|
|
1545
|
+
const claimed = await Job.query()
|
|
1546
|
+
.where("status", "pending")
|
|
1547
|
+
.orderBy("id", "asc")
|
|
1548
|
+
.limit(10)
|
|
1549
|
+
.lockForUpdate({ skipLocked: true })
|
|
1550
|
+
.get();
|
|
1551
|
+
|
|
1552
|
+
for (const job of claimed) {
|
|
1553
|
+
await job.merge({ status: "processing" }).save();
|
|
1554
|
+
}
|
|
1555
|
+
|
|
1556
|
+
return claimed;
|
|
1557
|
+
});
|
|
1558
|
+
```
|
|
1559
|
+
|
|
1560
|
+
`{ noWait: true }` errors immediately when a matching row is locked (instead of waiting or skipping) — mutually exclusive with `skipLocked`.
|
|
1561
|
+
|
|
1562
|
+
**Postgres-only.** MongoDB has no row-level SELECT locking — the MongoDB driver **throws** on `lockForUpdate()`. On Mongo, claim atomically instead (`findOneAndUpdate` with a reservation filter — see `@warlock.js/cascade/perform-atomic-ops/SKILL.md`).
|
|
1563
|
+
|
|
1534
1564
|
## Outside the transaction
|
|
1535
1565
|
|
|
1536
1566
|
Once the callback returns, the transaction is committed. Subsequent calls — including reads — see the committed state. Don't try to "share" a model instance between inside-transaction and outside contexts; reload outside if you need fresh state.
|
package/llms.txt
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
- [define-model](@warlock.js/cascade/define-model/SKILL.md): Define a Cascade model — `@RegisterModel()`, class extends `Model<TSchema>`, `static table`, `static schema`, three update idioms (`.set` / `.merge` / `.save`), `.unset`, `.destroy`, `static toJsonColumns` / `resource` for output shaping. Triggers: `Model`, `RegisterModel`, `static schema`, `.set`, `.merge`, `.save`, `.unset`, `.destroy`, `toJsonColumns`, `resource`; "how do I define a model", "shape the JSON output", "remove a field"; typical import `import { Model, RegisterModel } from "@warlock.js/cascade"`. Skip: querying — `@warlock.js/cascade/query-data/SKILL.md`; relations — `@warlock.js/cascade/define-relations/SKILL.md`; competing libs `mongoose`, `prisma`, `typeorm` `@Entity`.
|
|
14
14
|
- [define-relations](@warlock.js/cascade/define-relations/SKILL.md): Define and query relations — `@BelongsTo` / `@HasMany` / `@BelongsToMany`, `.with("relation")` eager loading, `.whereHas(relation, cb)` filter-by-related, `setRelation` on save, `.joinWith` for SQL joins, `loadRelation`, `lazy(() => Model)`. Triggers: `@BelongsTo`, `@HasMany`, `@BelongsToMany`, `.with`, `.whereHas`, `setRelation`, `.joinWith`, `lazy`; "define a relation", "avoid N+1", "eager load posts", "filter parents by child"; typical import `import { BelongsTo, HasMany, BelongsToMany } from "@warlock.js/cascade"`. Skip: model basics — `@warlock.js/cascade/define-model/SKILL.md`; competing libs `mongoose populate`, `prisma include`, `typeorm relations`.
|
|
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
|
-
- [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 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, `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", "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`.
|
|
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
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. Triggers: `Model.increase`, `Model.decrease`, `Model.atomic`, `Model.createMany`, `createMany bulk`, `batchSize`, `Model.findAndUpdate`, `Model.delete`, `$inc`, `$set`; "increment counter under concurrency", "bulk insert without N+1", "fast bulk insert", "insert thousands of rows", "atomic update without loading"; 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
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`. Triggers: `.where`, `.find`, `.first`, `.firstOrFail`, `.all`, `.get`, `.orderBy`, `.exists`, `.whereIn`, `.whereBetween`, `addScope`; "filter by status", "find by id", "fetch active users", "check existence"; 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`.
|
package/package.json
CHANGED
|
@@ -27,9 +27,9 @@
|
|
|
27
27
|
"@mongez/events": "^2.2.6",
|
|
28
28
|
"@mongez/reinforcements": "^3.3.0",
|
|
29
29
|
"@mongez/supportive-is": "^2.1.3",
|
|
30
|
-
"@warlock.js/context": "4.
|
|
31
|
-
"@warlock.js/logger": "4.
|
|
32
|
-
"@warlock.js/seal": "4.
|
|
30
|
+
"@warlock.js/context": "4.7.0",
|
|
31
|
+
"@warlock.js/logger": "4.7.0",
|
|
32
|
+
"@warlock.js/seal": "4.7.0",
|
|
33
33
|
"citty": "^0.2.2",
|
|
34
34
|
"fast-glob": "^3.3.3"
|
|
35
35
|
},
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
"bin": {
|
|
41
41
|
"cascade": "bin/cascade.js"
|
|
42
42
|
},
|
|
43
|
-
"version": "4.
|
|
43
|
+
"version": "4.7.0",
|
|
44
44
|
"main": "./cjs/index.cjs",
|
|
45
45
|
"module": "./esm/index.mjs",
|
|
46
46
|
"types": "./esm/index.d.mts",
|
package/skills/README.md
CHANGED
|
@@ -34,7 +34,7 @@ Configure multiple databases — register each via `connectToDatabase({ name, dr
|
|
|
34
34
|
|
|
35
35
|
### [`manage-transactions/`](./manage-transactions/SKILL.md)
|
|
36
36
|
|
|
37
|
-
Wrap multi-statement work in transaction(async () => {...}) — rollback on throw or `ctx.rollback()`, commit on resolve, `isolationLevel` option (Postgres). Postgres native, MongoDB requires replica set;
|
|
37
|
+
Wrap multi-statement work in transaction(async () => {...}) — rollback on throw or `ctx.rollback()`, commit on resolve, `isolationLevel` option (Postgres). Postgres native, MongoDB requires replica set; nested calls flat-join the outer transaction. Also covers row locking (`lockForUpdate({ skipLocked })` → `FOR UPDATE SKIP LOCKED`, Postgres-only — the concurrent queue-claim shape), transaction-aware raw SQL (`Model.raw` / `DataSource.raw` → `RawQueryResult`) and the Postgres `nativeArrayColumns` connection option. Load when two or more writes must succeed or fail together (creating parent + children, transferring balances, multi-step state machines), locking rows against concurrent workers, or when running raw SQL.
|
|
38
38
|
|
|
39
39
|
### [`paginate-results/`](./paginate-results/SKILL.md)
|
|
40
40
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: manage-transactions
|
|
3
|
-
description: 'Wrap multi-statement work in `transaction(async () => {...})` — rollback on throw, commit on resolve, optional `isolation` level (Postgres), per-`dataSource` scope. Also the home for 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, `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", "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`.'
|
|
3
|
+
description: '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`.'
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Use transactions
|
|
@@ -99,6 +99,36 @@ await transaction(async () => {
|
|
|
99
99
|
|
|
100
100
|
On `SERIALIZABLE`, Postgres may abort with a serialization failure when concurrent transactions conflict — wrap in retry logic at the caller.
|
|
101
101
|
|
|
102
|
+
## Row locking — `lockForUpdate({ skipLocked })` (Postgres)
|
|
103
|
+
|
|
104
|
+
Chain `lockForUpdate()` on any query to emit `SELECT ... FOR UPDATE`: the matched rows are locked until the transaction ends, so no other transaction can modify or lock them. Only meaningful **inside** a transaction — outside one the lock releases immediately.
|
|
105
|
+
|
|
106
|
+
`{ skipLocked: true }` adds `SKIP LOCKED` — rows another transaction already holds are silently skipped instead of waited on. That is the standard concurrent queue-claim shape: N workers each grab *different* pending rows with no double-processing and no blocking.
|
|
107
|
+
|
|
108
|
+
```ts
|
|
109
|
+
import { transaction } from "@warlock.js/cascade";
|
|
110
|
+
|
|
111
|
+
// Worker loop — each concurrent worker claims a disjoint batch:
|
|
112
|
+
const jobs = await transaction(async () => {
|
|
113
|
+
const claimed = await Job.query()
|
|
114
|
+
.where("status", "pending")
|
|
115
|
+
.orderBy("id", "asc")
|
|
116
|
+
.limit(10)
|
|
117
|
+
.lockForUpdate({ skipLocked: true })
|
|
118
|
+
.get();
|
|
119
|
+
|
|
120
|
+
for (const job of claimed) {
|
|
121
|
+
await job.merge({ status: "processing" }).save();
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
return claimed;
|
|
125
|
+
});
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
`{ noWait: true }` errors immediately when a matching row is locked (instead of waiting or skipping) — mutually exclusive with `skipLocked`.
|
|
129
|
+
|
|
130
|
+
**Postgres-only.** MongoDB has no row-level SELECT locking — the MongoDB driver **throws** on `lockForUpdate()`. On Mongo, claim atomically instead (`findOneAndUpdate` with a reservation filter — see `@warlock.js/cascade/perform-atomic-ops/SKILL.md`).
|
|
131
|
+
|
|
102
132
|
## Outside the transaction
|
|
103
133
|
|
|
104
134
|
Once the callback returns, the transaction is committed. Subsequent calls — including reads — see the committed state. Don't try to "share" a model instance between inside-transaction and outside contexts; reload outside if you need fresh state.
|