@mateusseiboth/ember-orm 0.1.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 ADDED
@@ -0,0 +1,4935 @@
1
+ // src/ast/index.ts
2
+ var SCALAR_TYPES = [
3
+ "String",
4
+ "Boolean",
5
+ "Int",
6
+ "BigInt",
7
+ "Float",
8
+ "Decimal",
9
+ "DateTime",
10
+ "Bytes",
11
+ "Json"
12
+ ];
13
+ function emptySchema() {
14
+ return { generators: [], models: [], enums: [] };
15
+ }
16
+ function findModel(schema, name) {
17
+ return schema.models.find((m) => m.name === name);
18
+ }
19
+ function findEnum(schema, name) {
20
+ return schema.enums.find((e) => e.name === name);
21
+ }
22
+ function fieldColumn(field) {
23
+ return field.dbName ?? field.name.toUpperCase();
24
+ }
25
+ function modelTable(model) {
26
+ return model.dbName ?? model.name.toUpperCase();
27
+ }
28
+ function scalarFields(model) {
29
+ return model.fields.filter((f) => f.kind !== "object");
30
+ }
31
+ function relationFields(model) {
32
+ return model.fields.filter((f) => f.kind === "object");
33
+ }
34
+ function idFields(model) {
35
+ if (model.primaryKey.length > 0) {
36
+ return model.primaryKey.map((n) => model.fields.find((f) => f.name === n)).filter((f) => !!f);
37
+ }
38
+ return model.fields.filter((f) => f.isId);
39
+ }
40
+
41
+ // src/schema/index.ts
42
+ import { readFileSync, existsSync } from "fs";
43
+ import { resolve, dirname } from "path";
44
+
45
+ // src/errors/index.ts
46
+ var EmberError = class extends Error {
47
+ constructor(message) {
48
+ super(message);
49
+ this.name = new.target.name;
50
+ Object.setPrototypeOf(this, new.target.prototype);
51
+ }
52
+ };
53
+ var SchemaParseError = class extends EmberError {
54
+ constructor(message, line, column, file) {
55
+ super(
56
+ `${message} (at ${file ? `${file}:` : ""}${line}:${column})`
57
+ );
58
+ this.line = line;
59
+ this.column = column;
60
+ this.file = file;
61
+ }
62
+ line;
63
+ column;
64
+ file;
65
+ };
66
+ var SchemaValidationError = class extends EmberError {
67
+ constructor(message, details = []) {
68
+ super(
69
+ details.length > 0 ? `${message}
70
+ - ${details.join("\n - ")}` : message
71
+ );
72
+ this.details = details;
73
+ }
74
+ details;
75
+ };
76
+ var QueryValidationError = class extends EmberError {
77
+ };
78
+ var DatabaseError = class extends EmberError {
79
+ constructor(message, cause, sql) {
80
+ super(message);
81
+ this.cause = cause;
82
+ this.sql = sql;
83
+ }
84
+ cause;
85
+ sql;
86
+ };
87
+ var RecordNotFoundError = class extends EmberError {
88
+ constructor(model) {
89
+ super(`No '${model}' record found matching the given criteria.`);
90
+ }
91
+ };
92
+ var UniqueConstraintError = class extends EmberError {
93
+ constructor(target, cause) {
94
+ super(`Unique constraint failed on: ${target}`);
95
+ this.target = target;
96
+ if (cause !== void 0) this.cause = cause;
97
+ }
98
+ target;
99
+ };
100
+
101
+ // src/schema/lexer.ts
102
+ var SINGLE_CHAR_TOKENS = {
103
+ "{": "lbrace",
104
+ "}": "rbrace",
105
+ "(": "lparen",
106
+ ")": "rparen",
107
+ "[": "lbracket",
108
+ "]": "rbracket",
109
+ "=": "equals",
110
+ ",": "comma",
111
+ ":": "colon",
112
+ "?": "question",
113
+ ".": "dot"
114
+ };
115
+ var IDENT_START = /[A-Za-z_]/;
116
+ var IDENT_PART = /[A-Za-z0-9_]/;
117
+ var Lexer = class {
118
+ constructor(source, file) {
119
+ this.source = source;
120
+ this.file = file;
121
+ }
122
+ source;
123
+ file;
124
+ pos = 0;
125
+ line = 1;
126
+ column = 1;
127
+ tokenize() {
128
+ const tokens = [];
129
+ let token = this.next();
130
+ while (token.type !== "eof") {
131
+ tokens.push(token);
132
+ token = this.next();
133
+ }
134
+ tokens.push(token);
135
+ return tokens;
136
+ }
137
+ next() {
138
+ this.skipWhitespaceAndComments();
139
+ if (this.pos >= this.source.length) {
140
+ return this.make("eof", "");
141
+ }
142
+ const startLine = this.line;
143
+ const startColumn = this.column;
144
+ const ch = this.source[this.pos];
145
+ if (ch === "/" && this.peek(1) === "/" && this.peek(2) === "/") {
146
+ return this.readDocComment(startLine, startColumn);
147
+ }
148
+ if (ch === '"') {
149
+ return this.readString(startLine, startColumn);
150
+ }
151
+ if (ch === "@") {
152
+ this.advance();
153
+ if (this.source[this.pos] === "@") {
154
+ this.advance();
155
+ return { type: "double_at", value: "@@", line: startLine, column: startColumn };
156
+ }
157
+ return { type: "at", value: "@", line: startLine, column: startColumn };
158
+ }
159
+ const single = SINGLE_CHAR_TOKENS[ch];
160
+ if (single) {
161
+ this.advance();
162
+ return { type: single, value: ch, line: startLine, column: startColumn };
163
+ }
164
+ if (ch === "-" || /[0-9]/.test(ch)) {
165
+ return this.readNumber(startLine, startColumn);
166
+ }
167
+ if (IDENT_START.test(ch)) {
168
+ return this.readIdentifier(startLine, startColumn);
169
+ }
170
+ throw new SchemaParseError(
171
+ `Unexpected character '${ch}'`,
172
+ startLine,
173
+ startColumn,
174
+ this.file
175
+ );
176
+ }
177
+ readDocComment(line, column) {
178
+ this.advance();
179
+ this.advance();
180
+ this.advance();
181
+ let value = "";
182
+ while (this.pos < this.source.length && this.source[this.pos] !== "\n") {
183
+ value += this.source[this.pos];
184
+ this.advance();
185
+ }
186
+ return { type: "doc_comment", value: value.trim(), line, column };
187
+ }
188
+ readString(line, column) {
189
+ this.advance();
190
+ let value = "";
191
+ while (this.pos < this.source.length && this.source[this.pos] !== '"') {
192
+ const c = this.source[this.pos];
193
+ if (c === "\\") {
194
+ this.advance();
195
+ const escaped = this.source[this.pos];
196
+ if (escaped === void 0) break;
197
+ value += escapeChar(escaped);
198
+ this.advance();
199
+ continue;
200
+ }
201
+ if (c === "\n") {
202
+ throw new SchemaParseError(
203
+ "Unterminated string literal",
204
+ line,
205
+ column,
206
+ this.file
207
+ );
208
+ }
209
+ value += c;
210
+ this.advance();
211
+ }
212
+ if (this.source[this.pos] !== '"') {
213
+ throw new SchemaParseError(
214
+ "Unterminated string literal",
215
+ line,
216
+ column,
217
+ this.file
218
+ );
219
+ }
220
+ this.advance();
221
+ return { type: "string", value, line, column };
222
+ }
223
+ readNumber(line, column) {
224
+ let value = "";
225
+ if (this.source[this.pos] === "-") {
226
+ value += "-";
227
+ this.advance();
228
+ }
229
+ while (this.pos < this.source.length && /[0-9.]/.test(this.source[this.pos])) {
230
+ value += this.source[this.pos];
231
+ this.advance();
232
+ }
233
+ return { type: "number", value, line, column };
234
+ }
235
+ readIdentifier(line, column) {
236
+ let value = "";
237
+ while (this.pos < this.source.length && IDENT_PART.test(this.source[this.pos])) {
238
+ value += this.source[this.pos];
239
+ this.advance();
240
+ }
241
+ return { type: "identifier", value, line, column };
242
+ }
243
+ skipWhitespaceAndComments() {
244
+ while (this.pos < this.source.length) {
245
+ const ch = this.source[this.pos];
246
+ if (ch === " " || ch === " " || ch === "\r" || ch === "\n") {
247
+ this.advance();
248
+ continue;
249
+ }
250
+ if (ch === "/" && this.peek(1) === "/" && this.peek(2) !== "/") {
251
+ while (this.pos < this.source.length && this.source[this.pos] !== "\n") {
252
+ this.advance();
253
+ }
254
+ continue;
255
+ }
256
+ if (ch === "/" && this.peek(1) === "*") {
257
+ this.advance();
258
+ this.advance();
259
+ while (this.pos < this.source.length && !(this.source[this.pos] === "*" && this.peek(1) === "/")) {
260
+ this.advance();
261
+ }
262
+ this.advance();
263
+ this.advance();
264
+ continue;
265
+ }
266
+ break;
267
+ }
268
+ }
269
+ peek(offset) {
270
+ return this.source[this.pos + offset];
271
+ }
272
+ advance() {
273
+ if (this.source[this.pos] === "\n") {
274
+ this.line++;
275
+ this.column = 1;
276
+ } else {
277
+ this.column++;
278
+ }
279
+ this.pos++;
280
+ }
281
+ make(type, value) {
282
+ return { type, value, line: this.line, column: this.column };
283
+ }
284
+ };
285
+ function escapeChar(c) {
286
+ switch (c) {
287
+ case "n":
288
+ return "\n";
289
+ case "t":
290
+ return " ";
291
+ case "r":
292
+ return "\r";
293
+ case '"':
294
+ return '"';
295
+ case "\\":
296
+ return "\\";
297
+ default:
298
+ return c;
299
+ }
300
+ }
301
+
302
+ // src/schema/parser.ts
303
+ var Parser = class {
304
+ constructor(source, file) {
305
+ this.file = file;
306
+ this.tokens = new Lexer(source, file).tokenize();
307
+ }
308
+ file;
309
+ tokens;
310
+ pos = 0;
311
+ parse() {
312
+ const doc = { generators: [], models: [], enums: [] };
313
+ let pendingDoc = [];
314
+ while (!this.isEof()) {
315
+ const tok = this.peek();
316
+ if (tok.type === "doc_comment") {
317
+ pendingDoc.push(tok.value);
318
+ this.advance();
319
+ continue;
320
+ }
321
+ if (tok.type !== "identifier") {
322
+ throw this.error(`Unexpected token '${tok.value}'`, tok);
323
+ }
324
+ const documentation = pendingDoc.length ? pendingDoc.join("\n") : void 0;
325
+ pendingDoc = [];
326
+ switch (tok.value) {
327
+ case "datasource":
328
+ doc.datasource = this.parseDatasource();
329
+ break;
330
+ case "generator":
331
+ doc.generators.push(this.parseGenerator());
332
+ break;
333
+ case "model":
334
+ doc.models.push(this.parseModel(documentation));
335
+ break;
336
+ case "enum":
337
+ doc.enums.push(this.parseEnum(documentation));
338
+ break;
339
+ default:
340
+ throw this.error(`Unknown top-level keyword '${tok.value}'`, tok);
341
+ }
342
+ }
343
+ resolveKinds(doc);
344
+ return doc;
345
+ }
346
+ // ---- Top-level blocks -------------------------------------------------
347
+ parseDatasource() {
348
+ this.expectKeyword("datasource");
349
+ const name = this.expect("identifier").value;
350
+ this.expect("lbrace");
351
+ const assignments = this.parseAssignments();
352
+ this.expect("rbrace");
353
+ const provider = literalString(assignments["provider"]);
354
+ const urlAssign = assignments["url"];
355
+ let url = { kind: "literal", value: "" };
356
+ if (urlAssign) {
357
+ if (urlAssign.kind === "function" && urlAssign.name === "env") {
358
+ url = { kind: "env", value: literalString(urlAssign.args[0]) };
359
+ } else if (urlAssign.kind === "string") {
360
+ url = { kind: "literal", value: urlAssign.value };
361
+ }
362
+ }
363
+ return { name, provider: provider ?? "firebird", url };
364
+ }
365
+ parseGenerator() {
366
+ this.expectKeyword("generator");
367
+ const name = this.expect("identifier").value;
368
+ this.expect("lbrace");
369
+ const assignments = this.parseAssignments();
370
+ this.expect("rbrace");
371
+ const config = {};
372
+ for (const [key, value] of Object.entries(assignments)) {
373
+ if (value.kind === "string") config[key] = value.value;
374
+ }
375
+ return {
376
+ name,
377
+ provider: literalString(assignments["provider"]) ?? "ember-client-js",
378
+ output: assignments["output"] ? literalString(assignments["output"]) : void 0,
379
+ config
380
+ };
381
+ }
382
+ parseAssignments() {
383
+ const out = {};
384
+ while (!this.check("rbrace") && !this.isEof()) {
385
+ if (this.check("doc_comment")) {
386
+ this.advance();
387
+ continue;
388
+ }
389
+ const key = this.expect("identifier").value;
390
+ this.expect("equals");
391
+ out[key] = this.parseValue();
392
+ }
393
+ return out;
394
+ }
395
+ parseModel(documentation) {
396
+ this.expectKeyword("model");
397
+ const name = this.expect("identifier").value;
398
+ this.expect("lbrace");
399
+ const model = {
400
+ name,
401
+ fields: [],
402
+ primaryKey: [],
403
+ uniqueIndexes: [],
404
+ indexes: [],
405
+ documentation
406
+ };
407
+ let pendingDoc = [];
408
+ while (!this.check("rbrace") && !this.isEof()) {
409
+ const tok = this.peek();
410
+ if (tok.type === "doc_comment") {
411
+ pendingDoc.push(tok.value);
412
+ this.advance();
413
+ continue;
414
+ }
415
+ if (tok.type === "double_at") {
416
+ this.parseBlockAttribute(model);
417
+ pendingDoc = [];
418
+ continue;
419
+ }
420
+ const field = this.parseField(
421
+ pendingDoc.length ? pendingDoc.join("\n") : void 0
422
+ );
423
+ pendingDoc = [];
424
+ model.fields.push(field);
425
+ if (field.isId && !model.primaryKey.includes(field.name)) {
426
+ model.primaryKey.push(field.name);
427
+ }
428
+ }
429
+ this.expect("rbrace");
430
+ return model;
431
+ }
432
+ parseField(documentation) {
433
+ const name = this.expect("identifier").value;
434
+ const typeName = this.expect("identifier").value;
435
+ let isList = false;
436
+ let isRequired = true;
437
+ while (this.check("lbracket") || this.check("question")) {
438
+ if (this.check("lbracket")) {
439
+ this.advance();
440
+ this.expect("rbracket");
441
+ isList = true;
442
+ } else {
443
+ this.advance();
444
+ isRequired = false;
445
+ }
446
+ }
447
+ const field = {
448
+ name,
449
+ type: typeName,
450
+ kind: "scalar",
451
+ // resolved later
452
+ isList,
453
+ isRequired,
454
+ isId: false,
455
+ isUnique: false,
456
+ isUpdatedAt: false,
457
+ documentation
458
+ };
459
+ while (this.check("at")) {
460
+ const attr = this.parseAttribute();
461
+ this.applyFieldAttribute(field, attr);
462
+ }
463
+ return field;
464
+ }
465
+ applyFieldAttribute(field, attr) {
466
+ if (attr.name.startsWith("db.")) {
467
+ field.nativeType = toNativeType(attr);
468
+ return;
469
+ }
470
+ switch (attr.name) {
471
+ case "id":
472
+ field.isId = true;
473
+ break;
474
+ case "unique":
475
+ field.isUnique = true;
476
+ break;
477
+ case "updatedAt":
478
+ field.isUpdatedAt = true;
479
+ break;
480
+ case "default":
481
+ field.default = toDefaultValue(attr.args[0]);
482
+ break;
483
+ case "map":
484
+ field.dbName = literalString(attr.args[0]);
485
+ break;
486
+ case "relation":
487
+ field.relation = toRelationInfo(attr.args);
488
+ break;
489
+ default:
490
+ throw this.error(`Unknown field attribute '@${attr.name}'`, attr);
491
+ }
492
+ }
493
+ parseBlockAttribute(model) {
494
+ const start = this.expect("double_at");
495
+ const name = this.expect("identifier").value;
496
+ const args = this.check("lparen") ? this.parseArgList() : [];
497
+ switch (name) {
498
+ case "id":
499
+ model.primaryKey = fieldNameList(args);
500
+ break;
501
+ case "unique":
502
+ model.uniqueIndexes.push({
503
+ fields: fieldNameList(args),
504
+ name: namedArg(args, "map")
505
+ });
506
+ break;
507
+ case "index":
508
+ model.indexes.push({
509
+ fields: fieldNameList(args),
510
+ name: namedArg(args, "map"),
511
+ unique: false
512
+ });
513
+ break;
514
+ case "map":
515
+ model.dbName = literalString(args[0]);
516
+ break;
517
+ default:
518
+ throw this.error(`Unknown block attribute '@@${name}'`, start);
519
+ }
520
+ }
521
+ parseEnum(documentation) {
522
+ this.expectKeyword("enum");
523
+ const name = this.expect("identifier").value;
524
+ this.expect("lbrace");
525
+ const node = { name, values: [], documentation };
526
+ while (!this.check("rbrace") && !this.isEof()) {
527
+ if (this.check("doc_comment")) {
528
+ this.advance();
529
+ continue;
530
+ }
531
+ if (this.check("double_at")) {
532
+ this.advance();
533
+ const attrName = this.expect("identifier").value;
534
+ const args = this.check("lparen") ? this.parseArgList() : [];
535
+ if (attrName === "map") node.dbName = literalString(args[0]);
536
+ continue;
537
+ }
538
+ const valueName = this.expect("identifier").value;
539
+ let dbName;
540
+ while (this.check("at")) {
541
+ const attr = this.parseAttribute();
542
+ if (attr.name === "map") dbName = literalString(attr.args[0]);
543
+ }
544
+ node.values.push({ name: valueName, dbName });
545
+ }
546
+ this.expect("rbrace");
547
+ return node;
548
+ }
549
+ // ---- Attributes & values ---------------------------------------------
550
+ parseAttribute() {
551
+ const at = this.expect("at");
552
+ let name = this.expect("identifier").value;
553
+ if (this.check("dot")) {
554
+ this.advance();
555
+ name += "." + this.expect("identifier").value;
556
+ }
557
+ const args = this.check("lparen") ? this.parseArgList() : [];
558
+ return { name, args, line: at.line, column: at.column };
559
+ }
560
+ parseArgList() {
561
+ this.expect("lparen");
562
+ const args = [];
563
+ while (!this.check("rparen") && !this.isEof()) {
564
+ args.push(this.parseArg());
565
+ if (this.check("comma")) this.advance();
566
+ }
567
+ this.expect("rparen");
568
+ return args;
569
+ }
570
+ /** Handles both positional values and `name: value` named arguments. */
571
+ parseArg() {
572
+ if (this.check("identifier") && this.peek(1)?.type === "colon") {
573
+ const key = this.expect("identifier").value;
574
+ this.expect("colon");
575
+ const value = this.parseValue();
576
+ return { kind: "function", name: `__named:${key}`, args: [value] };
577
+ }
578
+ return this.parseValue();
579
+ }
580
+ parseValue() {
581
+ const tok = this.peek();
582
+ switch (tok.type) {
583
+ case "string":
584
+ this.advance();
585
+ return { kind: "string", value: tok.value };
586
+ case "number":
587
+ this.advance();
588
+ return { kind: "number", value: Number(tok.value) };
589
+ case "lbracket": {
590
+ this.advance();
591
+ const items = [];
592
+ while (!this.check("rbracket") && !this.isEof()) {
593
+ items.push(this.parseValue());
594
+ if (this.check("comma")) this.advance();
595
+ }
596
+ this.expect("rbracket");
597
+ return { kind: "array", items };
598
+ }
599
+ case "identifier": {
600
+ this.advance();
601
+ if (tok.value === "true" || tok.value === "false") {
602
+ return { kind: "boolean", value: tok.value === "true" };
603
+ }
604
+ if (this.check("lparen")) {
605
+ const args = this.parseArgList();
606
+ return { kind: "function", name: tok.value, args };
607
+ }
608
+ return { kind: "ref", value: tok.value };
609
+ }
610
+ default:
611
+ throw this.error(`Unexpected value token '${tok.value}'`, tok);
612
+ }
613
+ }
614
+ // ---- Token helpers ----------------------------------------------------
615
+ peek(offset = 0) {
616
+ return this.tokens[this.pos + offset] ?? this.tokens[this.tokens.length - 1];
617
+ }
618
+ advance() {
619
+ const tok = this.tokens[this.pos];
620
+ if (this.pos < this.tokens.length - 1) this.pos++;
621
+ return tok;
622
+ }
623
+ check(type) {
624
+ return this.peek().type === type;
625
+ }
626
+ isEof() {
627
+ return this.peek().type === "eof";
628
+ }
629
+ expect(type) {
630
+ const tok = this.peek();
631
+ if (tok.type !== type) {
632
+ throw this.error(`Expected ${type} but found '${tok.value}' (${tok.type})`, tok);
633
+ }
634
+ return this.advance();
635
+ }
636
+ expectKeyword(keyword) {
637
+ const tok = this.peek();
638
+ if (tok.type !== "identifier" || tok.value !== keyword) {
639
+ throw this.error(`Expected keyword '${keyword}'`, tok);
640
+ }
641
+ return this.advance();
642
+ }
643
+ error(message, at) {
644
+ return new SchemaParseError(message, at.line, at.column, this.file);
645
+ }
646
+ };
647
+ function literalString(v) {
648
+ if (!v) return "";
649
+ if (v.kind === "string") return v.value;
650
+ if (v.kind === "ref") return v.value;
651
+ return String("value" in v ? v.value : "");
652
+ }
653
+ function toNativeType(attr) {
654
+ const name = attr.name.slice("db.".length);
655
+ const args = attr.args.filter((a) => a.kind === "number").map((a) => a.value);
656
+ return { name, args };
657
+ }
658
+ function toDefaultValue(v) {
659
+ if (!v) return {};
660
+ switch (v.kind) {
661
+ case "function":
662
+ return { function: { name: v.name, args: v.args } };
663
+ case "string":
664
+ return { literal: v.value };
665
+ case "number":
666
+ return { literal: v.value };
667
+ case "boolean":
668
+ return { literal: v.value };
669
+ case "ref":
670
+ return { literal: v.value };
671
+ default:
672
+ return {};
673
+ }
674
+ }
675
+ function toRelationInfo(args) {
676
+ const info = {};
677
+ for (const arg of args) {
678
+ if (arg.kind === "string") {
679
+ info.name = arg.value;
680
+ continue;
681
+ }
682
+ const named = asNamed(arg);
683
+ if (!named) continue;
684
+ const [key, value] = named;
685
+ if (key === "name" && value.kind === "string") info.name = value.value;
686
+ if (key === "fields") info.fields = refArray(value);
687
+ if (key === "references") info.references = refArray(value);
688
+ if (key === "onDelete") info.onDelete = refName(value);
689
+ if (key === "onUpdate") info.onUpdate = refName(value);
690
+ }
691
+ return info;
692
+ }
693
+ function asNamed(arg) {
694
+ if (arg.kind === "function" && arg.name.startsWith("__named:")) {
695
+ return [arg.name.slice("__named:".length), arg.args[0]];
696
+ }
697
+ return void 0;
698
+ }
699
+ function namedArg(args, key) {
700
+ for (const arg of args) {
701
+ const named = asNamed(arg);
702
+ if (named && named[0] === key && named[1].kind === "string") {
703
+ return named[1].value;
704
+ }
705
+ }
706
+ return void 0;
707
+ }
708
+ function refArray(v) {
709
+ if (v.kind === "array") {
710
+ return v.items.map((i) => refName(i)).filter((s) => !!s);
711
+ }
712
+ const single = refName(v);
713
+ return single ? [single] : [];
714
+ }
715
+ function refName(v) {
716
+ if (v.kind === "ref") return v.value;
717
+ if (v.kind === "string") return v.value;
718
+ return void 0;
719
+ }
720
+ function fieldNameList(args) {
721
+ for (const arg of args) {
722
+ if (arg.kind === "array") return refArray(arg);
723
+ const named = asNamed(arg);
724
+ if (named && named[0] === "fields") return refArray(named[1]);
725
+ }
726
+ return args.map((a) => refName(a)).filter((s) => !!s);
727
+ }
728
+ function resolveKinds(doc) {
729
+ const modelNames = new Set(doc.models.map((m) => m.name));
730
+ const enumNames = new Set(doc.enums.map((e) => e.name));
731
+ for (const model of doc.models) {
732
+ for (const field of model.fields) {
733
+ if (modelNames.has(field.type)) field.kind = "object";
734
+ else if (enumNames.has(field.type)) field.kind = "enum";
735
+ else field.kind = "scalar";
736
+ }
737
+ }
738
+ }
739
+
740
+ // src/schema/validator.ts
741
+ function validateSchema(doc) {
742
+ const errors = [];
743
+ const scalar = new Set(SCALAR_TYPES);
744
+ const enumNames = new Set(doc.enums.map((e) => e.name));
745
+ const modelNames = new Set(doc.models.map((m) => m.name));
746
+ const duplicateModels = findDuplicates(doc.models.map((m) => m.name));
747
+ for (const name of duplicateModels) {
748
+ errors.push(`Duplicate model '${name}'.`);
749
+ }
750
+ for (const model of doc.models) {
751
+ const fieldNames = /* @__PURE__ */ new Set();
752
+ for (const field of model.fields) {
753
+ if (fieldNames.has(field.name)) {
754
+ errors.push(`Duplicate field '${model.name}.${field.name}'.`);
755
+ }
756
+ fieldNames.add(field.name);
757
+ const known = scalar.has(field.type) || enumNames.has(field.type) || modelNames.has(field.type);
758
+ if (!known) {
759
+ errors.push(
760
+ `Field '${model.name}.${field.name}' has unknown type '${field.type}'.`
761
+ );
762
+ }
763
+ if (field.kind === "object" && field.relation) {
764
+ validateRelation(doc, model.name, field.name, field.relation, errors);
765
+ }
766
+ }
767
+ for (const pk of model.primaryKey) {
768
+ if (!fieldNames.has(pk)) {
769
+ errors.push(
770
+ `Primary key field '${pk}' does not exist on model '${model.name}'.`
771
+ );
772
+ }
773
+ }
774
+ const hasId = model.primaryKey.length > 0 || model.fields.some((f) => f.isId);
775
+ if (!hasId) {
776
+ errors.push(
777
+ `Model '${model.name}' has no @id / @@id. Every model needs a primary key.`
778
+ );
779
+ }
780
+ }
781
+ for (const enumNode of doc.enums) {
782
+ if (enumNode.values.length === 0) {
783
+ errors.push(`Enum '${enumNode.name}' has no values.`);
784
+ }
785
+ }
786
+ if (errors.length > 0) {
787
+ throw new SchemaValidationError("Schema validation failed", errors);
788
+ }
789
+ }
790
+ function validateRelation(doc, modelName, fieldName, relation, errors) {
791
+ const model = findModel(doc, modelName);
792
+ if (!model) return;
793
+ for (const f of relation.fields ?? []) {
794
+ if (!model.fields.some((mf) => mf.name === f)) {
795
+ errors.push(
796
+ `Relation '${modelName}.${fieldName}' references local field '${f}' which does not exist.`
797
+ );
798
+ }
799
+ }
800
+ }
801
+ function findDuplicates(values) {
802
+ const seen = /* @__PURE__ */ new Set();
803
+ const dups = /* @__PURE__ */ new Set();
804
+ for (const v of values) {
805
+ if (seen.has(v)) dups.add(v);
806
+ seen.add(v);
807
+ }
808
+ return [...dups];
809
+ }
810
+
811
+ // src/schema/printer.ts
812
+ function printSchema(doc) {
813
+ const blocks = [];
814
+ if (doc.datasource) {
815
+ const ds = doc.datasource;
816
+ const url = ds.url.kind === "env" ? `env("${ds.url.value}")` : `"${ds.url.value}"`;
817
+ blocks.push(
818
+ `datasource ${ds.name} {
819
+ provider = "${ds.provider}"
820
+ url = ${url}
821
+ }`
822
+ );
823
+ }
824
+ for (const gen of doc.generators) {
825
+ const lines = [` provider = "${gen.provider}"`];
826
+ if (gen.output) lines.push(` output = "${gen.output}"`);
827
+ for (const [k, v] of Object.entries(gen.config)) {
828
+ if (k === "provider" || k === "output") continue;
829
+ lines.push(` ${k} = "${v}"`);
830
+ }
831
+ blocks.push(`generator ${gen.name} {
832
+ ${lines.join("\n")}
833
+ }`);
834
+ }
835
+ for (const enumNode of doc.enums) {
836
+ blocks.push(printEnum(enumNode));
837
+ }
838
+ for (const model of doc.models) {
839
+ blocks.push(printModel(model));
840
+ }
841
+ return blocks.join("\n\n") + "\n";
842
+ }
843
+ function printEnum(node) {
844
+ const lines = [];
845
+ if (node.documentation) lines.push(...docLines(node.documentation));
846
+ lines.push(`enum ${node.name} {`);
847
+ for (const v of node.values) {
848
+ lines.push(` ${v.name}${v.dbName ? ` @map("${v.dbName}")` : ""}`);
849
+ }
850
+ if (node.dbName) lines.push(`
851
+ @@map("${node.dbName}")`);
852
+ lines.push(`}`);
853
+ return lines.join("\n");
854
+ }
855
+ function printModel(model) {
856
+ const lines = [];
857
+ if (model.documentation) lines.push(...docLines(model.documentation));
858
+ lines.push(`model ${model.name} {`);
859
+ const nameWidth = Math.max(...model.fields.map((f) => f.name.length), 0);
860
+ const typeWidth = Math.max(...model.fields.map((f) => fieldType(f).length), 0);
861
+ for (const field of model.fields) {
862
+ if (field.documentation) {
863
+ lines.push(...docLines(field.documentation).map((l) => ` ${l}`));
864
+ }
865
+ const attrs = fieldAttributes(field);
866
+ const name = field.name.padEnd(nameWidth);
867
+ const type = fieldType(field).padEnd(typeWidth);
868
+ lines.push(` ${name} ${type}${attrs ? ` ${attrs}` : ""}`.trimEnd());
869
+ }
870
+ const blockAttrs = modelBlockAttributes(model);
871
+ if (blockAttrs.length > 0) {
872
+ lines.push("");
873
+ for (const a of blockAttrs) lines.push(` ${a}`);
874
+ }
875
+ lines.push(`}`);
876
+ return lines.join("\n");
877
+ }
878
+ function fieldType(field) {
879
+ let t = field.type;
880
+ if (field.isList) t += "[]";
881
+ else if (!field.isRequired) t += "?";
882
+ return t;
883
+ }
884
+ function fieldAttributes(field) {
885
+ const parts = [];
886
+ if (field.isId) parts.push("@id");
887
+ if (field.isUnique) parts.push("@unique");
888
+ if (field.default) parts.push(`@default(${printDefault(field.default)})`);
889
+ if (field.isUpdatedAt) parts.push("@updatedAt");
890
+ if (field.relation) {
891
+ const rel = field.relation;
892
+ const args = [];
893
+ if (rel.name) args.push(`"${rel.name}"`);
894
+ if (rel.fields?.length) args.push(`fields: [${rel.fields.join(", ")}]`);
895
+ if (rel.references?.length)
896
+ args.push(`references: [${rel.references.join(", ")}]`);
897
+ if (rel.onDelete) args.push(`onDelete: ${rel.onDelete}`);
898
+ if (rel.onUpdate) args.push(`onUpdate: ${rel.onUpdate}`);
899
+ parts.push(args.length ? `@relation(${args.join(", ")})` : "@relation");
900
+ }
901
+ if (field.nativeType) {
902
+ const a = field.nativeType.args.length ? `(${field.nativeType.args.join(", ")})` : "";
903
+ parts.push(`@db.${field.nativeType.name}${a}`);
904
+ }
905
+ if (field.dbName) parts.push(`@map("${field.dbName}")`);
906
+ return parts.join(" ");
907
+ }
908
+ function printDefault(def) {
909
+ if (def.function) {
910
+ return `${def.function.name}(${def.function.args.map(printArgValue).join(", ")})`;
911
+ }
912
+ if (typeof def.literal === "string") {
913
+ return /^[A-Za-z_][A-Za-z0-9_]*$/.test(def.literal) ? def.literal : `"${def.literal}"`;
914
+ }
915
+ return String(def.literal);
916
+ }
917
+ function printArgValue(v) {
918
+ switch (v.kind) {
919
+ case "string":
920
+ return `"${v.value}"`;
921
+ case "number":
922
+ return String(v.value);
923
+ case "boolean":
924
+ return String(v.value);
925
+ case "ref":
926
+ return v.value;
927
+ case "array":
928
+ return `[${v.items.map(printArgValue).join(", ")}]`;
929
+ case "function":
930
+ return `${v.name}(${v.args.map(printArgValue).join(", ")})`;
931
+ }
932
+ }
933
+ function modelBlockAttributes(model) {
934
+ const out = [];
935
+ const inlineId = model.primaryKey.length === 1 && model.fields.find((f) => f.name === model.primaryKey[0])?.isId;
936
+ if (model.primaryKey.length > 0 && !inlineId) {
937
+ out.push(`@@id([${model.primaryKey.join(", ")}])`);
938
+ }
939
+ for (const u of model.uniqueIndexes) {
940
+ out.push(
941
+ `@@unique([${u.fields.join(", ")}]${u.name ? `, map: "${u.name}"` : ""})`
942
+ );
943
+ }
944
+ for (const i of model.indexes) {
945
+ out.push(
946
+ `@@index([${i.fields.join(", ")}]${i.name ? `, map: "${i.name}"` : ""})`
947
+ );
948
+ }
949
+ if (model.dbName) out.push(`@@map("${model.dbName}")`);
950
+ return out;
951
+ }
952
+ function docLines(documentation) {
953
+ return documentation.split("\n").map((l) => `/// ${l}`);
954
+ }
955
+
956
+ // src/utils/index.ts
957
+ function isPlainObject(v) {
958
+ return typeof v === "object" && v !== null && !Array.isArray(v) && !(v instanceof Date) && !Buffer.isBuffer(v);
959
+ }
960
+ function uniq(items) {
961
+ return [...new Set(items)];
962
+ }
963
+ function pascalCase(input) {
964
+ return input.toLowerCase().replace(
965
+ /[^a-zA-Z0-9]+(.)?/g,
966
+ (_, c) => c ? c.toUpperCase() : ""
967
+ ).replace(/^(.)/, (_, c) => c.toUpperCase());
968
+ }
969
+ function camelCase(input) {
970
+ const pascal = pascalCase(input);
971
+ return pascal.charAt(0).toLowerCase() + pascal.slice(1);
972
+ }
973
+ function pluralize(word) {
974
+ if (/[^aeiou]y$/i.test(word)) return word.replace(/y$/i, "ies");
975
+ if (/(s|x|z|ch|sh)$/i.test(word)) return `${word}es`;
976
+ return `${word}s`;
977
+ }
978
+ function lowerFirst(input) {
979
+ return input.charAt(0).toLowerCase() + input.slice(1);
980
+ }
981
+
982
+ // src/schema/index.ts
983
+ function parseSchema(source, file) {
984
+ return new Parser(source, file).parse();
985
+ }
986
+ function parseAndValidate(source, file) {
987
+ const doc = parseSchema(source, file);
988
+ validateSchema(doc);
989
+ return doc;
990
+ }
991
+ var DEFAULT_SCHEMA_PATHS = [
992
+ "ember/schema.ember",
993
+ "schema.ember",
994
+ "prisma/schema.ember"
995
+ ];
996
+ function findSchemaPath(base = process.cwd(), explicit) {
997
+ if (explicit) {
998
+ const p = resolve(base, explicit);
999
+ return existsSync(p) ? p : void 0;
1000
+ }
1001
+ for (const candidate of DEFAULT_SCHEMA_PATHS) {
1002
+ const p = resolve(base, candidate);
1003
+ if (existsSync(p)) return p;
1004
+ }
1005
+ return void 0;
1006
+ }
1007
+ function loadSchema(path) {
1008
+ const source = readFileSync(path, "utf8");
1009
+ const document = parseAndValidate(source, path);
1010
+ return {
1011
+ document,
1012
+ path,
1013
+ databaseUrl: resolveDatasourceUrl(document, dirname(path))
1014
+ };
1015
+ }
1016
+ function resolveDatasourceUrl(doc, _base) {
1017
+ const ds = doc.datasource;
1018
+ if (!ds) return void 0;
1019
+ if (ds.url.kind === "literal") return ds.url.value;
1020
+ return process.env[ds.url.value];
1021
+ }
1022
+
1023
+ // src/driver/firebird-driver.ts
1024
+ import { AsyncLocalStorage } from "async_hooks";
1025
+ import Firebird from "node-firebird";
1026
+ var fb = Firebird;
1027
+ function isolationConstant(level) {
1028
+ switch (level) {
1029
+ case "READ_COMMITTED_READ_ONLY":
1030
+ return fb.ISOLATION_READ_COMMITTED_READ_ONLY;
1031
+ case "REPEATABLE_READ":
1032
+ return fb.ISOLATION_REPEATABLE_READ;
1033
+ case "SERIALIZABLE":
1034
+ return fb.ISOLATION_SERIALIZABLE;
1035
+ case "READ_COMMITTED":
1036
+ default:
1037
+ return fb.ISOLATION_READ_COMMITTED;
1038
+ }
1039
+ }
1040
+ var FirebirdDriver = class {
1041
+ pool = null;
1042
+ options;
1043
+ poolMax;
1044
+ onQuery;
1045
+ activeTx = new AsyncLocalStorage();
1046
+ constructor(config, driverOptions) {
1047
+ this.poolMax = config.poolMax ?? 5;
1048
+ this.onQuery = driverOptions?.onQuery;
1049
+ this.options = {
1050
+ host: config.host,
1051
+ port: config.port,
1052
+ database: config.database,
1053
+ user: config.user,
1054
+ password: config.password,
1055
+ role: config.role ?? "",
1056
+ pageSize: config.pageSize ?? 4096,
1057
+ encoding: config.encoding ?? "UTF8",
1058
+ blobAsText: config.blobAsText ?? true,
1059
+ lowercase_keys: config.lowercaseKeys ?? false,
1060
+ retryConnectionInterval: 1e3,
1061
+ // FB3+ secure auth (Srp) is negotiated by default; set explicitly to force
1062
+ // a plugin, or "Legacy_Auth" for Firebird 2.1/2.5 servers.
1063
+ ...config.authPlugin ? { pluginName: config.authPlugin } : {},
1064
+ ...config.wireCompression != null ? { wireCompression: config.wireCompression } : {}
1065
+ };
1066
+ }
1067
+ async connect() {
1068
+ if (this.pool) return;
1069
+ this.pool = fb.pool(this.poolMax, this.options);
1070
+ }
1071
+ async disconnect() {
1072
+ if (!this.pool) return;
1073
+ this.pool.destroy();
1074
+ this.pool = null;
1075
+ }
1076
+ async transaction(fn, options) {
1077
+ const existing = this.activeTx.getStore();
1078
+ if (existing) {
1079
+ return fn(existing);
1080
+ }
1081
+ await this.connect();
1082
+ const db = await this.acquire();
1083
+ const tr = await this.begin(db, options?.isolation);
1084
+ const ctx = {
1085
+ query: (sql, params) => this.runOnTransaction(tr, sql, params)
1086
+ };
1087
+ try {
1088
+ const result = await this.activeTx.run(ctx, () => fn(ctx));
1089
+ await this.commit(tr);
1090
+ return result;
1091
+ } catch (err) {
1092
+ await this.safeRollback(tr);
1093
+ throw err;
1094
+ } finally {
1095
+ db.detach();
1096
+ }
1097
+ }
1098
+ // ---- promise wrappers over node-firebird -------------------------------
1099
+ acquire() {
1100
+ return new Promise((resolve3, reject) => {
1101
+ this.pool.get((err, db) => {
1102
+ if (err) return reject(wrap(err, "Failed to acquire connection"));
1103
+ resolve3(db);
1104
+ });
1105
+ });
1106
+ }
1107
+ begin(db, isolation) {
1108
+ return new Promise((resolve3, reject) => {
1109
+ db.transaction(isolationConstant(isolation), (err, tr) => {
1110
+ if (err) return reject(wrap(err, "Failed to start transaction"));
1111
+ resolve3(tr);
1112
+ });
1113
+ });
1114
+ }
1115
+ runOnTransaction(tr, sql, params = []) {
1116
+ const start = this.onQuery ? performance.now() : 0;
1117
+ return new Promise((resolve3, reject) => {
1118
+ tr.query(sql, [...params], (err, result) => {
1119
+ if (err) return reject(wrap(err, "Query failed", sql));
1120
+ const rows = normalizeRows(result);
1121
+ if (this.onQuery) {
1122
+ this.onQuery({
1123
+ sql,
1124
+ params,
1125
+ durationMs: performance.now() - start,
1126
+ rowCount: rows.length
1127
+ });
1128
+ }
1129
+ resolve3(rows);
1130
+ });
1131
+ });
1132
+ }
1133
+ commit(tr) {
1134
+ return new Promise((resolve3, reject) => {
1135
+ tr.commit((err) => {
1136
+ if (err) return reject(wrap(err, "Failed to commit transaction"));
1137
+ resolve3();
1138
+ });
1139
+ });
1140
+ }
1141
+ safeRollback(tr) {
1142
+ return new Promise((resolve3) => {
1143
+ tr.rollback(() => resolve3());
1144
+ });
1145
+ }
1146
+ };
1147
+ function normalizeRows(result) {
1148
+ if (Array.isArray(result)) return result;
1149
+ if (result === void 0 || result === null) return [];
1150
+ return [result];
1151
+ }
1152
+ function wrap(err, message, sql) {
1153
+ const detail = err && typeof err === "object" && "message" in err ? String(err.message) : String(err);
1154
+ return new DatabaseError(`${message}: ${detail}`, err, sql);
1155
+ }
1156
+
1157
+ // src/driver/url.ts
1158
+ function parseConnectionUrl(url) {
1159
+ let parsed;
1160
+ try {
1161
+ parsed = new URL(url);
1162
+ } catch {
1163
+ throw new EmberError(`Invalid Firebird connection URL: ${url}`);
1164
+ }
1165
+ if (!/^firebird:?$/.test(parsed.protocol.replace(":", "") + ":")) {
1166
+ if (parsed.protocol !== "firebird:") {
1167
+ throw new EmberError(
1168
+ `Unsupported protocol '${parsed.protocol}'. Expected 'firebird:'.`
1169
+ );
1170
+ }
1171
+ }
1172
+ const database = normalizeDatabasePath(parsed.pathname);
1173
+ if (!database) {
1174
+ throw new EmberError(`Connection URL is missing a database path: ${url}`);
1175
+ }
1176
+ const params = parsed.searchParams;
1177
+ const config = {
1178
+ host: parsed.hostname || "127.0.0.1",
1179
+ port: parsed.port ? Number(parsed.port) : 3050,
1180
+ database,
1181
+ user: decodeURIComponent(parsed.username || "SYSDBA"),
1182
+ password: decodeURIComponent(parsed.password || "masterkey"),
1183
+ encoding: params.get("encoding") ?? "UTF8"
1184
+ };
1185
+ const role = params.get("role");
1186
+ if (role) config.role = role;
1187
+ const poolMax = params.get("poolMax") ?? params.get("connection_limit");
1188
+ if (poolMax) config.poolMax = Number(poolMax);
1189
+ const pageSize = params.get("pageSize");
1190
+ if (pageSize) config.pageSize = Number(pageSize);
1191
+ const auth = (params.get("authPlugin") ?? params.get("auth"))?.toLowerCase();
1192
+ if (auth === "legacy" || auth === "legacy_auth") config.authPlugin = "Legacy_Auth";
1193
+ else if (auth === "srp") config.authPlugin = "Srp";
1194
+ const wireCompression = params.get("wireCompression");
1195
+ if (wireCompression != null) {
1196
+ config.wireCompression = wireCompression !== "false" && wireCompression !== "0";
1197
+ }
1198
+ const version = params.get("version");
1199
+ if (version) config.version = normalizeVersion(version);
1200
+ return config;
1201
+ }
1202
+ var VERSIONS = /* @__PURE__ */ new Set(["2.1", "2.5", "3", "4", "5"]);
1203
+ function normalizeVersion(raw) {
1204
+ const trimmed = raw.trim();
1205
+ if (VERSIONS.has(trimmed)) return trimmed;
1206
+ if (/^2\.1/.test(trimmed)) return "2.1";
1207
+ if (/^2\.5/.test(trimmed)) return "2.5";
1208
+ const major = trimmed.split(".")[0];
1209
+ if (major && VERSIONS.has(major)) return major;
1210
+ return void 0;
1211
+ }
1212
+ function normalizeDatabasePath(pathname) {
1213
+ let p = decodeURIComponent(pathname);
1214
+ if (p.startsWith("//")) p = p.slice(1);
1215
+ if (/^\/[A-Za-z]:\//.test(p)) p = p.slice(1);
1216
+ return p;
1217
+ }
1218
+ function buildConnectionUrl(config) {
1219
+ const auth = `${encodeURIComponent(config.user)}:${encodeURIComponent(
1220
+ config.password
1221
+ )}`;
1222
+ const dbPath = config.database.startsWith("/") ? `/${config.database}` : `/${config.database}`;
1223
+ const url = new URL(`firebird://${auth}@${config.host}:${config.port}${dbPath}`);
1224
+ if (config.role) url.searchParams.set("role", config.role);
1225
+ return url.toString();
1226
+ }
1227
+
1228
+ // src/driver/index.ts
1229
+ function createDriver(source, options) {
1230
+ const config = typeof source === "string" ? parseConnectionUrl(source) : source;
1231
+ return new FirebirdDriver(config, options);
1232
+ }
1233
+
1234
+ // src/sql/fragment.ts
1235
+ var Sql = class _Sql {
1236
+ parts = [];
1237
+ params = [];
1238
+ static raw(text) {
1239
+ return new _Sql().push(text);
1240
+ }
1241
+ static value(value) {
1242
+ return new _Sql().bind(value);
1243
+ }
1244
+ static join(fragments, separator) {
1245
+ const out = new _Sql();
1246
+ fragments.forEach((frag, i) => {
1247
+ if (i > 0) out.push(separator);
1248
+ out.append(frag);
1249
+ });
1250
+ return out;
1251
+ }
1252
+ /** Append raw, trusted SQL text (keywords, already-escaped identifiers). */
1253
+ push(text) {
1254
+ this.parts.push(text);
1255
+ return this;
1256
+ }
1257
+ /** Append a `?` placeholder bound to `value`. */
1258
+ bind(value) {
1259
+ this.parts.push("?");
1260
+ this.params.push(value);
1261
+ return this;
1262
+ }
1263
+ /** Append a comma-separated list of placeholders bound to `values`. */
1264
+ bindList(values) {
1265
+ this.parts.push(values.map(() => "?").join(", "));
1266
+ this.params.push(...values);
1267
+ return this;
1268
+ }
1269
+ /** Merge another fragment (text + params) into this one. */
1270
+ append(other) {
1271
+ this.parts.push(other.text);
1272
+ this.params.push(...other.params);
1273
+ return this;
1274
+ }
1275
+ get text() {
1276
+ return this.parts.join("");
1277
+ }
1278
+ isEmpty() {
1279
+ return this.text.trim().length === 0;
1280
+ }
1281
+ toQuery() {
1282
+ return { sql: this.text, params: this.params };
1283
+ }
1284
+ };
1285
+
1286
+ // src/sql/dialect.ts
1287
+ var FirebirdDialect = class {
1288
+ supportsReturning = true;
1289
+ version;
1290
+ supportsBooleanType;
1291
+ supportsIdentity;
1292
+ supportsWindowFunctions;
1293
+ constructor(options = {}) {
1294
+ this.version = options.version ?? "3";
1295
+ const rank = versionRank(this.version);
1296
+ this.supportsBooleanType = rank >= 30;
1297
+ this.supportsIdentity = rank >= 30;
1298
+ this.supportsWindowFunctions = rank >= 30;
1299
+ }
1300
+ booleanColumnType() {
1301
+ return this.supportsBooleanType ? "BOOLEAN" : "SMALLINT";
1302
+ }
1303
+ quoteId(name) {
1304
+ return `"${name.replace(/"/g, '""')}"`;
1305
+ }
1306
+ quoteRef(table, column) {
1307
+ return `${this.quoteId(table)}.${this.quoteId(column)}`;
1308
+ }
1309
+ paginationClause(take, skip) {
1310
+ const parts = [];
1311
+ if (typeof take === "number" && take >= 0) {
1312
+ parts.push(`FIRST ${Math.trunc(take)}`);
1313
+ }
1314
+ if (typeof skip === "number" && skip > 0) {
1315
+ parts.push(`SKIP ${Math.trunc(skip)}`);
1316
+ }
1317
+ return parts.join(" ");
1318
+ }
1319
+ caseInsensitive(expr) {
1320
+ return `UPPER(${expr})`;
1321
+ }
1322
+ defaultFunctionSql(name) {
1323
+ switch (name) {
1324
+ case "now":
1325
+ return "CURRENT_TIMESTAMP";
1326
+ case "uuid":
1327
+ case "cuid":
1328
+ return "UUID_TO_CHAR(GEN_UUID())";
1329
+ case "autoincrement":
1330
+ return null;
1331
+ // handled via generators/identity, not an inline default
1332
+ default:
1333
+ return null;
1334
+ }
1335
+ }
1336
+ coerceValue(value) {
1337
+ if (value === null || value === void 0) return null;
1338
+ if (value instanceof Date) return value;
1339
+ if (Buffer.isBuffer(value)) return value;
1340
+ if (typeof value === "boolean") {
1341
+ return this.supportsBooleanType ? value : value ? 1 : 0;
1342
+ }
1343
+ if (typeof value === "bigint") return value;
1344
+ if (typeof value === "number") return value;
1345
+ if (typeof value === "string") return value;
1346
+ return JSON.stringify(value);
1347
+ }
1348
+ };
1349
+ function versionRank(version) {
1350
+ switch (version) {
1351
+ case "2.1":
1352
+ return 21;
1353
+ case "2.5":
1354
+ return 25;
1355
+ case "3":
1356
+ return 30;
1357
+ case "4":
1358
+ return 40;
1359
+ case "5":
1360
+ return 50;
1361
+ default:
1362
+ return 30;
1363
+ }
1364
+ }
1365
+
1366
+ // src/query/order.ts
1367
+ function compileOrderBy(model, tableAlias, orderBy, dialect) {
1368
+ if (!orderBy) return new Sql();
1369
+ const entries = normalize(orderBy);
1370
+ if (entries.length === 0) return new Sql();
1371
+ const parts = entries.map(([fieldName, dir]) => {
1372
+ const field = model.fields.find((f) => f.name === fieldName);
1373
+ if (!field || field.kind === "object") {
1374
+ throw new QueryValidationError(
1375
+ `Cannot order '${model.name}' by '${fieldName}'.`
1376
+ );
1377
+ }
1378
+ const ref = dialect.quoteRef(tableAlias, fieldColumn(field));
1379
+ return Sql.raw(`${ref} ${dir === "desc" ? "DESC" : "ASC"}`);
1380
+ });
1381
+ return Sql.join(parts, ", ");
1382
+ }
1383
+ function normalize(orderBy) {
1384
+ const list = Array.isArray(orderBy) ? orderBy : [orderBy];
1385
+ const out = [];
1386
+ for (const obj of list) {
1387
+ for (const [key, value] of Object.entries(obj)) {
1388
+ if (value === "asc" || value === "desc") out.push([key, value]);
1389
+ }
1390
+ }
1391
+ return out;
1392
+ }
1393
+
1394
+ // src/query/relations.ts
1395
+ function resolveRelation(schema, model, field) {
1396
+ if (field.kind !== "object") {
1397
+ throw new QueryValidationError(
1398
+ `Field '${model.name}.${field.name}' is not a relation.`
1399
+ );
1400
+ }
1401
+ const relatedModel = findModel(schema, field.type);
1402
+ if (!relatedModel) {
1403
+ throw new QueryValidationError(
1404
+ `Relation '${model.name}.${field.name}' points to unknown model '${field.type}'.`
1405
+ );
1406
+ }
1407
+ if (field.relation?.fields?.length) {
1408
+ const fromFields2 = field.relation.fields;
1409
+ const toFields2 = field.relation.references ?? idFields(relatedModel).map((f) => f.name);
1410
+ return build(field, model, relatedModel, fromFields2, toFields2, true);
1411
+ }
1412
+ const partner = findPartnerField(schema, model, field, relatedModel);
1413
+ if (partner?.relation?.fields?.length) {
1414
+ const toFields2 = partner.relation.fields;
1415
+ const fromFields2 = partner.relation.references ?? idFields(model).map((f) => f.name);
1416
+ return build(field, model, relatedModel, fromFields2, toFields2, false);
1417
+ }
1418
+ const fromFields = idFields(model).map((f) => f.name);
1419
+ const toFields = idFields(relatedModel).map((f) => f.name);
1420
+ return build(field, model, relatedModel, fromFields, toFields, false);
1421
+ }
1422
+ function build(field, model, relatedModel, fromFields, toFields, owns) {
1423
+ return {
1424
+ field,
1425
+ relatedModel,
1426
+ fromFields,
1427
+ toFields,
1428
+ fromColumns: fromFields.map((n) => columnOf(model, n)),
1429
+ toColumns: toFields.map((n) => columnOf(relatedModel, n)),
1430
+ isList: field.isList,
1431
+ owns
1432
+ };
1433
+ }
1434
+ function columnOf(model, fieldName) {
1435
+ const f = model.fields.find((x) => x.name === fieldName);
1436
+ if (!f) {
1437
+ throw new QueryValidationError(
1438
+ `Field '${fieldName}' not found on model '${model.name}'.`
1439
+ );
1440
+ }
1441
+ return fieldColumn(f);
1442
+ }
1443
+ function findPartnerField(_schema, model, field, relatedModel) {
1444
+ const candidates = relatedModel.fields.filter(
1445
+ (f) => f.kind === "object" && f.type === model.name
1446
+ );
1447
+ if (field.relation?.name) {
1448
+ const byName = candidates.find(
1449
+ (f) => f.relation?.name === field.relation?.name
1450
+ );
1451
+ if (byName) return byName;
1452
+ }
1453
+ const owning = candidates.find((f) => f.relation?.fields?.length);
1454
+ return owning ?? candidates[0];
1455
+ }
1456
+
1457
+ // src/query/where.ts
1458
+ var SCALAR_OPERATORS = /* @__PURE__ */ new Set([
1459
+ "equals",
1460
+ "not",
1461
+ "in",
1462
+ "notIn",
1463
+ "lt",
1464
+ "lte",
1465
+ "gt",
1466
+ "gte",
1467
+ "contains",
1468
+ "startsWith",
1469
+ "endsWith",
1470
+ "mode"
1471
+ ]);
1472
+ function compileWhere(model, tableAlias, where, ctx) {
1473
+ if (!where || Object.keys(where).length === 0) return new Sql();
1474
+ const conditions = [];
1475
+ for (const [key, value] of Object.entries(where)) {
1476
+ if (value === void 0) continue;
1477
+ if (key === "AND") {
1478
+ conditions.push(combineList(asArray(value), model, tableAlias, ctx, "AND"));
1479
+ continue;
1480
+ }
1481
+ if (key === "OR") {
1482
+ conditions.push(combineList(asArray(value), model, tableAlias, ctx, "OR"));
1483
+ continue;
1484
+ }
1485
+ if (key === "NOT") {
1486
+ const inner = combineList(asArray(value), model, tableAlias, ctx, "AND");
1487
+ if (!inner.isEmpty()) {
1488
+ conditions.push(new Sql().push("NOT (").append(inner).push(")"));
1489
+ }
1490
+ continue;
1491
+ }
1492
+ const field = model.fields.find((f) => f.name === key);
1493
+ if (!field) {
1494
+ throw new QueryValidationError(
1495
+ `Unknown field '${key}' in where clause of model '${model.name}'.`
1496
+ );
1497
+ }
1498
+ if (field.kind === "object") {
1499
+ conditions.push(
1500
+ compileRelationFilter(model, tableAlias, field, value, ctx)
1501
+ );
1502
+ } else {
1503
+ conditions.push(compileScalar(tableAlias, field, value, ctx));
1504
+ }
1505
+ }
1506
+ const nonEmpty = conditions.filter((c) => !c.isEmpty());
1507
+ if (nonEmpty.length === 0) return new Sql();
1508
+ return wrapAnd(nonEmpty);
1509
+ }
1510
+ function combineList(list, model, tableAlias, ctx, op) {
1511
+ const parts = list.map((w) => compileWhere(model, tableAlias, w, ctx)).filter((p) => !p.isEmpty());
1512
+ if (parts.length === 0) return new Sql();
1513
+ if (parts.length === 1) return parts[0];
1514
+ const joined = Sql.join(
1515
+ parts.map((p) => new Sql().push("(").append(p).push(")")),
1516
+ ` ${op} `
1517
+ );
1518
+ return new Sql().push("(").append(joined).push(")");
1519
+ }
1520
+ function wrapAnd(parts) {
1521
+ if (parts.length === 1) return parts[0];
1522
+ const joined = Sql.join(
1523
+ parts.map((p) => new Sql().push("(").append(p).push(")")),
1524
+ " AND "
1525
+ );
1526
+ return new Sql().push("(").append(joined).push(")");
1527
+ }
1528
+ function compileScalar(tableAlias, field, value, ctx) {
1529
+ const ref = ctx.dialect.quoteRef(tableAlias, fieldColumn(field));
1530
+ if (field.type === "Json" && isJsonFilterObject(value)) {
1531
+ return compileJsonFilter(ref, field, value, ctx);
1532
+ }
1533
+ if (!isFilterObject(value)) {
1534
+ if (value === null) return Sql.raw(`${ref} IS NULL`);
1535
+ return new Sql().push(`${ref} = `).bind(ctx.dialect.coerceValue(value));
1536
+ }
1537
+ const filter = value;
1538
+ const insensitive = filter.mode === "insensitive";
1539
+ const parts = [];
1540
+ for (const [op, opValue] of Object.entries(filter)) {
1541
+ if (op === "mode") continue;
1542
+ if (opValue === void 0) continue;
1543
+ parts.push(compileOperator(ref, op, opValue, insensitive, ctx));
1544
+ }
1545
+ if (parts.length === 0) return new Sql();
1546
+ return wrapAnd(parts);
1547
+ }
1548
+ var JSON_OPERATORS = /* @__PURE__ */ new Set([
1549
+ "equals",
1550
+ "not",
1551
+ "string_contains",
1552
+ "string_starts_with",
1553
+ "string_ends_with",
1554
+ "path"
1555
+ ]);
1556
+ function isJsonFilterObject(value) {
1557
+ return isPlainObject(value) && Object.keys(value).some((k) => JSON_OPERATORS.has(k));
1558
+ }
1559
+ function compileJsonFilter(ref, field, filter, ctx) {
1560
+ const parts = [];
1561
+ for (const [op, raw] of Object.entries(filter)) {
1562
+ if (raw === void 0) continue;
1563
+ switch (op) {
1564
+ case "path":
1565
+ throw new QueryValidationError(
1566
+ `JSON 'path' filtering is not supported on Firebird (no JSON SQL functions). Filter '${field.name}' in application code or with a generated column.`
1567
+ );
1568
+ case "equals":
1569
+ parts.push(
1570
+ raw === null ? Sql.raw(`${ref} IS NULL`) : new Sql().push(`${ref} = `).bind(jsonText(raw))
1571
+ );
1572
+ break;
1573
+ case "not":
1574
+ parts.push(
1575
+ raw === null ? Sql.raw(`${ref} IS NOT NULL`) : new Sql().push(`${ref} <> `).bind(jsonText(raw))
1576
+ );
1577
+ break;
1578
+ case "string_contains":
1579
+ parts.push(jsonLike(ref, `%${escapeLike(String(raw))}%`));
1580
+ break;
1581
+ case "string_starts_with":
1582
+ parts.push(jsonLike(ref, `${escapeLike(String(raw))}%`));
1583
+ break;
1584
+ case "string_ends_with":
1585
+ parts.push(jsonLike(ref, `%${escapeLike(String(raw))}`));
1586
+ break;
1587
+ default:
1588
+ throw new QueryValidationError(`Unsupported JSON filter operator '${op}'.`);
1589
+ }
1590
+ }
1591
+ return parts.length ? wrapAnd(parts) : new Sql();
1592
+ }
1593
+ function jsonText(value) {
1594
+ return typeof value === "string" ? value : JSON.stringify(value);
1595
+ }
1596
+ function jsonLike(ref, pattern) {
1597
+ return new Sql().push(`${ref} LIKE `).bind(pattern).push(" ESCAPE '\\'");
1598
+ }
1599
+ function compileOperator(ref, op, value, insensitive, ctx) {
1600
+ const d = ctx.dialect;
1601
+ const bound = (v) => d.coerceValue(v);
1602
+ const lhs = insensitive ? d.caseInsensitive(ref) : ref;
1603
+ const rhs = (v) => insensitive && typeof v === "string" ? new Sql().push("UPPER(").bind(bound(v)).push(")") : Sql.value(bound(v));
1604
+ switch (op) {
1605
+ case "equals":
1606
+ if (value === null) return Sql.raw(`${ref} IS NULL`);
1607
+ return new Sql().push(`${lhs} = `).append(rhs(value));
1608
+ case "not":
1609
+ if (value === null) return Sql.raw(`${ref} IS NOT NULL`);
1610
+ if (isFilterObject(value)) {
1611
+ const inner = compileScalarFromRef(ref, value, insensitive, ctx);
1612
+ return new Sql().push("NOT (").append(inner).push(")");
1613
+ }
1614
+ return new Sql().push(`${lhs} <> `).append(rhs(value));
1615
+ case "in":
1616
+ return inClause(lhs, asUnknownArray(value), insensitive, ctx, false);
1617
+ case "notIn":
1618
+ return inClause(lhs, asUnknownArray(value), insensitive, ctx, true);
1619
+ case "lt":
1620
+ return new Sql().push(`${lhs} < `).append(rhs(value));
1621
+ case "lte":
1622
+ return new Sql().push(`${lhs} <= `).append(rhs(value));
1623
+ case "gt":
1624
+ return new Sql().push(`${lhs} > `).append(rhs(value));
1625
+ case "gte":
1626
+ return new Sql().push(`${lhs} >= `).append(rhs(value));
1627
+ case "contains":
1628
+ return likeClause(lhs, `%${escapeLike(String(value))}%`, insensitive);
1629
+ case "startsWith":
1630
+ return likeClause(lhs, `${escapeLike(String(value))}%`, insensitive);
1631
+ case "endsWith":
1632
+ return likeClause(lhs, `%${escapeLike(String(value))}`, insensitive);
1633
+ default:
1634
+ throw new QueryValidationError(`Unsupported filter operator '${op}'.`);
1635
+ }
1636
+ }
1637
+ function compileScalarFromRef(ref, filter, insensitive, ctx) {
1638
+ const parts = [];
1639
+ for (const [op, opValue] of Object.entries(filter)) {
1640
+ if (op === "mode" || opValue === void 0) continue;
1641
+ parts.push(compileOperator(ref, op, opValue, insensitive, ctx));
1642
+ }
1643
+ return parts.length ? wrapAnd(parts) : new Sql();
1644
+ }
1645
+ function inClause(lhs, values, insensitive, ctx, negate) {
1646
+ if (values.length === 0) {
1647
+ return Sql.raw(negate ? "1 = 1" : "1 = 0");
1648
+ }
1649
+ const coerced = values.map(
1650
+ (v) => insensitive && typeof v === "string" ? String(v).toUpperCase() : ctx.dialect.coerceValue(v)
1651
+ );
1652
+ const sql = new Sql().push(`${lhs} ${negate ? "NOT IN" : "IN"} (`);
1653
+ sql.bindList(coerced);
1654
+ sql.push(")");
1655
+ return sql;
1656
+ }
1657
+ function likeClause(lhs, pattern, insensitive) {
1658
+ const value = insensitive ? pattern.toUpperCase() : pattern;
1659
+ return new Sql().push(`${lhs} LIKE `).bind(value).push(" ESCAPE '\\'");
1660
+ }
1661
+ function compileRelationFilter(model, tableAlias, field, value, ctx) {
1662
+ const rel = resolveRelation(ctx.schema, model, field);
1663
+ const filter = isPlainObject(value) ? value : {};
1664
+ const hasOperator = "some" in filter || "every" in filter || "none" in filter || "is" in filter || "isNot" in filter;
1665
+ if (!rel.isList && !hasOperator) {
1666
+ return existsClause(model, tableAlias, rel, value, ctx, false);
1667
+ }
1668
+ const parts = [];
1669
+ if (filter.some !== void 0) {
1670
+ parts.push(existsClause(model, tableAlias, rel, filter.some ?? {}, ctx, false));
1671
+ }
1672
+ if (filter.none !== void 0) {
1673
+ parts.push(existsClause(model, tableAlias, rel, filter.none ?? {}, ctx, true));
1674
+ }
1675
+ if (filter.every !== void 0) {
1676
+ parts.push(
1677
+ existsClause(model, tableAlias, rel, filter.every ?? {}, ctx, true, true)
1678
+ );
1679
+ }
1680
+ if (filter.is !== void 0) {
1681
+ if (filter.is === null) {
1682
+ parts.push(notExists(model, tableAlias, rel, {}, ctx));
1683
+ } else {
1684
+ parts.push(existsClause(model, tableAlias, rel, filter.is, ctx, false));
1685
+ }
1686
+ }
1687
+ if (filter.isNot !== void 0) {
1688
+ if (filter.isNot === null) {
1689
+ parts.push(existsClause(model, tableAlias, rel, {}, ctx, false));
1690
+ } else {
1691
+ parts.push(existsClause(model, tableAlias, rel, filter.isNot, ctx, true));
1692
+ }
1693
+ }
1694
+ return parts.length ? wrapAnd(parts) : new Sql();
1695
+ }
1696
+ function notExists(model, tableAlias, rel, sub, ctx) {
1697
+ return existsClause(model, tableAlias, rel, sub, ctx, true);
1698
+ }
1699
+ function existsClause(_model, tableAlias, rel, sub, ctx, negate, everyMode = false) {
1700
+ const childAlias = `r${ctx.alias.next++}`;
1701
+ const childTable = ctx.dialect.quoteId(modelTable(rel.relatedModel));
1702
+ const sql = new Sql();
1703
+ sql.push(`${negate ? "NOT EXISTS" : "EXISTS"} (SELECT 1 FROM ${childTable} ${ctx.dialect.quoteId(childAlias)} WHERE `);
1704
+ const joins = rel.fromColumns.map((fromCol, i) => {
1705
+ const toCol = rel.toColumns[i];
1706
+ return `${ctx.dialect.quoteRef(childAlias, toCol)} = ${ctx.dialect.quoteRef(tableAlias, fromCol)}`;
1707
+ });
1708
+ sql.push(joins.join(" AND "));
1709
+ const subSql = compileWhere(rel.relatedModel, childAlias, sub, ctx);
1710
+ if (!subSql.isEmpty()) {
1711
+ if (everyMode) {
1712
+ sql.push(" AND NOT (").append(subSql).push(")");
1713
+ } else {
1714
+ sql.push(" AND ").append(subSql);
1715
+ }
1716
+ }
1717
+ sql.push(")");
1718
+ return sql;
1719
+ }
1720
+ function isFilterObject(value) {
1721
+ if (!isPlainObject(value)) return false;
1722
+ return Object.keys(value).some((k) => SCALAR_OPERATORS.has(k));
1723
+ }
1724
+ function asArray(value) {
1725
+ const list = Array.isArray(value) ? value : [value];
1726
+ return list.filter(
1727
+ (v) => v !== null && typeof v === "object"
1728
+ );
1729
+ }
1730
+ function asUnknownArray(value) {
1731
+ return Array.isArray(value) ? value : [value];
1732
+ }
1733
+ function escapeLike(input) {
1734
+ return input.replace(/[\\%_]/g, (c) => `\\${c}`);
1735
+ }
1736
+
1737
+ // src/query/having.ts
1738
+ var AGG_FN = {
1739
+ _count: "COUNT",
1740
+ _sum: "SUM",
1741
+ _avg: "AVG",
1742
+ _min: "MIN",
1743
+ _max: "MAX"
1744
+ };
1745
+ var COMPARATORS = {
1746
+ equals: "=",
1747
+ not: "<>",
1748
+ gt: ">",
1749
+ gte: ">=",
1750
+ lt: "<",
1751
+ lte: "<="
1752
+ };
1753
+ function compileHaving(model, alias, having, ctx) {
1754
+ if (!having || Object.keys(having).length === 0) return new Sql();
1755
+ const parts = [];
1756
+ for (const [key, value] of Object.entries(having)) {
1757
+ if (value === void 0) continue;
1758
+ if (key === "AND" || key === "OR") {
1759
+ const list = Array.isArray(value) ? value : [value];
1760
+ const compiled = list.map((w) => compileHaving(model, alias, w, ctx)).filter((s) => !s.isEmpty());
1761
+ if (compiled.length) parts.push(joinBool(compiled, key));
1762
+ continue;
1763
+ }
1764
+ if (key === "NOT") {
1765
+ const inner = compileHaving(model, alias, value, ctx);
1766
+ if (!inner.isEmpty()) parts.push(new Sql().push("NOT (").append(inner).push(")"));
1767
+ continue;
1768
+ }
1769
+ if (key in AGG_FN) {
1770
+ parts.push(...aggregateConditions(model, alias, key, value, ctx));
1771
+ continue;
1772
+ }
1773
+ const field = model.fields.find((f) => f.name === key && f.kind !== "object");
1774
+ if (!field) {
1775
+ throw new QueryValidationError(
1776
+ `Unknown having field '${model.name}.${key}'.`
1777
+ );
1778
+ }
1779
+ if (isAggregateWrapper(value)) {
1780
+ const ref = ctx.dialect.quoteRef(alias, fieldColumn(field));
1781
+ for (const [agg, filter] of Object.entries(value)) {
1782
+ if (!(agg in AGG_FN)) continue;
1783
+ parts.push(comparison(`${AGG_FN[agg]}(${ref})`, filter, ctx.dialect));
1784
+ }
1785
+ } else {
1786
+ const cond = compileWhere(model, alias, { [key]: value }, ctx);
1787
+ if (!cond.isEmpty()) parts.push(cond);
1788
+ }
1789
+ }
1790
+ const nonEmpty = parts.filter((p) => !p.isEmpty());
1791
+ if (nonEmpty.length === 0) return new Sql();
1792
+ return joinBool(nonEmpty, "AND");
1793
+ }
1794
+ function aggregateConditions(model, alias, agg, spec, ctx) {
1795
+ if (!isPlainObject(spec)) return [];
1796
+ const out = [];
1797
+ for (const [fieldName, filter] of Object.entries(spec)) {
1798
+ const field = model.fields.find((f) => f.name === fieldName && f.kind !== "object");
1799
+ if (!field) continue;
1800
+ const ref = ctx.dialect.quoteRef(alias, fieldColumn(field));
1801
+ out.push(comparison(`${AGG_FN[agg]}(${ref})`, filter, ctx.dialect));
1802
+ }
1803
+ return out;
1804
+ }
1805
+ function comparison(lhs, filter, dialect) {
1806
+ if (!isPlainObject(filter)) {
1807
+ return new Sql().push(`${lhs} = `).bind(dialect.coerceValue(filter));
1808
+ }
1809
+ const parts = [];
1810
+ for (const [op, v] of Object.entries(filter)) {
1811
+ const sqlOp = COMPARATORS[op];
1812
+ if (!sqlOp) {
1813
+ throw new QueryValidationError(`Unsupported having operator '${op}'.`);
1814
+ }
1815
+ parts.push(new Sql().push(`${lhs} ${sqlOp} `).bind(dialect.coerceValue(v)));
1816
+ }
1817
+ return parts.length === 1 ? parts[0] : joinBool(parts, "AND");
1818
+ }
1819
+ function isAggregateWrapper(value) {
1820
+ return isPlainObject(value) && Object.keys(value).some((k) => k in AGG_FN);
1821
+ }
1822
+ function joinBool(parts, op) {
1823
+ if (parts.length === 1) return parts[0];
1824
+ const joined = Sql.join(
1825
+ parts.map((p) => new Sql().push("(").append(p).push(")")),
1826
+ ` ${op} `
1827
+ );
1828
+ return new Sql().push("(").append(joined).push(")");
1829
+ }
1830
+
1831
+ // src/query/compiler.ts
1832
+ function setAssignments(values) {
1833
+ const out = /* @__PURE__ */ new Map();
1834
+ for (const [col, value] of values) out.set(col, { kind: "set", value });
1835
+ return out;
1836
+ }
1837
+ var ROOT_ALIAS = "t0";
1838
+ function newContext(schema, dialect) {
1839
+ return { schema, dialect, alias: { next: 1 } };
1840
+ }
1841
+ function compileFindMany(model, options, projection, ctx) {
1842
+ const d = ctx.dialect;
1843
+ const cols = projection.map(
1844
+ (f) => `${d.quoteRef(ROOT_ALIAS, fieldColumn(f))} AS ${d.quoteId(f.name)}`
1845
+ );
1846
+ const sql = new Sql();
1847
+ const pagination = d.paginationClause(options.take, options.skip);
1848
+ sql.push(pagination ? `SELECT ${pagination} ` : "SELECT ");
1849
+ sql.push(cols.join(", "));
1850
+ sql.push(` FROM ${d.quoteId(modelTable(model))} ${d.quoteId(ROOT_ALIAS)}`);
1851
+ appendWhere(sql, model, options.where, ctx);
1852
+ const order = compileOrderBy(model, ROOT_ALIAS, options.orderBy, d);
1853
+ if (!order.isEmpty()) sql.push(" ORDER BY ").append(order);
1854
+ return { sql, columns: projection };
1855
+ }
1856
+ function compileDistinctFindMany(model, options, projection, distinctFields, ctx) {
1857
+ const d = ctx.dialect;
1858
+ const innerOrder = options.orderBy ? compileOrderBy(model, ROOT_ALIAS, options.orderBy, d) : defaultOrder(model, d);
1859
+ const partitionBy = distinctFields.map((f) => d.quoteRef(ROOT_ALIAS, fieldColumn(f))).join(", ");
1860
+ const inner = new Sql();
1861
+ const innerCols = projection.map(
1862
+ (f) => `${d.quoteRef(ROOT_ALIAS, fieldColumn(f))} AS ${d.quoteId(f.name)}`
1863
+ );
1864
+ inner.push(`SELECT ${innerCols.join(", ")}, ROW_NUMBER() OVER (PARTITION BY ${partitionBy} ORDER BY `);
1865
+ inner.append(innerOrder);
1866
+ inner.push(`) AS ${d.quoteId("__rn")}`);
1867
+ inner.push(` FROM ${d.quoteId(modelTable(model))} ${d.quoteId(ROOT_ALIAS)}`);
1868
+ appendWhere(inner, model, options.where, ctx);
1869
+ const sql = new Sql();
1870
+ const pagination = d.paginationClause(options.take, options.skip);
1871
+ const sub = "sub";
1872
+ const outerCols = projection.map((f) => `${d.quoteRef(sub, f.name)} AS ${d.quoteId(f.name)}`).join(", ");
1873
+ sql.push(pagination ? `SELECT ${pagination} ` : "SELECT ");
1874
+ sql.push(outerCols);
1875
+ sql.push(` FROM (`).append(inner).push(`) ${d.quoteId(sub)}`);
1876
+ sql.push(` WHERE ${d.quoteRef(sub, "__rn")} = 1`);
1877
+ const outerOrder = outerOrderBy(model, sub, options.orderBy, d);
1878
+ if (!outerOrder.isEmpty()) sql.push(" ORDER BY ").append(outerOrder);
1879
+ return { sql, columns: projection };
1880
+ }
1881
+ function compileCount(model, where, ctx, pagination) {
1882
+ const d = ctx.dialect;
1883
+ const sql = new Sql();
1884
+ const clause = pagination ? d.paginationClause(pagination.take, pagination.skip) : "";
1885
+ sql.push(clause ? `SELECT ${clause} COUNT(*) AS "_count"` : `SELECT COUNT(*) AS "_count"`);
1886
+ sql.push(` FROM ${d.quoteId(modelTable(model))} ${d.quoteId(ROOT_ALIAS)}`);
1887
+ appendWhere(sql, model, where, ctx);
1888
+ return sql;
1889
+ }
1890
+ function compileAggregate(model, args, ctx) {
1891
+ const d = ctx.dialect;
1892
+ const selects = aggregateSelections(model, args, d);
1893
+ if (selects.length === 0) {
1894
+ throw new QueryValidationError("Aggregate requires at least one operator.");
1895
+ }
1896
+ const sql = new Sql();
1897
+ sql.push(`SELECT ${selects.join(", ")}`);
1898
+ sql.push(` FROM ${d.quoteId(modelTable(model))} ${d.quoteId(ROOT_ALIAS)}`);
1899
+ appendWhere(sql, model, args.where, ctx);
1900
+ return sql;
1901
+ }
1902
+ function compileGroupBy(model, args, ctx) {
1903
+ const d = ctx.dialect;
1904
+ const groupColumns = args.by.map((name) => requireScalar(model, name));
1905
+ const groupSelects = groupColumns.map(
1906
+ (f) => `${d.quoteRef(ROOT_ALIAS, fieldColumn(f))} AS ${d.quoteId(f.name)}`
1907
+ );
1908
+ const aggSelects = aggregateSelections(model, args, d);
1909
+ const sql = new Sql();
1910
+ sql.push(`SELECT ${[...groupSelects, ...aggSelects].join(", ")}`);
1911
+ sql.push(` FROM ${d.quoteId(modelTable(model))} ${d.quoteId(ROOT_ALIAS)}`);
1912
+ appendWhere(sql, model, args.where, ctx);
1913
+ sql.push(
1914
+ ` GROUP BY ${groupColumns.map((f) => d.quoteRef(ROOT_ALIAS, fieldColumn(f))).join(", ")}`
1915
+ );
1916
+ const having = compileHaving(model, ROOT_ALIAS, args.having, ctx);
1917
+ if (!having.isEmpty()) sql.push(" HAVING ").append(having);
1918
+ const order = compileOrderBy(model, ROOT_ALIAS, args.orderBy, d);
1919
+ if (!order.isEmpty()) sql.push(" ORDER BY ").append(order);
1920
+ return { sql, groupColumns };
1921
+ }
1922
+ function compileInsert(model, row, ctx, returning) {
1923
+ const d = ctx.dialect;
1924
+ const sql = new Sql();
1925
+ const columns = [...row.keys()];
1926
+ sql.push(`INSERT INTO ${d.quoteId(modelTable(model))} (`);
1927
+ sql.push(columns.map((c) => d.quoteId(c)).join(", "));
1928
+ sql.push(") VALUES (");
1929
+ sql.bindList(columns.map((c) => row.get(c)));
1930
+ sql.push(")");
1931
+ appendReturning(sql, d, returning);
1932
+ return { sql, columns: returning };
1933
+ }
1934
+ function compileUpdate(model, where, assignments, ctx, returning) {
1935
+ const d = ctx.dialect;
1936
+ if (assignments.size === 0) {
1937
+ throw new QueryValidationError("Update requires at least one field to set.");
1938
+ }
1939
+ const sql = new Sql();
1940
+ sql.push(`UPDATE ${d.quoteId(modelTable(model))} ${d.quoteId(ROOT_ALIAS)} SET `);
1941
+ const cols = [...assignments.keys()];
1942
+ cols.forEach((col, i) => {
1943
+ if (i > 0) sql.push(", ");
1944
+ const assignment = assignments.get(col);
1945
+ const quoted = d.quoteId(col);
1946
+ if (assignment.kind === "arith") {
1947
+ sql.push(`${quoted} = ${quoted} ${assignment.op} `).bind(assignment.value);
1948
+ } else {
1949
+ sql.push(`${quoted} = `).bind(assignment.value);
1950
+ }
1951
+ });
1952
+ appendWhere(sql, model, where, ctx);
1953
+ if (returning) appendReturning(sql, d, returning);
1954
+ return { sql, columns: returning ?? [] };
1955
+ }
1956
+ function compileDelete(model, where, ctx, returning) {
1957
+ const d = ctx.dialect;
1958
+ const sql = new Sql();
1959
+ sql.push(
1960
+ `DELETE FROM ${d.quoteId(modelTable(model))} ${d.quoteId(ROOT_ALIAS)}`
1961
+ );
1962
+ appendWhere(sql, model, where, ctx);
1963
+ if (returning) appendReturning(sql, d, returning);
1964
+ return { sql, columns: returning ?? [] };
1965
+ }
1966
+ function appendWhere(sql, model, where, ctx) {
1967
+ const compiled = compileWhere(model, ROOT_ALIAS, where, ctx);
1968
+ if (!compiled.isEmpty()) sql.push(" WHERE ").append(compiled);
1969
+ }
1970
+ function defaultOrder(model, d) {
1971
+ const ids = idFields(model);
1972
+ const cols = (ids.length ? ids : model.fields.filter((f) => f.kind !== "object")).map((f) => `${d.quoteRef(ROOT_ALIAS, fieldColumn(f))} ASC`);
1973
+ return Sql.raw(cols.join(", "));
1974
+ }
1975
+ function outerOrderBy(model, alias, orderBy, d) {
1976
+ if (!orderBy) return new Sql();
1977
+ const list = Array.isArray(orderBy) ? orderBy : [orderBy];
1978
+ const parts = [];
1979
+ for (const obj of list) {
1980
+ for (const [name, dir] of Object.entries(obj)) {
1981
+ if (dir !== "asc" && dir !== "desc") continue;
1982
+ if (!model.fields.some((f) => f.name === name && f.kind !== "object")) {
1983
+ continue;
1984
+ }
1985
+ parts.push(
1986
+ Sql.raw(`${d.quoteRef(alias, name)} ${dir === "desc" ? "DESC" : "ASC"}`)
1987
+ );
1988
+ }
1989
+ }
1990
+ return Sql.join(parts, ", ");
1991
+ }
1992
+ function appendReturning(sql, d, returning) {
1993
+ if (returning.length === 0) return;
1994
+ sql.push(" RETURNING ");
1995
+ sql.push(
1996
+ returning.map((f) => `${d.quoteId(fieldColumn(f))} AS ${d.quoteId(f.name)}`).join(", ")
1997
+ );
1998
+ }
1999
+ function aggregateSelections(model, args, d) {
2000
+ const out = [];
2001
+ if (args._count) {
2002
+ if (args._count === true) {
2003
+ out.push(`COUNT(*) AS "_count_all"`);
2004
+ } else {
2005
+ for (const field of trueKeys(args._count)) {
2006
+ const col = d.quoteRef(ROOT_ALIAS, fieldColumn(requireScalar(model, field)));
2007
+ out.push(`COUNT(${col}) AS ${d.quoteId(`_count_${field}`)}`);
2008
+ }
2009
+ }
2010
+ }
2011
+ pushAgg(out, "SUM", "_sum", model, args._sum, d);
2012
+ pushAgg(out, "AVG", "_avg", model, args._avg, d);
2013
+ pushAgg(out, "MIN", "_min", model, args._min, d);
2014
+ pushAgg(out, "MAX", "_max", model, args._max, d);
2015
+ return out;
2016
+ }
2017
+ function pushAgg(out, fn, prefix, model, spec, d) {
2018
+ if (!spec) return;
2019
+ for (const field of trueKeys(spec)) {
2020
+ const col = d.quoteRef(ROOT_ALIAS, fieldColumn(requireScalar(model, field)));
2021
+ out.push(`${fn}(${col}) AS ${d.quoteId(`${prefix}_${field}`)}`);
2022
+ }
2023
+ }
2024
+ function trueKeys(spec) {
2025
+ return Object.entries(spec).filter(([, v]) => v === true).map(([k]) => k);
2026
+ }
2027
+ function requireScalar(model, name) {
2028
+ const field = model.fields.find((f) => f.name === name);
2029
+ if (!field || field.kind === "object") {
2030
+ throw new QueryValidationError(
2031
+ `'${name}' is not a scalar field on model '${model.name}'.`
2032
+ );
2033
+ }
2034
+ return field;
2035
+ }
2036
+
2037
+ // src/query/coerce.ts
2038
+ function coerceFromDb(value, field) {
2039
+ if (value === null || value === void 0) return null;
2040
+ switch (field.type) {
2041
+ case "Boolean":
2042
+ return toBoolean(value);
2043
+ case "Int":
2044
+ return typeof value === "number" ? value : Number(value);
2045
+ case "BigInt":
2046
+ return typeof value === "bigint" ? value : BigInt(String(value));
2047
+ case "Float":
2048
+ case "Decimal":
2049
+ return typeof value === "number" ? value : Number(value);
2050
+ case "DateTime":
2051
+ return value instanceof Date ? value : new Date(String(value));
2052
+ case "Json":
2053
+ return parseJson(value);
2054
+ case "Bytes":
2055
+ return Buffer.isBuffer(value) ? value : Buffer.from(String(value));
2056
+ case "String":
2057
+ default:
2058
+ return typeof value === "string" ? value : String(value);
2059
+ }
2060
+ }
2061
+ function coerceRow(row, fields) {
2062
+ const out = {};
2063
+ for (const field of fields) {
2064
+ if (field.name in row) {
2065
+ out[field.name] = coerceFromDb(row[field.name], field);
2066
+ }
2067
+ }
2068
+ return out;
2069
+ }
2070
+ function toBoolean(value) {
2071
+ if (typeof value === "boolean") return value;
2072
+ if (typeof value === "number") return value !== 0;
2073
+ const s = String(value).trim().toUpperCase();
2074
+ return s === "1" || s === "T" || s === "TRUE" || s === "Y";
2075
+ }
2076
+ function parseJson(value) {
2077
+ if (typeof value !== "string") return value;
2078
+ try {
2079
+ return JSON.parse(value);
2080
+ } catch {
2081
+ return value;
2082
+ }
2083
+ }
2084
+
2085
+ // src/query/defaults.ts
2086
+ import { randomUUID } from "crypto";
2087
+ function applyCreateDefaults(model, data) {
2088
+ const out = { ...data };
2089
+ for (const field of model.fields) {
2090
+ if (field.kind === "object") continue;
2091
+ if (field.isUpdatedAt && out[field.name] === void 0) {
2092
+ out[field.name] = /* @__PURE__ */ new Date();
2093
+ continue;
2094
+ }
2095
+ if (out[field.name] !== void 0) continue;
2096
+ const def = field.default;
2097
+ if (!def) continue;
2098
+ if (def.function) {
2099
+ const computed = computeFunctionDefault(def.function.name);
2100
+ if (computed !== SKIP) out[field.name] = computed;
2101
+ continue;
2102
+ }
2103
+ if (def.literal !== void 0) {
2104
+ out[field.name] = def.literal;
2105
+ }
2106
+ }
2107
+ return out;
2108
+ }
2109
+ function applyUpdateDefaults(model, data) {
2110
+ const out = { ...data };
2111
+ for (const field of model.fields) {
2112
+ if (field.isUpdatedAt && out[field.name] === void 0) {
2113
+ out[field.name] = /* @__PURE__ */ new Date();
2114
+ }
2115
+ }
2116
+ return out;
2117
+ }
2118
+ var SKIP = /* @__PURE__ */ Symbol("skip-default");
2119
+ function computeFunctionDefault(name) {
2120
+ switch (name) {
2121
+ case "now":
2122
+ return /* @__PURE__ */ new Date();
2123
+ case "uuid":
2124
+ return randomUUID();
2125
+ case "cuid":
2126
+ return generateCuid();
2127
+ case "autoincrement":
2128
+ return SKIP;
2129
+ default:
2130
+ return SKIP;
2131
+ }
2132
+ }
2133
+ function generateCuid() {
2134
+ const time = Date.now().toString(36);
2135
+ const rand = Math.random().toString(36).slice(2, 10);
2136
+ return `c${time}${rand}`;
2137
+ }
2138
+ function isAutoincrement(field) {
2139
+ return field.default?.function?.name === "autoincrement";
2140
+ }
2141
+
2142
+ // src/query/writer.ts
2143
+ var WriteProcessor = class {
2144
+ constructor(schema, dialect, exec) {
2145
+ this.schema = schema;
2146
+ this.dialect = dialect;
2147
+ this.exec = exec;
2148
+ }
2149
+ schema;
2150
+ dialect;
2151
+ exec;
2152
+ ctx() {
2153
+ return newContext(this.schema, this.dialect);
2154
+ }
2155
+ /** Insert one row (with nested writes) and return its key values. */
2156
+ async create(model, data) {
2157
+ const { scalars, owning, children } = this.partition(model, data);
2158
+ for (const [field, op] of owning) {
2159
+ for (const [fieldName, value] of await this.resolveOwning(model, field, op)) {
2160
+ scalars.set(fieldName, value);
2161
+ }
2162
+ }
2163
+ const withDefaults = applyCreateDefaults(model, recordOf(scalars));
2164
+ const row = this.toColumnMap(model, withDefaults);
2165
+ const returning = idFields(model);
2166
+ const stmt = compileInsert(model, row, this.ctx(), returning);
2167
+ const result = await this.exec(stmt.sql);
2168
+ const keys = this.keyValues(model, result[0] ?? {}, withDefaults);
2169
+ for (const [field, op] of children) {
2170
+ await this.writeChildren(model, keys, field, op);
2171
+ }
2172
+ return keys;
2173
+ }
2174
+ /** Update rows matched by `where` (with nested writes). */
2175
+ async updateRow(model, where, data, keys) {
2176
+ const { scalars, owning, children } = this.partition(model, data, true);
2177
+ for (const [field, op] of owning) {
2178
+ for (const [fieldName, value] of await this.resolveOwningForUpdate(
2179
+ model,
2180
+ field,
2181
+ op
2182
+ )) {
2183
+ scalars.set(fieldName, value);
2184
+ }
2185
+ }
2186
+ const withDefaults = applyUpdateDefaults(model, recordOf(scalars));
2187
+ const assignments = this.toUpdateColumnMap(model, withDefaults);
2188
+ if (assignments.size > 0) {
2189
+ const stmt = compileUpdate(model, where, assignments, this.ctx());
2190
+ await this.exec(stmt.sql);
2191
+ }
2192
+ for (const [field, op] of children) {
2193
+ await this.writeChildren(model, keys, field, op);
2194
+ }
2195
+ }
2196
+ /**
2197
+ * Scalar-only bulk update (for `updateMany`). Atomic operators are supported;
2198
+ * nested relation writes are rejected, matching Prisma's `updateMany`.
2199
+ */
2200
+ async updateManyRows(model, where, data) {
2201
+ for (const key of Object.keys(data)) {
2202
+ const field = model.fields.find((f) => f.name === key);
2203
+ if (field?.kind === "object") {
2204
+ throw new QueryValidationError(
2205
+ `updateMany does not support nested relation writes ('${model.name}.${key}'). Use update() per record.`
2206
+ );
2207
+ }
2208
+ }
2209
+ const withDefaults = applyUpdateDefaults(model, data);
2210
+ const assignments = this.toUpdateColumnMap(model, withDefaults);
2211
+ if (assignments.size > 0) {
2212
+ const stmt = compileUpdate(model, where, assignments, this.ctx());
2213
+ await this.exec(stmt.sql);
2214
+ }
2215
+ }
2216
+ // ---- partition & mapping ------------------------------------------------
2217
+ partition(model, data, isUpdate = false) {
2218
+ const scalars = /* @__PURE__ */ new Map();
2219
+ const owning = [];
2220
+ const children = [];
2221
+ for (const [key, value] of Object.entries(data)) {
2222
+ if (value === void 0) continue;
2223
+ const field = model.fields.find((f) => f.name === key);
2224
+ if (!field) {
2225
+ throw new QueryValidationError(
2226
+ `Unknown field '${key}' on model '${model.name}'.`
2227
+ );
2228
+ }
2229
+ if (field.kind !== "object") {
2230
+ scalars.set(key, value);
2231
+ continue;
2232
+ }
2233
+ if (!isPlainObject(value)) {
2234
+ throw new QueryValidationError(
2235
+ `Relation '${model.name}.${key}' expects a nested write object.`
2236
+ );
2237
+ }
2238
+ const rel = resolveRelation(this.schema, model, field);
2239
+ if (rel.owns) owning.push([field, value]);
2240
+ else children.push([field, value]);
2241
+ }
2242
+ return { scalars, owning, children };
2243
+ }
2244
+ toColumnMap(model, record) {
2245
+ const out = /* @__PURE__ */ new Map();
2246
+ for (const [name, value] of Object.entries(record)) {
2247
+ const field = model.fields.find((f) => f.name === name);
2248
+ if (!field || field.kind === "object") continue;
2249
+ if (isAutoincrement(field) && (value === void 0 || value === null)) {
2250
+ continue;
2251
+ }
2252
+ out.set(fieldColumn(field), this.dialect.coerceValue(value));
2253
+ }
2254
+ return out;
2255
+ }
2256
+ /**
2257
+ * Build UPDATE assignments, interpreting atomic scalar operators:
2258
+ * `{ set }`, `{ increment }`, `{ decrement }`, `{ multiply }`, `{ divide }`.
2259
+ * A bare value is a plain `set`.
2260
+ */
2261
+ toUpdateColumnMap(model, record) {
2262
+ const out = /* @__PURE__ */ new Map();
2263
+ for (const [name, value] of Object.entries(record)) {
2264
+ const field = model.fields.find((f) => f.name === name);
2265
+ if (!field || field.kind === "object") continue;
2266
+ out.set(fieldColumn(field), this.toColumnUpdate(field, value));
2267
+ }
2268
+ return out;
2269
+ }
2270
+ toColumnUpdate(field, value) {
2271
+ const coerce = (v) => this.dialect.coerceValue(v);
2272
+ if (isPlainObject(value)) {
2273
+ if ("set" in value) return { kind: "set", value: coerce(value.set) };
2274
+ const arith = ARITH_OPS.find((op) => op.key in value);
2275
+ if (arith) {
2276
+ requireNumeric(field, arith.key);
2277
+ return { kind: "arith", op: arith.op, value: coerce(value[arith.key]) };
2278
+ }
2279
+ }
2280
+ return { kind: "set", value: coerce(value) };
2281
+ }
2282
+ keyValues(model, returnedRow, writtenData) {
2283
+ const out = {};
2284
+ for (const f of idFields(model)) {
2285
+ out[f.name] = returnedRow[f.name] ?? returnedRow[fieldColumn(f)] ?? writtenData[f.name];
2286
+ }
2287
+ return out;
2288
+ }
2289
+ // ---- owning-side relations ---------------------------------------------
2290
+ async resolveOwning(model, field, op) {
2291
+ const rel = resolveRelation(this.schema, model, field);
2292
+ if (op.connect) {
2293
+ const ref = await this.lookupReference(rel, op.connect);
2294
+ return this.mapFkByField(rel, ref);
2295
+ }
2296
+ if (op.create) {
2297
+ const childKeys = await this.create(
2298
+ rel.relatedModel,
2299
+ op.create
2300
+ );
2301
+ return this.mapFkByField(rel, childKeys);
2302
+ }
2303
+ if (op.connectOrCreate) {
2304
+ const spec = op.connectOrCreate;
2305
+ const existing = await this.tryLookupReference(rel, spec.where);
2306
+ const ref = existing ?? await this.create(rel.relatedModel, spec.create);
2307
+ return this.mapFkByField(rel, ref);
2308
+ }
2309
+ throw new QueryValidationError(
2310
+ `Unsupported nested write on '${model.name}.${field.name}'.`
2311
+ );
2312
+ }
2313
+ async resolveOwningForUpdate(model, field, op) {
2314
+ if (op.disconnect) {
2315
+ const rel = resolveRelation(this.schema, model, field);
2316
+ return rel.fromFields.map((name) => [name, null]);
2317
+ }
2318
+ return this.resolveOwning(model, field, op);
2319
+ }
2320
+ /** [localFieldName, value] pairs from the referenced model's key values. */
2321
+ mapFkByField(rel, refValues) {
2322
+ return rel.fromFields.map((fromField, i) => {
2323
+ const toField = rel.toFields[i];
2324
+ return [fromField, this.dialect.coerceValue(refValues[toField] ?? null)];
2325
+ });
2326
+ }
2327
+ async lookupReference(rel, where) {
2328
+ const found = await this.tryLookupReference(rel, where);
2329
+ if (!found) throw new RecordNotFoundError(rel.relatedModel.name);
2330
+ return found;
2331
+ }
2332
+ async tryLookupReference(rel, where) {
2333
+ const fields = rel.toFields.map((n) => requireField(rel.relatedModel, n));
2334
+ const stmt = this.selectKeys(rel.relatedModel, fields, where);
2335
+ const rows = await this.exec(stmt);
2336
+ return rows[0] ?? null;
2337
+ }
2338
+ selectKeys(model, fields, where) {
2339
+ const d = this.dialect;
2340
+ const alias = "t0";
2341
+ const sql = new Sql();
2342
+ sql.push("SELECT FIRST 1 ");
2343
+ sql.push(
2344
+ fields.map((f) => `${d.quoteRef(alias, fieldColumn(f))} AS ${d.quoteId(f.name)}`).join(", ")
2345
+ );
2346
+ sql.push(` FROM ${d.quoteId(modelTable(model))} ${d.quoteId(alias)}`);
2347
+ const cond = compileWhere(model, alias, where, this.ctx());
2348
+ if (!cond.isEmpty()) sql.push(" WHERE ").append(cond);
2349
+ return sql;
2350
+ }
2351
+ // ---- child-side relations ----------------------------------------------
2352
+ async writeChildren(parentModel, parentKeys, field, op) {
2353
+ const rel = resolveRelation(this.schema, parentModel, field);
2354
+ const fkColumns = rel.toColumns.map((toCol, i) => {
2355
+ const fromField = rel.fromFields[i];
2356
+ return [toCol, this.dialect.coerceValue(parentKeys[fromField] ?? null)];
2357
+ });
2358
+ for (const childData of toArray(op.create)) {
2359
+ await this.create(rel.relatedModel, { ...childData, ...fkRecord(rel, parentKeys) });
2360
+ }
2361
+ for (const where of toArray(op.connect)) {
2362
+ await this.setChildForeignKey(rel, where, fkColumns);
2363
+ }
2364
+ if (op.set !== void 0) {
2365
+ await this.clearChildren(rel, parentKeys);
2366
+ for (const where of toArray(op.set)) {
2367
+ await this.setChildForeignKey(rel, where, fkColumns);
2368
+ }
2369
+ }
2370
+ for (const where of toArray(op.disconnect)) {
2371
+ await this.setChildForeignKey(
2372
+ rel,
2373
+ where,
2374
+ rel.toColumns.map((c) => [c, null])
2375
+ );
2376
+ }
2377
+ for (const where of toArray(op.delete)) {
2378
+ const stmt = compileDelete(rel.relatedModel, where, this.ctx());
2379
+ await this.exec(stmt.sql);
2380
+ }
2381
+ }
2382
+ async setChildForeignKey(rel, where, fkColumns) {
2383
+ const assignments = setAssignments(new Map(fkColumns));
2384
+ const stmt = compileUpdate(rel.relatedModel, where, assignments, this.ctx());
2385
+ await this.exec(stmt.sql);
2386
+ }
2387
+ async clearChildren(rel, parentKeys) {
2388
+ const matchWhere = {};
2389
+ rel.toFields.forEach((toField, i) => {
2390
+ const fromField = rel.fromFields[i];
2391
+ matchWhere[toField] = parentKeys[fromField];
2392
+ });
2393
+ const assignments = setAssignments(
2394
+ new Map(
2395
+ rel.toColumns.map((c) => [c, null])
2396
+ )
2397
+ );
2398
+ const stmt = compileUpdate(rel.relatedModel, matchWhere, assignments, this.ctx());
2399
+ await this.exec(stmt.sql);
2400
+ }
2401
+ };
2402
+ var ARITH_OPS = [
2403
+ { key: "increment", op: "+" },
2404
+ { key: "decrement", op: "-" },
2405
+ { key: "multiply", op: "*" },
2406
+ { key: "divide", op: "/" }
2407
+ ];
2408
+ var NUMERIC_TYPES = /* @__PURE__ */ new Set(["Int", "BigInt", "Float", "Decimal"]);
2409
+ function requireNumeric(field, op) {
2410
+ if (!NUMERIC_TYPES.has(field.type)) {
2411
+ throw new QueryValidationError(
2412
+ `Operator '${op}' is only valid on numeric fields, not '${field.name}' (${field.type}).`
2413
+ );
2414
+ }
2415
+ }
2416
+ function requireField(model, name) {
2417
+ const f = model.fields.find((x) => x.name === name);
2418
+ if (!f) {
2419
+ throw new QueryValidationError(`Field '${name}' not found on '${model.name}'.`);
2420
+ }
2421
+ return f;
2422
+ }
2423
+ function fkRecord(rel, parentKeys) {
2424
+ const out = {};
2425
+ rel.toFields.forEach((toField, i) => {
2426
+ const fromField = rel.fromFields[i];
2427
+ out[toField] = parentKeys[fromField];
2428
+ });
2429
+ return out;
2430
+ }
2431
+ function recordOf(scalars) {
2432
+ const out = {};
2433
+ for (const [k, v] of scalars) out[k] = v;
2434
+ return out;
2435
+ }
2436
+ function toArray(value) {
2437
+ if (value === void 0 || value === null) return [];
2438
+ return Array.isArray(value) ? value : [value];
2439
+ }
2440
+
2441
+ // src/query/engine.ts
2442
+ function readShape(args) {
2443
+ return { select: args.select, include: args.include, omit: args.omit };
2444
+ }
2445
+ var QueryEngine = class {
2446
+ constructor(schema, dialect, driver) {
2447
+ this.schema = schema;
2448
+ this.dialect = dialect;
2449
+ this.driver = driver;
2450
+ }
2451
+ schema;
2452
+ dialect;
2453
+ driver;
2454
+ model(name) {
2455
+ const model = findModel(this.schema, name);
2456
+ if (!model) throw new QueryValidationError(`Unknown model '${name}'.`);
2457
+ return model;
2458
+ }
2459
+ run(fn, options) {
2460
+ return this.driver.transaction(fn, options);
2461
+ }
2462
+ execOn(tx) {
2463
+ return (sql) => tx.query(sql.text, sql.params);
2464
+ }
2465
+ // ---- reads --------------------------------------------------------------
2466
+ findMany(name, args = {}) {
2467
+ const model = this.model(name);
2468
+ return this.run((tx) => this.readMany(model, args, this.execOn(tx)));
2469
+ }
2470
+ async findFirst(name, args = {}) {
2471
+ const rows = await this.findMany(name, { ...args, take: 1 });
2472
+ return rows[0] ?? null;
2473
+ }
2474
+ async findFirstOrThrow(name, args = {}) {
2475
+ const row = await this.findFirst(name, args);
2476
+ if (!row) throw new RecordNotFoundError(name);
2477
+ return row;
2478
+ }
2479
+ async findUnique(name, args) {
2480
+ const model = this.model(name);
2481
+ this.assertUniqueWhere(model, args.where);
2482
+ const rows = await this.run(
2483
+ (tx) => this.readMany(
2484
+ model,
2485
+ { where: args.where, select: args.select, include: args.include, take: 1 },
2486
+ this.execOn(tx)
2487
+ )
2488
+ );
2489
+ return rows[0] ?? null;
2490
+ }
2491
+ async findUniqueOrThrow(name, args) {
2492
+ const row = await this.findUnique(name, args);
2493
+ if (!row) throw new RecordNotFoundError(name);
2494
+ return row;
2495
+ }
2496
+ // ---- aggregations -------------------------------------------------------
2497
+ count(name, args = {}) {
2498
+ const model = this.model(name);
2499
+ return this.run(async (tx) => {
2500
+ const stmt = compileCount(model, args.where, newContext(this.schema, this.dialect), {
2501
+ take: args.take,
2502
+ skip: args.skip
2503
+ });
2504
+ const rows = await tx.query(stmt.text, stmt.params);
2505
+ return Number(rows[0]?._count ?? 0);
2506
+ });
2507
+ }
2508
+ aggregate(name, args = {}) {
2509
+ const model = this.model(name);
2510
+ return this.run(async (tx) => {
2511
+ const stmt = compileAggregate(model, args, newContext(this.schema, this.dialect));
2512
+ const rows = await tx.query(stmt.text, stmt.params);
2513
+ return reshapeAggregate(rows[0] ?? {});
2514
+ });
2515
+ }
2516
+ groupBy(name, args) {
2517
+ const model = this.model(name);
2518
+ return this.run(async (tx) => {
2519
+ const { sql } = compileGroupBy(model, args, newContext(this.schema, this.dialect));
2520
+ const rows = await tx.query(sql.text, sql.params);
2521
+ return rows.map((r) => reshapeGroupRow(model, args.by, r));
2522
+ });
2523
+ }
2524
+ // ---- writes -------------------------------------------------------------
2525
+ create(name, args) {
2526
+ const model = this.model(name);
2527
+ return this.run(async (tx) => {
2528
+ const writer = new WriteProcessor(this.schema, this.dialect, this.execOn(tx));
2529
+ const keys = await writer.create(model, args.data);
2530
+ return this.readBack(model, keys, readShape(args), this.execOn(tx));
2531
+ });
2532
+ }
2533
+ createMany(name, args) {
2534
+ const model = this.model(name);
2535
+ return this.run(async (tx) => {
2536
+ const writer = new WriteProcessor(this.schema, this.dialect, this.execOn(tx));
2537
+ let count = 0;
2538
+ for (const data of args.data) {
2539
+ await writer.create(model, data);
2540
+ count++;
2541
+ }
2542
+ return { count };
2543
+ });
2544
+ }
2545
+ /** Like createMany, but returns the created records (no nested relations). */
2546
+ createManyAndReturn(name, args) {
2547
+ const model = this.model(name);
2548
+ return this.run(async (tx) => {
2549
+ const exec = this.execOn(tx);
2550
+ const writer = new WriteProcessor(this.schema, this.dialect, exec);
2551
+ const out = [];
2552
+ const shape = { select: args.select, omit: args.omit };
2553
+ for (const data of args.data) {
2554
+ const keys = await writer.create(model, data);
2555
+ out.push(await this.readBack(model, keys, shape, exec));
2556
+ }
2557
+ return out;
2558
+ });
2559
+ }
2560
+ update(name, args) {
2561
+ const model = this.model(name);
2562
+ this.assertUniqueWhere(model, args.where);
2563
+ return this.run(async (tx) => {
2564
+ const exec = this.execOn(tx);
2565
+ const target = await this.requireOne(model, args.where, exec);
2566
+ const keys = this.pickKeys(model, target);
2567
+ const writer = new WriteProcessor(this.schema, this.dialect, exec);
2568
+ await writer.updateRow(model, args.where, args.data, keys);
2569
+ return this.readBack(model, keys, readShape(args), exec);
2570
+ });
2571
+ }
2572
+ updateMany(name, args) {
2573
+ const model = this.model(name);
2574
+ return this.run(async (tx) => {
2575
+ const exec = this.execOn(tx);
2576
+ const count = await this.countMatching(model, args.where, exec);
2577
+ const writer = new WriteProcessor(this.schema, this.dialect, exec);
2578
+ await writer.updateManyRows(model, args.where, args.data);
2579
+ return { count };
2580
+ });
2581
+ }
2582
+ upsert(name, args) {
2583
+ const model = this.model(name);
2584
+ this.assertUniqueWhere(model, args.where);
2585
+ return this.run(async (tx) => {
2586
+ const exec = this.execOn(tx);
2587
+ const existing = await this.findOneRaw(model, args.where, exec);
2588
+ const writer = new WriteProcessor(this.schema, this.dialect, exec);
2589
+ if (existing) {
2590
+ const keys2 = this.pickKeys(model, existing);
2591
+ await writer.updateRow(model, args.where, args.update, keys2);
2592
+ return this.readBack(model, keys2, readShape(args), exec);
2593
+ }
2594
+ const keys = await writer.create(model, args.create);
2595
+ return this.readBack(model, keys, readShape(args), exec);
2596
+ });
2597
+ }
2598
+ delete(name, args) {
2599
+ const model = this.model(name);
2600
+ this.assertUniqueWhere(model, args.where);
2601
+ return this.run(async (tx) => {
2602
+ const exec = this.execOn(tx);
2603
+ const row = await this.readBackOne(model, args.where, readShape(args), exec);
2604
+ if (!row) throw new RecordNotFoundError(name);
2605
+ const stmt = compileDelete(model, args.where, newContext(this.schema, this.dialect));
2606
+ await exec(stmt.sql);
2607
+ return row;
2608
+ });
2609
+ }
2610
+ deleteMany(name, args = {}) {
2611
+ const model = this.model(name);
2612
+ return this.run(async (tx) => {
2613
+ const exec = this.execOn(tx);
2614
+ const count = await this.countMatching(model, args.where, exec);
2615
+ const stmt = compileDelete(model, args.where, newContext(this.schema, this.dialect));
2616
+ await exec(stmt.sql);
2617
+ return { count };
2618
+ });
2619
+ }
2620
+ // ---- read pipeline ------------------------------------------------------
2621
+ async readMany(model, args, exec, forceKeep = []) {
2622
+ const relations = this.readRelations(model, args.select, args.include);
2623
+ const countFields = this.readCountRelations(model, args.select, args.include);
2624
+ const selectedScalars = applyOmit(
2625
+ model,
2626
+ selectedScalarNames(model, args.select),
2627
+ args.omit
2628
+ );
2629
+ const distinctFields = normalizeDistinct(model, args.distinct);
2630
+ const useWindowDistinct = distinctFields.length > 0 && this.dialect.supportsWindowFunctions;
2631
+ const memoryPaginate = distinctFields.length > 0 && !useWindowDistinct;
2632
+ const { where, orderBy } = applyCursor(model, args, distinctFields);
2633
+ const relationKeyFields = uniq(
2634
+ [...relations.map((r) => r.field), ...countFields].flatMap(
2635
+ (field) => resolveRelation(this.schema, model, field).fromFields
2636
+ )
2637
+ );
2638
+ const internalKeys = uniq([
2639
+ ...relationKeyFields,
2640
+ ...distinctFields
2641
+ // fetched for de-dup; stripped below if not selected
2642
+ ]);
2643
+ const projectionNames = uniq([
2644
+ ...selectedScalars ?? scalarFields(model).map((f) => f.name),
2645
+ ...internalKeys,
2646
+ ...forceKeep
2647
+ ]);
2648
+ const projection = projectionNames.map((n) => fieldByName(model, n));
2649
+ const findOptions = {
2650
+ where,
2651
+ orderBy,
2652
+ take: memoryPaginate ? void 0 : args.take,
2653
+ skip: memoryPaginate ? void 0 : args.skip
2654
+ };
2655
+ const stmt = useWindowDistinct ? compileDistinctFindMany(
2656
+ model,
2657
+ findOptions,
2658
+ projection,
2659
+ distinctFields.map((n) => fieldByName(model, n)),
2660
+ newContext(this.schema, this.dialect)
2661
+ ) : compileFindMany(
2662
+ model,
2663
+ findOptions,
2664
+ projection,
2665
+ newContext(this.schema, this.dialect)
2666
+ );
2667
+ const rawRows = await exec(stmt.sql);
2668
+ let rows = rawRows.map((r) => coerceRow(r, projection));
2669
+ if (memoryPaginate) {
2670
+ rows = dedupeBy(rows, distinctFields);
2671
+ rows = applyPagination(rows, args.take, args.skip);
2672
+ }
2673
+ for (const relation of relations) {
2674
+ await this.loadRelation(model, rows, relation, exec);
2675
+ }
2676
+ if (countFields.length > 0) {
2677
+ await this.loadCounts(model, rows, countFields, exec);
2678
+ }
2679
+ if (selectedScalars) {
2680
+ const visible = /* @__PURE__ */ new Set([...selectedScalars, ...forceKeep]);
2681
+ for (const row of rows) {
2682
+ for (const name of internalKeys) {
2683
+ if (!visible.has(name)) delete row[name];
2684
+ }
2685
+ }
2686
+ }
2687
+ return rows;
2688
+ }
2689
+ async loadRelation(parentModel, parentRows, relation, exec) {
2690
+ if (parentRows.length === 0) return;
2691
+ const rel = resolveRelation(this.schema, parentModel, relation.field);
2692
+ const fieldName = relation.field.name;
2693
+ const { fromFields, toFields } = rel;
2694
+ const parentTuples = uniqueTuples(parentRows, fromFields);
2695
+ const grouped = /* @__PURE__ */ new Map();
2696
+ if (parentTuples.length > 0) {
2697
+ const childWhere = relationKeyWhere(
2698
+ toFields,
2699
+ parentTuples,
2700
+ relation.args.where
2701
+ );
2702
+ const childRows = await this.readMany(
2703
+ rel.relatedModel,
2704
+ {
2705
+ where: childWhere,
2706
+ orderBy: relation.args.orderBy,
2707
+ select: relation.args.select,
2708
+ include: relation.args.include,
2709
+ // take/skip on to-many are applied per parent below; skip here.
2710
+ ...rel.isList ? {} : { take: relation.args.take, skip: relation.args.skip }
2711
+ },
2712
+ exec,
2713
+ toFields
2714
+ );
2715
+ for (const child of childRows) {
2716
+ const k = tupleKey(child, toFields);
2717
+ const list = grouped.get(k) ?? [];
2718
+ list.push(child);
2719
+ grouped.set(k, list);
2720
+ }
2721
+ }
2722
+ const visible = selectedScalarNames(rel.relatedModel, relation.args.select);
2723
+ const stripFields = visible ? toFields.filter((f) => !visible.has(f)) : [];
2724
+ for (const parent of parentRows) {
2725
+ const k = tupleKey(parent, fromFields);
2726
+ let matches = grouped.get(k) ?? [];
2727
+ if (rel.isList && (relation.args.take != null || relation.args.skip != null)) {
2728
+ matches = applyPagination(matches, relation.args.take, relation.args.skip);
2729
+ }
2730
+ const attached = stripFields.length ? matches.map((m) => omitAll(m, stripFields)) : matches;
2731
+ parent[fieldName] = rel.isList ? attached : attached[0] ?? null;
2732
+ }
2733
+ }
2734
+ readRelations(model, select, include) {
2735
+ const out = [];
2736
+ const add = (key, value) => {
2737
+ if (!value) return;
2738
+ const field = model.fields.find((f) => f.name === key && f.kind === "object");
2739
+ if (!field) return;
2740
+ out.push({ field, args: typeof value === "object" ? value : {} });
2741
+ };
2742
+ if (include) for (const [k, v] of Object.entries(include)) add(k, v);
2743
+ if (select) {
2744
+ for (const [k, v] of Object.entries(select)) {
2745
+ const field = model.fields.find((f) => f.name === k);
2746
+ if (field?.kind === "object") add(k, v);
2747
+ }
2748
+ }
2749
+ return out;
2750
+ }
2751
+ /**
2752
+ * Resolve which to-many relations to count for `_count` in select/include.
2753
+ * `_count: true` counts every list relation; `_count: { select: { rel } }`
2754
+ * counts the named ones.
2755
+ */
2756
+ readCountRelations(model, select, include) {
2757
+ const spec = include?._count ?? select?._count;
2758
+ if (!spec) return [];
2759
+ const listRelations = model.fields.filter(
2760
+ (f) => f.kind === "object" && f.isList
2761
+ );
2762
+ if (spec === true) return listRelations;
2763
+ const selected = spec.select;
2764
+ if (!selected) return listRelations;
2765
+ return listRelations.filter((f) => selected[f.name]);
2766
+ }
2767
+ /** Attach a `_count` object to each row with per-relation child counts. */
2768
+ async loadCounts(model, rows, countFields, exec) {
2769
+ for (const row of rows) row._count = {};
2770
+ for (const field of countFields) {
2771
+ const rel = resolveRelation(this.schema, model, field);
2772
+ const tuples = uniqueTuples(rows, rel.fromFields);
2773
+ const grouped = /* @__PURE__ */ new Map();
2774
+ if (tuples.length > 0) {
2775
+ const where = relationKeyWhere(rel.toFields, tuples, void 0);
2776
+ const { sql } = compileGroupBy(
2777
+ rel.relatedModel,
2778
+ { by: rel.toFields, _count: true, where },
2779
+ newContext(this.schema, this.dialect)
2780
+ );
2781
+ const countRows = await exec(sql);
2782
+ for (const cr of countRows) {
2783
+ grouped.set(tupleKey(cr, rel.toFields), Number(cr._count_all ?? 0));
2784
+ }
2785
+ }
2786
+ for (const row of rows) {
2787
+ const count = grouped.get(tupleKey(row, rel.fromFields)) ?? 0;
2788
+ row._count[field.name] = count;
2789
+ }
2790
+ }
2791
+ }
2792
+ // ---- read-back & helpers ------------------------------------------------
2793
+ async readBack(model, keys, shape, exec) {
2794
+ const where = keysToWhere(model, keys);
2795
+ const row = await this.readBackOne(model, where, shape, exec);
2796
+ if (!row) throw new RecordNotFoundError(model.name);
2797
+ return row;
2798
+ }
2799
+ async readBackOne(model, where, shape, exec) {
2800
+ const rows = await this.readMany(
2801
+ model,
2802
+ { where, select: shape.select, include: shape.include, omit: shape.omit, take: 1 },
2803
+ exec
2804
+ );
2805
+ return rows[0] ?? null;
2806
+ }
2807
+ async requireOne(model, where, exec) {
2808
+ const row = await this.findOneRaw(model, where, exec);
2809
+ if (!row) throw new RecordNotFoundError(model.name);
2810
+ return row;
2811
+ }
2812
+ async findOneRaw(model, where, exec) {
2813
+ const rows = await this.readMany(model, { where, take: 1 }, exec);
2814
+ return rows[0] ?? null;
2815
+ }
2816
+ async countMatching(model, where, exec) {
2817
+ const stmt = compileCount(model, where, newContext(this.schema, this.dialect));
2818
+ const rows = await exec(stmt);
2819
+ return Number(rows[0]?._count ?? 0);
2820
+ }
2821
+ pickKeys(model, row) {
2822
+ const out = {};
2823
+ for (const f of idFields(model)) out[f.name] = row[f.name];
2824
+ return out;
2825
+ }
2826
+ assertUniqueWhere(model, where) {
2827
+ if (!where || Object.keys(where).length === 0) {
2828
+ throw new QueryValidationError(
2829
+ `A unique 'where' is required for this operation on '${model.name}'.`
2830
+ );
2831
+ }
2832
+ }
2833
+ };
2834
+ function fieldByName(model, name) {
2835
+ const f = model.fields.find((x) => x.name === name);
2836
+ if (!f) throw new QueryValidationError(`Field '${name}' not found on '${model.name}'.`);
2837
+ return f;
2838
+ }
2839
+ function selectedScalarNames(model, select) {
2840
+ if (!select) return null;
2841
+ const names = /* @__PURE__ */ new Set();
2842
+ for (const [k, v] of Object.entries(select)) {
2843
+ if (!v) continue;
2844
+ const field = model.fields.find((f) => f.name === k);
2845
+ if (field && field.kind !== "object") names.add(k);
2846
+ }
2847
+ return names;
2848
+ }
2849
+ function applyOmit(model, selected, omit) {
2850
+ if (selected || !omit) return selected;
2851
+ const omitted = new Set(
2852
+ Object.entries(omit).filter(([, v]) => v).map(([k]) => k)
2853
+ );
2854
+ if (omitted.size === 0) return null;
2855
+ return new Set(
2856
+ scalarFields(model).map((f) => f.name).filter((n) => !omitted.has(n))
2857
+ );
2858
+ }
2859
+ function keysToWhere(model, keys) {
2860
+ const where = {};
2861
+ for (const f of idFields(model)) where[f.name] = keys[f.name];
2862
+ return where;
2863
+ }
2864
+ function keyOf(value) {
2865
+ return value instanceof Date ? value.toISOString() : String(value);
2866
+ }
2867
+ function tupleKey(row, fields) {
2868
+ return fields.map((f) => keyOf(row[f])).join("\0");
2869
+ }
2870
+ function uniqueTuples(rows, fields) {
2871
+ const seen = /* @__PURE__ */ new Set();
2872
+ const out = [];
2873
+ for (const row of rows) {
2874
+ const tuple = fields.map((f) => row[f]);
2875
+ if (tuple.some((v) => v === null || v === void 0)) continue;
2876
+ const key = tuple.map(keyOf).join("\0");
2877
+ if (seen.has(key)) continue;
2878
+ seen.add(key);
2879
+ out.push(tuple);
2880
+ }
2881
+ return out;
2882
+ }
2883
+ function relationKeyWhere(toFields, parentTuples, baseWhere) {
2884
+ const base = baseWhere ?? {};
2885
+ if (toFields.length === 1) {
2886
+ const field = toFields[0];
2887
+ return { ...base, [field]: { in: parentTuples.map((t) => t[0]) } };
2888
+ }
2889
+ const or = parentTuples.map((tuple) => {
2890
+ const cond = {};
2891
+ toFields.forEach((f, i) => {
2892
+ cond[f] = tuple[i];
2893
+ });
2894
+ return cond;
2895
+ });
2896
+ if (Object.keys(base).length === 0) return { OR: or };
2897
+ return { AND: [base, { OR: or }] };
2898
+ }
2899
+ function omitAll(obj, keys) {
2900
+ const out = { ...obj };
2901
+ for (const k of keys) delete out[k];
2902
+ return out;
2903
+ }
2904
+ function applyPagination(rows, take, skip) {
2905
+ const start = skip ?? 0;
2906
+ const end = take != null ? start + take : void 0;
2907
+ return rows.slice(start, end);
2908
+ }
2909
+ function normalizeDistinct(model, distinct) {
2910
+ if (!distinct || distinct.length === 0) return [];
2911
+ for (const name of distinct) {
2912
+ const field = model.fields.find((f) => f.name === name);
2913
+ if (!field || field.kind === "object") {
2914
+ throw new QueryValidationError(
2915
+ `Cannot apply distinct on '${model.name}.${name}' (not a scalar field).`
2916
+ );
2917
+ }
2918
+ }
2919
+ return uniq(distinct);
2920
+ }
2921
+ function dedupeBy(rows, fields) {
2922
+ const seen = /* @__PURE__ */ new Set();
2923
+ const out = [];
2924
+ for (const row of rows) {
2925
+ const key = tupleKey(row, fields);
2926
+ if (seen.has(key)) continue;
2927
+ seen.add(key);
2928
+ out.push(row);
2929
+ }
2930
+ return out;
2931
+ }
2932
+ function applyCursor(model, args, _distinctFields) {
2933
+ if (!args.cursor || Object.keys(args.cursor).length === 0) {
2934
+ return { where: args.where, orderBy: args.orderBy };
2935
+ }
2936
+ const entries = Object.entries(args.cursor).filter(([, v]) => v !== void 0);
2937
+ if (entries.length === 0) return { where: args.where, orderBy: args.orderBy };
2938
+ const fields = entries.map(([name, value]) => {
2939
+ if (!model.fields.some((f) => f.name === name && f.kind !== "object")) {
2940
+ throw new QueryValidationError(
2941
+ `Cursor field '${model.name}.${name}' must be a scalar field.`
2942
+ );
2943
+ }
2944
+ return { name, value, direction: directionForField(args.orderBy, name) ?? "asc" };
2945
+ });
2946
+ const cursorCond = keysetCondition(fields);
2947
+ const where = args.where ? { AND: [args.where, cursorCond] } : cursorCond;
2948
+ const orderBy = args.orderBy ?? fields.map((f) => ({ [f.name]: f.direction }));
2949
+ return { where, orderBy };
2950
+ }
2951
+ function keysetCondition(fields) {
2952
+ const clauses = [];
2953
+ for (let i = 0; i < fields.length; i++) {
2954
+ const clause = {};
2955
+ for (let j = 0; j < i; j++) {
2956
+ clause[fields[j].name] = fields[j].value;
2957
+ }
2958
+ const last = i === fields.length - 1;
2959
+ const f = fields[i];
2960
+ const op = comparator(f.direction, last);
2961
+ clause[f.name] = { [op]: f.value };
2962
+ clauses.push(clause);
2963
+ }
2964
+ return clauses.length === 1 ? clauses[0] : { OR: clauses };
2965
+ }
2966
+ function comparator(direction, inclusive) {
2967
+ if (direction === "desc") return inclusive ? "lte" : "lt";
2968
+ return inclusive ? "gte" : "gt";
2969
+ }
2970
+ function directionForField(orderBy, field) {
2971
+ if (!orderBy) return void 0;
2972
+ const list = Array.isArray(orderBy) ? orderBy : [orderBy];
2973
+ for (const obj of list) {
2974
+ const dir = obj[field];
2975
+ if (dir === "asc" || dir === "desc") return dir;
2976
+ }
2977
+ return void 0;
2978
+ }
2979
+ function reshapeAggregate(row) {
2980
+ const out = {};
2981
+ for (const [key, value] of Object.entries(row)) {
2982
+ if (key === "_count_all") {
2983
+ out._count ??= {};
2984
+ out._count._all = Number(value);
2985
+ continue;
2986
+ }
2987
+ const m = /^(_count|_sum|_avg|_min|_max)_(.+)$/.exec(key);
2988
+ if (m) {
2989
+ const group = out[m[1]] ??= {};
2990
+ group[m[2]] = value === null ? null : Number(value);
2991
+ }
2992
+ }
2993
+ return out;
2994
+ }
2995
+ function reshapeGroupRow(model, by, row) {
2996
+ const out = {};
2997
+ const groupFields = scalarFields(model).filter((f) => by.includes(f.name));
2998
+ for (const f of groupFields) {
2999
+ out[f.name] = f.name in row ? coerceField(row[f.name], f) : row[f.name];
3000
+ }
3001
+ const aggregates = reshapeAggregate(row);
3002
+ return { ...out, ...aggregates };
3003
+ }
3004
+ function coerceField(value, field) {
3005
+ return coerceRow({ [field.name]: value }, [field])[field.name];
3006
+ }
3007
+
3008
+ // src/client/runtime.ts
3009
+ var RECORD_OPS = /* @__PURE__ */ new Set([
3010
+ "findMany",
3011
+ "findFirst",
3012
+ "findFirstOrThrow",
3013
+ "findUnique",
3014
+ "findUniqueOrThrow",
3015
+ "create",
3016
+ "createManyAndReturn",
3017
+ "update",
3018
+ "upsert",
3019
+ "delete"
3020
+ ]);
3021
+ var ALL_OPS = [
3022
+ "findMany",
3023
+ "findFirst",
3024
+ "findFirstOrThrow",
3025
+ "findUnique",
3026
+ "findUniqueOrThrow",
3027
+ "create",
3028
+ "createMany",
3029
+ "createManyAndReturn",
3030
+ "update",
3031
+ "updateMany",
3032
+ "upsert",
3033
+ "delete",
3034
+ "deleteMany",
3035
+ "count",
3036
+ "aggregate",
3037
+ "groupBy"
3038
+ ];
3039
+ var FLUENT_OPS = /* @__PURE__ */ new Set([
3040
+ "findUnique",
3041
+ "findUniqueOrThrow",
3042
+ "findFirst",
3043
+ "findFirstOrThrow"
3044
+ ]);
3045
+ function buildDelegate(ctx, modelName) {
3046
+ const model = findModel(ctx.schema, modelName);
3047
+ const delegate = {};
3048
+ for (const operation of ALL_OPS) {
3049
+ delegate[operation] = (args = {}) => runOperation(ctx, modelName, operation, args, delegate);
3050
+ }
3051
+ for (const ext of ctx.extensions) {
3052
+ const methods = {
3053
+ ...ext.model?.$allModels ?? {},
3054
+ ...ext.model?.[modelName] ?? {}
3055
+ };
3056
+ for (const [name, fn] of Object.entries(methods)) {
3057
+ delegate[name] = (...args) => fn.apply(delegate, args);
3058
+ }
3059
+ }
3060
+ void model;
3061
+ return delegate;
3062
+ }
3063
+ function runOperation(ctx, modelName, operation, args, delegate) {
3064
+ const base = async (a) => {
3065
+ const expanded = expandComputedSelect(ctx, modelName, operation, a);
3066
+ const raw = await runMiddleware(ctx, modelName, operation, expanded.args);
3067
+ return applyResultExtensions(ctx, modelName, operation, raw, expanded.computeOnly);
3068
+ };
3069
+ const hooks = gatherQueryHooks(ctx, modelName, operation);
3070
+ const run = composeHooks(hooks, modelName, operation, base);
3071
+ if (FLUENT_OPS.has(operation)) {
3072
+ return makeFluent(ctx, modelName, args, () => run(args));
3073
+ }
3074
+ void delegate;
3075
+ return run(args);
3076
+ }
3077
+ function gatherQueryHooks(ctx, model, operation) {
3078
+ const hooks = [];
3079
+ for (const ext of ctx.extensions) {
3080
+ const q = ext.query;
3081
+ if (!q) continue;
3082
+ pushHook(hooks, q[model], operation);
3083
+ pushHook(hooks, q.$allModels, operation);
3084
+ }
3085
+ return hooks;
3086
+ }
3087
+ function pushHook(hooks, map, operation) {
3088
+ if (!map) return;
3089
+ if (map[operation]) hooks.push(map[operation]);
3090
+ if (map.$allOperations) hooks.push(map.$allOperations);
3091
+ }
3092
+ function composeHooks(hooks, model, operation, base) {
3093
+ let next = base;
3094
+ for (const hook of hooks) {
3095
+ const inner = next;
3096
+ next = (args) => hook({ model, operation, args, query: (a) => inner(a) });
3097
+ }
3098
+ return next;
3099
+ }
3100
+ function runMiddleware(ctx, model, action, args) {
3101
+ const engine = ctx.engine;
3102
+ const engineCall = (a) => engine[a.action](model, a.args);
3103
+ let next = engineCall;
3104
+ for (const mw of ctx.middlewares) {
3105
+ const inner = next;
3106
+ next = (params) => mw(params, inner);
3107
+ }
3108
+ return next({ model, action, args });
3109
+ }
3110
+ function resultComputers(ctx, model) {
3111
+ const out = [];
3112
+ for (const ext of ctx.extensions) {
3113
+ const r = ext.result?.[model];
3114
+ if (!r) continue;
3115
+ for (const [field, def] of Object.entries(r)) {
3116
+ out.push({ field, compute: def.compute });
3117
+ }
3118
+ }
3119
+ return out;
3120
+ }
3121
+ function applyResultExtensions(ctx, model, operation, value, computeFields) {
3122
+ if (!RECORD_OPS.has(operation)) return value;
3123
+ const computers = resultComputers(ctx, model).filter(
3124
+ (c) => !computeFields || computeFields.has(c.field)
3125
+ );
3126
+ if (computers.length === 0) return value;
3127
+ const compute = (rec) => {
3128
+ if (!rec || typeof rec !== "object") return rec;
3129
+ const record = rec;
3130
+ for (const c of computers) {
3131
+ try {
3132
+ record[c.field] = c.compute(record);
3133
+ } catch {
3134
+ }
3135
+ }
3136
+ return record;
3137
+ };
3138
+ return Array.isArray(value) ? value.map(compute) : compute(value);
3139
+ }
3140
+ function expandComputedSelect(ctx, model, operation, args) {
3141
+ const computers = resultComputers(ctx, model);
3142
+ if (computers.length === 0) return { args, computeOnly: null };
3143
+ const select = args.select;
3144
+ if (!select) return { args, computeOnly: null };
3145
+ const byField = new Map(
3146
+ ctx.extensions.flatMap((e) => Object.entries(e.result?.[model] ?? {})).map(([f, def]) => [f, def])
3147
+ );
3148
+ const computeOnly = /* @__PURE__ */ new Set();
3149
+ const newSelect = { ...select };
3150
+ for (const key of Object.keys(select)) {
3151
+ const def = byField.get(key);
3152
+ if (!def) continue;
3153
+ computeOnly.add(key);
3154
+ delete newSelect[key];
3155
+ for (const need of Object.keys(def.needs ?? {})) newSelect[need] = true;
3156
+ }
3157
+ if (computeOnly.size === 0) return { args, computeOnly: null };
3158
+ return { args: { ...args, select: newSelect }, computeOnly };
3159
+ }
3160
+ function makeFluent(ctx, modelName, args, exec) {
3161
+ const model = findModel(ctx.schema, modelName);
3162
+ const where = args.where ?? {};
3163
+ let cached = null;
3164
+ const run = () => cached ??= exec();
3165
+ const fluent = {
3166
+ then: (res, rej) => run().then(res, rej),
3167
+ catch: (rej) => run().catch(rej),
3168
+ finally: (f) => run().finally(f)
3169
+ };
3170
+ for (const rel of relationFields(model)) {
3171
+ fluent[rel.name] = (subArgs = {}) => traverse(ctx, model, rel, where, subArgs);
3172
+ }
3173
+ return fluent;
3174
+ }
3175
+ function traverse(ctx, model, rel, parentWhere, subArgs) {
3176
+ const inverse = inverseField(ctx.schema, model, rel);
3177
+ const relatedWhere = inverse ? inverse.isList ? { [inverse.name]: { some: parentWhere } } : { [inverse.name]: parentWhere } : {};
3178
+ const where = subArgs.where ? { AND: [relatedWhere, subArgs.where] } : relatedWhere;
3179
+ const findArgs = { ...subArgs, where };
3180
+ if (rel.isList) {
3181
+ return ctx.engine.findMany(rel.type, findArgs);
3182
+ }
3183
+ return makeFluent(
3184
+ ctx,
3185
+ rel.type,
3186
+ { ...subArgs, where },
3187
+ () => ctx.engine.findFirst(rel.type, findArgs)
3188
+ );
3189
+ }
3190
+ function inverseField(schema, model, rel) {
3191
+ const related = findModel(schema, rel.type);
3192
+ if (!related) return void 0;
3193
+ const candidates = related.fields.filter(
3194
+ (f) => f.kind === "object" && f.type === model.name
3195
+ );
3196
+ if (rel.relation?.name) {
3197
+ const byName = candidates.find((f) => f.relation?.name === rel.relation?.name);
3198
+ if (byName) return byName;
3199
+ }
3200
+ return candidates[0];
3201
+ }
3202
+
3203
+ // src/client/index.ts
3204
+ var EmberClientBase = class {
3205
+ driver;
3206
+ engine;
3207
+ dialect;
3208
+ schema;
3209
+ connected = false;
3210
+ /** Mutated by $extends (per instance) and $use (shared). */
3211
+ extensions = [];
3212
+ middlewares = [];
3213
+ queryListeners = [];
3214
+ baseLog;
3215
+ constructor(options) {
3216
+ this.schema = options.schema;
3217
+ const url = options.datasourceUrl ?? resolveDatasourceUrl(options.schema, process.cwd());
3218
+ if (!options.datasource && !url) {
3219
+ throw new EmberError(
3220
+ "No datasource configured. Provide datasourceUrl, datasource, or a datasource block with a resolvable url."
3221
+ );
3222
+ }
3223
+ const config = options.datasource ?? parseConnectionUrl(url);
3224
+ this.dialect = new FirebirdDialect({ version: config.version });
3225
+ this.baseLog = buildLogger(options.log);
3226
+ this.driver = createDriver(config, { onQuery: (e) => this.dispatchQuery(e) });
3227
+ this.engine = new QueryEngine(options.schema, this.dialect, this.driver);
3228
+ this.installDelegates();
3229
+ }
3230
+ dispatchQuery(event) {
3231
+ this.baseLog?.(event);
3232
+ for (const listener of this.queryListeners) listener(event);
3233
+ }
3234
+ delegateContext() {
3235
+ return {
3236
+ engine: this.engine,
3237
+ schema: this.schema,
3238
+ extensions: this.extensions,
3239
+ middlewares: this.middlewares
3240
+ };
3241
+ }
3242
+ /** (Re)define one delegate property per model using the current extensions. */
3243
+ installDelegates() {
3244
+ const ctx = this.delegateContext();
3245
+ for (const model of this.schema.models) {
3246
+ Object.defineProperty(this, lowerFirst(model.name), {
3247
+ value: buildDelegate(ctx, model.name),
3248
+ enumerable: true,
3249
+ configurable: true
3250
+ });
3251
+ }
3252
+ }
3253
+ /**
3254
+ * Prisma-style Client Extensions. Returns a new client (the original is
3255
+ * unchanged) that shares the connection/engine but applies the extension's
3256
+ * `result` / `model` / `query` / `client` definitions.
3257
+ */
3258
+ $extends(extension) {
3259
+ const clone = Object.create(this);
3260
+ clone.extensions = [...this.extensions, extension];
3261
+ clone.installDelegates();
3262
+ if (extension.client) {
3263
+ for (const [key, value] of Object.entries(extension.client)) {
3264
+ Object.defineProperty(clone, key, {
3265
+ value: typeof value === "function" ? value.bind(clone) : value,
3266
+ enumerable: true,
3267
+ configurable: true
3268
+ });
3269
+ }
3270
+ }
3271
+ return clone;
3272
+ }
3273
+ /** Prisma-style middleware: runs around every operation. */
3274
+ $use(middleware) {
3275
+ this.middlewares.push(middleware);
3276
+ }
3277
+ /** Subscribe to query events (mirrors the `log` callback). */
3278
+ $on(event, listener) {
3279
+ if (event === "query") this.queryListeners.push(listener);
3280
+ }
3281
+ /** Type-safe access to a delegate by model name. */
3282
+ model(name) {
3283
+ return this[lowerFirst(name)];
3284
+ }
3285
+ async $connect() {
3286
+ if (this.connected) return;
3287
+ await this.driver.connect();
3288
+ this.connected = true;
3289
+ }
3290
+ async $disconnect() {
3291
+ if (!this.connected) return;
3292
+ await this.driver.disconnect();
3293
+ this.connected = false;
3294
+ }
3295
+ $transaction(arg, options) {
3296
+ if (Array.isArray(arg)) {
3297
+ return this.driver.transaction(async () => {
3298
+ const results = [];
3299
+ for (const thunk of arg) results.push(await thunk(this));
3300
+ return results;
3301
+ }, options);
3302
+ }
3303
+ return this.driver.transaction(() => arg(this), options);
3304
+ }
3305
+ /** Execute a raw read query (returns rows) inside a transaction. */
3306
+ $queryRawUnsafe(sql, ...params) {
3307
+ return this.driver.transaction(
3308
+ (tx) => tx.query(sql, params.map(toSqlValue))
3309
+ );
3310
+ }
3311
+ /** Execute a raw write statement inside a transaction; returns affected rows when available. */
3312
+ $executeRawUnsafe(sql, ...params) {
3313
+ return this.driver.transaction(async (tx) => {
3314
+ const rows = await tx.query(sql, params.map(toSqlValue));
3315
+ return Array.isArray(rows) ? rows.length : 0;
3316
+ });
3317
+ }
3318
+ /** Tagged-template raw query: `client.$queryRaw\`SELECT * FROM T WHERE id = ${id}\``. */
3319
+ $queryRaw(strings, ...values) {
3320
+ const { sql, params } = buildTemplate(strings, values);
3321
+ return this.$queryRawUnsafe(sql, ...params);
3322
+ }
3323
+ $executeRaw(strings, ...values) {
3324
+ const { sql, params } = buildTemplate(strings, values);
3325
+ return this.$executeRawUnsafe(sql, ...params);
3326
+ }
3327
+ };
3328
+ function buildTemplate(strings, values) {
3329
+ let sql = "";
3330
+ strings.forEach((part, i) => {
3331
+ sql += part;
3332
+ if (i < values.length) sql += "?";
3333
+ });
3334
+ return { sql, params: values };
3335
+ }
3336
+ function toSqlValue(v) {
3337
+ return v;
3338
+ }
3339
+ function buildLogger(log) {
3340
+ if (!log) return void 0;
3341
+ if (typeof log === "function") return log;
3342
+ return (e) => {
3343
+ const params = e.params.length ? ` -- ${JSON.stringify(e.params)}` : "";
3344
+ console.log(
3345
+ `ember:query (${e.durationMs.toFixed(1)}ms, ${e.rowCount} rows) ${e.sql}${params}`
3346
+ );
3347
+ };
3348
+ }
3349
+ function createClient(options) {
3350
+ return new EmberClientBase(options);
3351
+ }
3352
+
3353
+ // src/introspect/firebird-meta.ts
3354
+ var trim = (v) => v == null ? "" : String(v).trim();
3355
+ var numOrNull = (v) => v == null ? null : Number(v);
3356
+ var FirebirdMetadataReader = class {
3357
+ constructor(tx) {
3358
+ this.tx = tx;
3359
+ }
3360
+ tx;
3361
+ async tables() {
3362
+ const rows = await this.tx.query(
3363
+ `SELECT RDB$RELATION_NAME
3364
+ FROM RDB$RELATIONS
3365
+ WHERE RDB$VIEW_BLR IS NULL
3366
+ AND (RDB$SYSTEM_FLAG IS NULL OR RDB$SYSTEM_FLAG = 0)
3367
+ ORDER BY RDB$RELATION_NAME`
3368
+ );
3369
+ return rows.map((r) => trim(r["RDB$RELATION_NAME"]));
3370
+ }
3371
+ async columns() {
3372
+ const rows = await this.tx.query(
3373
+ `SELECT
3374
+ rf.RDB$RELATION_NAME AS TABLE_NAME,
3375
+ rf.RDB$FIELD_NAME AS FIELD_NAME,
3376
+ rf.RDB$FIELD_POSITION AS FIELD_POSITION,
3377
+ rf.RDB$NULL_FLAG AS NULL_FLAG,
3378
+ rf.RDB$DEFAULT_SOURCE AS DEFAULT_SOURCE,
3379
+ rf.RDB$IDENTITY_TYPE AS IDENTITY_TYPE,
3380
+ f.RDB$FIELD_TYPE AS FIELD_TYPE,
3381
+ f.RDB$FIELD_SUB_TYPE AS FIELD_SUB_TYPE,
3382
+ f.RDB$FIELD_LENGTH AS FIELD_LENGTH,
3383
+ f.RDB$CHARACTER_LENGTH AS CHAR_LENGTH,
3384
+ f.RDB$FIELD_PRECISION AS FIELD_PRECISION,
3385
+ f.RDB$FIELD_SCALE AS FIELD_SCALE
3386
+ FROM RDB$RELATION_FIELDS rf
3387
+ JOIN RDB$FIELDS f ON f.RDB$FIELD_NAME = rf.RDB$FIELD_SOURCE
3388
+ JOIN RDB$RELATIONS r ON r.RDB$RELATION_NAME = rf.RDB$RELATION_NAME
3389
+ WHERE r.RDB$VIEW_BLR IS NULL
3390
+ AND (r.RDB$SYSTEM_FLAG IS NULL OR r.RDB$SYSTEM_FLAG = 0)
3391
+ ORDER BY rf.RDB$RELATION_NAME, rf.RDB$FIELD_POSITION`
3392
+ );
3393
+ return rows.map((r) => ({
3394
+ table: trim(r.TABLE_NAME),
3395
+ name: trim(r.FIELD_NAME),
3396
+ position: Number(r.FIELD_POSITION ?? 0),
3397
+ fieldType: Number(r.FIELD_TYPE ?? 0),
3398
+ fieldSubType: numOrNull(r.FIELD_SUB_TYPE),
3399
+ length: numOrNull(r.FIELD_LENGTH),
3400
+ charLength: numOrNull(r.CHAR_LENGTH),
3401
+ precision: numOrNull(r.FIELD_PRECISION),
3402
+ scale: numOrNull(r.FIELD_SCALE),
3403
+ notNull: r.NULL_FLAG != null && Number(r.NULL_FLAG) === 1,
3404
+ defaultSource: r.DEFAULT_SOURCE == null ? null : trim(r.DEFAULT_SOURCE),
3405
+ isIdentity: r.IDENTITY_TYPE != null
3406
+ }));
3407
+ }
3408
+ async constraints() {
3409
+ const rows = await this.tx.query(
3410
+ `SELECT
3411
+ rc.RDB$CONSTRAINT_NAME AS CONSTRAINT_NAME,
3412
+ rc.RDB$CONSTRAINT_TYPE AS CONSTRAINT_TYPE,
3413
+ rc.RDB$RELATION_NAME AS TABLE_NAME,
3414
+ rc.RDB$INDEX_NAME AS INDEX_NAME,
3415
+ seg.RDB$FIELD_NAME AS FIELD_NAME,
3416
+ seg.RDB$FIELD_POSITION AS SEG_POSITION,
3417
+ refc.RDB$UPDATE_RULE AS UPDATE_RULE,
3418
+ refc.RDB$DELETE_RULE AS DELETE_RULE,
3419
+ refc.RDB$CONST_NAME_UQ AS UQ_NAME
3420
+ FROM RDB$RELATION_CONSTRAINTS rc
3421
+ JOIN RDB$INDEX_SEGMENTS seg ON seg.RDB$INDEX_NAME = rc.RDB$INDEX_NAME
3422
+ LEFT JOIN RDB$REF_CONSTRAINTS refc ON refc.RDB$CONSTRAINT_NAME = rc.RDB$CONSTRAINT_NAME
3423
+ WHERE rc.RDB$CONSTRAINT_TYPE IN ('PRIMARY KEY', 'UNIQUE', 'FOREIGN KEY')
3424
+ ORDER BY rc.RDB$CONSTRAINT_NAME, seg.RDB$FIELD_POSITION`
3425
+ );
3426
+ const byName = /* @__PURE__ */ new Map();
3427
+ const uqByName = /* @__PURE__ */ new Map();
3428
+ for (const r of rows) {
3429
+ const name = trim(r.CONSTRAINT_NAME);
3430
+ let c = byName.get(name);
3431
+ if (!c) {
3432
+ c = {
3433
+ table: trim(r.TABLE_NAME),
3434
+ type: trim(r.CONSTRAINT_TYPE),
3435
+ name,
3436
+ indexName: r.INDEX_NAME == null ? null : trim(r.INDEX_NAME),
3437
+ columns: []
3438
+ };
3439
+ if (c.type === "FOREIGN KEY") {
3440
+ c.updateRule = trim(r.UPDATE_RULE) || void 0;
3441
+ c.deleteRule = trim(r.DELETE_RULE) || void 0;
3442
+ uqByName.set(name, trim(r.UQ_NAME));
3443
+ }
3444
+ byName.set(name, c);
3445
+ }
3446
+ const col = trim(r.FIELD_NAME);
3447
+ if (col && !c.columns.includes(col)) c.columns.push(col);
3448
+ }
3449
+ for (const c of byName.values()) {
3450
+ if (c.type !== "FOREIGN KEY") continue;
3451
+ const uqName = uqByName.get(c.name);
3452
+ if (!uqName) continue;
3453
+ const target = byName.get(uqName);
3454
+ if (target) {
3455
+ c.references = { table: target.table, columns: [...target.columns] };
3456
+ } else {
3457
+ const resolved = await this.resolveConstraintColumns(uqName);
3458
+ if (resolved) c.references = resolved;
3459
+ }
3460
+ }
3461
+ return [...byName.values()];
3462
+ }
3463
+ async resolveConstraintColumns(constraintName2) {
3464
+ const rows = await this.tx.query(
3465
+ `SELECT rc.RDB$RELATION_NAME AS TABLE_NAME, seg.RDB$FIELD_NAME AS FIELD_NAME
3466
+ FROM RDB$RELATION_CONSTRAINTS rc
3467
+ JOIN RDB$INDEX_SEGMENTS seg ON seg.RDB$INDEX_NAME = rc.RDB$INDEX_NAME
3468
+ WHERE rc.RDB$CONSTRAINT_NAME = ?
3469
+ ORDER BY seg.RDB$FIELD_POSITION`,
3470
+ [constraintName2]
3471
+ );
3472
+ if (rows.length === 0) return null;
3473
+ return {
3474
+ table: trim(rows[0].TABLE_NAME),
3475
+ columns: rows.map((r) => trim(r.FIELD_NAME))
3476
+ };
3477
+ }
3478
+ };
3479
+
3480
+ // src/introspect/type-map.ts
3481
+ var FB = {
3482
+ SMALLINT: 7,
3483
+ INTEGER: 8,
3484
+ QUAD: 9,
3485
+ FLOAT: 10,
3486
+ DATE_LEGACY: 11,
3487
+ DATE: 12,
3488
+ TIME: 13,
3489
+ CHAR: 14,
3490
+ INT64: 16,
3491
+ // BIGINT / numeric
3492
+ BOOLEAN: 23,
3493
+ DOUBLE: 27,
3494
+ TIMESTAMP: 35,
3495
+ VARCHAR: 37,
3496
+ BLOB: 261
3497
+ };
3498
+ function mapColumnType(col) {
3499
+ const scale = col.scale ?? 0;
3500
+ switch (col.fieldType) {
3501
+ case FB.SMALLINT:
3502
+ return scale < 0 ? decimal(col) : { scalar: "Int", native: { name: "SmallInt", args: [] } };
3503
+ case FB.INTEGER:
3504
+ return scale < 0 ? decimal(col) : { scalar: "Int", native: { name: "Integer", args: [] } };
3505
+ case FB.INT64:
3506
+ return scale < 0 ? decimal(col) : { scalar: "BigInt", native: { name: "BigInt", args: [] } };
3507
+ case FB.FLOAT:
3508
+ return { scalar: "Float", native: { name: "Float", args: [] } };
3509
+ case FB.DOUBLE:
3510
+ return { scalar: "Float", native: { name: "DoublePrecision", args: [] } };
3511
+ case FB.BOOLEAN:
3512
+ return { scalar: "Boolean", native: { name: "Boolean", args: [] } };
3513
+ case FB.CHAR:
3514
+ return {
3515
+ scalar: "String",
3516
+ native: { name: "Char", args: col.charLength ? [col.charLength] : [] }
3517
+ };
3518
+ case FB.VARCHAR:
3519
+ return {
3520
+ scalar: "String",
3521
+ native: { name: "VarChar", args: col.charLength ? [col.charLength] : [] }
3522
+ };
3523
+ case FB.DATE:
3524
+ case FB.DATE_LEGACY:
3525
+ return { scalar: "DateTime", native: { name: "Date", args: [] } };
3526
+ case FB.TIME:
3527
+ return { scalar: "DateTime", native: { name: "Time", args: [] } };
3528
+ case FB.TIMESTAMP:
3529
+ return { scalar: "DateTime", native: { name: "Timestamp", args: [] } };
3530
+ case FB.BLOB:
3531
+ return col.fieldSubType === 1 ? { scalar: "String", native: { name: "Text", args: [] } } : { scalar: "Bytes", native: { name: "Blob", args: [] } };
3532
+ default:
3533
+ return { scalar: "String" };
3534
+ }
3535
+ }
3536
+ function decimal(col) {
3537
+ const precision = col.precision ?? 18;
3538
+ const scaleAbs = Math.abs(col.scale ?? 0);
3539
+ return {
3540
+ scalar: "Decimal",
3541
+ native: { name: "Decimal", args: [precision, scaleAbs] }
3542
+ };
3543
+ }
3544
+
3545
+ // src/introspect/index.ts
3546
+ var Introspector = class {
3547
+ constructor(driver) {
3548
+ this.driver = driver;
3549
+ }
3550
+ driver;
3551
+ async introspect(options = {}) {
3552
+ const { tables, columns, constraints } = await this.driver.transaction(
3553
+ async (tx) => {
3554
+ const reader = new FirebirdMetadataReader(tx);
3555
+ return {
3556
+ tables: await reader.tables(),
3557
+ columns: await reader.columns(),
3558
+ constraints: await reader.constraints()
3559
+ };
3560
+ },
3561
+ { isolation: "READ_COMMITTED_READ_ONLY" }
3562
+ );
3563
+ const columnsByTable = groupBy(columns, (c) => c.table);
3564
+ const constraintsByTable = groupBy(constraints, (c) => c.table);
3565
+ const modelNames = buildNameMap(tables);
3566
+ const models = tables.map(
3567
+ (table) => this.buildModel(
3568
+ table,
3569
+ modelNames,
3570
+ columnsByTable.get(table) ?? [],
3571
+ constraintsByTable.get(table) ?? []
3572
+ )
3573
+ );
3574
+ addRelations(models, modelNames, constraints);
3575
+ const enums = [];
3576
+ const doc = {
3577
+ generators: [
3578
+ { name: "client", provider: "ember-client-js", output: "../generated", config: {} }
3579
+ ],
3580
+ models,
3581
+ enums
3582
+ };
3583
+ if (options.datasource) {
3584
+ doc.datasource = {
3585
+ name: options.datasource.name,
3586
+ provider: options.datasource.provider,
3587
+ url: options.datasource.envVar ? { kind: "env", value: options.datasource.envVar } : { kind: "literal", value: options.datasource.url ?? "" }
3588
+ };
3589
+ }
3590
+ return doc;
3591
+ }
3592
+ buildModel(table, modelNames, columns, constraints) {
3593
+ const modelName = modelNames.get(table);
3594
+ const pk = constraints.find((c) => c.type === "PRIMARY KEY");
3595
+ const uniques = constraints.filter((c) => c.type === "UNIQUE");
3596
+ const fieldNameMap = buildNameMap(columns.map((c) => c.name), camelCase);
3597
+ const fields = columns.map(
3598
+ (col) => buildField(col, fieldNameMap, pk, uniques)
3599
+ );
3600
+ const model = {
3601
+ name: modelName,
3602
+ dbName: modelName !== table ? table : void 0,
3603
+ fields,
3604
+ primaryKey: pk && pk.columns.length > 1 ? pk.columns.map((c) => fieldNameMap.get(c)) : [],
3605
+ uniqueIndexes: uniques.filter((u) => u.columns.length > 1).map((u) => ({ fields: u.columns.map((c) => fieldNameMap.get(c)) })),
3606
+ indexes: []
3607
+ };
3608
+ return model;
3609
+ }
3610
+ };
3611
+ function buildField(col, fieldNameMap, pk, uniques) {
3612
+ const fieldName = fieldNameMap.get(col.name);
3613
+ const mapped = mapColumnType(col);
3614
+ const isSingleIdCol = !!pk && pk.columns.length === 1 && pk.columns[0] === col.name;
3615
+ const isSingleUnique = uniques.some(
3616
+ (u) => u.columns.length === 1 && u.columns[0] === col.name
3617
+ );
3618
+ const field = {
3619
+ name: fieldName,
3620
+ type: mapped.scalar,
3621
+ kind: "scalar",
3622
+ isList: false,
3623
+ isRequired: col.notNull || isSingleIdCol,
3624
+ isId: isSingleIdCol,
3625
+ isUnique: isSingleUnique,
3626
+ isUpdatedAt: false,
3627
+ dbName: fieldName !== col.name ? col.name : void 0,
3628
+ nativeType: mapped.native,
3629
+ default: parseDefault(col)
3630
+ };
3631
+ return field;
3632
+ }
3633
+ function parseDefault(col) {
3634
+ if (col.isIdentity) return { function: { name: "autoincrement", args: [] } };
3635
+ if (!col.defaultSource) return void 0;
3636
+ const src = col.defaultSource.replace(/^DEFAULT\s+/i, "").trim();
3637
+ if (/^CURRENT_TIMESTAMP/i.test(src)) return { function: { name: "now", args: [] } };
3638
+ if (/^(TRUE|FALSE)$/i.test(src)) return { literal: /^TRUE$/i.test(src) };
3639
+ if (/^-?\d+(\.\d+)?$/.test(src)) return { literal: Number(src) };
3640
+ const str = /^'(.*)'$/.exec(src);
3641
+ if (str) return { literal: str[1] };
3642
+ return void 0;
3643
+ }
3644
+ function addRelations(models, modelNames, constraints) {
3645
+ const byName = new Map(models.map((m) => [m.name, m]));
3646
+ for (const fk of constraints) {
3647
+ if (fk.type !== "FOREIGN KEY" || !fk.references) continue;
3648
+ const childModel = byName.get(modelNames.get(fk.table));
3649
+ const parentModel = byName.get(modelNames.get(fk.references.table));
3650
+ if (!childModel || !parentModel) continue;
3651
+ const localFieldNames = fk.columns.map((c) => columnToField(childModel, c));
3652
+ const refFieldNames = fk.references.columns.map(
3653
+ (c) => columnToField(parentModel, c)
3654
+ );
3655
+ const childFieldName = uniqueFieldName(childModel, lowerFirst(parentModel.name));
3656
+ childModel.fields.push({
3657
+ name: childFieldName,
3658
+ type: parentModel.name,
3659
+ kind: "object",
3660
+ isList: false,
3661
+ isRequired: localFieldNames.every(
3662
+ (n) => childModel.fields.find((f) => f.name === n)?.isRequired
3663
+ ),
3664
+ isId: false,
3665
+ isUnique: false,
3666
+ isUpdatedAt: false,
3667
+ relation: {
3668
+ fields: localFieldNames,
3669
+ references: refFieldNames,
3670
+ onDelete: mapRule(fk.deleteRule),
3671
+ onUpdate: mapRule(fk.updateRule)
3672
+ }
3673
+ });
3674
+ const backName = uniqueFieldName(
3675
+ parentModel,
3676
+ lowerFirst(pluralize(childModel.name))
3677
+ );
3678
+ parentModel.fields.push({
3679
+ name: backName,
3680
+ type: childModel.name,
3681
+ kind: "object",
3682
+ isList: true,
3683
+ isRequired: false,
3684
+ isId: false,
3685
+ isUnique: false,
3686
+ isUpdatedAt: false
3687
+ });
3688
+ }
3689
+ }
3690
+ function columnToField(model, column) {
3691
+ const f = model.fields.find((x) => (x.dbName ?? x.name) === column);
3692
+ return f ? f.name : camelCase(column);
3693
+ }
3694
+ function uniqueFieldName(model, base) {
3695
+ let name = base;
3696
+ let i = 1;
3697
+ while (model.fields.some((f) => f.name === name)) name = `${base}_${++i}`;
3698
+ return name;
3699
+ }
3700
+ function mapRule(rule) {
3701
+ switch ((rule ?? "").toUpperCase()) {
3702
+ case "CASCADE":
3703
+ return "Cascade";
3704
+ case "SET NULL":
3705
+ return "SetNull";
3706
+ case "SET DEFAULT":
3707
+ return "SetDefault";
3708
+ case "NO ACTION":
3709
+ return "NoAction";
3710
+ case "RESTRICT":
3711
+ return "Restrict";
3712
+ default:
3713
+ return void 0;
3714
+ }
3715
+ }
3716
+ function buildNameMap(dbNames, transform = pascalCase) {
3717
+ const map = /* @__PURE__ */ new Map();
3718
+ const used = /* @__PURE__ */ new Set();
3719
+ for (const dbName of uniq(dbNames)) {
3720
+ let name = transform(dbName);
3721
+ if (!name) name = dbName;
3722
+ let candidate = name;
3723
+ let i = 1;
3724
+ while (used.has(candidate)) candidate = `${name}_${++i}`;
3725
+ used.add(candidate);
3726
+ map.set(dbName, candidate);
3727
+ }
3728
+ return map;
3729
+ }
3730
+ function groupBy(items, key) {
3731
+ const map = /* @__PURE__ */ new Map();
3732
+ for (const item of items) {
3733
+ const k = key(item);
3734
+ const list = map.get(k) ?? [];
3735
+ list.push(item);
3736
+ map.set(k, list);
3737
+ }
3738
+ return map;
3739
+ }
3740
+
3741
+ // src/generator/index.ts
3742
+ import { mkdirSync, writeFileSync } from "fs";
3743
+ import { dirname as dirname2, resolve as resolve2 } from "path";
3744
+
3745
+ // src/generator/ts-types.ts
3746
+ var SCALAR_TS = {
3747
+ String: "string",
3748
+ Boolean: "boolean",
3749
+ Int: "number",
3750
+ BigInt: "bigint",
3751
+ Float: "number",
3752
+ Decimal: "number",
3753
+ DateTime: "Date",
3754
+ Bytes: "Buffer",
3755
+ Json: "JsonValue"
3756
+ };
3757
+ function baseTsType(field, schema) {
3758
+ if (field.kind === "enum") return field.type;
3759
+ if (field.kind === "object") return field.type;
3760
+ return SCALAR_TS[field.type] ?? "unknown";
3761
+ }
3762
+
3763
+ // src/generator/index.ts
3764
+ var ClientGenerator = class {
3765
+ constructor(schema) {
3766
+ this.schema = schema;
3767
+ }
3768
+ schema;
3769
+ generate() {
3770
+ const parts = [];
3771
+ parts.push(this.header());
3772
+ parts.push(this.enums());
3773
+ parts.push(this.modelTypes());
3774
+ parts.push(this.registry());
3775
+ parts.push(PAYLOAD_RESOLVER);
3776
+ parts.push(this.namespace());
3777
+ parts.push(this.clientClass());
3778
+ return parts.filter(Boolean).join("\n\n") + "\n";
3779
+ }
3780
+ // ---- header & shared ----------------------------------------------------
3781
+ header() {
3782
+ return `// AUTO-GENERATED by EmberORM. Do not edit by hand.
3783
+ /* eslint-disable */
3784
+ import { EmberClientBase } from "ember-orm/client";
3785
+ import type { ClientOptions } from "ember-orm/client";
3786
+
3787
+ export type JsonValue =
3788
+ | string
3789
+ | number
3790
+ | boolean
3791
+ | null
3792
+ | { [key: string]: JsonValue }
3793
+ | JsonValue[];
3794
+
3795
+ export type SortOrder = "asc" | "desc";
3796
+ export type QueryMode = "default" | "insensitive";
3797
+
3798
+ const schemaDocument = ${JSON.stringify(this.schema)} as unknown as ClientOptions["schema"];`;
3799
+ }
3800
+ enums() {
3801
+ return this.schema.enums.map((e) => this.enumType(e)).join("\n\n");
3802
+ }
3803
+ enumType(node) {
3804
+ const values = node.values.map((v) => ` ${v.name}: "${v.name}"`).join(",\n");
3805
+ return `export const ${node.name} = {
3806
+ ${values}
3807
+ } as const;
3808
+ export type ${node.name} = (typeof ${node.name})[keyof typeof ${node.name}];`;
3809
+ }
3810
+ modelTypes() {
3811
+ return this.schema.models.map((m) => this.modelType(m)).join("\n\n");
3812
+ }
3813
+ modelType(model) {
3814
+ const scalars = scalarFields(model).map((f) => ` ${f.name}: ${this.scalarType(f)};`).join("\n");
3815
+ const lines = [];
3816
+ lines.push(`export type ${model.name} = {
3817
+ ${scalars}
3818
+ };`);
3819
+ lines.push(`export type ${model.name}GetPayload<A> = $Payload<"${model.name}", A>;`);
3820
+ lines.push(this.fluentType(model));
3821
+ return lines.join("\n");
3822
+ }
3823
+ /**
3824
+ * Fluent-API return type for single-record reads: a Promise of the payload
3825
+ * that also exposes a method per relation (to-many -> array; to-one ->
3826
+ * chainable fluent).
3827
+ */
3828
+ fluentType(model) {
3829
+ const n = model.name;
3830
+ const methods = relationFields(model).map((f) => {
3831
+ if (f.isList) {
3832
+ return ` ${f.name}<S extends Prisma.${f.type}FindManyArgs = {}>(args?: S): Promise<${f.type}GetPayload<S>[]>;`;
3833
+ }
3834
+ return ` ${f.name}<S extends Prisma.${f.type}FindFirstArgs = {}>(args?: S): ${f.type}Fluent<S, null>;`;
3835
+ });
3836
+ return `export type ${n}Fluent<A, Null = null> = Promise<${n}GetPayload<A> | Null> & {
3837
+ ${methods.join("\n")}
3838
+ };`;
3839
+ }
3840
+ /**
3841
+ * Type-level registry powering recursive payload resolution: a model-name
3842
+ * union, a scalar-payload map, and a relation map carrying each relation's
3843
+ * target model, list-ness and nullability.
3844
+ */
3845
+ registry() {
3846
+ const names = this.schema.models.map((m) => `"${m.name}"`).join(" | ");
3847
+ const scalarEntries = this.schema.models.map((m) => ` ${m.name}: ${m.name};`).join("\n");
3848
+ const relationEntries = this.schema.models.map((m) => {
3849
+ const rels = relationFields(m).map((f) => {
3850
+ const nullable = !f.isList && !f.isRequired ? "; isNullable: true" : "";
3851
+ return ` ${f.name}: { model: "${f.type}"; isList: ${f.isList}${nullable} }`;
3852
+ }).join(";\n");
3853
+ return ` ${m.name}: {${rels ? `
3854
+ ${rels}
3855
+ ` : ""}};`;
3856
+ }).join("\n");
3857
+ return `export type $ModelName = ${names || "never"};
3858
+
3859
+ export interface $ScalarPayload {
3860
+ ${scalarEntries}
3861
+ }
3862
+
3863
+ export interface $RelationMap {
3864
+ ${relationEntries}
3865
+ }`;
3866
+ }
3867
+ scalarType(field) {
3868
+ const base = baseTsType(field, this.schema);
3869
+ if (field.isList) return `${base}[]`;
3870
+ return field.isRequired ? base : `${base} | null`;
3871
+ }
3872
+ // ---- input types (Prisma namespace) -------------------------------------
3873
+ namespace() {
3874
+ const blocks = [SHARED_FILTERS];
3875
+ for (const model of this.schema.models) {
3876
+ blocks.push(this.whereInput(model));
3877
+ blocks.push(this.orderByInput(model));
3878
+ blocks.push(this.selectInclude(model));
3879
+ blocks.push(this.dataInputs(model));
3880
+ blocks.push(this.argsTypes(model));
3881
+ blocks.push(this.delegateInterface(model));
3882
+ }
3883
+ return `export namespace Prisma {
3884
+ ${indentBlock(blocks.join("\n\n"))}
3885
+ }`;
3886
+ }
3887
+ whereInput(model) {
3888
+ const fields = [];
3889
+ for (const f of model.fields) {
3890
+ if (f.kind === "object") {
3891
+ fields.push(` ${f.name}?: ${this.relationFilter(f)};`);
3892
+ } else {
3893
+ fields.push(` ${f.name}?: ${this.fieldFilter(f)};`);
3894
+ }
3895
+ }
3896
+ return `export type ${model.name}WhereInput = {
3897
+ AND?: ${model.name}WhereInput | ${model.name}WhereInput[];
3898
+ OR?: ${model.name}WhereInput[];
3899
+ NOT?: ${model.name}WhereInput | ${model.name}WhereInput[];
3900
+ ${fields.join("\n")}
3901
+ };
3902
+
3903
+ export type ${model.name}WhereUniqueInput = ${this.whereUnique(model)};`;
3904
+ }
3905
+ whereUnique(model) {
3906
+ const uniques = /* @__PURE__ */ new Set();
3907
+ for (const f of model.fields) if (f.isId || f.isUnique) uniques.add(f.name);
3908
+ for (const f of model.primaryKey) uniques.add(f);
3909
+ const parts = [...uniques].map((name) => {
3910
+ const field = model.fields.find((x) => x.name === name);
3911
+ return `{ ${name}: ${baseTsType(field, this.schema)} }`;
3912
+ });
3913
+ const base = `Partial<${model.name}WhereInput>`;
3914
+ return parts.length ? `(${parts.join(" | ")}) & ${base}` : base;
3915
+ }
3916
+ fieldFilter(field) {
3917
+ const base = baseTsType(field, this.schema);
3918
+ const filterName = FILTER_FOR_SCALAR[field.type] ?? null;
3919
+ if (field.kind === "enum") {
3920
+ return `${base} | { equals?: ${base}; in?: ${base}[]; notIn?: ${base}[]; not?: ${base} }`;
3921
+ }
3922
+ if (filterName) return `${base} | ${filterName}`;
3923
+ return `${base}`;
3924
+ }
3925
+ relationFilter(field) {
3926
+ if (field.isList) {
3927
+ return `{ some?: ${field.type}WhereInput; every?: ${field.type}WhereInput; none?: ${field.type}WhereInput }`;
3928
+ }
3929
+ return `${field.type}WhereInput | { is?: ${field.type}WhereInput | null; isNot?: ${field.type}WhereInput | null } | null`;
3930
+ }
3931
+ orderByInput(model) {
3932
+ const fields = scalarFields(model).map((f) => ` ${f.name}?: SortOrder;`).join("\n");
3933
+ return `export type ${model.name}OrderByInput = {
3934
+ ${fields}
3935
+ };`;
3936
+ }
3937
+ selectInclude(model) {
3938
+ const scalarSel = scalarFields(model).map((f) => ` ${f.name}?: boolean;`).join("\n");
3939
+ const relSel = relationFields(model).map((f) => ` ${f.name}?: boolean | ${f.type}FindManyArgs;`).join("\n");
3940
+ const include = relationFields(model).map((f) => ` ${f.name}?: boolean | ${f.type}FindManyArgs;`).join("\n");
3941
+ const listRelations = relationFields(model).filter((f) => f.isList);
3942
+ const countLine = listRelations.length ? `
3943
+ _count?: boolean | { select?: { ${listRelations.map((f) => `${f.name}?: boolean`).join("; ")} } };` : "";
3944
+ return `export type ${model.name}Select = {
3945
+ ${scalarSel}${relSel ? "\n" + relSel : ""}${countLine}
3946
+ };
3947
+
3948
+ export type ${model.name}Include = {
3949
+ ${include || " [k: string]: never;"}${countLine}
3950
+ };`;
3951
+ }
3952
+ dataInputs(model) {
3953
+ const foreignKeys2 = /* @__PURE__ */ new Set();
3954
+ for (const rel of relationFields(model)) {
3955
+ for (const fk of rel.relation?.fields ?? []) foreignKeys2.add(fk);
3956
+ }
3957
+ const createScalars = scalarFields(model).filter((f) => !isGeneratedOnCreate(f)).map((f) => {
3958
+ const optional = isOptionalOnCreate(f) || foreignKeys2.has(f.name) ? "?" : "";
3959
+ return ` ${f.name}${optional}: ${this.inputScalarType(f)};`;
3960
+ });
3961
+ const createRelations = relationFields(model).map(
3962
+ (f) => ` ${f.name}?: ${this.nestedCreate(f)};`
3963
+ );
3964
+ const updateScalars = scalarFields(model).map(
3965
+ (f) => ` ${f.name}?: ${this.updateScalarType(f)};`
3966
+ );
3967
+ const updateRelations = relationFields(model).map(
3968
+ (f) => ` ${f.name}?: ${this.nestedUpdate(f)};`
3969
+ );
3970
+ return `export type ${model.name}CreateInput = {
3971
+ ${[...createScalars, ...createRelations].join("\n")}
3972
+ };
3973
+
3974
+ export type ${model.name}UpdateInput = {
3975
+ ${[...updateScalars, ...updateRelations].join("\n")}
3976
+ };`;
3977
+ }
3978
+ inputScalarType(field) {
3979
+ const base = baseTsType(field, this.schema);
3980
+ if (field.isList) return `${base}[]`;
3981
+ return field.isRequired ? base : `${base} | null`;
3982
+ }
3983
+ /**
3984
+ * Update value for a scalar field: a bare value, `{ set }`, and — for numeric
3985
+ * fields — the atomic operators increment/decrement/multiply/divide.
3986
+ */
3987
+ updateScalarType(field) {
3988
+ const value = this.inputScalarType(field);
3989
+ if (field.isList) return value;
3990
+ const base = baseTsType(field, this.schema);
3991
+ if (NUMERIC_SCALARS.has(field.type)) {
3992
+ const nullable = field.isRequired ? "" : " | null";
3993
+ return `${value} | { set?: ${base}${nullable}; increment?: ${base}; decrement?: ${base}; multiply?: ${base}; divide?: ${base} }`;
3994
+ }
3995
+ return `${value} | { set?: ${value} }`;
3996
+ }
3997
+ nestedCreate(field) {
3998
+ const t = field.type;
3999
+ if (field.isList) {
4000
+ return `{ create?: ${t}CreateInput | ${t}CreateInput[]; connect?: ${t}WhereUniqueInput | ${t}WhereUniqueInput[]; connectOrCreate?: { where: ${t}WhereUniqueInput; create: ${t}CreateInput } | { where: ${t}WhereUniqueInput; create: ${t}CreateInput }[] }`;
4001
+ }
4002
+ return `{ create?: ${t}CreateInput; connect?: ${t}WhereUniqueInput; connectOrCreate?: { where: ${t}WhereUniqueInput; create: ${t}CreateInput } }`;
4003
+ }
4004
+ nestedUpdate(field) {
4005
+ const t = field.type;
4006
+ if (field.isList) {
4007
+ return `{ create?: ${t}CreateInput | ${t}CreateInput[]; connect?: ${t}WhereUniqueInput | ${t}WhereUniqueInput[]; disconnect?: ${t}WhereUniqueInput | ${t}WhereUniqueInput[]; set?: ${t}WhereUniqueInput | ${t}WhereUniqueInput[]; delete?: ${t}WhereUniqueInput | ${t}WhereUniqueInput[] }`;
4008
+ }
4009
+ return `{ create?: ${t}CreateInput; connect?: ${t}WhereUniqueInput; disconnect?: boolean }`;
4010
+ }
4011
+ argsTypes(model) {
4012
+ const n = model.name;
4013
+ return `export type ${n}Omit = Partial<Record<keyof ${n}, boolean>>;
4014
+
4015
+ export type ${n}FindManyArgs = {
4016
+ where?: ${n}WhereInput;
4017
+ orderBy?: ${n}OrderByInput | ${n}OrderByInput[];
4018
+ select?: ${n}Select;
4019
+ include?: ${n}Include;
4020
+ omit?: ${n}Omit;
4021
+ take?: number;
4022
+ skip?: number;
4023
+ cursor?: ${n}WhereUniqueInput;
4024
+ distinct?: (keyof ${n})[];
4025
+ };
4026
+
4027
+ export type ${n}FindFirstArgs = ${n}FindManyArgs;
4028
+
4029
+ export type ${n}FindUniqueArgs = {
4030
+ where: ${n}WhereUniqueInput;
4031
+ select?: ${n}Select;
4032
+ include?: ${n}Include;
4033
+ omit?: ${n}Omit;
4034
+ };
4035
+
4036
+ export type ${n}CreateArgs = {
4037
+ data: ${n}CreateInput;
4038
+ select?: ${n}Select;
4039
+ include?: ${n}Include;
4040
+ omit?: ${n}Omit;
4041
+ };
4042
+
4043
+ export type ${n}CreateManyArgs = { data: ${n}CreateInput[]; skipDuplicates?: boolean };
4044
+
4045
+ export type ${n}CreateManyAndReturnArgs = {
4046
+ data: ${n}CreateInput[];
4047
+ select?: ${n}Select;
4048
+ omit?: ${n}Omit;
4049
+ skipDuplicates?: boolean;
4050
+ };
4051
+
4052
+ export type ${n}UpdateArgs = {
4053
+ where: ${n}WhereUniqueInput;
4054
+ data: ${n}UpdateInput;
4055
+ select?: ${n}Select;
4056
+ include?: ${n}Include;
4057
+ omit?: ${n}Omit;
4058
+ };
4059
+
4060
+ export type ${n}UpdateManyArgs = { where?: ${n}WhereInput; data: ${n}UpdateInput };
4061
+
4062
+ export type ${n}UpsertArgs = {
4063
+ where: ${n}WhereUniqueInput;
4064
+ create: ${n}CreateInput;
4065
+ update: ${n}UpdateInput;
4066
+ select?: ${n}Select;
4067
+ include?: ${n}Include;
4068
+ omit?: ${n}Omit;
4069
+ };
4070
+
4071
+ export type ${n}DeleteArgs = { where: ${n}WhereUniqueInput; select?: ${n}Select; include?: ${n}Include };
4072
+ export type ${n}DeleteManyArgs = { where?: ${n}WhereInput };
4073
+
4074
+ export type ${n}CountArgs = { where?: ${n}WhereInput; take?: number; skip?: number };
4075
+
4076
+ export type ${n}AggregateArgs = {
4077
+ where?: ${n}WhereInput;
4078
+ _count?: true | Partial<Record<keyof ${n}, boolean>>;
4079
+ _avg?: Partial<Record<keyof ${n}, boolean>>;
4080
+ _sum?: Partial<Record<keyof ${n}, boolean>>;
4081
+ _min?: Partial<Record<keyof ${n}, boolean>>;
4082
+ _max?: Partial<Record<keyof ${n}, boolean>>;
4083
+ };
4084
+
4085
+ export type ${n}GroupByArgs = ${n}AggregateArgs & {
4086
+ by: (keyof ${n})[];
4087
+ having?: ${n}WhereInput;
4088
+ orderBy?: ${n}OrderByInput | ${n}OrderByInput[];
4089
+ };`;
4090
+ }
4091
+ delegateInterface(model) {
4092
+ const n = model.name;
4093
+ return `export interface ${n}Delegate {
4094
+ findMany<A extends ${n}FindManyArgs>(args?: A): Promise<${n}GetPayload<A>[]>;
4095
+ findFirst<A extends ${n}FindFirstArgs>(args?: A): ${n}Fluent<A, null>;
4096
+ findFirstOrThrow<A extends ${n}FindFirstArgs>(args?: A): ${n}Fluent<A, never>;
4097
+ findUnique<A extends ${n}FindUniqueArgs>(args: A): ${n}Fluent<A, null>;
4098
+ findUniqueOrThrow<A extends ${n}FindUniqueArgs>(args: A): ${n}Fluent<A, never>;
4099
+ create<A extends ${n}CreateArgs>(args: A): Promise<${n}GetPayload<A>>;
4100
+ createMany(args: ${n}CreateManyArgs): Promise<{ count: number }>;
4101
+ createManyAndReturn<A extends ${n}CreateManyAndReturnArgs>(args: A): Promise<${n}GetPayload<A>[]>;
4102
+ update<A extends ${n}UpdateArgs>(args: A): Promise<${n}GetPayload<A>>;
4103
+ updateMany(args: ${n}UpdateManyArgs): Promise<{ count: number }>;
4104
+ upsert<A extends ${n}UpsertArgs>(args: A): Promise<${n}GetPayload<A>>;
4105
+ delete<A extends ${n}DeleteArgs>(args: A): Promise<${n}GetPayload<A>>;
4106
+ deleteMany(args?: ${n}DeleteManyArgs): Promise<{ count: number }>;
4107
+ count(args?: ${n}CountArgs): Promise<number>;
4108
+ aggregate(args?: ${n}AggregateArgs): Promise<Record<string, any>>;
4109
+ groupBy(args: ${n}GroupByArgs): Promise<Record<string, any>[]>;
4110
+ }`;
4111
+ }
4112
+ // ---- client class -------------------------------------------------------
4113
+ clientClass() {
4114
+ const delegates = this.schema.models.map((m) => ` declare readonly ${lowerFirst(m.name)}: Prisma.${m.name}Delegate;`).join("\n");
4115
+ return `export type EmberClientOptions = {
4116
+ datasourceUrl?: string;
4117
+ datasource?: ClientOptions["datasource"];
4118
+ log?: ClientOptions["log"];
4119
+ };
4120
+
4121
+ export class EmberClient extends EmberClientBase {
4122
+ constructor(options: EmberClientOptions = {}) {
4123
+ super({ schema: schemaDocument, ...options });
4124
+ }
4125
+
4126
+ ${delegates}
4127
+ }`;
4128
+ }
4129
+ };
4130
+ function generateClientSource(schema) {
4131
+ return new ClientGenerator(schema).generate();
4132
+ }
4133
+ function writeClient(schema, outDir) {
4134
+ const source = generateClientSource(schema);
4135
+ const file = resolve2(outDir, "index.ts");
4136
+ mkdirSync(dirname2(file), { recursive: true });
4137
+ writeFileSync(file, source, "utf8");
4138
+ return file;
4139
+ }
4140
+ var PAYLOAD_RESOLVER = `type $Scalar<M extends $ModelName> = $ScalarPayload[M];
4141
+ type $Relations<M extends $ModelName> = M extends keyof $RelationMap
4142
+ ? $RelationMap[M]
4143
+ : {};
4144
+
4145
+ // A relation sub-arg may be \`true\` (full payload) or a nested args object.
4146
+ type $NormalizeArgs<Sub> = Sub extends object ? Sub : {};
4147
+
4148
+ type $RelationResult<Info, Sub> = Info extends {
4149
+ model: infer RM extends $ModelName;
4150
+ isList: infer L;
4151
+ }
4152
+ ? L extends true
4153
+ ? $Payload<RM, $NormalizeArgs<Sub>>[]
4154
+ : Info extends { isNullable: true }
4155
+ ? $Payload<RM, $NormalizeArgs<Sub>> | null
4156
+ : $Payload<RM, $NormalizeArgs<Sub>>
4157
+ : never;
4158
+
4159
+ // To-many relation keys of a model (eligible for _count).
4160
+ type $ListRelations<M extends $ModelName> = {
4161
+ [K in keyof $Relations<M>]: $Relations<M>[K] extends { isList: true }
4162
+ ? K
4163
+ : never;
4164
+ }[keyof $Relations<M>];
4165
+
4166
+ type $CountResult<M extends $ModelName, C> = C extends { select: infer SC }
4167
+ ? { [K in keyof SC & $ListRelations<M>]: number }
4168
+ : { [K in $ListRelations<M> & string]: number };
4169
+
4170
+ type $CountPart<M extends $ModelName, A> = A extends { _count: infer C }
4171
+ ? { _count: $CountResult<M, C> }
4172
+ : {};
4173
+
4174
+ type $SelectResult<M extends $ModelName, S> = {
4175
+ [K in keyof S & keyof $Scalar<M>]: $Scalar<M>[K];
4176
+ } & {
4177
+ [K in keyof S & keyof $Relations<M>]: $RelationResult<$Relations<M>[K], S[K]>;
4178
+ } & $CountPart<M, S>;
4179
+
4180
+ type $IncludeResult<M extends $ModelName, I> = {
4181
+ [K in keyof I & keyof $Relations<M>]: $RelationResult<$Relations<M>[K], I[K]>;
4182
+ } & $CountPart<M, I>;
4183
+
4184
+ export type $Payload<M extends $ModelName, A> = A extends { select: infer S }
4185
+ ? $SelectResult<M, S>
4186
+ : A extends { include: infer I }
4187
+ ? $Scalar<M> & $IncludeResult<M, I>
4188
+ : $Scalar<M>;`;
4189
+ var NUMERIC_SCALARS = /* @__PURE__ */ new Set(["Int", "BigInt", "Float", "Decimal"]);
4190
+ var FILTER_FOR_SCALAR = {
4191
+ String: "StringFilter",
4192
+ Int: "IntFilter",
4193
+ BigInt: "BigIntFilter",
4194
+ Float: "FloatFilter",
4195
+ Decimal: "FloatFilter",
4196
+ Boolean: "BoolFilter",
4197
+ DateTime: "DateTimeFilter",
4198
+ Bytes: "BytesFilter",
4199
+ Json: "JsonFilter"
4200
+ };
4201
+ var SHARED_FILTERS = `export type StringFilter = {
4202
+ equals?: string;
4203
+ not?: string | StringFilter;
4204
+ in?: string[];
4205
+ notIn?: string[];
4206
+ lt?: string;
4207
+ lte?: string;
4208
+ gt?: string;
4209
+ gte?: string;
4210
+ contains?: string;
4211
+ startsWith?: string;
4212
+ endsWith?: string;
4213
+ mode?: QueryMode;
4214
+ };
4215
+
4216
+ export type IntFilter = {
4217
+ equals?: number;
4218
+ not?: number | IntFilter;
4219
+ in?: number[];
4220
+ notIn?: number[];
4221
+ lt?: number;
4222
+ lte?: number;
4223
+ gt?: number;
4224
+ gte?: number;
4225
+ };
4226
+
4227
+ export type FloatFilter = IntFilter;
4228
+ export type BigIntFilter = {
4229
+ equals?: bigint;
4230
+ not?: bigint | BigIntFilter;
4231
+ in?: bigint[];
4232
+ notIn?: bigint[];
4233
+ lt?: bigint;
4234
+ lte?: bigint;
4235
+ gt?: bigint;
4236
+ gte?: bigint;
4237
+ };
4238
+
4239
+ export type BoolFilter = { equals?: boolean; not?: boolean };
4240
+
4241
+ export type DateTimeFilter = {
4242
+ equals?: Date;
4243
+ not?: Date | DateTimeFilter;
4244
+ in?: Date[];
4245
+ notIn?: Date[];
4246
+ lt?: Date;
4247
+ lte?: Date;
4248
+ gt?: Date;
4249
+ gte?: Date;
4250
+ };
4251
+
4252
+ export type BytesFilter = { equals?: Buffer; not?: Buffer };
4253
+
4254
+ // Firebird has no JSON SQL functions: filters operate on the serialized text.
4255
+ // 'path' is accepted by the type but rejected at runtime on Firebird.
4256
+ export type JsonFilter = {
4257
+ equals?: JsonValue;
4258
+ not?: JsonValue;
4259
+ string_contains?: string;
4260
+ string_starts_with?: string;
4261
+ string_ends_with?: string;
4262
+ path?: string[];
4263
+ };`;
4264
+ function isGeneratedOnCreate(field) {
4265
+ return field.default?.function?.name === "autoincrement";
4266
+ }
4267
+ function isOptionalOnCreate(field) {
4268
+ return !field.isRequired || !!field.default || field.isUpdatedAt || field.isList;
4269
+ }
4270
+ function indentBlock(text) {
4271
+ return text.split("\n").map((l) => l ? " " + l : l).join("\n");
4272
+ }
4273
+
4274
+ // src/migrate/index.ts
4275
+ import { mkdirSync as mkdirSync2, writeFileSync as writeFileSync2 } from "fs";
4276
+ import { join as join2 } from "path";
4277
+
4278
+ // src/migrate/diff.ts
4279
+ function diffSchemas(desired, current) {
4280
+ const currentByTable = new Map(
4281
+ current.models.map((m) => [modelTable(m), m])
4282
+ );
4283
+ const desiredTables = new Set(desired.models.map((m) => modelTable(m)));
4284
+ const createdModels = [];
4285
+ const modelChanges = [];
4286
+ for (const model of desired.models) {
4287
+ const table = modelTable(model);
4288
+ const currentModel = currentByTable.get(table);
4289
+ if (!currentModel) {
4290
+ createdModels.push(model);
4291
+ continue;
4292
+ }
4293
+ const change = diffModel(desired, model, currentModel, table);
4294
+ if (hasModelChanges(change)) modelChanges.push(change);
4295
+ }
4296
+ const droppedTables = current.models.map((m) => modelTable(m)).filter((t) => !desiredTables.has(t));
4297
+ return { createdModels, droppedTables, modelChanges };
4298
+ }
4299
+ function diffModel(desired, model, current, table) {
4300
+ const desiredCols = new Map(
4301
+ scalarFields(model).map((f) => [fieldColumn(f), f])
4302
+ );
4303
+ const currentCols = new Map(
4304
+ scalarFields(current).map((f) => [fieldColumn(f), f])
4305
+ );
4306
+ const addedColumns = [];
4307
+ const changedColumns = [];
4308
+ for (const [col, field] of desiredCols) {
4309
+ const existing = currentCols.get(col);
4310
+ if (!existing) {
4311
+ addedColumns.push(field);
4312
+ continue;
4313
+ }
4314
+ const typeChanged = !sameColumnType(field, existing);
4315
+ const nullabilityChanged = field.isRequired !== existing.isRequired;
4316
+ if (typeChanged || nullabilityChanged) {
4317
+ changedColumns.push({ field, table, typeChanged, nullabilityChanged });
4318
+ }
4319
+ }
4320
+ const droppedColumns = [...currentCols.keys()].filter(
4321
+ (c) => !desiredCols.has(c)
4322
+ );
4323
+ const currentUniqueSets = uniqueColumnSets(current).map(setKey);
4324
+ const addedUniques = uniqueColumnSets(model).filter((cols) => !currentUniqueSets.includes(setKey(cols))).map((cols) => ({ name: constraintName("UQ", table, cols), columns: cols }));
4325
+ const currentFkSets = foreignKeys(desired, current).map(fkKey);
4326
+ const addedForeignKeys = foreignKeys(desired, model).filter((fk) => !currentFkSets.includes(fkKey(fk))).map((fk) => ({ ...fk, name: constraintName("FK", table, fk.columns) }));
4327
+ return {
4328
+ model,
4329
+ table,
4330
+ addedColumns,
4331
+ droppedColumns,
4332
+ changedColumns,
4333
+ addedUniques,
4334
+ addedForeignKeys
4335
+ };
4336
+ }
4337
+ function hasModelChanges(c) {
4338
+ return c.addedColumns.length > 0 || c.droppedColumns.length > 0 || c.changedColumns.length > 0 || c.addedUniques.length > 0 || c.addedForeignKeys.length > 0;
4339
+ }
4340
+ function uniqueColumnSets(model) {
4341
+ const sets = [];
4342
+ for (const f of scalarFields(model)) {
4343
+ if (f.isUnique) sets.push([fieldColumn(f)]);
4344
+ }
4345
+ for (const u of model.uniqueIndexes) {
4346
+ sets.push(u.fields.map((name) => columnFor(model, name)));
4347
+ }
4348
+ return sets;
4349
+ }
4350
+ function indexSpecs(model) {
4351
+ return model.indexes.map((i) => ({
4352
+ name: i.name ?? constraintName("IDX", modelTable(model), i.fields.map((n) => columnFor(model, n))),
4353
+ columns: i.fields.map((name) => columnFor(model, name)),
4354
+ unique: i.unique
4355
+ }));
4356
+ }
4357
+ function foreignKeys(schema, model) {
4358
+ const out = [];
4359
+ for (const f of relationFields(model)) {
4360
+ const rel = f.relation;
4361
+ if (!rel?.fields?.length) continue;
4362
+ const refModel = schema.models.find((m) => m.name === f.type);
4363
+ if (!refModel) continue;
4364
+ out.push({
4365
+ columns: rel.fields.map((name) => columnFor(model, name)),
4366
+ refTable: modelTable(refModel),
4367
+ refColumns: (rel.references ?? []).map((name) => columnFor(refModel, name)),
4368
+ onDelete: rel.onDelete,
4369
+ onUpdate: rel.onUpdate
4370
+ });
4371
+ }
4372
+ return out;
4373
+ }
4374
+ function columnFor(model, fieldName) {
4375
+ const f = model.fields.find((x) => x.name === fieldName);
4376
+ return f ? fieldColumn(f) : fieldName.toUpperCase();
4377
+ }
4378
+ function sameColumnType(a, b) {
4379
+ if (a.type !== b.type) return false;
4380
+ return sameNative(a.nativeType, b.nativeType);
4381
+ }
4382
+ function sameNative(a, b) {
4383
+ if (!a && !b) return true;
4384
+ if (!a || !b) return true;
4385
+ return a.name === b.name && a.args.join(",") === b.args.join(",");
4386
+ }
4387
+ function setKey(cols) {
4388
+ return [...cols].sort().join(",");
4389
+ }
4390
+ function fkKey(fk) {
4391
+ return `${setKey(fk.columns)}->${fk.refTable}(${setKey(fk.refColumns)})`;
4392
+ }
4393
+ function constraintName(prefix, table, columns) {
4394
+ const base = `${prefix}_${table}_${columns.join("_")}`.replace(/[^A-Za-z0-9_]/g, "_");
4395
+ if (base.length <= 31) return base;
4396
+ let hash = 0;
4397
+ for (let i = 0; i < base.length; i++) hash = hash * 31 + base.charCodeAt(i) | 0;
4398
+ const suffix = Math.abs(hash).toString(36).slice(0, 6);
4399
+ return `${base.slice(0, 24)}_${suffix}`;
4400
+ }
4401
+
4402
+ // src/migrate/ddl.ts
4403
+ var FirebirdDdl = class {
4404
+ constructor(d) {
4405
+ this.d = d;
4406
+ }
4407
+ d;
4408
+ // ---- column types -------------------------------------------------------
4409
+ /** Firebird column type for a field, honoring its `@db.*` native type. */
4410
+ columnType(field) {
4411
+ const native = field.nativeType;
4412
+ if (native) {
4413
+ const args = native.args.length ? `(${native.args.join(", ")})` : "";
4414
+ switch (native.name) {
4415
+ case "VarChar":
4416
+ return `VARCHAR${args || "(255)"}`;
4417
+ case "Char":
4418
+ return `CHAR${args || "(1)"}`;
4419
+ case "Text":
4420
+ return "BLOB SUB_TYPE TEXT";
4421
+ case "SmallInt":
4422
+ return "SMALLINT";
4423
+ case "Integer":
4424
+ return "INTEGER";
4425
+ case "BigInt":
4426
+ return "BIGINT";
4427
+ case "Float":
4428
+ return "FLOAT";
4429
+ case "DoublePrecision":
4430
+ return "DOUBLE PRECISION";
4431
+ case "Decimal":
4432
+ return `DECIMAL${args || "(18, 4)"}`;
4433
+ case "Boolean":
4434
+ return this.d.booleanColumnType();
4435
+ case "Date":
4436
+ return "DATE";
4437
+ case "Time":
4438
+ return "TIME";
4439
+ case "Timestamp":
4440
+ return "TIMESTAMP";
4441
+ case "Blob":
4442
+ return "BLOB SUB_TYPE BINARY";
4443
+ }
4444
+ }
4445
+ if (field.type === "Boolean") return this.d.booleanColumnType();
4446
+ return DEFAULT_DDL_TYPE[field.type] ?? "VARCHAR(255)";
4447
+ }
4448
+ /** `"COL" <type> [GENERATED ...] [DEFAULT ...] [NOT NULL]`. */
4449
+ columnDefinition(field) {
4450
+ const parts = [this.d.quoteId(fieldColumn(field)), this.columnType(field)];
4451
+ if (isIdentity(field) && this.d.supportsIdentity) {
4452
+ parts.push("GENERATED BY DEFAULT AS IDENTITY");
4453
+ } else if (!isIdentity(field)) {
4454
+ const def = defaultClause(field.default);
4455
+ if (def) parts.push(def);
4456
+ }
4457
+ if (field.isRequired) parts.push("NOT NULL");
4458
+ return parts.join(" ");
4459
+ }
4460
+ /**
4461
+ * On Firebird 2.1/2.5 (no IDENTITY), emulate autoincrement with a SEQUENCE
4462
+ * and a BEFORE INSERT trigger. Returns the extra DDL objects to create after
4463
+ * the table; an empty array when IDENTITY is supported.
4464
+ */
4465
+ autoIncrementObjects(model) {
4466
+ if (this.d.supportsIdentity) return [];
4467
+ const table = modelTable(model);
4468
+ const out = [];
4469
+ for (const field of scalarFields(model)) {
4470
+ if (isIdentity(field)) {
4471
+ out.push(...this.autoIncrementForColumn(table, fieldColumn(field)));
4472
+ }
4473
+ }
4474
+ return out;
4475
+ }
4476
+ autoIncrementForColumn(table, column) {
4477
+ if (this.d.supportsIdentity) return [];
4478
+ const seq = capName(`GEN_${table}_${column}`);
4479
+ const trig = capName(`${table}_BI_${column}`);
4480
+ const qTable = this.d.quoteId(table);
4481
+ const qCol = this.d.quoteId(column);
4482
+ const qSeq = this.d.quoteId(seq);
4483
+ return [
4484
+ `CREATE SEQUENCE ${qSeq}`,
4485
+ `CREATE TRIGGER ${this.d.quoteId(trig)} FOR ${qTable} ACTIVE BEFORE INSERT POSITION 0 AS
4486
+ BEGIN
4487
+ IF (NEW.${qCol} IS NULL) THEN NEW.${qCol} = GEN_ID(${qSeq}, 1);
4488
+ END`
4489
+ ];
4490
+ }
4491
+ // ---- table-level --------------------------------------------------------
4492
+ createTable(model) {
4493
+ const cols = scalarFields(model).map((f) => " " + this.columnDefinition(f));
4494
+ const pk = idFields(model);
4495
+ const lines = [...cols];
4496
+ if (pk.length > 0) {
4497
+ lines.push(
4498
+ ` PRIMARY KEY (${pk.map((f) => this.d.quoteId(fieldColumn(f))).join(", ")})`
4499
+ );
4500
+ }
4501
+ return `CREATE TABLE ${this.d.quoteId(modelTable(model))} (
4502
+ ${lines.join(",\n")}
4503
+ )`;
4504
+ }
4505
+ dropTable(table) {
4506
+ return `DROP TABLE ${this.d.quoteId(table)}`;
4507
+ }
4508
+ addColumn(table, field) {
4509
+ return `ALTER TABLE ${this.d.quoteId(table)} ADD ${this.columnDefinition(field)}`;
4510
+ }
4511
+ dropColumn(table, column) {
4512
+ return `ALTER TABLE ${this.d.quoteId(table)} DROP ${this.d.quoteId(column)}`;
4513
+ }
4514
+ /** Change a column's data type (Firebird: ALTER COLUMN ... TYPE ...). */
4515
+ alterColumnType(table, field) {
4516
+ return `ALTER TABLE ${this.d.quoteId(table)} ALTER COLUMN ${this.d.quoteId(
4517
+ fieldColumn(field)
4518
+ )} TYPE ${this.columnType(field)}`;
4519
+ }
4520
+ setNotNull(table, column, notNull) {
4521
+ const action = notNull ? "SET NOT NULL" : "DROP NOT NULL";
4522
+ return `ALTER TABLE ${this.d.quoteId(table)} ALTER COLUMN ${this.d.quoteId(
4523
+ column
4524
+ )} ${action}`;
4525
+ }
4526
+ // ---- constraints & indexes ---------------------------------------------
4527
+ addUnique(table, name, columns) {
4528
+ return `ALTER TABLE ${this.d.quoteId(table)} ADD CONSTRAINT ${this.d.quoteId(
4529
+ name
4530
+ )} UNIQUE (${columns.map((c) => this.d.quoteId(c)).join(", ")})`;
4531
+ }
4532
+ dropConstraint(table, name) {
4533
+ return `ALTER TABLE ${this.d.quoteId(table)} DROP CONSTRAINT ${this.d.quoteId(name)}`;
4534
+ }
4535
+ addForeignKey(table, name, columns, refTable, refColumns, onDelete, onUpdate) {
4536
+ let sql = `ALTER TABLE ${this.d.quoteId(table)} ADD CONSTRAINT ${this.d.quoteId(
4537
+ name
4538
+ )} FOREIGN KEY (${columns.map((c) => this.d.quoteId(c)).join(", ")}) REFERENCES ${this.d.quoteId(
4539
+ refTable
4540
+ )} (${refColumns.map((c) => this.d.quoteId(c)).join(", ")})`;
4541
+ if (onUpdate) sql += ` ON UPDATE ${referentialSql(onUpdate)}`;
4542
+ if (onDelete) sql += ` ON DELETE ${referentialSql(onDelete)}`;
4543
+ return sql;
4544
+ }
4545
+ createIndex(table, name, columns, unique) {
4546
+ return `CREATE ${unique ? "UNIQUE " : ""}INDEX ${this.d.quoteId(name)} ON ${this.d.quoteId(
4547
+ table
4548
+ )} (${columns.map((c) => this.d.quoteId(c)).join(", ")})`;
4549
+ }
4550
+ dropIndex(name) {
4551
+ return `DROP INDEX ${this.d.quoteId(name)}`;
4552
+ }
4553
+ };
4554
+ var DEFAULT_DDL_TYPE = {
4555
+ String: "VARCHAR(255)",
4556
+ Boolean: "BOOLEAN",
4557
+ Int: "INTEGER",
4558
+ BigInt: "BIGINT",
4559
+ Float: "DOUBLE PRECISION",
4560
+ Decimal: "DECIMAL(18, 4)",
4561
+ DateTime: "TIMESTAMP",
4562
+ Bytes: "BLOB SUB_TYPE BINARY",
4563
+ Json: "BLOB SUB_TYPE TEXT"
4564
+ };
4565
+ function isIdentity(field) {
4566
+ return field.default?.function?.name === "autoincrement";
4567
+ }
4568
+ function capName(name) {
4569
+ const safe = name.replace(/[^A-Za-z0-9_]/g, "_");
4570
+ if (safe.length <= 31) return safe;
4571
+ let hash = 0;
4572
+ for (let i = 0; i < safe.length; i++) hash = hash * 31 + safe.charCodeAt(i) | 0;
4573
+ return `${safe.slice(0, 24)}_${Math.abs(hash).toString(36).slice(0, 6)}`;
4574
+ }
4575
+ function defaultClause(def) {
4576
+ if (!def) return null;
4577
+ if (def.function) {
4578
+ switch (def.function.name) {
4579
+ case "now":
4580
+ return "DEFAULT CURRENT_TIMESTAMP";
4581
+ case "uuid":
4582
+ case "cuid":
4583
+ return null;
4584
+ // generated app-side
4585
+ default:
4586
+ return null;
4587
+ }
4588
+ }
4589
+ if (def.literal === void 0) return null;
4590
+ if (typeof def.literal === "boolean") {
4591
+ return `DEFAULT ${def.literal ? "TRUE" : "FALSE"}`;
4592
+ }
4593
+ if (typeof def.literal === "number") return `DEFAULT ${def.literal}`;
4594
+ return `DEFAULT '${String(def.literal).replace(/'/g, "''")}'`;
4595
+ }
4596
+ function referentialSql(action) {
4597
+ switch (action) {
4598
+ case "Cascade":
4599
+ return "CASCADE";
4600
+ case "SetNull":
4601
+ return "SET NULL";
4602
+ case "SetDefault":
4603
+ return "SET DEFAULT";
4604
+ case "NoAction":
4605
+ return "NO ACTION";
4606
+ case "Restrict":
4607
+ return "NO ACTION";
4608
+ // Firebird has no RESTRICT; NO ACTION is closest
4609
+ default:
4610
+ return "NO ACTION";
4611
+ }
4612
+ }
4613
+
4614
+ // src/migrate/planner.ts
4615
+ function planMigration(diff, desired, dialect) {
4616
+ const ddl = new FirebirdDdl(dialect);
4617
+ const drops = [];
4618
+ const creates = [];
4619
+ const alters = [];
4620
+ const constraints = [];
4621
+ const indexes = [];
4622
+ const foreignKeyStmts = [];
4623
+ for (const change of diff.modelChanges) {
4624
+ for (const col of change.droppedColumns) {
4625
+ drops.push(ddl.dropColumn(change.table, col));
4626
+ }
4627
+ }
4628
+ for (const table of diff.droppedTables) {
4629
+ drops.push(ddl.dropTable(table));
4630
+ }
4631
+ for (const model of diff.createdModels) {
4632
+ const table = modelTable(model);
4633
+ creates.push(ddl.createTable(model));
4634
+ creates.push(...ddl.autoIncrementObjects(model));
4635
+ for (const cols of uniqueColumnSets(model)) {
4636
+ if (cols.length === 1) continue;
4637
+ constraints.push(
4638
+ ddl.addUnique(table, constraintName("UQ", table, cols), cols)
4639
+ );
4640
+ }
4641
+ for (const idx of indexSpecs(model)) {
4642
+ indexes.push(ddl.createIndex(table, idx.name, idx.columns, idx.unique));
4643
+ }
4644
+ for (const fk of foreignKeys(desired, model)) {
4645
+ foreignKeyStmts.push(
4646
+ ddl.addForeignKey(
4647
+ table,
4648
+ constraintName("FK", table, fk.columns),
4649
+ fk.columns,
4650
+ fk.refTable,
4651
+ fk.refColumns,
4652
+ fk.onDelete,
4653
+ fk.onUpdate
4654
+ )
4655
+ );
4656
+ }
4657
+ }
4658
+ for (const change of diff.modelChanges) {
4659
+ for (const field of change.addedColumns) {
4660
+ alters.push(ddl.addColumn(change.table, field));
4661
+ if (isIdentity(field)) {
4662
+ creates.push(
4663
+ ...ddl.autoIncrementForColumn(change.table, fieldColumn(field))
4664
+ );
4665
+ }
4666
+ }
4667
+ for (const cc of change.changedColumns) {
4668
+ if (cc.typeChanged) alters.push(ddl.alterColumnType(cc.table, cc.field));
4669
+ if (cc.nullabilityChanged) {
4670
+ alters.push(
4671
+ ddl.setNotNull(cc.table, columnName(cc), cc.field.isRequired)
4672
+ );
4673
+ }
4674
+ }
4675
+ for (const uq of change.addedUniques) {
4676
+ if (uq.columns.length === 1) {
4677
+ constraints.push(ddl.addUnique(change.table, uq.name, uq.columns));
4678
+ } else {
4679
+ constraints.push(ddl.addUnique(change.table, uq.name, uq.columns));
4680
+ }
4681
+ }
4682
+ for (const fk of change.addedForeignKeys) {
4683
+ foreignKeyStmts.push(
4684
+ ddl.addForeignKey(
4685
+ change.table,
4686
+ fk.name,
4687
+ fk.columns,
4688
+ fk.refTable,
4689
+ fk.refColumns,
4690
+ fk.onDelete,
4691
+ fk.onUpdate
4692
+ )
4693
+ );
4694
+ }
4695
+ }
4696
+ return [
4697
+ ...drops,
4698
+ ...creates,
4699
+ ...alters,
4700
+ ...constraints,
4701
+ ...indexes,
4702
+ ...foreignKeyStmts
4703
+ ];
4704
+ }
4705
+ var BREAKPOINT = "--> statement-breakpoint";
4706
+ function renderMigrationSql(statements) {
4707
+ if (statements.length === 0) return "-- This migration is empty.\n";
4708
+ return statements.join(`
4709
+ ${BREAKPOINT}
4710
+ `) + "\n";
4711
+ }
4712
+ function splitStatements(sql) {
4713
+ return sql.split(BREAKPOINT).map((s) => stripComments(s).trim().replace(/;\s*$/, "").trim()).filter((s) => s.length > 0);
4714
+ }
4715
+ function stripComments(sql) {
4716
+ return sql.split("\n").filter((line) => !line.trim().startsWith("--")).join("\n");
4717
+ }
4718
+ function columnName(cc) {
4719
+ return cc.field.dbName ?? cc.field.name.toUpperCase();
4720
+ }
4721
+
4722
+ // src/migrate/history.ts
4723
+ import { existsSync as existsSync2, readFileSync as readFileSync2, readdirSync } from "fs";
4724
+ import { join } from "path";
4725
+ var HISTORY_TABLE = "_EMBER_MIGRATIONS";
4726
+ async function ensureHistoryTable(tx, dialect) {
4727
+ const exists = await tx.query(
4728
+ `SELECT COUNT(*) AS N FROM RDB$RELATIONS WHERE RDB$RELATION_NAME = ?`,
4729
+ [HISTORY_TABLE]
4730
+ );
4731
+ if (Number(exists[0]?.N ?? 0) > 0) return;
4732
+ const t = dialect.quoteId(HISTORY_TABLE);
4733
+ await tx.query(
4734
+ `CREATE TABLE ${t} (
4735
+ ${dialect.quoteId("ID")} VARCHAR(128) NOT NULL PRIMARY KEY,
4736
+ ${dialect.quoteId("CHECKSUM")} VARCHAR(64),
4737
+ ${dialect.quoteId("STEPS")} INTEGER,
4738
+ ${dialect.quoteId("APPLIED_AT")} TIMESTAMP DEFAULT CURRENT_TIMESTAMP
4739
+ )`
4740
+ );
4741
+ }
4742
+ async function appliedMigrations(tx, dialect) {
4743
+ const t = dialect.quoteId(HISTORY_TABLE);
4744
+ const rows = await tx.query(
4745
+ `SELECT ${dialect.quoteId("ID")} AS "id", ${dialect.quoteId("CHECKSUM")} AS "checksum", ${dialect.quoteId("STEPS")} AS "steps" FROM ${t} ORDER BY ${dialect.quoteId("ID")}`
4746
+ );
4747
+ return rows.map((r) => ({
4748
+ id: String(r.id).trim(),
4749
+ checksum: r.checksum == null ? "" : String(r.checksum).trim(),
4750
+ steps: Number(r.steps ?? 0)
4751
+ }));
4752
+ }
4753
+ async function recordMigration(tx, dialect, migration) {
4754
+ const t = dialect.quoteId(HISTORY_TABLE);
4755
+ await tx.query(
4756
+ `INSERT INTO ${t} (${dialect.quoteId("ID")}, ${dialect.quoteId("CHECKSUM")}, ${dialect.quoteId("STEPS")}) VALUES (?, ?, ?)`,
4757
+ [migration.id, migration.checksum, migration.steps]
4758
+ );
4759
+ }
4760
+ function listLocalMigrations(migrationsDir) {
4761
+ if (!existsSync2(migrationsDir)) return [];
4762
+ return readdirSync(migrationsDir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name).sort().map((id) => {
4763
+ const dir = join(migrationsDir, id);
4764
+ const file = join(dir, "migration.sql");
4765
+ return {
4766
+ id,
4767
+ dir,
4768
+ sql: existsSync2(file) ? readFileSync2(file, "utf8") : ""
4769
+ };
4770
+ }).filter((m) => m.sql.length > 0);
4771
+ }
4772
+ function checksum(sql) {
4773
+ let h = 2166136261;
4774
+ for (let i = 0; i < sql.length; i++) {
4775
+ h ^= sql.charCodeAt(i);
4776
+ h = Math.imul(h, 16777619);
4777
+ }
4778
+ return (h >>> 0).toString(16).padStart(8, "0");
4779
+ }
4780
+
4781
+ // src/migrate/index.ts
4782
+ var Migrator = class {
4783
+ constructor(driver, desired, migrationsDir, dialect) {
4784
+ this.driver = driver;
4785
+ this.desired = desired;
4786
+ this.migrationsDir = migrationsDir;
4787
+ this.dialect = dialect ?? new FirebirdDialect();
4788
+ }
4789
+ driver;
4790
+ desired;
4791
+ migrationsDir;
4792
+ dialect;
4793
+ /** Compute the diff between the desired schema and the live database. */
4794
+ async diff() {
4795
+ const current = await this.currentSchema();
4796
+ return diffSchemas(this.desired, current);
4797
+ }
4798
+ /** Plan (but do not apply) the DDL needed to reach the desired schema. */
4799
+ async plan() {
4800
+ const diff = await this.diff();
4801
+ return planMigration(diff, this.desired, this.dialect);
4802
+ }
4803
+ /**
4804
+ * `migrate dev`: create a timestamped migration from the current diff, apply
4805
+ * it, and record it in history.
4806
+ */
4807
+ async dev(name = "migration") {
4808
+ const statements = await this.plan();
4809
+ if (statements.length === 0) return { empty: true, statements: [] };
4810
+ const id = `${timestamp()}_${slug(name)}`;
4811
+ const dir = join2(this.migrationsDir, id);
4812
+ const body = renderMigrationSql(statements);
4813
+ mkdirSync2(dir, { recursive: true });
4814
+ writeFileSync2(join2(dir, "migration.sql"), body, "utf8");
4815
+ await this.driver.transaction(async (tx) => {
4816
+ await ensureHistoryTable(tx, this.dialect);
4817
+ for (const stmt of statements) await tx.query(stmt);
4818
+ await recordMigration(tx, this.dialect, {
4819
+ id,
4820
+ checksum: checksum(body),
4821
+ steps: statements.length
4822
+ });
4823
+ });
4824
+ return { empty: false, id, dir, statements };
4825
+ }
4826
+ /**
4827
+ * `db push`: apply the diff directly to the database without writing a
4828
+ * migration file (prototyping flow).
4829
+ */
4830
+ async push() {
4831
+ const statements = await this.plan();
4832
+ if (statements.length === 0) return { statements: [] };
4833
+ await this.driver.transaction(async (tx) => {
4834
+ for (const stmt of statements) await tx.query(stmt);
4835
+ });
4836
+ return { statements };
4837
+ }
4838
+ /** `migrate deploy`: apply every on-disk migration not yet recorded. */
4839
+ async deploy() {
4840
+ const local = listLocalMigrations(this.migrationsDir);
4841
+ const applied = [];
4842
+ const known = await this.driver.transaction(async (tx) => {
4843
+ await ensureHistoryTable(tx, this.dialect);
4844
+ return new Set((await appliedMigrations(tx, this.dialect)).map((m) => m.id));
4845
+ });
4846
+ for (const migration of local) {
4847
+ if (known.has(migration.id)) continue;
4848
+ const statements = splitStatements(migration.sql);
4849
+ await this.driver.transaction(async (tx) => {
4850
+ for (const stmt of statements) await tx.query(stmt);
4851
+ await recordMigration(tx, this.dialect, {
4852
+ id: migration.id,
4853
+ checksum: checksum(migration.sql),
4854
+ steps: statements.length
4855
+ });
4856
+ });
4857
+ applied.push({ id: migration.id, steps: statements.length });
4858
+ }
4859
+ return { applied };
4860
+ }
4861
+ /** `migrate status`: list applied vs pending migrations. */
4862
+ async status() {
4863
+ const local = listLocalMigrations(this.migrationsDir).map((m) => m.id);
4864
+ const applied = await this.driver.transaction(async (tx) => {
4865
+ await ensureHistoryTable(tx, this.dialect);
4866
+ return (await appliedMigrations(tx, this.dialect)).map((m) => m.id);
4867
+ });
4868
+ const appliedSet = new Set(applied);
4869
+ return {
4870
+ applied,
4871
+ pending: local.filter((id) => !appliedSet.has(id))
4872
+ };
4873
+ }
4874
+ async currentSchema() {
4875
+ const introspector = new Introspector(this.driver);
4876
+ const current = await introspector.introspect();
4877
+ current.models = current.models.filter(
4878
+ (m) => modelTable(m) !== HISTORY_TABLE
4879
+ );
4880
+ return current;
4881
+ }
4882
+ };
4883
+ function timestamp() {
4884
+ const d = /* @__PURE__ */ new Date();
4885
+ const p = (n, w = 2) => String(n).padStart(w, "0");
4886
+ return `${d.getUTCFullYear()}${p(d.getUTCMonth() + 1)}${p(d.getUTCDate())}${p(d.getUTCHours())}${p(d.getUTCMinutes())}${p(d.getUTCSeconds())}`;
4887
+ }
4888
+ function slug(name) {
4889
+ return name.toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_|_$/g, "") || "migration";
4890
+ }
4891
+ export {
4892
+ ClientGenerator,
4893
+ DatabaseError,
4894
+ EmberClientBase,
4895
+ EmberError,
4896
+ FirebirdDdl,
4897
+ FirebirdDialect,
4898
+ FirebirdDriver,
4899
+ Introspector,
4900
+ Migrator,
4901
+ QueryEngine,
4902
+ QueryValidationError,
4903
+ RecordNotFoundError,
4904
+ SCALAR_TYPES,
4905
+ SchemaParseError,
4906
+ SchemaValidationError,
4907
+ Sql,
4908
+ UniqueConstraintError,
4909
+ buildConnectionUrl,
4910
+ createClient,
4911
+ createDriver,
4912
+ diffSchemas,
4913
+ emptySchema,
4914
+ fieldColumn,
4915
+ findEnum,
4916
+ findModel,
4917
+ findSchemaPath,
4918
+ generateClientSource,
4919
+ idFields,
4920
+ loadSchema,
4921
+ modelTable,
4922
+ parseAndValidate,
4923
+ parseConnectionUrl,
4924
+ parseSchema,
4925
+ planMigration,
4926
+ printSchema,
4927
+ relationFields,
4928
+ renderMigrationSql,
4929
+ resolveDatasourceUrl,
4930
+ scalarFields,
4931
+ splitStatements,
4932
+ validateSchema,
4933
+ writeClient
4934
+ };
4935
+ //# sourceMappingURL=index.js.map