prisma-sql 1.3.0 → 1.4.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.
@@ -0,0 +1,4657 @@
1
+ 'use strict';
2
+
3
+ var generatorHelper = require('@prisma/generator-helper');
4
+ var schemaParser = require('@dee-wan/schema-parser');
5
+ var promises = require('fs/promises');
6
+ var path = require('path');
7
+ var internals = require('@prisma/internals');
8
+
9
+ var __defProp = Object.defineProperty;
10
+ var __defProps = Object.defineProperties;
11
+ var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
12
+ var __getOwnPropNames = Object.getOwnPropertyNames;
13
+ var __getOwnPropSymbols = Object.getOwnPropertySymbols;
14
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
15
+ var __propIsEnum = Object.prototype.propertyIsEnumerable;
16
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
17
+ var __spreadValues = (a, b) => {
18
+ for (var prop in b || (b = {}))
19
+ if (__hasOwnProp.call(b, prop))
20
+ __defNormalProp(a, prop, b[prop]);
21
+ if (__getOwnPropSymbols)
22
+ for (var prop of __getOwnPropSymbols(b)) {
23
+ if (__propIsEnum.call(b, prop))
24
+ __defNormalProp(a, prop, b[prop]);
25
+ }
26
+ return a;
27
+ };
28
+ var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
29
+ var __commonJS = (cb, mod) => function __require() {
30
+ return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
31
+ };
32
+ var __async = (__this, __arguments, generator) => {
33
+ return new Promise((resolve, reject) => {
34
+ var fulfilled = (value) => {
35
+ try {
36
+ step(generator.next(value));
37
+ } catch (e) {
38
+ reject(e);
39
+ }
40
+ };
41
+ var rejected = (value) => {
42
+ try {
43
+ step(generator.throw(value));
44
+ } catch (e) {
45
+ reject(e);
46
+ }
47
+ };
48
+ var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected);
49
+ step((generator = generator.apply(__this, __arguments)).next());
50
+ });
51
+ };
52
+
53
+ // package.json
54
+ var require_package = __commonJS({
55
+ "package.json"(exports$1, module) {
56
+ module.exports = {
57
+ name: "prisma-sql",
58
+ version: "1.4.0",
59
+ description: "Convert Prisma queries to optimized SQL with type safety. 2-7x faster than Prisma Client.",
60
+ main: "dist/index.cjs",
61
+ module: "dist/index.js",
62
+ types: "dist/index.d.ts",
63
+ exports: {
64
+ ".": {
65
+ types: "./dist/index.d.ts",
66
+ import: "./dist/index.js",
67
+ require: "./dist/index.cjs"
68
+ },
69
+ "./generator": {
70
+ types: "./dist/generator.d.ts",
71
+ import: "./dist/generator.js",
72
+ require: "./dist/generator.cjs"
73
+ }
74
+ },
75
+ files: [
76
+ "dist",
77
+ "README.md",
78
+ "LICENSE"
79
+ ],
80
+ scripts: {
81
+ build: "tsup",
82
+ test: "vitest",
83
+ "test:coverage": "vitest --coverage",
84
+ "test:e2e": "vitest run tests/e2e",
85
+ bench: "npx tsx tests/helpers/run-benchmarks.ts",
86
+ "prisma:generate": "npx prisma generate --schema=tests/prisma/schema.prisma",
87
+ "prisma:migrate": "npx prisma migrate dev --schema=tests/prisma/schema.prisma",
88
+ "prisma:reset": "npx prisma db push --force-reset --skip-generate --schema=tests/prisma/schema.prisma",
89
+ prepublishOnly: "npm run build",
90
+ "sonar-cli": "sonar-scanner -Dsonar.projectKey=prisma-sql -Dsonar.sources=./src -Dsonar.host.url=http://localhost:9000 -Dsonar.login=sqp_9fe07460d0aa83f711d0edf4f317f05019d0613b",
91
+ sonar: "yarn sonar-cli && npx tsx scripts/sonar.ts"
92
+ },
93
+ keywords: [
94
+ "prisma",
95
+ "sql",
96
+ "query",
97
+ "optimizer",
98
+ "postgresql",
99
+ "sqlite",
100
+ "cloudflare",
101
+ "d1",
102
+ "performance",
103
+ "typescript",
104
+ "generator"
105
+ ],
106
+ repository: {
107
+ type: "git",
108
+ url: "https://github.com/multipliedtwice/prisma-to-sql.git"
109
+ },
110
+ bugs: {
111
+ url: "https://github.com/multipliedtwice/prisma-to-sql/issues"
112
+ },
113
+ homepage: "https://github.com/multipliedtwice/prisma-to-sql#readme",
114
+ author: "multipliedtwice <multipliedtwice@gmail.com>",
115
+ license: "MIT",
116
+ dependencies: {
117
+ "@dee-wan/schema-parser": "1.1.0",
118
+ "@prisma/generator-helper": "^7.2.0",
119
+ "@prisma/internals": "^7.2.0"
120
+ },
121
+ devDependencies: {
122
+ "@faker-js/faker": "^10.2.0",
123
+ "@prisma/adapter-better-sqlite3": "^7.2.0",
124
+ "@prisma/adapter-pg": "^7.2.0",
125
+ "@prisma/client": "7.2.0",
126
+ "@types/better-sqlite3": "^7.6.13",
127
+ "@types/node": "^20.0.0",
128
+ "better-sqlite3": "^12.6.2",
129
+ "drizzle-kit": "^0.31.8",
130
+ "drizzle-orm": "^0.45.1",
131
+ postgres: "^3.4.8",
132
+ prisma: "7.2.0",
133
+ tsup: "^8.5.1",
134
+ tsx: "^4.7.0",
135
+ typescript: "^5.3.3",
136
+ vitest: "^4.0.14"
137
+ },
138
+ peerDependencies: {
139
+ "@prisma/client": ">=4.0.0"
140
+ },
141
+ peerDependenciesMeta: {
142
+ "@prisma/client": {
143
+ optional: false
144
+ }
145
+ },
146
+ engines: {
147
+ node: ">=16.0.0"
148
+ }
149
+ };
150
+ }
151
+ });
152
+
153
+ // src/sql-builder-dialect.ts
154
+ var globalDialect = "postgres";
155
+ function setGlobalDialect(dialect) {
156
+ if (dialect !== "postgres" && dialect !== "sqlite") {
157
+ throw new Error(
158
+ `Invalid dialect: ${dialect}. Must be 'postgres' or 'sqlite'`
159
+ );
160
+ }
161
+ globalDialect = dialect;
162
+ }
163
+ function getGlobalDialect() {
164
+ return globalDialect;
165
+ }
166
+ function assertNonEmpty(value, name) {
167
+ if (!value || value.trim().length === 0) {
168
+ throw new Error(`${name} is required and cannot be empty`);
169
+ }
170
+ }
171
+ function arrayContains(column, value, arrayType, dialect) {
172
+ assertNonEmpty(column, "arrayContains column");
173
+ assertNonEmpty(value, "arrayContains value");
174
+ if (dialect === "postgres") {
175
+ return `${column} @> ARRAY[${value}]::${arrayType}`;
176
+ }
177
+ return `EXISTS (SELECT 1 FROM json_each(${column}) WHERE value = ${value})`;
178
+ }
179
+ function arrayOverlaps(column, value, arrayType, dialect) {
180
+ assertNonEmpty(column, "arrayOverlaps column");
181
+ assertNonEmpty(value, "arrayOverlaps value");
182
+ if (dialect === "postgres") {
183
+ return `${column} && ${value}::${arrayType}`;
184
+ }
185
+ return `EXISTS (
186
+ SELECT 1 FROM json_each(${column}) AS col
187
+ JOIN json_each(${value}) AS val
188
+ WHERE col.value = val.value
189
+ )`;
190
+ }
191
+ function arrayContainsAll(column, value, arrayType, dialect) {
192
+ assertNonEmpty(column, "arrayContainsAll column");
193
+ assertNonEmpty(value, "arrayContainsAll value");
194
+ if (dialect === "postgres") {
195
+ return `${column} @> ${value}::${arrayType}`;
196
+ }
197
+ return `NOT EXISTS (
198
+ SELECT 1 FROM json_each(${value}) AS val
199
+ WHERE NOT EXISTS (
200
+ SELECT 1 FROM json_each(${column}) AS col
201
+ WHERE col.value = val.value
202
+ )
203
+ )`;
204
+ }
205
+ function arrayIsEmpty(column, dialect) {
206
+ assertNonEmpty(column, "arrayIsEmpty column");
207
+ if (dialect === "postgres") {
208
+ return `(${column} IS NULL OR array_length(${column}, 1) IS NULL)`;
209
+ }
210
+ return `(${column} IS NULL OR json_array_length(${column}) = 0)`;
211
+ }
212
+ function arrayIsNotEmpty(column, dialect) {
213
+ assertNonEmpty(column, "arrayIsNotEmpty column");
214
+ if (dialect === "postgres") {
215
+ return `(${column} IS NOT NULL AND array_length(${column}, 1) IS NOT NULL)`;
216
+ }
217
+ return `(${column} IS NOT NULL AND json_array_length(${column}) > 0)`;
218
+ }
219
+ function arrayEquals(column, value, arrayType, dialect) {
220
+ assertNonEmpty(column, "arrayEquals column");
221
+ assertNonEmpty(value, "arrayEquals value");
222
+ if (dialect === "postgres") {
223
+ return `${column} = ${value}::${arrayType}`;
224
+ }
225
+ return `json(${column}) = json(${value})`;
226
+ }
227
+ function caseInsensitiveLike(column, pattern, dialect) {
228
+ assertNonEmpty(column, "caseInsensitiveLike column");
229
+ assertNonEmpty(pattern, "caseInsensitiveLike pattern");
230
+ if (dialect === "postgres") {
231
+ return `${column} ILIKE ${pattern}`;
232
+ }
233
+ return `LOWER(${column}) LIKE LOWER(${pattern})`;
234
+ }
235
+ function caseInsensitiveEquals(column, value, dialect) {
236
+ assertNonEmpty(column, "caseInsensitiveEquals column");
237
+ assertNonEmpty(value, "caseInsensitiveEquals value");
238
+ return `LOWER(${column}) = LOWER(${value})`;
239
+ }
240
+ function jsonExtractText(column, path, dialect) {
241
+ assertNonEmpty(column, "jsonExtractText column");
242
+ assertNonEmpty(path, "jsonExtractText path");
243
+ if (dialect === "postgres") {
244
+ const p = String(path).trim();
245
+ const pathExpr = /^\$\d+$/.test(p) ? `${p}::text[]` : p;
246
+ return `${column}#>>${pathExpr}`;
247
+ }
248
+ return `json_extract(${column}, ${path})`;
249
+ }
250
+ function jsonExtractNumeric(column, path, dialect) {
251
+ assertNonEmpty(column, "jsonExtractNumeric column");
252
+ assertNonEmpty(path, "jsonExtractNumeric path");
253
+ if (dialect === "postgres") {
254
+ const p = String(path).trim();
255
+ const pathExpr = /^\$\d+$/.test(p) ? `${p}::text[]` : p;
256
+ return `(${column}#>>${pathExpr})::numeric`;
257
+ }
258
+ return `CAST(json_extract(${column}, ${path}) AS REAL)`;
259
+ }
260
+ function jsonToText(column, dialect) {
261
+ assertNonEmpty(column, "jsonToText column");
262
+ if (dialect === "postgres") {
263
+ return `${column}::text`;
264
+ }
265
+ return column;
266
+ }
267
+ function inArray(column, value, dialect) {
268
+ assertNonEmpty(column, "inArray column");
269
+ assertNonEmpty(value, "inArray value");
270
+ if (dialect === "postgres") {
271
+ return `${column} = ANY(${value})`;
272
+ }
273
+ return `${column} IN (SELECT value FROM json_each(${value}))`;
274
+ }
275
+ function notInArray(column, value, dialect) {
276
+ assertNonEmpty(column, "notInArray column");
277
+ assertNonEmpty(value, "notInArray value");
278
+ if (dialect === "postgres") {
279
+ return `${column} != ALL(${value})`;
280
+ }
281
+ return `${column} NOT IN (SELECT value FROM json_each(${value}))`;
282
+ }
283
+ function getArrayType(prismaType, dialect) {
284
+ if (!prismaType || prismaType.length === 0) {
285
+ return dialect === "sqlite" ? "TEXT" : "text[]";
286
+ }
287
+ if (dialect === "sqlite") {
288
+ return "TEXT";
289
+ }
290
+ const baseType = prismaType.replace(/\[\]|\?/g, "");
291
+ switch (baseType) {
292
+ case "String":
293
+ return "text[]";
294
+ case "Int":
295
+ return "integer[]";
296
+ case "Float":
297
+ return "double precision[]";
298
+ case "Decimal":
299
+ return "numeric[]";
300
+ case "Boolean":
301
+ return "boolean[]";
302
+ case "BigInt":
303
+ return "bigint[]";
304
+ case "DateTime":
305
+ return "timestamptz[]";
306
+ default:
307
+ return "text[]";
308
+ }
309
+ }
310
+ function jsonAgg(content, dialect) {
311
+ assertNonEmpty(content, "jsonAgg content");
312
+ if (dialect === "postgres") {
313
+ return `json_agg(${content})`;
314
+ }
315
+ return `json_group_array(${content})`;
316
+ }
317
+ function jsonBuildObject(pairs, dialect) {
318
+ const safePairs = (pairs != null ? pairs : "").trim();
319
+ if (dialect === "postgres") {
320
+ return safePairs.length > 0 ? `json_build_object(${safePairs})` : `json_build_object()`;
321
+ }
322
+ return safePairs.length > 0 ? `json_object(${safePairs})` : `json_object()`;
323
+ }
324
+ function prepareArrayParam(value, dialect) {
325
+ if (!Array.isArray(value)) {
326
+ throw new Error("prepareArrayParam requires array value");
327
+ }
328
+ if (dialect === "postgres") {
329
+ return value;
330
+ }
331
+ return JSON.stringify(value);
332
+ }
333
+
334
+ // src/builder/shared/constants.ts
335
+ var SQL_SEPARATORS = Object.freeze({
336
+ FIELD_LIST: ", ",
337
+ CONDITION_AND: " AND ",
338
+ CONDITION_OR: " OR ",
339
+ ORDER_BY: ", "
340
+ });
341
+ var SQL_RESERVED_WORDS = /* @__PURE__ */ new Set([
342
+ "select",
343
+ "from",
344
+ "where",
345
+ "and",
346
+ "or",
347
+ "not",
348
+ "in",
349
+ "like",
350
+ "between",
351
+ "order",
352
+ "by",
353
+ "group",
354
+ "having",
355
+ "limit",
356
+ "offset",
357
+ "join",
358
+ "inner",
359
+ "left",
360
+ "right",
361
+ "outer",
362
+ "on",
363
+ "as",
364
+ "table",
365
+ "column",
366
+ "index",
367
+ "user",
368
+ "users",
369
+ "values",
370
+ "update",
371
+ "insert",
372
+ "delete",
373
+ "create",
374
+ "drop",
375
+ "alter",
376
+ "truncate",
377
+ "grant",
378
+ "revoke",
379
+ "exec",
380
+ "execute",
381
+ "union",
382
+ "intersect",
383
+ "except",
384
+ "case",
385
+ "when",
386
+ "then",
387
+ "else",
388
+ "end",
389
+ "null",
390
+ "true",
391
+ "false",
392
+ "is",
393
+ "exists",
394
+ "all",
395
+ "any",
396
+ "some"
397
+ ]);
398
+ var SQL_KEYWORDS = SQL_RESERVED_WORDS;
399
+ var DEFAULT_WHERE_CLAUSE = "1=1";
400
+ var SPECIAL_FIELDS = Object.freeze({
401
+ ID: "id"
402
+ });
403
+ var SQL_TEMPLATES = Object.freeze({
404
+ PUBLIC_SCHEMA: "public",
405
+ WHERE: "WHERE",
406
+ SELECT: "SELECT",
407
+ FROM: "FROM",
408
+ ORDER_BY: "ORDER BY",
409
+ GROUP_BY: "GROUP BY",
410
+ HAVING: "HAVING",
411
+ LIMIT: "LIMIT",
412
+ OFFSET: "OFFSET",
413
+ COUNT_ALL: "COUNT(*)",
414
+ AS: "AS",
415
+ DISTINCT_ON: "DISTINCT ON",
416
+ IS_NULL: "IS NULL",
417
+ IS_NOT_NULL: "IS NOT NULL",
418
+ LIKE: "LIKE",
419
+ AND: "AND",
420
+ OR: "OR",
421
+ NOT: "NOT"
422
+ });
423
+ var SCHEMA_PREFIXES = Object.freeze({
424
+ INTERNAL: "@",
425
+ COMMENT: "//"
426
+ });
427
+ var Ops = Object.freeze({
428
+ EQUALS: "equals",
429
+ NOT: "not",
430
+ GT: "gt",
431
+ GTE: "gte",
432
+ LT: "lt",
433
+ LTE: "lte",
434
+ IN: "in",
435
+ NOT_IN: "notIn",
436
+ CONTAINS: "contains",
437
+ STARTS_WITH: "startsWith",
438
+ ENDS_WITH: "endsWith",
439
+ HAS: "has",
440
+ HAS_SOME: "hasSome",
441
+ HAS_EVERY: "hasEvery",
442
+ IS_EMPTY: "isEmpty",
443
+ PATH: "path",
444
+ STRING_CONTAINS: "string_contains",
445
+ STRING_STARTS_WITH: "string_starts_with",
446
+ STRING_ENDS_WITH: "string_ends_with"
447
+ });
448
+ var LogicalOps = Object.freeze({
449
+ AND: "AND",
450
+ OR: "OR",
451
+ NOT: "NOT"
452
+ });
453
+ var RelationFilters = Object.freeze({
454
+ SOME: "some",
455
+ EVERY: "every",
456
+ NONE: "none"
457
+ });
458
+ var Modes = Object.freeze({
459
+ INSENSITIVE: "insensitive",
460
+ DEFAULT: "default"
461
+ });
462
+ var Wildcards = Object.freeze({
463
+ [Ops.CONTAINS]: (v) => `%${v}%`,
464
+ [Ops.STARTS_WITH]: (v) => `${v}%`,
465
+ [Ops.ENDS_WITH]: (v) => `%${v}`
466
+ });
467
+ var REGEX_CACHE = {
468
+ VALID_IDENTIFIER: /^[a-z_][a-z0-9_]*$/
469
+ };
470
+ var LIMITS = Object.freeze({
471
+ MAX_QUERY_DEPTH: 50,
472
+ MAX_ARRAY_SIZE: 1e4,
473
+ MAX_STRING_LENGTH: 1e4
474
+ });
475
+
476
+ // src/builder/shared/validators/type-guards.ts
477
+ function isNotNullish(value) {
478
+ return value !== null && value !== void 0;
479
+ }
480
+ function isNonEmptyString(value) {
481
+ return typeof value === "string" && value.trim().length > 0;
482
+ }
483
+ function isEmptyString(value) {
484
+ return typeof value === "string" && value.trim().length === 0;
485
+ }
486
+ function isNonEmptyArray(value) {
487
+ return Array.isArray(value) && value.length > 0;
488
+ }
489
+ function isEmptyArray(value) {
490
+ return Array.isArray(value) && value.length === 0;
491
+ }
492
+ function isPlainObject(val) {
493
+ if (!isNotNullish(val)) return false;
494
+ if (Array.isArray(val)) return false;
495
+ if (typeof val !== "object") return false;
496
+ return Object.prototype.toString.call(val) === "[object Object]";
497
+ }
498
+ function hasProperty(obj, key) {
499
+ return isPlainObject(obj) && key in obj;
500
+ }
501
+ function isArrayType(t) {
502
+ if (!isNotNullish(t)) return false;
503
+ const normalized = t.replace(/\?$/, "");
504
+ return normalized.endsWith("[]");
505
+ }
506
+ function isJsonType(t) {
507
+ return isNotNullish(t) && (t === "Json" || t === "Json?");
508
+ }
509
+ function hasValidContent(sql) {
510
+ return isNotNullish(sql) && sql.trim().length > 0;
511
+ }
512
+ function hasRequiredKeywords(sql) {
513
+ const upper = sql.toUpperCase();
514
+ const hasSelect = upper.includes("SELECT");
515
+ const hasFrom = upper.includes("FROM");
516
+ return hasSelect && hasFrom && upper.indexOf("SELECT") < upper.indexOf("FROM");
517
+ }
518
+
519
+ // src/builder/shared/errors.ts
520
+ var SqlBuilderError = class extends Error {
521
+ constructor(message, code, context) {
522
+ super(message);
523
+ this.name = "SqlBuilderError";
524
+ this.code = code;
525
+ this.context = context;
526
+ }
527
+ };
528
+ function createError(message, ctx, code = "VALIDATION_ERROR") {
529
+ const parts = [message];
530
+ if (isNonEmptyArray(ctx.path)) {
531
+ parts.push(`Path: ${ctx.path.join(".")}`);
532
+ }
533
+ if (isNotNullish(ctx.modelName)) {
534
+ parts.push(`Model: ${ctx.modelName}`);
535
+ }
536
+ if (isNonEmptyArray(ctx.availableFields)) {
537
+ parts.push(`Available fields: ${ctx.availableFields.join(", ")}`);
538
+ }
539
+ return new SqlBuilderError(parts.join("\n"), code, ctx);
540
+ }
541
+
542
+ // src/builder/shared/model-field-cache.ts
543
+ var SCALAR_SET_CACHE = /* @__PURE__ */ new WeakMap();
544
+ var RELATION_SET_CACHE = /* @__PURE__ */ new WeakMap();
545
+ function getScalarFieldSet(model) {
546
+ const cached = SCALAR_SET_CACHE.get(model);
547
+ if (cached) return cached;
548
+ const s = /* @__PURE__ */ new Set();
549
+ for (const f of model.fields) if (!f.isRelation) s.add(f.name);
550
+ SCALAR_SET_CACHE.set(model, s);
551
+ return s;
552
+ }
553
+ function getRelationFieldSet(model) {
554
+ const cached = RELATION_SET_CACHE.get(model);
555
+ if (cached) return cached;
556
+ const s = /* @__PURE__ */ new Set();
557
+ for (const f of model.fields) if (f.isRelation) s.add(f.name);
558
+ RELATION_SET_CACHE.set(model, s);
559
+ return s;
560
+ }
561
+
562
+ // src/builder/shared/validators/sql-validators.ts
563
+ function isValidWhereClause(clause) {
564
+ return isNotNullish(clause) && clause.trim().length > 0 && clause !== DEFAULT_WHERE_CLAUSE;
565
+ }
566
+ function isEmptyWhere(where) {
567
+ if (!isNotNullish(where)) return true;
568
+ return Object.keys(where).length === 0;
569
+ }
570
+ function validateSelectQuery(sql) {
571
+ if (!hasValidContent(sql)) {
572
+ throw new Error("CRITICAL: Generated empty SQL query");
573
+ }
574
+ if (!hasRequiredKeywords(sql)) {
575
+ throw new Error(
576
+ `CRITICAL: Invalid SQL structure. SQL: ${sql.substring(0, 100)}...`
577
+ );
578
+ }
579
+ }
580
+ function sqlPreview(sql) {
581
+ return `${sql.substring(0, 100)}...`;
582
+ }
583
+ function parseDollarNumber(sql, start, n) {
584
+ let i = start;
585
+ let num = 0;
586
+ let hasDigit = false;
587
+ while (i < n) {
588
+ const c = sql.charCodeAt(i);
589
+ if (c < 48 || c > 57) break;
590
+ hasDigit = true;
591
+ num = num * 10 + (c - 48);
592
+ i++;
593
+ }
594
+ if (!hasDigit || num <= 0) return { next: i, num: 0, ok: false };
595
+ return { next: i, num, ok: true };
596
+ }
597
+ function scanDollarPlaceholders(sql, markUpTo) {
598
+ const seen = new Uint8Array(markUpTo + 1);
599
+ let count = 0;
600
+ let min = Number.POSITIVE_INFINITY;
601
+ let max = 0;
602
+ const n = sql.length;
603
+ let i = 0;
604
+ while (i < n) {
605
+ if (sql.charCodeAt(i) !== 36) {
606
+ i++;
607
+ continue;
608
+ }
609
+ const { next, num, ok } = parseDollarNumber(sql, i + 1, n);
610
+ i = next;
611
+ if (!ok) continue;
612
+ count++;
613
+ if (num < min) min = num;
614
+ if (num > max) max = num;
615
+ if (num <= markUpTo) seen[num] = 1;
616
+ }
617
+ return { count, min, max, seen };
618
+ }
619
+ function assertNoGaps(scan, rangeMin, rangeMax, sql) {
620
+ for (let k = rangeMin; k <= rangeMax; k++) {
621
+ if (scan.seen[k] !== 1) {
622
+ throw new Error(
623
+ `CRITICAL: Parameter mismatch - SQL is missing placeholder $${k}. Placeholders must cover ${rangeMin}..${rangeMax} with no gaps. SQL: ${sqlPreview(sql)}`
624
+ );
625
+ }
626
+ }
627
+ }
628
+ function validateParamConsistency(sql, params) {
629
+ const paramLen = params.length;
630
+ if (paramLen === 0) {
631
+ if (sql.indexOf("$") === -1) return;
632
+ }
633
+ const scan = scanDollarPlaceholders(sql, paramLen);
634
+ if (scan.count === 0) {
635
+ if (paramLen !== 0) {
636
+ throw new Error(
637
+ `CRITICAL: Parameter mismatch - SQL has no placeholders but ${paramLen} params provided.`
638
+ );
639
+ }
640
+ return;
641
+ }
642
+ if (scan.max !== paramLen) {
643
+ throw new Error(
644
+ `CRITICAL: Parameter mismatch - SQL max placeholder is $${scan.max} but ${paramLen} params provided. This will cause SQL execution to fail. SQL: ${sqlPreview(sql)}`
645
+ );
646
+ }
647
+ assertNoGaps(scan, 1, scan.max, sql);
648
+ }
649
+ function needsQuoting(id) {
650
+ if (!isNonEmptyString(id)) return true;
651
+ const isKeyword = SQL_KEYWORDS.has(id.toLowerCase());
652
+ if (isKeyword) return true;
653
+ const isValidIdentifier = REGEX_CACHE.VALID_IDENTIFIER.test(id);
654
+ return !isValidIdentifier;
655
+ }
656
+ function validateParamConsistencyFragment(sql, params) {
657
+ const paramLen = params.length;
658
+ const scan = scanDollarPlaceholders(sql, paramLen);
659
+ if (scan.max === 0) return;
660
+ if (scan.max > paramLen) {
661
+ throw new Error(
662
+ `CRITICAL: Parameter mismatch - SQL references $${scan.max} but only ${paramLen} params provided. SQL: ${sqlPreview(sql)}`
663
+ );
664
+ }
665
+ assertNoGaps(scan, scan.min, scan.max, sql);
666
+ }
667
+ function assertOrThrow(condition, message) {
668
+ if (!condition) throw new Error(message);
669
+ }
670
+ function dialectPlaceholderPrefix(dialect) {
671
+ return dialect === "sqlite" ? "?" : "$";
672
+ }
673
+ function parseSqlitePlaceholderIndices(sql) {
674
+ const re = /\?(?:(\d+))?/g;
675
+ const indices = [];
676
+ let anonCount = 0;
677
+ let sawNumbered = false;
678
+ let sawAnonymous = false;
679
+ for (const m of sql.matchAll(re)) {
680
+ const n = m[1];
681
+ if (n) {
682
+ sawNumbered = true;
683
+ indices.push(parseInt(n, 10));
684
+ } else {
685
+ sawAnonymous = true;
686
+ anonCount += 1;
687
+ indices.push(anonCount);
688
+ }
689
+ }
690
+ return { indices, sawNumbered, sawAnonymous };
691
+ }
692
+ function parseDollarPlaceholderIndices(sql) {
693
+ const re = /\$(\d+)/g;
694
+ const indices = [];
695
+ for (const m of sql.matchAll(re)) indices.push(parseInt(m[1], 10));
696
+ return indices;
697
+ }
698
+ function getPlaceholderIndices(sql, dialect) {
699
+ if (dialect === "sqlite") return parseSqlitePlaceholderIndices(sql);
700
+ return {
701
+ indices: parseDollarPlaceholderIndices(sql),
702
+ sawNumbered: false,
703
+ sawAnonymous: false
704
+ };
705
+ }
706
+ function maxIndex(indices) {
707
+ return indices.length > 0 ? Math.max(...indices) : 0;
708
+ }
709
+ function ensureNoMixedSqlitePlaceholders(sawNumbered, sawAnonymous) {
710
+ assertOrThrow(
711
+ !(sawNumbered && sawAnonymous),
712
+ `CRITICAL: Mixed sqlite placeholders ('?' and '?NNN') are not supported.`
713
+ );
714
+ }
715
+ function ensurePlaceholderMaxMatchesMappingsLength(max, mappingsLength, dialect) {
716
+ assertOrThrow(
717
+ max === mappingsLength,
718
+ `CRITICAL: SQL placeholder max mismatch - max is ${dialectPlaceholderPrefix(dialect)}${max}, but mappings length is ${mappingsLength}.`
719
+ );
720
+ }
721
+ function ensureSequentialPlaceholders(placeholders, max, dialect) {
722
+ const prefix = dialectPlaceholderPrefix(dialect);
723
+ for (let i = 1; i <= max; i++) {
724
+ assertOrThrow(
725
+ placeholders.has(i),
726
+ `CRITICAL: Missing SQL placeholder ${prefix}${i} - placeholders must be sequential 1..${max}.`
727
+ );
728
+ }
729
+ }
730
+ function validateMappingIndex(mapping, max) {
731
+ assertOrThrow(
732
+ Number.isInteger(mapping.index) && mapping.index >= 1 && mapping.index <= max,
733
+ `CRITICAL: ParamMapping index ${mapping.index} out of range 1..${max}.`
734
+ );
735
+ }
736
+ function ensureUniqueMappingIndex(mappingIndices, index, dialect) {
737
+ assertOrThrow(
738
+ !mappingIndices.has(index),
739
+ `CRITICAL: Duplicate ParamMapping index ${index} - each placeholder index must map to exactly one ParamMap.`
740
+ );
741
+ mappingIndices.add(index);
742
+ }
743
+ function ensureMappingIndexExistsInSql(placeholders, index) {
744
+ assertOrThrow(
745
+ placeholders.has(index),
746
+ `CRITICAL: ParamMapping index ${index} not found in SQL placeholders.`
747
+ );
748
+ }
749
+ function validateMappingValueShape(mapping) {
750
+ assertOrThrow(
751
+ !(mapping.dynamicName !== void 0 && mapping.value !== void 0),
752
+ `CRITICAL: ParamMap ${mapping.index} has both dynamicName and value`
753
+ );
754
+ assertOrThrow(
755
+ !(mapping.dynamicName === void 0 && mapping.value === void 0),
756
+ `CRITICAL: ParamMap ${mapping.index} has neither dynamicName nor value`
757
+ );
758
+ }
759
+ function ensureMappingsCoverAllIndices(mappingIndices, max, dialect) {
760
+ const prefix = dialectPlaceholderPrefix(dialect);
761
+ for (let i = 1; i <= max; i++) {
762
+ assertOrThrow(
763
+ mappingIndices.has(i),
764
+ `CRITICAL: Missing ParamMap for placeholder ${prefix}${i} - mappings must cover 1..${max} with no gaps.`
765
+ );
766
+ }
767
+ }
768
+ function validateMappingsAgainstPlaceholders(mappings, placeholders, max, dialect) {
769
+ const mappingIndices = /* @__PURE__ */ new Set();
770
+ for (const mapping of mappings) {
771
+ validateMappingIndex(mapping, max);
772
+ ensureUniqueMappingIndex(mappingIndices, mapping.index);
773
+ ensureMappingIndexExistsInSql(placeholders, mapping.index);
774
+ validateMappingValueShape(mapping);
775
+ }
776
+ ensureMappingsCoverAllIndices(mappingIndices, max, dialect);
777
+ }
778
+ function validateSqlPositions(sql, mappings, dialect) {
779
+ const { indices, sawNumbered, sawAnonymous } = getPlaceholderIndices(
780
+ sql,
781
+ dialect
782
+ );
783
+ if (dialect === "sqlite") {
784
+ ensureNoMixedSqlitePlaceholders(sawNumbered, sawAnonymous);
785
+ }
786
+ const placeholders = new Set(indices);
787
+ if (placeholders.size === 0 && mappings.length === 0) return;
788
+ const max = maxIndex(indices);
789
+ ensurePlaceholderMaxMatchesMappingsLength(max, mappings.length, dialect);
790
+ ensureSequentialPlaceholders(placeholders, max, dialect);
791
+ validateMappingsAgainstPlaceholders(mappings, placeholders, max, dialect);
792
+ }
793
+
794
+ // src/builder/shared/sql-utils.ts
795
+ var NUL = String.fromCharCode(0);
796
+ function containsControlChars(s) {
797
+ return s.includes(NUL) || s.includes("\n") || s.includes("\r");
798
+ }
799
+ function containsUnsafeSqlFragmentChars(s) {
800
+ return containsControlChars(s) || s.includes(";");
801
+ }
802
+ function quote(id) {
803
+ if (isEmptyString(id)) {
804
+ throw new Error("quote: identifier is required and cannot be empty");
805
+ }
806
+ if (containsControlChars(id)) {
807
+ throw new Error(
808
+ `quote: identifier contains invalid characters: ${JSON.stringify(id)}`
809
+ );
810
+ }
811
+ if (needsQuoting(id)) {
812
+ return `"${id.replace(/"/g, '""')}"`;
813
+ }
814
+ return id;
815
+ }
816
+ function col(alias, field) {
817
+ if (isEmptyString(alias)) {
818
+ throw new Error("col: alias is required and cannot be empty");
819
+ }
820
+ if (isEmptyString(field)) {
821
+ throw new Error("col: field is required and cannot be empty");
822
+ }
823
+ return `${alias}.${quote(field)}`;
824
+ }
825
+ function sqlStringLiteral(value) {
826
+ if (containsControlChars(value)) {
827
+ throw new Error("sqlStringLiteral: value contains invalid characters");
828
+ }
829
+ return `'${value.replace(/'/g, "''")}'`;
830
+ }
831
+ function buildTableReference(schemaName, tableName, dialect) {
832
+ if (isEmptyString(tableName)) {
833
+ throw new Error(
834
+ "buildTableReference: tableName is required and cannot be empty"
835
+ );
836
+ }
837
+ if (containsControlChars(tableName)) {
838
+ throw new Error(
839
+ "buildTableReference: tableName contains invalid characters"
840
+ );
841
+ }
842
+ const d = dialect != null ? dialect : "postgres";
843
+ if (d === "sqlite") {
844
+ return quote(tableName);
845
+ }
846
+ if (isEmptyString(schemaName)) {
847
+ throw new Error(
848
+ "buildTableReference: schemaName is required and cannot be empty"
849
+ );
850
+ }
851
+ if (containsControlChars(schemaName)) {
852
+ throw new Error(
853
+ "buildTableReference: schemaName contains invalid characters"
854
+ );
855
+ }
856
+ const safeSchema = schemaName.replace(/"/g, '""');
857
+ const safeTable = tableName.replace(/"/g, '""');
858
+ return `"${safeSchema}"."${safeTable}"`;
859
+ }
860
+ function assertSafeAlias(alias) {
861
+ const a = String(alias);
862
+ if (a.trim().length === 0) {
863
+ throw new Error("alias is required and cannot be empty");
864
+ }
865
+ if (containsUnsafeSqlFragmentChars(a)) {
866
+ throw new Error(`alias contains unsafe characters: ${JSON.stringify(a)}`);
867
+ }
868
+ if (!/^[A-Za-z_]\w*$/.test(a)) {
869
+ throw new Error(
870
+ `alias must be a simple identifier, got: ${JSON.stringify(a)}`
871
+ );
872
+ }
873
+ }
874
+ function assertSafeTableRef(tableRef) {
875
+ const t = String(tableRef);
876
+ if (t.trim().length === 0) {
877
+ throw new Error("tableName/tableRef is required and cannot be empty");
878
+ }
879
+ if (containsUnsafeSqlFragmentChars(t)) {
880
+ throw new Error(
881
+ `tableName/tableRef contains unsafe characters: ${JSON.stringify(t)}`
882
+ );
883
+ }
884
+ }
885
+ function normalizeKeyList(input) {
886
+ if (!isNotNullish(input)) return [];
887
+ if (Array.isArray(input)) {
888
+ const out = [];
889
+ for (const v of input) {
890
+ const s2 = String(v).trim();
891
+ if (s2.length > 0) out.push(s2);
892
+ }
893
+ return out;
894
+ }
895
+ if (typeof input === "string") {
896
+ const raw = input.trim();
897
+ if (raw.length === 0) return [];
898
+ if (raw.includes(",")) {
899
+ return raw.split(",").map((s2) => s2.trim()).filter((s2) => s2.length > 0);
900
+ }
901
+ return [raw];
902
+ }
903
+ const s = String(input).trim();
904
+ return s.length > 0 ? [s] : [];
905
+ }
906
+
907
+ // src/builder/joins.ts
908
+ function isRelationField(fieldName, model) {
909
+ return getRelationFieldSet(model).has(fieldName);
910
+ }
911
+ function isValidRelationField(field) {
912
+ if (!isNotNullish(field)) return false;
913
+ if (!field.isRelation) return false;
914
+ if (!isNotNullish(field.relatedModel) || field.relatedModel.trim().length === 0)
915
+ return false;
916
+ const fk = normalizeKeyList(field.foreignKey);
917
+ if (fk.length === 0) return false;
918
+ const refsRaw = field.references;
919
+ const refs = normalizeKeyList(refsRaw);
920
+ if (refs.length === 0) return false;
921
+ if (refs.length !== fk.length) return false;
922
+ return true;
923
+ }
924
+ function getReferenceFieldNames(field, foreignKeyCount) {
925
+ const refsRaw = field.references;
926
+ const refs = normalizeKeyList(refsRaw);
927
+ if (refs.length === 0) {
928
+ if (foreignKeyCount === 1) return [SPECIAL_FIELDS.ID];
929
+ return [];
930
+ }
931
+ if (refs.length !== foreignKeyCount) return [];
932
+ return refs;
933
+ }
934
+ function joinCondition(field, parentAlias, childAlias) {
935
+ const fkFields = normalizeKeyList(field.foreignKey);
936
+ if (fkFields.length === 0) {
937
+ throw createError(
938
+ `Relation '${field.name}' is missing foreignKey. This indicates a schema parsing error. Relations must specify fields/references.`,
939
+ { field: field.name }
940
+ );
941
+ }
942
+ const refFields = getReferenceFieldNames(field, fkFields.length);
943
+ if (refFields.length !== fkFields.length) {
944
+ throw createError(
945
+ `Relation '${field.name}' is missing references (or references count does not match foreignKey count). This is required to support non-id and composite keys.`,
946
+ { field: field.name }
947
+ );
948
+ }
949
+ const parts = [];
950
+ for (let i = 0; i < fkFields.length; i++) {
951
+ const fk = fkFields[i];
952
+ const ref = refFields[i];
953
+ const left = field.isForeignKeyLocal ? `${childAlias}.${quote(ref)}` : `${childAlias}.${quote(fk)}`;
954
+ const right = field.isForeignKeyLocal ? `${parentAlias}.${quote(fk)}` : `${parentAlias}.${quote(ref)}`;
955
+ parts.push(`${left} = ${right}`);
956
+ }
957
+ return parts.length === 1 ? parts[0] : `(${parts.join(" AND ")})`;
958
+ }
959
+ function getModelByName(schemas, name) {
960
+ return schemas.find((m) => m.name === name);
961
+ }
962
+ function buildNotComposite(expr, val, params, dialect, buildOp, separator) {
963
+ const entries = Object.entries(val).filter(
964
+ ([k, v]) => k !== "mode" && v !== void 0
965
+ );
966
+ if (entries.length === 0) return "";
967
+ const clauses = [];
968
+ for (const [subOp, subVal] of entries) {
969
+ const sub = buildOp(expr, subOp, subVal, params, dialect);
970
+ if (sub && sub.trim().length > 0) clauses.push(`(${sub})`);
971
+ }
972
+ if (clauses.length === 0) return "";
973
+ if (clauses.length === 1) return `${SQL_TEMPLATES.NOT} ${clauses[0]}`;
974
+ return `${SQL_TEMPLATES.NOT} (${clauses.join(separator)})`;
975
+ }
976
+ function buildScalarOperator(expr, op, val, params, mode, fieldType, dialect) {
977
+ if (val === void 0) return "";
978
+ if (val === null) {
979
+ return handleNullValue(expr, op);
980
+ }
981
+ if (op === Ops.NOT && isPlainObject(val)) {
982
+ return handleNotOperator(expr, val, params, mode, fieldType, dialect);
983
+ }
984
+ if (op === Ops.NOT) {
985
+ const placeholder = params.addAuto(val);
986
+ return `${expr} <> ${placeholder}`;
987
+ }
988
+ if (op === Ops.EQUALS && mode === Modes.INSENSITIVE && isNotNullish(dialect)) {
989
+ const placeholder = params.addAuto(val);
990
+ return caseInsensitiveEquals(expr, placeholder);
991
+ }
992
+ const STRING_LIKE_OPS = /* @__PURE__ */ new Set([
993
+ Ops.CONTAINS,
994
+ Ops.STARTS_WITH,
995
+ Ops.ENDS_WITH
996
+ ]);
997
+ if (STRING_LIKE_OPS.has(op)) {
998
+ if (!isNotNullish(dialect)) {
999
+ throw createError(`Like operators require a SQL dialect`, {
1000
+ operator: op
1001
+ });
1002
+ }
1003
+ return handleLikeOperator(expr, op, val, params, mode, dialect);
1004
+ }
1005
+ if (op === Ops.IN || op === Ops.NOT_IN) {
1006
+ if (!isNotNullish(dialect)) {
1007
+ throw createError(`IN operators require a SQL dialect`, { operator: op });
1008
+ }
1009
+ return handleInOperator(expr, op, val, params, dialect);
1010
+ }
1011
+ return handleComparisonOperator(expr, op, val, params);
1012
+ }
1013
+ function handleNullValue(expr, op) {
1014
+ if (op === Ops.EQUALS) return `${expr} ${SQL_TEMPLATES.IS_NULL}`;
1015
+ if (op === Ops.NOT) return `${expr} ${SQL_TEMPLATES.IS_NOT_NULL}`;
1016
+ throw createError(`Operator '${op}' doesn't support null`, { operator: op });
1017
+ }
1018
+ function normalizeMode(v) {
1019
+ if (v === Modes.INSENSITIVE) return Modes.INSENSITIVE;
1020
+ if (v === Modes.DEFAULT) return Modes.DEFAULT;
1021
+ return void 0;
1022
+ }
1023
+ function handleNotOperator(expr, val, params, outerMode, fieldType, dialect) {
1024
+ const innerMode = normalizeMode(val.mode);
1025
+ const effectiveMode = innerMode != null ? innerMode : outerMode;
1026
+ return buildNotComposite(
1027
+ expr,
1028
+ val,
1029
+ params,
1030
+ dialect,
1031
+ (e, subOp, subVal, p, d) => buildScalarOperator(e, subOp, subVal, p, effectiveMode, fieldType, d),
1032
+ ` ${SQL_TEMPLATES.AND} `
1033
+ );
1034
+ }
1035
+ function buildDynamicLikePattern(op, placeholder, dialect) {
1036
+ if (dialect === "postgres") {
1037
+ switch (op) {
1038
+ case Ops.CONTAINS:
1039
+ return `('%' || ${placeholder} || '%')`;
1040
+ case Ops.STARTS_WITH:
1041
+ return `(${placeholder} || '%')`;
1042
+ case Ops.ENDS_WITH:
1043
+ return `('%' || ${placeholder})`;
1044
+ default:
1045
+ return placeholder;
1046
+ }
1047
+ }
1048
+ switch (op) {
1049
+ case Ops.CONTAINS:
1050
+ return `('%' || ${placeholder} || '%')`;
1051
+ case Ops.STARTS_WITH:
1052
+ return `(${placeholder} || '%')`;
1053
+ case Ops.ENDS_WITH:
1054
+ return `('%' || ${placeholder})`;
1055
+ default:
1056
+ return placeholder;
1057
+ }
1058
+ }
1059
+ function handleLikeOperator(expr, op, val, params, mode, dialect) {
1060
+ if (val === void 0) return "";
1061
+ if (schemaParser.isDynamicParameter(val)) {
1062
+ const placeholder2 = params.addAuto(val);
1063
+ const patternExpr = buildDynamicLikePattern(op, placeholder2, dialect);
1064
+ if (mode === Modes.INSENSITIVE) {
1065
+ return caseInsensitiveLike(expr, patternExpr, dialect);
1066
+ }
1067
+ return `${expr} ${SQL_TEMPLATES.LIKE} ${patternExpr}`;
1068
+ }
1069
+ const placeholder = params.add(Wildcards[op](String(val)));
1070
+ if (mode === Modes.INSENSITIVE) {
1071
+ return caseInsensitiveLike(expr, placeholder, dialect);
1072
+ }
1073
+ return `${expr} ${SQL_TEMPLATES.LIKE} ${placeholder}`;
1074
+ }
1075
+ function handleInOperator(expr, op, val, params, dialect) {
1076
+ if (val === void 0) return "";
1077
+ if (schemaParser.isDynamicParameter(val)) {
1078
+ const placeholder2 = params.addAuto(val);
1079
+ return op === Ops.IN ? inArray(expr, placeholder2, dialect) : notInArray(expr, placeholder2, dialect);
1080
+ }
1081
+ if (!Array.isArray(val)) {
1082
+ throw createError(`IN operators require array value`, {
1083
+ operator: op,
1084
+ value: val
1085
+ });
1086
+ }
1087
+ if (val.length === 0) {
1088
+ return op === Ops.IN ? "0=1" : "1=1";
1089
+ }
1090
+ const paramValue = prepareArrayParam(val, dialect);
1091
+ const placeholder = params.add(paramValue);
1092
+ return op === Ops.IN ? inArray(expr, placeholder, dialect) : notInArray(expr, placeholder, dialect);
1093
+ }
1094
+ function handleComparisonOperator(expr, op, val, params) {
1095
+ if (val === void 0) return "";
1096
+ const COMPARISON_OPS2 = {
1097
+ [Ops.EQUALS]: "=",
1098
+ [Ops.GT]: ">",
1099
+ [Ops.GTE]: ">=",
1100
+ [Ops.LT]: "<",
1101
+ [Ops.LTE]: "<="
1102
+ };
1103
+ const sqlOp = COMPARISON_OPS2[op];
1104
+ if (!sqlOp) {
1105
+ throw createError(`Unsupported scalar operator: ${op}`, { operator: op });
1106
+ }
1107
+ const placeholder = params.addAuto(val);
1108
+ return `${expr} ${sqlOp} ${placeholder}`;
1109
+ }
1110
+ function buildArrayParam(val, params, dialect) {
1111
+ if (schemaParser.isDynamicParameter(val)) {
1112
+ return params.addAuto(val);
1113
+ }
1114
+ if (!Array.isArray(val)) {
1115
+ throw createError(`Array operation requires array value`, { value: val });
1116
+ }
1117
+ const paramValue = prepareArrayParam(val, dialect);
1118
+ return params.add(paramValue);
1119
+ }
1120
+ function buildArrayOperator(expr, op, val, params, fieldType, dialect) {
1121
+ if (val === void 0) return "";
1122
+ if (val === null) {
1123
+ if (op === Ops.EQUALS) return `${expr} ${SQL_TEMPLATES.IS_NULL}`;
1124
+ if (op === Ops.NOT) return `${expr} ${SQL_TEMPLATES.IS_NOT_NULL}`;
1125
+ }
1126
+ const cast = getArrayType(fieldType, dialect);
1127
+ if (op === Ops.EQUALS) {
1128
+ return handleArrayEquals(expr, val, params, cast, dialect);
1129
+ }
1130
+ if (op === Ops.NOT) {
1131
+ return handleArrayNot(expr, val, params, cast, dialect);
1132
+ }
1133
+ switch (op) {
1134
+ case Ops.HAS:
1135
+ return handleArrayHas(expr, val, params, cast, dialect);
1136
+ case Ops.HAS_SOME:
1137
+ return handleArrayHasSome(expr, val, params, cast, dialect);
1138
+ case Ops.HAS_EVERY:
1139
+ return handleArrayHasEvery(expr, val, params, cast, dialect);
1140
+ case Ops.IS_EMPTY:
1141
+ return handleArrayIsEmpty(expr, val, dialect);
1142
+ default:
1143
+ throw createError(`Unknown array operator: ${op}`, { operator: op });
1144
+ }
1145
+ }
1146
+ function handleArrayEquals(expr, val, params, cast, dialect) {
1147
+ if (val === void 0) return "";
1148
+ if (isEmptyArray(val)) {
1149
+ return arrayIsEmpty(expr, dialect);
1150
+ }
1151
+ const placeholder = buildArrayParam(val, params, dialect);
1152
+ return arrayEquals(expr, placeholder, cast, dialect);
1153
+ }
1154
+ function handleArrayNot(expr, val, params, cast, dialect) {
1155
+ if (val === void 0) return "";
1156
+ let target = val;
1157
+ if (isPlainObject(val)) {
1158
+ const entries = Object.entries(val).filter(([, v]) => v !== void 0);
1159
+ if (entries.length === 1 && entries[0][0] === Ops.EQUALS) {
1160
+ target = entries[0][1];
1161
+ } else {
1162
+ throw createError(`Array NOT only supports { equals: ... } shape`, {
1163
+ operator: Ops.NOT,
1164
+ value: val
1165
+ });
1166
+ }
1167
+ }
1168
+ if (target === null) {
1169
+ return `${expr} ${SQL_TEMPLATES.IS_NOT_NULL}`;
1170
+ }
1171
+ if (isEmptyArray(target)) {
1172
+ return arrayIsNotEmpty(expr, dialect);
1173
+ }
1174
+ const placeholder = buildArrayParam(target, params, dialect);
1175
+ return `${SQL_TEMPLATES.NOT} (${arrayEquals(expr, placeholder, cast, dialect)})`;
1176
+ }
1177
+ function handleArrayHas(expr, val, params, cast, dialect) {
1178
+ if (val === void 0) return "";
1179
+ if (val === null) {
1180
+ throw createError(`has requires scalar value`, {
1181
+ operator: Ops.HAS,
1182
+ value: val
1183
+ });
1184
+ }
1185
+ if (!schemaParser.isDynamicParameter(val) && Array.isArray(val)) {
1186
+ throw createError(`has requires scalar value (single element), not array`, {
1187
+ operator: Ops.HAS,
1188
+ value: val
1189
+ });
1190
+ }
1191
+ if (isPlainObject(val)) {
1192
+ throw createError(`has requires scalar value`, {
1193
+ operator: Ops.HAS,
1194
+ value: val
1195
+ });
1196
+ }
1197
+ const placeholder = params.addAuto(val);
1198
+ return arrayContains(expr, placeholder, cast, dialect);
1199
+ }
1200
+ function handleArrayHasSome(expr, val, params, cast, dialect) {
1201
+ if (val === void 0) return "";
1202
+ if (schemaParser.isDynamicParameter(val)) {
1203
+ const placeholder2 = params.addAuto(val);
1204
+ return arrayOverlaps(expr, placeholder2, cast, dialect);
1205
+ }
1206
+ if (!Array.isArray(val)) {
1207
+ throw createError(`hasSome requires array value`, {
1208
+ operator: Ops.HAS_SOME,
1209
+ value: val
1210
+ });
1211
+ }
1212
+ if (val.length > LIMITS.MAX_ARRAY_SIZE) {
1213
+ throw createError(
1214
+ `Array too large (${val.length} elements, max ${LIMITS.MAX_ARRAY_SIZE})`,
1215
+ { operator: Ops.HAS_SOME, value: `[${val.length} items]` }
1216
+ );
1217
+ }
1218
+ if (val.length === 0) return "0=1";
1219
+ const paramValue = prepareArrayParam(val, dialect);
1220
+ const placeholder = params.add(paramValue);
1221
+ return arrayOverlaps(expr, placeholder, cast, dialect);
1222
+ }
1223
+ function handleArrayHasEvery(expr, val, params, cast, dialect) {
1224
+ if (val === void 0) return "";
1225
+ const placeholder = buildArrayParam(val, params, dialect);
1226
+ return arrayContainsAll(expr, placeholder, cast, dialect);
1227
+ }
1228
+ function handleArrayIsEmpty(expr, val, dialect) {
1229
+ if (typeof val !== "boolean") {
1230
+ throw createError(`isEmpty requires boolean value`, {
1231
+ operator: Ops.IS_EMPTY,
1232
+ value: val
1233
+ });
1234
+ }
1235
+ return val === true ? arrayIsEmpty(expr, dialect) : arrayIsNotEmpty(expr, dialect);
1236
+ }
1237
+
1238
+ // src/builder/where/operators-json.ts
1239
+ var SAFE_JSON_PATH_SEGMENT = /^[a-zA-Z_]\w*$/;
1240
+ function validateJsonPathSegments(segments) {
1241
+ for (const segment of segments) {
1242
+ if (typeof segment !== "string") {
1243
+ throw createError("JSON path segments must be strings", {
1244
+ operator: Ops.PATH,
1245
+ value: segment
1246
+ });
1247
+ }
1248
+ if (!SAFE_JSON_PATH_SEGMENT.test(segment)) {
1249
+ throw createError(
1250
+ `Invalid JSON path segment: '${segment}'. Must be alphanumeric with underscores, starting with letter or underscore.`,
1251
+ { operator: Ops.PATH, value: segment }
1252
+ );
1253
+ }
1254
+ }
1255
+ }
1256
+ function buildJsonOperator(expr, op, val, params, dialect) {
1257
+ if (val === void 0) return "";
1258
+ if (op === Ops.PATH && isPlainObject(val) && "path" in val) {
1259
+ return handleJsonPath(expr, val, params, dialect);
1260
+ }
1261
+ const jsonWildcards = {
1262
+ [Ops.STRING_CONTAINS]: (v) => `%${v}%`,
1263
+ [Ops.STRING_STARTS_WITH]: (v) => `${v}%`,
1264
+ [Ops.STRING_ENDS_WITH]: (v) => `%${v}`
1265
+ };
1266
+ if (op in jsonWildcards) {
1267
+ return handleJsonWildcard(expr, op, val, params, jsonWildcards, dialect);
1268
+ }
1269
+ throw createError(`Unsupported JSON operator: ${op}`, { operator: op });
1270
+ }
1271
+ function handleJsonPath(expr, val, params, dialect) {
1272
+ const v = val;
1273
+ if (!Array.isArray(v.path)) {
1274
+ throw createError("JSON path must be an array", { operator: Ops.PATH });
1275
+ }
1276
+ if (v.path.length === 0) {
1277
+ throw createError("JSON path cannot be empty", { operator: Ops.PATH });
1278
+ }
1279
+ validateJsonPathSegments(v.path);
1280
+ const pathExpr = dialect === "sqlite" ? params.add(`$.${v.path.join(".")}`) : params.add(v.path);
1281
+ const rawOps = [
1282
+ ["=", v.equals],
1283
+ [">", v.gt],
1284
+ [">=", v.gte],
1285
+ ["<", v.lt],
1286
+ ["<=", v.lte]
1287
+ ];
1288
+ const ops = rawOps.filter(
1289
+ ([, value]) => value !== void 0
1290
+ );
1291
+ if (ops.length === 0) {
1292
+ throw createError("JSON path query missing comparison operator", {
1293
+ operator: Ops.PATH
1294
+ });
1295
+ }
1296
+ const parts = [];
1297
+ for (const [sqlOp, value] of ops) {
1298
+ if (value === null) {
1299
+ const base2 = jsonExtractText(expr, pathExpr, dialect);
1300
+ parts.push(`${base2} ${SQL_TEMPLATES.IS_NULL}`);
1301
+ continue;
1302
+ }
1303
+ const valPh = params.add(value);
1304
+ const base = typeof value === "number" ? jsonExtractNumeric(expr, pathExpr, dialect) : jsonExtractText(expr, pathExpr, dialect);
1305
+ parts.push(`${base} ${sqlOp} ${valPh}`);
1306
+ }
1307
+ return parts.length === 1 ? parts[0] : `(${parts.join(" AND ")})`;
1308
+ }
1309
+ function handleJsonWildcard(expr, op, val, params, wildcards, dialect) {
1310
+ if (!isNotNullish(val)) {
1311
+ throw createError(`JSON string operator requires non-null value`, {
1312
+ operator: op,
1313
+ value: val
1314
+ });
1315
+ }
1316
+ if (isPlainObject(val) || Array.isArray(val)) {
1317
+ throw createError(`JSON string operator requires scalar value`, {
1318
+ operator: op,
1319
+ value: val
1320
+ });
1321
+ }
1322
+ const strVal = String(val);
1323
+ if (strVal.length > LIMITS.MAX_STRING_LENGTH) {
1324
+ throw createError(
1325
+ `String too long (${strVal.length} chars, max ${LIMITS.MAX_STRING_LENGTH})`,
1326
+ { operator: op }
1327
+ );
1328
+ }
1329
+ const placeholder = params.add(wildcards[op](strVal));
1330
+ const jsonText = jsonToText(expr, dialect);
1331
+ return caseInsensitiveLike(jsonText, placeholder, dialect);
1332
+ }
1333
+
1334
+ // src/builder/where/relations.ts
1335
+ var NO_JOINS = Object.freeze([]);
1336
+ function isListRelation(fieldType) {
1337
+ return typeof fieldType === "string" && fieldType.endsWith("[]");
1338
+ }
1339
+ function buildToOneNullCheck(field, parentAlias, relTable, relAlias, join2, wantNull) {
1340
+ const isLocal = field.isForeignKeyLocal === true;
1341
+ const fkFields = normalizeKeyList(field.foreignKey);
1342
+ if (isLocal) {
1343
+ if (fkFields.length === 0) {
1344
+ throw createError(`Relation '${field.name}' is missing foreignKey`, {
1345
+ field: field.name
1346
+ });
1347
+ }
1348
+ const parts = fkFields.map((fk) => {
1349
+ const safe = fk.replace(/"/g, '""');
1350
+ const expr = `${parentAlias}."${safe}"`;
1351
+ return wantNull ? `${expr} ${SQL_TEMPLATES.IS_NULL}` : `${expr} ${SQL_TEMPLATES.IS_NOT_NULL}`;
1352
+ });
1353
+ if (parts.length === 1) return parts[0];
1354
+ return wantNull ? `(${parts.join(" OR ")})` : `(${parts.join(" AND ")})`;
1355
+ }
1356
+ const existsSql = `EXISTS (${SQL_TEMPLATES.SELECT} 1 ${SQL_TEMPLATES.FROM} ${relTable} ${relAlias} ${SQL_TEMPLATES.WHERE} ${join2})`;
1357
+ return wantNull ? `${SQL_TEMPLATES.NOT} ${existsSql}` : existsSql;
1358
+ }
1359
+ function buildToOneExistsMatch(relTable, relAlias, join2, sub) {
1360
+ const joins = sub.joins.length > 0 ? ` ${sub.joins.join(" ")}` : "";
1361
+ return `EXISTS (${SQL_TEMPLATES.SELECT} 1 ${SQL_TEMPLATES.FROM} ${relTable} ${relAlias}${joins} ${SQL_TEMPLATES.WHERE} ${join2} ${SQL_TEMPLATES.AND} ${sub.clause})`;
1362
+ }
1363
+ function buildToOneNotExistsMatch(relTable, relAlias, join2, sub) {
1364
+ const joins = sub.joins.length > 0 ? ` ${sub.joins.join(" ")}` : "";
1365
+ return `${SQL_TEMPLATES.NOT} EXISTS (${SQL_TEMPLATES.SELECT} 1 ${SQL_TEMPLATES.FROM} ${relTable} ${relAlias}${joins} ${SQL_TEMPLATES.WHERE} ${join2} ${SQL_TEMPLATES.AND} ${sub.clause})`;
1366
+ }
1367
+ function buildListRelationFilters(args) {
1368
+ const {
1369
+ fieldName,
1370
+ value,
1371
+ ctx,
1372
+ whereBuilder,
1373
+ relModel,
1374
+ relTable,
1375
+ relAlias,
1376
+ join: join2
1377
+ } = args;
1378
+ const noneValue = value[RelationFilters.NONE];
1379
+ if (noneValue !== void 0 && noneValue !== null) {
1380
+ const sub = whereBuilder.build(noneValue, __spreadProps(__spreadValues({}, ctx), {
1381
+ alias: relAlias,
1382
+ model: relModel,
1383
+ path: [...ctx.path, fieldName, RelationFilters.NONE],
1384
+ isSubquery: true,
1385
+ depth: ctx.depth + 1
1386
+ }));
1387
+ const isEmptyFilter = isPlainObject(noneValue) && Object.keys(noneValue).length === 0;
1388
+ const canOptimize = !ctx.isSubquery && isEmptyFilter && sub.clause === DEFAULT_WHERE_CLAUSE && sub.joins.length === 0;
1389
+ if (canOptimize) {
1390
+ const checkField = relModel.fields.find(
1391
+ (f) => !f.isRelation && f.isRequired && f.name !== "id"
1392
+ ) || relModel.fields.find((f) => !f.isRelation && f.name === "id");
1393
+ if (checkField) {
1394
+ const leftJoinSql = `LEFT JOIN ${relTable} ${relAlias} ON ${join2}`;
1395
+ const whereClause = `${relAlias}.${quote(checkField.name)} IS NULL`;
1396
+ return Object.freeze({
1397
+ clause: whereClause,
1398
+ joins: [leftJoinSql]
1399
+ });
1400
+ }
1401
+ }
1402
+ }
1403
+ const filters = [
1404
+ {
1405
+ key: RelationFilters.SOME,
1406
+ wrap: (c, j) => `EXISTS (${SQL_TEMPLATES.SELECT} 1 ${SQL_TEMPLATES.FROM} ${relTable} ${relAlias}${j} ${SQL_TEMPLATES.WHERE} ${join2} ${SQL_TEMPLATES.AND} ${c})`
1407
+ },
1408
+ {
1409
+ key: RelationFilters.EVERY,
1410
+ wrap: (c, j) => `${SQL_TEMPLATES.NOT} EXISTS (${SQL_TEMPLATES.SELECT} 1 ${SQL_TEMPLATES.FROM} ${relTable} ${relAlias}${j} ${SQL_TEMPLATES.WHERE} ${join2} ${SQL_TEMPLATES.AND} ${SQL_TEMPLATES.NOT} (${c}))`
1411
+ },
1412
+ {
1413
+ key: RelationFilters.NONE,
1414
+ wrap: (c, j) => {
1415
+ const condition = c === DEFAULT_WHERE_CLAUSE ? "" : ` ${SQL_TEMPLATES.AND} ${c}`;
1416
+ return `${SQL_TEMPLATES.NOT} EXISTS (${SQL_TEMPLATES.SELECT} 1 ${SQL_TEMPLATES.FROM} ${relTable} ${relAlias}${j} ${SQL_TEMPLATES.WHERE} ${join2}${condition})`;
1417
+ }
1418
+ }
1419
+ ];
1420
+ const clauses = [];
1421
+ for (const { key, wrap } of filters) {
1422
+ const raw = value[key];
1423
+ if (raw === void 0 || raw === null) continue;
1424
+ const sub = whereBuilder.build(raw, __spreadProps(__spreadValues({}, ctx), {
1425
+ alias: relAlias,
1426
+ model: relModel,
1427
+ path: [...ctx.path, fieldName, key],
1428
+ isSubquery: true,
1429
+ depth: ctx.depth + 1
1430
+ }));
1431
+ const j = sub.joins.length > 0 ? ` ${sub.joins.join(" ")}` : "";
1432
+ clauses.push(wrap(sub.clause, j));
1433
+ }
1434
+ if (clauses.length === 0) {
1435
+ throw createError(
1436
+ `List relation '${fieldName}' requires one of { some, every, none }`,
1437
+ { field: fieldName, path: ctx.path, modelName: ctx.model.name }
1438
+ );
1439
+ }
1440
+ return Object.freeze({
1441
+ clause: clauses.join(SQL_SEPARATORS.CONDITION_AND),
1442
+ joins: NO_JOINS
1443
+ });
1444
+ }
1445
+ function buildToOneRelationFilters(args) {
1446
+ const {
1447
+ fieldName,
1448
+ value,
1449
+ ctx,
1450
+ whereBuilder,
1451
+ field,
1452
+ relModel,
1453
+ relTable,
1454
+ relAlias,
1455
+ join: join2
1456
+ } = args;
1457
+ const hasSomeEveryNone = isNotNullish(value[RelationFilters.SOME]) || isNotNullish(value[RelationFilters.EVERY]) || isNotNullish(value[RelationFilters.NONE]);
1458
+ if (hasSomeEveryNone) {
1459
+ throw createError(
1460
+ `To-one relation '${fieldName}' does not support { some, every, none }; use { is, isNot }`,
1461
+ { field: fieldName, path: ctx.path, modelName: ctx.model.name }
1462
+ );
1463
+ }
1464
+ const hasIs = Object.prototype.hasOwnProperty.call(value, "is");
1465
+ const hasIsNot = Object.prototype.hasOwnProperty.call(value, "isNot");
1466
+ let filterKey;
1467
+ let filterVal;
1468
+ if (hasIs) {
1469
+ filterKey = "is";
1470
+ filterVal = value.is;
1471
+ } else if (hasIsNot) {
1472
+ filterKey = "isNot";
1473
+ filterVal = value.isNot;
1474
+ } else {
1475
+ filterKey = "is";
1476
+ filterVal = value;
1477
+ }
1478
+ if (filterVal === void 0) {
1479
+ return Object.freeze({
1480
+ clause: DEFAULT_WHERE_CLAUSE,
1481
+ joins: NO_JOINS
1482
+ });
1483
+ }
1484
+ if (filterVal === null) {
1485
+ const wantNull = filterKey === "is";
1486
+ const clause2 = buildToOneNullCheck(
1487
+ field,
1488
+ ctx.alias,
1489
+ relTable,
1490
+ relAlias,
1491
+ join2,
1492
+ wantNull
1493
+ );
1494
+ return Object.freeze({
1495
+ clause: clause2,
1496
+ joins: NO_JOINS
1497
+ });
1498
+ }
1499
+ if (!isPlainObject(filterVal)) {
1500
+ throw createError(
1501
+ `Relation '${fieldName}' filter must be an object or null`,
1502
+ {
1503
+ field: fieldName,
1504
+ path: ctx.path,
1505
+ modelName: ctx.model.name,
1506
+ value: filterVal
1507
+ }
1508
+ );
1509
+ }
1510
+ const sub = whereBuilder.build(filterVal, __spreadProps(__spreadValues({}, ctx), {
1511
+ alias: relAlias,
1512
+ model: relModel,
1513
+ path: [...ctx.path, fieldName, filterKey],
1514
+ isSubquery: true,
1515
+ depth: ctx.depth + 1
1516
+ }));
1517
+ const clause = filterKey === "is" ? buildToOneExistsMatch(relTable, relAlias, join2, sub) : buildToOneNotExistsMatch(relTable, relAlias, join2, sub);
1518
+ return Object.freeze({
1519
+ clause,
1520
+ joins: NO_JOINS
1521
+ });
1522
+ }
1523
+ function ensureRelationFilterObject(fieldName, value, ctx) {
1524
+ if (!isPlainObject(value)) {
1525
+ throw createError(`Relation filter '${fieldName}' must be an object`, {
1526
+ path: [...ctx.path, fieldName],
1527
+ field: fieldName,
1528
+ modelName: ctx.model.name,
1529
+ value
1530
+ });
1531
+ }
1532
+ }
1533
+ function buildRelation(fieldName, value, ctx, whereBuilder) {
1534
+ const field = ctx.model.fields.find((f) => f.name === fieldName);
1535
+ if (!isValidRelationField(field)) {
1536
+ throw createError(`Invalid relation '${fieldName}'`, {
1537
+ field: fieldName,
1538
+ path: ctx.path,
1539
+ modelName: ctx.model.name
1540
+ });
1541
+ }
1542
+ const relModel = ctx.schemaModels.find((m) => m.name === field.relatedModel);
1543
+ if (!isNotNullish(relModel)) {
1544
+ throw createError(
1545
+ `Related model '${field.relatedModel}' not found in schema. Available models: ${ctx.schemaModels.map((m) => m.name).join(", ")}`,
1546
+ {
1547
+ field: fieldName,
1548
+ path: ctx.path,
1549
+ modelName: ctx.model.name
1550
+ }
1551
+ );
1552
+ }
1553
+ const relTable = buildTableReference(
1554
+ SQL_TEMPLATES.PUBLIC_SCHEMA,
1555
+ relModel.tableName,
1556
+ ctx.dialect
1557
+ );
1558
+ const relAlias = ctx.aliasGen.next(fieldName);
1559
+ const join2 = joinCondition(field, ctx.alias, relAlias);
1560
+ const args = {
1561
+ fieldName,
1562
+ value,
1563
+ ctx,
1564
+ whereBuilder,
1565
+ field,
1566
+ relModel,
1567
+ relTable,
1568
+ relAlias,
1569
+ join: join2
1570
+ };
1571
+ if (isListRelation(field.type)) return buildListRelationFilters(args);
1572
+ return buildToOneRelationFilters(args);
1573
+ }
1574
+ function buildTopLevelRelation(fieldName, value, ctx, whereBuilder) {
1575
+ ensureRelationFilterObject(fieldName, value, ctx);
1576
+ return buildRelation(fieldName, value, ctx, whereBuilder);
1577
+ }
1578
+ function buildNestedRelation(fieldName, value, ctx, whereBuilder) {
1579
+ return buildTopLevelRelation(fieldName, value, ctx, whereBuilder);
1580
+ }
1581
+
1582
+ // src/builder/shared/validators/field-validators.ts
1583
+ function assertFieldExists(name, model, path) {
1584
+ const field = model.fields.find((f) => f.name === name);
1585
+ if (!isNotNullish(field)) {
1586
+ throw createError(`Field '${name}' does not exist on '${model.name}'`, {
1587
+ field: name,
1588
+ path,
1589
+ modelName: model.name,
1590
+ availableFields: model.fields.map((f) => f.name)
1591
+ });
1592
+ }
1593
+ return field;
1594
+ }
1595
+ function assertValidOperator(fieldName, op, fieldType, path, modelName) {
1596
+ if (!isNotNullish(fieldType)) return;
1597
+ const ARRAY_OPS = /* @__PURE__ */ new Set([
1598
+ Ops.HAS,
1599
+ Ops.HAS_SOME,
1600
+ Ops.HAS_EVERY,
1601
+ Ops.IS_EMPTY
1602
+ ]);
1603
+ const JSON_OPS = /* @__PURE__ */ new Set([
1604
+ Ops.PATH,
1605
+ Ops.STRING_CONTAINS,
1606
+ Ops.STRING_STARTS_WITH,
1607
+ Ops.STRING_ENDS_WITH
1608
+ ]);
1609
+ const isArrayOp = ARRAY_OPS.has(op);
1610
+ const isFieldArray = isArrayType(fieldType);
1611
+ const arrayOpMismatch = isArrayOp && !isFieldArray;
1612
+ if (arrayOpMismatch) {
1613
+ throw createError(`'${op}' requires array field, got '${fieldType}'`, {
1614
+ operator: op,
1615
+ field: fieldName,
1616
+ path,
1617
+ modelName
1618
+ });
1619
+ }
1620
+ const isJsonOp = JSON_OPS.has(op);
1621
+ const isFieldJson = isJsonType(fieldType);
1622
+ const jsonOpMismatch = isJsonOp && !isFieldJson;
1623
+ if (jsonOpMismatch) {
1624
+ throw createError(`'${op}' requires JSON field, got '${fieldType}'`, {
1625
+ operator: op,
1626
+ field: fieldName,
1627
+ path,
1628
+ modelName
1629
+ });
1630
+ }
1631
+ }
1632
+
1633
+ // src/builder/where/builder.ts
1634
+ var WhereBuilder = class {
1635
+ build(where, ctx) {
1636
+ if (!isPlainObject(where)) {
1637
+ throw createError("where must be an object", {
1638
+ path: ctx.path,
1639
+ modelName: ctx.model.name
1640
+ });
1641
+ }
1642
+ return buildWhereInternal(where, ctx, this);
1643
+ }
1644
+ };
1645
+ var MAX_QUERY_DEPTH = 50;
1646
+ var EMPTY_JOINS = Object.freeze([]);
1647
+ var whereBuilderInstance = new WhereBuilder();
1648
+ function freezeResult(clause, joins = EMPTY_JOINS) {
1649
+ return Object.freeze({ clause, joins });
1650
+ }
1651
+ function dedupePreserveOrder(items) {
1652
+ if (items.length <= 1) return Object.freeze([...items]);
1653
+ const seen = /* @__PURE__ */ new Set();
1654
+ const out = [];
1655
+ for (const s of items) {
1656
+ if (!seen.has(s)) {
1657
+ seen.add(s);
1658
+ out.push(s);
1659
+ }
1660
+ }
1661
+ return Object.freeze(out);
1662
+ }
1663
+ function appendResult(result, clauses, allJoins) {
1664
+ if (isValidWhereClause(result.clause)) clauses.push(result.clause);
1665
+ if (isNonEmptyArray(result.joins)) allJoins.push(...result.joins);
1666
+ }
1667
+ function asLogicalOperator(key) {
1668
+ if (key === LogicalOps.AND) return "AND";
1669
+ if (key === LogicalOps.OR) return "OR";
1670
+ if (key === LogicalOps.NOT) return "NOT";
1671
+ return null;
1672
+ }
1673
+ function nextContext(ctx) {
1674
+ return __spreadProps(__spreadValues({}, ctx), { depth: ctx.depth + 1 });
1675
+ }
1676
+ function buildRelationFilter(fieldName, value, ctx, builder) {
1677
+ const ctx2 = nextContext(ctx);
1678
+ if (ctx.isSubquery) {
1679
+ return buildNestedRelation(fieldName, value, ctx2, builder);
1680
+ }
1681
+ return buildTopLevelRelation(fieldName, value, ctx2, builder);
1682
+ }
1683
+ function buildWhereEntry(key, value, ctx, builder) {
1684
+ const op = asLogicalOperator(key);
1685
+ if (op) return buildLogical(op, value, ctx, builder);
1686
+ if (isRelationField(key, ctx.model)) {
1687
+ if (!isPlainObject(value)) {
1688
+ throw createError(`Relation filter '${key}' must be an object`, {
1689
+ path: [...ctx.path, key],
1690
+ field: key,
1691
+ modelName: ctx.model.name,
1692
+ value
1693
+ });
1694
+ }
1695
+ return buildRelationFilter(key, value, ctx, builder);
1696
+ }
1697
+ return buildScalarField(key, value, ctx);
1698
+ }
1699
+ function buildWhereInternal(where, ctx, builder) {
1700
+ if (ctx.depth > MAX_QUERY_DEPTH) {
1701
+ throw createError(
1702
+ `Query nesting too deep (max ${MAX_QUERY_DEPTH} levels). This usually indicates a circular reference.`,
1703
+ { path: ctx.path, modelName: ctx.model.name }
1704
+ );
1705
+ }
1706
+ if (isEmptyWhere(where)) {
1707
+ return freezeResult(DEFAULT_WHERE_CLAUSE, EMPTY_JOINS);
1708
+ }
1709
+ const allJoins = [];
1710
+ const clauses = [];
1711
+ for (const [key, value] of Object.entries(where)) {
1712
+ if (value === void 0) continue;
1713
+ const result = buildWhereEntry(key, value, ctx, builder);
1714
+ appendResult(result, clauses, allJoins);
1715
+ }
1716
+ const finalClause = clauses.length > 0 ? clauses.join(SQL_SEPARATORS.CONDITION_AND) : DEFAULT_WHERE_CLAUSE;
1717
+ return freezeResult(finalClause, dedupePreserveOrder(allJoins));
1718
+ }
1719
+ function normalizeLogicalValue(operator, value, ctx) {
1720
+ if (Array.isArray(value)) {
1721
+ const out = [];
1722
+ for (let i = 0; i < value.length; i++) {
1723
+ const v = value[i];
1724
+ if (v === void 0) continue;
1725
+ if (!isPlainObject(v)) {
1726
+ throw createError(`${operator} entries must be objects`, {
1727
+ path: [...ctx.path, operator, String(i)],
1728
+ modelName: ctx.model.name,
1729
+ value: v
1730
+ });
1731
+ }
1732
+ out.push(v);
1733
+ }
1734
+ return out;
1735
+ }
1736
+ if (isPlainObject(value)) {
1737
+ return [value];
1738
+ }
1739
+ throw createError(`${operator} must be an object or array of objects`, {
1740
+ path: [...ctx.path, operator],
1741
+ modelName: ctx.model.name,
1742
+ value
1743
+ });
1744
+ }
1745
+ function collectLogicalParts(operator, conditions, ctx, builder) {
1746
+ const allJoins = [];
1747
+ const clauses = [];
1748
+ for (let i = 0; i < conditions.length; i++) {
1749
+ const result = builder.build(conditions[i], __spreadProps(__spreadValues({}, ctx), {
1750
+ path: [...ctx.path, operator, String(i)],
1751
+ depth: ctx.depth + 1
1752
+ }));
1753
+ if (isNonEmptyArray(result.joins)) allJoins.push(...result.joins);
1754
+ if (result.clause && result.clause !== DEFAULT_WHERE_CLAUSE) {
1755
+ clauses.push(`(${result.clause})`);
1756
+ }
1757
+ }
1758
+ return {
1759
+ joins: dedupePreserveOrder(allJoins),
1760
+ clauses: Object.freeze(clauses)
1761
+ };
1762
+ }
1763
+ function buildLogicalClause(operator, clauses) {
1764
+ if (clauses.length === 0) return DEFAULT_WHERE_CLAUSE;
1765
+ if (operator === "NOT") {
1766
+ if (clauses.length === 1) return `${SQL_TEMPLATES.NOT} ${clauses[0]}`;
1767
+ return `${SQL_TEMPLATES.NOT} (${clauses.join(SQL_SEPARATORS.CONDITION_AND)})`;
1768
+ }
1769
+ return clauses.join(` ${operator} `);
1770
+ }
1771
+ function buildLogical(operator, value, ctx, builder) {
1772
+ const conditions = normalizeLogicalValue(operator, value, ctx);
1773
+ if (conditions.length === 0) {
1774
+ const clause2 = operator === "OR" ? "0=1" : DEFAULT_WHERE_CLAUSE;
1775
+ return freezeResult(clause2, EMPTY_JOINS);
1776
+ }
1777
+ const { joins, clauses } = collectLogicalParts(
1778
+ operator,
1779
+ conditions,
1780
+ ctx,
1781
+ builder
1782
+ );
1783
+ const clause = buildLogicalClause(operator, clauses);
1784
+ return freezeResult(clause, joins);
1785
+ }
1786
+ function buildScalarField(fieldName, value, ctx) {
1787
+ const field = assertFieldExists(fieldName, ctx.model, ctx.path);
1788
+ const expr = col(ctx.alias, fieldName);
1789
+ if (value === null) {
1790
+ return freezeResult(`${expr} ${SQL_TEMPLATES.IS_NULL}`, EMPTY_JOINS);
1791
+ }
1792
+ if (isPlainObject(value)) {
1793
+ const mode = value.mode;
1794
+ const ops = Object.entries(value).filter(
1795
+ ([k, v]) => k !== "mode" && v !== void 0
1796
+ );
1797
+ if (ops.length === 0) {
1798
+ return freezeResult(DEFAULT_WHERE_CLAUSE, EMPTY_JOINS);
1799
+ }
1800
+ const parts = [];
1801
+ for (const [op, val] of ops) {
1802
+ assertValidOperator(fieldName, op, field.type, ctx.path, ctx.model.name);
1803
+ const clause3 = buildOperator(expr, op, val, ctx, mode, field.type);
1804
+ if (isValidWhereClause(clause3)) parts.push(clause3);
1805
+ }
1806
+ const clause2 = parts.length > 0 ? parts.join(SQL_SEPARATORS.CONDITION_AND) : DEFAULT_WHERE_CLAUSE;
1807
+ return freezeResult(clause2, EMPTY_JOINS);
1808
+ }
1809
+ const clause = buildOperator(
1810
+ expr,
1811
+ Ops.EQUALS,
1812
+ value,
1813
+ ctx,
1814
+ void 0,
1815
+ field.type
1816
+ );
1817
+ return freezeResult(clause || DEFAULT_WHERE_CLAUSE, EMPTY_JOINS);
1818
+ }
1819
+ function buildOperator(expr, op, val, ctx, mode, fieldType) {
1820
+ if (fieldType && isArrayType(fieldType)) {
1821
+ return buildArrayOperator(expr, op, val, ctx.params, fieldType, ctx.dialect);
1822
+ }
1823
+ if (fieldType && isJsonType(fieldType)) {
1824
+ return buildJsonOperator(expr, op, val, ctx.params, ctx.dialect);
1825
+ }
1826
+ return buildScalarOperator(
1827
+ expr,
1828
+ op,
1829
+ val,
1830
+ ctx.params,
1831
+ mode,
1832
+ fieldType,
1833
+ ctx.dialect
1834
+ );
1835
+ }
1836
+
1837
+ // src/builder/shared/alias-generator.ts
1838
+ function toSafeSqlIdentifier(input) {
1839
+ const raw = String(input);
1840
+ const cleaned = raw.replace(/\W/g, "_");
1841
+ const startsOk = /^[a-zA-Z_]/.test(cleaned);
1842
+ const base = startsOk ? cleaned : `_${cleaned}`;
1843
+ const fallback = base.length > 0 ? base : "_t";
1844
+ const lowered = fallback.toLowerCase();
1845
+ return SQL_KEYWORDS.has(lowered) ? `_${lowered}` : lowered;
1846
+ }
1847
+ function createAliasGenerator(maxAliases = 1e4) {
1848
+ let counter = 0;
1849
+ const usedAliases = /* @__PURE__ */ new Set();
1850
+ return {
1851
+ next(baseName) {
1852
+ if (usedAliases.size >= maxAliases) {
1853
+ throw new Error(
1854
+ `Alias generator exceeded maximum of ${maxAliases} aliases. This indicates a query complexity issue or potential infinite loop.`
1855
+ );
1856
+ }
1857
+ const base = toSafeSqlIdentifier(baseName);
1858
+ const suffix = `_${counter}`;
1859
+ const maxLen = 63;
1860
+ const baseMax = Math.max(1, maxLen - suffix.length);
1861
+ const trimmedBase = base.length > baseMax ? base.slice(0, baseMax) : base;
1862
+ const alias = `${trimmedBase}${suffix}`;
1863
+ counter += 1;
1864
+ if (usedAliases.has(alias)) {
1865
+ throw new Error(
1866
+ `CRITICAL: Duplicate alias '${alias}' at counter=${counter}. This indicates a bug in alias generation logic.`
1867
+ );
1868
+ }
1869
+ usedAliases.add(alias);
1870
+ return alias;
1871
+ }
1872
+ };
1873
+ }
1874
+ var MAX_PARAM_INDEX = Number.MAX_SAFE_INTEGER - 1e3;
1875
+ function assertSameLength(params, mappings) {
1876
+ if (params.length !== mappings.length) {
1877
+ throw new Error(
1878
+ `CRITICAL: State corruption - params=${params.length}, mappings=${mappings.length}`
1879
+ );
1880
+ }
1881
+ }
1882
+ function assertValidNextIndex(index) {
1883
+ if (!Number.isInteger(index) || index < 1) {
1884
+ throw new Error(`CRITICAL: Index must be integer >= 1, got ${index}`);
1885
+ }
1886
+ }
1887
+ function assertNextIndexMatches(mappingsLength, nextIndex) {
1888
+ const expected = mappingsLength + 1;
1889
+ if (nextIndex !== expected) {
1890
+ throw new Error(
1891
+ `CRITICAL: Next index mismatch - expected ${expected}, got ${nextIndex}`
1892
+ );
1893
+ }
1894
+ }
1895
+ function assertSequentialIndex(actual, expected) {
1896
+ if (actual !== expected) {
1897
+ throw new Error(
1898
+ `CRITICAL: Indices must be sequential from 1..N. Expected ${expected}, got ${actual}`
1899
+ );
1900
+ }
1901
+ }
1902
+ function assertExactlyOneOfDynamicOrValue(m) {
1903
+ const hasDynamic = typeof m.dynamicName === "string";
1904
+ const hasStatic = m.value !== void 0;
1905
+ if (hasDynamic === hasStatic) {
1906
+ throw new Error(
1907
+ `CRITICAL: ParamMap ${m.index} must have exactly one of dynamicName or value`
1908
+ );
1909
+ }
1910
+ }
1911
+ function normalizeDynamicNameOrThrow(dynamicName, index) {
1912
+ const dn = dynamicName.trim();
1913
+ if (dn.length === 0) {
1914
+ throw new Error(`CRITICAL: dynamicName cannot be empty (index=${index})`);
1915
+ }
1916
+ return dn;
1917
+ }
1918
+ function assertUniqueDynamicName(dn, seen) {
1919
+ if (seen.has(dn)) {
1920
+ throw new Error(`CRITICAL: Duplicate dynamic param name in mappings: ${dn}`);
1921
+ }
1922
+ seen.add(dn);
1923
+ }
1924
+ function validateMappingEntry(m, expectedIndex, seenDynamic) {
1925
+ assertSequentialIndex(m.index, expectedIndex);
1926
+ assertExactlyOneOfDynamicOrValue(m);
1927
+ if (typeof m.dynamicName === "string") {
1928
+ const dn = normalizeDynamicNameOrThrow(m.dynamicName, m.index);
1929
+ assertUniqueDynamicName(dn, seenDynamic);
1930
+ }
1931
+ }
1932
+ function validateMappings(mappings) {
1933
+ const seenDynamic = /* @__PURE__ */ new Set();
1934
+ for (let i = 0; i < mappings.length; i++) {
1935
+ validateMappingEntry(mappings[i], i + 1, seenDynamic);
1936
+ }
1937
+ }
1938
+ function validateState(params, mappings, index) {
1939
+ assertSameLength(params, mappings);
1940
+ assertValidNextIndex(index);
1941
+ if (mappings.length === 0) return;
1942
+ validateMappings(mappings);
1943
+ assertNextIndexMatches(mappings.length, index);
1944
+ }
1945
+ function normalizeValue(value) {
1946
+ if (value instanceof Date) {
1947
+ return value.toISOString();
1948
+ }
1949
+ return value;
1950
+ }
1951
+ function createStoreInternal(startIndex, initialParams = [], initialMappings = []) {
1952
+ let index = startIndex;
1953
+ const params = [...initialParams];
1954
+ const mappings = [...initialMappings];
1955
+ const dynamicNameToIndex = /* @__PURE__ */ new Map();
1956
+ for (const m of initialMappings) {
1957
+ if (typeof m.dynamicName === "string") {
1958
+ dynamicNameToIndex.set(m.dynamicName.trim(), m.index);
1959
+ }
1960
+ }
1961
+ function assertCanAdd() {
1962
+ if (index > MAX_PARAM_INDEX) {
1963
+ throw new Error(
1964
+ `CRITICAL: Cannot add param - would overflow MAX_SAFE_INTEGER. Current index: ${index}`
1965
+ );
1966
+ }
1967
+ }
1968
+ function normalizeDynamicName(dynamicName) {
1969
+ const dn = dynamicName.trim();
1970
+ if (dn.length === 0) {
1971
+ throw new Error("CRITICAL: dynamicName cannot be empty");
1972
+ }
1973
+ return dn;
1974
+ }
1975
+ function format(position) {
1976
+ return `$${position}`;
1977
+ }
1978
+ function addDynamic(dynamicName) {
1979
+ const dn = normalizeDynamicName(dynamicName);
1980
+ const existing = dynamicNameToIndex.get(dn);
1981
+ if (existing !== void 0) {
1982
+ return format(existing);
1983
+ }
1984
+ const position = index;
1985
+ dynamicNameToIndex.set(dn, position);
1986
+ params.push(void 0);
1987
+ mappings.push({ index: position, dynamicName: dn });
1988
+ index++;
1989
+ return format(position);
1990
+ }
1991
+ function addStatic(value) {
1992
+ const position = index;
1993
+ const normalizedValue = normalizeValue(value);
1994
+ params.push(normalizedValue);
1995
+ mappings.push({ index: position, value: normalizedValue });
1996
+ index++;
1997
+ return format(position);
1998
+ }
1999
+ function add(value, dynamicName) {
2000
+ assertCanAdd();
2001
+ return dynamicName === void 0 ? addStatic(value) : addDynamic(dynamicName);
2002
+ }
2003
+ function addAuto(value) {
2004
+ if (schemaParser.isDynamicParameter(value)) {
2005
+ const dynamicName = schemaParser.extractDynamicName(value);
2006
+ return add(void 0, dynamicName);
2007
+ }
2008
+ return add(value);
2009
+ }
2010
+ function snapshot() {
2011
+ return Object.freeze({
2012
+ index,
2013
+ params: Object.freeze([...params]),
2014
+ mappings: Object.freeze([...mappings])
2015
+ });
2016
+ }
2017
+ return {
2018
+ add,
2019
+ addAuto,
2020
+ snapshot,
2021
+ get index() {
2022
+ return index;
2023
+ }
2024
+ };
2025
+ }
2026
+ function createParamStore(startIndex = 1) {
2027
+ if (!Number.isInteger(startIndex) || startIndex < 1) {
2028
+ throw new Error(`Start index must be integer >= 1, got ${startIndex}`);
2029
+ }
2030
+ if (startIndex > MAX_PARAM_INDEX) {
2031
+ throw new Error(
2032
+ `Start index too high (${startIndex}), risk of overflow at MAX_SAFE_INTEGER`
2033
+ );
2034
+ }
2035
+ return createStoreInternal(startIndex);
2036
+ }
2037
+ function createParamStoreFrom(existingParams, existingMappings, nextIndex) {
2038
+ validateState([...existingParams], [...existingMappings], nextIndex);
2039
+ return createStoreInternal(
2040
+ nextIndex,
2041
+ [...existingParams],
2042
+ [...existingMappings]
2043
+ );
2044
+ }
2045
+
2046
+ // src/builder/shared/state.ts
2047
+ function toPublicResult(clause, joins, params) {
2048
+ const snapshot = params.snapshot();
2049
+ return Object.freeze({
2050
+ clause: clause || DEFAULT_WHERE_CLAUSE,
2051
+ joins: Object.freeze([...joins]),
2052
+ params: snapshot.params,
2053
+ paramMappings: snapshot.mappings,
2054
+ nextParamIndex: snapshot.index
2055
+ });
2056
+ }
2057
+
2058
+ // src/builder/where.ts
2059
+ function buildWhereClause(where, options) {
2060
+ var _a, _b, _c, _d, _e;
2061
+ const dialect = options.dialect || getGlobalDialect();
2062
+ const params = (_a = options.params) != null ? _a : createParamStore();
2063
+ const ctx = {
2064
+ alias: options.alias,
2065
+ model: options.model,
2066
+ schemaModels: (_b = options.schemaModels) != null ? _b : [],
2067
+ path: (_c = options.path) != null ? _c : [],
2068
+ isSubquery: (_d = options.isSubquery) != null ? _d : false,
2069
+ aliasGen: (_e = options.aliasGen) != null ? _e : createAliasGenerator(),
2070
+ dialect,
2071
+ params,
2072
+ depth: 0
2073
+ };
2074
+ const result = whereBuilderInstance.build(where, ctx);
2075
+ const publicResult = toPublicResult(result.clause, result.joins, params);
2076
+ if (!options.isSubquery) {
2077
+ const nums = [...publicResult.clause.matchAll(/\$(\d+)/g)].map(
2078
+ (m) => parseInt(m[1], 10)
2079
+ );
2080
+ if (nums.length > 0) {
2081
+ const min = Math.min(...nums);
2082
+ if (min === 1) {
2083
+ validateParamConsistency(publicResult.clause, publicResult.params);
2084
+ } else {
2085
+ validateParamConsistencyFragment(
2086
+ publicResult.clause,
2087
+ publicResult.params
2088
+ );
2089
+ }
2090
+ }
2091
+ }
2092
+ return publicResult;
2093
+ }
2094
+ function normalizeIntLike(name, v, opts = {}) {
2095
+ var _a, _b;
2096
+ if (!isNotNullish(v)) return void 0;
2097
+ if (schemaParser.isDynamicParameter(v)) return v;
2098
+ if (typeof v !== "number" || !Number.isFinite(v) || !Number.isInteger(v)) {
2099
+ throw new Error(`${name} must be an integer`);
2100
+ }
2101
+ const min = (_a = opts.min) != null ? _a : 0;
2102
+ const allowZero = (_b = opts.allowZero) != null ? _b : true;
2103
+ if (!allowZero && v === 0) {
2104
+ throw new Error(`${name} must be > 0`);
2105
+ }
2106
+ if (v < min) {
2107
+ throw new Error(`${name} must be >= ${min}`);
2108
+ }
2109
+ if (typeof opts.max === "number" && v > opts.max) {
2110
+ throw new Error(`${name} must be <= ${opts.max}`);
2111
+ }
2112
+ return v;
2113
+ }
2114
+ function scopeName(scope, dynamicName) {
2115
+ const s = String(scope).trim();
2116
+ const dn = String(dynamicName).trim();
2117
+ if (s.length === 0) return dn;
2118
+ return `${s}:${dn}`;
2119
+ }
2120
+ function addAutoScoped(params, value, scope) {
2121
+ if (schemaParser.isDynamicParameter(value)) {
2122
+ const dn = schemaParser.extractDynamicName(value);
2123
+ return params.add(void 0, scopeName(scope, dn));
2124
+ }
2125
+ return params.add(value);
2126
+ }
2127
+
2128
+ // src/builder/shared/order-by-utils.ts
2129
+ var flipNulls = (v) => {
2130
+ const s = String(v).toLowerCase();
2131
+ if (s === "first") return "last";
2132
+ if (s === "last") return "first";
2133
+ return v;
2134
+ };
2135
+ var flipSortString = (v) => {
2136
+ if (typeof v !== "string") return v;
2137
+ const s = v.toLowerCase();
2138
+ if (s === "asc") return "desc";
2139
+ if (s === "desc") return "asc";
2140
+ return v;
2141
+ };
2142
+ var getNextSort = (sortRaw) => {
2143
+ if (typeof sortRaw !== "string") return sortRaw;
2144
+ const s = sortRaw.toLowerCase();
2145
+ if (s === "asc") return "desc";
2146
+ if (s === "desc") return "asc";
2147
+ return sortRaw;
2148
+ };
2149
+ var flipObjectSort = (obj) => {
2150
+ const sortRaw = obj.sort;
2151
+ const out = __spreadProps(__spreadValues({}, obj), { sort: getNextSort(sortRaw) });
2152
+ const nullsRaw = obj.nulls;
2153
+ if (typeof nullsRaw === "string") {
2154
+ out.nulls = flipNulls(nullsRaw);
2155
+ }
2156
+ return out;
2157
+ };
2158
+ var flipValue = (v) => {
2159
+ if (typeof v === "string") return flipSortString(v);
2160
+ if (isPlainObject(v)) return flipObjectSort(v);
2161
+ return v;
2162
+ };
2163
+ var assertSingleFieldObject = (item) => {
2164
+ if (!isPlainObject(item)) {
2165
+ throw new Error("orderBy array entries must be objects");
2166
+ }
2167
+ const entries = Object.entries(item);
2168
+ if (entries.length !== 1) {
2169
+ throw new Error("orderBy array entries must have exactly one field");
2170
+ }
2171
+ return entries[0];
2172
+ };
2173
+ var flipOrderByArray = (orderBy) => {
2174
+ return orderBy.map((item) => {
2175
+ const [k, v] = assertSingleFieldObject(item);
2176
+ return { [k]: flipValue(v) };
2177
+ });
2178
+ };
2179
+ var flipOrderByObject = (orderBy) => {
2180
+ const out = {};
2181
+ for (const [k, v] of Object.entries(orderBy)) {
2182
+ out[k] = flipValue(v);
2183
+ }
2184
+ return out;
2185
+ };
2186
+ function reverseOrderByInput(orderBy) {
2187
+ if (!isNotNullish(orderBy)) return orderBy;
2188
+ if (Array.isArray(orderBy)) {
2189
+ return flipOrderByArray(orderBy);
2190
+ }
2191
+ if (isPlainObject(orderBy)) {
2192
+ return flipOrderByObject(orderBy);
2193
+ }
2194
+ throw new Error("orderBy must be an object or array of objects");
2195
+ }
2196
+ var normalizePairs = (pairs, parseValue) => {
2197
+ return pairs.map(([field, rawValue]) => {
2198
+ const parsed = parseValue(rawValue, field);
2199
+ return {
2200
+ [field]: parsed.nulls !== void 0 ? { sort: parsed.direction, nulls: parsed.nulls } : parsed.direction
2201
+ };
2202
+ });
2203
+ };
2204
+ function normalizeOrderByInput(orderBy, parseValue) {
2205
+ if (!isNotNullish(orderBy)) return [];
2206
+ if (Array.isArray(orderBy)) {
2207
+ const pairs = orderBy.map(assertSingleFieldObject);
2208
+ return normalizePairs(pairs, parseValue);
2209
+ }
2210
+ if (isPlainObject(orderBy)) {
2211
+ return normalizePairs(Object.entries(orderBy), parseValue);
2212
+ }
2213
+ throw new Error("orderBy must be an object or array of objects");
2214
+ }
2215
+
2216
+ // src/builder/pagination.ts
2217
+ var MAX_LIMIT_OFFSET = 2147483647;
2218
+ function parseDirectionRaw(raw, errorLabel) {
2219
+ const s = String(raw).toLowerCase();
2220
+ if (s === "asc" || s === "desc") return s;
2221
+ throw new Error(`Invalid ${errorLabel}: ${raw}`);
2222
+ }
2223
+ function parseNullsRaw(raw, errorLabel) {
2224
+ if (!isNotNullish(raw)) return void 0;
2225
+ const s = String(raw).toLowerCase();
2226
+ if (s === "first" || s === "last") return s;
2227
+ throw new Error(`Invalid ${errorLabel}: ${raw}`);
2228
+ }
2229
+ function requireOrderByObject(v, errorPrefix) {
2230
+ if (!isPlainObject(v) || !("sort" in v)) {
2231
+ throw new Error(`${errorPrefix} must be 'asc' | 'desc' or { sort, nulls? }`);
2232
+ }
2233
+ return v;
2234
+ }
2235
+ function assertAllowedOrderByKeys(obj, fieldName) {
2236
+ const allowed = /* @__PURE__ */ new Set(["sort", "nulls"]);
2237
+ for (const k of Object.keys(obj)) {
2238
+ if (!allowed.has(k)) {
2239
+ throw new Error(
2240
+ fieldName ? `Unsupported orderBy key '${k}' for field '${fieldName}'` : `Unsupported orderBy key '${k}'`
2241
+ );
2242
+ }
2243
+ }
2244
+ }
2245
+ function parseOrderByValue(v, fieldName) {
2246
+ const errorPrefix = fieldName ? `orderBy for '${fieldName}'` : "orderBy value";
2247
+ if (typeof v === "string") {
2248
+ return { direction: parseDirectionRaw(v, `${errorPrefix} direction`) };
2249
+ }
2250
+ const obj = requireOrderByObject(v, errorPrefix);
2251
+ const direction = parseDirectionRaw(obj.sort, `${errorPrefix}.sort`);
2252
+ const nulls = parseNullsRaw(obj.nulls, `${errorPrefix}.nulls`);
2253
+ assertAllowedOrderByKeys(obj, fieldName);
2254
+ return { direction, nulls };
2255
+ }
2256
+ function normalizeFiniteInteger(name, v) {
2257
+ if (typeof v !== "number" || !Number.isFinite(v) || !Number.isInteger(v)) {
2258
+ throw new Error(`${name} must be an integer`);
2259
+ }
2260
+ return v;
2261
+ }
2262
+ function normalizeNonNegativeInt(name, v) {
2263
+ if (schemaParser.isDynamicParameter(v)) return v;
2264
+ const n = normalizeFiniteInteger(name, v);
2265
+ if (n < 0) {
2266
+ throw new Error(`${name} must be >= 0`);
2267
+ }
2268
+ if (n > MAX_LIMIT_OFFSET) {
2269
+ throw new Error(`${name} must be <= ${MAX_LIMIT_OFFSET}`);
2270
+ }
2271
+ return n;
2272
+ }
2273
+ function hasNonNullishProp(v, key) {
2274
+ return isPlainObject(v) && key in v && isNotNullish(v[key]);
2275
+ }
2276
+ function normalizeIntegerOrDynamic(name, v) {
2277
+ if (schemaParser.isDynamicParameter(v)) return v;
2278
+ return normalizeFiniteInteger(name, v);
2279
+ }
2280
+ function readSkipTake(relArgs) {
2281
+ const hasSkip = hasNonNullishProp(relArgs, "skip");
2282
+ const hasTake = hasNonNullishProp(relArgs, "take");
2283
+ if (!hasSkip && !hasTake) {
2284
+ return {
2285
+ hasSkip: false,
2286
+ hasTake: false,
2287
+ skipVal: void 0,
2288
+ takeVal: void 0
2289
+ };
2290
+ }
2291
+ const obj = relArgs;
2292
+ const skipVal = hasSkip ? normalizeNonNegativeInt("skip", obj.skip) : void 0;
2293
+ const takeVal = hasTake ? normalizeIntegerOrDynamic("take", obj.take) : void 0;
2294
+ return { hasSkip, hasTake, skipVal, takeVal };
2295
+ }
2296
+ function buildOrderByFragment(entries, alias, dialect) {
2297
+ if (entries.length === 0) return "";
2298
+ const out = [];
2299
+ for (const e of entries) {
2300
+ const dir = e.direction.toUpperCase();
2301
+ const c = col(alias, e.field);
2302
+ if (dialect === "postgres") {
2303
+ const nulls = isNotNullish(e.nulls) ? ` NULLS ${e.nulls.toUpperCase()}` : "";
2304
+ out.push(`${c} ${dir}${nulls}`);
2305
+ continue;
2306
+ }
2307
+ if (isNotNullish(e.nulls)) {
2308
+ const isNullExpr = `(${c} IS NULL)`;
2309
+ const nullRankDir = e.nulls === "first" ? "DESC" : "ASC";
2310
+ out.push(`${isNullExpr} ${nullRankDir}`);
2311
+ out.push(`${c} ${dir}`);
2312
+ continue;
2313
+ }
2314
+ out.push(`${c} ${dir}`);
2315
+ }
2316
+ return out.join(SQL_SEPARATORS.ORDER_BY);
2317
+ }
2318
+ function defaultNullsFor(dialect, direction) {
2319
+ if (dialect === "postgres") {
2320
+ return direction === "asc" ? "last" : "first";
2321
+ }
2322
+ return direction === "asc" ? "first" : "last";
2323
+ }
2324
+ function ensureCursorFieldsInOrder(orderEntries, cursorEntries) {
2325
+ const existing = /* @__PURE__ */ new Map();
2326
+ for (const e of orderEntries) existing.set(e.field, e);
2327
+ const out = [...orderEntries];
2328
+ for (const [field] of cursorEntries) {
2329
+ if (!existing.has(field)) {
2330
+ out.push({ field, direction: "asc" });
2331
+ existing.set(field, out[out.length - 1]);
2332
+ }
2333
+ }
2334
+ return out;
2335
+ }
2336
+ function buildCursorFilterParts(cursor, cursorAlias, params) {
2337
+ const entries = Object.entries(cursor);
2338
+ if (entries.length === 0) {
2339
+ throw new Error("cursor must have at least one field");
2340
+ }
2341
+ const placeholdersByField = /* @__PURE__ */ new Map();
2342
+ const parts = [];
2343
+ for (const [field, value] of entries) {
2344
+ const c = `${cursorAlias}.${quote(field)}`;
2345
+ if (value === null) {
2346
+ parts.push(`${c} IS NULL`);
2347
+ continue;
2348
+ }
2349
+ const ph = addAutoScoped(params, value, `cursor.filter.${field}`);
2350
+ placeholdersByField.set(field, ph);
2351
+ parts.push(`${c} = ${ph}`);
2352
+ }
2353
+ return {
2354
+ whereSql: parts.length === 1 ? parts[0] : `(${parts.join(" AND ")})`,
2355
+ placeholdersByField
2356
+ };
2357
+ }
2358
+ function cursorValueExpr(tableName, cursorAlias, cursorWhereSql, field) {
2359
+ return `(SELECT ${cursorAlias}.${quote(field)} ${SQL_TEMPLATES.FROM} ${tableName} ${cursorAlias} ${SQL_TEMPLATES.WHERE} ${cursorWhereSql} ${SQL_TEMPLATES.LIMIT} 1)`;
2360
+ }
2361
+ function buildCursorRowExistsExpr(tableName, cursorAlias, cursorWhereSql) {
2362
+ return `EXISTS (${SQL_TEMPLATES.SELECT} 1 ${SQL_TEMPLATES.FROM} ${tableName} ${cursorAlias} ${SQL_TEMPLATES.WHERE} ${cursorWhereSql} ${SQL_TEMPLATES.LIMIT} 1)`;
2363
+ }
2364
+ function buildCursorEqualityExpr(columnExpr, valueExpr) {
2365
+ return `((${valueExpr} IS NULL AND ${columnExpr} IS NULL) OR (${valueExpr} IS NOT NULL AND ${columnExpr} = ${valueExpr}))`;
2366
+ }
2367
+ function buildCursorInequalityExpr(columnExpr, direction, nulls, valueExpr) {
2368
+ const op = direction === "asc" ? ">" : "<";
2369
+ if (nulls === "first") {
2370
+ return `(CASE WHEN ${valueExpr} IS NULL THEN (${columnExpr} IS NOT NULL) ELSE (${columnExpr} ${op} ${valueExpr}) END)`;
2371
+ }
2372
+ return `(CASE WHEN ${valueExpr} IS NULL THEN 0=1 ELSE ((${columnExpr} ${op} ${valueExpr}) OR (${columnExpr} IS NULL)) END)`;
2373
+ }
2374
+ function buildOuterCursorMatch(cursor, outerAlias, placeholdersByField, params) {
2375
+ const parts = [];
2376
+ for (const [field, value] of Object.entries(cursor)) {
2377
+ const c = col(outerAlias, field);
2378
+ if (value === null) {
2379
+ parts.push(`${c} IS NULL`);
2380
+ continue;
2381
+ }
2382
+ const existing = placeholdersByField.get(field);
2383
+ if (typeof existing === "string" && existing.length > 0) {
2384
+ parts.push(`${c} = ${existing}`);
2385
+ continue;
2386
+ }
2387
+ const ph = addAutoScoped(params, value, `cursor.outerMatch.${field}`);
2388
+ parts.push(`${c} = ${ph}`);
2389
+ }
2390
+ return parts.length === 1 ? parts[0] : `(${parts.join(" AND ")})`;
2391
+ }
2392
+ function buildOrderEntries(orderBy) {
2393
+ const normalized = normalizeOrderByInput(orderBy, parseOrderByValue);
2394
+ const entries = [];
2395
+ for (const item of normalized) {
2396
+ for (const [field, value] of Object.entries(item)) {
2397
+ if (typeof value === "string") {
2398
+ entries.push({ field, direction: value });
2399
+ } else {
2400
+ entries.push({
2401
+ field,
2402
+ direction: value.sort,
2403
+ nulls: value.nulls
2404
+ });
2405
+ }
2406
+ }
2407
+ }
2408
+ return entries;
2409
+ }
2410
+ function buildCursorCondition(cursor, orderBy, tableName, alias, params, dialect) {
2411
+ var _a;
2412
+ const d = dialect != null ? dialect : getGlobalDialect();
2413
+ const cursorEntries = Object.entries(cursor);
2414
+ if (cursorEntries.length === 0) {
2415
+ throw new Error("cursor must have at least one field");
2416
+ }
2417
+ const cursorAlias = "__tp_cursor_src";
2418
+ const { whereSql: cursorWhereSql, placeholdersByField } = buildCursorFilterParts(cursor, cursorAlias, params);
2419
+ let orderEntries = buildOrderEntries(orderBy);
2420
+ if (orderEntries.length === 0) {
2421
+ orderEntries = cursorEntries.map(([field]) => ({
2422
+ field,
2423
+ direction: "asc"
2424
+ }));
2425
+ } else {
2426
+ orderEntries = ensureCursorFieldsInOrder(orderEntries, cursorEntries);
2427
+ }
2428
+ const existsExpr = buildCursorRowExistsExpr(
2429
+ tableName,
2430
+ cursorAlias,
2431
+ cursorWhereSql
2432
+ );
2433
+ const outerCursorMatch = buildOuterCursorMatch(
2434
+ cursor,
2435
+ alias,
2436
+ placeholdersByField,
2437
+ params
2438
+ );
2439
+ const orClauses = [];
2440
+ for (let level = 0; level < orderEntries.length; level++) {
2441
+ const andParts = [];
2442
+ for (let i = 0; i < level; i++) {
2443
+ const e2 = orderEntries[i];
2444
+ const c2 = col(alias, e2.field);
2445
+ const v2 = cursorValueExpr(tableName, cursorAlias, cursorWhereSql, e2.field);
2446
+ andParts.push(buildCursorEqualityExpr(c2, v2));
2447
+ }
2448
+ const e = orderEntries[level];
2449
+ const c = col(alias, e.field);
2450
+ const v = cursorValueExpr(tableName, cursorAlias, cursorWhereSql, e.field);
2451
+ const nulls = (_a = e.nulls) != null ? _a : defaultNullsFor(d, e.direction);
2452
+ andParts.push(buildCursorInequalityExpr(c, e.direction, nulls, v));
2453
+ orClauses.push(`(${andParts.join(SQL_SEPARATORS.CONDITION_AND)})`);
2454
+ }
2455
+ const exclusive = orClauses.join(SQL_SEPARATORS.CONDITION_OR);
2456
+ return `(${existsExpr} ${SQL_SEPARATORS.CONDITION_AND} ((${exclusive})${SQL_SEPARATORS.CONDITION_OR}(${outerCursorMatch})))`;
2457
+ }
2458
+ function buildOrderBy(orderBy, alias, dialect) {
2459
+ const entries = buildOrderEntries(orderBy);
2460
+ if (entries.length === 0) return "";
2461
+ const d = dialect != null ? dialect : getGlobalDialect();
2462
+ return buildOrderByFragment(entries, alias, d);
2463
+ }
2464
+ function buildOrderByClause(args, alias, dialect) {
2465
+ if (!isNotNullish(args.orderBy)) return "";
2466
+ const result = buildOrderBy(args.orderBy, alias, dialect);
2467
+ if (!isNonEmptyString(result)) {
2468
+ throw new Error(
2469
+ "buildOrderByClause: orderBy specified but produced empty result"
2470
+ );
2471
+ }
2472
+ return result;
2473
+ }
2474
+ function normalizeTakeLike(v) {
2475
+ const n = normalizeIntLike("take", v, {
2476
+ min: Number.MIN_SAFE_INTEGER,
2477
+ max: MAX_LIMIT_OFFSET,
2478
+ allowZero: true
2479
+ });
2480
+ if (typeof n === "number") {
2481
+ if (n === 0) return 0;
2482
+ }
2483
+ return n;
2484
+ }
2485
+ function normalizeSkipLike(v) {
2486
+ return normalizeIntLike("skip", v, {
2487
+ min: 0,
2488
+ max: MAX_LIMIT_OFFSET,
2489
+ allowZero: true
2490
+ });
2491
+ }
2492
+ function getPaginationParams(method, args) {
2493
+ if (method === "findMany") {
2494
+ return {
2495
+ take: normalizeTakeLike(args.take),
2496
+ skip: normalizeSkipLike(args.skip),
2497
+ cursor: args.cursor
2498
+ };
2499
+ }
2500
+ if (method === "findFirst") {
2501
+ const skip = normalizeSkipLike(args.skip);
2502
+ return { take: 1, skip: skip != null ? skip : 0 };
2503
+ }
2504
+ if (method === "findUnique") {
2505
+ return { take: 1, skip: 0 };
2506
+ }
2507
+ return {};
2508
+ }
2509
+
2510
+ // src/builder/select/fields.ts
2511
+ function toSelectEntries(select) {
2512
+ const out = [];
2513
+ for (const [k, v] of Object.entries(select)) {
2514
+ if (v !== false && v !== void 0) out.push([k, v]);
2515
+ }
2516
+ return out;
2517
+ }
2518
+ function validateSelectKeys(entries, scalarSet, relationSet) {
2519
+ const unknown = [];
2520
+ for (const [k] of entries) {
2521
+ if (k === "_count") continue;
2522
+ if (!scalarSet.has(k) && !relationSet.has(k)) unknown.push(k);
2523
+ }
2524
+ if (unknown.length > 0) {
2525
+ throw new Error(`Select contains unknown fields: ${unknown.join(", ")}`);
2526
+ }
2527
+ }
2528
+ function analyzeSelectEntries(entries, scalarSet, relationSet) {
2529
+ const scalarSelected = [];
2530
+ let hasRelationSelection = false;
2531
+ let hasCount = false;
2532
+ for (const [k, v] of entries) {
2533
+ if (k === "_count") {
2534
+ hasCount = true;
2535
+ continue;
2536
+ }
2537
+ if (relationSet.has(k)) hasRelationSelection = true;
2538
+ if (scalarSet.has(k) && v === true) scalarSelected.push(k);
2539
+ }
2540
+ return { scalarSelected, hasRelationSelection, hasCount };
2541
+ }
2542
+ function buildDefaultScalarFields(model, alias) {
2543
+ const excludedPrefixes = [
2544
+ SCHEMA_PREFIXES.INTERNAL,
2545
+ SCHEMA_PREFIXES.COMMENT
2546
+ ];
2547
+ const out = [];
2548
+ for (const f of model.fields) {
2549
+ if (f.isRelation) continue;
2550
+ const excluded = excludedPrefixes.some((p) => f.name.startsWith(p));
2551
+ if (!excluded) out.push(col(alias, f.name));
2552
+ }
2553
+ if (!isNonEmptyArray(out)) {
2554
+ throw new Error(`Model ${model.name} has no selectable fields`);
2555
+ }
2556
+ return out;
2557
+ }
2558
+ function buildSelectFields(args, model, alias) {
2559
+ const scalarSet = getScalarFieldSet(model);
2560
+ const relationSet = getRelationFieldSet(model);
2561
+ if (!isNotNullish(args.select)) {
2562
+ return buildDefaultScalarFields(model, alias).join(
2563
+ SQL_SEPARATORS.FIELD_LIST
2564
+ );
2565
+ }
2566
+ const entries = toSelectEntries(args.select);
2567
+ validateSelectKeys(entries, scalarSet, relationSet);
2568
+ const { scalarSelected, hasRelationSelection, hasCount } = analyzeSelectEntries(entries, scalarSet, relationSet);
2569
+ const fields = scalarSelected.map((field) => col(alias, field));
2570
+ if (!isNonEmptyArray(fields)) {
2571
+ if (hasRelationSelection) return "";
2572
+ if (!hasCount) {
2573
+ throw new Error("Select must have at least one scalar field set to true");
2574
+ }
2575
+ }
2576
+ return fields.join(SQL_SEPARATORS.FIELD_LIST);
2577
+ }
2578
+ function buildAllScalarParts(model, alias) {
2579
+ const scalarFields = model.fields.filter((f) => !f.isRelation);
2580
+ if (!isNonEmptyArray(scalarFields)) {
2581
+ throw new Error(`Model ${model.name} has no scalar fields`);
2582
+ }
2583
+ const parts = [];
2584
+ for (const field of scalarFields) {
2585
+ parts.push(`${sqlStringLiteral(field.name)}, ${col(alias, field.name)}`);
2586
+ }
2587
+ return parts;
2588
+ }
2589
+ function validateRelationSelectKeys(entries, scalarNames, relationNames) {
2590
+ const unknown = [];
2591
+ for (const [k] of entries) {
2592
+ if (!scalarNames.has(k) && !relationNames.has(k)) unknown.push(k);
2593
+ }
2594
+ if (unknown.length > 0) {
2595
+ throw new Error(`Select contains unknown fields: ${unknown.join(", ")}`);
2596
+ }
2597
+ }
2598
+ function buildSelectedScalarParts(entries, scalarNames, alias) {
2599
+ const parts = [];
2600
+ for (const [key, value] of entries) {
2601
+ if (!scalarNames.has(key)) continue;
2602
+ if (value === true) {
2603
+ parts.push(`${sqlStringLiteral(key)}, ${col(alias, key)}`);
2604
+ }
2605
+ }
2606
+ return parts;
2607
+ }
2608
+ function buildRelationSelect(relArgs, relModel, relAlias) {
2609
+ if (relArgs === true) {
2610
+ return buildAllScalarParts(relModel, relAlias).join(
2611
+ SQL_SEPARATORS.FIELD_LIST
2612
+ );
2613
+ }
2614
+ if (isPlainObject(relArgs) && hasProperty(relArgs, "select")) {
2615
+ const sel = relArgs.select;
2616
+ if (!isPlainObject(sel)) {
2617
+ throw new Error(
2618
+ `Relation select must be an object for model ${relModel.name}`
2619
+ );
2620
+ }
2621
+ const scalarNames = new Set(
2622
+ relModel.fields.filter((f) => !f.isRelation).map((f) => f.name)
2623
+ );
2624
+ const relationNames = new Set(
2625
+ relModel.fields.filter((f) => f.isRelation).map((f) => f.name)
2626
+ );
2627
+ const entries = toSelectEntries(sel);
2628
+ validateRelationSelectKeys(entries, scalarNames, relationNames);
2629
+ return buildSelectedScalarParts(entries, scalarNames, relAlias).join(
2630
+ SQL_SEPARATORS.FIELD_LIST
2631
+ );
2632
+ }
2633
+ return buildAllScalarParts(relModel, relAlias).join(SQL_SEPARATORS.FIELD_LIST);
2634
+ }
2635
+
2636
+ // src/builder/select/includes.ts
2637
+ function getRelationTableReference(relModel, dialect) {
2638
+ return buildTableReference(
2639
+ SQL_TEMPLATES.PUBLIC_SCHEMA,
2640
+ relModel.tableName,
2641
+ dialect
2642
+ );
2643
+ }
2644
+ function resolveRelationOrThrow(model, schemas, relName) {
2645
+ const field = model.fields.find((f) => f.name === relName);
2646
+ if (!isNotNullish(field)) {
2647
+ throw new Error(
2648
+ `Unknown relation '${relName}' on model ${model.name}. Available relation fields: ${model.fields.filter((f) => f.isRelation).map((f) => f.name).join(", ")}`
2649
+ );
2650
+ }
2651
+ if (!isValidRelationField(field)) {
2652
+ throw new Error(
2653
+ `Invalid relation metadata for '${relName}' on model ${model.name}. This usually indicates a schema parsing error (missing foreignKey/references).`
2654
+ );
2655
+ }
2656
+ const relModel = schemas.find((m) => m.name === field.relatedModel);
2657
+ if (!isNotNullish(relModel)) {
2658
+ throw new Error(
2659
+ `Relation '${relName}' on model ${model.name} references missing model '${field.relatedModel}'.`
2660
+ );
2661
+ }
2662
+ return { field, relModel };
2663
+ }
2664
+ function relationEntriesFromArgs(args, model) {
2665
+ const relationSet = getRelationFieldSet(model);
2666
+ const out = [];
2667
+ const seen = /* @__PURE__ */ new Set();
2668
+ const pushFrom = (src) => {
2669
+ if (!isPlainObject(src)) return;
2670
+ for (const [k, v] of Object.entries(src)) {
2671
+ if (v === false) continue;
2672
+ if (!relationSet.has(k)) continue;
2673
+ if (seen.has(k)) continue;
2674
+ seen.add(k);
2675
+ out.push([k, v]);
2676
+ }
2677
+ };
2678
+ pushFrom(args.include);
2679
+ pushFrom(args.select);
2680
+ return out;
2681
+ }
2682
+ function assertScalarField(model, fieldName) {
2683
+ const f = model.fields.find((x) => x.name === fieldName);
2684
+ if (!f) {
2685
+ throw new Error(
2686
+ `orderBy references unknown field '${fieldName}' on model ${model.name}`
2687
+ );
2688
+ }
2689
+ if (f.isRelation) {
2690
+ throw new Error(
2691
+ `orderBy does not support relation field '${fieldName}' on model ${model.name}`
2692
+ );
2693
+ }
2694
+ }
2695
+ function validateOrderByDirection(fieldName, v) {
2696
+ const s = String(v).toLowerCase();
2697
+ if (s !== "asc" && s !== "desc") {
2698
+ throw new Error(
2699
+ `Invalid orderBy direction for '${fieldName}': ${String(v)}`
2700
+ );
2701
+ }
2702
+ }
2703
+ function validateOrderByObject(fieldName, v) {
2704
+ if (!("sort" in v)) {
2705
+ throw new Error(
2706
+ `orderBy for '${fieldName}' must be 'asc' | 'desc' or { sort, nulls? }`
2707
+ );
2708
+ }
2709
+ validateOrderByDirection(fieldName, v.sort);
2710
+ if ("nulls" in v && isNotNullish(v.nulls)) {
2711
+ const n = String(v.nulls).toLowerCase();
2712
+ if (n !== "first" && n !== "last") {
2713
+ throw new Error(
2714
+ `Invalid orderBy.nulls for '${fieldName}': ${String(v.nulls)}`
2715
+ );
2716
+ }
2717
+ }
2718
+ const allowed = /* @__PURE__ */ new Set(["sort", "nulls"]);
2719
+ for (const k of Object.keys(v)) {
2720
+ if (!allowed.has(k)) {
2721
+ throw new Error(`Unsupported orderBy key '${k}' for field '${fieldName}'`);
2722
+ }
2723
+ }
2724
+ }
2725
+ function normalizeOrderByFieldName(name) {
2726
+ const fieldName = String(name).trim();
2727
+ if (fieldName.length === 0) {
2728
+ throw new Error("orderBy field name cannot be empty");
2729
+ }
2730
+ return fieldName;
2731
+ }
2732
+ function requirePlainObjectForOrderByEntry(v) {
2733
+ if (typeof v !== "object" || v === null || Array.isArray(v)) {
2734
+ throw new Error("orderBy array entries must be objects");
2735
+ }
2736
+ return v;
2737
+ }
2738
+ function parseSingleFieldOrderByObject(obj) {
2739
+ const entries = Object.entries(obj);
2740
+ if (entries.length !== 1) {
2741
+ throw new Error("orderBy array entries must have exactly one field");
2742
+ }
2743
+ const fieldName = normalizeOrderByFieldName(entries[0][0]);
2744
+ return [fieldName, entries[0][1]];
2745
+ }
2746
+ function parseOrderByArray(orderBy) {
2747
+ return orderBy.map(
2748
+ (item) => parseSingleFieldOrderByObject(requirePlainObjectForOrderByEntry(item))
2749
+ );
2750
+ }
2751
+ function parseOrderByObject(orderBy) {
2752
+ const out = [];
2753
+ for (const [k, v] of Object.entries(orderBy)) {
2754
+ out.push([normalizeOrderByFieldName(k), v]);
2755
+ }
2756
+ return out;
2757
+ }
2758
+ function getOrderByEntries(orderBy) {
2759
+ if (!isNotNullish(orderBy)) return [];
2760
+ if (Array.isArray(orderBy)) {
2761
+ return parseOrderByArray(orderBy);
2762
+ }
2763
+ if (typeof orderBy === "object" && orderBy !== null) {
2764
+ return parseOrderByObject(orderBy);
2765
+ }
2766
+ throw new Error("orderBy must be an object or array of objects");
2767
+ }
2768
+ function validateOrderByForModel(model, orderBy) {
2769
+ const entries = getOrderByEntries(orderBy);
2770
+ for (const [fieldName, v] of entries) {
2771
+ assertScalarField(model, fieldName);
2772
+ if (typeof v === "string") {
2773
+ validateOrderByDirection(fieldName, v);
2774
+ continue;
2775
+ }
2776
+ if (isPlainObject(v)) {
2777
+ validateOrderByObject(fieldName, v);
2778
+ continue;
2779
+ }
2780
+ throw new Error(
2781
+ `orderBy for '${fieldName}' must be 'asc' | 'desc' or { sort, nulls? }`
2782
+ );
2783
+ }
2784
+ }
2785
+ function appendLimitOffset(sql, dialect, params, takeVal, skipVal, scope) {
2786
+ const hasTake = isNotNullish(takeVal);
2787
+ const hasSkip = isNotNullish(skipVal);
2788
+ if (dialect === "sqlite" && !hasTake && hasSkip) {
2789
+ const skipPh = addAutoScoped(params, skipVal, `${scope}.skip`);
2790
+ return `${sql} ${SQL_TEMPLATES.LIMIT} -1 ${SQL_TEMPLATES.OFFSET} ${skipPh}`;
2791
+ }
2792
+ if (hasTake) {
2793
+ const takePh = addAutoScoped(params, takeVal, `${scope}.take`);
2794
+ sql = `${sql} ${SQL_TEMPLATES.LIMIT} ${takePh}`;
2795
+ }
2796
+ if (hasSkip) {
2797
+ const skipPh = addAutoScoped(params, skipVal, `${scope}.skip`);
2798
+ sql = `${sql} ${SQL_TEMPLATES.OFFSET} ${skipPh}`;
2799
+ }
2800
+ return sql;
2801
+ }
2802
+ function readWhereInput(relArgs) {
2803
+ if (!isPlainObject(relArgs)) return {};
2804
+ if (!hasProperty(relArgs, "where")) return {};
2805
+ const w = relArgs.where;
2806
+ return isPlainObject(w) ? w : {};
2807
+ }
2808
+ function readOrderByInput(relArgs) {
2809
+ if (!isPlainObject(relArgs)) return { hasOrderBy: false, orderBy: void 0 };
2810
+ if (!("orderBy" in relArgs)) return { hasOrderBy: false, orderBy: void 0 };
2811
+ return { hasOrderBy: true, orderBy: relArgs.orderBy };
2812
+ }
2813
+ function extractRelationPaginationConfig(relArgs) {
2814
+ const { hasOrderBy, orderBy: rawOrderByInput } = readOrderByInput(relArgs);
2815
+ const {
2816
+ hasSkip,
2817
+ hasTake,
2818
+ skipVal,
2819
+ takeVal: rawTakeVal
2820
+ } = readSkipTake(relArgs);
2821
+ return {
2822
+ hasOrderBy,
2823
+ orderBy: rawOrderByInput,
2824
+ hasSkip,
2825
+ hasTake,
2826
+ skipVal,
2827
+ takeVal: rawTakeVal
2828
+ };
2829
+ }
2830
+ function maybeReverseNegativeTake(takeVal, hasOrderBy, orderByInput) {
2831
+ if (typeof takeVal !== "number") return { takeVal, orderByInput };
2832
+ if (takeVal >= 0) return { takeVal, orderByInput };
2833
+ if (!hasOrderBy) {
2834
+ throw new Error("Negative take requires orderBy for deterministic results");
2835
+ }
2836
+ return {
2837
+ takeVal: Math.abs(takeVal),
2838
+ orderByInput: reverseOrderByInput(orderByInput)
2839
+ };
2840
+ }
2841
+ function hasIdTiebreaker(orderByInput) {
2842
+ const entries = Array.isArray(orderByInput) ? orderByInput : [orderByInput];
2843
+ return entries.some(
2844
+ (entry) => isPlainObject(entry) ? Object.prototype.hasOwnProperty.call(entry, "id") : false
2845
+ );
2846
+ }
2847
+ function modelHasScalarId(relModel) {
2848
+ const idField = relModel.fields.find((f) => f.name === "id");
2849
+ return Boolean(idField && !idField.isRelation);
2850
+ }
2851
+ function addIdTiebreaker(orderByInput) {
2852
+ if (Array.isArray(orderByInput)) return [...orderByInput, { id: "asc" }];
2853
+ return [orderByInput, { id: "asc" }];
2854
+ }
2855
+ function ensureDeterministicOrderBy(relModel, hasOrderBy, orderByInput, hasPagination) {
2856
+ if (!hasPagination) {
2857
+ if (hasOrderBy && isNotNullish(orderByInput)) {
2858
+ validateOrderByForModel(relModel, orderByInput);
2859
+ }
2860
+ return orderByInput;
2861
+ }
2862
+ if (!hasOrderBy) {
2863
+ return modelHasScalarId(relModel) ? { id: "asc" } : orderByInput;
2864
+ }
2865
+ if (isNotNullish(orderByInput)) {
2866
+ validateOrderByForModel(relModel, orderByInput);
2867
+ }
2868
+ if (!modelHasScalarId(relModel)) return orderByInput;
2869
+ if (hasIdTiebreaker(orderByInput)) return orderByInput;
2870
+ return addIdTiebreaker(orderByInput);
2871
+ }
2872
+ function buildSelectWithNestedIncludes(relArgs, relModel, relAlias, ctx) {
2873
+ let relSelect = buildRelationSelect(relArgs, relModel, relAlias);
2874
+ const nestedIncludes = isPlainObject(relArgs) ? buildIncludeSqlInternal(
2875
+ relArgs,
2876
+ relModel,
2877
+ ctx.schemas,
2878
+ relAlias,
2879
+ ctx.aliasGen,
2880
+ ctx.params,
2881
+ ctx.dialect
2882
+ ) : [];
2883
+ if (isNonEmptyArray(nestedIncludes)) {
2884
+ const emptyJson = ctx.dialect === "postgres" ? `'[]'::json` : `json('[]')`;
2885
+ const nestedSelects = nestedIncludes.map(
2886
+ (inc) => inc.isOneToOne ? `${sqlStringLiteral(inc.name)}, (${inc.sql})` : `${sqlStringLiteral(inc.name)}, COALESCE((${inc.sql}), ${emptyJson})`
2887
+ ).join(SQL_SEPARATORS.FIELD_LIST);
2888
+ relSelect = isNotNullish(relSelect) && relSelect.trim().length > 0 ? `${relSelect}${SQL_SEPARATORS.FIELD_LIST}${nestedSelects}` : nestedSelects;
2889
+ }
2890
+ if (!isNotNullish(relSelect) || relSelect.trim().length === 0) {
2891
+ throw new Error(
2892
+ `Select must include at least one field or nested relation for model ${relModel.name}`
2893
+ );
2894
+ }
2895
+ return relSelect;
2896
+ }
2897
+ function buildWhereParts(whereInput, relModel, relAlias, ctx) {
2898
+ const whereResult = buildWhereClause(whereInput, {
2899
+ alias: relAlias,
2900
+ schemaModels: ctx.schemas,
2901
+ model: relModel,
2902
+ params: ctx.params,
2903
+ isSubquery: true,
2904
+ aliasGen: ctx.aliasGen,
2905
+ dialect: ctx.dialect
2906
+ });
2907
+ const joins = whereResult.joins.join(" ");
2908
+ const whereClause = isValidWhereClause(whereResult.clause) ? ` ${SQL_TEMPLATES.AND} ${whereResult.clause}` : "";
2909
+ return { joins, whereClause };
2910
+ }
2911
+ function limitOneSql(sql, params, skipVal, scope) {
2912
+ if (isNotNullish(skipVal)) {
2913
+ const skipPh = addAutoScoped(params, skipVal, `${scope}.skip`);
2914
+ return `${sql} ${SQL_TEMPLATES.LIMIT} 1 ${SQL_TEMPLATES.OFFSET} ${skipPh}`;
2915
+ }
2916
+ return `${sql} ${SQL_TEMPLATES.LIMIT} 1`;
2917
+ }
2918
+ function buildOrderBySql(finalOrderByInput, relAlias, dialect) {
2919
+ return isNotNullish(finalOrderByInput) ? buildOrderBy(finalOrderByInput, relAlias, dialect) : "";
2920
+ }
2921
+ function buildBaseSql(args) {
2922
+ return `${SQL_TEMPLATES.SELECT} ${args.selectExpr} ${SQL_TEMPLATES.FROM} ${args.relTable} ${args.relAlias} ${args.joins} ${SQL_TEMPLATES.WHERE} ${args.joinPredicate}${args.whereClause}`;
2923
+ }
2924
+ function buildOneToOneIncludeSql(args) {
2925
+ const objExpr = jsonBuildObject(args.relSelect, args.ctx.dialect);
2926
+ let sql = buildBaseSql({
2927
+ selectExpr: objExpr,
2928
+ relTable: args.relTable,
2929
+ relAlias: args.relAlias,
2930
+ joins: args.joins,
2931
+ joinPredicate: args.joinPredicate,
2932
+ whereClause: args.whereClause
2933
+ });
2934
+ if (args.orderBySql) {
2935
+ sql += ` ${SQL_TEMPLATES.ORDER_BY} ${args.orderBySql}`;
2936
+ }
2937
+ if (isNotNullish(args.takeVal)) {
2938
+ return appendLimitOffset(
2939
+ sql,
2940
+ args.ctx.dialect,
2941
+ args.ctx.params,
2942
+ args.takeVal,
2943
+ args.skipVal,
2944
+ `include.${args.relName}`
2945
+ );
2946
+ }
2947
+ return limitOneSql(
2948
+ sql,
2949
+ args.ctx.params,
2950
+ args.skipVal,
2951
+ `include.${args.relName}`
2952
+ );
2953
+ }
2954
+ function buildListIncludeSpec(args) {
2955
+ const rowExpr = jsonBuildObject(args.relSelect, args.ctx.dialect);
2956
+ const noTake = !isNotNullish(args.takeVal);
2957
+ const noSkip = !isNotNullish(args.skipVal);
2958
+ if (args.ctx.dialect === "postgres" && noTake && noSkip) {
2959
+ const selectExpr2 = args.orderBySql ? `json_agg(${rowExpr} ORDER BY ${args.orderBySql})` : `json_agg(${rowExpr})`;
2960
+ const sql2 = buildBaseSql({
2961
+ selectExpr: selectExpr2,
2962
+ relTable: args.relTable,
2963
+ relAlias: args.relAlias,
2964
+ joins: args.joins,
2965
+ joinPredicate: args.joinPredicate,
2966
+ whereClause: args.whereClause
2967
+ });
2968
+ return Object.freeze({
2969
+ name: args.relName,
2970
+ sql: sql2,
2971
+ isOneToOne: false
2972
+ });
2973
+ }
2974
+ const rowAlias = args.ctx.aliasGen.next(`${args.relName}_row`);
2975
+ let base = buildBaseSql({
2976
+ selectExpr: `${rowExpr} ${SQL_TEMPLATES.AS} row`,
2977
+ relTable: args.relTable,
2978
+ relAlias: args.relAlias,
2979
+ joins: args.joins,
2980
+ joinPredicate: args.joinPredicate,
2981
+ whereClause: args.whereClause
2982
+ });
2983
+ if (args.orderBySql) {
2984
+ base += ` ${SQL_TEMPLATES.ORDER_BY} ${args.orderBySql}`;
2985
+ }
2986
+ base = appendLimitOffset(
2987
+ base,
2988
+ args.ctx.dialect,
2989
+ args.ctx.params,
2990
+ args.takeVal,
2991
+ args.skipVal,
2992
+ `include.${args.relName}`
2993
+ );
2994
+ const selectExpr = jsonAgg("row", args.ctx.dialect);
2995
+ const sql = `${SQL_TEMPLATES.SELECT} ${selectExpr} ${SQL_TEMPLATES.FROM} (${base}) ${rowAlias}`;
2996
+ return Object.freeze({
2997
+ name: args.relName,
2998
+ sql,
2999
+ isOneToOne: false
3000
+ });
3001
+ }
3002
+ function buildSingleInclude(relName, relArgs, field, relModel, ctx) {
3003
+ const relTable = getRelationTableReference(relModel, ctx.dialect);
3004
+ const relAlias = ctx.aliasGen.next(relName);
3005
+ const isList = typeof field.type === "string" && field.type.endsWith("[]");
3006
+ const joinPredicate = joinCondition(field, ctx.parentAlias, relAlias);
3007
+ const whereInput = readWhereInput(relArgs);
3008
+ const relSelect = buildSelectWithNestedIncludes(
3009
+ relArgs,
3010
+ relModel,
3011
+ relAlias,
3012
+ ctx
3013
+ );
3014
+ const whereParts = buildWhereParts(whereInput, relModel, relAlias, ctx);
3015
+ const paginationConfig = extractRelationPaginationConfig(relArgs);
3016
+ if (!isList && typeof paginationConfig.takeVal === "number" && paginationConfig.takeVal < 0) {
3017
+ throw new Error("Negative take is only supported for list relations");
3018
+ }
3019
+ const adjusted = maybeReverseNegativeTake(
3020
+ paginationConfig.takeVal,
3021
+ paginationConfig.hasOrderBy,
3022
+ paginationConfig.orderBy
3023
+ );
3024
+ const hasPagination = paginationConfig.hasSkip || paginationConfig.hasTake;
3025
+ const finalOrderByInput = ensureDeterministicOrderBy(
3026
+ relModel,
3027
+ paginationConfig.hasOrderBy,
3028
+ adjusted.orderByInput,
3029
+ hasPagination
3030
+ );
3031
+ const orderBySql = buildOrderBySql(finalOrderByInput, relAlias, ctx.dialect);
3032
+ if (!isList) {
3033
+ const sql = buildOneToOneIncludeSql({
3034
+ relName,
3035
+ relTable,
3036
+ relAlias,
3037
+ joins: whereParts.joins,
3038
+ joinPredicate,
3039
+ whereClause: whereParts.whereClause,
3040
+ orderBySql,
3041
+ relSelect,
3042
+ takeVal: adjusted.takeVal,
3043
+ skipVal: paginationConfig.skipVal,
3044
+ ctx
3045
+ });
3046
+ return Object.freeze({
3047
+ name: relName,
3048
+ sql,
3049
+ isOneToOne: true
3050
+ });
3051
+ }
3052
+ return buildListIncludeSpec({
3053
+ relName,
3054
+ relTable,
3055
+ relAlias,
3056
+ joins: whereParts.joins,
3057
+ joinPredicate,
3058
+ whereClause: whereParts.whereClause,
3059
+ orderBySql,
3060
+ relSelect,
3061
+ takeVal: adjusted.takeVal,
3062
+ skipVal: paginationConfig.skipVal,
3063
+ ctx
3064
+ });
3065
+ }
3066
+ function buildIncludeSqlInternal(args, model, schemas, parentAlias, aliasGen, params, dialect) {
3067
+ const includes = [];
3068
+ const entries = relationEntriesFromArgs(args, model);
3069
+ for (const [relName, relArgs] of entries) {
3070
+ if (relArgs === false) continue;
3071
+ const resolved = resolveRelationOrThrow(model, schemas, relName);
3072
+ const include = buildSingleInclude(
3073
+ relName,
3074
+ relArgs,
3075
+ resolved.field,
3076
+ resolved.relModel,
3077
+ {
3078
+ model,
3079
+ schemas,
3080
+ parentAlias,
3081
+ aliasGen,
3082
+ dialect,
3083
+ params
3084
+ }
3085
+ );
3086
+ includes.push(include);
3087
+ }
3088
+ return includes;
3089
+ }
3090
+ function buildIncludeSql(args, model, schemas, parentAlias, params, dialect) {
3091
+ const aliasGen = createAliasGenerator();
3092
+ return buildIncludeSqlInternal(
3093
+ args,
3094
+ model,
3095
+ schemas,
3096
+ parentAlias,
3097
+ aliasGen,
3098
+ params,
3099
+ dialect
3100
+ );
3101
+ }
3102
+ function resolveCountRelationOrThrow(relName, model, schemas) {
3103
+ const relationSet = getRelationFieldSet(model);
3104
+ if (!relationSet.has(relName)) {
3105
+ throw new Error(
3106
+ `_count.${relName} references unknown relation on model ${model.name}`
3107
+ );
3108
+ }
3109
+ const field = model.fields.find((f) => f.name === relName);
3110
+ if (!field) {
3111
+ throw new Error(
3112
+ `_count.${relName} references unknown relation on model ${model.name}`
3113
+ );
3114
+ }
3115
+ const relModel = schemas.find((m) => m.name === field.relatedModel);
3116
+ if (!relModel) {
3117
+ throw new Error(
3118
+ `Related model '${field.relatedModel}' not found for _count.${relName}`
3119
+ );
3120
+ }
3121
+ return { field, relModel };
3122
+ }
3123
+ function groupByColForCount(field, countAlias) {
3124
+ const fkFields = normalizeKeyList(field.foreignKey);
3125
+ const refFields = normalizeKeyList(field.references);
3126
+ return field.isForeignKeyLocal ? `${countAlias}.${quote(refFields[0] || "id")}` : `${countAlias}.${quote(fkFields[0])}`;
3127
+ }
3128
+ function leftJoinOnForCount(field, parentAlias, joinAlias) {
3129
+ const fkFields = normalizeKeyList(field.foreignKey);
3130
+ const refFields = normalizeKeyList(field.references);
3131
+ return field.isForeignKeyLocal ? `${joinAlias}.__fk = ${parentAlias}.${quote(fkFields[0])}` : `${joinAlias}.__fk = ${parentAlias}.${quote(refFields[0] || "id")}`;
3132
+ }
3133
+ function subqueryForCount(dialect, relTable, countAlias, groupByCol) {
3134
+ return dialect === "postgres" ? `(SELECT ${groupByCol} AS __fk, COUNT(*)::int AS __cnt FROM ${relTable} ${countAlias} GROUP BY ${groupByCol})` : `(SELECT ${groupByCol} AS __fk, COUNT(*) AS __cnt FROM ${relTable} ${countAlias} GROUP BY ${groupByCol})`;
3135
+ }
3136
+ function buildCountJoinAndPair(args) {
3137
+ const relTable = getRelationTableReference(args.relModel, args.dialect);
3138
+ const countAlias = `__tp_cnt_${args.relName}`;
3139
+ const groupByCol = groupByColForCount(args.field, countAlias);
3140
+ const subquery = subqueryForCount(
3141
+ args.dialect,
3142
+ relTable,
3143
+ countAlias,
3144
+ groupByCol
3145
+ );
3146
+ const joinAlias = `__tp_cnt_j_${args.relName}`;
3147
+ const leftJoinOn = leftJoinOnForCount(args.field, args.parentAlias, joinAlias);
3148
+ return {
3149
+ joinSql: `LEFT JOIN ${subquery} ${joinAlias} ON ${leftJoinOn}`,
3150
+ pairSql: `${sqlStringLiteral(args.relName)}, COALESCE(${joinAlias}.__cnt, 0)`
3151
+ };
3152
+ }
3153
+ function buildRelationCountSql(countSelect, model, schemas, parentAlias, _params, dialect) {
3154
+ const joins = [];
3155
+ const pairs = [];
3156
+ for (const [relName, shouldCount] of Object.entries(countSelect)) {
3157
+ if (!shouldCount) continue;
3158
+ const resolved = resolveCountRelationOrThrow(relName, model, schemas);
3159
+ const built = buildCountJoinAndPair({
3160
+ relName,
3161
+ field: resolved.field,
3162
+ relModel: resolved.relModel,
3163
+ parentAlias,
3164
+ dialect
3165
+ });
3166
+ joins.push(built.joinSql);
3167
+ pairs.push(built.pairSql);
3168
+ }
3169
+ return {
3170
+ joins,
3171
+ jsonPairs: pairs.join(SQL_SEPARATORS.FIELD_LIST)
3172
+ };
3173
+ }
3174
+
3175
+ // src/builder/select/assembly.ts
3176
+ var SIMPLE_SELECT_RE_CACHE = /* @__PURE__ */ new Map();
3177
+ function joinNonEmpty(parts, sep) {
3178
+ return parts.filter((s) => s.trim().length > 0).join(sep);
3179
+ }
3180
+ function buildWhereSql(conditions) {
3181
+ if (!isNonEmptyArray(conditions)) return "";
3182
+ return ` ${SQL_TEMPLATES.WHERE} ${conditions.join(SQL_SEPARATORS.CONDITION_AND)}`;
3183
+ }
3184
+ function buildJoinsSql(...joinGroups) {
3185
+ const all = [];
3186
+ for (const g of joinGroups) {
3187
+ if (isNonEmptyArray(g)) all.push(...g);
3188
+ }
3189
+ return all.length > 0 ? ` ${all.join(" ")}` : "";
3190
+ }
3191
+ function buildSelectList(baseSelect, extraCols) {
3192
+ const base = baseSelect.trim();
3193
+ const extra = extraCols.trim();
3194
+ if (base && extra) return `${base}${SQL_SEPARATORS.FIELD_LIST}${extra}`;
3195
+ return base || extra;
3196
+ }
3197
+ function finalizeSql(sql, params) {
3198
+ const snapshot = params.snapshot();
3199
+ validateSelectQuery(sql);
3200
+ validateParamConsistency(sql, snapshot.params);
3201
+ return Object.freeze({
3202
+ sql,
3203
+ params: snapshot.params,
3204
+ paramMappings: snapshot.mappings
3205
+ });
3206
+ }
3207
+ function parseSimpleScalarSelect(select, alias) {
3208
+ var _a, _b;
3209
+ const raw = select.trim();
3210
+ if (raw.length === 0) return [];
3211
+ let re = SIMPLE_SELECT_RE_CACHE.get(alias);
3212
+ if (!re) {
3213
+ const safeAlias2 = alias.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
3214
+ re = new RegExp(`^${safeAlias2}\\.(?:"([^"]+)"|([a-z_][a-z0-9_]*))$`, "i");
3215
+ SIMPLE_SELECT_RE_CACHE.set(alias, re);
3216
+ }
3217
+ const parts = raw.split(SQL_SEPARATORS.FIELD_LIST);
3218
+ const names = [];
3219
+ for (const part of parts) {
3220
+ const p = part.trim();
3221
+ const m = p.match(re);
3222
+ if (!m) {
3223
+ throw new Error(
3224
+ `sqlite distinct emulation requires scalar select fields to be simple columns. Got: ${p}`
3225
+ );
3226
+ }
3227
+ const name = ((_b = (_a = m[1]) != null ? _a : m[2]) != null ? _b : "").trim();
3228
+ if (name.length === 0) {
3229
+ throw new Error(`Failed to parse selected column name from: ${p}`);
3230
+ }
3231
+ names.push(name);
3232
+ }
3233
+ return names;
3234
+ }
3235
+ function replaceOrderByAlias(orderBy, fromAlias, outerAlias) {
3236
+ const needle = `${fromAlias}.`;
3237
+ const replacement = `${outerAlias}.`;
3238
+ return orderBy.split(needle).join(replacement);
3239
+ }
3240
+ function buildDistinctColumns(distinct, fromAlias) {
3241
+ return distinct.map((f) => col(fromAlias, f)).join(SQL_SEPARATORS.FIELD_LIST);
3242
+ }
3243
+ function buildOutputColumns(scalarNames, includeNames, hasCount) {
3244
+ const outputCols = [...scalarNames, ...includeNames];
3245
+ if (hasCount) {
3246
+ outputCols.push("_count");
3247
+ }
3248
+ const formatted = outputCols.map((n) => quote(n)).join(SQL_SEPARATORS.FIELD_LIST);
3249
+ if (!isNonEmptyString(formatted)) {
3250
+ throw new Error("distinct emulation requires at least one output column");
3251
+ }
3252
+ return formatted;
3253
+ }
3254
+ function buildWindowOrder(args) {
3255
+ const { baseOrder, idField, fromAlias } = args;
3256
+ const orderFields = baseOrder.split(SQL_SEPARATORS.ORDER_BY).map((s) => s.trim().toLowerCase());
3257
+ const hasIdInOrder = orderFields.some(
3258
+ (f) => f.startsWith(`${fromAlias}.id `) || f.startsWith(`${fromAlias}."id" `)
3259
+ );
3260
+ if (hasIdInOrder) return baseOrder;
3261
+ const idTiebreaker = idField ? `, ${col(fromAlias, "id")} ASC` : "";
3262
+ return `${baseOrder}${idTiebreaker}`;
3263
+ }
3264
+ function buildSqliteDistinctQuery(spec, selectWithIncludes, countJoins) {
3265
+ var _a, _b;
3266
+ const { includes, from, whereClause, whereJoins, orderBy, distinct, model } = spec;
3267
+ if (!isNotNullish(distinct) || !isNonEmptyArray(distinct)) {
3268
+ throw new Error("buildSqliteDistinctQuery requires distinct fields");
3269
+ }
3270
+ const scalarNames = parseSimpleScalarSelect(spec.select, from.alias);
3271
+ const includeNames = includes.map((i) => i.name);
3272
+ const hasCount = Boolean((_b = (_a = spec.args) == null ? void 0 : _a.select) == null ? void 0 : _b._count);
3273
+ const outerSelectCols = buildOutputColumns(
3274
+ scalarNames,
3275
+ includeNames,
3276
+ hasCount
3277
+ );
3278
+ const distinctCols = buildDistinctColumns([...distinct], from.alias);
3279
+ const fallbackOrder = [...distinct].map((f) => `${col(from.alias, f)} ASC`).join(SQL_SEPARATORS.FIELD_LIST);
3280
+ const idField = model.fields.find((f) => f.name === "id" && !f.isRelation);
3281
+ const baseOrder = isNonEmptyString(orderBy) ? orderBy : fallbackOrder;
3282
+ const windowOrder = buildWindowOrder({
3283
+ baseOrder,
3284
+ idField,
3285
+ fromAlias: from.alias
3286
+ });
3287
+ const outerOrder = isNonEmptyString(orderBy) ? replaceOrderByAlias(orderBy, from.alias, `"__tp_distinct"`) : replaceOrderByAlias(fallbackOrder, from.alias, `"__tp_distinct"`);
3288
+ const joins = buildJoinsSql(whereJoins, countJoins);
3289
+ const conditions = [];
3290
+ if (whereClause && whereClause !== "1=1") {
3291
+ conditions.push(whereClause);
3292
+ }
3293
+ const whereSql = buildWhereSql(conditions);
3294
+ const innerSelectList = selectWithIncludes.trim();
3295
+ const innerComma = innerSelectList.length > 0 ? SQL_SEPARATORS.FIELD_LIST : "";
3296
+ const inner = `${SQL_TEMPLATES.SELECT} ${innerSelectList}${innerComma}ROW_NUMBER() OVER (PARTITION BY ${distinctCols} ORDER BY ${windowOrder}) ${SQL_TEMPLATES.AS} "__tp_rn" ${SQL_TEMPLATES.FROM} ${from.table} ${from.alias}${joins}${whereSql}`;
3297
+ const outer = `${SQL_TEMPLATES.SELECT} ${outerSelectCols} ${SQL_TEMPLATES.FROM} (${inner}) ${SQL_TEMPLATES.AS} "__tp_distinct" ${SQL_TEMPLATES.WHERE} "__tp_rn" = 1` + (isNonEmptyString(outerOrder) ? ` ${SQL_TEMPLATES.ORDER_BY} ${outerOrder}` : "");
3298
+ return outer;
3299
+ }
3300
+ function buildIncludeColumns(spec) {
3301
+ var _a, _b;
3302
+ const { select, includes, dialect, model, schemas, from, params } = spec;
3303
+ const baseSelect = (select != null ? select : "").trim();
3304
+ let countCols = "";
3305
+ let countJoins = [];
3306
+ const countSelect = (_b = (_a = spec.args) == null ? void 0 : _a.select) == null ? void 0 : _b._count;
3307
+ if (countSelect) {
3308
+ if (isPlainObject(countSelect) && "select" in countSelect) {
3309
+ const countBuild = buildRelationCountSql(
3310
+ countSelect.select,
3311
+ model,
3312
+ schemas,
3313
+ from.alias,
3314
+ params,
3315
+ dialect
3316
+ );
3317
+ if (countBuild.jsonPairs) {
3318
+ countCols = `${jsonBuildObject(countBuild.jsonPairs, dialect)} ${SQL_TEMPLATES.AS} ${quote("_count")}`;
3319
+ }
3320
+ countJoins = countBuild.joins;
3321
+ }
3322
+ }
3323
+ const hasIncludes = isNonEmptyArray(includes);
3324
+ const hasCountCols = isNonEmptyString(countCols);
3325
+ if (!hasIncludes && !hasCountCols) {
3326
+ return { includeCols: "", selectWithIncludes: baseSelect, countJoins: [] };
3327
+ }
3328
+ const emptyJson = dialect === "postgres" ? `'[]'::json` : `json('[]')`;
3329
+ const includeCols = hasIncludes ? includes.map((inc) => {
3330
+ const expr = inc.isOneToOne ? `(${inc.sql})` : `COALESCE((${inc.sql}), ${emptyJson})`;
3331
+ return `${expr} ${SQL_TEMPLATES.AS} ${quote(inc.name)}`;
3332
+ }).join(SQL_SEPARATORS.FIELD_LIST) : "";
3333
+ const allCols = joinNonEmpty(
3334
+ [includeCols, countCols],
3335
+ SQL_SEPARATORS.FIELD_LIST
3336
+ );
3337
+ const selectWithIncludes = buildSelectList(baseSelect, allCols);
3338
+ return { includeCols: allCols, selectWithIncludes, countJoins };
3339
+ }
3340
+ function appendPagination(sql, spec) {
3341
+ const { method, pagination, params } = spec;
3342
+ const isFindUniqueOrFirst = method === "findUnique" || method === "findFirst";
3343
+ if (isFindUniqueOrFirst) {
3344
+ const parts2 = [sql, SQL_TEMPLATES.LIMIT, "1"];
3345
+ const hasSkip = isNotNullish(pagination.skip) && (schemaParser.isDynamicParameter(pagination.skip) || typeof pagination.skip === "number" && pagination.skip > 0) && method === "findFirst";
3346
+ if (hasSkip) {
3347
+ const placeholder = addAutoScoped(
3348
+ params,
3349
+ pagination.skip,
3350
+ "root.pagination.skip"
3351
+ );
3352
+ parts2.push(SQL_TEMPLATES.OFFSET, placeholder);
3353
+ }
3354
+ return parts2.join(" ");
3355
+ }
3356
+ const parts = [sql];
3357
+ if (isNotNullish(pagination.take)) {
3358
+ const placeholder = addAutoScoped(
3359
+ params,
3360
+ pagination.take,
3361
+ "root.pagination.take"
3362
+ );
3363
+ parts.push(SQL_TEMPLATES.LIMIT, placeholder);
3364
+ }
3365
+ if (isNotNullish(pagination.skip)) {
3366
+ const placeholder = addAutoScoped(
3367
+ params,
3368
+ pagination.skip,
3369
+ "root.pagination.skip"
3370
+ );
3371
+ parts.push(SQL_TEMPLATES.OFFSET, placeholder);
3372
+ }
3373
+ return parts.join(" ");
3374
+ }
3375
+ function hasWindowDistinct(spec) {
3376
+ const d = spec.distinct;
3377
+ return isNotNullish(d) && isNonEmptyArray(d);
3378
+ }
3379
+ function assertDistinctAllowed(method, enabled) {
3380
+ if (enabled && method !== "findMany") {
3381
+ throw new Error(
3382
+ "distinct is only supported for findMany in this SQL builder"
3383
+ );
3384
+ }
3385
+ }
3386
+ function assertHasSelectFields(baseSelect, includeCols) {
3387
+ if (!isNonEmptyString(baseSelect) && !isNonEmptyString(includeCols)) {
3388
+ throw new Error("SELECT requires at least one selected field or include");
3389
+ }
3390
+ }
3391
+ function withCountJoins(spec, countJoins, whereJoins) {
3392
+ return __spreadProps(__spreadValues({}, spec), {
3393
+ whereJoins: [...whereJoins || [], ...countJoins || []]
3394
+ });
3395
+ }
3396
+ function buildPostgresDistinctOnClause(fromAlias, distinct) {
3397
+ if (!isNonEmptyArray(distinct)) return null;
3398
+ const distinctCols = buildDistinctColumns([...distinct], fromAlias);
3399
+ return `${SQL_TEMPLATES.DISTINCT_ON} (${distinctCols})`;
3400
+ }
3401
+ function pushJoinGroups(parts, ...groups) {
3402
+ for (const g of groups) {
3403
+ if (isNonEmptyArray(g)) parts.push(g.join(" "));
3404
+ }
3405
+ }
3406
+ function buildConditions(whereClause, cursorClause) {
3407
+ const conditions = [];
3408
+ if (whereClause && whereClause !== "1=1") conditions.push(whereClause);
3409
+ if (isNotNullish(cursorClause) && isNonEmptyString(cursorClause))
3410
+ conditions.push(cursorClause);
3411
+ return conditions;
3412
+ }
3413
+ function pushWhere(parts, conditions) {
3414
+ if (!isNonEmptyArray(conditions)) return;
3415
+ parts.push(SQL_TEMPLATES.WHERE, conditions.join(SQL_SEPARATORS.CONDITION_AND));
3416
+ }
3417
+ function constructFinalSql(spec) {
3418
+ const {
3419
+ select,
3420
+ from,
3421
+ whereClause,
3422
+ whereJoins,
3423
+ orderBy,
3424
+ distinct,
3425
+ method,
3426
+ cursorClause,
3427
+ params,
3428
+ dialect
3429
+ } = spec;
3430
+ const useWindowDistinct = hasWindowDistinct(spec);
3431
+ assertDistinctAllowed(method, useWindowDistinct);
3432
+ const { includeCols, selectWithIncludes, countJoins } = buildIncludeColumns(spec);
3433
+ if (useWindowDistinct) {
3434
+ const baseSelect2 = (select != null ? select : "").trim();
3435
+ assertHasSelectFields(baseSelect2, includeCols);
3436
+ const spec2 = withCountJoins(spec, countJoins, whereJoins);
3437
+ let sql2 = buildSqliteDistinctQuery(spec2, selectWithIncludes).trim();
3438
+ sql2 = appendPagination(sql2, spec);
3439
+ return finalizeSql(sql2, params);
3440
+ }
3441
+ const parts = [SQL_TEMPLATES.SELECT];
3442
+ const distinctOn = dialect === "postgres" ? buildPostgresDistinctOnClause(from.alias, distinct) : null;
3443
+ if (distinctOn) parts.push(distinctOn);
3444
+ const baseSelect = (select != null ? select : "").trim();
3445
+ const fullSelectList = buildSelectList(baseSelect, includeCols);
3446
+ if (!isNonEmptyString(fullSelectList)) {
3447
+ throw new Error("SELECT requires at least one selected field or include");
3448
+ }
3449
+ parts.push(fullSelectList);
3450
+ parts.push(SQL_TEMPLATES.FROM, from.table, from.alias);
3451
+ pushJoinGroups(parts, whereJoins, countJoins);
3452
+ const conditions = buildConditions(whereClause, cursorClause);
3453
+ pushWhere(parts, conditions);
3454
+ if (isNonEmptyString(orderBy)) parts.push(SQL_TEMPLATES.ORDER_BY, orderBy);
3455
+ let sql = parts.join(" ").trim();
3456
+ sql = appendPagination(sql, spec);
3457
+ return finalizeSql(sql, params);
3458
+ }
3459
+
3460
+ // src/builder/select.ts
3461
+ function normalizeOrderByInput2(orderBy) {
3462
+ return normalizeOrderByInput(orderBy, parseOrderByValue);
3463
+ }
3464
+ function normalizeDistinctFields(distinct) {
3465
+ if (!isNonEmptyArray(distinct)) return [];
3466
+ return distinct.filter((f) => typeof f === "string").map((f) => f.trim()).filter((f) => f.length > 0);
3467
+ }
3468
+ function mapFirstOrderByByField(existing) {
3469
+ const m = /* @__PURE__ */ new Map();
3470
+ for (const obj of existing) {
3471
+ const field = Object.keys(obj)[0];
3472
+ if (field && !m.has(field)) m.set(field, obj);
3473
+ }
3474
+ return m;
3475
+ }
3476
+ function buildPostgresDistinctOrderBy(distinctFields, existing) {
3477
+ var _a;
3478
+ const firstByField = mapFirstOrderByByField(existing);
3479
+ const next = [];
3480
+ for (const f of distinctFields) {
3481
+ next.push((_a = firstByField.get(f)) != null ? _a : { [f]: "asc" });
3482
+ }
3483
+ const distinctSet = new Set(distinctFields);
3484
+ for (const obj of existing) {
3485
+ const field = Object.keys(obj)[0];
3486
+ if (!distinctSet.has(field)) next.push(obj);
3487
+ }
3488
+ return next;
3489
+ }
3490
+ function applyPostgresDistinctOrderBy(args, _model) {
3491
+ const distinctFields = normalizeDistinctFields(args.distinct);
3492
+ if (distinctFields.length === 0) return args;
3493
+ if (!isNotNullish(args.orderBy)) return args;
3494
+ const existing = normalizeOrderByInput2(args.orderBy);
3495
+ if (existing.length === 0) return args;
3496
+ return __spreadProps(__spreadValues({}, args), {
3497
+ orderBy: buildPostgresDistinctOrderBy(distinctFields, existing)
3498
+ });
3499
+ }
3500
+ function assertScalarFieldOnModel(model, fieldName, ctx) {
3501
+ const f = model.fields.find((x) => x.name === fieldName);
3502
+ if (!f) {
3503
+ throw new Error(
3504
+ `${ctx} references unknown field '${fieldName}' on model ${model.name}`
3505
+ );
3506
+ }
3507
+ if (f.isRelation) {
3508
+ throw new Error(
3509
+ `${ctx} does not support relation field '${fieldName}' on model ${model.name}`
3510
+ );
3511
+ }
3512
+ }
3513
+ function validateDistinct(model, distinct) {
3514
+ if (!isNotNullish(distinct) || !isNonEmptyArray(distinct)) return;
3515
+ const seen = /* @__PURE__ */ new Set();
3516
+ for (const raw of distinct) {
3517
+ const f = String(raw).trim();
3518
+ if (f.length === 0) continue;
3519
+ if (seen.has(f)) {
3520
+ throw new Error(`distinct must not contain duplicates (field: '${f}')`);
3521
+ }
3522
+ seen.add(f);
3523
+ assertScalarFieldOnModel(model, f, "distinct");
3524
+ }
3525
+ }
3526
+ function validateOrderByValue(fieldName, v) {
3527
+ parseOrderByValue(v, fieldName);
3528
+ }
3529
+ function validateOrderBy(model, orderBy) {
3530
+ if (!isNotNullish(orderBy)) return;
3531
+ const items = normalizeOrderByInput2(orderBy);
3532
+ if (items.length === 0) return;
3533
+ for (const it of items) {
3534
+ const entries = Object.entries(it);
3535
+ const fieldName = String(entries[0][0]).trim();
3536
+ if (fieldName.length === 0) {
3537
+ throw new Error("orderBy field name cannot be empty");
3538
+ }
3539
+ assertScalarFieldOnModel(model, fieldName, "orderBy");
3540
+ validateOrderByValue(fieldName, entries[0][1]);
3541
+ }
3542
+ }
3543
+ function validateCursor(model, cursor) {
3544
+ if (!isNotNullish(cursor)) return;
3545
+ if (!isPlainObject(cursor)) {
3546
+ throw new Error("cursor must be an object");
3547
+ }
3548
+ const entries = Object.entries(cursor);
3549
+ if (entries.length === 0) {
3550
+ throw new Error("cursor must have at least one field");
3551
+ }
3552
+ for (const [fieldName] of entries) {
3553
+ const f = String(fieldName).trim();
3554
+ if (f.length === 0) {
3555
+ throw new Error("cursor field name cannot be empty");
3556
+ }
3557
+ assertScalarFieldOnModel(model, f, "cursor");
3558
+ }
3559
+ }
3560
+ function resolveDialect(dialect) {
3561
+ return dialect != null ? dialect : getGlobalDialect();
3562
+ }
3563
+ function normalizeArgsForNegativeTake(method, args) {
3564
+ if (method !== "findMany") return args;
3565
+ if (typeof args.take !== "number") return args;
3566
+ if (!Number.isInteger(args.take)) return args;
3567
+ if (args.take >= 0) return args;
3568
+ if (!isNotNullish(args.orderBy)) {
3569
+ throw new Error("Negative take requires orderBy for deterministic results");
3570
+ }
3571
+ return __spreadProps(__spreadValues({}, args), {
3572
+ take: Math.abs(args.take),
3573
+ orderBy: reverseOrderByInput(args.orderBy)
3574
+ });
3575
+ }
3576
+ function normalizeArgsForDialect(dialect, args, model) {
3577
+ if (dialect !== "postgres") return args;
3578
+ return applyPostgresDistinctOrderBy(args);
3579
+ }
3580
+ function buildCursorClauseIfAny(input) {
3581
+ const { cursor, orderBy, tableName, alias, params, dialect } = input;
3582
+ if (!isNotNullish(cursor)) return void 0;
3583
+ return buildCursorCondition(
3584
+ cursor,
3585
+ orderBy,
3586
+ tableName,
3587
+ alias,
3588
+ params,
3589
+ dialect
3590
+ );
3591
+ }
3592
+ function buildSelectSpec(input) {
3593
+ const {
3594
+ method,
3595
+ normalizedArgs,
3596
+ model,
3597
+ schemas,
3598
+ tableName,
3599
+ alias,
3600
+ whereResult,
3601
+ dialect
3602
+ } = input;
3603
+ const selectFields = buildSelectFields(
3604
+ { select: normalizedArgs.select },
3605
+ model,
3606
+ alias
3607
+ );
3608
+ const orderByClause = buildOrderByClause(normalizedArgs, alias, dialect);
3609
+ const { take, skip, cursor } = getPaginationParams(method, normalizedArgs);
3610
+ const params = createParamStoreFrom(
3611
+ whereResult.params,
3612
+ whereResult.paramMappings,
3613
+ whereResult.nextParamIndex
3614
+ );
3615
+ const includes = buildIncludeSql(
3616
+ normalizedArgs,
3617
+ model,
3618
+ schemas,
3619
+ alias,
3620
+ params,
3621
+ dialect
3622
+ );
3623
+ const cursorClause = buildCursorClauseIfAny({
3624
+ cursor,
3625
+ orderBy: normalizedArgs.orderBy,
3626
+ tableName,
3627
+ alias,
3628
+ params,
3629
+ dialect
3630
+ });
3631
+ return {
3632
+ select: selectFields,
3633
+ includes,
3634
+ from: { table: tableName, alias },
3635
+ whereClause: whereResult.clause,
3636
+ whereJoins: whereResult.joins,
3637
+ orderBy: orderByClause,
3638
+ pagination: { take, skip },
3639
+ distinct: normalizedArgs.distinct,
3640
+ method,
3641
+ cursorClause,
3642
+ params,
3643
+ dialect,
3644
+ model,
3645
+ schemas,
3646
+ args: normalizedArgs
3647
+ };
3648
+ }
3649
+ function buildSelectSql(input) {
3650
+ const { method, args, model, schemas, from, whereResult, dialect } = input;
3651
+ assertSafeAlias(from.alias);
3652
+ assertSafeTableRef(from.tableName);
3653
+ const dialectToUse = resolveDialect(dialect);
3654
+ const argsForSql = normalizeArgsForNegativeTake(method, args);
3655
+ const normalizedArgs = normalizeArgsForDialect(
3656
+ dialectToUse,
3657
+ argsForSql);
3658
+ validateDistinct(model, normalizedArgs.distinct);
3659
+ validateOrderBy(model, normalizedArgs.orderBy);
3660
+ validateCursor(model, normalizedArgs.cursor);
3661
+ const spec = buildSelectSpec({
3662
+ method,
3663
+ normalizedArgs,
3664
+ model,
3665
+ schemas,
3666
+ tableName: from.tableName,
3667
+ alias: from.alias,
3668
+ whereResult,
3669
+ dialect: dialectToUse
3670
+ });
3671
+ return constructFinalSql(spec);
3672
+ }
3673
+ var MODEL_FIELD_CACHE = /* @__PURE__ */ new WeakMap();
3674
+ var NUMERIC_TYPES = /* @__PURE__ */ new Set(["Int", "Float", "Decimal", "BigInt"]);
3675
+ var AGGREGATES = [
3676
+ ["_sum", "SUM"],
3677
+ ["_avg", "AVG"],
3678
+ ["_min", "MIN"],
3679
+ ["_max", "MAX"]
3680
+ ];
3681
+ var COMPARISON_OPS = {
3682
+ [Ops.EQUALS]: "=",
3683
+ [Ops.NOT]: "<>",
3684
+ [Ops.GT]: ">",
3685
+ [Ops.GTE]: ">=",
3686
+ [Ops.LT]: "<",
3687
+ [Ops.LTE]: "<="
3688
+ };
3689
+ function getModelFieldMap(model) {
3690
+ const cached = MODEL_FIELD_CACHE.get(model);
3691
+ if (cached) return cached;
3692
+ const m = /* @__PURE__ */ new Map();
3693
+ for (const f of model.fields) {
3694
+ m.set(f.name, { name: f.name, type: f.type, isRelation: !!f.isRelation });
3695
+ }
3696
+ MODEL_FIELD_CACHE.set(model, m);
3697
+ return m;
3698
+ }
3699
+ function isTruthySelection(v) {
3700
+ return v === true;
3701
+ }
3702
+ function aggExprForField(aggKey, field, alias) {
3703
+ if (aggKey === "_count") {
3704
+ return field === "_all" ? `COUNT(*)` : `COUNT(${col(alias, field)})`;
3705
+ }
3706
+ if (field === "_all") {
3707
+ throw new Error(`'${aggKey}' does not support '_all'`);
3708
+ }
3709
+ if (aggKey === "_sum") return `SUM(${col(alias, field)})`;
3710
+ if (aggKey === "_avg") return `AVG(${col(alias, field)})`;
3711
+ if (aggKey === "_min") return `MIN(${col(alias, field)})`;
3712
+ return `MAX(${col(alias, field)})`;
3713
+ }
3714
+ function buildComparisonOp(op) {
3715
+ const sqlOp = COMPARISON_OPS[op];
3716
+ if (!sqlOp) {
3717
+ throw new Error(`Unsupported HAVING operator: ${op}`);
3718
+ }
3719
+ return sqlOp;
3720
+ }
3721
+ function addHavingParam(params, op, value) {
3722
+ return addAutoScoped(params, value, `having.${op}`);
3723
+ }
3724
+ function normalizeLogicalValue2(operator, value) {
3725
+ if (Array.isArray(value)) {
3726
+ const out = [];
3727
+ for (const v of value) {
3728
+ if (!isPlainObject(v)) {
3729
+ throw new Error(`${operator} entries must be objects in HAVING`);
3730
+ }
3731
+ out.push(v);
3732
+ }
3733
+ return out;
3734
+ }
3735
+ if (isPlainObject(value)) {
3736
+ return [value];
3737
+ }
3738
+ throw new Error(`${operator} must be an object or array of objects in HAVING`);
3739
+ }
3740
+ function assertScalarField2(model, fieldName, ctx) {
3741
+ const m = getModelFieldMap(model);
3742
+ const field = m.get(fieldName);
3743
+ if (!field) {
3744
+ throw new Error(
3745
+ `${ctx} references unknown field '${fieldName}' on model ${model.name}. Available fields: ${model.fields.map((f) => f.name).join(", ")}`
3746
+ );
3747
+ }
3748
+ if (field.isRelation) {
3749
+ throw new Error(`${ctx} does not support relation field '${fieldName}'`);
3750
+ }
3751
+ return { name: field.name, type: field.type };
3752
+ }
3753
+ function assertAggregateFieldType(aggKey, fieldType, fieldName, modelName) {
3754
+ const baseType = fieldType.replace(/\[\]|\?/g, "");
3755
+ if ((aggKey === "_sum" || aggKey === "_avg") && !NUMERIC_TYPES.has(baseType)) {
3756
+ throw new Error(
3757
+ `Cannot use ${aggKey} on non-numeric field '${fieldName}' (type: ${fieldType}) on model ${modelName}`
3758
+ );
3759
+ }
3760
+ }
3761
+ function buildNullComparison(expr, op) {
3762
+ if (op === Ops.EQUALS) return `${expr} ${SQL_TEMPLATES.IS_NULL}`;
3763
+ if (op === Ops.NOT) return `${expr} ${SQL_TEMPLATES.IS_NOT_NULL}`;
3764
+ throw new Error(`Operator '${op}' doesn't support null in HAVING`);
3765
+ }
3766
+ function buildInComparison(expr, op, val, params, dialect) {
3767
+ if (schemaParser.isDynamicParameter(val)) {
3768
+ const placeholder2 = addHavingParam(params, op, val);
3769
+ return op === Ops.IN ? inArray(expr, placeholder2, dialect) : notInArray(expr, placeholder2, dialect);
3770
+ }
3771
+ if (!Array.isArray(val)) {
3772
+ throw new Error(`HAVING '${op}' requires array value`);
3773
+ }
3774
+ if (val.length === 0) {
3775
+ return op === Ops.IN ? "0=1" : "1=1";
3776
+ }
3777
+ const paramValue = prepareArrayParam(val, dialect);
3778
+ const placeholder = params.add(paramValue);
3779
+ return op === Ops.IN ? inArray(expr, placeholder, dialect) : notInArray(expr, placeholder, dialect);
3780
+ }
3781
+ function buildBinaryComparison(expr, op, val, params) {
3782
+ const sqlOp = buildComparisonOp(op);
3783
+ const placeholder = addHavingParam(params, op, val);
3784
+ return `${expr} ${sqlOp} ${placeholder}`;
3785
+ }
3786
+ function buildSimpleComparison(expr, op, val, params, dialect) {
3787
+ if (val === null) return buildNullComparison(expr, op);
3788
+ if (op === Ops.NOT && isPlainObject(val)) {
3789
+ return buildNotComposite(
3790
+ expr,
3791
+ val,
3792
+ params,
3793
+ dialect,
3794
+ buildSimpleComparison,
3795
+ SQL_SEPARATORS.CONDITION_AND
3796
+ );
3797
+ }
3798
+ if (op === Ops.IN || op === Ops.NOT_IN) {
3799
+ return buildInComparison(expr, op, val, params, dialect);
3800
+ }
3801
+ return buildBinaryComparison(expr, op, val, params);
3802
+ }
3803
+ function isLogicalKey(key) {
3804
+ return key === LogicalOps.AND || key === LogicalOps.OR || key === LogicalOps.NOT;
3805
+ }
3806
+ function isAggregateKey(key) {
3807
+ return key === "_count" || key === "_sum" || key === "_avg" || key === "_min" || key === "_max";
3808
+ }
3809
+ function negateClauses(subClauses) {
3810
+ if (subClauses.length === 1) return `${SQL_TEMPLATES.NOT} ${subClauses[0]}`;
3811
+ return `${SQL_TEMPLATES.NOT} (${subClauses.join(SQL_SEPARATORS.CONDITION_AND)})`;
3812
+ }
3813
+ function combineLogical(key, subClauses) {
3814
+ if (key === LogicalOps.NOT) return negateClauses(subClauses);
3815
+ return subClauses.join(` ${key} `);
3816
+ }
3817
+ function buildLogicalClause2(key, value, alias, params, dialect, model) {
3818
+ const items = normalizeLogicalValue2(key, value);
3819
+ const subClauses = [];
3820
+ for (const it of items) {
3821
+ const c = buildHavingNode(it, alias, params, dialect, model);
3822
+ if (c && c !== "") subClauses.push(`(${c})`);
3823
+ }
3824
+ if (subClauses.length === 0) return "";
3825
+ return combineLogical(key, subClauses);
3826
+ }
3827
+ function buildHavingEntry(key, value, alias, params, dialect, model) {
3828
+ if (isLogicalKey(key)) {
3829
+ const logical = buildLogicalClause2(
3830
+ key,
3831
+ value,
3832
+ alias,
3833
+ params,
3834
+ dialect,
3835
+ model
3836
+ );
3837
+ return logical ? [logical] : [];
3838
+ }
3839
+ if (isAggregateKey(key)) {
3840
+ return buildHavingForAggregateFirstShape(
3841
+ key,
3842
+ value,
3843
+ alias,
3844
+ params,
3845
+ dialect,
3846
+ model
3847
+ );
3848
+ }
3849
+ return buildHavingForFieldFirstShape(
3850
+ key,
3851
+ value,
3852
+ alias,
3853
+ params,
3854
+ dialect,
3855
+ model
3856
+ );
3857
+ }
3858
+ function buildHavingNode(node, alias, params, dialect, model) {
3859
+ const clauses = [];
3860
+ for (const [key, value] of Object.entries(node)) {
3861
+ const built = buildHavingEntry(key, value, alias, params, dialect, model);
3862
+ for (const c of built) {
3863
+ if (c && c.trim().length > 0) clauses.push(c);
3864
+ }
3865
+ }
3866
+ return clauses.join(SQL_SEPARATORS.CONDITION_AND);
3867
+ }
3868
+ function assertHavingAggTarget(aggKey, field, model) {
3869
+ if (field === "_all") {
3870
+ if (aggKey !== "_count") {
3871
+ throw new Error(`HAVING '${aggKey}' does not support '_all'`);
3872
+ }
3873
+ return;
3874
+ }
3875
+ const f = assertScalarField2(model, field, "HAVING");
3876
+ assertAggregateFieldType(aggKey, f.type, f.name, model.name);
3877
+ }
3878
+ function buildHavingOpsForExpr(expr, filter, params, dialect) {
3879
+ const out = [];
3880
+ for (const [op, val] of Object.entries(filter)) {
3881
+ if (op === "mode") continue;
3882
+ const built = buildSimpleComparison(expr, op, val, params, dialect);
3883
+ if (built && built.trim().length > 0) out.push(built);
3884
+ }
3885
+ return out;
3886
+ }
3887
+ function buildHavingForAggregateFirstShape(aggKey, target, alias, params, dialect, model) {
3888
+ if (!isPlainObject(target)) return [];
3889
+ const out = [];
3890
+ for (const [field, filter] of Object.entries(target)) {
3891
+ assertHavingAggTarget(aggKey, field, model);
3892
+ if (!isPlainObject(filter) || Object.keys(filter).length === 0) continue;
3893
+ const expr = aggExprForField(aggKey, field, alias);
3894
+ out.push(...buildHavingOpsForExpr(expr, filter, params, dialect));
3895
+ }
3896
+ return out;
3897
+ }
3898
+ function buildHavingForFieldFirstShape(fieldName, target, alias, params, dialect, model) {
3899
+ if (!isPlainObject(target)) return [];
3900
+ const field = assertScalarField2(model, fieldName, "HAVING");
3901
+ const out = [];
3902
+ const obj = target;
3903
+ const keys = ["_count", "_sum", "_avg", "_min", "_max"];
3904
+ for (const aggKey of keys) {
3905
+ const aggFilter = obj[aggKey];
3906
+ if (!isPlainObject(aggFilter)) continue;
3907
+ assertAggregateFieldType(aggKey, field.type, field.name, model.name);
3908
+ const entries = Object.entries(aggFilter);
3909
+ if (entries.length === 0) continue;
3910
+ const expr = aggExprForField(aggKey, fieldName, alias);
3911
+ for (const [op, val] of entries) {
3912
+ if (op === "mode") continue;
3913
+ const built = buildSimpleComparison(expr, op, val, params, dialect);
3914
+ if (built && built.trim().length > 0) out.push(built);
3915
+ }
3916
+ }
3917
+ return out;
3918
+ }
3919
+ function buildHavingClause(having, alias, params, model, dialect) {
3920
+ if (!isNotNullish(having)) return "";
3921
+ const d = dialect != null ? dialect : getGlobalDialect();
3922
+ if (!isPlainObject(having)) return "";
3923
+ return buildHavingNode(having, alias, params, d, model);
3924
+ }
3925
+ function normalizeCountArg(v) {
3926
+ if (!isNotNullish(v)) return void 0;
3927
+ if (v === true) return true;
3928
+ if (isPlainObject(v)) return v;
3929
+ return void 0;
3930
+ }
3931
+ function pushCountAllField(fields) {
3932
+ fields.push(
3933
+ `${SQL_TEMPLATES.COUNT_ALL} ${SQL_TEMPLATES.AS} ${quote("_count._all")}`
3934
+ );
3935
+ }
3936
+ function assertCountableScalarField(fieldMap, model, fieldName) {
3937
+ const field = fieldMap.get(fieldName);
3938
+ if (!field) {
3939
+ throw new Error(
3940
+ `Field '${fieldName}' does not exist on model ${model.name}`
3941
+ );
3942
+ }
3943
+ if (field.isRelation) {
3944
+ throw new Error(
3945
+ `Cannot use _count on relation field '${fieldName}' on model ${model.name}`
3946
+ );
3947
+ }
3948
+ }
3949
+ function pushCountField(fields, alias, fieldName) {
3950
+ const outAlias = `_count.${fieldName}`;
3951
+ fields.push(
3952
+ `COUNT(${col(alias, fieldName)}) ${SQL_TEMPLATES.AS} ${quote(outAlias)}`
3953
+ );
3954
+ }
3955
+ function addCountFields(fields, countArg, alias, model, fieldMap) {
3956
+ if (!isNotNullish(countArg)) return;
3957
+ if (countArg === true) {
3958
+ pushCountAllField(fields);
3959
+ return;
3960
+ }
3961
+ if (!isPlainObject(countArg)) return;
3962
+ if (countArg._all === true) {
3963
+ pushCountAllField(fields);
3964
+ }
3965
+ const selected = Object.entries(countArg).filter(
3966
+ ([f, v]) => f !== "_all" && isTruthySelection(v)
3967
+ );
3968
+ for (const [f] of selected) {
3969
+ assertCountableScalarField(fieldMap, model, f);
3970
+ pushCountField(fields, alias, f);
3971
+ }
3972
+ }
3973
+ function getAggregateSelectionObject(args, agg) {
3974
+ const obj = args[agg];
3975
+ return isPlainObject(obj) ? obj : void 0;
3976
+ }
3977
+ function assertAggregatableScalarField(fieldMap, model, agg, fieldName) {
3978
+ const field = fieldMap.get(fieldName);
3979
+ if (!field) {
3980
+ throw new Error(
3981
+ `Field '${fieldName}' does not exist on model ${model.name}`
3982
+ );
3983
+ }
3984
+ if (field.isRelation) {
3985
+ throw new Error(
3986
+ `Cannot use ${agg} on relation field '${fieldName}' on model ${model.name}`
3987
+ );
3988
+ }
3989
+ return field;
3990
+ }
3991
+ function pushAggregateFieldSql(fields, aggFn, alias, agg, fieldName) {
3992
+ const outAlias = `${agg}.${fieldName}`;
3993
+ fields.push(
3994
+ `${aggFn}(${col(alias, fieldName)}) ${SQL_TEMPLATES.AS} ${quote(outAlias)}`
3995
+ );
3996
+ }
3997
+ function addAggregateFields(fields, args, alias, model, fieldMap) {
3998
+ for (const [agg, aggFn] of AGGREGATES) {
3999
+ const obj = getAggregateSelectionObject(args, agg);
4000
+ if (!obj) continue;
4001
+ for (const [fieldName, selection] of Object.entries(obj)) {
4002
+ if (fieldName === "_all")
4003
+ throw new Error(`'${agg}' does not support '_all'`);
4004
+ if (!isTruthySelection(selection)) continue;
4005
+ const field = assertAggregatableScalarField(
4006
+ fieldMap,
4007
+ model,
4008
+ agg,
4009
+ fieldName
4010
+ );
4011
+ assertAggregateFieldType(agg, field.type, fieldName, model.name);
4012
+ pushAggregateFieldSql(fields, aggFn, alias, agg, fieldName);
4013
+ }
4014
+ }
4015
+ }
4016
+ function buildAggregateFields(args, alias, model) {
4017
+ const fields = [];
4018
+ const fieldMap = getModelFieldMap(model);
4019
+ const countArg = normalizeCountArg(args._count);
4020
+ addCountFields(fields, countArg, alias, model, fieldMap);
4021
+ addAggregateFields(fields, args, alias, model, fieldMap);
4022
+ return fields;
4023
+ }
4024
+ function buildAggregateSql(args, whereResult, tableName, alias, model) {
4025
+ assertSafeAlias(alias);
4026
+ assertSafeTableRef(tableName);
4027
+ const aggFields = buildAggregateFields(args, alias, model);
4028
+ if (!isNonEmptyArray(aggFields)) {
4029
+ throw new Error("buildAggregateSql requires at least one aggregate field");
4030
+ }
4031
+ const selectClause = aggFields.join(SQL_SEPARATORS.FIELD_LIST);
4032
+ const whereClause = isValidWhereClause(whereResult.clause) ? `${SQL_TEMPLATES.WHERE} ${whereResult.clause}` : "";
4033
+ const sql = [
4034
+ SQL_TEMPLATES.SELECT,
4035
+ selectClause,
4036
+ SQL_TEMPLATES.FROM,
4037
+ tableName,
4038
+ alias,
4039
+ whereClause
4040
+ ].filter((x) => x && String(x).trim().length > 0).join(" ").trim();
4041
+ validateSelectQuery(sql);
4042
+ validateParamConsistency(sql, whereResult.params);
4043
+ return Object.freeze({
4044
+ sql,
4045
+ params: Object.freeze([...whereResult.params]),
4046
+ paramMappings: Object.freeze([...whereResult.paramMappings])
4047
+ });
4048
+ }
4049
+ function assertGroupByBy(args, model) {
4050
+ if (!isNotNullish(args.by) || !isNonEmptyArray(args.by)) {
4051
+ throw new Error("buildGroupBySql: by is required and cannot be empty");
4052
+ }
4053
+ const byFields = args.by.map((f) => String(f));
4054
+ const bySet = new Set(byFields);
4055
+ if (bySet.size !== byFields.length) {
4056
+ throw new Error("buildGroupBySql: by must not contain duplicates");
4057
+ }
4058
+ const modelFieldMap = getModelFieldMap(model);
4059
+ for (const f of byFields) {
4060
+ const field = modelFieldMap.get(f);
4061
+ if (!field) {
4062
+ throw new Error(
4063
+ `groupBy.by references unknown field '${f}' on model ${model.name}`
4064
+ );
4065
+ }
4066
+ if (field.isRelation) {
4067
+ throw new Error(
4068
+ `groupBy.by does not support relation field '${f}' on model ${model.name}`
4069
+ );
4070
+ }
4071
+ }
4072
+ return byFields;
4073
+ }
4074
+ function buildGroupBySelectParts(args, alias, model, byFields) {
4075
+ const groupCols = byFields.map((f) => col(alias, f));
4076
+ const groupFields = groupCols.join(SQL_SEPARATORS.FIELD_LIST);
4077
+ const aggFields = buildAggregateFields(args, alias, model);
4078
+ const selectFields = isNonEmptyArray(aggFields) ? groupCols.concat(aggFields).join(SQL_SEPARATORS.FIELD_LIST) : groupCols.join(SQL_SEPARATORS.FIELD_LIST);
4079
+ return { groupCols, groupFields, selectFields };
4080
+ }
4081
+ function buildGroupByHaving(args, alias, params, model, dialect) {
4082
+ if (!isNotNullish(args.having)) return "";
4083
+ if (!isPlainObject(args.having)) return "";
4084
+ const h = buildHavingClause(args.having, alias, params, model, dialect);
4085
+ if (!h || h.trim().length === 0) return "";
4086
+ return `${SQL_TEMPLATES.HAVING} ${h}`;
4087
+ }
4088
+ function buildGroupBySql(args, whereResult, tableName, alias, model, dialect) {
4089
+ assertSafeAlias(alias);
4090
+ assertSafeTableRef(tableName);
4091
+ const byFields = assertGroupByBy(args, model);
4092
+ const d = getGlobalDialect();
4093
+ const params = createParamStore(whereResult.nextParamIndex);
4094
+ const { groupFields, selectFields } = buildGroupBySelectParts(
4095
+ args,
4096
+ alias,
4097
+ model,
4098
+ byFields
4099
+ );
4100
+ const havingClause = buildGroupByHaving(args, alias, params, model, d);
4101
+ const whereClause = isValidWhereClause(whereResult.clause) ? `${SQL_TEMPLATES.WHERE} ${whereResult.clause}` : "";
4102
+ const sql = [
4103
+ SQL_TEMPLATES.SELECT,
4104
+ selectFields,
4105
+ SQL_TEMPLATES.FROM,
4106
+ tableName,
4107
+ alias,
4108
+ whereClause,
4109
+ SQL_TEMPLATES.GROUP_BY,
4110
+ groupFields,
4111
+ havingClause
4112
+ ].filter((x) => x && String(x).trim().length > 0).join(" ").trim();
4113
+ const snapshot = params.snapshot();
4114
+ validateSelectQuery(sql);
4115
+ validateParamConsistency(sql, [...whereResult.params, ...snapshot.params]);
4116
+ return Object.freeze({
4117
+ sql,
4118
+ params: Object.freeze([...whereResult.params, ...snapshot.params]),
4119
+ paramMappings: Object.freeze([
4120
+ ...whereResult.paramMappings,
4121
+ ...snapshot.mappings
4122
+ ])
4123
+ });
4124
+ }
4125
+ function buildCountSql(whereResult, tableName, alias, skip, dialect) {
4126
+ assertSafeAlias(alias);
4127
+ assertSafeTableRef(tableName);
4128
+ const d = getGlobalDialect();
4129
+ const whereClause = isValidWhereClause(whereResult.clause) ? `${SQL_TEMPLATES.WHERE} ${whereResult.clause}` : "";
4130
+ const params = createParamStore(whereResult.nextParamIndex);
4131
+ const baseSubSelect = [
4132
+ SQL_TEMPLATES.SELECT,
4133
+ "1",
4134
+ SQL_TEMPLATES.FROM,
4135
+ tableName,
4136
+ alias,
4137
+ whereClause
4138
+ ].filter((x) => x && String(x).trim().length > 0).join(" ").trim();
4139
+ const normalizedSkip = normalizeSkipLike(skip);
4140
+ const subSelect = applyCountSkip(baseSubSelect, normalizedSkip, params, d);
4141
+ const sql = [
4142
+ SQL_TEMPLATES.SELECT,
4143
+ SQL_TEMPLATES.COUNT_ALL,
4144
+ SQL_TEMPLATES.AS,
4145
+ quote("_count._all"),
4146
+ SQL_TEMPLATES.FROM,
4147
+ `(${subSelect})`,
4148
+ SQL_TEMPLATES.AS,
4149
+ `"sub"`
4150
+ ].filter((x) => x && String(x).trim().length > 0).join(" ").trim();
4151
+ validateSelectQuery(sql);
4152
+ const snapshot = params.snapshot();
4153
+ const mergedParams = [...whereResult.params, ...snapshot.params];
4154
+ validateParamConsistency(sql, mergedParams);
4155
+ return Object.freeze({
4156
+ sql,
4157
+ params: Object.freeze(mergedParams),
4158
+ paramMappings: Object.freeze([
4159
+ ...whereResult.paramMappings,
4160
+ ...snapshot.mappings
4161
+ ])
4162
+ });
4163
+ }
4164
+ function applyCountSkip(subSelect, normalizedSkip, params, dialect) {
4165
+ const shouldApply = schemaParser.isDynamicParameter(normalizedSkip) || typeof normalizedSkip === "number" && normalizedSkip > 0;
4166
+ if (!shouldApply) return subSelect;
4167
+ const placeholder = addAutoScoped(params, normalizedSkip, "count.skip");
4168
+ if (dialect === "sqlite") {
4169
+ return `${subSelect} ${SQL_TEMPLATES.LIMIT} -1 ${SQL_TEMPLATES.OFFSET} ${placeholder}`;
4170
+ }
4171
+ return `${subSelect} ${SQL_TEMPLATES.OFFSET} ${placeholder}`;
4172
+ }
4173
+ function safeAlias(input) {
4174
+ const raw = String(input).toLowerCase();
4175
+ const cleaned = raw.replace(/[^a-z0-9_]/g, "_");
4176
+ const startsOk = /^[a-z_]/.test(cleaned);
4177
+ let base = startsOk ? cleaned : `_${cleaned}`;
4178
+ base = base.length > 0 ? base : "_t";
4179
+ if (SQL_RESERVED_WORDS.has(base)) {
4180
+ base = `_${base}`;
4181
+ }
4182
+ return base;
4183
+ }
4184
+ function isPrismaMethod(v) {
4185
+ return v === "findMany" || v === "findFirst" || v === "findUnique" || v === "aggregate" || v === "groupBy" || v === "count";
4186
+ }
4187
+ function getMethodFromProcessed(processed) {
4188
+ const maybe = processed == null ? void 0 : processed.method;
4189
+ if (isPrismaMethod(maybe)) return maybe;
4190
+ return "findMany";
4191
+ }
4192
+ function buildSqlResult(args) {
4193
+ const {
4194
+ method,
4195
+ processed,
4196
+ whereResult,
4197
+ tableName,
4198
+ alias,
4199
+ modelDef,
4200
+ schemaModels,
4201
+ dialect
4202
+ } = args;
4203
+ if (method === "aggregate") {
4204
+ return buildAggregateSql(processed, whereResult, tableName, alias, modelDef);
4205
+ }
4206
+ if (method === "groupBy") {
4207
+ return buildGroupBySql(processed, whereResult, tableName, alias, modelDef);
4208
+ }
4209
+ if (method === "count") {
4210
+ return buildCountSql(
4211
+ whereResult,
4212
+ tableName,
4213
+ alias,
4214
+ processed.skip
4215
+ );
4216
+ }
4217
+ return buildSelectSql({
4218
+ method,
4219
+ args: processed,
4220
+ model: modelDef,
4221
+ schemas: schemaModels,
4222
+ from: { tableName, alias },
4223
+ whereResult,
4224
+ dialect
4225
+ });
4226
+ }
4227
+ function normalizeSqlAndMappingsForDialect(sql, paramMappings, dialect) {
4228
+ if (dialect !== "sqlite") return { sql, paramMappings };
4229
+ const byIndex = /* @__PURE__ */ new Map();
4230
+ for (const m of paramMappings) byIndex.set(m.index, m);
4231
+ const placeholderPositions = [];
4232
+ const normalizedSql = sql.replace(/\$(\d+)/g, (_, num) => {
4233
+ placeholderPositions.push(parseInt(num, 10));
4234
+ return "?";
4235
+ });
4236
+ const expandedMappings = placeholderPositions.map(
4237
+ (originalIndex, i) => {
4238
+ const originalMapping = byIndex.get(originalIndex);
4239
+ if (!originalMapping) {
4240
+ throw new Error(
4241
+ `CRITICAL: No mapping found for parameter $${originalIndex}`
4242
+ );
4243
+ }
4244
+ return {
4245
+ index: i + 1,
4246
+ value: originalMapping.value,
4247
+ dynamicName: originalMapping.dynamicName
4248
+ };
4249
+ }
4250
+ );
4251
+ return { sql: normalizedSql, paramMappings: expandedMappings };
4252
+ }
4253
+ function buildParamsFromMappings(mappings) {
4254
+ const sorted = [...mappings].sort((a, b) => a.index - b.index);
4255
+ return sorted.reduce(
4256
+ (acc, m) => {
4257
+ if (m.dynamicName !== void 0) {
4258
+ acc.dynamicKeys.push(m.dynamicName);
4259
+ return acc;
4260
+ }
4261
+ if (m.value !== void 0) {
4262
+ acc.staticParams.push(m.value);
4263
+ return acc;
4264
+ }
4265
+ throw new Error(
4266
+ `CRITICAL: ParamMap ${m.index} has neither dynamicName nor value`
4267
+ );
4268
+ },
4269
+ { staticParams: [], dynamicKeys: [] }
4270
+ );
4271
+ }
4272
+ function resolveModelContext(directive) {
4273
+ const { model, datamodel } = directive.context;
4274
+ const schemaModels = schemaParser.convertDMMFToModels(datamodel);
4275
+ const modelDef = getModelByName(schemaModels, model.name);
4276
+ if (!modelDef) throw new Error(`Model ${model.name} not found in schema`);
4277
+ return { schemaModels, modelDef };
4278
+ }
4279
+ function buildMainTableAndAlias(args) {
4280
+ const { modelDef, dialect } = args;
4281
+ const baseName = modelDef.tableName || modelDef.name;
4282
+ return {
4283
+ tableName: buildTableReference(
4284
+ SQL_TEMPLATES.PUBLIC_SCHEMA,
4285
+ baseName,
4286
+ dialect
4287
+ ),
4288
+ alias: safeAlias(`${baseName}_main`)
4289
+ };
4290
+ }
4291
+ function buildMainWhere(args) {
4292
+ const { processed, alias, schemaModels, modelDef, dialect } = args;
4293
+ return buildWhereClause(processed.where || {}, {
4294
+ alias,
4295
+ schemaModels,
4296
+ model: modelDef,
4297
+ path: ["where"],
4298
+ isSubquery: false,
4299
+ dialect
4300
+ });
4301
+ }
4302
+ function buildAndNormalizeSql(args) {
4303
+ const {
4304
+ processed,
4305
+ whereResult,
4306
+ tableName,
4307
+ alias,
4308
+ modelDef,
4309
+ schemaModels,
4310
+ dialect
4311
+ } = args;
4312
+ const method = getMethodFromProcessed(processed);
4313
+ const sqlResult = buildSqlResult({
4314
+ method,
4315
+ processed,
4316
+ whereResult,
4317
+ tableName,
4318
+ alias,
4319
+ modelDef,
4320
+ schemaModels,
4321
+ dialect
4322
+ });
4323
+ return normalizeSqlAndMappingsForDialect(
4324
+ sqlResult.sql,
4325
+ sqlResult.paramMappings,
4326
+ dialect
4327
+ );
4328
+ }
4329
+ function finalizeDirective(args) {
4330
+ const { directive, normalizedSql, normalizedMappings } = args;
4331
+ validateSqlPositions(normalizedSql, normalizedMappings, getGlobalDialect());
4332
+ const { staticParams, dynamicKeys } = buildParamsFromMappings(normalizedMappings);
4333
+ return {
4334
+ header: directive.header,
4335
+ sql: normalizedSql,
4336
+ staticParams,
4337
+ dynamicKeys,
4338
+ paramMappings: normalizedMappings,
4339
+ originalDirective: directive
4340
+ };
4341
+ }
4342
+ function generateSQL(directive) {
4343
+ const { query } = directive;
4344
+ const { schemaModels, modelDef } = resolveModelContext(directive);
4345
+ const dialect = getGlobalDialect();
4346
+ const { tableName, alias } = buildMainTableAndAlias({ modelDef, dialect });
4347
+ const whereResult = buildMainWhere({
4348
+ processed: query.processed,
4349
+ alias,
4350
+ schemaModels,
4351
+ modelDef,
4352
+ dialect
4353
+ });
4354
+ const normalized = buildAndNormalizeSql({
4355
+ processed: query.processed,
4356
+ whereResult,
4357
+ tableName,
4358
+ alias,
4359
+ modelDef,
4360
+ schemaModels,
4361
+ dialect
4362
+ });
4363
+ return finalizeDirective({
4364
+ directive,
4365
+ normalizedSql: normalized.sql,
4366
+ normalizedMappings: normalized.paramMappings
4367
+ });
4368
+ }
4369
+ function generateSQL2(directive) {
4370
+ return generateSQL(directive);
4371
+ }
4372
+
4373
+ // src/code-emitter.ts
4374
+ function generateClient(options) {
4375
+ return __async(this, null, function* () {
4376
+ const { datamodel, outputDir, config } = options;
4377
+ setGlobalDialect(config.dialect);
4378
+ const models = schemaParser.convertDMMFToModels(datamodel);
4379
+ const directiveResults = schemaParser.processAllDirectives(
4380
+ datamodel.models,
4381
+ datamodel,
4382
+ {
4383
+ skipInvalid: config.skipInvalid
4384
+ }
4385
+ );
4386
+ const queries = /* @__PURE__ */ new Map();
4387
+ for (const [modelName, result] of directiveResults) {
4388
+ if (result.directives.length === 0) continue;
4389
+ const modelQueries = /* @__PURE__ */ new Map();
4390
+ for (const directive of result.directives) {
4391
+ try {
4392
+ const sqlDirective = generateSQL2(directive);
4393
+ const queryKey = JSON.stringify(
4394
+ directive.query.original,
4395
+ Object.keys(directive.query.original).sort()
4396
+ );
4397
+ modelQueries.set(queryKey, {
4398
+ sql: sqlDirective.sql,
4399
+ params: sqlDirective.staticParams,
4400
+ dynamicKeys: sqlDirective.dynamicKeys,
4401
+ paramMappings: sqlDirective.paramMappings
4402
+ });
4403
+ } catch (error) {
4404
+ if (!config.skipInvalid) throw error;
4405
+ }
4406
+ }
4407
+ if (modelQueries.size > 0) {
4408
+ queries.set(modelName, modelQueries);
4409
+ }
4410
+ }
4411
+ yield promises.mkdir(outputDir, { recursive: true });
4412
+ const code = generateCode(models, queries, config.dialect);
4413
+ yield promises.writeFile(path.join(outputDir, "index.ts"), code);
4414
+ const totalQueries = Array.from(queries.values()).reduce(
4415
+ (sum, m) => sum + m.size,
4416
+ 0
4417
+ );
4418
+ console.log(`\u2713 Generated ${queries.size} models, ${totalQueries} queries`);
4419
+ });
4420
+ }
4421
+ function generateCode(models, queries, dialect) {
4422
+ return `// Generated by @prisma-sql/generator - DO NOT EDIT
4423
+ import { buildSQL, transformQueryResults, type PrismaMethod } from 'prisma-sql'
4424
+
4425
+ const MODELS = ${JSON.stringify(models, null, 2)}
4426
+
4427
+ const QUERIES = ${formatQueries(queries)}
4428
+
4429
+ const DIALECT = ${JSON.stringify(dialect)}
4430
+
4431
+ function normalizeQuery(args: any): string {
4432
+ if (!args) return '{}'
4433
+ return JSON.stringify(args, Object.keys(args).sort())
4434
+ }
4435
+
4436
+ function extractDynamicParams(args: any, dynamicKeys: string[]): unknown[] {
4437
+ const params: unknown[] = []
4438
+
4439
+ for (const key of dynamicKeys) {
4440
+ const parts = key.split('.')
4441
+ let value: any = args
4442
+
4443
+ for (const part of parts) {
4444
+ if (value === null || value === undefined) break
4445
+ value = value[part]
4446
+ }
4447
+
4448
+ if (value === undefined) {
4449
+ throw new Error(\`Missing required parameter: \${key}\`)
4450
+ }
4451
+
4452
+ params.push(value)
4453
+ }
4454
+
4455
+ return params
4456
+ }
4457
+
4458
+ async function executeQuery(client: any, sql: string, params: unknown[]): Promise<unknown[]> {
4459
+ if (DIALECT === 'postgres') {
4460
+ return await client.unsafe(sql, params)
4461
+ }
4462
+
4463
+ const stmt = client.prepare(sql)
4464
+
4465
+ if (sql.toUpperCase().includes('COUNT(*) AS')) {
4466
+ return [stmt.get(...params)]
4467
+ }
4468
+
4469
+ return stmt.all(...params)
4470
+ }
4471
+
4472
+ export function createExtension(config: {
4473
+ postgres?: any
4474
+ sqlite?: any
4475
+ debug?: boolean
4476
+ onQuery?: (info: {
4477
+ model: string
4478
+ method: string
4479
+ sql: string
4480
+ params: unknown[]
4481
+ duration: number
4482
+ prebaked: boolean
4483
+ }) => void
4484
+ }) {
4485
+ const { postgres, sqlite, debug, onQuery } = config
4486
+
4487
+ if (!postgres && !sqlite) {
4488
+ throw new Error('Extension requires postgres or sqlite client')
4489
+ }
4490
+
4491
+ const client = postgres || sqlite
4492
+ const actualDialect = postgres ? 'postgres' : 'sqlite'
4493
+
4494
+ if (actualDialect !== DIALECT) {
4495
+ throw new Error(\`Generated code is for \${DIALECT}, but you provided \${actualDialect}\`)
4496
+ }
4497
+
4498
+ return (prisma: any) => {
4499
+ const handleMethod = async function(this: any, method: PrismaMethod, args: any) {
4500
+ const modelName = this?.name || this?.$name
4501
+ const startTime = Date.now()
4502
+
4503
+ // Try prebaked query first
4504
+ const queryKey = normalizeQuery(args)
4505
+ const prebakedQuery = QUERIES[modelName]?.[queryKey]
4506
+
4507
+ let sql: string
4508
+ let params: unknown[]
4509
+ let prebaked = false
4510
+
4511
+ if (prebakedQuery) {
4512
+ // \u26A1 Use prebaked SQL
4513
+ sql = prebakedQuery.sql
4514
+ params = [...prebakedQuery.params, ...extractDynamicParams(args, prebakedQuery.dynamicKeys)]
4515
+ prebaked = true
4516
+ } else {
4517
+ // \u{1F528} Fall back to runtime generation
4518
+ const model = MODELS.find((m: any) => m.name === modelName)
4519
+
4520
+ if (!model) {
4521
+ return this.$parent[modelName][method](args)
4522
+ }
4523
+
4524
+ const result = buildSQL(model, MODELS, method, args, DIALECT)
4525
+ sql = result.sql
4526
+ params = result.params
4527
+ }
4528
+
4529
+ if (debug) {
4530
+ console.log(\`[\${DIALECT}] \${modelName}.\${method} \${prebaked ? '\u26A1 PREBAKED' : '\u{1F528} RUNTIME'}\`)
4531
+ console.log('SQL:', sql)
4532
+ console.log('Params:', params)
4533
+ }
4534
+
4535
+ const results = await executeQuery(client, sql, params)
4536
+ const duration = Date.now() - startTime
4537
+
4538
+ onQuery?.({
4539
+ model: modelName,
4540
+ method,
4541
+ sql,
4542
+ params,
4543
+ duration,
4544
+ prebaked,
4545
+ })
4546
+
4547
+ return transformQueryResults(method, results)
4548
+ }
4549
+
4550
+ return prisma.$extends({
4551
+ name: 'prisma-sql-generated',
4552
+
4553
+ client: {
4554
+ $original: prisma,
4555
+ },
4556
+
4557
+ model: {
4558
+ $allModels: {
4559
+ async findMany(args: any) {
4560
+ return handleMethod.call(this, 'findMany', args)
4561
+ },
4562
+ async findFirst(args: any) {
4563
+ return handleMethod.call(this, 'findFirst', args)
4564
+ },
4565
+ async findUnique(args: any) {
4566
+ return handleMethod.call(this, 'findUnique', args)
4567
+ },
4568
+ async count(args: any) {
4569
+ return handleMethod.call(this, 'count', args)
4570
+ },
4571
+ async aggregate(args: any) {
4572
+ return handleMethod.call(this, 'aggregate', args)
4573
+ },
4574
+ async groupBy(args: any) {
4575
+ return handleMethod.call(this, 'groupBy', args)
4576
+ },
4577
+ },
4578
+ },
4579
+ })
4580
+ }
4581
+ }
4582
+ `;
4583
+ }
4584
+ function formatQueries(queries) {
4585
+ const entries = [];
4586
+ for (const [modelName, modelQueries] of queries) {
4587
+ const queryEntries = [];
4588
+ for (const [queryKey, query] of modelQueries) {
4589
+ queryEntries.push(` ${JSON.stringify(queryKey)}: {
4590
+ sql: ${JSON.stringify(query.sql)},
4591
+ params: ${JSON.stringify(query.params)},
4592
+ dynamicKeys: ${JSON.stringify(query.dynamicKeys)},
4593
+ paramMappings: ${JSON.stringify(query.paramMappings)},
4594
+ }`);
4595
+ }
4596
+ entries.push(` ${JSON.stringify(modelName)}: {
4597
+ ${queryEntries.join(",\n")}
4598
+ }`);
4599
+ }
4600
+ return `{
4601
+ ${entries.join(",\n")}
4602
+ }`;
4603
+ }
4604
+ var { version } = require_package();
4605
+ function getDialectFromProvider(provider) {
4606
+ const normalized = provider.toLowerCase();
4607
+ if (normalized === "sqlite") return "sqlite";
4608
+ if (normalized === "postgresql" || normalized === "postgres")
4609
+ return "postgres";
4610
+ throw new Error(
4611
+ `Unsupported database provider: ${provider}. Supported: postgresql, postgres, sqlite`
4612
+ );
4613
+ }
4614
+ generatorHelper.generatorHandler({
4615
+ onManifest() {
4616
+ return {
4617
+ version,
4618
+ defaultOutput: "../node_modules/.prisma/client/sql",
4619
+ prettyName: "Prisma SQL Generator",
4620
+ requiresGenerators: ["prisma-client-js"]
4621
+ };
4622
+ },
4623
+ onGenerate(options) {
4624
+ return __async(this, null, function* () {
4625
+ var _a;
4626
+ const { generator, dmmf, datasources } = options;
4627
+ if (!datasources || datasources.length === 0) {
4628
+ throw new Error("No datasource found in schema");
4629
+ }
4630
+ const autoDialect = getDialectFromProvider(datasources[0].provider);
4631
+ const configDialect = generator.config.dialect;
4632
+ const dialect = configDialect || autoDialect;
4633
+ if (configDialect && configDialect !== autoDialect) {
4634
+ internals.logger.warn(
4635
+ `Generator dialect (${configDialect}) differs from datasource provider (${datasources[0].provider}). Using generator config: ${configDialect}`
4636
+ );
4637
+ }
4638
+ const config = {
4639
+ dialect,
4640
+ skipInvalid: generator.config.skipInvalid === "true"
4641
+ };
4642
+ const outputDir = ((_a = generator.output) == null ? void 0 : _a.value) || "../node_modules/.prisma/client/sql";
4643
+ internals.logger.info(`Generating SQL client to ${outputDir}`);
4644
+ internals.logger.info(`Datasource: ${datasources[0].provider}`);
4645
+ internals.logger.info(`Dialect: ${config.dialect}`);
4646
+ internals.logger.info(`Skip invalid: ${config.skipInvalid}`);
4647
+ yield generateClient({
4648
+ datamodel: dmmf.datamodel,
4649
+ outputDir,
4650
+ config
4651
+ });
4652
+ internals.logger.info("\u2713 Generated SQL client successfully");
4653
+ });
4654
+ }
4655
+ });
4656
+ //# sourceMappingURL=generator.cjs.map
4657
+ //# sourceMappingURL=generator.cjs.map