prisma-sql 1.38.0 → 1.40.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/dist/generator.js CHANGED
@@ -54,7 +54,7 @@ var require_package = __commonJS({
54
54
  "package.json"(exports$1, module) {
55
55
  module.exports = {
56
56
  name: "prisma-sql",
57
- version: "1.38.0",
57
+ version: "1.40.0",
58
58
  description: "Convert Prisma queries to optimized SQL with type safety. 2-7x faster than Prisma Client.",
59
59
  main: "dist/index.cjs",
60
60
  module: "dist/index.js",
@@ -95,17 +95,6 @@ var require_package = __commonJS({
95
95
  "sonar-cli": "sonar-scanner -Dsonar.projectKey=prisma-sql -Dsonar.sources=./src -Dsonar.host.url=http://localhost:9000 -Dsonar.login=sqp_9fe07460d0aa83f711d0edf4f317f05019d0613b",
96
96
  sonar: "yarn sonar-cli && npx tsx scripts/sonar.ts"
97
97
  },
98
- workspaces: {
99
- packages: [
100
- "demo"
101
- ],
102
- nohoist: [
103
- "**/prisma",
104
- "**/prisma/**",
105
- "**/@prisma/**",
106
- "**/prisma-*"
107
- ]
108
- },
109
98
  keywords: [
110
99
  "prisma",
111
100
  "sql",
@@ -138,7 +127,6 @@ var require_package = __commonJS({
138
127
  "@faker-js/faker": "^10.2.0",
139
128
  "@prisma/adapter-better-sqlite3": "^7.2.0",
140
129
  "@prisma/adapter-pg": "^7.2.0",
141
- "@prisma/client": "7.2.0",
142
130
  "@types/better-sqlite3": "^7.6.13",
143
131
  "@types/node": "^25.0.10",
144
132
  "@vitest/coverage-v8": "4.0.18",
@@ -146,11 +134,12 @@ var require_package = __commonJS({
146
134
  "drizzle-kit": "^0.31.8",
147
135
  "drizzle-orm": "^0.45.1",
148
136
  postgres: "^3.4.8",
149
- prisma: "7.2.0",
150
137
  tsup: "^8.5.1",
151
138
  tsx: "^4.21.0",
152
139
  typescript: "^5.9.3",
153
- vitest: "^4.0.18"
140
+ vitest: "^4.0.18",
141
+ "@prisma/client": "7.2.0",
142
+ prisma: "7.2.0"
154
143
  },
155
144
  engines: {
156
145
  node: ">=16.0.0"
@@ -159,6 +148,159 @@ var require_package = __commonJS({
159
148
  }
160
149
  });
161
150
 
151
+ // src/builder/shared/constants.ts
152
+ var SQL_SEPARATORS = Object.freeze({
153
+ FIELD_LIST: ", ",
154
+ CONDITION_AND: " AND ",
155
+ CONDITION_OR: " OR ",
156
+ ORDER_BY: ", "
157
+ });
158
+ var SQL_RESERVED_WORDS = /* @__PURE__ */ new Set([
159
+ "select",
160
+ "from",
161
+ "where",
162
+ "and",
163
+ "or",
164
+ "not",
165
+ "in",
166
+ "like",
167
+ "between",
168
+ "order",
169
+ "by",
170
+ "group",
171
+ "having",
172
+ "limit",
173
+ "offset",
174
+ "join",
175
+ "inner",
176
+ "left",
177
+ "right",
178
+ "outer",
179
+ "on",
180
+ "as",
181
+ "table",
182
+ "column",
183
+ "index",
184
+ "user",
185
+ "users",
186
+ "values",
187
+ "update",
188
+ "insert",
189
+ "delete",
190
+ "create",
191
+ "drop",
192
+ "alter",
193
+ "truncate",
194
+ "grant",
195
+ "revoke",
196
+ "exec",
197
+ "execute",
198
+ "union",
199
+ "intersect",
200
+ "except",
201
+ "case",
202
+ "when",
203
+ "then",
204
+ "else",
205
+ "end",
206
+ "null",
207
+ "true",
208
+ "false",
209
+ "is",
210
+ "exists",
211
+ "all",
212
+ "any",
213
+ "some"
214
+ ]);
215
+ var SQL_KEYWORDS = SQL_RESERVED_WORDS;
216
+ var DEFAULT_WHERE_CLAUSE = "1=1";
217
+ var SPECIAL_FIELDS = Object.freeze({
218
+ ID: "id"
219
+ });
220
+ var SQL_TEMPLATES = Object.freeze({
221
+ PUBLIC_SCHEMA: "public",
222
+ WHERE: "WHERE",
223
+ SELECT: "SELECT",
224
+ FROM: "FROM",
225
+ ORDER_BY: "ORDER BY",
226
+ GROUP_BY: "GROUP BY",
227
+ HAVING: "HAVING",
228
+ LIMIT: "LIMIT",
229
+ OFFSET: "OFFSET",
230
+ COUNT_ALL: "COUNT(*)",
231
+ AS: "AS",
232
+ DISTINCT_ON: "DISTINCT ON",
233
+ IS_NULL: "IS NULL",
234
+ IS_NOT_NULL: "IS NOT NULL",
235
+ LIKE: "LIKE",
236
+ AND: "AND",
237
+ OR: "OR",
238
+ NOT: "NOT"
239
+ });
240
+ var SCHEMA_PREFIXES = Object.freeze({
241
+ INTERNAL: "@",
242
+ COMMENT: "//"
243
+ });
244
+ var Ops = Object.freeze({
245
+ EQUALS: "equals",
246
+ NOT: "not",
247
+ GT: "gt",
248
+ GTE: "gte",
249
+ LT: "lt",
250
+ LTE: "lte",
251
+ IN: "in",
252
+ NOT_IN: "notIn",
253
+ CONTAINS: "contains",
254
+ STARTS_WITH: "startsWith",
255
+ ENDS_WITH: "endsWith",
256
+ HAS: "has",
257
+ HAS_SOME: "hasSome",
258
+ HAS_EVERY: "hasEvery",
259
+ IS_EMPTY: "isEmpty",
260
+ PATH: "path",
261
+ STRING_CONTAINS: "string_contains",
262
+ STRING_STARTS_WITH: "string_starts_with",
263
+ STRING_ENDS_WITH: "string_ends_with"
264
+ });
265
+ var LogicalOps = Object.freeze({
266
+ AND: "AND",
267
+ OR: "OR",
268
+ NOT: "NOT"
269
+ });
270
+ var RelationFilters = Object.freeze({
271
+ SOME: "some",
272
+ EVERY: "every",
273
+ NONE: "none"
274
+ });
275
+ var Modes = Object.freeze({
276
+ INSENSITIVE: "insensitive",
277
+ DEFAULT: "default"
278
+ });
279
+ var Wildcards = Object.freeze({
280
+ [Ops.CONTAINS]: (v) => `%${v}%`,
281
+ [Ops.STARTS_WITH]: (v) => `${v}%`,
282
+ [Ops.ENDS_WITH]: (v) => `%${v}`
283
+ });
284
+ var REGEX_CACHE = {
285
+ VALID_IDENTIFIER: /^[a-z_][a-z0-9_]*$/
286
+ };
287
+ var LIMITS = Object.freeze({
288
+ MAX_QUERY_DEPTH: 50,
289
+ MAX_ARRAY_SIZE: 1e4,
290
+ MAX_STRING_LENGTH: 1e4
291
+ });
292
+
293
+ // src/utils/normalize-value.ts
294
+ function normalizeValue(value) {
295
+ if (value instanceof Date) {
296
+ return value.toISOString();
297
+ }
298
+ if (Array.isArray(value)) {
299
+ return value.map(normalizeValue);
300
+ }
301
+ return value;
302
+ }
303
+
162
304
  // src/sql-builder-dialect.ts
163
305
  var globalDialect = "postgres";
164
306
  function setGlobalDialect(dialect) {
@@ -335,153 +477,11 @@ function prepareArrayParam(value, dialect) {
335
477
  throw new Error("prepareArrayParam requires array value");
336
478
  }
337
479
  if (dialect === "postgres") {
338
- return value;
480
+ return value.map(normalizeValue);
339
481
  }
340
482
  return JSON.stringify(value);
341
483
  }
342
484
 
343
- // src/builder/shared/constants.ts
344
- var SQL_SEPARATORS = Object.freeze({
345
- FIELD_LIST: ", ",
346
- CONDITION_AND: " AND ",
347
- CONDITION_OR: " OR ",
348
- ORDER_BY: ", "
349
- });
350
- var SQL_RESERVED_WORDS = /* @__PURE__ */ new Set([
351
- "select",
352
- "from",
353
- "where",
354
- "and",
355
- "or",
356
- "not",
357
- "in",
358
- "like",
359
- "between",
360
- "order",
361
- "by",
362
- "group",
363
- "having",
364
- "limit",
365
- "offset",
366
- "join",
367
- "inner",
368
- "left",
369
- "right",
370
- "outer",
371
- "on",
372
- "as",
373
- "table",
374
- "column",
375
- "index",
376
- "user",
377
- "users",
378
- "values",
379
- "update",
380
- "insert",
381
- "delete",
382
- "create",
383
- "drop",
384
- "alter",
385
- "truncate",
386
- "grant",
387
- "revoke",
388
- "exec",
389
- "execute",
390
- "union",
391
- "intersect",
392
- "except",
393
- "case",
394
- "when",
395
- "then",
396
- "else",
397
- "end",
398
- "null",
399
- "true",
400
- "false",
401
- "is",
402
- "exists",
403
- "all",
404
- "any",
405
- "some"
406
- ]);
407
- var SQL_KEYWORDS = SQL_RESERVED_WORDS;
408
- var DEFAULT_WHERE_CLAUSE = "1=1";
409
- var SPECIAL_FIELDS = Object.freeze({
410
- ID: "id"
411
- });
412
- var SQL_TEMPLATES = Object.freeze({
413
- PUBLIC_SCHEMA: "public",
414
- WHERE: "WHERE",
415
- SELECT: "SELECT",
416
- FROM: "FROM",
417
- ORDER_BY: "ORDER BY",
418
- GROUP_BY: "GROUP BY",
419
- HAVING: "HAVING",
420
- LIMIT: "LIMIT",
421
- OFFSET: "OFFSET",
422
- COUNT_ALL: "COUNT(*)",
423
- AS: "AS",
424
- DISTINCT_ON: "DISTINCT ON",
425
- IS_NULL: "IS NULL",
426
- IS_NOT_NULL: "IS NOT NULL",
427
- LIKE: "LIKE",
428
- AND: "AND",
429
- OR: "OR",
430
- NOT: "NOT"
431
- });
432
- var SCHEMA_PREFIXES = Object.freeze({
433
- INTERNAL: "@",
434
- COMMENT: "//"
435
- });
436
- var Ops = Object.freeze({
437
- EQUALS: "equals",
438
- NOT: "not",
439
- GT: "gt",
440
- GTE: "gte",
441
- LT: "lt",
442
- LTE: "lte",
443
- IN: "in",
444
- NOT_IN: "notIn",
445
- CONTAINS: "contains",
446
- STARTS_WITH: "startsWith",
447
- ENDS_WITH: "endsWith",
448
- HAS: "has",
449
- HAS_SOME: "hasSome",
450
- HAS_EVERY: "hasEvery",
451
- IS_EMPTY: "isEmpty",
452
- PATH: "path",
453
- STRING_CONTAINS: "string_contains",
454
- STRING_STARTS_WITH: "string_starts_with",
455
- STRING_ENDS_WITH: "string_ends_with"
456
- });
457
- var LogicalOps = Object.freeze({
458
- AND: "AND",
459
- OR: "OR",
460
- NOT: "NOT"
461
- });
462
- var RelationFilters = Object.freeze({
463
- SOME: "some",
464
- EVERY: "every",
465
- NONE: "none"
466
- });
467
- var Modes = Object.freeze({
468
- INSENSITIVE: "insensitive",
469
- DEFAULT: "default"
470
- });
471
- var Wildcards = Object.freeze({
472
- [Ops.CONTAINS]: (v) => `%${v}%`,
473
- [Ops.STARTS_WITH]: (v) => `${v}%`,
474
- [Ops.ENDS_WITH]: (v) => `%${v}`
475
- });
476
- var REGEX_CACHE = {
477
- VALID_IDENTIFIER: /^[a-z_][a-z0-9_]*$/
478
- };
479
- var LIMITS = Object.freeze({
480
- MAX_QUERY_DEPTH: 50,
481
- MAX_ARRAY_SIZE: 1e4,
482
- MAX_STRING_LENGTH: 1e4
483
- });
484
-
485
485
  // src/builder/shared/validators/type-guards.ts
486
486
  function isNotNullish(value) {
487
487
  return value !== null && value !== void 0;
@@ -567,6 +567,19 @@ function getRelationFieldSet(model) {
567
567
  RELATION_SET_CACHE.set(model, s);
568
568
  return s;
569
569
  }
570
+ var COLUMN_MAP_CACHE = /* @__PURE__ */ new WeakMap();
571
+ function getColumnMap(model) {
572
+ const cached = COLUMN_MAP_CACHE.get(model);
573
+ if (cached) return cached;
574
+ const map = /* @__PURE__ */ new Map();
575
+ for (const f of model.fields) {
576
+ if (!f.isRelation) {
577
+ map.set(f.name, f.dbName || f.name);
578
+ }
579
+ }
580
+ COLUMN_MAP_CACHE.set(model, map);
581
+ return map;
582
+ }
570
583
 
571
584
  // src/builder/shared/validators/sql-validators.ts
572
585
  function isValidWhereClause(clause) {
@@ -954,27 +967,10 @@ function quote(id) {
954
967
  }
955
968
  return id;
956
969
  }
957
- function pickDbFieldName(field) {
958
- const candidates = [
959
- field == null ? void 0 : field.dbName,
960
- field == null ? void 0 : field.columnName,
961
- field == null ? void 0 : field.databaseName,
962
- field == null ? void 0 : field.mappedName
963
- ];
964
- for (const c of candidates) {
965
- if (typeof c === "string") {
966
- const s = c.trim();
967
- if (s.length > 0) return s;
968
- }
969
- }
970
- return void 0;
971
- }
972
970
  function resolveColumnName(model, fieldName) {
973
- var _a;
974
971
  if (!model) return fieldName;
975
- const f = model.fields.find((x) => (x == null ? void 0 : x.name) === fieldName);
976
- if (!f) return fieldName;
977
- return (_a = pickDbFieldName(f)) != null ? _a : fieldName;
972
+ const columnMap = getColumnMap(model);
973
+ return columnMap.get(fieldName) || fieldName;
978
974
  }
979
975
  function quoteColumn(model, fieldName) {
980
976
  return quote(resolveColumnName(model, fieldName));
@@ -1132,1570 +1128,1570 @@ function joinCondition(field, parentModel, childModel, parentAlias, childAlias)
1132
1128
  function getModelByName(schemas, name) {
1133
1129
  return schemas.find((m) => m.name === name);
1134
1130
  }
1135
- function buildNotComposite(expr, val, params, dialect, buildOp, separator) {
1136
- const entries = Object.entries(val).filter(
1137
- ([k, v]) => k !== "mode" && v !== void 0
1138
- );
1139
- if (entries.length === 0) return "";
1140
- const clauses = [];
1141
- for (const [subOp, subVal] of entries) {
1142
- const sub = buildOp(expr, subOp, subVal, params, dialect);
1143
- if (sub && sub.trim().length > 0) clauses.push(`(${sub})`);
1144
- }
1145
- if (clauses.length === 0) return "";
1146
- if (clauses.length === 1) return `${SQL_TEMPLATES.NOT} ${clauses[0]}`;
1147
- return `${SQL_TEMPLATES.NOT} (${clauses.join(separator)})`;
1148
- }
1149
- function buildScalarOperator(expr, op, val, params, mode, fieldType, dialect) {
1150
- if (val === void 0) return "";
1151
- if (val === null) {
1152
- return handleNullValue(expr, op);
1153
- }
1154
- if (op === Ops.NOT && isPlainObject(val)) {
1155
- return handleNotOperator(expr, val, params, mode, fieldType, dialect);
1156
- }
1157
- if (op === Ops.NOT) {
1158
- const placeholder = params.addAuto(val);
1159
- return `${expr} <> ${placeholder}`;
1131
+ function normalizeIntLike(name, v, opts = {}) {
1132
+ var _a, _b;
1133
+ if (!isNotNullish(v)) return void 0;
1134
+ if (isDynamicParameter(v)) return v;
1135
+ if (typeof v !== "number" || !Number.isFinite(v) || !Number.isInteger(v)) {
1136
+ throw new Error(`${name} must be an integer`);
1160
1137
  }
1161
- if (op === Ops.EQUALS && mode === Modes.INSENSITIVE && isNotNullish(dialect)) {
1162
- const placeholder = params.addAuto(val);
1163
- return caseInsensitiveEquals(expr, placeholder);
1138
+ const min = (_a = opts.min) != null ? _a : 0;
1139
+ const allowZero = (_b = opts.allowZero) != null ? _b : true;
1140
+ if (!allowZero && v === 0) {
1141
+ throw new Error(`${name} must be > 0`);
1164
1142
  }
1165
- const STRING_LIKE_OPS = /* @__PURE__ */ new Set([
1166
- Ops.CONTAINS,
1167
- Ops.STARTS_WITH,
1168
- Ops.ENDS_WITH
1169
- ]);
1170
- if (STRING_LIKE_OPS.has(op)) {
1171
- if (!isNotNullish(dialect)) {
1172
- throw createError(`Like operators require a SQL dialect`, {
1173
- operator: op
1174
- });
1175
- }
1176
- return handleLikeOperator(expr, op, val, params, mode, dialect);
1143
+ if (v < min) {
1144
+ throw new Error(`${name} must be >= ${min}`);
1177
1145
  }
1178
- if (op === Ops.IN || op === Ops.NOT_IN) {
1179
- if (!isNotNullish(dialect)) {
1180
- throw createError(`IN operators require a SQL dialect`, { operator: op });
1181
- }
1182
- return handleInOperator(expr, op, val, params, dialect);
1146
+ if (typeof opts.max === "number" && v > opts.max) {
1147
+ throw new Error(`${name} must be <= ${opts.max}`);
1183
1148
  }
1184
- return handleComparisonOperator(expr, op, val, params);
1185
- }
1186
- function handleNullValue(expr, op) {
1187
- if (op === Ops.EQUALS) return `${expr} ${SQL_TEMPLATES.IS_NULL}`;
1188
- if (op === Ops.NOT) return `${expr} ${SQL_TEMPLATES.IS_NOT_NULL}`;
1189
- throw createError(`Operator '${op}' doesn't support null`, { operator: op });
1190
- }
1191
- function normalizeMode(v) {
1192
- if (v === Modes.INSENSITIVE) return Modes.INSENSITIVE;
1193
- if (v === Modes.DEFAULT) return Modes.DEFAULT;
1194
- return void 0;
1149
+ return v;
1195
1150
  }
1196
- function handleNotOperator(expr, val, params, outerMode, fieldType, dialect) {
1197
- const innerMode = normalizeMode(val.mode);
1198
- const effectiveMode = innerMode != null ? innerMode : outerMode;
1199
- return buildNotComposite(
1200
- expr,
1201
- val,
1202
- params,
1203
- dialect,
1204
- (e, subOp, subVal, p, d) => buildScalarOperator(e, subOp, subVal, p, effectiveMode, fieldType, d),
1205
- ` ${SQL_TEMPLATES.AND} `
1206
- );
1151
+ function scopeName(scope, dynamicName) {
1152
+ const s = String(scope).trim();
1153
+ const dn = String(dynamicName).trim();
1154
+ if (s.length === 0) return dn;
1155
+ return `${s}:${dn}`;
1207
1156
  }
1208
- function buildDynamicLikePattern(op, placeholder, dialect) {
1209
- if (dialect === "postgres") {
1210
- switch (op) {
1211
- case Ops.CONTAINS:
1212
- return `('%' || ${placeholder} || '%')`;
1213
- case Ops.STARTS_WITH:
1214
- return `(${placeholder} || '%')`;
1215
- case Ops.ENDS_WITH:
1216
- return `('%' || ${placeholder})`;
1217
- default:
1218
- return placeholder;
1219
- }
1220
- }
1221
- switch (op) {
1222
- case Ops.CONTAINS:
1223
- return `('%' || ${placeholder} || '%')`;
1224
- case Ops.STARTS_WITH:
1225
- return `(${placeholder} || '%')`;
1226
- case Ops.ENDS_WITH:
1227
- return `('%' || ${placeholder})`;
1228
- default:
1229
- return placeholder;
1157
+ function addAutoScoped(params, value, scope) {
1158
+ if (isDynamicParameter(value)) {
1159
+ const dn = extractDynamicName(value);
1160
+ return params.add(void 0, scopeName(scope, dn));
1230
1161
  }
1162
+ return params.add(value);
1231
1163
  }
1232
- function handleLikeOperator(expr, op, val, params, mode, dialect) {
1233
- if (val === void 0) return "";
1234
- if (isDynamicParameter(val)) {
1235
- const placeholder2 = params.addAuto(val);
1236
- const patternExpr = buildDynamicLikePattern(op, placeholder2, dialect);
1237
- if (mode === Modes.INSENSITIVE) {
1238
- return caseInsensitiveLike(expr, patternExpr, dialect);
1239
- }
1240
- return `${expr} ${SQL_TEMPLATES.LIKE} ${patternExpr}`;
1164
+
1165
+ // src/builder/shared/order-by-utils.ts
1166
+ var flipNulls = (v) => {
1167
+ const s = String(v).toLowerCase();
1168
+ if (s === "first") return "last";
1169
+ if (s === "last") return "first";
1170
+ return v;
1171
+ };
1172
+ var flipSortString = (v) => {
1173
+ if (typeof v !== "string") return v;
1174
+ const s = v.toLowerCase();
1175
+ if (s === "asc") return "desc";
1176
+ if (s === "desc") return "asc";
1177
+ return v;
1178
+ };
1179
+ var getNextSort = (sortRaw) => {
1180
+ if (typeof sortRaw !== "string") return sortRaw;
1181
+ const s = sortRaw.toLowerCase();
1182
+ if (s === "asc") return "desc";
1183
+ if (s === "desc") return "asc";
1184
+ return sortRaw;
1185
+ };
1186
+ var flipObjectSort = (obj) => {
1187
+ const sortRaw = obj.sort;
1188
+ const out = __spreadProps(__spreadValues({}, obj), { sort: getNextSort(sortRaw) });
1189
+ const nullsRaw = obj.nulls;
1190
+ if (typeof nullsRaw === "string") {
1191
+ out.nulls = flipNulls(nullsRaw);
1241
1192
  }
1242
- const placeholder = params.add(Wildcards[op](String(val)));
1243
- if (mode === Modes.INSENSITIVE) {
1244
- return caseInsensitiveLike(expr, placeholder, dialect);
1193
+ return out;
1194
+ };
1195
+ var flipValue = (v) => {
1196
+ if (typeof v === "string") return flipSortString(v);
1197
+ if (isPlainObject(v)) return flipObjectSort(v);
1198
+ return v;
1199
+ };
1200
+ var assertSingleFieldObject = (item) => {
1201
+ if (!isPlainObject(item)) {
1202
+ throw new Error("orderBy array entries must be objects");
1245
1203
  }
1246
- return `${expr} ${SQL_TEMPLATES.LIKE} ${placeholder}`;
1247
- }
1248
- function handleInOperator(expr, op, val, params, dialect) {
1249
- if (val === void 0) return "";
1250
- if (isDynamicParameter(val)) {
1251
- const placeholder2 = params.addAuto(val);
1252
- return op === Ops.IN ? inArray(expr, placeholder2, dialect) : notInArray(expr, placeholder2, dialect);
1204
+ const entries = Object.entries(item);
1205
+ if (entries.length !== 1) {
1206
+ throw new Error("orderBy array entries must have exactly one field");
1253
1207
  }
1254
- if (!Array.isArray(val)) {
1255
- throw createError(`IN operators require array value`, {
1256
- operator: op,
1257
- value: val
1258
- });
1208
+ return entries[0];
1209
+ };
1210
+ var flipOrderByArray = (orderBy) => {
1211
+ return orderBy.map((item) => {
1212
+ const [k, v] = assertSingleFieldObject(item);
1213
+ return { [k]: flipValue(v) };
1214
+ });
1215
+ };
1216
+ var flipOrderByObject = (orderBy) => {
1217
+ const out = {};
1218
+ for (const [k, v] of Object.entries(orderBy)) {
1219
+ out[k] = flipValue(v);
1259
1220
  }
1260
- if (val.length === 0) {
1261
- return op === Ops.IN ? "0=1" : "1=1";
1221
+ return out;
1222
+ };
1223
+ function reverseOrderByInput(orderBy) {
1224
+ if (!isNotNullish(orderBy)) return orderBy;
1225
+ if (Array.isArray(orderBy)) {
1226
+ return flipOrderByArray(orderBy);
1262
1227
  }
1263
- const paramValue = prepareArrayParam(val, dialect);
1264
- const placeholder = params.add(paramValue);
1265
- return op === Ops.IN ? inArray(expr, placeholder, dialect) : notInArray(expr, placeholder, dialect);
1266
- }
1267
- function handleComparisonOperator(expr, op, val, params) {
1268
- if (val === void 0) return "";
1269
- const COMPARISON_OPS2 = {
1270
- [Ops.EQUALS]: "=",
1271
- [Ops.GT]: ">",
1272
- [Ops.GTE]: ">=",
1273
- [Ops.LT]: "<",
1274
- [Ops.LTE]: "<="
1275
- };
1276
- const sqlOp = COMPARISON_OPS2[op];
1277
- if (!sqlOp) {
1278
- throw createError(`Unsupported scalar operator: ${op}`, { operator: op });
1228
+ if (isPlainObject(orderBy)) {
1229
+ return flipOrderByObject(orderBy);
1279
1230
  }
1280
- const placeholder = params.addAuto(val);
1281
- return `${expr} ${sqlOp} ${placeholder}`;
1231
+ throw new Error("orderBy must be an object or array of objects");
1282
1232
  }
1283
- function buildArrayParam(val, params, dialect) {
1284
- if (isDynamicParameter(val)) {
1285
- return params.addAuto(val);
1233
+ var normalizePairs = (pairs, parseValue) => {
1234
+ return pairs.map(([field, rawValue]) => {
1235
+ const parsed = parseValue(rawValue, field);
1236
+ return {
1237
+ [field]: parsed.nulls !== void 0 ? { sort: parsed.direction, nulls: parsed.nulls } : parsed.direction
1238
+ };
1239
+ });
1240
+ };
1241
+ function normalizeOrderByInput(orderBy, parseValue) {
1242
+ if (!isNotNullish(orderBy)) return [];
1243
+ if (Array.isArray(orderBy)) {
1244
+ const pairs = orderBy.map(assertSingleFieldObject);
1245
+ return normalizePairs(pairs, parseValue);
1286
1246
  }
1287
- if (!Array.isArray(val)) {
1288
- throw createError(`Array operation requires array value`, { value: val });
1247
+ if (isPlainObject(orderBy)) {
1248
+ return normalizePairs(Object.entries(orderBy), parseValue);
1289
1249
  }
1290
- const paramValue = prepareArrayParam(val, dialect);
1291
- return params.add(paramValue);
1250
+ throw new Error("orderBy must be an object or array of objects");
1292
1251
  }
1293
- function buildArrayOperator(expr, op, val, params, fieldType, dialect) {
1294
- if (val === void 0) return "";
1295
- if (val === null) {
1296
- if (op === Ops.EQUALS) return `${expr} ${SQL_TEMPLATES.IS_NULL}`;
1297
- if (op === Ops.NOT) return `${expr} ${SQL_TEMPLATES.IS_NOT_NULL}`;
1298
- }
1299
- const cast = getArrayType(fieldType, dialect);
1300
- if (op === Ops.EQUALS) {
1301
- return handleArrayEquals(expr, val, params, cast, dialect);
1302
- }
1303
- if (op === Ops.NOT) {
1304
- return handleArrayNot(expr, val, params, cast, dialect);
1305
- }
1306
- switch (op) {
1307
- case Ops.HAS:
1308
- return handleArrayHas(expr, val, params, cast, dialect);
1309
- case Ops.HAS_SOME:
1310
- return handleArrayHasSome(expr, val, params, cast, dialect);
1311
- case Ops.HAS_EVERY:
1312
- return handleArrayHasEvery(expr, val, params, cast, dialect);
1313
- case Ops.IS_EMPTY:
1314
- return handleArrayIsEmpty(expr, val, dialect);
1315
- default:
1316
- throw createError(`Unknown array operator: ${op}`, { operator: op });
1317
- }
1252
+
1253
+ // src/builder/pagination.ts
1254
+ var MAX_LIMIT_OFFSET = 2147483647;
1255
+ function parseDirectionRaw(raw, errorLabel) {
1256
+ const s = String(raw).toLowerCase();
1257
+ if (s === "asc" || s === "desc") return s;
1258
+ throw new Error(`Invalid ${errorLabel}: ${raw}`);
1318
1259
  }
1319
- function handleArrayEquals(expr, val, params, cast, dialect) {
1320
- if (val === void 0) return "";
1321
- if (isEmptyArray(val)) {
1322
- return arrayIsEmpty(expr, dialect);
1260
+ function parseNullsRaw(raw, errorLabel) {
1261
+ if (!isNotNullish(raw)) return void 0;
1262
+ const s = String(raw).toLowerCase();
1263
+ if (s === "first" || s === "last") return s;
1264
+ throw new Error(`Invalid ${errorLabel}: ${raw}`);
1265
+ }
1266
+ function requireOrderByObject(v, errorPrefix) {
1267
+ if (!isPlainObject(v) || !("sort" in v)) {
1268
+ throw new Error(`${errorPrefix} must be 'asc' | 'desc' or { sort, nulls? }`);
1323
1269
  }
1324
- const placeholder = buildArrayParam(val, params, dialect);
1325
- return arrayEquals(expr, placeholder, cast, dialect);
1270
+ return v;
1326
1271
  }
1327
- function handleArrayNot(expr, val, params, cast, dialect) {
1328
- if (val === void 0) return "";
1329
- let target = val;
1330
- if (isPlainObject(val)) {
1331
- const entries = Object.entries(val).filter(([, v]) => v !== void 0);
1332
- if (entries.length === 1 && entries[0][0] === Ops.EQUALS) {
1333
- target = entries[0][1];
1334
- } else {
1335
- throw createError(`Array NOT only supports { equals: ... } shape`, {
1336
- operator: Ops.NOT,
1337
- value: val
1338
- });
1272
+ function assertAllowedOrderByKeys(obj, fieldName) {
1273
+ const allowed = /* @__PURE__ */ new Set(["sort", "nulls"]);
1274
+ for (const k of Object.keys(obj)) {
1275
+ if (!allowed.has(k)) {
1276
+ throw new Error(
1277
+ fieldName ? `Unsupported orderBy key '${k}' for field '${fieldName}'` : `Unsupported orderBy key '${k}'`
1278
+ );
1339
1279
  }
1340
1280
  }
1341
- if (target === null) {
1342
- return `${expr} ${SQL_TEMPLATES.IS_NOT_NULL}`;
1343
- }
1344
- if (isEmptyArray(target)) {
1345
- return arrayIsNotEmpty(expr, dialect);
1346
- }
1347
- const placeholder = buildArrayParam(target, params, dialect);
1348
- return `${SQL_TEMPLATES.NOT} (${arrayEquals(expr, placeholder, cast, dialect)})`;
1349
1281
  }
1350
- function handleArrayHas(expr, val, params, cast, dialect) {
1351
- if (val === void 0) return "";
1352
- if (val === null) {
1353
- throw createError(`has requires scalar value`, {
1354
- operator: Ops.HAS,
1355
- value: val
1356
- });
1357
- }
1358
- if (!isDynamicParameter(val) && Array.isArray(val)) {
1359
- throw createError(`has requires scalar value (single element), not array`, {
1360
- operator: Ops.HAS,
1361
- value: val
1362
- });
1363
- }
1364
- if (isPlainObject(val)) {
1365
- throw createError(`has requires scalar value`, {
1366
- operator: Ops.HAS,
1367
- value: val
1368
- });
1282
+ function parseOrderByValue(v, fieldName) {
1283
+ const errorPrefix = fieldName ? `orderBy for '${fieldName}'` : "orderBy value";
1284
+ if (typeof v === "string") {
1285
+ return { direction: parseDirectionRaw(v, `${errorPrefix} direction`) };
1369
1286
  }
1370
- const placeholder = params.addAuto(val);
1371
- return arrayContains(expr, placeholder, cast, dialect);
1287
+ const obj = requireOrderByObject(v, errorPrefix);
1288
+ const direction = parseDirectionRaw(obj.sort, `${errorPrefix}.sort`);
1289
+ const nulls = parseNullsRaw(obj.nulls, `${errorPrefix}.nulls`);
1290
+ assertAllowedOrderByKeys(obj, fieldName);
1291
+ return { direction, nulls };
1372
1292
  }
1373
- function handleArrayHasSome(expr, val, params, cast, dialect) {
1374
- if (val === void 0) return "";
1375
- if (isDynamicParameter(val)) {
1376
- const placeholder2 = params.addAuto(val);
1377
- return arrayOverlaps(expr, placeholder2, cast, dialect);
1293
+ function normalizeFiniteInteger(name, v) {
1294
+ if (typeof v !== "number" || !Number.isFinite(v) || !Number.isInteger(v)) {
1295
+ throw new Error(`${name} must be an integer`);
1378
1296
  }
1379
- if (!Array.isArray(val)) {
1380
- throw createError(`hasSome requires array value`, {
1381
- operator: Ops.HAS_SOME,
1382
- value: val
1383
- });
1297
+ return v;
1298
+ }
1299
+ function normalizeNonNegativeInt(name, v) {
1300
+ if (isDynamicParameter(v)) return v;
1301
+ const n = normalizeFiniteInteger(name, v);
1302
+ if (n < 0) {
1303
+ throw new Error(`${name} must be >= 0`);
1384
1304
  }
1385
- if (val.length > LIMITS.MAX_ARRAY_SIZE) {
1386
- throw createError(
1387
- `Array too large (${val.length} elements, max ${LIMITS.MAX_ARRAY_SIZE})`,
1388
- { operator: Ops.HAS_SOME, value: `[${val.length} items]` }
1389
- );
1305
+ if (n > MAX_LIMIT_OFFSET) {
1306
+ throw new Error(`${name} must be <= ${MAX_LIMIT_OFFSET}`);
1390
1307
  }
1391
- if (val.length === 0) return "0=1";
1392
- const paramValue = prepareArrayParam(val, dialect);
1393
- const placeholder = params.add(paramValue);
1394
- return arrayOverlaps(expr, placeholder, cast, dialect);
1308
+ return n;
1395
1309
  }
1396
- function handleArrayHasEvery(expr, val, params, cast, dialect) {
1397
- if (val === void 0) return "";
1398
- const placeholder = buildArrayParam(val, params, dialect);
1399
- return arrayContainsAll(expr, placeholder, cast, dialect);
1310
+ function hasNonNullishProp(v, key) {
1311
+ return isPlainObject(v) && key in v && isNotNullish(v[key]);
1400
1312
  }
1401
- function handleArrayIsEmpty(expr, val, dialect) {
1402
- if (typeof val !== "boolean") {
1403
- throw createError(`isEmpty requires boolean value`, {
1404
- operator: Ops.IS_EMPTY,
1405
- value: val
1406
- });
1313
+ function normalizeIntegerOrDynamic(name, v) {
1314
+ if (isDynamicParameter(v)) return v;
1315
+ return normalizeFiniteInteger(name, v);
1316
+ }
1317
+ function readSkipTake(relArgs) {
1318
+ const hasSkip = hasNonNullishProp(relArgs, "skip");
1319
+ const hasTake = hasNonNullishProp(relArgs, "take");
1320
+ if (!hasSkip && !hasTake) {
1321
+ return {
1322
+ hasSkip: false,
1323
+ hasTake: false,
1324
+ skipVal: void 0,
1325
+ takeVal: void 0
1326
+ };
1407
1327
  }
1408
- return val === true ? arrayIsEmpty(expr, dialect) : arrayIsNotEmpty(expr, dialect);
1328
+ const obj = relArgs;
1329
+ const skipVal = hasSkip ? normalizeNonNegativeInt("skip", obj.skip) : void 0;
1330
+ const takeVal = hasTake ? normalizeIntegerOrDynamic("take", obj.take) : void 0;
1331
+ return { hasSkip, hasTake, skipVal, takeVal };
1409
1332
  }
1410
-
1411
- // src/builder/where/operators-json.ts
1412
- var SAFE_JSON_PATH_SEGMENT = /^[a-zA-Z_]\w*$/;
1413
- function validateJsonPathSegments(segments) {
1414
- for (const segment of segments) {
1415
- if (typeof segment !== "string") {
1416
- throw createError("JSON path segments must be strings", {
1417
- operator: Ops.PATH,
1418
- value: segment
1419
- });
1333
+ function buildOrderByFragment(entries, alias, dialect, model) {
1334
+ if (entries.length === 0) return "";
1335
+ const out = [];
1336
+ for (const e of entries) {
1337
+ const dir = e.direction.toUpperCase();
1338
+ const c = col(alias, e.field, model);
1339
+ if (dialect === "postgres") {
1340
+ const nulls = isNotNullish(e.nulls) ? ` NULLS ${e.nulls.toUpperCase()}` : "";
1341
+ out.push(`${c} ${dir}${nulls}`);
1342
+ continue;
1420
1343
  }
1421
- if (!SAFE_JSON_PATH_SEGMENT.test(segment)) {
1422
- throw createError(
1423
- `Invalid JSON path segment: '${segment}'. Must be alphanumeric with underscores, starting with letter or underscore.`,
1424
- { operator: Ops.PATH, value: segment }
1425
- );
1344
+ if (isNotNullish(e.nulls)) {
1345
+ const isNullExpr = `(${c} IS NULL)`;
1346
+ const nullRankDir = e.nulls === "first" ? "DESC" : "ASC";
1347
+ out.push(`${isNullExpr} ${nullRankDir}`);
1348
+ out.push(`${c} ${dir}`);
1349
+ continue;
1426
1350
  }
1351
+ out.push(`${c} ${dir}`);
1427
1352
  }
1353
+ return out.join(SQL_SEPARATORS.ORDER_BY);
1428
1354
  }
1429
- function buildJsonOperator(expr, op, val, params, dialect) {
1430
- if (val === void 0) return "";
1431
- if (op === Ops.PATH && isPlainObject(val) && "path" in val) {
1432
- return handleJsonPath(expr, val, params, dialect);
1433
- }
1434
- const jsonWildcards = {
1435
- [Ops.STRING_CONTAINS]: (v) => `%${v}%`,
1436
- [Ops.STRING_STARTS_WITH]: (v) => `${v}%`,
1437
- [Ops.STRING_ENDS_WITH]: (v) => `%${v}`
1438
- };
1439
- if (op in jsonWildcards) {
1440
- return handleJsonWildcard(expr, op, val, params, jsonWildcards, dialect);
1355
+ function defaultNullsFor(dialect, direction) {
1356
+ if (dialect === "postgres") {
1357
+ return direction === "asc" ? "last" : "first";
1441
1358
  }
1442
- throw createError(`Unsupported JSON operator: ${op}`, { operator: op });
1359
+ return direction === "asc" ? "first" : "last";
1443
1360
  }
1444
- function handleJsonPath(expr, val, params, dialect) {
1445
- const v = val;
1446
- if (!Array.isArray(v.path)) {
1447
- throw createError("JSON path must be an array", { operator: Ops.PATH });
1448
- }
1449
- if (v.path.length === 0) {
1450
- throw createError("JSON path cannot be empty", { operator: Ops.PATH });
1361
+ function ensureCursorFieldsInOrder(orderEntries, cursorEntries) {
1362
+ const existing = /* @__PURE__ */ new Map();
1363
+ for (const e of orderEntries) existing.set(e.field, e);
1364
+ const out = [...orderEntries];
1365
+ for (const [field] of cursorEntries) {
1366
+ if (!existing.has(field)) {
1367
+ out.push({ field, direction: "asc" });
1368
+ existing.set(field, out[out.length - 1]);
1369
+ }
1451
1370
  }
1452
- validateJsonPathSegments(v.path);
1453
- const pathExpr = dialect === "sqlite" ? params.add(`$.${v.path.join(".")}`) : params.add(v.path);
1454
- const rawOps = [
1455
- ["=", v.equals],
1456
- [">", v.gt],
1457
- [">=", v.gte],
1458
- ["<", v.lt],
1459
- ["<=", v.lte]
1460
- ];
1461
- const ops = rawOps.filter(
1462
- ([, value]) => value !== void 0
1463
- );
1464
- if (ops.length === 0) {
1465
- throw createError("JSON path query missing comparison operator", {
1466
- operator: Ops.PATH
1467
- });
1371
+ return out;
1372
+ }
1373
+ function buildCursorFilterParts(cursor, cursorAlias, params, model) {
1374
+ const entries = Object.entries(cursor);
1375
+ if (entries.length === 0) {
1376
+ throw new Error("cursor must have at least one field");
1468
1377
  }
1378
+ const placeholdersByField = /* @__PURE__ */ new Map();
1469
1379
  const parts = [];
1470
- for (const [sqlOp, value] of ops) {
1380
+ for (const [field, value] of entries) {
1381
+ const c = `${cursorAlias}.${quote(field)}`;
1471
1382
  if (value === null) {
1472
- const base2 = jsonExtractText(expr, pathExpr, dialect);
1473
- parts.push(`${base2} ${SQL_TEMPLATES.IS_NULL}`);
1383
+ parts.push(`${c} IS NULL`);
1474
1384
  continue;
1475
1385
  }
1476
- const valPh = params.add(value);
1477
- const base = typeof value === "number" ? jsonExtractNumeric(expr, pathExpr, dialect) : jsonExtractText(expr, pathExpr, dialect);
1478
- parts.push(`${base} ${sqlOp} ${valPh}`);
1479
- }
1480
- return parts.length === 1 ? parts[0] : `(${parts.join(" AND ")})`;
1481
- }
1482
- function handleJsonWildcard(expr, op, val, params, wildcards, dialect) {
1483
- if (!isNotNullish(val)) {
1484
- throw createError(`JSON string operator requires non-null value`, {
1485
- operator: op,
1486
- value: val
1487
- });
1488
- }
1489
- if (isPlainObject(val) || Array.isArray(val)) {
1490
- throw createError(`JSON string operator requires scalar value`, {
1491
- operator: op,
1492
- value: val
1493
- });
1494
- }
1495
- const strVal = String(val);
1496
- if (strVal.length > LIMITS.MAX_STRING_LENGTH) {
1497
- throw createError(
1498
- `String too long (${strVal.length} chars, max ${LIMITS.MAX_STRING_LENGTH})`,
1499
- { operator: op }
1500
- );
1386
+ const ph = addAutoScoped(params, value, `cursor.filter.${field}`);
1387
+ placeholdersByField.set(field, ph);
1388
+ parts.push(`${c} = ${ph}`);
1501
1389
  }
1502
- const placeholder = params.add(wildcards[op](strVal));
1503
- const jsonText = jsonToText(expr, dialect);
1504
- return caseInsensitiveLike(jsonText, placeholder, dialect);
1390
+ return {
1391
+ whereSql: parts.length === 1 ? parts[0] : `(${parts.join(" AND ")})`,
1392
+ placeholdersByField
1393
+ };
1505
1394
  }
1506
-
1507
- // src/builder/where/relations.ts
1508
- var NO_JOINS = Object.freeze([]);
1509
- function isListRelation(fieldType) {
1510
- return typeof fieldType === "string" && fieldType.endsWith("[]");
1395
+ function cursorValueExpr(tableName, cursorAlias, cursorWhereSql, field, model) {
1396
+ const colName = quote(field);
1397
+ return `(SELECT ${cursorAlias}.${colName} ${SQL_TEMPLATES.FROM} ${tableName} ${cursorAlias} ${SQL_TEMPLATES.WHERE} ${cursorWhereSql} ${SQL_TEMPLATES.LIMIT} 1)`;
1511
1398
  }
1512
- function buildToOneNullCheck(field, parentAlias, relTable, relAlias, join3, wantNull) {
1513
- const isLocal = field.isForeignKeyLocal === true;
1514
- const fkFields = normalizeKeyList(field.foreignKey);
1515
- if (isLocal) {
1516
- if (fkFields.length === 0) {
1517
- throw createError(`Relation '${field.name}' is missing foreignKey`, {
1518
- field: field.name
1519
- });
1520
- }
1521
- const parts = fkFields.map((fk) => {
1522
- const safe = fk.replace(/"/g, '""');
1523
- const expr = `${parentAlias}."${safe}"`;
1524
- return wantNull ? `${expr} ${SQL_TEMPLATES.IS_NULL}` : `${expr} ${SQL_TEMPLATES.IS_NOT_NULL}`;
1525
- });
1526
- if (parts.length === 1) return parts[0];
1527
- return wantNull ? `(${parts.join(" OR ")})` : `(${parts.join(" AND ")})`;
1528
- }
1529
- const existsSql = `EXISTS (${SQL_TEMPLATES.SELECT} 1 ${SQL_TEMPLATES.FROM} ${relTable} ${relAlias} ${SQL_TEMPLATES.WHERE} ${join3})`;
1530
- return wantNull ? `${SQL_TEMPLATES.NOT} ${existsSql}` : existsSql;
1399
+ function buildCursorRowExistsExpr(tableName, cursorAlias, cursorWhereSql) {
1400
+ return `EXISTS (${SQL_TEMPLATES.SELECT} 1 ${SQL_TEMPLATES.FROM} ${tableName} ${cursorAlias} ${SQL_TEMPLATES.WHERE} ${cursorWhereSql} ${SQL_TEMPLATES.LIMIT} 1)`;
1531
1401
  }
1532
- function buildToOneExistsMatch(relTable, relAlias, join3, sub) {
1533
- const joins = sub.joins.length > 0 ? ` ${sub.joins.join(" ")}` : "";
1534
- return `EXISTS (${SQL_TEMPLATES.SELECT} 1 ${SQL_TEMPLATES.FROM} ${relTable} ${relAlias}${joins} ${SQL_TEMPLATES.WHERE} ${join3} ${SQL_TEMPLATES.AND} ${sub.clause})`;
1402
+ function buildCursorEqualityExpr(columnExpr, valueExpr) {
1403
+ return `((${valueExpr} IS NULL AND ${columnExpr} IS NULL) OR (${valueExpr} IS NOT NULL AND ${columnExpr} = ${valueExpr}))`;
1535
1404
  }
1536
- function buildToOneNotExistsMatch(relTable, relAlias, join3, sub) {
1537
- const joins = sub.joins.length > 0 ? ` ${sub.joins.join(" ")}` : "";
1538
- return `${SQL_TEMPLATES.NOT} EXISTS (${SQL_TEMPLATES.SELECT} 1 ${SQL_TEMPLATES.FROM} ${relTable} ${relAlias}${joins} ${SQL_TEMPLATES.WHERE} ${join3} ${SQL_TEMPLATES.AND} ${sub.clause})`;
1405
+ function buildCursorInequalityExpr(columnExpr, direction, nulls, valueExpr) {
1406
+ const op = direction === "asc" ? ">" : "<";
1407
+ if (nulls === "first") {
1408
+ return `(CASE WHEN ${valueExpr} IS NULL THEN (${columnExpr} IS NOT NULL) ELSE (${columnExpr} ${op} ${valueExpr}) END)`;
1409
+ }
1410
+ return `(CASE WHEN ${valueExpr} IS NULL THEN 0=1 ELSE ((${columnExpr} ${op} ${valueExpr}) OR (${columnExpr} IS NULL)) END)`;
1539
1411
  }
1540
- function buildListRelationFilters(args) {
1541
- const {
1542
- fieldName,
1543
- value,
1544
- ctx,
1545
- whereBuilder,
1546
- relModel,
1547
- relTable,
1548
- relAlias,
1549
- join: join3
1550
- } = args;
1551
- const noneValue = value[RelationFilters.NONE];
1552
- if (noneValue !== void 0 && noneValue !== null) {
1553
- const sub = whereBuilder.build(noneValue, __spreadProps(__spreadValues({}, ctx), {
1554
- alias: relAlias,
1555
- model: relModel,
1556
- path: [...ctx.path, fieldName, RelationFilters.NONE],
1557
- isSubquery: true,
1558
- depth: ctx.depth + 1
1559
- }));
1560
- const isEmptyFilter = isPlainObject(noneValue) && Object.keys(noneValue).length === 0;
1561
- const canOptimize = !ctx.isSubquery && isEmptyFilter && sub.clause === DEFAULT_WHERE_CLAUSE && sub.joins.length === 0;
1562
- if (canOptimize) {
1563
- const checkField = relModel.fields.find(
1564
- (f) => !f.isRelation && f.isRequired && f.name !== "id"
1565
- ) || relModel.fields.find((f) => !f.isRelation && f.name === "id");
1566
- if (checkField) {
1567
- const leftJoinSql = `LEFT JOIN ${relTable} ${relAlias} ON ${join3}`;
1568
- const whereClause = `${relAlias}.${quote(checkField.name)} IS NULL`;
1569
- return Object.freeze({
1570
- clause: whereClause,
1571
- joins: [leftJoinSql]
1572
- });
1573
- }
1412
+ function buildOuterCursorMatch(cursor, outerAlias, placeholdersByField, params, model) {
1413
+ const parts = [];
1414
+ for (const [field, value] of Object.entries(cursor)) {
1415
+ const c = col(outerAlias, field, model);
1416
+ if (value === null) {
1417
+ parts.push(`${c} IS NULL`);
1418
+ continue;
1419
+ }
1420
+ const existing = placeholdersByField.get(field);
1421
+ if (typeof existing === "string" && existing.length > 0) {
1422
+ parts.push(`${c} = ${existing}`);
1423
+ continue;
1574
1424
  }
1425
+ const ph = addAutoScoped(params, value, `cursor.outerMatch.${field}`);
1426
+ parts.push(`${c} = ${ph}`);
1575
1427
  }
1576
- const filters = [
1577
- {
1578
- key: RelationFilters.SOME,
1579
- wrap: (c, j) => `EXISTS (${SQL_TEMPLATES.SELECT} 1 ${SQL_TEMPLATES.FROM} ${relTable} ${relAlias}${j} ${SQL_TEMPLATES.WHERE} ${join3} ${SQL_TEMPLATES.AND} ${c})`
1580
- },
1581
- {
1582
- key: RelationFilters.EVERY,
1583
- wrap: (c, j) => `${SQL_TEMPLATES.NOT} EXISTS (${SQL_TEMPLATES.SELECT} 1 ${SQL_TEMPLATES.FROM} ${relTable} ${relAlias}${j} ${SQL_TEMPLATES.WHERE} ${join3} ${SQL_TEMPLATES.AND} ${SQL_TEMPLATES.NOT} (${c}))`
1584
- },
1585
- {
1586
- key: RelationFilters.NONE,
1587
- wrap: (c, j) => {
1588
- const condition = c === DEFAULT_WHERE_CLAUSE ? "" : ` ${SQL_TEMPLATES.AND} ${c}`;
1589
- return `${SQL_TEMPLATES.NOT} EXISTS (${SQL_TEMPLATES.SELECT} 1 ${SQL_TEMPLATES.FROM} ${relTable} ${relAlias}${j} ${SQL_TEMPLATES.WHERE} ${join3}${condition})`;
1428
+ return parts.length === 1 ? parts[0] : `(${parts.join(" AND ")})`;
1429
+ }
1430
+ function buildOrderEntries(orderBy) {
1431
+ const normalized = normalizeOrderByInput(orderBy, parseOrderByValue);
1432
+ const entries = [];
1433
+ for (const item of normalized) {
1434
+ for (const [field, value] of Object.entries(item)) {
1435
+ if (typeof value === "string") {
1436
+ entries.push({ field, direction: value });
1437
+ } else {
1438
+ entries.push({
1439
+ field,
1440
+ direction: value.sort,
1441
+ nulls: value.nulls
1442
+ });
1590
1443
  }
1591
1444
  }
1592
- ];
1593
- const clauses = [];
1594
- for (const { key, wrap } of filters) {
1595
- const raw = value[key];
1596
- if (raw === void 0 || raw === null) continue;
1597
- const sub = whereBuilder.build(raw, __spreadProps(__spreadValues({}, ctx), {
1598
- alias: relAlias,
1599
- model: relModel,
1600
- path: [...ctx.path, fieldName, key],
1601
- isSubquery: true,
1602
- depth: ctx.depth + 1
1603
- }));
1604
- const j = sub.joins.length > 0 ? ` ${sub.joins.join(" ")}` : "";
1605
- clauses.push(wrap(sub.clause, j));
1606
- }
1607
- if (clauses.length === 0) {
1608
- throw createError(
1609
- `List relation '${fieldName}' requires one of { some, every, none }`,
1610
- { field: fieldName, path: ctx.path, modelName: ctx.model.name }
1611
- );
1612
1445
  }
1613
- return Object.freeze({
1614
- clause: clauses.join(SQL_SEPARATORS.CONDITION_AND),
1615
- joins: NO_JOINS
1616
- });
1446
+ return entries;
1617
1447
  }
1618
- function buildToOneRelationFilters(args) {
1619
- const {
1620
- fieldName,
1621
- value,
1622
- ctx,
1623
- whereBuilder,
1624
- field,
1625
- relModel,
1626
- relTable,
1627
- relAlias,
1628
- join: join3
1629
- } = args;
1630
- const hasSomeEveryNone = isNotNullish(value[RelationFilters.SOME]) || isNotNullish(value[RelationFilters.EVERY]) || isNotNullish(value[RelationFilters.NONE]);
1631
- if (hasSomeEveryNone) {
1632
- throw createError(
1633
- `To-one relation '${fieldName}' does not support { some, every, none }; use { is, isNot }`,
1634
- { field: fieldName, path: ctx.path, modelName: ctx.model.name }
1635
- );
1448
+ function buildCursorCondition(cursor, orderBy, tableName, alias, params, dialect, model) {
1449
+ var _a;
1450
+ const d = dialect != null ? dialect : getGlobalDialect();
1451
+ const cursorEntries = Object.entries(cursor);
1452
+ if (cursorEntries.length === 0) {
1453
+ throw new Error("cursor must have at least one field");
1636
1454
  }
1637
- const hasIs = Object.prototype.hasOwnProperty.call(value, "is");
1638
- const hasIsNot = Object.prototype.hasOwnProperty.call(value, "isNot");
1639
- let filterKey;
1640
- let filterVal;
1641
- if (hasIs) {
1642
- filterKey = "is";
1643
- filterVal = value.is;
1644
- } else if (hasIsNot) {
1645
- filterKey = "isNot";
1646
- filterVal = value.isNot;
1455
+ const cursorAlias = "__tp_cursor_src";
1456
+ const { whereSql: cursorWhereSql, placeholdersByField } = buildCursorFilterParts(cursor, cursorAlias, params);
1457
+ let orderEntries = buildOrderEntries(orderBy);
1458
+ if (orderEntries.length === 0) {
1459
+ orderEntries = cursorEntries.map(([field]) => ({
1460
+ field,
1461
+ direction: "asc"
1462
+ }));
1647
1463
  } else {
1648
- filterKey = "is";
1649
- filterVal = value;
1464
+ orderEntries = ensureCursorFieldsInOrder(orderEntries, cursorEntries);
1650
1465
  }
1651
- if (filterVal === void 0) {
1652
- return Object.freeze({
1653
- clause: DEFAULT_WHERE_CLAUSE,
1654
- joins: NO_JOINS
1655
- });
1466
+ const existsExpr = buildCursorRowExistsExpr(
1467
+ tableName,
1468
+ cursorAlias,
1469
+ cursorWhereSql
1470
+ );
1471
+ const outerCursorMatch = buildOuterCursorMatch(
1472
+ cursor,
1473
+ alias,
1474
+ placeholdersByField,
1475
+ params,
1476
+ model
1477
+ );
1478
+ const orClauses = [];
1479
+ for (let level = 0; level < orderEntries.length; level++) {
1480
+ const andParts = [];
1481
+ for (let i = 0; i < level; i++) {
1482
+ const e2 = orderEntries[i];
1483
+ const c2 = col(alias, e2.field, model);
1484
+ const v2 = cursorValueExpr(
1485
+ tableName,
1486
+ cursorAlias,
1487
+ cursorWhereSql,
1488
+ e2.field);
1489
+ andParts.push(buildCursorEqualityExpr(c2, v2));
1490
+ }
1491
+ const e = orderEntries[level];
1492
+ const c = col(alias, e.field, model);
1493
+ const v = cursorValueExpr(
1494
+ tableName,
1495
+ cursorAlias,
1496
+ cursorWhereSql,
1497
+ e.field);
1498
+ const nulls = (_a = e.nulls) != null ? _a : defaultNullsFor(d, e.direction);
1499
+ andParts.push(buildCursorInequalityExpr(c, e.direction, nulls, v));
1500
+ orClauses.push(`(${andParts.join(SQL_SEPARATORS.CONDITION_AND)})`);
1656
1501
  }
1657
- if (filterVal === null) {
1658
- const wantNull = filterKey === "is";
1659
- const clause2 = buildToOneNullCheck(
1660
- field,
1661
- ctx.alias,
1662
- relTable,
1663
- relAlias,
1664
- join3,
1665
- wantNull
1502
+ const exclusive = orClauses.join(SQL_SEPARATORS.CONDITION_OR);
1503
+ return `(${existsExpr} ${SQL_SEPARATORS.CONDITION_AND} ((${exclusive})${SQL_SEPARATORS.CONDITION_OR}(${outerCursorMatch})))`;
1504
+ }
1505
+ function buildOrderBy(orderBy, alias, dialect, model) {
1506
+ const entries = buildOrderEntries(orderBy);
1507
+ if (entries.length === 0) return "";
1508
+ const d = dialect != null ? dialect : getGlobalDialect();
1509
+ return buildOrderByFragment(entries, alias, d, model);
1510
+ }
1511
+ function buildOrderByClause(args, alias, dialect, model) {
1512
+ if (!isNotNullish(args.orderBy)) return "";
1513
+ const result = buildOrderBy(args.orderBy, alias, dialect, model);
1514
+ if (!isNonEmptyString(result)) {
1515
+ throw new Error(
1516
+ "buildOrderByClause: orderBy specified but produced empty result"
1666
1517
  );
1667
- return Object.freeze({
1668
- clause: clause2,
1669
- joins: NO_JOINS
1670
- });
1671
1518
  }
1672
- if (!isPlainObject(filterVal)) {
1673
- throw createError(
1674
- `Relation '${fieldName}' filter must be an object or null`,
1675
- {
1676
- field: fieldName,
1677
- path: ctx.path,
1678
- modelName: ctx.model.name,
1679
- value: filterVal
1680
- }
1681
- );
1519
+ return result;
1520
+ }
1521
+ function normalizeTakeLike(v) {
1522
+ const n = normalizeIntLike("take", v, {
1523
+ min: Number.MIN_SAFE_INTEGER,
1524
+ max: MAX_LIMIT_OFFSET,
1525
+ allowZero: true
1526
+ });
1527
+ if (typeof n === "number") {
1528
+ if (n === 0) return 0;
1682
1529
  }
1683
- const sub = whereBuilder.build(filterVal, __spreadProps(__spreadValues({}, ctx), {
1684
- alias: relAlias,
1685
- model: relModel,
1686
- path: [...ctx.path, fieldName, filterKey],
1687
- isSubquery: true,
1688
- depth: ctx.depth + 1
1689
- }));
1690
- const clause = filterKey === "is" ? buildToOneExistsMatch(relTable, relAlias, join3, sub) : buildToOneNotExistsMatch(relTable, relAlias, join3, sub);
1691
- return Object.freeze({
1692
- clause,
1693
- joins: NO_JOINS
1530
+ return n;
1531
+ }
1532
+ function normalizeSkipLike(v) {
1533
+ return normalizeIntLike("skip", v, {
1534
+ min: 0,
1535
+ max: MAX_LIMIT_OFFSET,
1536
+ allowZero: true
1694
1537
  });
1695
1538
  }
1696
- function ensureRelationFilterObject(fieldName, value, ctx) {
1697
- if (!isPlainObject(value)) {
1698
- throw createError(`Relation filter '${fieldName}' must be an object`, {
1699
- path: [...ctx.path, fieldName],
1700
- field: fieldName,
1701
- modelName: ctx.model.name,
1702
- value
1703
- });
1539
+ function getPaginationParams(method, args) {
1540
+ if (method === "findMany") {
1541
+ return {
1542
+ take: normalizeTakeLike(args.take),
1543
+ skip: normalizeSkipLike(args.skip),
1544
+ cursor: args.cursor
1545
+ };
1704
1546
  }
1705
- }
1706
- function buildRelation(fieldName, value, ctx, whereBuilder) {
1707
- const field = ctx.model.fields.find((f) => f.name === fieldName);
1708
- if (!isValidRelationField(field)) {
1709
- throw createError(`Invalid relation '${fieldName}'`, {
1710
- field: fieldName,
1711
- path: ctx.path,
1712
- modelName: ctx.model.name
1713
- });
1547
+ if (method === "findFirst") {
1548
+ const skip = normalizeSkipLike(args.skip);
1549
+ return { take: 1, skip: skip != null ? skip : 0 };
1714
1550
  }
1715
- const relModel = ctx.schemaModels.find((m) => m.name === field.relatedModel);
1716
- if (!isNotNullish(relModel)) {
1717
- throw createError(
1718
- `Related model '${field.relatedModel}' not found in schema. Available models: ${ctx.schemaModels.map((m) => m.name).join(", ")}`,
1719
- {
1720
- field: fieldName,
1721
- path: ctx.path,
1722
- modelName: ctx.model.name
1723
- }
1724
- );
1551
+ if (method === "findUnique") {
1552
+ return { take: 1, skip: 0 };
1725
1553
  }
1726
- const relTable = buildTableReference(
1727
- SQL_TEMPLATES.PUBLIC_SCHEMA,
1728
- relModel.tableName,
1729
- ctx.dialect
1730
- );
1731
- const relAlias = ctx.aliasGen.next(fieldName);
1732
- const join3 = joinCondition(field, ctx.model, relModel, ctx.alias, relAlias);
1733
- const args = {
1734
- fieldName,
1735
- value,
1736
- ctx,
1737
- whereBuilder,
1738
- field,
1739
- relModel,
1740
- relTable,
1741
- relAlias,
1742
- join: join3
1743
- };
1744
- if (isListRelation(field.type)) return buildListRelationFilters(args);
1745
- return buildToOneRelationFilters(args);
1746
- }
1747
- function buildTopLevelRelation(fieldName, value, ctx, whereBuilder) {
1748
- ensureRelationFilterObject(fieldName, value, ctx);
1749
- return buildRelation(fieldName, value, ctx, whereBuilder);
1750
- }
1751
- function buildNestedRelation(fieldName, value, ctx, whereBuilder) {
1752
- return buildTopLevelRelation(fieldName, value, ctx, whereBuilder);
1554
+ return {};
1753
1555
  }
1754
-
1755
- // src/builder/shared/validators/field-validators.ts
1756
- function assertFieldExists(name, model, path) {
1757
- const field = model.fields.find((f) => f.name === name);
1758
- if (!isNotNullish(field)) {
1759
- throw createError(`Field '${name}' does not exist on '${model.name}'`, {
1760
- field: name,
1761
- path,
1762
- modelName: model.name,
1763
- availableFields: model.fields.map((f) => f.name)
1764
- });
1556
+ function buildNotComposite(expr, val, params, dialect, buildOp, separator) {
1557
+ const entries = Object.entries(val).filter(
1558
+ ([k, v]) => k !== "mode" && v !== void 0
1559
+ );
1560
+ if (entries.length === 0) return "";
1561
+ const clauses = [];
1562
+ for (const [subOp, subVal] of entries) {
1563
+ const sub = buildOp(expr, subOp, subVal, params, dialect);
1564
+ if (sub && sub.trim().length > 0) clauses.push(`(${sub})`);
1765
1565
  }
1766
- return field;
1566
+ if (clauses.length === 0) return "";
1567
+ if (clauses.length === 1) return `${SQL_TEMPLATES.NOT} ${clauses[0]}`;
1568
+ return `${SQL_TEMPLATES.NOT} (${clauses.join(separator)})`;
1767
1569
  }
1768
- function assertValidOperator(fieldName, op, fieldType, path, modelName) {
1769
- if (!isNotNullish(fieldType)) return;
1770
- const ARRAY_OPS = /* @__PURE__ */ new Set([
1771
- Ops.HAS,
1772
- Ops.HAS_SOME,
1773
- Ops.HAS_EVERY,
1774
- Ops.IS_EMPTY
1775
- ]);
1776
- const JSON_OPS = /* @__PURE__ */ new Set([
1777
- Ops.PATH,
1778
- Ops.STRING_CONTAINS,
1779
- Ops.STRING_STARTS_WITH,
1780
- Ops.STRING_ENDS_WITH
1781
- ]);
1782
- const isArrayOp = ARRAY_OPS.has(op);
1783
- const isFieldArray = isArrayType(fieldType);
1784
- const arrayOpMismatch = isArrayOp && !isFieldArray;
1785
- if (arrayOpMismatch) {
1786
- throw createError(`'${op}' requires array field, got '${fieldType}'`, {
1787
- operator: op,
1788
- field: fieldName,
1789
- path,
1790
- modelName
1791
- });
1570
+ function buildScalarOperator(expr, op, val, params, mode, fieldType, dialect) {
1571
+ if (val === void 0) return "";
1572
+ if (val === null) {
1573
+ return handleNullValue(expr, op);
1792
1574
  }
1793
- const isJsonOp = JSON_OPS.has(op);
1794
- const isFieldJson = isJsonType(fieldType);
1795
- const jsonOpMismatch = isJsonOp && !isFieldJson;
1796
- if (jsonOpMismatch) {
1797
- throw createError(`'${op}' requires JSON field, got '${fieldType}'`, {
1798
- operator: op,
1799
- field: fieldName,
1800
- path,
1801
- modelName
1802
- });
1575
+ if (op === Ops.NOT && isPlainObject(val)) {
1576
+ return handleNotOperator(expr, val, params, mode, fieldType, dialect);
1803
1577
  }
1804
- }
1805
-
1806
- // src/builder/where/builder.ts
1807
- var WhereBuilder = class {
1808
- build(where, ctx) {
1809
- if (!isPlainObject(where)) {
1810
- throw createError("where must be an object", {
1811
- path: ctx.path,
1812
- modelName: ctx.model.name
1578
+ if (op === Ops.NOT) {
1579
+ const placeholder = params.addAuto(val);
1580
+ return `${expr} <> ${placeholder}`;
1581
+ }
1582
+ if (op === Ops.EQUALS && mode === Modes.INSENSITIVE && isNotNullish(dialect)) {
1583
+ const placeholder = params.addAuto(val);
1584
+ return caseInsensitiveEquals(expr, placeholder);
1585
+ }
1586
+ const STRING_LIKE_OPS = /* @__PURE__ */ new Set([
1587
+ Ops.CONTAINS,
1588
+ Ops.STARTS_WITH,
1589
+ Ops.ENDS_WITH
1590
+ ]);
1591
+ if (STRING_LIKE_OPS.has(op)) {
1592
+ if (!isNotNullish(dialect)) {
1593
+ throw createError(`Like operators require a SQL dialect`, {
1594
+ operator: op
1813
1595
  });
1814
1596
  }
1815
- return buildWhereInternal(where, ctx, this);
1597
+ return handleLikeOperator(expr, op, val, params, mode, dialect);
1816
1598
  }
1817
- };
1818
- var MAX_QUERY_DEPTH = 50;
1819
- var EMPTY_JOINS = Object.freeze([]);
1820
- var whereBuilderInstance = new WhereBuilder();
1821
- function freezeResult(clause, joins = EMPTY_JOINS) {
1822
- return Object.freeze({ clause, joins });
1823
- }
1824
- function dedupePreserveOrder(items) {
1825
- if (items.length <= 1) return Object.freeze([...items]);
1826
- const seen = /* @__PURE__ */ new Set();
1827
- const out = [];
1828
- for (const s of items) {
1829
- if (!seen.has(s)) {
1830
- seen.add(s);
1831
- out.push(s);
1599
+ if (op === Ops.IN || op === Ops.NOT_IN) {
1600
+ if (!isNotNullish(dialect)) {
1601
+ throw createError(`IN operators require a SQL dialect`, { operator: op });
1832
1602
  }
1603
+ return handleInOperator(expr, op, val, params, dialect);
1833
1604
  }
1834
- return Object.freeze(out);
1605
+ return handleComparisonOperator(expr, op, val, params);
1835
1606
  }
1836
- function appendResult(result, clauses, allJoins) {
1837
- if (isValidWhereClause(result.clause)) clauses.push(result.clause);
1838
- if (isNonEmptyArray(result.joins)) allJoins.push(...result.joins);
1607
+ function handleNullValue(expr, op) {
1608
+ if (op === Ops.EQUALS) return `${expr} ${SQL_TEMPLATES.IS_NULL}`;
1609
+ if (op === Ops.NOT) return `${expr} ${SQL_TEMPLATES.IS_NOT_NULL}`;
1610
+ throw createError(`Operator '${op}' doesn't support null`, { operator: op });
1839
1611
  }
1840
- function asLogicalOperator(key) {
1841
- if (key === LogicalOps.AND) return "AND";
1842
- if (key === LogicalOps.OR) return "OR";
1843
- if (key === LogicalOps.NOT) return "NOT";
1844
- return null;
1612
+ function normalizeMode(v) {
1613
+ if (v === Modes.INSENSITIVE) return Modes.INSENSITIVE;
1614
+ if (v === Modes.DEFAULT) return Modes.DEFAULT;
1615
+ return void 0;
1845
1616
  }
1846
- function nextContext(ctx) {
1847
- return __spreadProps(__spreadValues({}, ctx), { depth: ctx.depth + 1 });
1617
+ function handleNotOperator(expr, val, params, outerMode, fieldType, dialect) {
1618
+ const innerMode = normalizeMode(val.mode);
1619
+ const effectiveMode = innerMode != null ? innerMode : outerMode;
1620
+ return buildNotComposite(
1621
+ expr,
1622
+ val,
1623
+ params,
1624
+ dialect,
1625
+ (e, subOp, subVal, p, d) => buildScalarOperator(e, subOp, subVal, p, effectiveMode, fieldType, d),
1626
+ ` ${SQL_TEMPLATES.AND} `
1627
+ );
1848
1628
  }
1849
- function buildRelationFilter(fieldName, value, ctx, builder) {
1850
- const ctx2 = nextContext(ctx);
1851
- if (ctx.isSubquery) {
1852
- return buildNestedRelation(fieldName, value, ctx2, builder);
1629
+ function buildDynamicLikePattern(op, placeholder, dialect) {
1630
+ if (dialect === "postgres") {
1631
+ switch (op) {
1632
+ case Ops.CONTAINS:
1633
+ return `('%' || ${placeholder} || '%')`;
1634
+ case Ops.STARTS_WITH:
1635
+ return `(${placeholder} || '%')`;
1636
+ case Ops.ENDS_WITH:
1637
+ return `('%' || ${placeholder})`;
1638
+ default:
1639
+ return placeholder;
1640
+ }
1641
+ }
1642
+ switch (op) {
1643
+ case Ops.CONTAINS:
1644
+ return `('%' || ${placeholder} || '%')`;
1645
+ case Ops.STARTS_WITH:
1646
+ return `(${placeholder} || '%')`;
1647
+ case Ops.ENDS_WITH:
1648
+ return `('%' || ${placeholder})`;
1649
+ default:
1650
+ return placeholder;
1853
1651
  }
1854
- return buildTopLevelRelation(fieldName, value, ctx2, builder);
1855
1652
  }
1856
- function buildWhereEntry(key, value, ctx, builder) {
1857
- const op = asLogicalOperator(key);
1858
- if (op) return buildLogical(op, value, ctx, builder);
1859
- if (isRelationField(key, ctx.model)) {
1860
- if (!isPlainObject(value)) {
1861
- throw createError(`Relation filter '${key}' must be an object`, {
1862
- path: [...ctx.path, key],
1863
- field: key,
1864
- modelName: ctx.model.name,
1865
- value
1866
- });
1653
+ function handleLikeOperator(expr, op, val, params, mode, dialect) {
1654
+ if (val === void 0) return "";
1655
+ if (isDynamicParameter(val)) {
1656
+ const placeholder2 = params.addAuto(val);
1657
+ const patternExpr = buildDynamicLikePattern(op, placeholder2, dialect);
1658
+ if (mode === Modes.INSENSITIVE) {
1659
+ return caseInsensitiveLike(expr, patternExpr, dialect);
1867
1660
  }
1868
- return buildRelationFilter(key, value, ctx, builder);
1661
+ return `${expr} ${SQL_TEMPLATES.LIKE} ${patternExpr}`;
1869
1662
  }
1870
- return buildScalarField(key, value, ctx);
1663
+ const placeholder = params.add(Wildcards[op](String(val)));
1664
+ if (mode === Modes.INSENSITIVE) {
1665
+ return caseInsensitiveLike(expr, placeholder, dialect);
1666
+ }
1667
+ return `${expr} ${SQL_TEMPLATES.LIKE} ${placeholder}`;
1871
1668
  }
1872
- function buildWhereInternal(where, ctx, builder) {
1873
- if (ctx.depth > MAX_QUERY_DEPTH) {
1874
- throw createError(
1875
- `Query nesting too deep (max ${MAX_QUERY_DEPTH} levels). This usually indicates a circular reference.`,
1876
- { path: ctx.path, modelName: ctx.model.name }
1877
- );
1669
+ function handleInOperator(expr, op, val, params, dialect) {
1670
+ if (val === void 0) return "";
1671
+ if (isDynamicParameter(val)) {
1672
+ const placeholder2 = params.addAuto(val);
1673
+ return op === Ops.IN ? inArray(expr, placeholder2, dialect) : notInArray(expr, placeholder2, dialect);
1878
1674
  }
1879
- if (isEmptyWhere(where)) {
1880
- return freezeResult(DEFAULT_WHERE_CLAUSE, EMPTY_JOINS);
1675
+ if (!Array.isArray(val)) {
1676
+ throw createError(`IN operators require array value`, {
1677
+ operator: op,
1678
+ value: val
1679
+ });
1881
1680
  }
1882
- const allJoins = [];
1883
- const clauses = [];
1884
- for (const [key, value] of Object.entries(where)) {
1885
- if (value === void 0) continue;
1886
- const result = buildWhereEntry(key, value, ctx, builder);
1887
- appendResult(result, clauses, allJoins);
1681
+ if (val.length === 0) {
1682
+ return op === Ops.IN ? "0=1" : "1=1";
1888
1683
  }
1889
- const finalClause = clauses.length > 0 ? clauses.join(SQL_SEPARATORS.CONDITION_AND) : DEFAULT_WHERE_CLAUSE;
1890
- return freezeResult(finalClause, dedupePreserveOrder(allJoins));
1684
+ const paramValue = prepareArrayParam(val, dialect);
1685
+ const placeholder = params.add(paramValue);
1686
+ return op === Ops.IN ? inArray(expr, placeholder, dialect) : notInArray(expr, placeholder, dialect);
1891
1687
  }
1892
- function normalizeLogicalValue(operator, value, ctx) {
1893
- if (Array.isArray(value)) {
1894
- const out = [];
1895
- for (let i = 0; i < value.length; i++) {
1896
- const v = value[i];
1897
- if (v === void 0) continue;
1898
- if (!isPlainObject(v)) {
1899
- throw createError(`${operator} entries must be objects`, {
1900
- path: [...ctx.path, operator, String(i)],
1901
- modelName: ctx.model.name,
1902
- value: v
1903
- });
1904
- }
1905
- out.push(v);
1906
- }
1907
- return out;
1908
- }
1909
- if (isPlainObject(value)) {
1910
- return [value];
1688
+ function handleComparisonOperator(expr, op, val, params) {
1689
+ if (val === void 0) return "";
1690
+ const COMPARISON_OPS2 = {
1691
+ [Ops.EQUALS]: "=",
1692
+ [Ops.GT]: ">",
1693
+ [Ops.GTE]: ">=",
1694
+ [Ops.LT]: "<",
1695
+ [Ops.LTE]: "<="
1696
+ };
1697
+ const sqlOp = COMPARISON_OPS2[op];
1698
+ if (!sqlOp) {
1699
+ throw createError(`Unsupported scalar operator: ${op}`, { operator: op });
1911
1700
  }
1912
- throw createError(`${operator} must be an object or array of objects`, {
1913
- path: [...ctx.path, operator],
1914
- modelName: ctx.model.name,
1915
- value
1916
- });
1701
+ const placeholder = params.addAuto(val);
1702
+ return `${expr} ${sqlOp} ${placeholder}`;
1917
1703
  }
1918
- function collectLogicalParts(operator, conditions, ctx, builder) {
1919
- const allJoins = [];
1920
- const clauses = [];
1921
- for (let i = 0; i < conditions.length; i++) {
1922
- const result = builder.build(conditions[i], __spreadProps(__spreadValues({}, ctx), {
1923
- path: [...ctx.path, operator, String(i)],
1924
- depth: ctx.depth + 1
1925
- }));
1926
- if (isNonEmptyArray(result.joins)) allJoins.push(...result.joins);
1927
- if (result.clause && result.clause !== DEFAULT_WHERE_CLAUSE) {
1928
- clauses.push(`(${result.clause})`);
1929
- }
1704
+ function buildArrayParam(val, params, dialect) {
1705
+ if (isDynamicParameter(val)) {
1706
+ return params.addAuto(val);
1930
1707
  }
1931
- return {
1932
- joins: dedupePreserveOrder(allJoins),
1933
- clauses: Object.freeze(clauses)
1934
- };
1935
- }
1936
- function buildLogicalClause(operator, clauses) {
1937
- if (clauses.length === 0) return DEFAULT_WHERE_CLAUSE;
1938
- if (operator === "NOT") {
1939
- if (clauses.length === 1) return `${SQL_TEMPLATES.NOT} ${clauses[0]}`;
1940
- return `${SQL_TEMPLATES.NOT} (${clauses.join(SQL_SEPARATORS.CONDITION_AND)})`;
1708
+ if (!Array.isArray(val)) {
1709
+ throw createError(`Array operation requires array value`, { value: val });
1941
1710
  }
1942
- return clauses.join(` ${operator} `);
1711
+ const paramValue = prepareArrayParam(val, dialect);
1712
+ return params.add(paramValue);
1943
1713
  }
1944
- function buildLogical(operator, value, ctx, builder) {
1945
- const conditions = normalizeLogicalValue(operator, value, ctx);
1946
- if (conditions.length === 0) {
1947
- const clause2 = operator === "OR" ? "0=1" : DEFAULT_WHERE_CLAUSE;
1948
- return freezeResult(clause2, EMPTY_JOINS);
1714
+ function buildArrayOperator(expr, op, val, params, fieldType, dialect) {
1715
+ if (val === void 0) return "";
1716
+ if (val === null) {
1717
+ if (op === Ops.EQUALS) return `${expr} ${SQL_TEMPLATES.IS_NULL}`;
1718
+ if (op === Ops.NOT) return `${expr} ${SQL_TEMPLATES.IS_NOT_NULL}`;
1949
1719
  }
1950
- const { joins, clauses } = collectLogicalParts(
1951
- operator,
1952
- conditions,
1953
- ctx,
1954
- builder
1955
- );
1956
- const clause = buildLogicalClause(operator, clauses);
1957
- return freezeResult(clause, joins);
1958
- }
1959
- function buildScalarField(fieldName, value, ctx) {
1960
- const field = assertFieldExists(fieldName, ctx.model, ctx.path);
1961
- const expr = col(ctx.alias, fieldName, ctx.model);
1962
- if (value === null) {
1963
- return freezeResult(`${expr} ${SQL_TEMPLATES.IS_NULL}`, EMPTY_JOINS);
1720
+ const cast = getArrayType(fieldType, dialect);
1721
+ if (op === Ops.EQUALS) {
1722
+ return handleArrayEquals(expr, val, params, cast, dialect);
1964
1723
  }
1965
- if (isPlainObject(value)) {
1966
- const mode = value.mode;
1967
- const ops = Object.entries(value).filter(
1968
- ([k, v]) => k !== "mode" && v !== void 0
1969
- );
1970
- if (ops.length === 0) {
1971
- return freezeResult(DEFAULT_WHERE_CLAUSE, EMPTY_JOINS);
1972
- }
1973
- const parts = [];
1974
- for (const [op, val] of ops) {
1975
- assertValidOperator(fieldName, op, field.type, ctx.path, ctx.model.name);
1976
- const clause3 = buildOperator(expr, op, val, ctx, mode, field.type);
1977
- if (isValidWhereClause(clause3)) parts.push(clause3);
1978
- }
1979
- const clause2 = parts.length > 0 ? parts.join(SQL_SEPARATORS.CONDITION_AND) : DEFAULT_WHERE_CLAUSE;
1980
- return freezeResult(clause2, EMPTY_JOINS);
1981
- }
1982
- const clause = buildOperator(
1983
- expr,
1984
- Ops.EQUALS,
1985
- value,
1986
- ctx,
1987
- void 0,
1988
- field.type
1989
- );
1990
- return freezeResult(clause || DEFAULT_WHERE_CLAUSE, EMPTY_JOINS);
1991
- }
1992
- function buildOperator(expr, op, val, ctx, mode, fieldType) {
1993
- if (fieldType && isArrayType(fieldType)) {
1994
- return buildArrayOperator(expr, op, val, ctx.params, fieldType, ctx.dialect);
1724
+ if (op === Ops.NOT) {
1725
+ return handleArrayNot(expr, val, params, cast, dialect);
1995
1726
  }
1996
- if (fieldType && isJsonType(fieldType)) {
1997
- const JSON_OPS = /* @__PURE__ */ new Set([
1998
- Ops.PATH,
1999
- Ops.STRING_CONTAINS,
2000
- Ops.STRING_STARTS_WITH,
2001
- Ops.STRING_ENDS_WITH
2002
- ]);
2003
- if (JSON_OPS.has(op)) {
2004
- return buildJsonOperator(expr, op, val, ctx.params, ctx.dialect);
2005
- }
1727
+ switch (op) {
1728
+ case Ops.HAS:
1729
+ return handleArrayHas(expr, val, params, cast, dialect);
1730
+ case Ops.HAS_SOME:
1731
+ return handleArrayHasSome(expr, val, params, cast, dialect);
1732
+ case Ops.HAS_EVERY:
1733
+ return handleArrayHasEvery(expr, val, params, cast, dialect);
1734
+ case Ops.IS_EMPTY:
1735
+ return handleArrayIsEmpty(expr, val, dialect);
1736
+ default:
1737
+ throw createError(`Unknown array operator: ${op}`, { operator: op });
2006
1738
  }
2007
- return buildScalarOperator(
2008
- expr,
2009
- op,
2010
- val,
2011
- ctx.params,
2012
- mode,
2013
- fieldType,
2014
- ctx.dialect
2015
- );
2016
1739
  }
2017
-
2018
- // src/builder/shared/alias-generator.ts
2019
- function toSafeSqlIdentifier(input) {
2020
- const raw = String(input);
2021
- const cleaned = raw.replace(/\W/g, "_");
2022
- const startsOk = /^[a-zA-Z_]/.test(cleaned);
2023
- const base = startsOk ? cleaned : `_${cleaned}`;
2024
- const fallback = base.length > 0 ? base : "_t";
2025
- const lowered = fallback.toLowerCase();
2026
- return SQL_KEYWORDS.has(lowered) ? `_${lowered}` : lowered;
1740
+ function handleArrayEquals(expr, val, params, cast, dialect) {
1741
+ if (val === void 0) return "";
1742
+ if (isEmptyArray(val)) {
1743
+ return arrayIsEmpty(expr, dialect);
1744
+ }
1745
+ const placeholder = buildArrayParam(val, params, dialect);
1746
+ return arrayEquals(expr, placeholder, cast, dialect);
2027
1747
  }
2028
- function createAliasGenerator(maxAliases = 1e4) {
2029
- let counter = 0;
2030
- const usedAliases = /* @__PURE__ */ new Set();
2031
- return {
2032
- next(baseName) {
2033
- if (usedAliases.size >= maxAliases) {
2034
- throw new Error(
2035
- `Alias generator exceeded maximum of ${maxAliases} aliases. This indicates a query complexity issue or potential infinite loop.`
2036
- );
2037
- }
2038
- const base = toSafeSqlIdentifier(baseName);
2039
- const suffix = `_${counter}`;
2040
- const maxLen = 63;
2041
- const baseMax = Math.max(1, maxLen - suffix.length);
2042
- const trimmedBase = base.length > baseMax ? base.slice(0, baseMax) : base;
2043
- const alias = `${trimmedBase}${suffix}`;
2044
- counter += 1;
2045
- if (usedAliases.has(alias)) {
2046
- throw new Error(
2047
- `CRITICAL: Duplicate alias '${alias}' at counter=${counter}. This indicates a bug in alias generation logic.`
2048
- );
2049
- }
2050
- usedAliases.add(alias);
2051
- return alias;
1748
+ function handleArrayNot(expr, val, params, cast, dialect) {
1749
+ if (val === void 0) return "";
1750
+ let target = val;
1751
+ if (isPlainObject(val)) {
1752
+ const entries = Object.entries(val).filter(([, v]) => v !== void 0);
1753
+ if (entries.length === 1 && entries[0][0] === Ops.EQUALS) {
1754
+ target = entries[0][1];
1755
+ } else {
1756
+ throw createError(`Array NOT only supports { equals: ... } shape`, {
1757
+ operator: Ops.NOT,
1758
+ value: val
1759
+ });
2052
1760
  }
2053
- };
2054
- }
2055
- var MAX_PARAM_INDEX = Number.MAX_SAFE_INTEGER - 1e3;
2056
- function assertSameLength(params, mappings) {
2057
- if (params.length !== mappings.length) {
2058
- throw new Error(
2059
- `CRITICAL: State corruption - params=${params.length}, mappings=${mappings.length}`
2060
- );
2061
1761
  }
2062
- }
2063
- function assertValidNextIndex(index) {
2064
- if (!Number.isInteger(index) || index < 1) {
2065
- throw new Error(`CRITICAL: Index must be integer >= 1, got ${index}`);
1762
+ if (target === null) {
1763
+ return `${expr} ${SQL_TEMPLATES.IS_NOT_NULL}`;
2066
1764
  }
2067
- }
2068
- function assertNextIndexMatches(mappingsLength, nextIndex) {
2069
- const expected = mappingsLength + 1;
2070
- if (nextIndex !== expected) {
2071
- throw new Error(
2072
- `CRITICAL: Next index mismatch - expected ${expected}, got ${nextIndex}`
2073
- );
1765
+ if (isEmptyArray(target)) {
1766
+ return arrayIsNotEmpty(expr, dialect);
2074
1767
  }
1768
+ const placeholder = buildArrayParam(target, params, dialect);
1769
+ return `${SQL_TEMPLATES.NOT} (${arrayEquals(expr, placeholder, cast, dialect)})`;
2075
1770
  }
2076
- function assertSequentialIndex(actual, expected) {
2077
- if (actual !== expected) {
2078
- throw new Error(
2079
- `CRITICAL: Indices must be sequential from 1..N. Expected ${expected}, got ${actual}`
2080
- );
1771
+ function handleArrayHas(expr, val, params, cast, dialect) {
1772
+ if (val === void 0) return "";
1773
+ if (val === null) {
1774
+ throw createError(`has requires scalar value`, {
1775
+ operator: Ops.HAS,
1776
+ value: val
1777
+ });
2081
1778
  }
2082
- }
2083
- function assertExactlyOneOfDynamicOrValue(m) {
2084
- const hasDynamic = typeof m.dynamicName === "string";
2085
- const hasStatic = m.value !== void 0;
2086
- if (hasDynamic === hasStatic) {
2087
- throw new Error(
2088
- `CRITICAL: ParamMap ${m.index} must have exactly one of dynamicName or value`
2089
- );
1779
+ if (!isDynamicParameter(val) && Array.isArray(val)) {
1780
+ throw createError(`has requires scalar value (single element), not array`, {
1781
+ operator: Ops.HAS,
1782
+ value: val
1783
+ });
2090
1784
  }
2091
- }
2092
- function normalizeDynamicNameOrThrow(dynamicName, index) {
2093
- const dn = dynamicName.trim();
2094
- if (dn.length === 0) {
2095
- throw new Error(`CRITICAL: dynamicName cannot be empty (index=${index})`);
1785
+ if (isPlainObject(val)) {
1786
+ throw createError(`has requires scalar value`, {
1787
+ operator: Ops.HAS,
1788
+ value: val
1789
+ });
2096
1790
  }
2097
- return dn;
1791
+ const placeholder = params.addAuto(val);
1792
+ return arrayContains(expr, placeholder, cast, dialect);
2098
1793
  }
2099
- function assertUniqueDynamicName(dn, seen) {
2100
- if (seen.has(dn)) {
2101
- throw new Error(`CRITICAL: Duplicate dynamic param name in mappings: ${dn}`);
1794
+ function handleArrayHasSome(expr, val, params, cast, dialect) {
1795
+ if (val === void 0) return "";
1796
+ if (isDynamicParameter(val)) {
1797
+ const placeholder2 = params.addAuto(val);
1798
+ return arrayOverlaps(expr, placeholder2, cast, dialect);
2102
1799
  }
2103
- seen.add(dn);
2104
- }
2105
- function validateMappingEntry(m, expectedIndex, seenDynamic) {
2106
- assertSequentialIndex(m.index, expectedIndex);
2107
- assertExactlyOneOfDynamicOrValue(m);
2108
- if (typeof m.dynamicName === "string") {
2109
- const dn = normalizeDynamicNameOrThrow(m.dynamicName, m.index);
2110
- assertUniqueDynamicName(dn, seenDynamic);
1800
+ if (!Array.isArray(val)) {
1801
+ throw createError(`hasSome requires array value`, {
1802
+ operator: Ops.HAS_SOME,
1803
+ value: val
1804
+ });
2111
1805
  }
2112
- }
2113
- function validateMappings(mappings) {
2114
- const seenDynamic = /* @__PURE__ */ new Set();
2115
- for (let i = 0; i < mappings.length; i++) {
2116
- validateMappingEntry(mappings[i], i + 1, seenDynamic);
1806
+ if (val.length > LIMITS.MAX_ARRAY_SIZE) {
1807
+ throw createError(
1808
+ `Array too large (${val.length} elements, max ${LIMITS.MAX_ARRAY_SIZE})`,
1809
+ { operator: Ops.HAS_SOME, value: `[${val.length} items]` }
1810
+ );
2117
1811
  }
1812
+ if (val.length === 0) return "0=1";
1813
+ const paramValue = prepareArrayParam(val, dialect);
1814
+ const placeholder = params.add(paramValue);
1815
+ return arrayOverlaps(expr, placeholder, cast, dialect);
2118
1816
  }
2119
- function validateState(params, mappings, index) {
2120
- assertSameLength(params, mappings);
2121
- assertValidNextIndex(index);
2122
- if (mappings.length === 0) return;
2123
- validateMappings(mappings);
2124
- assertNextIndexMatches(mappings.length, index);
1817
+ function handleArrayHasEvery(expr, val, params, cast, dialect) {
1818
+ if (val === void 0) return "";
1819
+ const placeholder = buildArrayParam(val, params, dialect);
1820
+ return arrayContainsAll(expr, placeholder, cast, dialect);
2125
1821
  }
2126
- function normalizeValue(value) {
2127
- if (value instanceof Date) {
2128
- return value.toISOString();
1822
+ function handleArrayIsEmpty(expr, val, dialect) {
1823
+ if (typeof val !== "boolean") {
1824
+ throw createError(`isEmpty requires boolean value`, {
1825
+ operator: Ops.IS_EMPTY,
1826
+ value: val
1827
+ });
2129
1828
  }
2130
- return value;
1829
+ return val === true ? arrayIsEmpty(expr, dialect) : arrayIsNotEmpty(expr, dialect);
2131
1830
  }
2132
- function createStoreInternal(startIndex, initialParams = [], initialMappings = []) {
2133
- let index = startIndex;
2134
- const params = [...initialParams];
2135
- const mappings = [...initialMappings];
2136
- const dynamicNameToIndex = /* @__PURE__ */ new Map();
2137
- for (const m of initialMappings) {
2138
- if (typeof m.dynamicName === "string") {
2139
- dynamicNameToIndex.set(m.dynamicName.trim(), m.index);
1831
+
1832
+ // src/builder/where/operators-json.ts
1833
+ var SAFE_JSON_PATH_SEGMENT = /^[a-zA-Z_]\w*$/;
1834
+ function validateJsonPathSegments(segments) {
1835
+ for (const segment of segments) {
1836
+ if (typeof segment !== "string") {
1837
+ throw createError("JSON path segments must be strings", {
1838
+ operator: Ops.PATH,
1839
+ value: segment
1840
+ });
1841
+ }
1842
+ if (!SAFE_JSON_PATH_SEGMENT.test(segment)) {
1843
+ throw createError(
1844
+ `Invalid JSON path segment: '${segment}'. Must be alphanumeric with underscores, starting with letter or underscore.`,
1845
+ { operator: Ops.PATH, value: segment }
1846
+ );
2140
1847
  }
2141
1848
  }
2142
- function assertCanAdd() {
2143
- if (index > MAX_PARAM_INDEX) {
2144
- throw new Error(
2145
- `CRITICAL: Cannot add param - would overflow MAX_SAFE_INTEGER. Current index: ${index}`
2146
- );
2147
- }
2148
- }
2149
- function normalizeDynamicName(dynamicName) {
2150
- const dn = dynamicName.trim();
2151
- if (dn.length === 0) {
2152
- throw new Error("CRITICAL: dynamicName cannot be empty");
2153
- }
2154
- return dn;
1849
+ }
1850
+ function buildJsonOperator(expr, op, val, params, dialect) {
1851
+ if (val === void 0) return "";
1852
+ if (op === Ops.PATH && isPlainObject(val) && "path" in val) {
1853
+ return handleJsonPath(expr, val, params, dialect);
2155
1854
  }
2156
- function format(position) {
2157
- return `$${position}`;
1855
+ const jsonWildcards = {
1856
+ [Ops.STRING_CONTAINS]: (v) => `%${v}%`,
1857
+ [Ops.STRING_STARTS_WITH]: (v) => `${v}%`,
1858
+ [Ops.STRING_ENDS_WITH]: (v) => `%${v}`
1859
+ };
1860
+ if (op in jsonWildcards) {
1861
+ return handleJsonWildcard(expr, op, val, params, jsonWildcards, dialect);
2158
1862
  }
2159
- function addDynamic(dynamicName) {
2160
- const dn = normalizeDynamicName(dynamicName);
2161
- const existing = dynamicNameToIndex.get(dn);
2162
- if (existing !== void 0) {
2163
- return format(existing);
2164
- }
2165
- const position = index;
2166
- dynamicNameToIndex.set(dn, position);
2167
- params.push(void 0);
2168
- mappings.push({ index: position, dynamicName: dn });
2169
- index++;
2170
- return format(position);
1863
+ throw createError(`Unsupported JSON operator: ${op}`, { operator: op });
1864
+ }
1865
+ function handleJsonPath(expr, val, params, dialect) {
1866
+ const v = val;
1867
+ if (!Array.isArray(v.path)) {
1868
+ throw createError("JSON path must be an array", { operator: Ops.PATH });
2171
1869
  }
2172
- function addStatic(value) {
2173
- const position = index;
2174
- const normalizedValue = normalizeValue(value);
2175
- params.push(normalizedValue);
2176
- mappings.push({ index: position, value: normalizedValue });
2177
- index++;
2178
- return format(position);
1870
+ if (v.path.length === 0) {
1871
+ throw createError("JSON path cannot be empty", { operator: Ops.PATH });
2179
1872
  }
2180
- function add(value, dynamicName) {
2181
- assertCanAdd();
2182
- return dynamicName === void 0 ? addStatic(value) : addDynamic(dynamicName);
1873
+ validateJsonPathSegments(v.path);
1874
+ const pathExpr = dialect === "sqlite" ? params.add(`$.${v.path.join(".")}`) : params.add(v.path);
1875
+ const rawOps = [
1876
+ ["=", v.equals],
1877
+ [">", v.gt],
1878
+ [">=", v.gte],
1879
+ ["<", v.lt],
1880
+ ["<=", v.lte]
1881
+ ];
1882
+ const ops = rawOps.filter(
1883
+ ([, value]) => value !== void 0
1884
+ );
1885
+ if (ops.length === 0) {
1886
+ throw createError("JSON path query missing comparison operator", {
1887
+ operator: Ops.PATH
1888
+ });
2183
1889
  }
2184
- function addAuto(value) {
2185
- if (isDynamicParameter(value)) {
2186
- const dynamicName = extractDynamicName(value);
2187
- return add(void 0, dynamicName);
1890
+ const parts = [];
1891
+ for (const [sqlOp, value] of ops) {
1892
+ if (value === null) {
1893
+ const base2 = jsonExtractText(expr, pathExpr, dialect);
1894
+ parts.push(`${base2} ${SQL_TEMPLATES.IS_NULL}`);
1895
+ continue;
2188
1896
  }
2189
- return add(value);
1897
+ const valPh = params.add(value);
1898
+ const base = typeof value === "number" ? jsonExtractNumeric(expr, pathExpr, dialect) : jsonExtractText(expr, pathExpr, dialect);
1899
+ parts.push(`${base} ${sqlOp} ${valPh}`);
2190
1900
  }
2191
- function snapshot() {
2192
- return Object.freeze({
2193
- index,
2194
- params: Object.freeze([...params]),
2195
- mappings: Object.freeze([...mappings])
1901
+ return parts.length === 1 ? parts[0] : `(${parts.join(" AND ")})`;
1902
+ }
1903
+ function handleJsonWildcard(expr, op, val, params, wildcards, dialect) {
1904
+ if (!isNotNullish(val)) {
1905
+ throw createError(`JSON string operator requires non-null value`, {
1906
+ operator: op,
1907
+ value: val
2196
1908
  });
2197
1909
  }
2198
- return {
2199
- add,
2200
- addAuto,
2201
- snapshot,
2202
- get index() {
2203
- return index;
2204
- }
2205
- };
2206
- }
2207
- function createParamStore(startIndex = 1) {
2208
- if (!Number.isInteger(startIndex) || startIndex < 1) {
2209
- throw new Error(`Start index must be integer >= 1, got ${startIndex}`);
1910
+ if (isPlainObject(val) || Array.isArray(val)) {
1911
+ throw createError(`JSON string operator requires scalar value`, {
1912
+ operator: op,
1913
+ value: val
1914
+ });
2210
1915
  }
2211
- if (startIndex > MAX_PARAM_INDEX) {
2212
- throw new Error(
2213
- `Start index too high (${startIndex}), risk of overflow at MAX_SAFE_INTEGER`
1916
+ const strVal = String(val);
1917
+ if (strVal.length > LIMITS.MAX_STRING_LENGTH) {
1918
+ throw createError(
1919
+ `String too long (${strVal.length} chars, max ${LIMITS.MAX_STRING_LENGTH})`,
1920
+ { operator: op }
2214
1921
  );
2215
1922
  }
2216
- return createStoreInternal(startIndex);
2217
- }
2218
- function createParamStoreFrom(existingParams, existingMappings, nextIndex) {
2219
- validateState([...existingParams], [...existingMappings], nextIndex);
2220
- return createStoreInternal(
2221
- nextIndex,
2222
- [...existingParams],
2223
- [...existingMappings]
2224
- );
1923
+ const placeholder = params.add(wildcards[op](strVal));
1924
+ const jsonText = jsonToText(expr, dialect);
1925
+ return caseInsensitiveLike(jsonText, placeholder, dialect);
2225
1926
  }
2226
1927
 
2227
- // src/builder/shared/state.ts
2228
- function toPublicResult(clause, joins, params) {
2229
- const snapshot = params.snapshot();
2230
- return Object.freeze({
2231
- clause: clause || DEFAULT_WHERE_CLAUSE,
2232
- joins: Object.freeze([...joins]),
2233
- params: snapshot.params,
2234
- paramMappings: snapshot.mappings,
2235
- nextParamIndex: snapshot.index
2236
- });
1928
+ // src/builder/where/relations.ts
1929
+ var NO_JOINS = Object.freeze([]);
1930
+ function isListRelation(fieldType) {
1931
+ return typeof fieldType === "string" && fieldType.endsWith("[]");
2237
1932
  }
2238
-
2239
- // src/builder/where.ts
2240
- function buildWhereClause(where, options) {
2241
- var _a, _b, _c, _d, _e;
2242
- const dialect = options.dialect || getGlobalDialect();
2243
- const params = (_a = options.params) != null ? _a : createParamStore();
2244
- const ctx = {
2245
- alias: options.alias,
2246
- model: options.model,
2247
- schemaModels: (_b = options.schemaModels) != null ? _b : [],
2248
- path: (_c = options.path) != null ? _c : [],
2249
- isSubquery: (_d = options.isSubquery) != null ? _d : false,
2250
- aliasGen: (_e = options.aliasGen) != null ? _e : createAliasGenerator(),
2251
- dialect,
2252
- params,
2253
- depth: 0
2254
- };
2255
- const result = whereBuilderInstance.build(where, ctx);
2256
- const publicResult = toPublicResult(result.clause, result.joins, params);
2257
- if (!options.isSubquery) {
2258
- const nums = [...publicResult.clause.matchAll(/\$(\d+)/g)].map(
2259
- (m) => parseInt(m[1], 10)
2260
- );
2261
- if (nums.length > 0) {
2262
- const min = Math.min(...nums);
2263
- if (min === 1) {
2264
- validateParamConsistency(publicResult.clause, publicResult.params);
2265
- } else {
2266
- validateParamConsistencyFragment(
2267
- publicResult.clause,
2268
- publicResult.params
2269
- );
2270
- }
1933
+ function buildToOneNullCheck(field, parentAlias, relTable, relAlias, join3, wantNull) {
1934
+ const isLocal = field.isForeignKeyLocal === true;
1935
+ const fkFields = normalizeKeyList(field.foreignKey);
1936
+ if (isLocal) {
1937
+ if (fkFields.length === 0) {
1938
+ throw createError(`Relation '${field.name}' is missing foreignKey`, {
1939
+ field: field.name
1940
+ });
2271
1941
  }
1942
+ const parts = fkFields.map((fk) => {
1943
+ const safe = fk.replace(/"/g, '""');
1944
+ const expr = `${parentAlias}."${safe}"`;
1945
+ return wantNull ? `${expr} ${SQL_TEMPLATES.IS_NULL}` : `${expr} ${SQL_TEMPLATES.IS_NOT_NULL}`;
1946
+ });
1947
+ if (parts.length === 1) return parts[0];
1948
+ return wantNull ? `(${parts.join(" OR ")})` : `(${parts.join(" AND ")})`;
2272
1949
  }
2273
- return publicResult;
2274
- }
2275
- function normalizeIntLike(name, v, opts = {}) {
2276
- var _a, _b;
2277
- if (!isNotNullish(v)) return void 0;
2278
- if (isDynamicParameter(v)) return v;
2279
- if (typeof v !== "number" || !Number.isFinite(v) || !Number.isInteger(v)) {
2280
- throw new Error(`${name} must be an integer`);
2281
- }
2282
- const min = (_a = opts.min) != null ? _a : 0;
2283
- const allowZero = (_b = opts.allowZero) != null ? _b : true;
2284
- if (!allowZero && v === 0) {
2285
- throw new Error(`${name} must be > 0`);
2286
- }
2287
- if (v < min) {
2288
- throw new Error(`${name} must be >= ${min}`);
2289
- }
2290
- if (typeof opts.max === "number" && v > opts.max) {
2291
- throw new Error(`${name} must be <= ${opts.max}`);
2292
- }
2293
- return v;
1950
+ const existsSql = `EXISTS (${SQL_TEMPLATES.SELECT} 1 ${SQL_TEMPLATES.FROM} ${relTable} ${relAlias} ${SQL_TEMPLATES.WHERE} ${join3})`;
1951
+ return wantNull ? `${SQL_TEMPLATES.NOT} ${existsSql}` : existsSql;
2294
1952
  }
2295
- function scopeName(scope, dynamicName) {
2296
- const s = String(scope).trim();
2297
- const dn = String(dynamicName).trim();
2298
- if (s.length === 0) return dn;
2299
- return `${s}:${dn}`;
1953
+ function buildToOneExistsMatch(relTable, relAlias, join3, sub) {
1954
+ const joins = sub.joins.length > 0 ? ` ${sub.joins.join(" ")}` : "";
1955
+ return `EXISTS (${SQL_TEMPLATES.SELECT} 1 ${SQL_TEMPLATES.FROM} ${relTable} ${relAlias}${joins} ${SQL_TEMPLATES.WHERE} ${join3} ${SQL_TEMPLATES.AND} ${sub.clause})`;
2300
1956
  }
2301
- function addAutoScoped(params, value, scope) {
2302
- if (isDynamicParameter(value)) {
2303
- const dn = extractDynamicName(value);
2304
- return params.add(void 0, scopeName(scope, dn));
2305
- }
2306
- return params.add(value);
1957
+ function buildToOneNotExistsMatch(relTable, relAlias, join3, sub) {
1958
+ const joins = sub.joins.length > 0 ? ` ${sub.joins.join(" ")}` : "";
1959
+ return `${SQL_TEMPLATES.NOT} EXISTS (${SQL_TEMPLATES.SELECT} 1 ${SQL_TEMPLATES.FROM} ${relTable} ${relAlias}${joins} ${SQL_TEMPLATES.WHERE} ${join3} ${SQL_TEMPLATES.AND} ${sub.clause})`;
2307
1960
  }
2308
-
2309
- // src/builder/shared/order-by-utils.ts
2310
- var flipNulls = (v) => {
2311
- const s = String(v).toLowerCase();
2312
- if (s === "first") return "last";
2313
- if (s === "last") return "first";
2314
- return v;
2315
- };
2316
- var flipSortString = (v) => {
2317
- if (typeof v !== "string") return v;
2318
- const s = v.toLowerCase();
2319
- if (s === "asc") return "desc";
2320
- if (s === "desc") return "asc";
2321
- return v;
2322
- };
2323
- var getNextSort = (sortRaw) => {
2324
- if (typeof sortRaw !== "string") return sortRaw;
2325
- const s = sortRaw.toLowerCase();
2326
- if (s === "asc") return "desc";
2327
- if (s === "desc") return "asc";
2328
- return sortRaw;
2329
- };
2330
- var flipObjectSort = (obj) => {
2331
- const sortRaw = obj.sort;
2332
- const out = __spreadProps(__spreadValues({}, obj), { sort: getNextSort(sortRaw) });
2333
- const nullsRaw = obj.nulls;
2334
- if (typeof nullsRaw === "string") {
2335
- out.nulls = flipNulls(nullsRaw);
1961
+ function buildListRelationFilters(args) {
1962
+ const {
1963
+ fieldName,
1964
+ value,
1965
+ ctx,
1966
+ whereBuilder,
1967
+ relModel,
1968
+ relTable,
1969
+ relAlias,
1970
+ join: join3
1971
+ } = args;
1972
+ const noneValue = value[RelationFilters.NONE];
1973
+ if (noneValue !== void 0 && noneValue !== null) {
1974
+ const sub = whereBuilder.build(noneValue, __spreadProps(__spreadValues({}, ctx), {
1975
+ alias: relAlias,
1976
+ model: relModel,
1977
+ path: [...ctx.path, fieldName, RelationFilters.NONE],
1978
+ isSubquery: true,
1979
+ depth: ctx.depth + 1
1980
+ }));
1981
+ const isEmptyFilter = isPlainObject(noneValue) && Object.keys(noneValue).length === 0;
1982
+ const canOptimize = !ctx.isSubquery && isEmptyFilter && sub.clause === DEFAULT_WHERE_CLAUSE && sub.joins.length === 0;
1983
+ if (canOptimize) {
1984
+ const checkField = relModel.fields.find(
1985
+ (f) => !f.isRelation && f.isRequired && f.name !== "id"
1986
+ ) || relModel.fields.find((f) => !f.isRelation && f.name === "id");
1987
+ if (checkField) {
1988
+ const leftJoinSql = `LEFT JOIN ${relTable} ${relAlias} ON ${join3}`;
1989
+ const whereClause = `${relAlias}.${quote(checkField.name)} IS NULL`;
1990
+ return Object.freeze({
1991
+ clause: whereClause,
1992
+ joins: [leftJoinSql]
1993
+ });
1994
+ }
1995
+ }
2336
1996
  }
2337
- return out;
2338
- };
2339
- var flipValue = (v) => {
2340
- if (typeof v === "string") return flipSortString(v);
2341
- if (isPlainObject(v)) return flipObjectSort(v);
2342
- return v;
2343
- };
2344
- var assertSingleFieldObject = (item) => {
2345
- if (!isPlainObject(item)) {
2346
- throw new Error("orderBy array entries must be objects");
1997
+ const filters = [
1998
+ {
1999
+ key: RelationFilters.SOME,
2000
+ wrap: (c, j) => `EXISTS (${SQL_TEMPLATES.SELECT} 1 ${SQL_TEMPLATES.FROM} ${relTable} ${relAlias}${j} ${SQL_TEMPLATES.WHERE} ${join3} ${SQL_TEMPLATES.AND} ${c})`
2001
+ },
2002
+ {
2003
+ key: RelationFilters.EVERY,
2004
+ wrap: (c, j) => `${SQL_TEMPLATES.NOT} EXISTS (${SQL_TEMPLATES.SELECT} 1 ${SQL_TEMPLATES.FROM} ${relTable} ${relAlias}${j} ${SQL_TEMPLATES.WHERE} ${join3} ${SQL_TEMPLATES.AND} ${SQL_TEMPLATES.NOT} (${c}))`
2005
+ },
2006
+ {
2007
+ key: RelationFilters.NONE,
2008
+ wrap: (c, j) => {
2009
+ const condition = c === DEFAULT_WHERE_CLAUSE ? "" : ` ${SQL_TEMPLATES.AND} ${c}`;
2010
+ return `${SQL_TEMPLATES.NOT} EXISTS (${SQL_TEMPLATES.SELECT} 1 ${SQL_TEMPLATES.FROM} ${relTable} ${relAlias}${j} ${SQL_TEMPLATES.WHERE} ${join3}${condition})`;
2011
+ }
2012
+ }
2013
+ ];
2014
+ const clauses = [];
2015
+ for (const { key, wrap } of filters) {
2016
+ const raw = value[key];
2017
+ if (raw === void 0 || raw === null) continue;
2018
+ const sub = whereBuilder.build(raw, __spreadProps(__spreadValues({}, ctx), {
2019
+ alias: relAlias,
2020
+ model: relModel,
2021
+ path: [...ctx.path, fieldName, key],
2022
+ isSubquery: true,
2023
+ depth: ctx.depth + 1
2024
+ }));
2025
+ const j = sub.joins.length > 0 ? ` ${sub.joins.join(" ")}` : "";
2026
+ clauses.push(wrap(sub.clause, j));
2347
2027
  }
2348
- const entries = Object.entries(item);
2349
- if (entries.length !== 1) {
2350
- throw new Error("orderBy array entries must have exactly one field");
2028
+ if (clauses.length === 0) {
2029
+ throw createError(
2030
+ `List relation '${fieldName}' requires one of { some, every, none }`,
2031
+ { field: fieldName, path: ctx.path, modelName: ctx.model.name }
2032
+ );
2351
2033
  }
2352
- return entries[0];
2353
- };
2354
- var flipOrderByArray = (orderBy) => {
2355
- return orderBy.map((item) => {
2356
- const [k, v] = assertSingleFieldObject(item);
2357
- return { [k]: flipValue(v) };
2034
+ return Object.freeze({
2035
+ clause: clauses.join(SQL_SEPARATORS.CONDITION_AND),
2036
+ joins: NO_JOINS
2358
2037
  });
2359
- };
2360
- var flipOrderByObject = (orderBy) => {
2361
- const out = {};
2362
- for (const [k, v] of Object.entries(orderBy)) {
2363
- out[k] = flipValue(v);
2038
+ }
2039
+ function buildToOneRelationFilters(args) {
2040
+ const {
2041
+ fieldName,
2042
+ value,
2043
+ ctx,
2044
+ whereBuilder,
2045
+ field,
2046
+ relModel,
2047
+ relTable,
2048
+ relAlias,
2049
+ join: join3
2050
+ } = args;
2051
+ const hasSomeEveryNone = isNotNullish(value[RelationFilters.SOME]) || isNotNullish(value[RelationFilters.EVERY]) || isNotNullish(value[RelationFilters.NONE]);
2052
+ if (hasSomeEveryNone) {
2053
+ throw createError(
2054
+ `To-one relation '${fieldName}' does not support { some, every, none }; use { is, isNot }`,
2055
+ { field: fieldName, path: ctx.path, modelName: ctx.model.name }
2056
+ );
2057
+ }
2058
+ const hasIs = Object.prototype.hasOwnProperty.call(value, "is");
2059
+ const hasIsNot = Object.prototype.hasOwnProperty.call(value, "isNot");
2060
+ let filterKey;
2061
+ let filterVal;
2062
+ if (hasIs) {
2063
+ filterKey = "is";
2064
+ filterVal = value.is;
2065
+ } else if (hasIsNot) {
2066
+ filterKey = "isNot";
2067
+ filterVal = value.isNot;
2068
+ } else {
2069
+ filterKey = "is";
2070
+ filterVal = value;
2071
+ }
2072
+ if (filterVal === void 0) {
2073
+ return Object.freeze({
2074
+ clause: DEFAULT_WHERE_CLAUSE,
2075
+ joins: NO_JOINS
2076
+ });
2077
+ }
2078
+ if (filterVal === null) {
2079
+ const wantNull = filterKey === "is";
2080
+ const clause2 = buildToOneNullCheck(
2081
+ field,
2082
+ ctx.alias,
2083
+ relTable,
2084
+ relAlias,
2085
+ join3,
2086
+ wantNull
2087
+ );
2088
+ return Object.freeze({
2089
+ clause: clause2,
2090
+ joins: NO_JOINS
2091
+ });
2092
+ }
2093
+ if (!isPlainObject(filterVal)) {
2094
+ throw createError(
2095
+ `Relation '${fieldName}' filter must be an object or null`,
2096
+ {
2097
+ field: fieldName,
2098
+ path: ctx.path,
2099
+ modelName: ctx.model.name,
2100
+ value: filterVal
2101
+ }
2102
+ );
2103
+ }
2104
+ const sub = whereBuilder.build(filterVal, __spreadProps(__spreadValues({}, ctx), {
2105
+ alias: relAlias,
2106
+ model: relModel,
2107
+ path: [...ctx.path, fieldName, filterKey],
2108
+ isSubquery: true,
2109
+ depth: ctx.depth + 1
2110
+ }));
2111
+ const clause = filterKey === "is" ? buildToOneExistsMatch(relTable, relAlias, join3, sub) : buildToOneNotExistsMatch(relTable, relAlias, join3, sub);
2112
+ return Object.freeze({
2113
+ clause,
2114
+ joins: NO_JOINS
2115
+ });
2116
+ }
2117
+ function ensureRelationFilterObject(fieldName, value, ctx) {
2118
+ if (!isPlainObject(value)) {
2119
+ throw createError(`Relation filter '${fieldName}' must be an object`, {
2120
+ path: [...ctx.path, fieldName],
2121
+ field: fieldName,
2122
+ modelName: ctx.model.name,
2123
+ value
2124
+ });
2125
+ }
2126
+ }
2127
+ function buildRelation(fieldName, value, ctx, whereBuilder) {
2128
+ const field = ctx.model.fields.find((f) => f.name === fieldName);
2129
+ if (!isValidRelationField(field)) {
2130
+ throw createError(`Invalid relation '${fieldName}'`, {
2131
+ field: fieldName,
2132
+ path: ctx.path,
2133
+ modelName: ctx.model.name
2134
+ });
2135
+ }
2136
+ const relModel = ctx.schemaModels.find((m) => m.name === field.relatedModel);
2137
+ if (!isNotNullish(relModel)) {
2138
+ throw createError(
2139
+ `Related model '${field.relatedModel}' not found in schema. Available models: ${ctx.schemaModels.map((m) => m.name).join(", ")}`,
2140
+ {
2141
+ field: fieldName,
2142
+ path: ctx.path,
2143
+ modelName: ctx.model.name
2144
+ }
2145
+ );
2146
+ }
2147
+ const relTable = buildTableReference(
2148
+ SQL_TEMPLATES.PUBLIC_SCHEMA,
2149
+ relModel.tableName,
2150
+ ctx.dialect
2151
+ );
2152
+ const relAlias = ctx.aliasGen.next(fieldName);
2153
+ const join3 = joinCondition(field, ctx.model, relModel, ctx.alias, relAlias);
2154
+ const args = {
2155
+ fieldName,
2156
+ value,
2157
+ ctx,
2158
+ whereBuilder,
2159
+ field,
2160
+ relModel,
2161
+ relTable,
2162
+ relAlias,
2163
+ join: join3
2164
+ };
2165
+ if (isListRelation(field.type)) return buildListRelationFilters(args);
2166
+ return buildToOneRelationFilters(args);
2167
+ }
2168
+ function buildTopLevelRelation(fieldName, value, ctx, whereBuilder) {
2169
+ ensureRelationFilterObject(fieldName, value, ctx);
2170
+ return buildRelation(fieldName, value, ctx, whereBuilder);
2171
+ }
2172
+ function buildNestedRelation(fieldName, value, ctx, whereBuilder) {
2173
+ return buildTopLevelRelation(fieldName, value, ctx, whereBuilder);
2174
+ }
2175
+
2176
+ // src/builder/shared/validators/field-validators.ts
2177
+ function assertFieldExists(name, model, path) {
2178
+ const field = model.fields.find((f) => f.name === name);
2179
+ if (!isNotNullish(field)) {
2180
+ throw createError(`Field '${name}' does not exist on '${model.name}'`, {
2181
+ field: name,
2182
+ path,
2183
+ modelName: model.name,
2184
+ availableFields: model.fields.map((f) => f.name)
2185
+ });
2186
+ }
2187
+ return field;
2188
+ }
2189
+ function assertValidOperator(fieldName, op, fieldType, path, modelName) {
2190
+ if (!isNotNullish(fieldType)) return;
2191
+ const ARRAY_OPS = /* @__PURE__ */ new Set([
2192
+ Ops.HAS,
2193
+ Ops.HAS_SOME,
2194
+ Ops.HAS_EVERY,
2195
+ Ops.IS_EMPTY
2196
+ ]);
2197
+ const JSON_OPS = /* @__PURE__ */ new Set([
2198
+ Ops.PATH,
2199
+ Ops.STRING_CONTAINS,
2200
+ Ops.STRING_STARTS_WITH,
2201
+ Ops.STRING_ENDS_WITH
2202
+ ]);
2203
+ const isArrayOp = ARRAY_OPS.has(op);
2204
+ const isFieldArray = isArrayType(fieldType);
2205
+ const arrayOpMismatch = isArrayOp && !isFieldArray;
2206
+ if (arrayOpMismatch) {
2207
+ throw createError(`'${op}' requires array field, got '${fieldType}'`, {
2208
+ operator: op,
2209
+ field: fieldName,
2210
+ path,
2211
+ modelName
2212
+ });
2213
+ }
2214
+ const isJsonOp = JSON_OPS.has(op);
2215
+ const isFieldJson = isJsonType(fieldType);
2216
+ const jsonOpMismatch = isJsonOp && !isFieldJson;
2217
+ if (jsonOpMismatch) {
2218
+ throw createError(`'${op}' requires JSON field, got '${fieldType}'`, {
2219
+ operator: op,
2220
+ field: fieldName,
2221
+ path,
2222
+ modelName
2223
+ });
2224
+ }
2225
+ }
2226
+
2227
+ // src/builder/where/builder.ts
2228
+ var WhereBuilder = class {
2229
+ build(where, ctx) {
2230
+ if (!isPlainObject(where)) {
2231
+ throw createError("where must be an object", {
2232
+ path: ctx.path,
2233
+ modelName: ctx.model.name
2234
+ });
2235
+ }
2236
+ return buildWhereInternal(where, ctx, this);
2364
2237
  }
2365
- return out;
2366
2238
  };
2367
- function reverseOrderByInput(orderBy) {
2368
- if (!isNotNullish(orderBy)) return orderBy;
2369
- if (Array.isArray(orderBy)) {
2370
- return flipOrderByArray(orderBy);
2239
+ var MAX_QUERY_DEPTH = 50;
2240
+ var EMPTY_JOINS = Object.freeze([]);
2241
+ var whereBuilderInstance = new WhereBuilder();
2242
+ function freezeResult(clause, joins = EMPTY_JOINS) {
2243
+ return Object.freeze({ clause, joins });
2244
+ }
2245
+ function dedupePreserveOrder(items) {
2246
+ if (items.length <= 1) return Object.freeze([...items]);
2247
+ const seen = /* @__PURE__ */ new Set();
2248
+ const out = [];
2249
+ for (const s of items) {
2250
+ if (!seen.has(s)) {
2251
+ seen.add(s);
2252
+ out.push(s);
2253
+ }
2371
2254
  }
2372
- if (isPlainObject(orderBy)) {
2373
- return flipOrderByObject(orderBy);
2255
+ return Object.freeze(out);
2256
+ }
2257
+ function appendResult(result, clauses, allJoins) {
2258
+ if (isValidWhereClause(result.clause)) clauses.push(result.clause);
2259
+ if (isNonEmptyArray(result.joins)) allJoins.push(...result.joins);
2260
+ }
2261
+ function asLogicalOperator(key) {
2262
+ if (key === LogicalOps.AND) return "AND";
2263
+ if (key === LogicalOps.OR) return "OR";
2264
+ if (key === LogicalOps.NOT) return "NOT";
2265
+ return null;
2266
+ }
2267
+ function nextContext(ctx) {
2268
+ return __spreadProps(__spreadValues({}, ctx), { depth: ctx.depth + 1 });
2269
+ }
2270
+ function buildRelationFilter(fieldName, value, ctx, builder) {
2271
+ const ctx2 = nextContext(ctx);
2272
+ if (ctx.isSubquery) {
2273
+ return buildNestedRelation(fieldName, value, ctx2, builder);
2374
2274
  }
2375
- throw new Error("orderBy must be an object or array of objects");
2275
+ return buildTopLevelRelation(fieldName, value, ctx2, builder);
2276
+ }
2277
+ function buildWhereEntry(key, value, ctx, builder) {
2278
+ const op = asLogicalOperator(key);
2279
+ if (op) return buildLogical(op, value, ctx, builder);
2280
+ if (isRelationField(key, ctx.model)) {
2281
+ if (!isPlainObject(value)) {
2282
+ throw createError(`Relation filter '${key}' must be an object`, {
2283
+ path: [...ctx.path, key],
2284
+ field: key,
2285
+ modelName: ctx.model.name,
2286
+ value
2287
+ });
2288
+ }
2289
+ return buildRelationFilter(key, value, ctx, builder);
2290
+ }
2291
+ return buildScalarField(key, value, ctx);
2292
+ }
2293
+ function buildWhereInternal(where, ctx, builder) {
2294
+ if (ctx.depth > MAX_QUERY_DEPTH) {
2295
+ throw createError(
2296
+ `Query nesting too deep (max ${MAX_QUERY_DEPTH} levels). This usually indicates a circular reference.`,
2297
+ { path: ctx.path, modelName: ctx.model.name }
2298
+ );
2299
+ }
2300
+ if (isEmptyWhere(where)) {
2301
+ return freezeResult(DEFAULT_WHERE_CLAUSE, EMPTY_JOINS);
2302
+ }
2303
+ const allJoins = [];
2304
+ const clauses = [];
2305
+ for (const [key, value] of Object.entries(where)) {
2306
+ if (value === void 0) continue;
2307
+ const result = buildWhereEntry(key, value, ctx, builder);
2308
+ appendResult(result, clauses, allJoins);
2309
+ }
2310
+ const finalClause = clauses.length > 0 ? clauses.join(SQL_SEPARATORS.CONDITION_AND) : DEFAULT_WHERE_CLAUSE;
2311
+ return freezeResult(finalClause, dedupePreserveOrder(allJoins));
2312
+ }
2313
+ function normalizeLogicalValue(operator, value, ctx) {
2314
+ if (Array.isArray(value)) {
2315
+ const out = [];
2316
+ for (let i = 0; i < value.length; i++) {
2317
+ const v = value[i];
2318
+ if (v === void 0) continue;
2319
+ if (!isPlainObject(v)) {
2320
+ throw createError(`${operator} entries must be objects`, {
2321
+ path: [...ctx.path, operator, String(i)],
2322
+ modelName: ctx.model.name,
2323
+ value: v
2324
+ });
2325
+ }
2326
+ out.push(v);
2327
+ }
2328
+ return out;
2329
+ }
2330
+ if (isPlainObject(value)) {
2331
+ return [value];
2332
+ }
2333
+ throw createError(`${operator} must be an object or array of objects`, {
2334
+ path: [...ctx.path, operator],
2335
+ modelName: ctx.model.name,
2336
+ value
2337
+ });
2338
+ }
2339
+ function collectLogicalParts(operator, conditions, ctx, builder) {
2340
+ const allJoins = [];
2341
+ const clauses = [];
2342
+ for (let i = 0; i < conditions.length; i++) {
2343
+ const result = builder.build(conditions[i], __spreadProps(__spreadValues({}, ctx), {
2344
+ path: [...ctx.path, operator, String(i)],
2345
+ depth: ctx.depth + 1
2346
+ }));
2347
+ if (isNonEmptyArray(result.joins)) allJoins.push(...result.joins);
2348
+ if (result.clause && result.clause !== DEFAULT_WHERE_CLAUSE) {
2349
+ clauses.push(`(${result.clause})`);
2350
+ }
2351
+ }
2352
+ return {
2353
+ joins: dedupePreserveOrder(allJoins),
2354
+ clauses: Object.freeze(clauses)
2355
+ };
2356
+ }
2357
+ function buildLogicalClause(operator, clauses) {
2358
+ if (clauses.length === 0) return DEFAULT_WHERE_CLAUSE;
2359
+ if (operator === "NOT") {
2360
+ if (clauses.length === 1) return `${SQL_TEMPLATES.NOT} ${clauses[0]}`;
2361
+ return `${SQL_TEMPLATES.NOT} (${clauses.join(SQL_SEPARATORS.CONDITION_AND)})`;
2362
+ }
2363
+ return clauses.join(` ${operator} `);
2364
+ }
2365
+ function buildLogical(operator, value, ctx, builder) {
2366
+ const conditions = normalizeLogicalValue(operator, value, ctx);
2367
+ if (conditions.length === 0) {
2368
+ const clause2 = operator === "OR" ? "0=1" : DEFAULT_WHERE_CLAUSE;
2369
+ return freezeResult(clause2, EMPTY_JOINS);
2370
+ }
2371
+ const { joins, clauses } = collectLogicalParts(
2372
+ operator,
2373
+ conditions,
2374
+ ctx,
2375
+ builder
2376
+ );
2377
+ const clause = buildLogicalClause(operator, clauses);
2378
+ return freezeResult(clause, joins);
2379
+ }
2380
+ function buildScalarField(fieldName, value, ctx) {
2381
+ const field = assertFieldExists(fieldName, ctx.model, ctx.path);
2382
+ const expr = col(ctx.alias, fieldName, ctx.model);
2383
+ if (value === null) {
2384
+ return freezeResult(`${expr} ${SQL_TEMPLATES.IS_NULL}`, EMPTY_JOINS);
2385
+ }
2386
+ if (isPlainObject(value)) {
2387
+ const mode = value.mode;
2388
+ const ops = Object.entries(value).filter(
2389
+ ([k, v]) => k !== "mode" && v !== void 0
2390
+ );
2391
+ if (ops.length === 0) {
2392
+ return freezeResult(DEFAULT_WHERE_CLAUSE, EMPTY_JOINS);
2393
+ }
2394
+ const parts = [];
2395
+ for (const [op, val] of ops) {
2396
+ assertValidOperator(fieldName, op, field.type, ctx.path, ctx.model.name);
2397
+ const clause3 = buildOperator(expr, op, val, ctx, mode, field.type);
2398
+ if (isValidWhereClause(clause3)) parts.push(clause3);
2399
+ }
2400
+ const clause2 = parts.length > 0 ? parts.join(SQL_SEPARATORS.CONDITION_AND) : DEFAULT_WHERE_CLAUSE;
2401
+ return freezeResult(clause2, EMPTY_JOINS);
2402
+ }
2403
+ const clause = buildOperator(
2404
+ expr,
2405
+ Ops.EQUALS,
2406
+ value,
2407
+ ctx,
2408
+ void 0,
2409
+ field.type
2410
+ );
2411
+ return freezeResult(clause || DEFAULT_WHERE_CLAUSE, EMPTY_JOINS);
2376
2412
  }
2377
- var normalizePairs = (pairs, parseValue) => {
2378
- return pairs.map(([field, rawValue]) => {
2379
- const parsed = parseValue(rawValue, field);
2380
- return {
2381
- [field]: parsed.nulls !== void 0 ? { sort: parsed.direction, nulls: parsed.nulls } : parsed.direction
2382
- };
2383
- });
2384
- };
2385
- function normalizeOrderByInput(orderBy, parseValue) {
2386
- if (!isNotNullish(orderBy)) return [];
2387
- if (Array.isArray(orderBy)) {
2388
- const pairs = orderBy.map(assertSingleFieldObject);
2389
- return normalizePairs(pairs, parseValue);
2413
+ function buildOperator(expr, op, val, ctx, mode, fieldType) {
2414
+ if (fieldType && isArrayType(fieldType)) {
2415
+ return buildArrayOperator(expr, op, val, ctx.params, fieldType, ctx.dialect);
2390
2416
  }
2391
- if (isPlainObject(orderBy)) {
2392
- return normalizePairs(Object.entries(orderBy), parseValue);
2417
+ if (fieldType && isJsonType(fieldType)) {
2418
+ const JSON_OPS = /* @__PURE__ */ new Set([
2419
+ Ops.PATH,
2420
+ Ops.STRING_CONTAINS,
2421
+ Ops.STRING_STARTS_WITH,
2422
+ Ops.STRING_ENDS_WITH
2423
+ ]);
2424
+ if (JSON_OPS.has(op)) {
2425
+ return buildJsonOperator(expr, op, val, ctx.params, ctx.dialect);
2426
+ }
2393
2427
  }
2394
- throw new Error("orderBy must be an object or array of objects");
2428
+ return buildScalarOperator(
2429
+ expr,
2430
+ op,
2431
+ val,
2432
+ ctx.params,
2433
+ mode,
2434
+ fieldType,
2435
+ ctx.dialect
2436
+ );
2395
2437
  }
2396
2438
 
2397
- // src/builder/pagination.ts
2398
- var MAX_LIMIT_OFFSET = 2147483647;
2399
- function parseDirectionRaw(raw, errorLabel) {
2400
- const s = String(raw).toLowerCase();
2401
- if (s === "asc" || s === "desc") return s;
2402
- throw new Error(`Invalid ${errorLabel}: ${raw}`);
2403
- }
2404
- function parseNullsRaw(raw, errorLabel) {
2405
- if (!isNotNullish(raw)) return void 0;
2406
- const s = String(raw).toLowerCase();
2407
- if (s === "first" || s === "last") return s;
2408
- throw new Error(`Invalid ${errorLabel}: ${raw}`);
2409
- }
2410
- function requireOrderByObject(v, errorPrefix) {
2411
- if (!isPlainObject(v) || !("sort" in v)) {
2412
- throw new Error(`${errorPrefix} must be 'asc' | 'desc' or { sort, nulls? }`);
2413
- }
2414
- return v;
2439
+ // src/builder/shared/alias-generator.ts
2440
+ function toSafeSqlIdentifier(input) {
2441
+ const raw = String(input);
2442
+ const cleaned = raw.replace(/\W/g, "_");
2443
+ const startsOk = /^[a-zA-Z_]/.test(cleaned);
2444
+ const base = startsOk ? cleaned : `_${cleaned}`;
2445
+ const fallback = base.length > 0 ? base : "_t";
2446
+ const lowered = fallback.toLowerCase();
2447
+ return SQL_KEYWORDS.has(lowered) ? `_${lowered}` : lowered;
2415
2448
  }
2416
- function assertAllowedOrderByKeys(obj, fieldName) {
2417
- const allowed = /* @__PURE__ */ new Set(["sort", "nulls"]);
2418
- for (const k of Object.keys(obj)) {
2419
- if (!allowed.has(k)) {
2420
- throw new Error(
2421
- fieldName ? `Unsupported orderBy key '${k}' for field '${fieldName}'` : `Unsupported orderBy key '${k}'`
2422
- );
2449
+ function createAliasGenerator(maxAliases = 1e4) {
2450
+ let counter = 0;
2451
+ const usedAliases = /* @__PURE__ */ new Set();
2452
+ return {
2453
+ next(baseName) {
2454
+ if (usedAliases.size >= maxAliases) {
2455
+ throw new Error(
2456
+ `Alias generator exceeded maximum of ${maxAliases} aliases. This indicates a query complexity issue or potential infinite loop.`
2457
+ );
2458
+ }
2459
+ const base = toSafeSqlIdentifier(baseName);
2460
+ const suffix = `_${counter}`;
2461
+ const maxLen = 63;
2462
+ const baseMax = Math.max(1, maxLen - suffix.length);
2463
+ const trimmedBase = base.length > baseMax ? base.slice(0, baseMax) : base;
2464
+ const alias = `${trimmedBase}${suffix}`;
2465
+ counter += 1;
2466
+ if (usedAliases.has(alias)) {
2467
+ throw new Error(
2468
+ `CRITICAL: Duplicate alias '${alias}' at counter=${counter}. This indicates a bug in alias generation logic.`
2469
+ );
2470
+ }
2471
+ usedAliases.add(alias);
2472
+ return alias;
2423
2473
  }
2424
- }
2474
+ };
2425
2475
  }
2426
- function parseOrderByValue(v, fieldName) {
2427
- const errorPrefix = fieldName ? `orderBy for '${fieldName}'` : "orderBy value";
2428
- if (typeof v === "string") {
2429
- return { direction: parseDirectionRaw(v, `${errorPrefix} direction`) };
2476
+ var MAX_PARAM_INDEX = Number.MAX_SAFE_INTEGER - 1e3;
2477
+ function assertSameLength(params, mappings) {
2478
+ if (params.length !== mappings.length) {
2479
+ throw new Error(
2480
+ `CRITICAL: State corruption - params=${params.length}, mappings=${mappings.length}`
2481
+ );
2430
2482
  }
2431
- const obj = requireOrderByObject(v, errorPrefix);
2432
- const direction = parseDirectionRaw(obj.sort, `${errorPrefix}.sort`);
2433
- const nulls = parseNullsRaw(obj.nulls, `${errorPrefix}.nulls`);
2434
- assertAllowedOrderByKeys(obj, fieldName);
2435
- return { direction, nulls };
2436
2483
  }
2437
- function normalizeFiniteInteger(name, v) {
2438
- if (typeof v !== "number" || !Number.isFinite(v) || !Number.isInteger(v)) {
2439
- throw new Error(`${name} must be an integer`);
2484
+ function assertValidNextIndex(index) {
2485
+ if (!Number.isInteger(index) || index < 1) {
2486
+ throw new Error(`CRITICAL: Index must be integer >= 1, got ${index}`);
2440
2487
  }
2441
- return v;
2442
2488
  }
2443
- function normalizeNonNegativeInt(name, v) {
2444
- if (isDynamicParameter(v)) return v;
2445
- const n = normalizeFiniteInteger(name, v);
2446
- if (n < 0) {
2447
- throw new Error(`${name} must be >= 0`);
2448
- }
2449
- if (n > MAX_LIMIT_OFFSET) {
2450
- throw new Error(`${name} must be <= ${MAX_LIMIT_OFFSET}`);
2489
+ function assertNextIndexMatches(mappingsLength, nextIndex) {
2490
+ const expected = mappingsLength + 1;
2491
+ if (nextIndex !== expected) {
2492
+ throw new Error(
2493
+ `CRITICAL: Next index mismatch - expected ${expected}, got ${nextIndex}`
2494
+ );
2451
2495
  }
2452
- return n;
2453
- }
2454
- function hasNonNullishProp(v, key) {
2455
- return isPlainObject(v) && key in v && isNotNullish(v[key]);
2456
- }
2457
- function normalizeIntegerOrDynamic(name, v) {
2458
- if (isDynamicParameter(v)) return v;
2459
- return normalizeFiniteInteger(name, v);
2460
2496
  }
2461
- function readSkipTake(relArgs) {
2462
- const hasSkip = hasNonNullishProp(relArgs, "skip");
2463
- const hasTake = hasNonNullishProp(relArgs, "take");
2464
- if (!hasSkip && !hasTake) {
2465
- return {
2466
- hasSkip: false,
2467
- hasTake: false,
2468
- skipVal: void 0,
2469
- takeVal: void 0
2470
- };
2497
+ function assertSequentialIndex(actual, expected) {
2498
+ if (actual !== expected) {
2499
+ throw new Error(
2500
+ `CRITICAL: Indices must be sequential from 1..N. Expected ${expected}, got ${actual}`
2501
+ );
2471
2502
  }
2472
- const obj = relArgs;
2473
- const skipVal = hasSkip ? normalizeNonNegativeInt("skip", obj.skip) : void 0;
2474
- const takeVal = hasTake ? normalizeIntegerOrDynamic("take", obj.take) : void 0;
2475
- return { hasSkip, hasTake, skipVal, takeVal };
2476
2503
  }
2477
- function buildOrderByFragment(entries, alias, dialect, model) {
2478
- if (entries.length === 0) return "";
2479
- const out = [];
2480
- for (const e of entries) {
2481
- const dir = e.direction.toUpperCase();
2482
- const c = col(alias, e.field, model);
2483
- if (dialect === "postgres") {
2484
- const nulls = isNotNullish(e.nulls) ? ` NULLS ${e.nulls.toUpperCase()}` : "";
2485
- out.push(`${c} ${dir}${nulls}`);
2486
- continue;
2487
- }
2488
- if (isNotNullish(e.nulls)) {
2489
- const isNullExpr = `(${c} IS NULL)`;
2490
- const nullRankDir = e.nulls === "first" ? "DESC" : "ASC";
2491
- out.push(`${isNullExpr} ${nullRankDir}`);
2492
- out.push(`${c} ${dir}`);
2493
- continue;
2494
- }
2495
- out.push(`${c} ${dir}`);
2504
+ function assertExactlyOneOfDynamicOrValue(m) {
2505
+ const hasDynamic = typeof m.dynamicName === "string";
2506
+ const hasStatic = m.value !== void 0;
2507
+ if (hasDynamic === hasStatic) {
2508
+ throw new Error(
2509
+ `CRITICAL: ParamMap ${m.index} must have exactly one of dynamicName or value`
2510
+ );
2496
2511
  }
2497
- return out.join(SQL_SEPARATORS.ORDER_BY);
2498
2512
  }
2499
- function defaultNullsFor(dialect, direction) {
2500
- if (dialect === "postgres") {
2501
- return direction === "asc" ? "last" : "first";
2513
+ function normalizeDynamicNameOrThrow(dynamicName, index) {
2514
+ const dn = dynamicName.trim();
2515
+ if (dn.length === 0) {
2516
+ throw new Error(`CRITICAL: dynamicName cannot be empty (index=${index})`);
2502
2517
  }
2503
- return direction === "asc" ? "first" : "last";
2518
+ return dn;
2504
2519
  }
2505
- function ensureCursorFieldsInOrder(orderEntries, cursorEntries) {
2506
- const existing = /* @__PURE__ */ new Map();
2507
- for (const e of orderEntries) existing.set(e.field, e);
2508
- const out = [...orderEntries];
2509
- for (const [field] of cursorEntries) {
2510
- if (!existing.has(field)) {
2511
- out.push({ field, direction: "asc" });
2512
- existing.set(field, out[out.length - 1]);
2513
- }
2520
+ function assertUniqueDynamicName(dn, seen) {
2521
+ if (seen.has(dn)) {
2522
+ throw new Error(`CRITICAL: Duplicate dynamic param name in mappings: ${dn}`);
2514
2523
  }
2515
- return out;
2524
+ seen.add(dn);
2516
2525
  }
2517
- function buildCursorFilterParts(cursor, cursorAlias, params, model) {
2518
- const entries = Object.entries(cursor);
2519
- if (entries.length === 0) {
2520
- throw new Error("cursor must have at least one field");
2521
- }
2522
- const placeholdersByField = /* @__PURE__ */ new Map();
2523
- const parts = [];
2524
- for (const [field, value] of entries) {
2525
- const c = `${cursorAlias}.${quote(field)}`;
2526
- if (value === null) {
2527
- parts.push(`${c} IS NULL`);
2528
- continue;
2529
- }
2530
- const ph = addAutoScoped(params, value, `cursor.filter.${field}`);
2531
- placeholdersByField.set(field, ph);
2532
- parts.push(`${c} = ${ph}`);
2526
+ function validateMappingEntry(m, expectedIndex, seenDynamic) {
2527
+ assertSequentialIndex(m.index, expectedIndex);
2528
+ assertExactlyOneOfDynamicOrValue(m);
2529
+ if (typeof m.dynamicName === "string") {
2530
+ const dn = normalizeDynamicNameOrThrow(m.dynamicName, m.index);
2531
+ assertUniqueDynamicName(dn, seenDynamic);
2533
2532
  }
2534
- return {
2535
- whereSql: parts.length === 1 ? parts[0] : `(${parts.join(" AND ")})`,
2536
- placeholdersByField
2537
- };
2538
- }
2539
- function cursorValueExpr(tableName, cursorAlias, cursorWhereSql, field, model) {
2540
- const colName = quote(field);
2541
- return `(SELECT ${cursorAlias}.${colName} ${SQL_TEMPLATES.FROM} ${tableName} ${cursorAlias} ${SQL_TEMPLATES.WHERE} ${cursorWhereSql} ${SQL_TEMPLATES.LIMIT} 1)`;
2542
2533
  }
2543
- function buildCursorRowExistsExpr(tableName, cursorAlias, cursorWhereSql) {
2544
- return `EXISTS (${SQL_TEMPLATES.SELECT} 1 ${SQL_TEMPLATES.FROM} ${tableName} ${cursorAlias} ${SQL_TEMPLATES.WHERE} ${cursorWhereSql} ${SQL_TEMPLATES.LIMIT} 1)`;
2534
+ function validateMappings(mappings) {
2535
+ const seenDynamic = /* @__PURE__ */ new Set();
2536
+ for (let i = 0; i < mappings.length; i++) {
2537
+ validateMappingEntry(mappings[i], i + 1, seenDynamic);
2538
+ }
2545
2539
  }
2546
- function buildCursorEqualityExpr(columnExpr, valueExpr) {
2547
- return `((${valueExpr} IS NULL AND ${columnExpr} IS NULL) OR (${valueExpr} IS NOT NULL AND ${columnExpr} = ${valueExpr}))`;
2540
+ function validateState(params, mappings, index) {
2541
+ assertSameLength(params, mappings);
2542
+ assertValidNextIndex(index);
2543
+ if (mappings.length === 0) return;
2544
+ validateMappings(mappings);
2545
+ assertNextIndexMatches(mappings.length, index);
2548
2546
  }
2549
- function buildCursorInequalityExpr(columnExpr, direction, nulls, valueExpr) {
2550
- const op = direction === "asc" ? ">" : "<";
2551
- if (nulls === "first") {
2552
- return `(CASE WHEN ${valueExpr} IS NULL THEN (${columnExpr} IS NOT NULL) ELSE (${columnExpr} ${op} ${valueExpr}) END)`;
2547
+ function normalizeValue2(value) {
2548
+ if (value instanceof Date) {
2549
+ return value.toISOString();
2553
2550
  }
2554
- return `(CASE WHEN ${valueExpr} IS NULL THEN 0=1 ELSE ((${columnExpr} ${op} ${valueExpr}) OR (${columnExpr} IS NULL)) END)`;
2551
+ return value;
2555
2552
  }
2556
- function buildOuterCursorMatch(cursor, outerAlias, placeholdersByField, params, model) {
2557
- const parts = [];
2558
- for (const [field, value] of Object.entries(cursor)) {
2559
- const c = col(outerAlias, field, model);
2560
- if (value === null) {
2561
- parts.push(`${c} IS NULL`);
2562
- continue;
2553
+ function createStoreInternal(startIndex, initialParams = [], initialMappings = []) {
2554
+ let index = startIndex;
2555
+ const params = [...initialParams];
2556
+ const mappings = [...initialMappings];
2557
+ const dynamicNameToIndex = /* @__PURE__ */ new Map();
2558
+ for (const m of initialMappings) {
2559
+ if (typeof m.dynamicName === "string") {
2560
+ dynamicNameToIndex.set(m.dynamicName.trim(), m.index);
2563
2561
  }
2564
- const existing = placeholdersByField.get(field);
2565
- if (typeof existing === "string" && existing.length > 0) {
2566
- parts.push(`${c} = ${existing}`);
2567
- continue;
2562
+ }
2563
+ function assertCanAdd() {
2564
+ if (index > MAX_PARAM_INDEX) {
2565
+ throw new Error(
2566
+ `CRITICAL: Cannot add param - would overflow MAX_SAFE_INTEGER. Current index: ${index}`
2567
+ );
2568
2568
  }
2569
- const ph = addAutoScoped(params, value, `cursor.outerMatch.${field}`);
2570
- parts.push(`${c} = ${ph}`);
2571
2569
  }
2572
- return parts.length === 1 ? parts[0] : `(${parts.join(" AND ")})`;
2573
- }
2574
- function buildOrderEntries(orderBy) {
2575
- const normalized = normalizeOrderByInput(orderBy, parseOrderByValue);
2576
- const entries = [];
2577
- for (const item of normalized) {
2578
- for (const [field, value] of Object.entries(item)) {
2579
- if (typeof value === "string") {
2580
- entries.push({ field, direction: value });
2581
- } else {
2582
- entries.push({
2583
- field,
2584
- direction: value.sort,
2585
- nulls: value.nulls
2586
- });
2587
- }
2570
+ function normalizeDynamicName(dynamicName) {
2571
+ const dn = dynamicName.trim();
2572
+ if (dn.length === 0) {
2573
+ throw new Error("CRITICAL: dynamicName cannot be empty");
2588
2574
  }
2575
+ return dn;
2589
2576
  }
2590
- return entries;
2591
- }
2592
- function buildCursorCondition(cursor, orderBy, tableName, alias, params, dialect, model) {
2593
- var _a;
2594
- const d = dialect != null ? dialect : getGlobalDialect();
2595
- const cursorEntries = Object.entries(cursor);
2596
- if (cursorEntries.length === 0) {
2597
- throw new Error("cursor must have at least one field");
2577
+ function format(position) {
2578
+ return `$${position}`;
2598
2579
  }
2599
- const cursorAlias = "__tp_cursor_src";
2600
- const { whereSql: cursorWhereSql, placeholdersByField } = buildCursorFilterParts(cursor, cursorAlias, params);
2601
- let orderEntries = buildOrderEntries(orderBy);
2602
- if (orderEntries.length === 0) {
2603
- orderEntries = cursorEntries.map(([field]) => ({
2604
- field,
2605
- direction: "asc"
2606
- }));
2607
- } else {
2608
- orderEntries = ensureCursorFieldsInOrder(orderEntries, cursorEntries);
2580
+ function addDynamic(dynamicName) {
2581
+ const dn = normalizeDynamicName(dynamicName);
2582
+ const existing = dynamicNameToIndex.get(dn);
2583
+ if (existing !== void 0) {
2584
+ return format(existing);
2585
+ }
2586
+ const position = index;
2587
+ dynamicNameToIndex.set(dn, position);
2588
+ params.push(void 0);
2589
+ mappings.push({ index: position, dynamicName: dn });
2590
+ index++;
2591
+ return format(position);
2609
2592
  }
2610
- const existsExpr = buildCursorRowExistsExpr(
2611
- tableName,
2612
- cursorAlias,
2613
- cursorWhereSql
2614
- );
2615
- const outerCursorMatch = buildOuterCursorMatch(
2616
- cursor,
2617
- alias,
2618
- placeholdersByField,
2619
- params,
2620
- model
2621
- );
2622
- const orClauses = [];
2623
- for (let level = 0; level < orderEntries.length; level++) {
2624
- const andParts = [];
2625
- for (let i = 0; i < level; i++) {
2626
- const e2 = orderEntries[i];
2627
- const c2 = col(alias, e2.field, model);
2628
- const v2 = cursorValueExpr(
2629
- tableName,
2630
- cursorAlias,
2631
- cursorWhereSql,
2632
- e2.field);
2633
- andParts.push(buildCursorEqualityExpr(c2, v2));
2593
+ function addStatic(value) {
2594
+ const position = index;
2595
+ const normalizedValue = normalizeValue2(value);
2596
+ params.push(normalizedValue);
2597
+ mappings.push({ index: position, value: normalizedValue });
2598
+ index++;
2599
+ return format(position);
2600
+ }
2601
+ function add(value, dynamicName) {
2602
+ assertCanAdd();
2603
+ return dynamicName === void 0 ? addStatic(value) : addDynamic(dynamicName);
2604
+ }
2605
+ function addAuto(value) {
2606
+ if (isDynamicParameter(value)) {
2607
+ const dynamicName = extractDynamicName(value);
2608
+ return add(void 0, dynamicName);
2634
2609
  }
2635
- const e = orderEntries[level];
2636
- const c = col(alias, e.field, model);
2637
- const v = cursorValueExpr(
2638
- tableName,
2639
- cursorAlias,
2640
- cursorWhereSql,
2641
- e.field);
2642
- const nulls = (_a = e.nulls) != null ? _a : defaultNullsFor(d, e.direction);
2643
- andParts.push(buildCursorInequalityExpr(c, e.direction, nulls, v));
2644
- orClauses.push(`(${andParts.join(SQL_SEPARATORS.CONDITION_AND)})`);
2610
+ return add(value);
2645
2611
  }
2646
- const exclusive = orClauses.join(SQL_SEPARATORS.CONDITION_OR);
2647
- return `(${existsExpr} ${SQL_SEPARATORS.CONDITION_AND} ((${exclusive})${SQL_SEPARATORS.CONDITION_OR}(${outerCursorMatch})))`;
2648
- }
2649
- function buildOrderBy(orderBy, alias, dialect, model) {
2650
- const entries = buildOrderEntries(orderBy);
2651
- if (entries.length === 0) return "";
2652
- const d = dialect != null ? dialect : getGlobalDialect();
2653
- return buildOrderByFragment(entries, alias, d, model);
2612
+ function snapshot() {
2613
+ return Object.freeze({
2614
+ index,
2615
+ params: Object.freeze([...params]),
2616
+ mappings: Object.freeze([...mappings])
2617
+ });
2618
+ }
2619
+ return {
2620
+ add,
2621
+ addAuto,
2622
+ snapshot,
2623
+ get index() {
2624
+ return index;
2625
+ }
2626
+ };
2654
2627
  }
2655
- function buildOrderByClause(args, alias, dialect, model) {
2656
- if (!isNotNullish(args.orderBy)) return "";
2657
- const result = buildOrderBy(args.orderBy, alias, dialect, model);
2658
- if (!isNonEmptyString(result)) {
2628
+ function createParamStore(startIndex = 1) {
2629
+ if (!Number.isInteger(startIndex) || startIndex < 1) {
2630
+ throw new Error(`Start index must be integer >= 1, got ${startIndex}`);
2631
+ }
2632
+ if (startIndex > MAX_PARAM_INDEX) {
2659
2633
  throw new Error(
2660
- "buildOrderByClause: orderBy specified but produced empty result"
2634
+ `Start index too high (${startIndex}), risk of overflow at MAX_SAFE_INTEGER`
2661
2635
  );
2662
2636
  }
2663
- return result;
2637
+ return createStoreInternal(startIndex);
2664
2638
  }
2665
- function normalizeTakeLike(v) {
2666
- const n = normalizeIntLike("take", v, {
2667
- min: Number.MIN_SAFE_INTEGER,
2668
- max: MAX_LIMIT_OFFSET,
2669
- allowZero: true
2670
- });
2671
- if (typeof n === "number") {
2672
- if (n === 0) return 0;
2673
- }
2674
- return n;
2639
+ function createParamStoreFrom(existingParams, existingMappings, nextIndex) {
2640
+ validateState([...existingParams], [...existingMappings], nextIndex);
2641
+ return createStoreInternal(
2642
+ nextIndex,
2643
+ [...existingParams],
2644
+ [...existingMappings]
2645
+ );
2675
2646
  }
2676
- function normalizeSkipLike(v) {
2677
- return normalizeIntLike("skip", v, {
2678
- min: 0,
2679
- max: MAX_LIMIT_OFFSET,
2680
- allowZero: true
2647
+
2648
+ // src/builder/shared/state.ts
2649
+ function toPublicResult(clause, joins, params) {
2650
+ const snapshot = params.snapshot();
2651
+ return Object.freeze({
2652
+ clause: clause || DEFAULT_WHERE_CLAUSE,
2653
+ joins: Object.freeze([...joins]),
2654
+ params: snapshot.params,
2655
+ paramMappings: snapshot.mappings,
2656
+ nextParamIndex: snapshot.index
2681
2657
  });
2682
2658
  }
2683
- function getPaginationParams(method, args) {
2684
- if (method === "findMany") {
2685
- return {
2686
- take: normalizeTakeLike(args.take),
2687
- skip: normalizeSkipLike(args.skip),
2688
- cursor: args.cursor
2689
- };
2690
- }
2691
- if (method === "findFirst") {
2692
- const skip = normalizeSkipLike(args.skip);
2693
- return { take: 1, skip: skip != null ? skip : 0 };
2694
- }
2695
- if (method === "findUnique") {
2696
- return { take: 1, skip: 0 };
2659
+
2660
+ // src/builder/where.ts
2661
+ function buildWhereClause(where, options) {
2662
+ var _a, _b, _c, _d, _e;
2663
+ const dialect = options.dialect || getGlobalDialect();
2664
+ const params = (_a = options.params) != null ? _a : createParamStore();
2665
+ const ctx = {
2666
+ alias: options.alias,
2667
+ model: options.model,
2668
+ schemaModels: (_b = options.schemaModels) != null ? _b : [],
2669
+ path: (_c = options.path) != null ? _c : [],
2670
+ isSubquery: (_d = options.isSubquery) != null ? _d : false,
2671
+ aliasGen: (_e = options.aliasGen) != null ? _e : createAliasGenerator(),
2672
+ dialect,
2673
+ params,
2674
+ depth: 0
2675
+ };
2676
+ const result = whereBuilderInstance.build(where, ctx);
2677
+ const publicResult = toPublicResult(result.clause, result.joins, params);
2678
+ if (!options.isSubquery) {
2679
+ const nums = [...publicResult.clause.matchAll(/\$(\d+)/g)].map(
2680
+ (m) => parseInt(m[1], 10)
2681
+ );
2682
+ if (nums.length > 0) {
2683
+ const min = Math.min(...nums);
2684
+ if (min === 1) {
2685
+ validateParamConsistency(publicResult.clause, publicResult.params);
2686
+ } else {
2687
+ validateParamConsistencyFragment(
2688
+ publicResult.clause,
2689
+ publicResult.params
2690
+ );
2691
+ }
2692
+ }
2697
2693
  }
2698
- return {};
2694
+ return publicResult;
2699
2695
  }
2700
2696
 
2701
2697
  // src/builder/select/fields.ts