prisma-sql 1.59.0 → 1.60.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,5 +1,4 @@
1
1
  import { convertDMMFToModels, isDynamicParameter, extractDynamicName } from '@dee-wan/schema-parser';
2
- export { convertDMMFToModels } from '@dee-wan/schema-parser';
3
2
 
4
3
  var __defProp = Object.defineProperty;
5
4
  var __defProps = Object.defineProperties;
@@ -7,6 +6,9 @@ var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
7
6
  var __getOwnPropSymbols = Object.getOwnPropertySymbols;
8
7
  var __hasOwnProp = Object.prototype.hasOwnProperty;
9
8
  var __propIsEnum = Object.prototype.propertyIsEnumerable;
9
+ var __typeError = (msg) => {
10
+ throw TypeError(msg);
11
+ };
10
12
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
11
13
  var __spreadValues = (a, b) => {
12
14
  for (var prop in b || (b = {}))
@@ -20,6 +22,18 @@ var __spreadValues = (a, b) => {
20
22
  return a;
21
23
  };
22
24
  var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
25
+ var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg);
26
+ var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
27
+ var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
28
+ var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), member.set(obj, value), value);
29
+ var __privateWrapper = (obj, member, setter, getter) => ({
30
+ set _(value) {
31
+ __privateSet(obj, member, value);
32
+ },
33
+ get _() {
34
+ return __privateGet(obj, member, getter);
35
+ }
36
+ });
23
37
  var __async = (__this, __arguments, generator) => {
24
38
  return new Promise((resolve, reject) => {
25
39
  var fulfilled = (value) => {
@@ -41,155 +55,6 @@ var __async = (__this, __arguments, generator) => {
41
55
  });
42
56
  };
43
57
 
44
- // src/builder/shared/constants.ts
45
- var IS_PRODUCTION = process.env.NODE_ENV === "production";
46
- var SQL_SEPARATORS = Object.freeze({
47
- FIELD_LIST: ", ",
48
- CONDITION_AND: " AND ",
49
- CONDITION_OR: " OR ",
50
- ORDER_BY: ", "
51
- });
52
- var ALIAS_FORBIDDEN_KEYWORDS = /* @__PURE__ */ new Set([
53
- "select",
54
- "from",
55
- "where",
56
- "having",
57
- "order",
58
- "group",
59
- "limit",
60
- "offset",
61
- "join",
62
- "inner",
63
- "left",
64
- "right",
65
- "outer",
66
- "cross",
67
- "full",
68
- "and",
69
- "or",
70
- "not",
71
- "by",
72
- "as",
73
- "on",
74
- "union",
75
- "intersect",
76
- "except",
77
- "case",
78
- "when",
79
- "then",
80
- "else",
81
- "end"
82
- ]);
83
- var SQL_KEYWORDS = /* @__PURE__ */ new Set([
84
- ...ALIAS_FORBIDDEN_KEYWORDS,
85
- "user",
86
- "users",
87
- "table",
88
- "column",
89
- "index",
90
- "values",
91
- "in",
92
- "like",
93
- "between",
94
- "is",
95
- "exists",
96
- "null",
97
- "true",
98
- "false",
99
- "all",
100
- "any",
101
- "some",
102
- "update",
103
- "insert",
104
- "delete",
105
- "create",
106
- "drop",
107
- "alter",
108
- "truncate",
109
- "grant",
110
- "revoke",
111
- "exec",
112
- "execute"
113
- ]);
114
- var SQL_RESERVED_WORDS = SQL_KEYWORDS;
115
- var DEFAULT_WHERE_CLAUSE = "1=1";
116
- var SPECIAL_FIELDS = Object.freeze({
117
- ID: "id"
118
- });
119
- var SQL_TEMPLATES = Object.freeze({
120
- PUBLIC_SCHEMA: "public",
121
- WHERE: "WHERE",
122
- SELECT: "SELECT",
123
- FROM: "FROM",
124
- ORDER_BY: "ORDER BY",
125
- GROUP_BY: "GROUP BY",
126
- HAVING: "HAVING",
127
- LIMIT: "LIMIT",
128
- OFFSET: "OFFSET",
129
- COUNT_ALL: "COUNT(*)",
130
- AS: "AS",
131
- DISTINCT_ON: "DISTINCT ON",
132
- IS_NULL: "IS NULL",
133
- IS_NOT_NULL: "IS NOT NULL",
134
- LIKE: "LIKE",
135
- AND: "AND",
136
- OR: "OR",
137
- NOT: "NOT"
138
- });
139
- var SCHEMA_PREFIXES = Object.freeze({
140
- INTERNAL: "@",
141
- COMMENT: "//"
142
- });
143
- var Ops = Object.freeze({
144
- EQUALS: "equals",
145
- NOT: "not",
146
- GT: "gt",
147
- GTE: "gte",
148
- LT: "lt",
149
- LTE: "lte",
150
- IN: "in",
151
- NOT_IN: "notIn",
152
- CONTAINS: "contains",
153
- STARTS_WITH: "startsWith",
154
- ENDS_WITH: "endsWith",
155
- HAS: "has",
156
- HAS_SOME: "hasSome",
157
- HAS_EVERY: "hasEvery",
158
- IS_EMPTY: "isEmpty",
159
- PATH: "path",
160
- STRING_CONTAINS: "string_contains",
161
- STRING_STARTS_WITH: "string_starts_with",
162
- STRING_ENDS_WITH: "string_ends_with"
163
- });
164
- var LogicalOps = Object.freeze({
165
- AND: "AND",
166
- OR: "OR",
167
- NOT: "NOT"
168
- });
169
- var RelationFilters = Object.freeze({
170
- SOME: "some",
171
- EVERY: "every",
172
- NONE: "none"
173
- });
174
- var Modes = Object.freeze({
175
- INSENSITIVE: "insensitive",
176
- DEFAULT: "default"
177
- });
178
- var Wildcards = Object.freeze({
179
- [Ops.CONTAINS]: (v) => `%${v}%`,
180
- [Ops.STARTS_WITH]: (v) => `${v}%`,
181
- [Ops.ENDS_WITH]: (v) => `%${v}`
182
- });
183
- var REGEX_CACHE = {
184
- VALID_IDENTIFIER: /^[a-z_][a-z0-9_]*$/
185
- };
186
- var LIMITS = Object.freeze({
187
- MAX_QUERY_DEPTH: 50,
188
- MAX_ARRAY_SIZE: 1e4,
189
- MAX_STRING_LENGTH: 1e4,
190
- MAX_HAVING_DEPTH: 50
191
- });
192
-
193
58
  // src/utils/normalize-value.ts
194
59
  var MAX_DEPTH = 20;
195
60
  function normalizeValue(value, seen = /* @__PURE__ */ new WeakSet(), depth = 0) {
@@ -197,45 +62,54 @@ function normalizeValue(value, seen = /* @__PURE__ */ new WeakSet(), depth = 0)
197
62
  throw new Error(`Max normalization depth exceeded (${MAX_DEPTH} levels)`);
198
63
  }
199
64
  if (value instanceof Date) {
200
- const t = value.getTime();
201
- if (!Number.isFinite(t)) {
202
- throw new Error("Invalid Date value in SQL params");
203
- }
204
- return value.toISOString();
65
+ return normalizeDateValue(value);
205
66
  }
206
67
  if (typeof value === "bigint") {
207
68
  return value.toString();
208
69
  }
209
70
  if (Array.isArray(value)) {
210
- const arrRef = value;
211
- if (seen.has(arrRef)) {
212
- throw new Error("Circular reference in SQL params");
213
- }
214
- seen.add(arrRef);
215
- const out = value.map((v) => normalizeValue(v, seen, depth + 1));
216
- seen.delete(arrRef);
217
- return out;
71
+ return normalizeArrayValue(value, seen, depth);
218
72
  }
219
73
  if (value && typeof value === "object") {
220
- if (value instanceof Uint8Array) return value;
221
- if (typeof Buffer !== "undefined" && Buffer.isBuffer(value)) return value;
222
- const proto = Object.getPrototypeOf(value);
223
- const isPlain = proto === Object.prototype || proto === null;
224
- if (!isPlain) return value;
225
- const obj = value;
226
- if (seen.has(obj)) {
227
- throw new Error("Circular reference in SQL params");
228
- }
229
- seen.add(obj);
230
- const out = {};
231
- for (const [k, v] of Object.entries(obj)) {
232
- out[k] = normalizeValue(v, seen, depth + 1);
233
- }
234
- seen.delete(obj);
235
- return out;
74
+ return normalizeObjectValue(value, seen, depth);
236
75
  }
237
76
  return value;
238
77
  }
78
+ function normalizeDateValue(date) {
79
+ const t = date.getTime();
80
+ if (!Number.isFinite(t)) {
81
+ throw new Error("Invalid Date value in SQL params");
82
+ }
83
+ return date.toISOString();
84
+ }
85
+ function normalizeArrayValue(value, seen, depth) {
86
+ const arrRef = value;
87
+ if (seen.has(arrRef)) {
88
+ throw new Error("Circular reference in SQL params");
89
+ }
90
+ seen.add(arrRef);
91
+ const out = value.map((v) => normalizeValue(v, seen, depth + 1));
92
+ seen.delete(arrRef);
93
+ return out;
94
+ }
95
+ function normalizeObjectValue(value, seen, depth) {
96
+ if (value instanceof Uint8Array) return value;
97
+ if (typeof Buffer !== "undefined" && Buffer.isBuffer(value)) return value;
98
+ const proto = Object.getPrototypeOf(value);
99
+ const isPlain = proto === Object.prototype || proto === null;
100
+ if (!isPlain) return value;
101
+ const obj = value;
102
+ if (seen.has(obj)) {
103
+ throw new Error("Circular reference in SQL params");
104
+ }
105
+ seen.add(obj);
106
+ const out = {};
107
+ for (const [k, v] of Object.entries(obj)) {
108
+ out[k] = normalizeValue(v, seen, depth + 1);
109
+ }
110
+ seen.delete(obj);
111
+ return out;
112
+ }
239
113
 
240
114
  // src/sql-builder-dialect.ts
241
115
  var globalDialect = "postgres";
@@ -250,15 +124,6 @@ function setGlobalDialect(dialect) {
250
124
  function getGlobalDialect() {
251
125
  return globalDialect;
252
126
  }
253
- function withDialect(dialect, fn) {
254
- const prev = globalDialect;
255
- globalDialect = dialect;
256
- try {
257
- return fn();
258
- } finally {
259
- globalDialect = prev;
260
- }
261
- }
262
127
  function assertNonEmpty(value, name) {
263
128
  if (!value || value.trim().length === 0) {
264
129
  throw new Error(`${name} is required and cannot be empty`);
@@ -424,94 +289,243 @@ function prepareArrayParam(value, dialect) {
424
289
  if (dialect === "postgres") {
425
290
  return value.map((v) => normalizeValue(v));
426
291
  }
427
- return JSON.stringify(value);
428
- }
429
-
430
- // src/builder/shared/validators/type-guards.ts
431
- function isNotNullish(value) {
432
- return value !== null && value !== void 0;
433
- }
434
- function isNonEmptyString(value) {
435
- return typeof value === "string" && value.trim().length > 0;
436
- }
437
- function isEmptyString(value) {
438
- return typeof value === "string" && value.trim().length === 0;
439
- }
440
- function isNonEmptyArray(value) {
441
- return Array.isArray(value) && value.length > 0;
442
- }
443
- function isEmptyArray(value) {
444
- return Array.isArray(value) && value.length === 0;
445
- }
446
- function isPlainObject(val) {
447
- if (!isNotNullish(val)) return false;
448
- if (Array.isArray(val)) return false;
449
- if (typeof val !== "object") return false;
450
- return Object.prototype.toString.call(val) === "[object Object]";
451
- }
452
- function hasProperty(obj, key) {
453
- return isPlainObject(obj) && key in obj;
454
- }
455
- function isArrayType(t) {
456
- if (!isNotNullish(t)) return false;
457
- const normalized = t.replace(/\?$/, "");
458
- return normalized.endsWith("[]");
459
- }
460
- function isJsonType(t) {
461
- return isNotNullish(t) && (t === "Json" || t === "Json?");
462
- }
463
- function hasValidContent(sql) {
464
- return isNotNullish(sql) && sql.trim().length > 0;
465
- }
466
- function hasRequiredKeywords(sql) {
467
- const upper = sql.toUpperCase();
468
- const hasSelect = upper.includes("SELECT");
469
- const hasFrom = upper.includes("FROM");
470
- return hasSelect && hasFrom && upper.indexOf("SELECT") < upper.indexOf("FROM");
471
- }
472
-
473
- // src/builder/shared/errors.ts
474
- var SqlBuilderError = class extends Error {
475
- constructor(message, code, context) {
476
- super(message);
477
- this.name = "SqlBuilderError";
478
- this.code = code;
479
- this.context = context;
480
- }
481
- };
482
- function createError(message, ctx, code = "VALIDATION_ERROR") {
483
- const parts = [message];
484
- if (isNonEmptyArray(ctx.path)) {
485
- parts.push(`Path: ${ctx.path.join(".")}`);
486
- }
487
- if (isNotNullish(ctx.modelName)) {
488
- parts.push(`Model: ${ctx.modelName}`);
489
- }
490
- if (isNonEmptyArray(ctx.availableFields)) {
491
- parts.push(`Available fields: ${ctx.availableFields.join(", ")}`);
492
- }
493
- return new SqlBuilderError(parts.join("\n"), code, ctx);
292
+ return JSON.stringify(value.map((v) => normalizeValue(v)));
494
293
  }
495
294
 
496
- // src/builder/shared/validators/sql-validators.ts
497
- function isValidWhereClause(clause) {
498
- return isNotNullish(clause) && clause.trim().length > 0 && clause !== DEFAULT_WHERE_CLAUSE;
499
- }
500
- function isEmptyWhere(where) {
501
- if (!isNotNullish(where)) return true;
502
- return Object.keys(where).length === 0;
503
- }
504
- function sqlPreview(sql) {
505
- const s = String(sql);
506
- if (s.length <= 160) return s;
507
- return `${s.slice(0, 160)}...`;
508
- }
509
- function validateSelectQuery(sql) {
510
- if (IS_PRODUCTION) return;
511
- if (!hasValidContent(sql)) {
512
- throw new Error("CRITICAL: Generated empty SQL query");
513
- }
514
- if (!hasRequiredKeywords(sql)) {
295
+ // src/builder/shared/constants.ts
296
+ var IS_PRODUCTION = process.env.NODE_ENV === "production";
297
+ var SQL_SEPARATORS = Object.freeze({
298
+ FIELD_LIST: ", ",
299
+ CONDITION_AND: " AND ",
300
+ CONDITION_OR: " OR ",
301
+ ORDER_BY: ", "
302
+ });
303
+ var ALIAS_FORBIDDEN_KEYWORDS = /* @__PURE__ */ new Set([
304
+ "select",
305
+ "from",
306
+ "where",
307
+ "having",
308
+ "order",
309
+ "group",
310
+ "limit",
311
+ "offset",
312
+ "join",
313
+ "inner",
314
+ "left",
315
+ "right",
316
+ "outer",
317
+ "cross",
318
+ "full",
319
+ "and",
320
+ "or",
321
+ "not",
322
+ "by",
323
+ "as",
324
+ "on",
325
+ "union",
326
+ "intersect",
327
+ "except",
328
+ "case",
329
+ "when",
330
+ "then",
331
+ "else",
332
+ "end"
333
+ ]);
334
+ var SQL_KEYWORDS = /* @__PURE__ */ new Set([
335
+ ...ALIAS_FORBIDDEN_KEYWORDS,
336
+ "user",
337
+ "users",
338
+ "table",
339
+ "column",
340
+ "index",
341
+ "values",
342
+ "in",
343
+ "like",
344
+ "between",
345
+ "is",
346
+ "exists",
347
+ "null",
348
+ "true",
349
+ "false",
350
+ "all",
351
+ "any",
352
+ "some",
353
+ "update",
354
+ "insert",
355
+ "delete",
356
+ "create",
357
+ "drop",
358
+ "alter",
359
+ "truncate",
360
+ "grant",
361
+ "revoke",
362
+ "exec",
363
+ "execute"
364
+ ]);
365
+ var SQL_RESERVED_WORDS = SQL_KEYWORDS;
366
+ var DEFAULT_WHERE_CLAUSE = "1=1";
367
+ var SPECIAL_FIELDS = Object.freeze({
368
+ ID: "id"
369
+ });
370
+ var SQL_TEMPLATES = Object.freeze({
371
+ PUBLIC_SCHEMA: "public",
372
+ WHERE: "WHERE",
373
+ SELECT: "SELECT",
374
+ FROM: "FROM",
375
+ ORDER_BY: "ORDER BY",
376
+ GROUP_BY: "GROUP BY",
377
+ HAVING: "HAVING",
378
+ LIMIT: "LIMIT",
379
+ OFFSET: "OFFSET",
380
+ COUNT_ALL: "COUNT(*)",
381
+ AS: "AS",
382
+ DISTINCT_ON: "DISTINCT ON",
383
+ IS_NULL: "IS NULL",
384
+ IS_NOT_NULL: "IS NOT NULL",
385
+ LIKE: "LIKE",
386
+ AND: "AND",
387
+ OR: "OR",
388
+ NOT: "NOT"
389
+ });
390
+ var SCHEMA_PREFIXES = Object.freeze({
391
+ INTERNAL: "@",
392
+ COMMENT: "//"
393
+ });
394
+ var Ops = Object.freeze({
395
+ EQUALS: "equals",
396
+ NOT: "not",
397
+ GT: "gt",
398
+ GTE: "gte",
399
+ LT: "lt",
400
+ LTE: "lte",
401
+ IN: "in",
402
+ NOT_IN: "notIn",
403
+ CONTAINS: "contains",
404
+ STARTS_WITH: "startsWith",
405
+ ENDS_WITH: "endsWith",
406
+ HAS: "has",
407
+ HAS_SOME: "hasSome",
408
+ HAS_EVERY: "hasEvery",
409
+ IS_EMPTY: "isEmpty",
410
+ PATH: "path",
411
+ STRING_CONTAINS: "string_contains",
412
+ STRING_STARTS_WITH: "string_starts_with",
413
+ STRING_ENDS_WITH: "string_ends_with"
414
+ });
415
+ var LogicalOps = Object.freeze({
416
+ AND: "AND",
417
+ OR: "OR",
418
+ NOT: "NOT"
419
+ });
420
+ var RelationFilters = Object.freeze({
421
+ SOME: "some",
422
+ EVERY: "every",
423
+ NONE: "none"
424
+ });
425
+ var Modes = Object.freeze({
426
+ INSENSITIVE: "insensitive",
427
+ DEFAULT: "default"
428
+ });
429
+ var Wildcards = Object.freeze({
430
+ [Ops.CONTAINS]: (v) => `%${v}%`,
431
+ [Ops.STARTS_WITH]: (v) => `${v}%`,
432
+ [Ops.ENDS_WITH]: (v) => `%${v}`
433
+ });
434
+ var REGEX_CACHE = {
435
+ VALID_IDENTIFIER: /^[a-z_][a-z0-9_]*$/
436
+ };
437
+ var LIMITS = Object.freeze({
438
+ MAX_QUERY_DEPTH: 50,
439
+ MAX_ARRAY_SIZE: 1e4,
440
+ MAX_STRING_LENGTH: 1e4,
441
+ MAX_HAVING_DEPTH: 50
442
+ });
443
+
444
+ // src/builder/shared/validators/type-guards.ts
445
+ function isNotNullish(value) {
446
+ return value !== null && value !== void 0;
447
+ }
448
+ function isNonEmptyString(value) {
449
+ return typeof value === "string" && value.trim().length > 0;
450
+ }
451
+ function isEmptyString(value) {
452
+ return typeof value === "string" && value.trim().length === 0;
453
+ }
454
+ function isNonEmptyArray(value) {
455
+ return Array.isArray(value) && value.length > 0;
456
+ }
457
+ function isEmptyArray(value) {
458
+ return Array.isArray(value) && value.length === 0;
459
+ }
460
+ function isPlainObject(val) {
461
+ if (!isNotNullish(val)) return false;
462
+ if (Array.isArray(val)) return false;
463
+ if (typeof val !== "object") return false;
464
+ return Object.prototype.toString.call(val) === "[object Object]";
465
+ }
466
+ function hasProperty(obj, key) {
467
+ return isPlainObject(obj) && key in obj;
468
+ }
469
+ function isArrayType(t) {
470
+ if (!isNotNullish(t)) return false;
471
+ const normalized = t.replace(/\?$/, "");
472
+ return normalized.endsWith("[]");
473
+ }
474
+ function isJsonType(t) {
475
+ return isNotNullish(t) && (t === "Json" || t === "Json?");
476
+ }
477
+ function hasValidContent(sql) {
478
+ return isNotNullish(sql) && sql.trim().length > 0;
479
+ }
480
+ function hasRequiredKeywords(sql) {
481
+ const upper = sql.toUpperCase();
482
+ const hasSelect = upper.includes("SELECT");
483
+ const hasFrom = upper.includes("FROM");
484
+ return hasSelect && hasFrom && upper.indexOf("SELECT") < upper.indexOf("FROM");
485
+ }
486
+
487
+ // src/builder/shared/errors.ts
488
+ var SqlBuilderError = class extends Error {
489
+ constructor(message, code, context) {
490
+ super(message);
491
+ this.name = "SqlBuilderError";
492
+ this.code = code;
493
+ this.context = context;
494
+ }
495
+ };
496
+ function createError(message, ctx, code = "VALIDATION_ERROR") {
497
+ const parts = [message];
498
+ if (isNonEmptyArray(ctx.path)) {
499
+ parts.push(`Path: ${ctx.path.join(".")}`);
500
+ }
501
+ if (isNotNullish(ctx.modelName)) {
502
+ parts.push(`Model: ${ctx.modelName}`);
503
+ }
504
+ if (isNonEmptyArray(ctx.availableFields)) {
505
+ parts.push(`Available fields: ${ctx.availableFields.join(", ")}`);
506
+ }
507
+ return new SqlBuilderError(parts.join("\n"), code, ctx);
508
+ }
509
+
510
+ // src/builder/shared/validators/sql-validators.ts
511
+ function isValidWhereClause(clause) {
512
+ return isNotNullish(clause) && clause.trim().length > 0 && clause !== DEFAULT_WHERE_CLAUSE;
513
+ }
514
+ function isEmptyWhere(where) {
515
+ if (!isNotNullish(where)) return true;
516
+ return Object.keys(where).length === 0;
517
+ }
518
+ function sqlPreview(sql) {
519
+ const s = String(sql);
520
+ if (s.length <= 160) return s;
521
+ return `${s.slice(0, 160)}...`;
522
+ }
523
+ function validateSelectQuery(sql) {
524
+ if (IS_PRODUCTION) return;
525
+ if (!hasValidContent(sql)) {
526
+ throw new Error("CRITICAL: Generated empty SQL query");
527
+ }
528
+ if (!hasRequiredKeywords(sql)) {
515
529
  throw new Error(`CRITICAL: Invalid SQL structure. SQL: ${sqlPreview(sql)}`);
516
530
  }
517
531
  }
@@ -642,10 +656,13 @@ function quoteIdent(id) {
642
656
  if (typeof id !== "string" || id.trim().length === 0) {
643
657
  throw new Error("quoteIdent: identifier is required and cannot be empty");
644
658
  }
645
- if (/[\u0000-\u001F\u007F]/.test(id)) {
646
- throw new Error(
647
- `quoteIdent: identifier contains invalid characters: ${JSON.stringify(id)}`
648
- );
659
+ for (let i = 0; i < id.length; i++) {
660
+ const code = id.charCodeAt(i);
661
+ if (code >= 0 && code <= 31 || code === 127) {
662
+ throw new Error(
663
+ `quoteIdent: identifier contains invalid characters: ${JSON.stringify(id)}`
664
+ );
665
+ }
649
666
  }
650
667
  if (needsQuoting(id)) {
651
668
  return `"${id.replace(/"/g, '""')}"`;
@@ -710,7 +727,13 @@ function getQuotedColumn(model, fieldName) {
710
727
 
711
728
  // src/builder/shared/sql-utils.ts
712
729
  function containsControlChars(s) {
713
- return /[\u0000-\u001F\u007F]/.test(s);
730
+ for (let i = 0; i < s.length; i++) {
731
+ const code = s.charCodeAt(i);
732
+ if (code >= 0 && code <= 31 || code === 127) {
733
+ return true;
734
+ }
735
+ }
736
+ return false;
714
737
  }
715
738
  function assertNoControlChars(label, s) {
716
739
  if (containsControlChars(s)) {
@@ -785,20 +808,9 @@ function parseUnquotedPart(input, start) {
785
808
  }
786
809
  return i;
787
810
  }
788
- function assertSafeQualifiedName(input) {
789
- const raw = String(input);
790
- const trimmed = raw.trim();
791
- if (trimmed.length === 0) {
792
- throw new Error("tableName/tableRef is required and cannot be empty");
793
- }
794
- if (raw !== trimmed) {
795
- throw new Error(
796
- `tableName/tableRef must not contain leading/trailing whitespace: ${JSON.stringify(raw)}`
797
- );
798
- }
799
- assertNoControlChars("tableName/tableRef", trimmed);
800
- for (let i2 = 0; i2 < trimmed.length; i2++) {
801
- const c = trimmed.charCodeAt(i2);
811
+ function validateQualifiedNameFormat(trimmed) {
812
+ for (let i = 0; i < trimmed.length; i++) {
813
+ const c = trimmed.charCodeAt(i);
802
814
  if (c === 9 || c === 11 || c === 12 || c === 32) {
803
815
  throw new Error(
804
816
  `tableName/tableRef must not contain whitespace: ${JSON.stringify(trimmed)}`
@@ -815,6 +827,8 @@ function assertSafeQualifiedName(input) {
815
827
  );
816
828
  }
817
829
  }
830
+ }
831
+ function parseQualifiedNameParts(trimmed) {
818
832
  let i = 0;
819
833
  const n = trimmed.length;
820
834
  let parts = 0;
@@ -850,6 +864,21 @@ function assertSafeQualifiedName(input) {
850
864
  }
851
865
  }
852
866
  }
867
+ function assertSafeQualifiedName(input) {
868
+ const raw = String(input);
869
+ const trimmed = raw.trim();
870
+ if (trimmed.length === 0) {
871
+ throw new Error("tableName/tableRef is required and cannot be empty");
872
+ }
873
+ if (raw !== trimmed) {
874
+ throw new Error(
875
+ `tableName/tableRef must not contain leading/trailing whitespace: ${JSON.stringify(raw)}`
876
+ );
877
+ }
878
+ assertNoControlChars("tableName/tableRef", trimmed);
879
+ validateQualifiedNameFormat(trimmed);
880
+ parseQualifiedNameParts(trimmed);
881
+ }
853
882
  function quote(id) {
854
883
  if (isEmptyString(id)) {
855
884
  throw new Error("quote: identifier is required and cannot be empty");
@@ -939,7 +968,7 @@ function assertSafeAlias(alias) {
939
968
  if (a !== alias) {
940
969
  throw new Error("Invalid alias: leading/trailing whitespace");
941
970
  }
942
- if (/[\u0000-\u001F\u007F]/.test(a)) {
971
+ if (containsControlChars(a)) {
943
972
  throw new Error(
944
973
  "Invalid alias: contains unsafe characters (control characters)"
945
974
  );
@@ -960,7 +989,7 @@ function assertSafeAlias(alias) {
960
989
  "Invalid alias: must be a simple identifier without whitespace"
961
990
  );
962
991
  }
963
- if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(a)) {
992
+ if (!/^[A-Za-z_]\w*$/.test(a)) {
964
993
  throw new Error(
965
994
  `Invalid alias: must be a simple identifier (alphanumeric with underscores): "${alias}"`
966
995
  );
@@ -1008,8 +1037,7 @@ function isValidRelationField(field) {
1008
1037
  return false;
1009
1038
  const fk = normalizeKeyList(field.foreignKey);
1010
1039
  if (fk.length === 0) return false;
1011
- const refsRaw = field.references;
1012
- const refs = normalizeKeyList(refsRaw);
1040
+ const refs = normalizeKeyList(field.references);
1013
1041
  if (refs.length === 0) {
1014
1042
  return fk.length === 1;
1015
1043
  }
@@ -1017,8 +1045,7 @@ function isValidRelationField(field) {
1017
1045
  return true;
1018
1046
  }
1019
1047
  function getReferenceFieldNames(field, foreignKeyCount) {
1020
- const refsRaw = field.references;
1021
- const refs = normalizeKeyList(refsRaw);
1048
+ const refs = normalizeKeyList(field.references);
1022
1049
  if (refs.length === 0) {
1023
1050
  if (foreignKeyCount === 1) return [SPECIAL_FIELDS.ID];
1024
1051
  return [];
@@ -1179,29 +1206,35 @@ function normalizeOrderByInput(orderBy, parseValue) {
1179
1206
  }
1180
1207
 
1181
1208
  // src/builder/shared/order-by-determinism.ts
1182
- function modelHasScalarId(model) {
1183
- if (!model) return false;
1184
- return getScalarFieldSet(model).has("id");
1209
+ function findTiebreakerField(model) {
1210
+ if (!model) return null;
1211
+ const scalarSet = getScalarFieldSet(model);
1212
+ for (const f of model.fields) {
1213
+ if (f.isId && !f.isRelation && scalarSet.has(f.name)) return f.name;
1214
+ }
1215
+ if (scalarSet.has("id")) return "id";
1216
+ return null;
1185
1217
  }
1186
- function hasIdTiebreaker(orderBy, parse) {
1218
+ function hasTiebreaker(orderBy, parse, field) {
1187
1219
  if (!isNotNullish(orderBy)) return false;
1188
1220
  const normalized = normalizeOrderByInput(orderBy, parse);
1189
1221
  return normalized.some(
1190
- (obj) => Object.prototype.hasOwnProperty.call(obj, "id")
1222
+ (obj) => Object.prototype.hasOwnProperty.call(obj, field)
1191
1223
  );
1192
1224
  }
1193
- function addIdTiebreaker(orderBy) {
1194
- if (Array.isArray(orderBy)) return [...orderBy, { id: "asc" }];
1195
- return [orderBy, { id: "asc" }];
1225
+ function addTiebreaker(orderBy, field) {
1226
+ if (Array.isArray(orderBy)) return [...orderBy, { [field]: "asc" }];
1227
+ return [orderBy, { [field]: "asc" }];
1196
1228
  }
1197
1229
  function ensureDeterministicOrderByInput(args) {
1198
1230
  const { orderBy, model, parseValue } = args;
1199
- if (!modelHasScalarId(model)) return orderBy;
1231
+ const tiebreaker = findTiebreakerField(model);
1232
+ if (!tiebreaker) return orderBy;
1200
1233
  if (!isNotNullish(orderBy)) {
1201
- return { id: "asc" };
1234
+ return { [tiebreaker]: "asc" };
1202
1235
  }
1203
- if (hasIdTiebreaker(orderBy, parseValue)) return orderBy;
1204
- return addIdTiebreaker(orderBy);
1236
+ if (hasTiebreaker(orderBy, parseValue, tiebreaker)) return orderBy;
1237
+ return addTiebreaker(orderBy, tiebreaker);
1205
1238
  }
1206
1239
 
1207
1240
  // src/builder/shared/validators/field-assertions.ts
@@ -1348,11 +1381,9 @@ function defaultNullsFor(dialect, direction) {
1348
1381
  function ensureCursorFieldsInOrder(orderEntries, cursorEntries) {
1349
1382
  if (cursorEntries.length === 0) return orderEntries;
1350
1383
  const existing = /* @__PURE__ */ new Set();
1351
- for (let i = 0; i < orderEntries.length; i++)
1352
- existing.add(orderEntries[i].field);
1384
+ for (const entry of orderEntries) existing.add(entry.field);
1353
1385
  let out = null;
1354
- for (let i = 0; i < cursorEntries.length; i++) {
1355
- const field = cursorEntries[i][0];
1386
+ for (const [field] of cursorEntries) {
1356
1387
  if (!existing.has(field)) {
1357
1388
  if (!out) out = orderEntries.slice();
1358
1389
  out.push({ field, direction: "asc" });
@@ -1592,7 +1623,44 @@ function buildNotComposite(expr, val, params, dialect, buildOp, separator) {
1592
1623
  if (clauses.length === 1) return `${SQL_TEMPLATES.NOT} ${clauses[0]}`;
1593
1624
  return `${SQL_TEMPLATES.NOT} (${clauses.join(separator)})`;
1594
1625
  }
1595
- function buildScalarOperator(expr, op, val, params, mode, fieldType, dialect, depth = 0) {
1626
+ function validateOperatorRequirements(op, mode, dialect) {
1627
+ const STRING_LIKE_OPS = /* @__PURE__ */ new Set([
1628
+ Ops.CONTAINS,
1629
+ Ops.STARTS_WITH,
1630
+ Ops.ENDS_WITH
1631
+ ]);
1632
+ if (STRING_LIKE_OPS.has(op) && !isNotNullish(dialect)) {
1633
+ throw createError(`Like operators require a SQL dialect`, { operator: op });
1634
+ }
1635
+ if ((op === Ops.IN || op === Ops.NOT_IN) && !isNotNullish(dialect)) {
1636
+ throw createError(`IN operators require a SQL dialect`, { operator: op });
1637
+ }
1638
+ if (op === Ops.EQUALS && mode === Modes.INSENSITIVE && !isNotNullish(dialect)) {
1639
+ throw createError(`Insensitive equals requires a SQL dialect`, {
1640
+ operator: op
1641
+ });
1642
+ }
1643
+ }
1644
+ function routeOperatorHandler(expr, op, val, params, mode, dialect) {
1645
+ const STRING_LIKE_OPS = /* @__PURE__ */ new Set([
1646
+ Ops.CONTAINS,
1647
+ Ops.STARTS_WITH,
1648
+ Ops.ENDS_WITH
1649
+ ]);
1650
+ if (STRING_LIKE_OPS.has(op)) {
1651
+ return handleLikeOperator(expr, op, val, params, mode, dialect);
1652
+ }
1653
+ if (op === Ops.IN || op === Ops.NOT_IN) {
1654
+ return handleInOperator(expr, op, val, params, dialect);
1655
+ }
1656
+ if (op === Ops.EQUALS && mode === Modes.INSENSITIVE && isNotNullish(dialect)) {
1657
+ const placeholder = params.addAuto(val);
1658
+ return caseInsensitiveEquals(expr, placeholder);
1659
+ }
1660
+ return null;
1661
+ }
1662
+ function buildScalarOperator(expr, op, val, params, options = {}) {
1663
+ const { mode, fieldType, dialect, depth = 0 } = options;
1596
1664
  if (val === void 0) return "";
1597
1665
  if (depth > MAX_NOT_DEPTH) {
1598
1666
  throw new Error(
@@ -1609,33 +1677,17 @@ function buildScalarOperator(expr, op, val, params, mode, fieldType, dialect, de
1609
1677
  const placeholder = params.addAuto(val);
1610
1678
  return `${expr} <> ${placeholder}`;
1611
1679
  }
1612
- if (op === Ops.EQUALS && mode === Modes.INSENSITIVE && isNotNullish(dialect)) {
1613
- const placeholder = params.addAuto(val);
1614
- return caseInsensitiveEquals(expr, placeholder);
1615
- }
1616
- const STRING_LIKE_OPS = /* @__PURE__ */ new Set([
1617
- Ops.CONTAINS,
1618
- Ops.STARTS_WITH,
1619
- Ops.ENDS_WITH
1620
- ]);
1621
- if (STRING_LIKE_OPS.has(op)) {
1622
- if (!isNotNullish(dialect)) {
1623
- throw createError(`Like operators require a SQL dialect`, {
1624
- operator: op
1625
- });
1626
- }
1627
- return handleLikeOperator(expr, op, val, params, mode, dialect);
1628
- }
1629
- if (op === Ops.IN || op === Ops.NOT_IN) {
1630
- if (!isNotNullish(dialect)) {
1631
- throw createError(`IN operators require a SQL dialect`, { operator: op });
1632
- }
1633
- return handleInOperator(expr, op, val, params, dialect);
1634
- }
1635
- if (op === Ops.EQUALS && mode === Modes.INSENSITIVE && !isNotNullish(dialect)) {
1636
- throw createError(`Insensitive equals requires a SQL dialect`, {
1637
- operator: op
1638
- });
1680
+ validateOperatorRequirements(op, mode, dialect);
1681
+ const specialHandler = routeOperatorHandler(
1682
+ expr,
1683
+ op,
1684
+ val,
1685
+ params,
1686
+ mode,
1687
+ dialect
1688
+ );
1689
+ if (specialHandler !== null) {
1690
+ return specialHandler;
1639
1691
  }
1640
1692
  return handleComparisonOperator(expr, op, val, params);
1641
1693
  }
@@ -1659,38 +1711,32 @@ function handleNotOperator(expr, val, params, outerMode, fieldType, dialect, dep
1659
1711
  if (!isNotNullish(dialect)) {
1660
1712
  const clauses = [];
1661
1713
  for (const [subOp, subVal] of entries) {
1662
- const sub = buildScalarOperator(
1663
- expr,
1664
- subOp,
1665
- subVal,
1666
- params,
1667
- effectiveMode,
1714
+ const sub = buildScalarOperator(expr, subOp, subVal, params, {
1715
+ mode: effectiveMode,
1668
1716
  fieldType,
1669
- void 0,
1670
- depth + 1
1671
- );
1717
+ dialect: void 0,
1718
+ depth: depth + 1
1719
+ });
1672
1720
  if (sub && sub.trim().length > 0) clauses.push(`(${sub})`);
1673
1721
  }
1674
1722
  if (clauses.length === 0) return "";
1675
1723
  if (clauses.length === 1) return `${SQL_TEMPLATES.NOT} ${clauses[0]}`;
1676
- return `${SQL_TEMPLATES.NOT} (${clauses.join(` ${SQL_TEMPLATES.AND} `)})`;
1724
+ const separator2 = ` ${SQL_TEMPLATES.AND} `;
1725
+ return `${SQL_TEMPLATES.NOT} (${clauses.join(separator2)})`;
1677
1726
  }
1727
+ const separator = ` ${SQL_TEMPLATES.AND} `;
1678
1728
  return buildNotComposite(
1679
1729
  expr,
1680
1730
  val,
1681
1731
  params,
1682
1732
  dialect,
1683
- (e, subOp, subVal, p, d) => buildScalarOperator(
1684
- e,
1685
- subOp,
1686
- subVal,
1687
- p,
1688
- effectiveMode,
1733
+ (e, subOp, subVal, p, d) => buildScalarOperator(e, subOp, subVal, p, {
1734
+ mode: effectiveMode,
1689
1735
  fieldType,
1690
- d,
1691
- depth + 1
1692
- ),
1693
- ` ${SQL_TEMPLATES.AND} `
1736
+ dialect: d,
1737
+ depth: depth + 1
1738
+ }),
1739
+ separator
1694
1740
  );
1695
1741
  }
1696
1742
  function buildDynamicLikePattern(op, placeholder) {
@@ -1887,24 +1933,42 @@ function handleArrayIsEmpty(expr, val, dialect) {
1887
1933
  // src/builder/where/operators-json.ts
1888
1934
  var SAFE_JSON_PATH_SEGMENT = /^[a-zA-Z_]\w*$/;
1889
1935
  var MAX_PATH_SEGMENT_LENGTH = 255;
1936
+ var MAX_PATH_SEGMENTS = 100;
1937
+ function sanitizeForError(s) {
1938
+ let result = "";
1939
+ for (let i = 0; i < s.length; i++) {
1940
+ const code = s.charCodeAt(i);
1941
+ if (code >= 0 && code <= 31 || code === 127) {
1942
+ result += `\\x${code.toString(16).padStart(2, "0")}`;
1943
+ } else {
1944
+ result += s[i];
1945
+ }
1946
+ }
1947
+ return result;
1948
+ }
1890
1949
  function validateJsonPathSegments(segments) {
1891
- for (const segment of segments) {
1950
+ if (segments.length > MAX_PATH_SEGMENTS) {
1951
+ throw createError(`JSON path too long: max ${MAX_PATH_SEGMENTS} segments`, {
1952
+ operator: Ops.PATH
1953
+ });
1954
+ }
1955
+ for (let i = 0; i < segments.length; i++) {
1956
+ const segment = segments[i];
1892
1957
  if (typeof segment !== "string") {
1893
- throw createError("JSON path segments must be strings", {
1894
- operator: Ops.PATH,
1895
- value: segment
1958
+ throw createError(`JSON path segment at index ${i} must be string`, {
1959
+ operator: Ops.PATH
1896
1960
  });
1897
1961
  }
1898
1962
  if (segment.length > MAX_PATH_SEGMENT_LENGTH) {
1899
1963
  throw createError(
1900
- `JSON path segment too long: max ${MAX_PATH_SEGMENT_LENGTH} characters`,
1901
- { operator: Ops.PATH, value: `[${segment.length} chars]` }
1964
+ `JSON path segment at index ${i} too long: max ${MAX_PATH_SEGMENT_LENGTH} characters`,
1965
+ { operator: Ops.PATH }
1902
1966
  );
1903
1967
  }
1904
1968
  if (!SAFE_JSON_PATH_SEGMENT.test(segment)) {
1905
1969
  throw createError(
1906
- `Invalid JSON path segment: '${segment}'. Must be alphanumeric with underscores, starting with letter or underscore.`,
1907
- { operator: Ops.PATH, value: segment }
1970
+ `Invalid JSON path segment at index ${i}: '${sanitizeForError(segment)}'`,
1971
+ { operator: Ops.PATH }
1908
1972
  );
1909
1973
  }
1910
1974
  }
@@ -2022,6 +2086,35 @@ function buildToOneNotExistsMatch(relTable, relAlias, join, sub) {
2022
2086
  const joins = sub.joins.length > 0 ? ` ${sub.joins.join(" ")}` : "";
2023
2087
  return `${SQL_TEMPLATES.NOT} EXISTS (${SQL_TEMPLATES.SELECT} 1 ${SQL_TEMPLATES.FROM} ${relTable} ${relAlias}${joins} ${SQL_TEMPLATES.WHERE} ${join} ${SQL_TEMPLATES.AND} ${sub.clause})`;
2024
2088
  }
2089
+ function tryOptimizeNoneFilter(noneValue, ctx, relModel, relTable, relAlias, join, sub) {
2090
+ const isEmptyFilter = isPlainObject(noneValue) && Object.keys(noneValue).length === 0;
2091
+ const canOptimize = !ctx.isSubquery && isEmptyFilter && sub.clause === DEFAULT_WHERE_CLAUSE && sub.joins.length === 0;
2092
+ if (!canOptimize) return null;
2093
+ const checkField = relModel.fields.find(
2094
+ (f) => !f.isRelation && f.isRequired && f.name !== "id"
2095
+ ) || relModel.fields.find((f) => !f.isRelation && f.name === "id");
2096
+ if (!checkField) return null;
2097
+ const leftJoinSql = `LEFT JOIN ${relTable} ${relAlias} ON ${join}`;
2098
+ const whereClause = `${relAlias}.${quoteColumn(relModel, checkField.name)} IS NULL`;
2099
+ return Object.freeze({
2100
+ clause: whereClause,
2101
+ joins: freezeJoins([leftJoinSql])
2102
+ });
2103
+ }
2104
+ function processRelationFilter(key, wrap, args) {
2105
+ const { value, fieldName, ctx, relAlias, relModel, whereBuilder } = args;
2106
+ const raw = value[key];
2107
+ if (raw === void 0 || raw === null) return null;
2108
+ const sub = whereBuilder.build(raw, __spreadProps(__spreadValues({}, ctx), {
2109
+ alias: relAlias,
2110
+ model: relModel,
2111
+ path: [...ctx.path, fieldName, key],
2112
+ isSubquery: true,
2113
+ depth: ctx.depth + 1
2114
+ }));
2115
+ const j = sub.joins.length > 0 ? ` ${sub.joins.join(" ")}` : "";
2116
+ return wrap(sub.clause, j);
2117
+ }
2025
2118
  function buildListRelationFilters(args) {
2026
2119
  const {
2027
2120
  fieldName,
@@ -2042,21 +2135,16 @@ function buildListRelationFilters(args) {
2042
2135
  isSubquery: true,
2043
2136
  depth: ctx.depth + 1
2044
2137
  }));
2045
- const isEmptyFilter = isPlainObject(noneValue) && Object.keys(noneValue).length === 0;
2046
- const canOptimize = !ctx.isSubquery && isEmptyFilter && sub.clause === DEFAULT_WHERE_CLAUSE && sub.joins.length === 0;
2047
- if (canOptimize) {
2048
- const checkField = relModel.fields.find(
2049
- (f) => !f.isRelation && f.isRequired && f.name !== "id"
2050
- ) || relModel.fields.find((f) => !f.isRelation && f.name === "id");
2051
- if (checkField) {
2052
- const leftJoinSql = `LEFT JOIN ${relTable} ${relAlias} ON ${join}`;
2053
- const whereClause = `${relAlias}.${quote(checkField.name)} IS NULL`;
2054
- return Object.freeze({
2055
- clause: whereClause,
2056
- joins: freezeJoins([leftJoinSql])
2057
- });
2058
- }
2059
- }
2138
+ const optimized = tryOptimizeNoneFilter(
2139
+ noneValue,
2140
+ ctx,
2141
+ relModel,
2142
+ relTable,
2143
+ relAlias,
2144
+ join,
2145
+ sub
2146
+ );
2147
+ if (optimized) return optimized;
2060
2148
  }
2061
2149
  const filters = [
2062
2150
  {
@@ -2077,17 +2165,8 @@ function buildListRelationFilters(args) {
2077
2165
  ];
2078
2166
  const clauses = [];
2079
2167
  for (const { key, wrap } of filters) {
2080
- const raw = value[key];
2081
- if (raw === void 0 || raw === null) continue;
2082
- const sub = whereBuilder.build(raw, __spreadProps(__spreadValues({}, ctx), {
2083
- alias: relAlias,
2084
- model: relModel,
2085
- path: [...ctx.path, fieldName, key],
2086
- isSubquery: true,
2087
- depth: ctx.depth + 1
2088
- }));
2089
- const j = sub.joins.length > 0 ? ` ${sub.joins.join(" ")}` : "";
2090
- clauses.push(wrap(sub.clause, j));
2168
+ const clause = processRelationFilter(key, wrap, args);
2169
+ if (clause) clauses.push(clause);
2091
2170
  }
2092
2171
  if (clauses.length === 0) {
2093
2172
  throw createError(
@@ -2489,15 +2568,11 @@ function buildOperator(expr, op, val, ctx, mode, fieldType) {
2489
2568
  if (fieldType && isJsonType(fieldType) && JSON_OPS.has(op)) {
2490
2569
  return buildJsonOperator(expr, op, val, ctx.params, ctx.dialect);
2491
2570
  }
2492
- return buildScalarOperator(
2493
- expr,
2494
- op,
2495
- val,
2496
- ctx.params,
2571
+ return buildScalarOperator(expr, op, val, ctx.params, {
2497
2572
  mode,
2498
2573
  fieldType,
2499
- ctx.dialect
2500
- );
2574
+ dialect: ctx.dialect
2575
+ });
2501
2576
  }
2502
2577
 
2503
2578
  // src/builder/shared/alias-generator.ts
@@ -2611,47 +2686,50 @@ function validateState(params, mappings, index) {
2611
2686
  validateMappings(mappings);
2612
2687
  assertNextIndexMatches(mappings.length, index);
2613
2688
  }
2614
- function createStoreInternal(startIndex, initialParams = [], initialMappings = []) {
2615
- let index = startIndex;
2616
- const params = initialParams.length > 0 ? initialParams.slice() : [];
2617
- const mappings = initialMappings.length > 0 ? initialMappings.slice() : [];
2689
+ function buildDynamicNameIndex(mappings) {
2618
2690
  const dynamicNameToIndex = /* @__PURE__ */ new Map();
2619
- for (let i = 0; i < mappings.length; i++) {
2620
- const m = mappings[i];
2691
+ for (const m of mappings) {
2621
2692
  if (typeof m.dynamicName === "string") {
2622
2693
  dynamicNameToIndex.set(m.dynamicName.trim(), m.index);
2623
2694
  }
2624
2695
  }
2625
- let dirty = true;
2626
- let cachedSnapshot = null;
2627
- function assertCanAdd() {
2628
- if (index > MAX_PARAM_INDEX) {
2629
- throw new Error(
2630
- `CRITICAL: Cannot add param - would overflow MAX_SAFE_INTEGER. Current index: ${index}`
2631
- );
2632
- }
2633
- }
2634
- function format(position) {
2635
- return `$${position}`;
2696
+ return dynamicNameToIndex;
2697
+ }
2698
+ function assertCanAddParam(currentIndex) {
2699
+ if (currentIndex > MAX_PARAM_INDEX) {
2700
+ throw new Error(
2701
+ `CRITICAL: Cannot add param - would overflow MAX_SAFE_INTEGER. Current index: ${currentIndex}`
2702
+ );
2636
2703
  }
2637
- function normalizeDynamicName(dynamicName) {
2638
- const dn = dynamicName.trim();
2639
- if (dn.length === 0) {
2640
- throw new Error("CRITICAL: dynamicName cannot be empty");
2641
- }
2642
- return dn;
2704
+ }
2705
+ function formatPosition(position) {
2706
+ return `$${position}`;
2707
+ }
2708
+ function validateDynamicName(dynamicName) {
2709
+ const dn = dynamicName.trim();
2710
+ if (dn.length === 0) {
2711
+ throw new Error("CRITICAL: dynamicName cannot be empty");
2643
2712
  }
2713
+ return dn;
2714
+ }
2715
+ function createStoreInternal(startIndex, initialParams = [], initialMappings = []) {
2716
+ let index = startIndex;
2717
+ const params = initialParams.length > 0 ? initialParams.slice() : [];
2718
+ const mappings = initialMappings.length > 0 ? initialMappings.slice() : [];
2719
+ const dynamicNameToIndex = buildDynamicNameIndex(mappings);
2720
+ let dirty = true;
2721
+ let cachedSnapshot = null;
2644
2722
  function addDynamic(dynamicName) {
2645
- const dn = normalizeDynamicName(dynamicName);
2723
+ const dn = validateDynamicName(dynamicName);
2646
2724
  const existing = dynamicNameToIndex.get(dn);
2647
- if (existing !== void 0) return format(existing);
2725
+ if (existing !== void 0) return formatPosition(existing);
2648
2726
  const position = index;
2649
2727
  dynamicNameToIndex.set(dn, position);
2650
2728
  params.push(void 0);
2651
2729
  mappings.push({ index: position, dynamicName: dn });
2652
2730
  index++;
2653
2731
  dirty = true;
2654
- return format(position);
2732
+ return formatPosition(position);
2655
2733
  }
2656
2734
  function addStatic(value) {
2657
2735
  const position = index;
@@ -2660,10 +2738,10 @@ function createStoreInternal(startIndex, initialParams = [], initialMappings = [
2660
2738
  mappings.push({ index: position, value: normalizedValue });
2661
2739
  index++;
2662
2740
  dirty = true;
2663
- return format(position);
2741
+ return formatPosition(position);
2664
2742
  }
2665
2743
  function add(value, dynamicName) {
2666
- assertCanAdd();
2744
+ assertCanAddParam(index);
2667
2745
  return dynamicName === void 0 ? addStatic(value) : addDynamic(dynamicName);
2668
2746
  }
2669
2747
  function addAuto(value) {
@@ -2884,6 +2962,17 @@ function buildRelationSelect(relArgs, relModel, relAlias) {
2884
2962
  var MAX_INCLUDE_DEPTH = 10;
2885
2963
  var MAX_TOTAL_SUBQUERIES = 100;
2886
2964
  var MAX_TOTAL_INCLUDES = 50;
2965
+ function buildIncludeScope(includePath) {
2966
+ if (includePath.length === 0) return "include";
2967
+ let scope = "include";
2968
+ for (let i = 0; i < includePath.length; i++) {
2969
+ scope += `.${includePath[i]}`;
2970
+ if (i < includePath.length - 1) {
2971
+ scope += ".include";
2972
+ }
2973
+ }
2974
+ return scope;
2975
+ }
2887
2976
  function getRelationTableReference(relModel, dialect) {
2888
2977
  return buildTableReference(
2889
2978
  SQL_TEMPLATES.PUBLIC_SCHEMA,
@@ -3027,19 +3116,11 @@ function finalizeOrderByForInclude(args) {
3027
3116
  }
3028
3117
  function buildSelectWithNestedIncludes(relArgs, relModel, relAlias, ctx) {
3029
3118
  let relSelect = buildRelationSelect(relArgs, relModel, relAlias);
3030
- const nestedIncludes = isPlainObject(relArgs) ? buildIncludeSqlInternal(
3031
- relArgs,
3032
- relModel,
3033
- ctx.schemas,
3034
- ctx.schemaByName,
3035
- relAlias,
3036
- ctx.aliasGen,
3037
- ctx.params,
3038
- ctx.dialect,
3039
- ctx.visitPath || [],
3040
- (ctx.depth || 0) + 1,
3041
- ctx.stats
3042
- ) : [];
3119
+ const nestedIncludes = isPlainObject(relArgs) ? buildIncludeSqlInternal(relArgs, __spreadProps(__spreadValues({}, ctx), {
3120
+ model: relModel,
3121
+ parentAlias: relAlias,
3122
+ depth: (ctx.depth || 0) + 1
3123
+ })) : [];
3043
3124
  if (isNonEmptyArray(nestedIncludes)) {
3044
3125
  const emptyJson = ctx.dialect === "postgres" ? `'[]'::json` : `json('[]')`;
3045
3126
  const nestedSelects = nestedIncludes.map(
@@ -3094,6 +3175,7 @@ function buildOneToOneIncludeSql(args) {
3094
3175
  whereClause: args.whereClause
3095
3176
  });
3096
3177
  if (args.orderBySql) sql += ` ${SQL_TEMPLATES.ORDER_BY} ${args.orderBySql}`;
3178
+ const scopeBase = buildIncludeScope(args.ctx.includePath);
3097
3179
  if (isNotNullish(args.takeVal)) {
3098
3180
  return appendLimitOffset(
3099
3181
  sql,
@@ -3101,15 +3183,10 @@ function buildOneToOneIncludeSql(args) {
3101
3183
  args.ctx.params,
3102
3184
  args.takeVal,
3103
3185
  args.skipVal,
3104
- `include.${args.relName}`
3186
+ scopeBase
3105
3187
  );
3106
3188
  }
3107
- return limitOneSql(
3108
- sql,
3109
- args.ctx.params,
3110
- args.skipVal,
3111
- `include.${args.relName}`
3112
- );
3189
+ return limitOneSql(sql, args.ctx.params, args.skipVal, scopeBase);
3113
3190
  }
3114
3191
  function buildListIncludeSpec(args) {
3115
3192
  const rowExpr = jsonBuildObject(args.relSelect, args.ctx.dialect);
@@ -3137,13 +3214,14 @@ function buildListIncludeSpec(args) {
3137
3214
  whereClause: args.whereClause
3138
3215
  });
3139
3216
  if (args.orderBySql) base += ` ${SQL_TEMPLATES.ORDER_BY} ${args.orderBySql}`;
3217
+ const scopeBase = buildIncludeScope(args.ctx.includePath);
3140
3218
  base = appendLimitOffset(
3141
3219
  base,
3142
3220
  args.ctx.dialect,
3143
3221
  args.ctx.params,
3144
3222
  args.takeVal,
3145
3223
  args.skipVal,
3146
- `include.${args.relName}`
3224
+ scopeBase
3147
3225
  );
3148
3226
  const selectExpr = jsonAgg("row", args.ctx.dialect);
3149
3227
  const sql = `${SQL_TEMPLATES.SELECT} ${selectExpr} ${SQL_TEMPLATES.FROM} (${base}) ${SQL_TEMPLATES.AS} ${rowAlias}`;
@@ -3192,7 +3270,6 @@ function buildSingleInclude(relName, relArgs, field, relModel, ctx) {
3192
3270
  );
3193
3271
  if (!isList) {
3194
3272
  const sql = buildOneToOneIncludeSql({
3195
- relName,
3196
3273
  relTable,
3197
3274
  relAlias,
3198
3275
  joins: whereParts.joins,
@@ -3220,8 +3297,14 @@ function buildSingleInclude(relName, relArgs, field, relModel, ctx) {
3220
3297
  ctx
3221
3298
  });
3222
3299
  }
3223
- function buildIncludeSqlInternal(args, model, schemas, schemaByName, parentAlias, aliasGen, params, dialect, visitPath = [], depth = 0, stats) {
3224
- if (!stats) stats = { totalIncludes: 0, totalSubqueries: 0, maxDepth: 0 };
3300
+ function buildIncludeSqlInternal(args, ctx) {
3301
+ const stats = ctx.stats || {
3302
+ totalIncludes: 0,
3303
+ totalSubqueries: 0,
3304
+ maxDepth: 0
3305
+ };
3306
+ const depth = ctx.depth || 0;
3307
+ const visitPath = ctx.visitPath || [];
3225
3308
  if (depth > MAX_INCLUDE_DEPTH) {
3226
3309
  throw new Error(
3227
3310
  `Maximum include depth of ${MAX_INCLUDE_DEPTH} exceeded. Path: ${visitPath.join(" -> ")}. Deep includes cause exponential SQL complexity and performance issues.`
@@ -3229,7 +3312,7 @@ function buildIncludeSqlInternal(args, model, schemas, schemaByName, parentAlias
3229
3312
  }
3230
3313
  stats.maxDepth = Math.max(stats.maxDepth, depth);
3231
3314
  const includes = [];
3232
- const entries = relationEntriesFromArgs(args, model);
3315
+ const entries = relationEntriesFromArgs(args, ctx.model);
3233
3316
  for (const [relName, relArgs] of entries) {
3234
3317
  if (relArgs === false) continue;
3235
3318
  stats.totalIncludes++;
@@ -3244,8 +3327,12 @@ function buildIncludeSqlInternal(args, model, schemas, schemaByName, parentAlias
3244
3327
  `Query complexity limit exceeded: ${stats.totalSubqueries} subqueries generated. Maximum allowed: ${MAX_TOTAL_SUBQUERIES}. This indicates exponential include nesting. Stats: depth=${stats.maxDepth}, includes=${stats.totalIncludes}. Path: ${visitPath.join(" -> ")}. Simplify your include structure or split into multiple queries.`
3245
3328
  );
3246
3329
  }
3247
- const resolved = resolveRelationOrThrow(model, schemaByName, relName);
3248
- const relationPath = `${model.name}.${relName}`;
3330
+ const resolved = resolveRelationOrThrow(
3331
+ ctx.model,
3332
+ ctx.schemaByName,
3333
+ relName
3334
+ );
3335
+ const relationPath = `${ctx.model.name}.${relName}`;
3249
3336
  const currentPath = [...visitPath, relationPath];
3250
3337
  if (visitPath.includes(relationPath)) {
3251
3338
  throw new Error(
@@ -3260,19 +3347,14 @@ function buildIncludeSqlInternal(args, model, schemas, schemaByName, parentAlias
3260
3347
  `Include too deeply nested: model '${resolved.relModel.name}' appears ${modelOccurrences} times in path: ${currentPath.join(" -> ")}`
3261
3348
  );
3262
3349
  }
3350
+ const nextIncludePath = [...ctx.includePath, relName];
3263
3351
  includes.push(
3264
- buildSingleInclude(relName, relArgs, resolved.field, resolved.relModel, {
3265
- model,
3266
- schemas,
3267
- schemaByName,
3268
- parentAlias,
3269
- aliasGen,
3270
- dialect,
3271
- params,
3352
+ buildSingleInclude(relName, relArgs, resolved.field, resolved.relModel, __spreadProps(__spreadValues({}, ctx), {
3353
+ includePath: nextIncludePath,
3272
3354
  visitPath: currentPath,
3273
3355
  depth: depth + 1,
3274
3356
  stats
3275
- })
3357
+ }))
3276
3358
  );
3277
3359
  }
3278
3360
  return includes;
@@ -3286,8 +3368,7 @@ function buildIncludeSql(args, model, schemas, parentAlias, params, dialect) {
3286
3368
  };
3287
3369
  const schemaByName = /* @__PURE__ */ new Map();
3288
3370
  for (const m of schemas) schemaByName.set(m.name, m);
3289
- return buildIncludeSqlInternal(
3290
- args,
3371
+ return buildIncludeSqlInternal(args, {
3291
3372
  model,
3292
3373
  schemas,
3293
3374
  schemaByName,
@@ -3295,10 +3376,11 @@ function buildIncludeSql(args, model, schemas, parentAlias, params, dialect) {
3295
3376
  aliasGen,
3296
3377
  params,
3297
3378
  dialect,
3298
- [],
3299
- 0,
3379
+ includePath: [],
3380
+ visitPath: [],
3381
+ depth: 0,
3300
3382
  stats
3301
- );
3383
+ });
3302
3384
  }
3303
3385
  function resolveCountRelationOrThrow(relName, model, schemaByName) {
3304
3386
  const relationSet = getRelationFieldSet(model);
@@ -3343,8 +3425,7 @@ function resolveCountKeyPairs(field) {
3343
3425
  if (fkFields.length === 0) {
3344
3426
  throw new Error("Relation count requires foreignKey");
3345
3427
  }
3346
- const refsRaw = field.references;
3347
- const refs = normalizeKeyList(refsRaw);
3428
+ const refs = normalizeKeyList(field.references);
3348
3429
  const refFields = refs.length > 0 ? refs : defaultReferencesForCount(fkFields.length);
3349
3430
  if (refFields.length !== fkFields.length) {
3350
3431
  throw new Error(
@@ -3479,132 +3560,139 @@ function finalizeSql(sql, params, dialect) {
3479
3560
  paramMappings: snapshot.mappings
3480
3561
  };
3481
3562
  }
3482
- function parseSimpleScalarSelect(select, fromAlias) {
3483
- const raw = select.trim();
3484
- if (raw.length === 0) return [];
3485
- const fromLower = fromAlias.toLowerCase();
3486
- const parts = raw.split(SQL_SEPARATORS.FIELD_LIST);
3487
- const names = [];
3488
- const isIdent = (s) => /^[A-Za-z_][A-Za-z0-9_]*$/.test(s);
3489
- const readIdentOrQuoted = (s, start) => {
3490
- const n = s.length;
3491
- if (start >= n) return { text: "", next: start, quoted: false };
3492
- if (s.charCodeAt(start) === 34) {
3493
- let i2 = start + 1;
3494
- let out = "";
3495
- let saw = false;
3496
- while (i2 < n) {
3497
- const c = s.charCodeAt(i2);
3498
- if (c === 34) {
3499
- const next = i2 + 1;
3500
- if (next < n && s.charCodeAt(next) === 34) {
3501
- out += '"';
3502
- saw = true;
3503
- i2 += 2;
3504
- continue;
3505
- }
3506
- if (!saw)
3507
- throw new Error(
3508
- `sqlite distinct emulation: empty quoted identifier in: ${s}`
3509
- );
3510
- return { text: out, next: i2 + 1, quoted: true };
3511
- }
3512
- out += s[i2];
3513
- saw = true;
3514
- i2++;
3515
- }
3516
- throw new Error(
3517
- `sqlite distinct emulation: unterminated quoted identifier in: ${s}`
3518
- );
3519
- }
3520
- let i = start;
3521
- while (i < n) {
3522
- const c = s.charCodeAt(i);
3523
- if (c === 32 || c === 9) break;
3524
- if (c === 46) break;
3525
- i++;
3526
- }
3527
- return { text: s.slice(start, i), next: i, quoted: false };
3528
- };
3529
- const skipSpaces = (s, i) => {
3530
- while (i < s.length) {
3531
- const c = s.charCodeAt(i);
3532
- if (c !== 32 && c !== 9) break;
3563
+ function isIdent(s) {
3564
+ return /^[A-Za-z_]\w*$/.test(s);
3565
+ }
3566
+ function skipSpaces(s, i) {
3567
+ while (i < s.length) {
3568
+ const c = s.charCodeAt(i);
3569
+ if (c !== 32 && c !== 9) break;
3570
+ i++;
3571
+ }
3572
+ return i;
3573
+ }
3574
+ function parseQuotedIdentifier(s, start) {
3575
+ const n = s.length;
3576
+ let i = start + 1;
3577
+ let out = "";
3578
+ let saw = false;
3579
+ while (i < n) {
3580
+ const c = s.charCodeAt(i);
3581
+ if (c !== 34) {
3582
+ out += s[i];
3583
+ saw = true;
3533
3584
  i++;
3585
+ continue;
3534
3586
  }
3535
- return i;
3536
- };
3537
- for (let idx = 0; idx < parts.length; idx++) {
3538
- const p = parts[idx].trim();
3539
- if (p.length === 0) continue;
3540
- let i = 0;
3541
- i = skipSpaces(p, i);
3542
- const a = readIdentOrQuoted(p, i);
3543
- const actualAlias = a.text.toLowerCase();
3544
- if (!isIdent(a.text)) {
3545
- throw new Error(
3546
- `sqlite distinct emulation requires scalar select fields to be simple columns (alias.column). Got: ${p}`
3547
- );
3548
- }
3549
- if (actualAlias !== fromLower) {
3550
- throw new Error(`Expected alias '${fromAlias}', got '${a.text}' in: ${p}`);
3587
+ const next = i + 1;
3588
+ if (next < n && s.charCodeAt(next) === 34) {
3589
+ out += '"';
3590
+ saw = true;
3591
+ i += 2;
3592
+ continue;
3551
3593
  }
3552
- i = a.next;
3553
- if (i >= p.length || p.charCodeAt(i) !== 46) {
3594
+ if (!saw) {
3554
3595
  throw new Error(
3555
- `sqlite distinct emulation requires scalar select fields to be simple columns (alias.column). Got: ${p}`
3596
+ `sqlite distinct emulation: empty quoted identifier in: ${s}`
3556
3597
  );
3557
3598
  }
3599
+ return { text: out, next: i + 1, quoted: true };
3600
+ }
3601
+ throw new Error(
3602
+ `sqlite distinct emulation: unterminated quoted identifier in: ${s}`
3603
+ );
3604
+ }
3605
+ function parseUnquotedIdentifier(s, start) {
3606
+ const n = s.length;
3607
+ let i = start;
3608
+ while (i < n) {
3609
+ const c = s.charCodeAt(i);
3610
+ if (c === 32 || c === 9 || c === 46) break;
3558
3611
  i++;
3559
- i = skipSpaces(p, i);
3560
- const colPart = readIdentOrQuoted(p, i);
3561
- const columnName = colPart.text.trim();
3562
- if (columnName.length === 0) {
3563
- throw new Error(`Failed to parse selected column name from: ${p}`);
3564
- }
3565
- i = colPart.next;
3566
- i = skipSpaces(p, i);
3567
- let outAlias = "";
3568
- if (i < p.length) {
3569
- const rest = p.slice(i).trim();
3570
- if (rest.length > 0) {
3571
- const m = rest.match(/^AS\s+/i);
3572
- if (!m) {
3573
- throw new Error(
3574
- `sqlite distinct emulation requires scalar select fields to be simple columns (optionally with AS). Got: ${p}`
3575
- );
3576
- }
3577
- let j = i;
3578
- j = skipSpaces(p, j);
3579
- if (!/^AS\b/i.test(p.slice(j))) {
3580
- throw new Error(`Failed to parse AS in: ${p}`);
3581
- }
3582
- j += 2;
3583
- j = skipSpaces(p, j);
3584
- const out = readIdentOrQuoted(p, j);
3585
- outAlias = out.text.trim();
3586
- if (outAlias.length === 0) {
3587
- throw new Error(`Failed to parse output alias from: ${p}`);
3588
- }
3589
- j = skipSpaces(p, out.next);
3590
- if (j !== p.length) {
3591
- throw new Error(
3592
- `sqlite distinct emulation requires scalar select fields to be simple columns (optionally with AS). Got: ${p}`
3593
- );
3594
- }
3595
- }
3596
- }
3597
- const name = outAlias.length > 0 ? outAlias : columnName;
3598
- names.push(name);
3599
3612
  }
3600
- return names;
3613
+ return { text: s.slice(start, i), next: i, quoted: false };
3614
+ }
3615
+ function readIdentOrQuoted(s, start) {
3616
+ const n = s.length;
3617
+ if (start >= n) return { text: "", next: start, quoted: false };
3618
+ if (s.charCodeAt(start) === 34) {
3619
+ return parseQuotedIdentifier(s, start);
3620
+ }
3621
+ return parseUnquotedIdentifier(s, start);
3622
+ }
3623
+ function parseOutputAlias(p, i) {
3624
+ if (i >= p.length) return "";
3625
+ const rest = p.slice(i).trim();
3626
+ if (rest.length === 0) return "";
3627
+ const m = rest.match(/^AS\s+/i);
3628
+ if (!m) {
3629
+ throw new Error(
3630
+ `sqlite distinct emulation requires scalar select fields to be simple columns (optionally with AS). Got: ${p}`
3631
+ );
3632
+ }
3633
+ let j = i;
3634
+ j = skipSpaces(p, j);
3635
+ if (!/^AS\b/i.test(p.slice(j))) {
3636
+ throw new Error(`Failed to parse AS in: ${p}`);
3637
+ }
3638
+ j += 2;
3639
+ j = skipSpaces(p, j);
3640
+ const out = readIdentOrQuoted(p, j);
3641
+ const outAlias = out.text.trim();
3642
+ if (outAlias.length === 0) {
3643
+ throw new Error(`Failed to parse output alias from: ${p}`);
3644
+ }
3645
+ j = skipSpaces(p, out.next);
3646
+ if (j !== p.length) {
3647
+ throw new Error(
3648
+ `sqlite distinct emulation requires scalar select fields to be simple columns (optionally with AS). Got: ${p}`
3649
+ );
3650
+ }
3651
+ return outAlias;
3652
+ }
3653
+ function parseSelectField(p, fromAlias) {
3654
+ const fromLower = fromAlias.toLowerCase();
3655
+ let i = 0;
3656
+ i = skipSpaces(p, i);
3657
+ const a = readIdentOrQuoted(p, i);
3658
+ const actualAlias = a.text.toLowerCase();
3659
+ if (!isIdent(a.text)) {
3660
+ throw new Error(
3661
+ `sqlite distinct emulation requires scalar select fields to be simple columns (alias.column). Got: ${p}`
3662
+ );
3663
+ }
3664
+ if (actualAlias !== fromLower) {
3665
+ throw new Error(`Expected alias '${fromAlias}', got '${a.text}' in: ${p}`);
3666
+ }
3667
+ i = a.next;
3668
+ if (i >= p.length || p.charCodeAt(i) !== 46) {
3669
+ throw new Error(
3670
+ `sqlite distinct emulation requires scalar select fields to be simple columns (alias.column). Got: ${p}`
3671
+ );
3672
+ }
3673
+ i++;
3674
+ i = skipSpaces(p, i);
3675
+ const colPart = readIdentOrQuoted(p, i);
3676
+ const columnName = colPart.text.trim();
3677
+ if (columnName.length === 0) {
3678
+ throw new Error(`Failed to parse selected column name from: ${p}`);
3679
+ }
3680
+ i = colPart.next;
3681
+ i = skipSpaces(p, i);
3682
+ const outAlias = parseOutputAlias(p, i);
3683
+ return outAlias.length > 0 ? outAlias : columnName;
3601
3684
  }
3602
- function replaceOrderByAlias(orderBy, fromAlias, outerAlias) {
3603
- const src = String(fromAlias);
3604
- if (src.length === 0) return orderBy;
3605
- const escaped = src.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
3606
- const re = new RegExp(`\\b${escaped}\\.`, "gi");
3607
- return orderBy.replace(re, outerAlias + ".");
3685
+ function parseSimpleScalarSelect(select, fromAlias) {
3686
+ const raw = select.trim();
3687
+ if (raw.length === 0) return [];
3688
+ const parts = raw.split(SQL_SEPARATORS.FIELD_LIST);
3689
+ const names = [];
3690
+ for (const p of parts) {
3691
+ const trimmed = p.trim();
3692
+ if (trimmed.length === 0) continue;
3693
+ names.push(parseSelectField(trimmed, fromAlias));
3694
+ }
3695
+ return names;
3608
3696
  }
3609
3697
  function buildDistinctColumns(distinct, fromAlias, model) {
3610
3698
  return distinct.map((f) => col(fromAlias, f, model)).join(SQL_SEPARATORS.FIELD_LIST);
@@ -3628,14 +3716,60 @@ function buildWindowOrder(args) {
3628
3716
  const idTiebreaker = idField ? ", " + col(fromAlias, "id", model) + " ASC" : "";
3629
3717
  return baseOrder + idTiebreaker;
3630
3718
  }
3631
- function buildSqliteDistinctQuery(spec, selectWithIncludes, countJoins) {
3632
- var _a, _b;
3633
- const { includes, from, whereClause, whereJoins, orderBy, distinct, model } = spec;
3634
- if (!isNotNullish(distinct) || !isNonEmptyArray(distinct)) {
3635
- throw new Error("buildSqliteDistinctQuery requires distinct fields");
3636
- }
3637
- const scalarNames = parseSimpleScalarSelect(spec.select, from.alias);
3638
- const includeNames = includes.map((i) => i.name);
3719
+ function extractDistinctOrderEntries(spec) {
3720
+ if (isNotNullish(spec.args.orderBy)) {
3721
+ const normalized = normalizeOrderByInput(
3722
+ spec.args.orderBy,
3723
+ parseOrderByValue
3724
+ );
3725
+ const entries = [];
3726
+ for (const item of normalized) {
3727
+ for (const field in item) {
3728
+ if (!Object.prototype.hasOwnProperty.call(item, field)) continue;
3729
+ const value = item[field];
3730
+ if (typeof value === "string") {
3731
+ entries.push({ field, direction: value });
3732
+ } else {
3733
+ const obj = value;
3734
+ entries.push({ field, direction: obj.sort, nulls: obj.nulls });
3735
+ }
3736
+ }
3737
+ }
3738
+ if (entries.length > 0) return entries;
3739
+ }
3740
+ if (isNotNullish(spec.distinct) && isNonEmptyArray(spec.distinct)) {
3741
+ return [...spec.distinct].map((f) => ({
3742
+ field: f,
3743
+ direction: "asc"
3744
+ }));
3745
+ }
3746
+ return [];
3747
+ }
3748
+ function buildFieldNameOrderBy(entries, alias) {
3749
+ if (entries.length === 0) return "";
3750
+ const out = [];
3751
+ for (const e of entries) {
3752
+ const dir = e.direction.toUpperCase();
3753
+ const c = `${alias}.${quote(e.field)}`;
3754
+ if (isNotNullish(e.nulls)) {
3755
+ const isNullExpr = `(${c} IS NULL)`;
3756
+ const nullRankDir = e.nulls === "first" ? "DESC" : "ASC";
3757
+ out.push(isNullExpr + " " + nullRankDir);
3758
+ out.push(c + " " + dir);
3759
+ continue;
3760
+ }
3761
+ out.push(c + " " + dir);
3762
+ }
3763
+ return out.join(SQL_SEPARATORS.ORDER_BY);
3764
+ }
3765
+ function buildSqliteDistinctQuery(spec, selectWithIncludes, countJoins) {
3766
+ var _a, _b;
3767
+ const { includes, from, whereClause, whereJoins, orderBy, distinct, model } = spec;
3768
+ if (!isNotNullish(distinct) || !isNonEmptyArray(distinct)) {
3769
+ throw new Error("buildSqliteDistinctQuery requires distinct fields");
3770
+ }
3771
+ const scalarNames = parseSimpleScalarSelect(spec.select, from.alias);
3772
+ const includeNames = includes.map((i) => i.name);
3639
3773
  const hasCount = Boolean((_b = (_a = spec.args) == null ? void 0 : _a.select) == null ? void 0 : _b._count);
3640
3774
  const outerSelectCols = buildOutputColumns(
3641
3775
  scalarNames,
@@ -3654,7 +3788,8 @@ function buildSqliteDistinctQuery(spec, selectWithIncludes, countJoins) {
3654
3788
  fromAlias: from.alias,
3655
3789
  model
3656
3790
  });
3657
- const outerOrder = isNonEmptyString(orderBy) ? replaceOrderByAlias(orderBy, from.alias, '"__tp_distinct"') : replaceOrderByAlias(fallbackOrder, from.alias, '"__tp_distinct"');
3791
+ const outerEntries = extractDistinctOrderEntries(spec);
3792
+ const outerOrder = buildFieldNameOrderBy(outerEntries, '"__tp_distinct"');
3658
3793
  const joins = buildJoinsSql(whereJoins, countJoins);
3659
3794
  const conditions = [];
3660
3795
  if (whereClause && whereClause !== "1=1") conditions.push(whereClause);
@@ -3689,17 +3824,33 @@ function buildSqliteDistinctQuery(spec, selectWithIncludes, countJoins) {
3689
3824
  }
3690
3825
  return outerParts.join(" ");
3691
3826
  }
3827
+ function resolveCountSelect(countSelectRaw, model) {
3828
+ if (countSelectRaw === true) {
3829
+ const relationSet = getRelationFieldSet(model);
3830
+ if (relationSet.size === 0) return null;
3831
+ const allRelations = {};
3832
+ for (const name of relationSet) {
3833
+ allRelations[name] = true;
3834
+ }
3835
+ return allRelations;
3836
+ }
3837
+ if (isPlainObject(countSelectRaw) && "select" in countSelectRaw) {
3838
+ return countSelectRaw.select;
3839
+ }
3840
+ return null;
3841
+ }
3692
3842
  function buildIncludeColumns(spec) {
3693
3843
  var _a, _b;
3694
3844
  const { select, includes, dialect, model, schemas, from, params } = spec;
3695
3845
  const baseSelect = (select != null ? select : "").trim();
3696
3846
  let countCols = "";
3697
3847
  let countJoins = [];
3698
- const countSelect = (_b = (_a = spec.args) == null ? void 0 : _a.select) == null ? void 0 : _b._count;
3699
- if (countSelect) {
3700
- if (isPlainObject(countSelect) && "select" in countSelect) {
3848
+ const countSelectRaw = (_b = (_a = spec.args) == null ? void 0 : _a.select) == null ? void 0 : _b._count;
3849
+ if (countSelectRaw) {
3850
+ const resolvedCountSelect = resolveCountSelect(countSelectRaw, model);
3851
+ if (resolvedCountSelect && Object.keys(resolvedCountSelect).length > 0) {
3701
3852
  const countBuild = buildRelationCountSql(
3702
- countSelect.select,
3853
+ resolvedCountSelect,
3703
3854
  model,
3704
3855
  schemas,
3705
3856
  from.alias,
@@ -4524,32 +4675,32 @@ function buildGroupBySql(args, whereResult, tableName, alias, model, dialect) {
4524
4675
  paramMappings: allMappings
4525
4676
  };
4526
4677
  }
4678
+ function isPositiveInteger(value) {
4679
+ return Number.isFinite(value) && Number.isInteger(value) && value > 0;
4680
+ }
4681
+ function parseSkipValue(skip) {
4682
+ return typeof skip === "string" ? Number(skip.trim()) : skip;
4683
+ }
4684
+ function validateSkipParameter(skip) {
4685
+ if (skip === void 0 || skip === null) {
4686
+ return;
4687
+ }
4688
+ if (isDynamicParameter(skip)) {
4689
+ throw new Error(
4690
+ "count() with skip is not supported because it produces nondeterministic results. Dynamic skip cannot be validated at build time. Use findMany().length or add explicit orderBy + cursor/skip logic in a deterministic query."
4691
+ );
4692
+ }
4693
+ const skipValue = parseSkipValue(skip);
4694
+ if (isPositiveInteger(skipValue)) {
4695
+ throw new Error(
4696
+ "count() with skip is not supported because it produces nondeterministic results. Use findMany().length or add explicit orderBy to ensure deterministic behavior."
4697
+ );
4698
+ }
4699
+ }
4527
4700
  function buildCountSql(whereResult, tableName, alias, skip, _dialect) {
4528
4701
  assertSafeAlias(alias);
4529
4702
  assertSafeTableRef(tableName);
4530
- if (skip !== void 0 && skip !== null) {
4531
- if (isDynamicParameter(skip)) {
4532
- throw new Error(
4533
- "count() with skip is not supported because it produces nondeterministic results. Dynamic skip cannot be validated at build time. Use findMany().length or add explicit orderBy + cursor/skip logic in a deterministic query."
4534
- );
4535
- }
4536
- if (typeof skip === "string") {
4537
- const s = skip.trim();
4538
- if (s.length > 0) {
4539
- const n = Number(s);
4540
- if (Number.isFinite(n) && Number.isInteger(n) && n > 0) {
4541
- throw new Error(
4542
- "count() with skip is not supported because it produces nondeterministic results. Use findMany().length or add explicit orderBy to ensure deterministic behavior."
4543
- );
4544
- }
4545
- }
4546
- }
4547
- if (typeof skip === "number" && Number.isFinite(skip) && Number.isInteger(skip) && skip > 0) {
4548
- throw new Error(
4549
- "count() with skip is not supported because it produces nondeterministic results. Use findMany().length or add explicit orderBy to ensure deterministic behavior."
4550
- );
4551
- }
4552
- }
4703
+ validateSkipParameter(skip);
4553
4704
  const whereClause = isValidWhereClause(whereResult.clause) ? SQL_TEMPLATES.WHERE + " " + whereResult.clause : "";
4554
4705
  const parts = [
4555
4706
  SQL_TEMPLATES.SELECT,
@@ -4774,62 +4925,6 @@ function generateSQL(directive) {
4774
4925
  });
4775
4926
  }
4776
4927
 
4777
- // src/result-transformers.ts
4778
- function parseAggregateValue(value) {
4779
- if (typeof value === "string" && /^-?\d+(\.\d+)?$/.test(value)) {
4780
- return parseFloat(value);
4781
- }
4782
- return value;
4783
- }
4784
- function transformGroupByResults(results) {
4785
- return results.map((row) => {
4786
- const raw = row;
4787
- const parsed = {};
4788
- for (const [key, value] of Object.entries(raw)) {
4789
- const parts = key.split(".");
4790
- if (parts.length === 2) {
4791
- const [group, field] = parts;
4792
- if (!parsed[group]) parsed[group] = {};
4793
- parsed[group][field] = parseAggregateValue(value);
4794
- } else {
4795
- parsed[key] = value;
4796
- }
4797
- }
4798
- return parsed;
4799
- });
4800
- }
4801
- function transformCountResults(results) {
4802
- const result = results[0];
4803
- const count = (result == null ? void 0 : result["_count._all"]) || (result == null ? void 0 : result.count) || 0;
4804
- return typeof count === "string" ? parseInt(count, 10) : count;
4805
- }
4806
- function transformAggregateResults(results) {
4807
- const raw = results[0] || {};
4808
- const parsed = {};
4809
- for (const [key, value] of Object.entries(raw)) {
4810
- const parts = key.split(".");
4811
- if (parts.length === 2) {
4812
- const [group, field] = parts;
4813
- if (!parsed[group]) parsed[group] = {};
4814
- parsed[group][field] = parseAggregateValue(value);
4815
- } else {
4816
- parsed[key] = value;
4817
- }
4818
- }
4819
- return parsed;
4820
- }
4821
- var RESULT_TRANSFORMERS = {
4822
- findFirst: (results) => results[0] || null,
4823
- findUnique: (results) => results[0] || null,
4824
- count: transformCountResults,
4825
- aggregate: transformAggregateResults,
4826
- groupBy: transformGroupByResults
4827
- };
4828
- function transformQueryResults(method, results) {
4829
- const transformer = RESULT_TRANSFORMERS[method];
4830
- return transformer ? transformer(results) : results;
4831
- }
4832
-
4833
4928
  // src/utils/s3-fifo.ts
4834
4929
  function withDispose(it) {
4835
4930
  const anyIt = it;
@@ -5049,89 +5144,309 @@ function createBoundedCache(maxSize) {
5049
5144
  return new BoundedCache(maxSize);
5050
5145
  }
5051
5146
 
5147
+ // src/fast-path.ts
5148
+ function getIdField(model) {
5149
+ const idField = model.fields.find((f) => f.name === "id" && !f.isRelation);
5150
+ if (!idField) return null;
5151
+ return {
5152
+ name: "id",
5153
+ dbName: idField.dbName || "id"
5154
+ };
5155
+ }
5156
+ function buildColumnList(model, alias) {
5157
+ const cols = [];
5158
+ for (const f of model.fields) {
5159
+ if (f.isRelation) continue;
5160
+ if (f.name.startsWith("@") || f.name.startsWith("//")) continue;
5161
+ const dbName = f.dbName || f.name;
5162
+ if (dbName !== f.name) {
5163
+ cols.push(`${alias}.${quote(dbName)} AS ${quote(f.name)}`);
5164
+ } else {
5165
+ cols.push(`${alias}.${quote(dbName)}`);
5166
+ }
5167
+ }
5168
+ return cols.join(", ");
5169
+ }
5170
+ function getTableAndAlias(model, dialect) {
5171
+ const tableName = buildTableReference(
5172
+ SQL_TEMPLATES.PUBLIC_SCHEMA,
5173
+ model.tableName,
5174
+ dialect
5175
+ );
5176
+ const alias = makeAlias(model.tableName);
5177
+ return { tableName, alias };
5178
+ }
5179
+ function buildSimpleQuery(model, dialect, where, params, suffix = "") {
5180
+ const { tableName, alias } = getTableAndAlias(model, dialect);
5181
+ const columns = buildColumnList(model, alias);
5182
+ const sql = `SELECT ${columns} FROM ${tableName} ${alias} ${where}${suffix}`;
5183
+ return { sql, params };
5184
+ }
5185
+ function norm(value) {
5186
+ return normalizeValue(value);
5187
+ }
5188
+ function isScalar(value) {
5189
+ return value !== null && typeof value !== "object";
5190
+ }
5191
+ function tryFindUniqueById(model, args, dialect) {
5192
+ const where = args.where;
5193
+ if (!isPlainObject(where) || Object.keys(where).length !== 1 || !("id" in where) || !isScalar(where.id) || args.select || args.include) {
5194
+ return null;
5195
+ }
5196
+ const idField = getIdField(model);
5197
+ if (!idField) return null;
5198
+ const { alias } = getTableAndAlias(model, dialect);
5199
+ const whereClause = dialect === "sqlite" ? `WHERE ${alias}.${quote(idField.dbName)} = ?` : `WHERE ${alias}.${quote(idField.dbName)} = $1`;
5200
+ return buildSimpleQuery(
5201
+ model,
5202
+ dialect,
5203
+ whereClause,
5204
+ [norm(where.id)],
5205
+ " LIMIT 1"
5206
+ );
5207
+ }
5208
+ function tryFindManyById(model, args, dialect) {
5209
+ const where = args.where;
5210
+ if (!isPlainObject(where) || Object.keys(where).length !== 1 || !("id" in where) || !isScalar(where.id) || args.select || args.include || args.orderBy || args.take || args.skip || args.distinct || args.cursor) {
5211
+ return null;
5212
+ }
5213
+ const idField = getIdField(model);
5214
+ if (!idField) return null;
5215
+ const { alias } = getTableAndAlias(model, dialect);
5216
+ const whereClause = dialect === "sqlite" ? `WHERE ${alias}.${quote(idField.dbName)} = ?` : `WHERE ${alias}.${quote(idField.dbName)} = $1`;
5217
+ return buildSimpleQuery(model, dialect, whereClause, [norm(where.id)]);
5218
+ }
5219
+ function tryCountAll(model, args, dialect) {
5220
+ const where = args.where;
5221
+ if (where && Object.keys(where).length > 0 || args.select || args.skip) {
5222
+ return null;
5223
+ }
5224
+ const { tableName, alias } = getTableAndAlias(model, dialect);
5225
+ const sql = `SELECT COUNT(*) AS ${quote("_count._all")} FROM ${tableName} ${alias}`;
5226
+ return { sql, params: [] };
5227
+ }
5228
+ function tryFindManyWithLimit(model, args, dialect) {
5229
+ const where = args.where;
5230
+ if (where && Object.keys(where).length > 0 || args.select || args.include || args.orderBy || args.skip || args.distinct || args.cursor || typeof args.take !== "number" || !Number.isInteger(args.take) || args.take <= 0) {
5231
+ return null;
5232
+ }
5233
+ const { tableName, alias } = getTableAndAlias(model, dialect);
5234
+ const columns = buildColumnList(model, alias);
5235
+ const sql = dialect === "sqlite" ? `SELECT ${columns} FROM ${tableName} ${alias} LIMIT ?` : `SELECT ${columns} FROM ${tableName} ${alias} LIMIT $1`;
5236
+ return { sql, params: [norm(args.take)] };
5237
+ }
5238
+ function tryFindFirstBySingleField(model, args, dialect) {
5239
+ const where = args.where;
5240
+ if (!isPlainObject(where) || Object.keys(where).length !== 1 || args.select || args.include || args.orderBy || args.skip) {
5241
+ return null;
5242
+ }
5243
+ const field = Object.keys(where)[0];
5244
+ const value = where[field];
5245
+ if (value === null || typeof value === "object") {
5246
+ return null;
5247
+ }
5248
+ const fieldDef = model.fields.find((f) => f.name === field);
5249
+ if (!fieldDef || fieldDef.isRelation) {
5250
+ return null;
5251
+ }
5252
+ const { alias } = getTableAndAlias(model, dialect);
5253
+ const columnName = fieldDef.dbName || field;
5254
+ const whereClause = dialect === "sqlite" ? `WHERE ${alias}.${quote(columnName)} = ?` : `WHERE ${alias}.${quote(columnName)} = $1`;
5255
+ return buildSimpleQuery(
5256
+ model,
5257
+ dialect,
5258
+ whereClause,
5259
+ [norm(value)],
5260
+ " LIMIT 1"
5261
+ );
5262
+ }
5263
+ function tryFindManyBySingleField(model, args, dialect) {
5264
+ const where = args.where;
5265
+ if (!isPlainObject(where) || Object.keys(where).length !== 1 || args.select || args.include || args.orderBy || args.take || args.skip || args.distinct || args.cursor) {
5266
+ return null;
5267
+ }
5268
+ const field = Object.keys(where)[0];
5269
+ const value = where[field];
5270
+ if (value === null || typeof value === "object") {
5271
+ return null;
5272
+ }
5273
+ const fieldDef = model.fields.find((f) => f.name === field);
5274
+ if (!fieldDef || fieldDef.isRelation) {
5275
+ return null;
5276
+ }
5277
+ const { alias } = getTableAndAlias(model, dialect);
5278
+ const columnName = fieldDef.dbName || field;
5279
+ const whereClause = dialect === "sqlite" ? `WHERE ${alias}.${quote(columnName)} = ?` : `WHERE ${alias}.${quote(columnName)} = $1`;
5280
+ return buildSimpleQuery(model, dialect, whereClause, [norm(value)]);
5281
+ }
5282
+ function tryFindManyAll(model, args, dialect) {
5283
+ const where = args.where;
5284
+ if (where && Object.keys(where).length > 0 || args.select || args.include || args.orderBy || args.take !== void 0 || args.skip !== void 0 || args.distinct || args.cursor) {
5285
+ return null;
5286
+ }
5287
+ const { tableName, alias } = getTableAndAlias(model, dialect);
5288
+ const columns = buildColumnList(model, alias);
5289
+ const sql = `SELECT ${columns} FROM ${tableName} ${alias}`;
5290
+ return { sql, params: [] };
5291
+ }
5292
+ function tryFastPath(model, method, args, dialect) {
5293
+ if (method === "findUnique") {
5294
+ return tryFindUniqueById(model, args, dialect);
5295
+ }
5296
+ if (method === "count") {
5297
+ return tryCountAll(model, args, dialect);
5298
+ }
5299
+ if (method === "findFirst") {
5300
+ return tryFindFirstBySingleField(model, args, dialect);
5301
+ }
5302
+ if (method === "findMany") {
5303
+ return tryFindManyById(model, args, dialect) || tryFindManyWithLimit(model, args, dialect) || tryFindManyBySingleField(model, args, dialect) || tryFindManyAll(model, args, dialect);
5304
+ }
5305
+ return null;
5306
+ }
5307
+
5052
5308
  // src/query-cache.ts
5309
+ var _hits, _misses;
5310
+ var QueryCacheStats = class {
5311
+ constructor() {
5312
+ __privateAdd(this, _hits, 0);
5313
+ __privateAdd(this, _misses, 0);
5314
+ }
5315
+ hit() {
5316
+ __privateWrapper(this, _hits)._++;
5317
+ }
5318
+ miss() {
5319
+ __privateWrapper(this, _misses)._++;
5320
+ }
5321
+ reset() {
5322
+ __privateSet(this, _hits, 0);
5323
+ __privateSet(this, _misses, 0);
5324
+ }
5325
+ get snapshot() {
5326
+ return Object.freeze({
5327
+ hits: __privateGet(this, _hits),
5328
+ misses: __privateGet(this, _misses),
5329
+ size: queryCache.size
5330
+ });
5331
+ }
5332
+ };
5333
+ _hits = new WeakMap();
5334
+ _misses = new WeakMap();
5053
5335
  var queryCache = createBoundedCache(1e3);
5336
+ var queryCacheStats = new QueryCacheStats();
5054
5337
  function makeAlias(name) {
5055
5338
  const base = name.toLowerCase().replace(/[^a-z0-9_]/g, "_").slice(0, 50);
5056
5339
  const safe = /^[a-z_]/.test(base) ? base : `_${base}`;
5057
5340
  return SQL_RESERVED_WORDS.has(safe) ? `${safe}_t` : safe;
5058
5341
  }
5059
- function toSqliteParams(sql, params) {
5060
- const reorderedParams = [];
5342
+ function handleSingleQuote(sql, state) {
5061
5343
  const n = sql.length;
5062
- let i = 0;
5063
- let out = "";
5064
- let mode = "normal";
5344
+ let i = state.position;
5345
+ let out = state.output + sql[i];
5346
+ i++;
5065
5347
  while (i < n) {
5066
- const ch = sql.charCodeAt(i);
5067
- if (mode === "normal") {
5068
- if (ch === 39) {
5069
- out += sql[i];
5070
- mode = "single";
5071
- i++;
5348
+ out += sql[i];
5349
+ if (sql.charCodeAt(i) === 39) {
5350
+ if (i + 1 < n && sql.charCodeAt(i + 1) === 39) {
5351
+ out += sql[i + 1];
5352
+ i += 2;
5072
5353
  continue;
5073
5354
  }
5074
- if (ch === 34) {
5075
- out += sql[i];
5076
- mode = "double";
5077
- i++;
5355
+ return __spreadProps(__spreadValues({}, state), { mode: "normal", position: i + 1, output: out });
5356
+ }
5357
+ i++;
5358
+ }
5359
+ return __spreadProps(__spreadValues({}, state), { position: i, output: out });
5360
+ }
5361
+ function handleDoubleQuote(sql, state) {
5362
+ const n = sql.length;
5363
+ let i = state.position;
5364
+ let out = state.output + sql[i];
5365
+ i++;
5366
+ while (i < n) {
5367
+ out += sql[i];
5368
+ if (sql.charCodeAt(i) === 34) {
5369
+ if (i + 1 < n && sql.charCodeAt(i + 1) === 34) {
5370
+ out += sql[i + 1];
5371
+ i += 2;
5078
5372
  continue;
5079
5373
  }
5080
- if (ch === 36) {
5081
- let j = i + 1;
5082
- let num = 0;
5083
- let hasDigit = false;
5084
- while (j < n) {
5085
- const d = sql.charCodeAt(j);
5086
- if (d >= 48 && d <= 57) {
5087
- num = num * 10 + (d - 48);
5088
- hasDigit = true;
5089
- j++;
5090
- } else {
5091
- break;
5092
- }
5093
- }
5094
- if (hasDigit && num >= 1) {
5095
- out += "?";
5096
- reorderedParams.push(params[num - 1]);
5097
- i = j;
5098
- continue;
5099
- }
5100
- }
5101
- out += sql[i];
5102
- i++;
5374
+ return __spreadProps(__spreadValues({}, state), { mode: "normal", position: i + 1, output: out });
5375
+ }
5376
+ i++;
5377
+ }
5378
+ return __spreadProps(__spreadValues({}, state), { position: i, output: out });
5379
+ }
5380
+ function extractParameterNumber(sql, startPos) {
5381
+ const n = sql.length;
5382
+ let j = startPos + 1;
5383
+ let num = 0;
5384
+ let hasDigit = false;
5385
+ while (j < n) {
5386
+ const d = sql.charCodeAt(j);
5387
+ if (d >= 48 && d <= 57) {
5388
+ num = num * 10 + (d - 48);
5389
+ hasDigit = true;
5390
+ j++;
5391
+ } else {
5392
+ break;
5393
+ }
5394
+ }
5395
+ if (hasDigit && num >= 1) {
5396
+ return { num, nextPos: j };
5397
+ }
5398
+ return null;
5399
+ }
5400
+ function handleParameterSubstitution(sql, params, state) {
5401
+ const result = extractParameterNumber(sql, state.position);
5402
+ if (result) {
5403
+ return __spreadProps(__spreadValues({}, state), {
5404
+ position: result.nextPos,
5405
+ output: state.output + "?",
5406
+ reorderedParams: [...state.reorderedParams, params[result.num - 1]]
5407
+ });
5408
+ }
5409
+ return __spreadProps(__spreadValues({}, state), {
5410
+ position: state.position + 1,
5411
+ output: state.output + sql[state.position]
5412
+ });
5413
+ }
5414
+ function toSqliteParams(sql, params) {
5415
+ const n = sql.length;
5416
+ let state = {
5417
+ mode: "normal",
5418
+ position: 0,
5419
+ output: "",
5420
+ reorderedParams: []
5421
+ };
5422
+ while (state.position < n) {
5423
+ const ch = sql.charCodeAt(state.position);
5424
+ if (state.mode === "single") {
5425
+ state = handleSingleQuote(sql, state);
5103
5426
  continue;
5104
5427
  }
5105
- if (mode === "single") {
5106
- out += sql[i];
5107
- if (ch === 39) {
5108
- if (i + 1 < n && sql.charCodeAt(i + 1) === 39) {
5109
- out += sql[i + 1];
5110
- i += 2;
5111
- continue;
5112
- }
5113
- mode = "normal";
5114
- }
5115
- i++;
5428
+ if (state.mode === "double") {
5429
+ state = handleDoubleQuote(sql, state);
5116
5430
  continue;
5117
5431
  }
5118
- if (mode === "double") {
5119
- out += sql[i];
5120
- if (ch === 34) {
5121
- if (i + 1 < n && sql.charCodeAt(i + 1) === 34) {
5122
- out += sql[i + 1];
5123
- i += 2;
5124
- continue;
5125
- }
5126
- mode = "normal";
5127
- }
5128
- i++;
5432
+ if (ch === 39) {
5433
+ state = __spreadProps(__spreadValues({}, state), { mode: "single" });
5129
5434
  continue;
5130
5435
  }
5131
- out += sql[i];
5132
- i++;
5436
+ if (ch === 34) {
5437
+ state = __spreadProps(__spreadValues({}, state), { mode: "double" });
5438
+ continue;
5439
+ }
5440
+ if (ch === 36) {
5441
+ state = handleParameterSubstitution(sql, params, state);
5442
+ continue;
5443
+ }
5444
+ state = __spreadProps(__spreadValues({}, state), {
5445
+ position: state.position + 1,
5446
+ output: state.output + sql[state.position]
5447
+ });
5133
5448
  }
5134
- return { sql: out, params: reorderedParams };
5449
+ return { sql: state.output, params: state.reorderedParams };
5135
5450
  }
5136
5451
  function canonicalizeReplacer(key, value) {
5137
5452
  if (typeof value === "bigint") return `__bigint__${value.toString()}`;
@@ -5139,11 +5454,7 @@ function canonicalizeReplacer(key, value) {
5139
5454
  const obj = value;
5140
5455
  const sorted = {};
5141
5456
  for (const k of Object.keys(obj).sort()) {
5142
- const v = obj[k];
5143
- if (v && typeof v === "object" && !Array.isArray(v) && Object.keys(v).length === 0) {
5144
- continue;
5145
- }
5146
- sorted[k] = v;
5457
+ sorted[k] = obj[k];
5147
5458
  }
5148
5459
  return sorted;
5149
5460
  }
@@ -5217,11 +5528,20 @@ function buildSQLWithCache(model, models, method, args, dialect) {
5217
5528
  const cacheKey = canonicalizeQuery(model.name, method, args, dialect);
5218
5529
  const cached = queryCache.get(cacheKey);
5219
5530
  if (cached) {
5531
+ queryCacheStats.hit();
5220
5532
  return { sql: cached.sql, params: [...cached.params] };
5221
5533
  }
5534
+ queryCacheStats.miss();
5535
+ const fastResult = tryFastPath(model, method, args, dialect);
5536
+ if (fastResult) {
5537
+ queryCache.set(cacheKey, {
5538
+ sql: fastResult.sql,
5539
+ params: [...fastResult.params]
5540
+ });
5541
+ return fastResult;
5542
+ }
5222
5543
  const result = buildSQLFull(model, models, method, args, dialect);
5223
- queryCache.set(cacheKey, result);
5224
- queryCache.size;
5544
+ queryCache.set(cacheKey, { sql: result.sql, params: [...result.params] });
5225
5545
  return result;
5226
5546
  }
5227
5547
 
@@ -5234,159 +5554,172 @@ function quoteBatchIdent(id) {
5234
5554
  function makeBatchAlias(i) {
5235
5555
  return `k${i}`;
5236
5556
  }
5237
- function replacePgPlaceholders(sql, replace) {
5238
- const s = String(sql);
5239
- const n = s.length;
5240
- let i = 0;
5241
- let mode = "normal";
5242
- let dollarTag = null;
5243
- let out = "";
5244
- const startsWith = (pos, lit) => s.slice(pos, pos + lit.length) === lit;
5245
- const readDollarTag = (pos) => {
5246
- if (s.charCodeAt(pos) !== 36) return null;
5247
- let j = pos + 1;
5248
- while (j < n) {
5249
- const c = s.charCodeAt(j);
5250
- if (c >= 48 && c <= 57 || c >= 65 && c <= 90 || c >= 97 && c <= 122 || c === 95) {
5251
- j++;
5252
- continue;
5253
- }
5254
- break;
5255
- }
5256
- if (j < n && s.charCodeAt(j) === 36 && j > pos) {
5257
- return s.slice(pos, j + 1);
5258
- }
5259
- if (pos + 1 < n && s.charCodeAt(pos + 1) === 36) {
5260
- return "$$";
5261
- }
5262
- return null;
5263
- };
5264
- while (i < n) {
5265
- const ch = s.charCodeAt(i);
5266
- if (mode === "normal") {
5267
- if (ch === 39) {
5268
- out += s[i];
5269
- mode = "single";
5270
- i++;
5271
- continue;
5272
- }
5273
- if (ch === 34) {
5274
- out += s[i];
5275
- mode = "double";
5276
- i++;
5277
- continue;
5278
- }
5279
- if (ch === 45 && i + 1 < n && s.charCodeAt(i + 1) === 45) {
5280
- out += "--";
5281
- mode = "lineComment";
5282
- i += 2;
5283
- continue;
5284
- }
5285
- if (ch === 47 && i + 1 < n && s.charCodeAt(i + 1) === 42) {
5286
- out += "/*";
5287
- mode = "blockComment";
5288
- i += 2;
5289
- continue;
5290
- }
5291
- if (ch === 36) {
5292
- const tag = readDollarTag(i);
5293
- if (tag) {
5294
- out += tag;
5295
- mode = "dollar";
5296
- dollarTag = tag;
5297
- i += tag.length;
5298
- continue;
5299
- }
5300
- let j = i + 1;
5301
- if (j < n) {
5302
- const c1 = s.charCodeAt(j);
5303
- if (c1 >= 48 && c1 <= 57) {
5304
- while (j < n) {
5305
- const cj = s.charCodeAt(j);
5306
- if (cj >= 48 && cj <= 57) {
5307
- j++;
5308
- continue;
5309
- }
5310
- break;
5311
- }
5312
- const numStr = s.slice(i + 1, j);
5313
- const oldIndex = Number(numStr);
5314
- if (!Number.isInteger(oldIndex) || oldIndex < 1) {
5315
- throw new Error(`Invalid param placeholder: $${numStr}`);
5316
- }
5317
- out += replace(oldIndex);
5318
- i = j;
5319
- continue;
5320
- }
5321
- }
5322
- }
5323
- out += s[i];
5324
- i++;
5325
- continue;
5326
- }
5327
- if (mode === "single") {
5328
- out += s[i];
5329
- if (ch === 39) {
5330
- if (i + 1 < n && s.charCodeAt(i + 1) === 39) {
5331
- out += s[i + 1];
5332
- i += 2;
5333
- continue;
5334
- }
5335
- mode = "normal";
5336
- i++;
5337
- continue;
5338
- }
5339
- i++;
5340
- continue;
5341
- }
5342
- if (mode === "double") {
5343
- out += s[i];
5344
- if (ch === 34) {
5345
- if (i + 1 < n && s.charCodeAt(i + 1) === 34) {
5346
- out += s[i + 1];
5347
- i += 2;
5348
- continue;
5349
- }
5350
- mode = "normal";
5351
- i++;
5352
- continue;
5353
- }
5354
- i++;
5355
- continue;
5356
- }
5357
- if (mode === "lineComment") {
5358
- out += s[i];
5359
- if (ch === 10) {
5360
- mode = "normal";
5361
- }
5362
- i++;
5363
- continue;
5364
- }
5365
- if (mode === "blockComment") {
5366
- if (ch === 42 && i + 1 < n && s.charCodeAt(i + 1) === 47) {
5367
- out += "*/";
5368
- i += 2;
5369
- mode = "normal";
5370
- continue;
5371
- }
5372
- out += s[i];
5373
- i++;
5374
- continue;
5557
+ function isDigit(charCode) {
5558
+ return charCode >= 48 && charCode <= 57;
5559
+ }
5560
+ function isAlphaNumericOrUnderscore(charCode) {
5561
+ return isDigit(charCode) || charCode >= 65 && charCode <= 90 || charCode >= 97 && charCode <= 122 || charCode === 95;
5562
+ }
5563
+ function readDollarTag(s, pos, n) {
5564
+ if (s.charCodeAt(pos) !== 36) return null;
5565
+ let j = pos + 1;
5566
+ while (j < n && isAlphaNumericOrUnderscore(s.charCodeAt(j))) {
5567
+ j++;
5568
+ }
5569
+ if (j < n && s.charCodeAt(j) === 36 && j > pos) {
5570
+ return s.slice(pos, j + 1);
5571
+ }
5572
+ if (pos + 1 < n && s.charCodeAt(pos + 1) === 36) {
5573
+ return "$$";
5574
+ }
5575
+ return null;
5576
+ }
5577
+ function parseParamPlaceholder(s, i, n, replace) {
5578
+ let j = i + 1;
5579
+ if (j >= n) {
5580
+ return { consumed: 1, output: s[i] };
5581
+ }
5582
+ const c1 = s.charCodeAt(j);
5583
+ if (!isDigit(c1)) {
5584
+ return { consumed: 1, output: s[i] };
5585
+ }
5586
+ while (j < n && isDigit(s.charCodeAt(j))) {
5587
+ j++;
5588
+ }
5589
+ const numStr = s.slice(i + 1, j);
5590
+ const oldIndex = Number(numStr);
5591
+ if (!Number.isInteger(oldIndex) || oldIndex < 1) {
5592
+ throw new Error(`Invalid param placeholder: $${numStr}`);
5593
+ }
5594
+ return { consumed: j - i, output: replace(oldIndex) };
5595
+ }
5596
+ function handleDollarInNormalMode(s, i, n, state, replace) {
5597
+ const tag = readDollarTag(s, i, n);
5598
+ if (tag) {
5599
+ return {
5600
+ consumed: tag.length,
5601
+ output: tag,
5602
+ newState: { mode: "dollar", dollarTag: tag }
5603
+ };
5604
+ }
5605
+ const placeholder = parseParamPlaceholder(s, i, n, replace);
5606
+ return __spreadProps(__spreadValues({}, placeholder), { newState: state });
5607
+ }
5608
+ function handleCommentStart(s, i, n, ch) {
5609
+ if (ch === 45 && i + 1 < n && s.charCodeAt(i + 1) === 45) {
5610
+ return {
5611
+ consumed: 2,
5612
+ output: "--",
5613
+ newState: { mode: "lineComment", dollarTag: null }
5614
+ };
5615
+ }
5616
+ if (ch === 47 && i + 1 < n && s.charCodeAt(i + 1) === 42) {
5617
+ return {
5618
+ consumed: 2,
5619
+ output: "/*",
5620
+ newState: { mode: "blockComment", dollarTag: null }
5621
+ };
5622
+ }
5623
+ return null;
5624
+ }
5625
+ function processNormalMode(s, i, n, state, replace) {
5626
+ const ch = s.charCodeAt(i);
5627
+ if (ch === 39) {
5628
+ return {
5629
+ consumed: 1,
5630
+ output: s[i],
5631
+ newState: { mode: "single", dollarTag: null }
5632
+ };
5633
+ }
5634
+ if (ch === 34) {
5635
+ return {
5636
+ consumed: 1,
5637
+ output: s[i],
5638
+ newState: { mode: "double", dollarTag: null }
5639
+ };
5640
+ }
5641
+ const commentResult = handleCommentStart(s, i, n, ch);
5642
+ if (commentResult) return commentResult;
5643
+ if (ch === 36) {
5644
+ return handleDollarInNormalMode(s, i, n, state, replace);
5645
+ }
5646
+ return { consumed: 1, output: s[i], newState: state };
5647
+ }
5648
+ function processQuoteMode(s, i, n, quoteChar) {
5649
+ const ch = s.charCodeAt(i);
5650
+ if (ch === quoteChar) {
5651
+ if (i + 1 < n && s.charCodeAt(i + 1) === quoteChar) {
5652
+ return { consumed: 2, output: s[i] + s[i + 1], shouldExitMode: false };
5375
5653
  }
5376
- if (mode === "dollar") {
5377
- if (dollarTag && startsWith(i, dollarTag)) {
5378
- out += dollarTag;
5379
- i += dollarTag.length;
5380
- mode = "normal";
5381
- dollarTag = null;
5382
- continue;
5383
- }
5384
- out += s[i];
5385
- i++;
5386
- continue;
5654
+ return { consumed: 1, output: s[i], shouldExitMode: true };
5655
+ }
5656
+ return { consumed: 1, output: s[i], shouldExitMode: false };
5657
+ }
5658
+ function processBlockCommentMode(s, i, n) {
5659
+ const ch = s.charCodeAt(i);
5660
+ if (ch === 42 && i + 1 < n && s.charCodeAt(i + 1) === 47) {
5661
+ return { consumed: 2, output: "*/", shouldExitMode: true };
5662
+ }
5663
+ return { consumed: 1, output: s[i], shouldExitMode: false };
5664
+ }
5665
+ function processDollarMode(s, i, dollarTag) {
5666
+ if (s.slice(i, i + dollarTag.length) === dollarTag) {
5667
+ return {
5668
+ consumed: dollarTag.length,
5669
+ output: dollarTag,
5670
+ shouldExitMode: true
5671
+ };
5672
+ }
5673
+ return { consumed: 1, output: s[i], shouldExitMode: false };
5674
+ }
5675
+ function processLineCommentMode(ch) {
5676
+ return { shouldExitMode: ch === 10 };
5677
+ }
5678
+ function processCharacter(s, i, n, state, replace) {
5679
+ const ch = s.charCodeAt(i);
5680
+ switch (state.mode) {
5681
+ case "normal":
5682
+ return processNormalMode(s, i, n, state, replace);
5683
+ case "single":
5684
+ return processQuoteMode(s, i, n, 39);
5685
+ case "double":
5686
+ return processQuoteMode(s, i, n, 34);
5687
+ case "lineComment": {
5688
+ const result = processLineCommentMode(ch);
5689
+ return {
5690
+ consumed: 1,
5691
+ output: s[i],
5692
+ shouldExitMode: result.shouldExitMode
5693
+ };
5387
5694
  }
5388
- out += s[i];
5389
- i++;
5695
+ case "blockComment":
5696
+ return processBlockCommentMode(s, i, n);
5697
+ case "dollar":
5698
+ return state.dollarTag ? processDollarMode(s, i, state.dollarTag) : { consumed: 1, output: s[i], shouldExitMode: false };
5699
+ default:
5700
+ return { consumed: 1, output: s[i], shouldExitMode: false };
5701
+ }
5702
+ }
5703
+ function updateStateAfterProcessing(currentState, result) {
5704
+ if (result.newState) {
5705
+ return result.newState;
5706
+ }
5707
+ if (result.shouldExitMode) {
5708
+ return { mode: "normal", dollarTag: null };
5709
+ }
5710
+ return currentState;
5711
+ }
5712
+ function replacePgPlaceholders(sql, replace) {
5713
+ const s = String(sql);
5714
+ const n = s.length;
5715
+ let i = 0;
5716
+ let state = { mode: "normal", dollarTag: null };
5717
+ let out = "";
5718
+ while (i < n) {
5719
+ const result = processCharacter(s, i, n, state, replace);
5720
+ out += result.output;
5721
+ i += result.consumed;
5722
+ state = updateStateAfterProcessing(state, result);
5390
5723
  }
5391
5724
  return out;
5392
5725
  }
@@ -5439,21 +5772,70 @@ function wrapQueryForMethod(method, cteName, resultAlias) {
5439
5772
  }
5440
5773
  function isAllCountQueries(queries, keys) {
5441
5774
  var _a;
5442
- for (let i = 0; i < keys.length; i++) {
5443
- if (((_a = queries[keys[i]]) == null ? void 0 : _a.method) !== "count") return false;
5775
+ for (const key of keys) {
5776
+ if (((_a = queries[key]) == null ? void 0 : _a.method) !== "count") return false;
5444
5777
  }
5445
5778
  return true;
5446
5779
  }
5447
5780
  function looksTooComplexForFilter(sql) {
5448
5781
  const s = sql.toLowerCase();
5449
- if (s.includes(" group by ")) return true;
5450
- if (s.includes(" having ")) return true;
5451
- if (s.includes(" union ")) return true;
5452
- if (s.includes(" intersect ")) return true;
5453
- if (s.includes(" except ")) return true;
5454
- if (s.includes(" window ")) return true;
5455
- if (s.includes(" distinct ")) return true;
5456
- return false;
5782
+ const complexKeywords = [
5783
+ " group by ",
5784
+ " having ",
5785
+ " union ",
5786
+ " intersect ",
5787
+ " except ",
5788
+ " window ",
5789
+ " distinct "
5790
+ ];
5791
+ return complexKeywords.some((keyword) => s.includes(keyword));
5792
+ }
5793
+ function skipWhitespace(s, i) {
5794
+ while (i < s.length && /\s/.test(s[i])) {
5795
+ i++;
5796
+ }
5797
+ return i;
5798
+ }
5799
+ function matchKeyword(s, i, keyword) {
5800
+ const lower = s.slice(i).toLowerCase();
5801
+ if (!lower.startsWith(keyword)) return -1;
5802
+ const endPos = i + keyword.length;
5803
+ if (endPos < s.length && /[a-z0-9_]/i.test(s[endPos])) return -1;
5804
+ return endPos;
5805
+ }
5806
+ function parseQuotedIdentifier2(s, i) {
5807
+ let j = i + 1;
5808
+ while (j < s.length) {
5809
+ if (s[j] === '"') {
5810
+ if (j + 1 < s.length && s[j + 1] === '"') {
5811
+ j += 2;
5812
+ continue;
5813
+ }
5814
+ return { value: s.slice(i, j + 1), endPos: j + 1 };
5815
+ }
5816
+ j++;
5817
+ }
5818
+ return null;
5819
+ }
5820
+ function parseUnquotedIdentifier2(s, i) {
5821
+ if (!/[a-z_]/i.test(s[i])) return null;
5822
+ let j = i;
5823
+ while (j < s.length && /[a-z0-9_.]/i.test(s[j])) {
5824
+ j++;
5825
+ }
5826
+ return { value: s.slice(i, j), endPos: j };
5827
+ }
5828
+ function parseIdentifier(s, i) {
5829
+ if (i >= s.length) return null;
5830
+ if (s[i] === '"') {
5831
+ return parseQuotedIdentifier2(s, i);
5832
+ }
5833
+ return parseUnquotedIdentifier2(s, i);
5834
+ }
5835
+ function findFromClauseEnd(s, fromStart) {
5836
+ const lower = s.slice(fromStart).toLowerCase();
5837
+ const whereIdx = lower.indexOf(" where ");
5838
+ return whereIdx === -1 ? s.length : fromStart + whereIdx;
5457
5839
  }
5458
5840
  function parseSimpleCountSql(sql) {
5459
5841
  const trimmed = sql.trim().replace(/;$/, "").trim();
@@ -5461,111 +5843,181 @@ function parseSimpleCountSql(sql) {
5461
5843
  if (!lower.startsWith("select")) return null;
5462
5844
  if (!lower.includes("count(*)")) return null;
5463
5845
  if (looksTooComplexForFilter(trimmed)) return null;
5464
- const match = trimmed.match(
5465
- /^select\s+count\(\*\)(?:\s*::\s*[a-zA-Z0-9_\."]+)?\s+as\s+("[^"]+"|[a-zA-Z_][a-zA-Z0-9_\.]*)\s+from\s+([\s\S]+?)(?:\s+where\s+([\s\S]+))?$/i
5466
- );
5467
- if (!match) return null;
5468
- const fromSql = match[2].trim();
5469
- const whereSql = match[3] ? match[3].trim() : null;
5846
+ let pos = matchKeyword(trimmed, 0, "select");
5847
+ if (pos === -1) return null;
5848
+ pos = skipWhitespace(trimmed, pos);
5849
+ const countMatch = trimmed.slice(pos).match(/^count\(\*\)/i);
5850
+ if (!countMatch) return null;
5851
+ pos += countMatch[0].length;
5852
+ pos = skipWhitespace(trimmed, pos);
5853
+ const castMatch = trimmed.slice(pos).match(/^::\s*\w+/);
5854
+ if (castMatch) {
5855
+ pos += castMatch[0].length;
5856
+ pos = skipWhitespace(trimmed, pos);
5857
+ }
5858
+ const asPos = matchKeyword(trimmed, pos, "as");
5859
+ if (asPos === -1) return null;
5860
+ pos = skipWhitespace(trimmed, asPos);
5861
+ const ident = parseIdentifier(trimmed, pos);
5862
+ if (!ident) return null;
5863
+ pos = skipWhitespace(trimmed, ident.endPos);
5864
+ const fromPos = matchKeyword(trimmed, pos, "from");
5865
+ if (fromPos === -1) return null;
5866
+ pos = skipWhitespace(trimmed, fromPos);
5867
+ const fromEnd = findFromClauseEnd(trimmed, pos);
5868
+ const fromSql = trimmed.slice(pos, fromEnd).trim();
5470
5869
  if (!fromSql) return null;
5870
+ let whereSql = null;
5871
+ if (fromEnd < trimmed.length) {
5872
+ const adjustedPos = skipWhitespace(trimmed, fromEnd);
5873
+ const wherePos = matchKeyword(trimmed, adjustedPos, "where");
5874
+ if (wherePos !== -1) {
5875
+ whereSql = trimmed.slice(skipWhitespace(trimmed, wherePos)).trim();
5876
+ }
5877
+ }
5471
5878
  return { fromSql, whereSql };
5472
5879
  }
5473
- function buildMergedCountBatchSql(queries, keys, aliasesByKey, modelMap, models, dialect) {
5880
+ function processCountQuery(item, model, models, dialect, sharedFrom, localParams) {
5881
+ const built = buildSQLWithCache(model, models, "count", item.args, dialect);
5882
+ const parsed = parseSimpleCountSql(built.sql);
5883
+ if (!parsed) return null;
5884
+ if (containsPgPlaceholder(parsed.fromSql)) return null;
5885
+ const currentFrom = parsed.fromSql;
5886
+ if (sharedFrom !== null && sharedFrom !== currentFrom) return null;
5887
+ if (!parsed.whereSql) {
5888
+ if (built.params.length > 0) return null;
5889
+ return {
5890
+ expression: `count(*) AS ${quoteBatchIdent(item.alias)}`,
5891
+ reindexedParams: [],
5892
+ sharedFrom: currentFrom
5893
+ };
5894
+ }
5895
+ const re = reindexParams(parsed.whereSql, built.params, localParams.length);
5896
+ return {
5897
+ expression: `count(*) FILTER (WHERE ${re.sql}) AS ${quoteBatchIdent(item.alias)}`,
5898
+ reindexedParams: re.params,
5899
+ sharedFrom: currentFrom
5900
+ };
5901
+ }
5902
+ function buildCountSubqueriesForModel(items, model, models, dialect, aliasIndex) {
5903
+ let sharedFrom = null;
5904
+ const expressions = [];
5905
+ const localParams = [];
5906
+ const localKeys = [];
5907
+ const localAliases = [];
5908
+ for (const item of items) {
5909
+ const result = processCountQuery(
5910
+ item,
5911
+ model,
5912
+ models,
5913
+ dialect,
5914
+ sharedFrom,
5915
+ localParams
5916
+ );
5917
+ if (!result) return null;
5918
+ sharedFrom = result.sharedFrom;
5919
+ expressions.push(result.expression);
5920
+ for (const param of result.reindexedParams) {
5921
+ localParams.push(param);
5922
+ }
5923
+ localKeys.push(item.key);
5924
+ localAliases.push(item.alias);
5925
+ }
5926
+ if (!sharedFrom) return null;
5927
+ const alias = `m_${aliasIndex}`;
5928
+ const subSql = `(SELECT ${expressions.join(", ")} FROM ${sharedFrom}) ${alias}`;
5929
+ return {
5930
+ alias,
5931
+ sql: subSql,
5932
+ params: localParams,
5933
+ keys: localKeys,
5934
+ aliases: localAliases
5935
+ };
5936
+ }
5937
+ function groupQueriesByModel(queries, keys, aliasesByKey) {
5474
5938
  const modelGroups = /* @__PURE__ */ new Map();
5475
- for (let i = 0; i < keys.length; i++) {
5476
- const key = keys[i];
5939
+ for (const key of keys) {
5477
5940
  const q = queries[key];
5478
5941
  const alias = aliasesByKey.get(key);
5479
5942
  if (!alias) return null;
5480
- if (!modelGroups.has(q.model)) modelGroups.set(q.model, []);
5481
- modelGroups.get(q.model).push({ key, alias, args: q.args || {} });
5943
+ if (!modelGroups.has(q.model)) {
5944
+ modelGroups.set(q.model, []);
5945
+ }
5946
+ const items = modelGroups.get(q.model);
5947
+ if (items) {
5948
+ items.push({
5949
+ key,
5950
+ alias,
5951
+ args: q.args || {}
5952
+ });
5953
+ }
5482
5954
  }
5955
+ return modelGroups;
5956
+ }
5957
+ function buildSubqueriesFromGroups(modelGroups, modelMap, models, dialect) {
5483
5958
  const subqueries = [];
5484
5959
  let aliasIndex = 0;
5485
5960
  for (const [modelName, items] of modelGroups) {
5486
5961
  const model = modelMap.get(modelName);
5487
5962
  if (!model) return null;
5488
- let sharedFrom = null;
5489
- const expressions = [];
5490
- const localParams = [];
5491
- const localKeys = [];
5492
- const localAliases = [];
5493
- for (let i = 0; i < items.length; i++) {
5494
- const { key, alias: alias2, args } = items[i];
5495
- const built = buildSQLWithCache(model, models, "count", args, dialect);
5496
- const parsed = parseSimpleCountSql(built.sql);
5497
- if (!parsed) return null;
5498
- if (containsPgPlaceholder(parsed.fromSql)) {
5499
- return null;
5500
- }
5501
- if (!parsed.whereSql) {
5502
- if (built.params.length > 0) return null;
5503
- if (sharedFrom === null) sharedFrom = parsed.fromSql;
5504
- if (sharedFrom !== parsed.fromSql) return null;
5505
- expressions.push(`count(*) AS ${quoteBatchIdent(alias2)}`);
5506
- localKeys.push(key);
5507
- localAliases.push(alias2);
5508
- continue;
5509
- }
5510
- if (sharedFrom === null) sharedFrom = parsed.fromSql;
5511
- if (sharedFrom !== parsed.fromSql) return null;
5512
- const re = reindexParams(
5513
- parsed.whereSql,
5514
- built.params,
5515
- localParams.length
5516
- );
5517
- for (let p = 0; p < re.params.length; p++) localParams.push(re.params[p]);
5518
- expressions.push(
5519
- `count(*) FILTER (WHERE ${re.sql}) AS ${quoteBatchIdent(alias2)}`
5520
- );
5521
- localKeys.push(key);
5522
- localAliases.push(alias2);
5523
- }
5524
- if (!sharedFrom) return null;
5525
- const alias = `m_${aliasIndex++}`;
5526
- const subSql = `(SELECT ${expressions.join(", ")} FROM ${sharedFrom}) ${alias}`;
5527
- subqueries.push({
5528
- alias,
5529
- sql: subSql,
5530
- params: localParams,
5531
- keys: localKeys,
5532
- aliases: localAliases
5533
- });
5963
+ const subquery = buildCountSubqueriesForModel(
5964
+ items,
5965
+ model,
5966
+ models,
5967
+ dialect,
5968
+ aliasIndex++
5969
+ );
5970
+ if (!subquery) return null;
5971
+ subqueries.push(subquery);
5534
5972
  }
5535
- if (subqueries.length === 0) return null;
5973
+ return subqueries.length > 0 ? subqueries : null;
5974
+ }
5975
+ function reindexSubqueries(subqueries) {
5536
5976
  let offset = 0;
5537
5977
  const rewrittenSubs = [];
5538
5978
  const finalParams = [];
5539
- for (let i = 0; i < subqueries.length; i++) {
5540
- const sq = subqueries[i];
5979
+ for (const sq of subqueries) {
5541
5980
  const re = reindexParams(sq.sql, sq.params, offset);
5542
5981
  offset += re.params.length;
5543
5982
  rewrittenSubs.push(re.sql);
5544
- for (let p = 0; p < re.params.length; p++) finalParams.push(re.params[p]);
5983
+ for (const p of re.params) {
5984
+ finalParams.push(p);
5985
+ }
5545
5986
  }
5987
+ return { sql: rewrittenSubs, params: finalParams };
5988
+ }
5989
+ function buildSelectParts(subqueries) {
5546
5990
  const selectParts = [];
5547
- for (let i = 0; i < subqueries.length; i++) {
5548
- const sq = subqueries[i];
5549
- for (let k = 0; k < sq.aliases.length; k++) {
5550
- const outAlias = sq.aliases[k];
5991
+ for (const sq of subqueries) {
5992
+ for (const outAlias of sq.aliases) {
5551
5993
  selectParts.push(
5552
5994
  `${sq.alias}.${quoteBatchIdent(outAlias)} AS ${quoteBatchIdent(outAlias)}`
5553
5995
  );
5554
5996
  }
5555
5997
  }
5998
+ return selectParts;
5999
+ }
6000
+ function buildMergedCountBatchSql(queries, keys, aliasesByKey, modelMap, models, dialect) {
6001
+ const modelGroups = groupQueriesByModel(queries, keys, aliasesByKey);
6002
+ if (!modelGroups) return null;
6003
+ const subqueries = buildSubqueriesFromGroups(
6004
+ modelGroups,
6005
+ modelMap,
6006
+ models,
6007
+ dialect
6008
+ );
6009
+ if (!subqueries) return null;
6010
+ const { sql: rewrittenSubs, params: finalParams } = reindexSubqueries(subqueries);
6011
+ const selectParts = buildSelectParts(subqueries);
5556
6012
  const fromSql = rewrittenSubs.join(" CROSS JOIN ");
5557
6013
  const sql = `SELECT ${selectParts.join(", ")} FROM ${fromSql}`;
5558
- const aliases = keys.map((k) => aliasesByKey.get(k));
6014
+ const aliases = keys.map((k) => {
6015
+ var _a;
6016
+ return (_a = aliasesByKey.get(k)) != null ? _a : "";
6017
+ });
5559
6018
  return { sql, params: finalParams, keys, aliases };
5560
6019
  }
5561
- function buildBatchSql(queries, modelMap, models, dialect) {
5562
- const keys = Object.keys(queries);
5563
- if (keys.length === 0) {
5564
- throw new Error("buildBatchSql requires at least one query");
5565
- }
5566
- if (dialect !== "postgres") {
5567
- throw new Error("Batch queries are only supported for postgres dialect");
5568
- }
6020
+ function buildAliasesForKeys(keys) {
5569
6021
  const aliases = new Array(keys.length);
5570
6022
  const aliasesByKey = /* @__PURE__ */ new Map();
5571
6023
  for (let i = 0; i < keys.length; i++) {
@@ -5573,17 +6025,9 @@ function buildBatchSql(queries, modelMap, models, dialect) {
5573
6025
  aliases[i] = a;
5574
6026
  aliasesByKey.set(keys[i], a);
5575
6027
  }
5576
- if (isAllCountQueries(queries, keys)) {
5577
- const merged = buildMergedCountBatchSql(
5578
- queries,
5579
- keys,
5580
- aliasesByKey,
5581
- modelMap,
5582
- models,
5583
- dialect
5584
- );
5585
- if (merged) return merged;
5586
- }
6028
+ return { aliases, aliasesByKey };
6029
+ }
6030
+ function buildRegularBatchQueries(queries, keys, aliases, modelMap, models, dialect) {
5587
6031
  const ctes = new Array(keys.length);
5588
6032
  const selects = new Array(keys.length);
5589
6033
  const allParams = [];
@@ -5608,695 +6052,51 @@ function buildBatchSql(queries, modelMap, models, dialect) {
5608
6052
  queryParams,
5609
6053
  allParams.length
5610
6054
  );
5611
- for (let p = 0; p < reindexedParams.length; p++) {
5612
- allParams.push(reindexedParams[p]);
6055
+ for (const p of reindexedParams) {
6056
+ allParams.push(p);
5613
6057
  }
5614
6058
  const cteName = `batch_${i}`;
5615
6059
  ctes[i] = `${cteName} AS (${reindexedSql})`;
5616
6060
  selects[i] = wrapQueryForMethod(query.method, cteName, aliases[i]);
5617
6061
  }
5618
6062
  const sql = `WITH ${ctes.join(", ")} SELECT ${selects.join(", ")}`;
5619
- return { sql, params: allParams, keys, aliases };
6063
+ return { sql, params: allParams };
5620
6064
  }
5621
- function buildBatchCountSql(queries, modelMap, models, dialect) {
5622
- if (queries.length === 0) {
5623
- throw new Error("buildBatchCountSql requires at least one query");
6065
+ function buildBatchSql(queries, modelMap, models, dialect) {
6066
+ const keys = Object.keys(queries);
6067
+ if (keys.length === 0) {
6068
+ throw new Error("buildBatchSql requires at least one query");
5624
6069
  }
5625
6070
  if (dialect !== "postgres") {
5626
- throw new Error(
5627
- "Batch count queries are only supported for postgres dialect"
5628
- );
6071
+ throw new Error("Batch queries are only supported for postgres dialect");
5629
6072
  }
5630
- const ctes = new Array(queries.length);
5631
- const selects = new Array(queries.length);
5632
- const allParams = [];
5633
- for (let i = 0; i < queries.length; i++) {
5634
- const query = queries[i];
5635
- const model = modelMap.get(query.model);
5636
- if (!model) {
5637
- throw new Error(
5638
- `Model '${query.model}' not found. Available: ${[...modelMap.keys()].join(", ")}`
5639
- );
5640
- }
5641
- const { sql: querySql, params: queryParams } = buildSQLWithCache(
5642
- model,
6073
+ const { aliases, aliasesByKey } = buildAliasesForKeys(keys);
6074
+ if (isAllCountQueries(queries, keys)) {
6075
+ const merged = buildMergedCountBatchSql(
6076
+ queries,
6077
+ keys,
6078
+ aliasesByKey,
6079
+ modelMap,
5643
6080
  models,
5644
- "count",
5645
- query.args || {},
5646
6081
  dialect
5647
6082
  );
5648
- const { sql: reindexedSql, params: reindexedParams } = reindexParams(
5649
- querySql,
5650
- queryParams,
5651
- allParams.length
5652
- );
5653
- for (let p = 0; p < reindexedParams.length; p++) {
5654
- allParams.push(reindexedParams[p]);
5655
- }
5656
- const cteName = `count_${i}`;
5657
- const resultKey = `count_${i}`;
5658
- ctes[i] = `${cteName} AS (${reindexedSql})`;
5659
- selects[i] = `(SELECT * FROM ${cteName}) AS ${quoteBatchIdent(resultKey)}`;
5660
- }
5661
- const sql = `WITH ${ctes.join(", ")} SELECT ${selects.join(", ")}`;
5662
- return { sql, params: allParams };
5663
- }
5664
- function looksLikeJsonString(s) {
5665
- const t = s.trim();
5666
- if (t.length === 0) return false;
5667
- const c0 = t.charCodeAt(0);
5668
- const cN = t.charCodeAt(t.length - 1);
5669
- if (c0 === 123 && cN === 125) return true;
5670
- if (c0 === 91 && cN === 93) return true;
5671
- if (t === "null" || t === "true" || t === "false") return true;
5672
- return false;
5673
- }
5674
- function parseJsonValue(value) {
5675
- if (typeof value !== "string") return value;
5676
- if (!looksLikeJsonString(value)) return value;
5677
- try {
5678
- return JSON.parse(value);
5679
- } catch (e) {
5680
- return value;
5681
- }
5682
- }
5683
- function parseCountValue(value) {
5684
- if (value === null || value === void 0) return 0;
5685
- if (typeof value === "number") return value;
5686
- if (typeof value === "bigint") {
5687
- const n = Number(value);
5688
- return Number.isSafeInteger(n) ? n : 0;
5689
- }
5690
- if (typeof value === "string") {
5691
- const n = Number.parseInt(value, 10);
5692
- return Number.isFinite(n) ? n : 0;
5693
- }
5694
- if (typeof value === "object") {
5695
- const obj = value;
5696
- const countKey = Object.prototype.hasOwnProperty.call(obj, "count") ? "count" : Object.prototype.hasOwnProperty.call(obj, "_count") ? "_count" : Object.keys(obj).find((k) => k.endsWith("_count"));
5697
- if (countKey !== void 0) {
5698
- const v = obj[countKey];
5699
- if (typeof v === "number") return v;
5700
- if (typeof v === "bigint") {
5701
- const n = Number(v);
5702
- return Number.isSafeInteger(n) ? n : 0;
5703
- }
5704
- if (typeof v === "string") {
5705
- const n = Number.parseInt(v, 10);
5706
- return Number.isFinite(n) ? n : 0;
5707
- }
5708
- }
5709
- }
5710
- return 0;
5711
- }
5712
- function parseBatchCountResults(row, count) {
5713
- const results = [];
5714
- for (let i = 0; i < count; i++) {
5715
- const key = `count_${i}`;
5716
- const value = row[key];
5717
- results.push(parseCountValue(value));
5718
- }
5719
- return results;
5720
- }
5721
- function parseBatchResults(row, keys, queries, aliases) {
5722
- const results = {};
5723
- for (let i = 0; i < keys.length; i++) {
5724
- const key = keys[i];
5725
- const columnKey = aliases && aliases[i] ? aliases[i] : key;
5726
- const rawValue = row[columnKey];
5727
- const query = queries[key];
5728
- switch (query.method) {
5729
- case "findMany": {
5730
- const parsed = parseJsonValue(rawValue);
5731
- results[key] = Array.isArray(parsed) ? parsed : [];
5732
- break;
5733
- }
5734
- case "findFirst":
5735
- case "findUnique": {
5736
- const parsed = parseJsonValue(rawValue);
5737
- results[key] = parsed != null ? parsed : null;
5738
- break;
5739
- }
5740
- case "count": {
5741
- results[key] = parseCountValue(rawValue);
5742
- break;
5743
- }
5744
- case "aggregate": {
5745
- const parsed = parseJsonValue(rawValue);
5746
- const obj = parsed != null ? parsed : {};
5747
- results[key] = transformQueryResults("aggregate", [obj]);
5748
- break;
5749
- }
5750
- case "groupBy": {
5751
- const parsed = parseJsonValue(rawValue);
5752
- const arr = Array.isArray(parsed) ? parsed : [];
5753
- results[key] = transformQueryResults("groupBy", arr);
5754
- break;
5755
- }
5756
- default:
5757
- results[key] = rawValue;
5758
- }
5759
- }
5760
- return results;
5761
- }
5762
-
5763
- // src/transaction.ts
5764
- function isolationLevelToPostgresKeyword(level) {
5765
- switch (level) {
5766
- case "ReadCommitted":
5767
- return "read committed";
5768
- case "RepeatableRead":
5769
- return "repeatable read";
5770
- case "Serializable":
5771
- return "serializable";
5772
- default:
5773
- return void 0;
5774
- }
5775
- }
5776
- function validateTimeout(timeout) {
5777
- if (typeof timeout !== "number") {
5778
- throw new Error(
5779
- `Transaction timeout must be a number, got ${typeof timeout}`
5780
- );
5781
- }
5782
- if (!Number.isFinite(timeout)) {
5783
- throw new Error(`Transaction timeout must be finite, got ${timeout}`);
5784
- }
5785
- if (timeout < 0) {
5786
- throw new Error(`Transaction timeout must be non-negative, got ${timeout}`);
5787
- }
5788
- return Math.floor(timeout);
5789
- }
5790
- function createTransactionExecutor(deps) {
5791
- const { modelMap, allModels, dialect, executeRaw, postgresClient } = deps;
5792
- return {
5793
- execute(queries, options) {
5794
- return __async(this, null, function* () {
5795
- if (queries.length === 0) return [];
5796
- if (dialect !== "postgres") {
5797
- throw new Error("$transaction is only supported for postgres dialect");
5798
- }
5799
- if (!postgresClient) {
5800
- throw new Error("postgresClient is required for transactions");
5801
- }
5802
- const transactionCallback = (sql) => __async(null, null, function* () {
5803
- const results = [];
5804
- const isolationLevel = isolationLevelToPostgresKeyword(
5805
- options == null ? void 0 : options.isolationLevel
5806
- );
5807
- if (isolationLevel) {
5808
- yield sql.unsafe(
5809
- `SET TRANSACTION ISOLATION LEVEL ${isolationLevel.toUpperCase()}`
5810
- );
5811
- }
5812
- if ((options == null ? void 0 : options.timeout) !== void 0 && options.timeout !== null) {
5813
- const validatedTimeout = validateTimeout(options.timeout);
5814
- yield sql.unsafe(`SET LOCAL statement_timeout = $1`, [
5815
- validatedTimeout
5816
- ]);
5817
- }
5818
- for (const q of queries) {
5819
- const model = modelMap.get(q.model);
5820
- if (!model) {
5821
- throw new Error(
5822
- `Model '${q.model}' not found. Available: ${[...modelMap.keys()].join(", ")}`
5823
- );
5824
- }
5825
- const { sql: sqlStr, params } = buildSQLWithCache(
5826
- model,
5827
- allModels,
5828
- q.method,
5829
- q.args || {},
5830
- dialect
5831
- );
5832
- const rawResults = yield sql.unsafe(sqlStr, params);
5833
- results.push(transformQueryResults(q.method, rawResults));
5834
- }
5835
- return results;
5836
- });
5837
- return yield postgresClient.begin(transactionCallback);
5838
- });
5839
- }
5840
- };
5841
- }
5842
-
5843
- // src/fast-path.ts
5844
- function getIdField(model) {
5845
- const idField = model.fields.find((f) => f.name === "id" && !f.isRelation);
5846
- if (!idField) return null;
5847
- return {
5848
- name: "id",
5849
- dbName: idField.dbName || "id"
5850
- };
5851
- }
5852
- function buildColumnList(model) {
5853
- const cols = [];
5854
- for (const f of model.fields) {
5855
- if (f.isRelation) continue;
5856
- if (f.name.startsWith("@") || f.name.startsWith("//")) continue;
5857
- const dbName = f.dbName || f.name;
5858
- if (dbName !== f.name) {
5859
- cols.push(`${quote(dbName)} AS ${quote(f.name)}`);
5860
- } else {
5861
- cols.push(quote(dbName));
5862
- }
6083
+ if (merged) return merged;
5863
6084
  }
5864
- return cols.join(", ");
5865
- }
5866
- function buildSimpleQuery(model, dialect, where, params, suffix = "") {
5867
- const tableName = buildTableReference(
5868
- SQL_TEMPLATES.PUBLIC_SCHEMA,
5869
- model.tableName,
6085
+ const result = buildRegularBatchQueries(
6086
+ queries,
6087
+ keys,
6088
+ aliases,
6089
+ modelMap,
6090
+ models,
5870
6091
  dialect
5871
6092
  );
5872
- const columns = buildColumnList(model);
5873
- const sql = `SELECT ${columns} FROM ${tableName} ${where}${suffix}`;
5874
- return { sql, params };
5875
- }
5876
- function norm(value) {
5877
- return normalizeValue(value);
5878
- }
5879
- function tryFastPath(model, method, args, dialect) {
5880
- const where = args.where;
5881
- if (method === "findUnique" && isPlainObject(where) && Object.keys(where).length === 1 && "id" in where && !args.select && !args.include) {
5882
- const idField = getIdField(model);
5883
- if (!idField) return null;
5884
- const whereClause = dialect === "sqlite" ? `WHERE ${quote(idField.dbName)} = ?` : `WHERE ${quote(idField.dbName)} = $1`;
5885
- return buildSimpleQuery(
5886
- model,
5887
- dialect,
5888
- whereClause,
5889
- [norm(where.id)],
5890
- " LIMIT 1"
5891
- );
5892
- }
5893
- if (method === "findMany" && isPlainObject(where) && Object.keys(where).length === 1 && "id" in where && !args.select && !args.include && !args.orderBy && !args.take && !args.skip && !args.distinct && !args.cursor) {
5894
- const idField = getIdField(model);
5895
- if (!idField) return null;
5896
- const whereClause = dialect === "sqlite" ? `WHERE ${quote(idField.dbName)} = ?` : `WHERE ${quote(idField.dbName)} = $1`;
5897
- return buildSimpleQuery(model, dialect, whereClause, [norm(where.id)]);
5898
- }
5899
- if (method === "count" && (!where || Object.keys(where).length === 0) && !args.select && !args.skip) {
5900
- const tableName = buildTableReference(
5901
- SQL_TEMPLATES.PUBLIC_SCHEMA,
5902
- model.tableName,
5903
- dialect
5904
- );
5905
- const sql = `SELECT COUNT(*) AS ${quote("_count._all")} FROM ${tableName}`;
5906
- return { sql, params: [] };
5907
- }
5908
- if (method === "findMany" && (!where || Object.keys(where).length === 0) && !args.select && !args.include && !args.orderBy && !args.skip && !args.distinct && !args.cursor && typeof args.take === "number") {
5909
- const tableName = buildTableReference(
5910
- SQL_TEMPLATES.PUBLIC_SCHEMA,
5911
- model.tableName,
5912
- dialect
5913
- );
5914
- const columns = buildColumnList(model);
5915
- const sql = dialect === "sqlite" ? `SELECT ${columns} FROM ${tableName} LIMIT ?` : `SELECT ${columns} FROM ${tableName} LIMIT $1`;
5916
- return { sql, params: [norm(args.take)] };
5917
- }
5918
- if (method === "findFirst" && isPlainObject(where) && Object.keys(where).length === 1 && !args.select && !args.include && !args.orderBy && !args.skip) {
5919
- const field = Object.keys(where)[0];
5920
- const value = where[field];
5921
- if (value !== null && typeof value !== "object") {
5922
- const fieldDef = model.fields.find((f) => f.name === field);
5923
- if (fieldDef && !fieldDef.isRelation) {
5924
- const columnName = fieldDef.dbName || field;
5925
- const whereClause = dialect === "sqlite" ? `WHERE ${quote(columnName)} = ?` : `WHERE ${quote(columnName)} = $1`;
5926
- return buildSimpleQuery(
5927
- model,
5928
- dialect,
5929
- whereClause,
5930
- [norm(value)],
5931
- " LIMIT 1"
5932
- );
5933
- }
5934
- }
5935
- }
5936
- if (method === "findMany" && isPlainObject(where) && Object.keys(where).length === 1 && !args.select && !args.include && !args.orderBy && !args.take && !args.skip && !args.distinct && !args.cursor) {
5937
- const field = Object.keys(where)[0];
5938
- const value = where[field];
5939
- if (value !== null && typeof value !== "object") {
5940
- const fieldDef = model.fields.find((f) => f.name === field);
5941
- if (fieldDef && !fieldDef.isRelation) {
5942
- const columnName = fieldDef.dbName || field;
5943
- const whereClause = dialect === "sqlite" ? `WHERE ${quote(columnName)} = ?` : `WHERE ${quote(columnName)} = $1`;
5944
- return buildSimpleQuery(model, dialect, whereClause, [norm(value)]);
5945
- }
5946
- }
5947
- }
5948
- if (method === "findMany" && (!where || Object.keys(where).length === 0) && !args.select && !args.include && !args.orderBy && !args.take && !args.skip && !args.distinct && !args.cursor) {
5949
- const tableName = buildTableReference(
5950
- SQL_TEMPLATES.PUBLIC_SCHEMA,
5951
- model.tableName,
5952
- dialect
5953
- );
5954
- const columns = buildColumnList(model);
5955
- const sql = `SELECT ${columns} FROM ${tableName}`;
5956
- return { sql, params: [] };
5957
- }
5958
- return null;
6093
+ return __spreadProps(__spreadValues({}, result), { keys, aliases });
5959
6094
  }
5960
- var ACCELERATED_METHODS = /* @__PURE__ */ new Set([
5961
- "findMany",
5962
- "findFirst",
5963
- "findUnique",
5964
- "count",
5965
- "aggregate",
5966
- "groupBy"
5967
- ]);
6095
+
6096
+ // src/index.ts
5968
6097
  function buildSQL(model, models, method, args, dialect) {
5969
6098
  return buildSQLWithCache(model, models, method, args, dialect);
5970
6099
  }
5971
- function executePostgres(client, sql, params) {
5972
- return __async(this, null, function* () {
5973
- return yield client.unsafe(sql, params);
5974
- });
5975
- }
5976
- function shouldSqliteUseGet(method) {
5977
- return method === "count" || method === "findFirst" || method === "findUnique" || method === "aggregate";
5978
- }
5979
- function executeSqlite(db, method, sql, params) {
5980
- const stmt = db.prepare(sql);
5981
- if (shouldSqliteUseGet(method)) {
5982
- const row = stmt.get(...params);
5983
- if (row === void 0) return [];
5984
- return [row];
5985
- }
5986
- return stmt.all(...params);
5987
- }
5988
- function executeWithTiming(input) {
5989
- return __async(this, null, function* () {
5990
- var _a;
5991
- const startTime = Date.now();
5992
- const { sql, params } = buildSQL(
5993
- input.model,
5994
- input.allModels,
5995
- input.method,
5996
- input.args,
5997
- input.dialect
5998
- );
5999
- if (input.debug) {
6000
- console.log(`[${input.dialect}] ${input.modelName}.${input.method}`);
6001
- console.log("SQL:", sql);
6002
- console.log("Params:", params);
6003
- }
6004
- const results = yield input.executeQuery(input.method, sql, params);
6005
- const duration = Date.now() - startTime;
6006
- (_a = input.onQuery) == null ? void 0 : _a.call(input, {
6007
- model: input.modelName,
6008
- method: input.method,
6009
- sql,
6010
- params,
6011
- duration
6012
- });
6013
- return results;
6014
- });
6015
- }
6016
- function resolveModelName(ctx) {
6017
- return (ctx == null ? void 0 : ctx.name) || (ctx == null ? void 0 : ctx.$name);
6018
- }
6019
- function isAllowedModel(allowedModels, modelName) {
6020
- if (!allowedModels) return true;
6021
- return allowedModels.includes(modelName);
6022
- }
6023
- function getModelOrNull(modelMap, modelName) {
6024
- var _a;
6025
- return (_a = modelMap.get(modelName)) != null ? _a : null;
6026
- }
6027
- function fallbackToPrisma(ctx, modelName, method, args) {
6028
- return ctx.$parent[modelName][method](args);
6029
- }
6030
- function createExecuteQuery(client, dialect) {
6031
- return (method, sql, params) => __async(null, null, function* () {
6032
- if (dialect === "postgres") {
6033
- return yield executePostgres(client, sql, params);
6034
- }
6035
- return executeSqlite(client, method, sql, params);
6036
- });
6037
- }
6038
- function logAcceleratedError(debug, dialect, modelName, method, error) {
6039
- const msg = error instanceof Error ? error.message : String(error);
6040
- console.warn(
6041
- `[prisma-sql] ${modelName}.${method} acceleration failed, falling back to Prisma: ${msg}`
6042
- );
6043
- if (debug && error instanceof Error && error.stack) {
6044
- console.warn(error.stack);
6045
- }
6046
- }
6047
- function canAccelerate(deps, modelName, method) {
6048
- if (!ACCELERATED_METHODS.has(method)) return false;
6049
- if (!isAllowedModel(deps.allowedModels, modelName)) return false;
6050
- return deps.modelMap.has(modelName);
6051
- }
6052
- function runAccelerated(deps, modelName, method, model, args) {
6053
- return __async(this, null, function* () {
6054
- return withDialect(deps.dialect, () => __async(null, null, function* () {
6055
- const fastPath = tryFastPath(model, method, args || {}, deps.dialect);
6056
- if (fastPath) {
6057
- if (deps.debug) {
6058
- console.log(`[${deps.dialect}] ${modelName}.${method} \u26A1 FAST PATH`);
6059
- console.log("SQL:", fastPath.sql);
6060
- console.log("Params:", fastPath.params);
6061
- }
6062
- const results2 = yield deps.executeQuery(
6063
- method,
6064
- fastPath.sql,
6065
- fastPath.params
6066
- );
6067
- return transformQueryResults(method, results2);
6068
- }
6069
- const results = yield executeWithTiming({
6070
- modelName,
6071
- method,
6072
- model,
6073
- allModels: deps.allModels,
6074
- args: args || {},
6075
- dialect: deps.dialect,
6076
- debug: deps.debug,
6077
- executeQuery: deps.executeQuery,
6078
- onQuery: deps.onQuery
6079
- });
6080
- return transformQueryResults(method, results);
6081
- }));
6082
- });
6083
- }
6084
- function handleMethodCall(ctx, method, args, deps) {
6085
- return __async(this, null, function* () {
6086
- const modelName = resolveModelName(ctx);
6087
- if (!canAccelerate(deps, modelName, method)) {
6088
- return fallbackToPrisma(ctx, modelName, method, args);
6089
- }
6090
- const model = getModelOrNull(deps.modelMap, modelName);
6091
- if (!model) {
6092
- return fallbackToPrisma(ctx, modelName, method, args);
6093
- }
6094
- try {
6095
- return yield runAccelerated(deps, modelName, method, model, args);
6096
- } catch (error) {
6097
- logAcceleratedError(deps.debug, deps.dialect, modelName, method, error);
6098
- return fallbackToPrisma(ctx, modelName, method, args);
6099
- }
6100
- });
6101
- }
6102
- var DeferredQuery = class {
6103
- constructor(model, method, args) {
6104
- this.model = model;
6105
- this.method = method;
6106
- this.args = args;
6107
- }
6108
- then(onfulfilled, onrejected) {
6109
- throw new Error(
6110
- "Cannot await a batch query. Batch queries must not be awaited inside the $batch callback."
6111
- );
6112
- }
6113
- };
6114
- function createBatchProxy(modelMap, allowedModels) {
6115
- return new Proxy(
6116
- {},
6117
- {
6118
- get(_target, modelName) {
6119
- if (typeof modelName === "symbol") return void 0;
6120
- const model = modelMap.get(modelName);
6121
- if (!model) {
6122
- throw new Error(
6123
- `Model '${modelName}' not found. Available: ${[...modelMap.keys()].join(", ")}`
6124
- );
6125
- }
6126
- if (allowedModels && !allowedModels.includes(modelName)) {
6127
- throw new Error(
6128
- `Model '${modelName}' not allowed. Allowed: ${allowedModels.join(", ")}`
6129
- );
6130
- }
6131
- return new Proxy(
6132
- {},
6133
- {
6134
- get(_target2, method) {
6135
- if (!ACCELERATED_METHODS.has(method)) {
6136
- throw new Error(
6137
- `Method '${method}' not supported in batch. Supported: ${[...ACCELERATED_METHODS].join(", ")}`
6138
- );
6139
- }
6140
- return (args) => {
6141
- return new DeferredQuery(
6142
- modelName,
6143
- method,
6144
- args
6145
- );
6146
- };
6147
- }
6148
- }
6149
- );
6150
- }
6151
- }
6152
- );
6153
- }
6154
- function speedExtension(config) {
6155
- const {
6156
- postgres,
6157
- sqlite,
6158
- models: providedModels,
6159
- dmmf,
6160
- debug = false,
6161
- allowedModels,
6162
- onQuery
6163
- } = config;
6164
- if (!postgres && !sqlite) {
6165
- throw new Error("speedExtension requires either postgres or sqlite client");
6166
- }
6167
- if (postgres && sqlite) {
6168
- throw new Error(
6169
- "speedExtension cannot use both postgres and sqlite clients"
6170
- );
6171
- }
6172
- let models;
6173
- if (providedModels) {
6174
- models = providedModels;
6175
- } else if (dmmf) {
6176
- models = convertDMMFToModels(dmmf.datamodel);
6177
- } else {
6178
- throw new Error("speedExtension requires either models or dmmf parameter.");
6179
- }
6180
- if (!Array.isArray(models) || models.length === 0) {
6181
- throw new Error("speedExtension: models array is empty or invalid");
6182
- }
6183
- const dialect = postgres ? "postgres" : "sqlite";
6184
- const client = postgres || sqlite;
6185
- setGlobalDialect(dialect);
6186
- return (prisma) => {
6187
- const modelMap = new Map(models.map((m) => [m.name, m]));
6188
- const executeQuery = createExecuteQuery(client, dialect);
6189
- const deps = {
6190
- dialect,
6191
- debug,
6192
- onQuery,
6193
- allowedModels,
6194
- allModels: models,
6195
- modelMap,
6196
- executeQuery
6197
- };
6198
- const createMethodHandler = (method) => {
6199
- return function(args) {
6200
- return __async(this, null, function* () {
6201
- return handleMethodCall(this, method, args, deps);
6202
- });
6203
- };
6204
- };
6205
- const methodHandlers = {};
6206
- for (const method of ACCELERATED_METHODS) {
6207
- methodHandlers[method] = createMethodHandler(method);
6208
- }
6209
- const executeRaw = (sql, params) => __async(null, null, function* () {
6210
- if (dialect === "postgres") {
6211
- return yield client.unsafe(sql, params);
6212
- }
6213
- throw new Error("Raw execution for sqlite not supported in transactions");
6214
- });
6215
- const txExecutor = createTransactionExecutor({
6216
- modelMap,
6217
- allModels: models,
6218
- dialect,
6219
- executeRaw,
6220
- postgresClient: postgres
6221
- });
6222
- function batch(callback) {
6223
- return __async(this, null, function* () {
6224
- const batchProxy = createBatchProxy(modelMap, allowedModels);
6225
- const queries = yield callback(batchProxy);
6226
- const batchQueries = {};
6227
- for (const [key, deferred] of Object.entries(queries)) {
6228
- if (!(deferred instanceof DeferredQuery)) {
6229
- throw new Error(
6230
- `Batch query '${key}' must be a deferred query. Did you await it?`
6231
- );
6232
- }
6233
- batchQueries[key] = {
6234
- model: deferred.model,
6235
- method: deferred.method,
6236
- args: deferred.args || {}
6237
- };
6238
- }
6239
- const startTime = Date.now();
6240
- const { sql, params, keys, aliases } = buildBatchSql(
6241
- batchQueries,
6242
- modelMap,
6243
- models,
6244
- dialect
6245
- );
6246
- if (debug) {
6247
- console.log(`[${dialect}] $batch (${keys.length} queries)`);
6248
- console.log("SQL:", sql);
6249
- console.log("Params:", params);
6250
- }
6251
- const rows = yield executeQuery("findMany", sql, params);
6252
- const row = rows[0];
6253
- const results = parseBatchResults(row, keys, batchQueries, aliases);
6254
- const duration = Date.now() - startTime;
6255
- onQuery == null ? void 0 : onQuery({
6256
- model: "_batch",
6257
- method: "batch",
6258
- sql,
6259
- params,
6260
- duration
6261
- });
6262
- return results;
6263
- });
6264
- }
6265
- function transaction(queries, options) {
6266
- return __async(this, null, function* () {
6267
- const startTime = Date.now();
6268
- if (debug) {
6269
- console.log(`[${dialect}] $transaction (${queries.length} queries)`);
6270
- }
6271
- const results = yield txExecutor.execute(queries, options);
6272
- const duration = Date.now() - startTime;
6273
- onQuery == null ? void 0 : onQuery({
6274
- model: "_transaction",
6275
- method: "transaction",
6276
- sql: `TRANSACTION(${queries.length})`,
6277
- params: [],
6278
- duration
6279
- });
6280
- return results;
6281
- });
6282
- }
6283
- return prisma.$extends({
6284
- name: "prisma-sql-speed",
6285
- client: {
6286
- $original: prisma,
6287
- $batch: batch,
6288
- $transaction: transaction
6289
- },
6290
- model: {
6291
- $allModels: methodHandlers
6292
- }
6293
- });
6294
- };
6295
- }
6296
- function extendPrisma(prisma, config) {
6297
- const extension = speedExtension(config);
6298
- return extension(prisma);
6299
- }
6300
6100
  function createToSQLFunction(models, dialect) {
6301
6101
  if (!models || !Array.isArray(models) || models.length === 0) {
6302
6102
  throw new Error("createToSQL requires non-empty models array");
@@ -6384,6 +6184,6 @@ function generateSQLByModel(directives) {
6384
6184
  return byModel;
6385
6185
  }
6386
6186
 
6387
- export { buildBatchCountSql, buildBatchSql, buildSQL, createPrismaSQL, createToSQL, createTransactionExecutor, extendPrisma, generateAllSQL, generateSQL2 as generateSQL, generateSQLByModel, getGlobalDialect, parseBatchCountResults, parseBatchResults, setGlobalDialect, speedExtension, transformQueryResults };
6187
+ export { buildSQL, createPrismaSQL, createToSQL, generateAllSQL, generateSQL2 as generateSQL, generateSQLByModel };
6388
6188
  //# sourceMappingURL=index.js.map
6389
6189
  //# sourceMappingURL=index.js.map