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