@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.
@@ -0,0 +1,3265 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ var __create = Object.create;
4
+ var __defProp = Object.defineProperty;
5
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
+ var __getOwnPropNames = Object.getOwnPropertyNames;
7
+ var __getProtoOf = Object.getPrototypeOf;
8
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
9
+ var __copyProps = (to, from, except, desc) => {
10
+ if (from && typeof from === "object" || typeof from === "function") {
11
+ for (let key of __getOwnPropNames(from))
12
+ if (!__hasOwnProp.call(to, key) && key !== except)
13
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
14
+ }
15
+ return to;
16
+ };
17
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
18
+ // If the importer is in node compatibility mode or this is not an ESM
19
+ // file that has been converted to a CommonJS file using a Babel-
20
+ // compatible transform (i.e. "__esModule" has not been set), then set
21
+ // "default" to the CommonJS "module.exports" for node compatibility.
22
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
23
+ mod
24
+ ));
25
+
26
+ // src/cli/bin.ts
27
+ var import_node_fs7 = require("fs");
28
+ var import_node_url = require("url");
29
+
30
+ // src/errors/index.ts
31
+ var EmberError = class extends Error {
32
+ constructor(message) {
33
+ super(message);
34
+ this.name = new.target.name;
35
+ Object.setPrototypeOf(this, new.target.prototype);
36
+ }
37
+ };
38
+ var SchemaParseError = class extends EmberError {
39
+ constructor(message, line, column, file) {
40
+ super(
41
+ `${message} (at ${file ? `${file}:` : ""}${line}:${column})`
42
+ );
43
+ this.line = line;
44
+ this.column = column;
45
+ this.file = file;
46
+ }
47
+ line;
48
+ column;
49
+ file;
50
+ };
51
+ var SchemaValidationError = class extends EmberError {
52
+ constructor(message, details = []) {
53
+ super(
54
+ details.length > 0 ? `${message}
55
+ - ${details.join("\n - ")}` : message
56
+ );
57
+ this.details = details;
58
+ }
59
+ details;
60
+ };
61
+ var DatabaseError = class extends EmberError {
62
+ constructor(message, cause, sql) {
63
+ super(message);
64
+ this.cause = cause;
65
+ this.sql = sql;
66
+ }
67
+ cause;
68
+ sql;
69
+ };
70
+
71
+ // src/cli/commands.ts
72
+ var import_node_fs5 = require("fs");
73
+ var import_node_path5 = require("path");
74
+
75
+ // src/schema/index.ts
76
+ var import_node_fs = require("fs");
77
+ var import_node_path = require("path");
78
+
79
+ // src/schema/lexer.ts
80
+ var SINGLE_CHAR_TOKENS = {
81
+ "{": "lbrace",
82
+ "}": "rbrace",
83
+ "(": "lparen",
84
+ ")": "rparen",
85
+ "[": "lbracket",
86
+ "]": "rbracket",
87
+ "=": "equals",
88
+ ",": "comma",
89
+ ":": "colon",
90
+ "?": "question",
91
+ ".": "dot"
92
+ };
93
+ var IDENT_START = /[A-Za-z_]/;
94
+ var IDENT_PART = /[A-Za-z0-9_]/;
95
+ var Lexer = class {
96
+ constructor(source, file) {
97
+ this.source = source;
98
+ this.file = file;
99
+ }
100
+ source;
101
+ file;
102
+ pos = 0;
103
+ line = 1;
104
+ column = 1;
105
+ tokenize() {
106
+ const tokens = [];
107
+ let token = this.next();
108
+ while (token.type !== "eof") {
109
+ tokens.push(token);
110
+ token = this.next();
111
+ }
112
+ tokens.push(token);
113
+ return tokens;
114
+ }
115
+ next() {
116
+ this.skipWhitespaceAndComments();
117
+ if (this.pos >= this.source.length) {
118
+ return this.make("eof", "");
119
+ }
120
+ const startLine = this.line;
121
+ const startColumn = this.column;
122
+ const ch = this.source[this.pos];
123
+ if (ch === "/" && this.peek(1) === "/" && this.peek(2) === "/") {
124
+ return this.readDocComment(startLine, startColumn);
125
+ }
126
+ if (ch === '"') {
127
+ return this.readString(startLine, startColumn);
128
+ }
129
+ if (ch === "@") {
130
+ this.advance();
131
+ if (this.source[this.pos] === "@") {
132
+ this.advance();
133
+ return { type: "double_at", value: "@@", line: startLine, column: startColumn };
134
+ }
135
+ return { type: "at", value: "@", line: startLine, column: startColumn };
136
+ }
137
+ const single = SINGLE_CHAR_TOKENS[ch];
138
+ if (single) {
139
+ this.advance();
140
+ return { type: single, value: ch, line: startLine, column: startColumn };
141
+ }
142
+ if (ch === "-" || /[0-9]/.test(ch)) {
143
+ return this.readNumber(startLine, startColumn);
144
+ }
145
+ if (IDENT_START.test(ch)) {
146
+ return this.readIdentifier(startLine, startColumn);
147
+ }
148
+ throw new SchemaParseError(
149
+ `Unexpected character '${ch}'`,
150
+ startLine,
151
+ startColumn,
152
+ this.file
153
+ );
154
+ }
155
+ readDocComment(line, column) {
156
+ this.advance();
157
+ this.advance();
158
+ this.advance();
159
+ let value = "";
160
+ while (this.pos < this.source.length && this.source[this.pos] !== "\n") {
161
+ value += this.source[this.pos];
162
+ this.advance();
163
+ }
164
+ return { type: "doc_comment", value: value.trim(), line, column };
165
+ }
166
+ readString(line, column) {
167
+ this.advance();
168
+ let value = "";
169
+ while (this.pos < this.source.length && this.source[this.pos] !== '"') {
170
+ const c = this.source[this.pos];
171
+ if (c === "\\") {
172
+ this.advance();
173
+ const escaped = this.source[this.pos];
174
+ if (escaped === void 0) break;
175
+ value += escapeChar(escaped);
176
+ this.advance();
177
+ continue;
178
+ }
179
+ if (c === "\n") {
180
+ throw new SchemaParseError(
181
+ "Unterminated string literal",
182
+ line,
183
+ column,
184
+ this.file
185
+ );
186
+ }
187
+ value += c;
188
+ this.advance();
189
+ }
190
+ if (this.source[this.pos] !== '"') {
191
+ throw new SchemaParseError(
192
+ "Unterminated string literal",
193
+ line,
194
+ column,
195
+ this.file
196
+ );
197
+ }
198
+ this.advance();
199
+ return { type: "string", value, line, column };
200
+ }
201
+ readNumber(line, column) {
202
+ let value = "";
203
+ if (this.source[this.pos] === "-") {
204
+ value += "-";
205
+ this.advance();
206
+ }
207
+ while (this.pos < this.source.length && /[0-9.]/.test(this.source[this.pos])) {
208
+ value += this.source[this.pos];
209
+ this.advance();
210
+ }
211
+ return { type: "number", value, line, column };
212
+ }
213
+ readIdentifier(line, column) {
214
+ let value = "";
215
+ while (this.pos < this.source.length && IDENT_PART.test(this.source[this.pos])) {
216
+ value += this.source[this.pos];
217
+ this.advance();
218
+ }
219
+ return { type: "identifier", value, line, column };
220
+ }
221
+ skipWhitespaceAndComments() {
222
+ while (this.pos < this.source.length) {
223
+ const ch = this.source[this.pos];
224
+ if (ch === " " || ch === " " || ch === "\r" || ch === "\n") {
225
+ this.advance();
226
+ continue;
227
+ }
228
+ if (ch === "/" && this.peek(1) === "/" && this.peek(2) !== "/") {
229
+ while (this.pos < this.source.length && this.source[this.pos] !== "\n") {
230
+ this.advance();
231
+ }
232
+ continue;
233
+ }
234
+ if (ch === "/" && this.peek(1) === "*") {
235
+ this.advance();
236
+ this.advance();
237
+ while (this.pos < this.source.length && !(this.source[this.pos] === "*" && this.peek(1) === "/")) {
238
+ this.advance();
239
+ }
240
+ this.advance();
241
+ this.advance();
242
+ continue;
243
+ }
244
+ break;
245
+ }
246
+ }
247
+ peek(offset) {
248
+ return this.source[this.pos + offset];
249
+ }
250
+ advance() {
251
+ if (this.source[this.pos] === "\n") {
252
+ this.line++;
253
+ this.column = 1;
254
+ } else {
255
+ this.column++;
256
+ }
257
+ this.pos++;
258
+ }
259
+ make(type, value) {
260
+ return { type, value, line: this.line, column: this.column };
261
+ }
262
+ };
263
+ function escapeChar(c) {
264
+ switch (c) {
265
+ case "n":
266
+ return "\n";
267
+ case "t":
268
+ return " ";
269
+ case "r":
270
+ return "\r";
271
+ case '"':
272
+ return '"';
273
+ case "\\":
274
+ return "\\";
275
+ default:
276
+ return c;
277
+ }
278
+ }
279
+
280
+ // src/schema/parser.ts
281
+ var Parser = class {
282
+ constructor(source, file) {
283
+ this.file = file;
284
+ this.tokens = new Lexer(source, file).tokenize();
285
+ }
286
+ file;
287
+ tokens;
288
+ pos = 0;
289
+ parse() {
290
+ const doc = { generators: [], models: [], enums: [] };
291
+ let pendingDoc = [];
292
+ while (!this.isEof()) {
293
+ const tok = this.peek();
294
+ if (tok.type === "doc_comment") {
295
+ pendingDoc.push(tok.value);
296
+ this.advance();
297
+ continue;
298
+ }
299
+ if (tok.type !== "identifier") {
300
+ throw this.error(`Unexpected token '${tok.value}'`, tok);
301
+ }
302
+ const documentation = pendingDoc.length ? pendingDoc.join("\n") : void 0;
303
+ pendingDoc = [];
304
+ switch (tok.value) {
305
+ case "datasource":
306
+ doc.datasource = this.parseDatasource();
307
+ break;
308
+ case "generator":
309
+ doc.generators.push(this.parseGenerator());
310
+ break;
311
+ case "model":
312
+ doc.models.push(this.parseModel(documentation));
313
+ break;
314
+ case "enum":
315
+ doc.enums.push(this.parseEnum(documentation));
316
+ break;
317
+ default:
318
+ throw this.error(`Unknown top-level keyword '${tok.value}'`, tok);
319
+ }
320
+ }
321
+ resolveKinds(doc);
322
+ return doc;
323
+ }
324
+ // ---- Top-level blocks -------------------------------------------------
325
+ parseDatasource() {
326
+ this.expectKeyword("datasource");
327
+ const name = this.expect("identifier").value;
328
+ this.expect("lbrace");
329
+ const assignments = this.parseAssignments();
330
+ this.expect("rbrace");
331
+ const provider = literalString(assignments["provider"]);
332
+ const urlAssign = assignments["url"];
333
+ let url = { kind: "literal", value: "" };
334
+ if (urlAssign) {
335
+ if (urlAssign.kind === "function" && urlAssign.name === "env") {
336
+ url = { kind: "env", value: literalString(urlAssign.args[0]) };
337
+ } else if (urlAssign.kind === "string") {
338
+ url = { kind: "literal", value: urlAssign.value };
339
+ }
340
+ }
341
+ return { name, provider: provider ?? "firebird", url };
342
+ }
343
+ parseGenerator() {
344
+ this.expectKeyword("generator");
345
+ const name = this.expect("identifier").value;
346
+ this.expect("lbrace");
347
+ const assignments = this.parseAssignments();
348
+ this.expect("rbrace");
349
+ const config = {};
350
+ for (const [key, value] of Object.entries(assignments)) {
351
+ if (value.kind === "string") config[key] = value.value;
352
+ }
353
+ return {
354
+ name,
355
+ provider: literalString(assignments["provider"]) ?? "ember-client-js",
356
+ output: assignments["output"] ? literalString(assignments["output"]) : void 0,
357
+ config
358
+ };
359
+ }
360
+ parseAssignments() {
361
+ const out = {};
362
+ while (!this.check("rbrace") && !this.isEof()) {
363
+ if (this.check("doc_comment")) {
364
+ this.advance();
365
+ continue;
366
+ }
367
+ const key = this.expect("identifier").value;
368
+ this.expect("equals");
369
+ out[key] = this.parseValue();
370
+ }
371
+ return out;
372
+ }
373
+ parseModel(documentation) {
374
+ this.expectKeyword("model");
375
+ const name = this.expect("identifier").value;
376
+ this.expect("lbrace");
377
+ const model = {
378
+ name,
379
+ fields: [],
380
+ primaryKey: [],
381
+ uniqueIndexes: [],
382
+ indexes: [],
383
+ documentation
384
+ };
385
+ let pendingDoc = [];
386
+ while (!this.check("rbrace") && !this.isEof()) {
387
+ const tok = this.peek();
388
+ if (tok.type === "doc_comment") {
389
+ pendingDoc.push(tok.value);
390
+ this.advance();
391
+ continue;
392
+ }
393
+ if (tok.type === "double_at") {
394
+ this.parseBlockAttribute(model);
395
+ pendingDoc = [];
396
+ continue;
397
+ }
398
+ const field = this.parseField(
399
+ pendingDoc.length ? pendingDoc.join("\n") : void 0
400
+ );
401
+ pendingDoc = [];
402
+ model.fields.push(field);
403
+ if (field.isId && !model.primaryKey.includes(field.name)) {
404
+ model.primaryKey.push(field.name);
405
+ }
406
+ }
407
+ this.expect("rbrace");
408
+ return model;
409
+ }
410
+ parseField(documentation) {
411
+ const name = this.expect("identifier").value;
412
+ const typeName = this.expect("identifier").value;
413
+ let isList = false;
414
+ let isRequired = true;
415
+ while (this.check("lbracket") || this.check("question")) {
416
+ if (this.check("lbracket")) {
417
+ this.advance();
418
+ this.expect("rbracket");
419
+ isList = true;
420
+ } else {
421
+ this.advance();
422
+ isRequired = false;
423
+ }
424
+ }
425
+ const field = {
426
+ name,
427
+ type: typeName,
428
+ kind: "scalar",
429
+ // resolved later
430
+ isList,
431
+ isRequired,
432
+ isId: false,
433
+ isUnique: false,
434
+ isUpdatedAt: false,
435
+ documentation
436
+ };
437
+ while (this.check("at")) {
438
+ const attr = this.parseAttribute();
439
+ this.applyFieldAttribute(field, attr);
440
+ }
441
+ return field;
442
+ }
443
+ applyFieldAttribute(field, attr) {
444
+ if (attr.name.startsWith("db.")) {
445
+ field.nativeType = toNativeType(attr);
446
+ return;
447
+ }
448
+ switch (attr.name) {
449
+ case "id":
450
+ field.isId = true;
451
+ break;
452
+ case "unique":
453
+ field.isUnique = true;
454
+ break;
455
+ case "updatedAt":
456
+ field.isUpdatedAt = true;
457
+ break;
458
+ case "default":
459
+ field.default = toDefaultValue(attr.args[0]);
460
+ break;
461
+ case "map":
462
+ field.dbName = literalString(attr.args[0]);
463
+ break;
464
+ case "relation":
465
+ field.relation = toRelationInfo(attr.args);
466
+ break;
467
+ default:
468
+ throw this.error(`Unknown field attribute '@${attr.name}'`, attr);
469
+ }
470
+ }
471
+ parseBlockAttribute(model) {
472
+ const start = this.expect("double_at");
473
+ const name = this.expect("identifier").value;
474
+ const args = this.check("lparen") ? this.parseArgList() : [];
475
+ switch (name) {
476
+ case "id":
477
+ model.primaryKey = fieldNameList(args);
478
+ break;
479
+ case "unique":
480
+ model.uniqueIndexes.push({
481
+ fields: fieldNameList(args),
482
+ name: namedArg(args, "map")
483
+ });
484
+ break;
485
+ case "index":
486
+ model.indexes.push({
487
+ fields: fieldNameList(args),
488
+ name: namedArg(args, "map"),
489
+ unique: false
490
+ });
491
+ break;
492
+ case "map":
493
+ model.dbName = literalString(args[0]);
494
+ break;
495
+ default:
496
+ throw this.error(`Unknown block attribute '@@${name}'`, start);
497
+ }
498
+ }
499
+ parseEnum(documentation) {
500
+ this.expectKeyword("enum");
501
+ const name = this.expect("identifier").value;
502
+ this.expect("lbrace");
503
+ const node = { name, values: [], documentation };
504
+ while (!this.check("rbrace") && !this.isEof()) {
505
+ if (this.check("doc_comment")) {
506
+ this.advance();
507
+ continue;
508
+ }
509
+ if (this.check("double_at")) {
510
+ this.advance();
511
+ const attrName = this.expect("identifier").value;
512
+ const args = this.check("lparen") ? this.parseArgList() : [];
513
+ if (attrName === "map") node.dbName = literalString(args[0]);
514
+ continue;
515
+ }
516
+ const valueName = this.expect("identifier").value;
517
+ let dbName;
518
+ while (this.check("at")) {
519
+ const attr = this.parseAttribute();
520
+ if (attr.name === "map") dbName = literalString(attr.args[0]);
521
+ }
522
+ node.values.push({ name: valueName, dbName });
523
+ }
524
+ this.expect("rbrace");
525
+ return node;
526
+ }
527
+ // ---- Attributes & values ---------------------------------------------
528
+ parseAttribute() {
529
+ const at = this.expect("at");
530
+ let name = this.expect("identifier").value;
531
+ if (this.check("dot")) {
532
+ this.advance();
533
+ name += "." + this.expect("identifier").value;
534
+ }
535
+ const args = this.check("lparen") ? this.parseArgList() : [];
536
+ return { name, args, line: at.line, column: at.column };
537
+ }
538
+ parseArgList() {
539
+ this.expect("lparen");
540
+ const args = [];
541
+ while (!this.check("rparen") && !this.isEof()) {
542
+ args.push(this.parseArg());
543
+ if (this.check("comma")) this.advance();
544
+ }
545
+ this.expect("rparen");
546
+ return args;
547
+ }
548
+ /** Handles both positional values and `name: value` named arguments. */
549
+ parseArg() {
550
+ if (this.check("identifier") && this.peek(1)?.type === "colon") {
551
+ const key = this.expect("identifier").value;
552
+ this.expect("colon");
553
+ const value = this.parseValue();
554
+ return { kind: "function", name: `__named:${key}`, args: [value] };
555
+ }
556
+ return this.parseValue();
557
+ }
558
+ parseValue() {
559
+ const tok = this.peek();
560
+ switch (tok.type) {
561
+ case "string":
562
+ this.advance();
563
+ return { kind: "string", value: tok.value };
564
+ case "number":
565
+ this.advance();
566
+ return { kind: "number", value: Number(tok.value) };
567
+ case "lbracket": {
568
+ this.advance();
569
+ const items = [];
570
+ while (!this.check("rbracket") && !this.isEof()) {
571
+ items.push(this.parseValue());
572
+ if (this.check("comma")) this.advance();
573
+ }
574
+ this.expect("rbracket");
575
+ return { kind: "array", items };
576
+ }
577
+ case "identifier": {
578
+ this.advance();
579
+ if (tok.value === "true" || tok.value === "false") {
580
+ return { kind: "boolean", value: tok.value === "true" };
581
+ }
582
+ if (this.check("lparen")) {
583
+ const args = this.parseArgList();
584
+ return { kind: "function", name: tok.value, args };
585
+ }
586
+ return { kind: "ref", value: tok.value };
587
+ }
588
+ default:
589
+ throw this.error(`Unexpected value token '${tok.value}'`, tok);
590
+ }
591
+ }
592
+ // ---- Token helpers ----------------------------------------------------
593
+ peek(offset = 0) {
594
+ return this.tokens[this.pos + offset] ?? this.tokens[this.tokens.length - 1];
595
+ }
596
+ advance() {
597
+ const tok = this.tokens[this.pos];
598
+ if (this.pos < this.tokens.length - 1) this.pos++;
599
+ return tok;
600
+ }
601
+ check(type) {
602
+ return this.peek().type === type;
603
+ }
604
+ isEof() {
605
+ return this.peek().type === "eof";
606
+ }
607
+ expect(type) {
608
+ const tok = this.peek();
609
+ if (tok.type !== type) {
610
+ throw this.error(`Expected ${type} but found '${tok.value}' (${tok.type})`, tok);
611
+ }
612
+ return this.advance();
613
+ }
614
+ expectKeyword(keyword) {
615
+ const tok = this.peek();
616
+ if (tok.type !== "identifier" || tok.value !== keyword) {
617
+ throw this.error(`Expected keyword '${keyword}'`, tok);
618
+ }
619
+ return this.advance();
620
+ }
621
+ error(message, at) {
622
+ return new SchemaParseError(message, at.line, at.column, this.file);
623
+ }
624
+ };
625
+ function literalString(v) {
626
+ if (!v) return "";
627
+ if (v.kind === "string") return v.value;
628
+ if (v.kind === "ref") return v.value;
629
+ return String("value" in v ? v.value : "");
630
+ }
631
+ function toNativeType(attr) {
632
+ const name = attr.name.slice("db.".length);
633
+ const args = attr.args.filter((a) => a.kind === "number").map((a) => a.value);
634
+ return { name, args };
635
+ }
636
+ function toDefaultValue(v) {
637
+ if (!v) return {};
638
+ switch (v.kind) {
639
+ case "function":
640
+ return { function: { name: v.name, args: v.args } };
641
+ case "string":
642
+ return { literal: v.value };
643
+ case "number":
644
+ return { literal: v.value };
645
+ case "boolean":
646
+ return { literal: v.value };
647
+ case "ref":
648
+ return { literal: v.value };
649
+ default:
650
+ return {};
651
+ }
652
+ }
653
+ function toRelationInfo(args) {
654
+ const info = {};
655
+ for (const arg of args) {
656
+ if (arg.kind === "string") {
657
+ info.name = arg.value;
658
+ continue;
659
+ }
660
+ const named = asNamed(arg);
661
+ if (!named) continue;
662
+ const [key, value] = named;
663
+ if (key === "name" && value.kind === "string") info.name = value.value;
664
+ if (key === "fields") info.fields = refArray(value);
665
+ if (key === "references") info.references = refArray(value);
666
+ if (key === "onDelete") info.onDelete = refName(value);
667
+ if (key === "onUpdate") info.onUpdate = refName(value);
668
+ }
669
+ return info;
670
+ }
671
+ function asNamed(arg) {
672
+ if (arg.kind === "function" && arg.name.startsWith("__named:")) {
673
+ return [arg.name.slice("__named:".length), arg.args[0]];
674
+ }
675
+ return void 0;
676
+ }
677
+ function namedArg(args, key) {
678
+ for (const arg of args) {
679
+ const named = asNamed(arg);
680
+ if (named && named[0] === key && named[1].kind === "string") {
681
+ return named[1].value;
682
+ }
683
+ }
684
+ return void 0;
685
+ }
686
+ function refArray(v) {
687
+ if (v.kind === "array") {
688
+ return v.items.map((i) => refName(i)).filter((s) => !!s);
689
+ }
690
+ const single = refName(v);
691
+ return single ? [single] : [];
692
+ }
693
+ function refName(v) {
694
+ if (v.kind === "ref") return v.value;
695
+ if (v.kind === "string") return v.value;
696
+ return void 0;
697
+ }
698
+ function fieldNameList(args) {
699
+ for (const arg of args) {
700
+ if (arg.kind === "array") return refArray(arg);
701
+ const named = asNamed(arg);
702
+ if (named && named[0] === "fields") return refArray(named[1]);
703
+ }
704
+ return args.map((a) => refName(a)).filter((s) => !!s);
705
+ }
706
+ function resolveKinds(doc) {
707
+ const modelNames = new Set(doc.models.map((m) => m.name));
708
+ const enumNames = new Set(doc.enums.map((e) => e.name));
709
+ for (const model of doc.models) {
710
+ for (const field of model.fields) {
711
+ if (modelNames.has(field.type)) field.kind = "object";
712
+ else if (enumNames.has(field.type)) field.kind = "enum";
713
+ else field.kind = "scalar";
714
+ }
715
+ }
716
+ }
717
+
718
+ // src/ast/index.ts
719
+ var SCALAR_TYPES = [
720
+ "String",
721
+ "Boolean",
722
+ "Int",
723
+ "BigInt",
724
+ "Float",
725
+ "Decimal",
726
+ "DateTime",
727
+ "Bytes",
728
+ "Json"
729
+ ];
730
+ function findModel(schema, name) {
731
+ return schema.models.find((m) => m.name === name);
732
+ }
733
+ function fieldColumn(field) {
734
+ return field.dbName ?? field.name.toUpperCase();
735
+ }
736
+ function modelTable(model) {
737
+ return model.dbName ?? model.name.toUpperCase();
738
+ }
739
+ function scalarFields(model) {
740
+ return model.fields.filter((f) => f.kind !== "object");
741
+ }
742
+ function relationFields(model) {
743
+ return model.fields.filter((f) => f.kind === "object");
744
+ }
745
+ function idFields(model) {
746
+ if (model.primaryKey.length > 0) {
747
+ return model.primaryKey.map((n) => model.fields.find((f) => f.name === n)).filter((f) => !!f);
748
+ }
749
+ return model.fields.filter((f) => f.isId);
750
+ }
751
+
752
+ // src/schema/validator.ts
753
+ function validateSchema(doc) {
754
+ const errors = [];
755
+ const scalar = new Set(SCALAR_TYPES);
756
+ const enumNames = new Set(doc.enums.map((e) => e.name));
757
+ const modelNames = new Set(doc.models.map((m) => m.name));
758
+ const duplicateModels = findDuplicates(doc.models.map((m) => m.name));
759
+ for (const name of duplicateModels) {
760
+ errors.push(`Duplicate model '${name}'.`);
761
+ }
762
+ for (const model of doc.models) {
763
+ const fieldNames = /* @__PURE__ */ new Set();
764
+ for (const field of model.fields) {
765
+ if (fieldNames.has(field.name)) {
766
+ errors.push(`Duplicate field '${model.name}.${field.name}'.`);
767
+ }
768
+ fieldNames.add(field.name);
769
+ const known = scalar.has(field.type) || enumNames.has(field.type) || modelNames.has(field.type);
770
+ if (!known) {
771
+ errors.push(
772
+ `Field '${model.name}.${field.name}' has unknown type '${field.type}'.`
773
+ );
774
+ }
775
+ if (field.kind === "object" && field.relation) {
776
+ validateRelation(doc, model.name, field.name, field.relation, errors);
777
+ }
778
+ }
779
+ for (const pk of model.primaryKey) {
780
+ if (!fieldNames.has(pk)) {
781
+ errors.push(
782
+ `Primary key field '${pk}' does not exist on model '${model.name}'.`
783
+ );
784
+ }
785
+ }
786
+ const hasId = model.primaryKey.length > 0 || model.fields.some((f) => f.isId);
787
+ if (!hasId) {
788
+ errors.push(
789
+ `Model '${model.name}' has no @id / @@id. Every model needs a primary key.`
790
+ );
791
+ }
792
+ }
793
+ for (const enumNode of doc.enums) {
794
+ if (enumNode.values.length === 0) {
795
+ errors.push(`Enum '${enumNode.name}' has no values.`);
796
+ }
797
+ }
798
+ if (errors.length > 0) {
799
+ throw new SchemaValidationError("Schema validation failed", errors);
800
+ }
801
+ }
802
+ function validateRelation(doc, modelName, fieldName, relation, errors) {
803
+ const model = findModel(doc, modelName);
804
+ if (!model) return;
805
+ for (const f of relation.fields ?? []) {
806
+ if (!model.fields.some((mf) => mf.name === f)) {
807
+ errors.push(
808
+ `Relation '${modelName}.${fieldName}' references local field '${f}' which does not exist.`
809
+ );
810
+ }
811
+ }
812
+ }
813
+ function findDuplicates(values) {
814
+ const seen = /* @__PURE__ */ new Set();
815
+ const dups = /* @__PURE__ */ new Set();
816
+ for (const v of values) {
817
+ if (seen.has(v)) dups.add(v);
818
+ seen.add(v);
819
+ }
820
+ return [...dups];
821
+ }
822
+
823
+ // src/schema/printer.ts
824
+ function printSchema(doc) {
825
+ const blocks = [];
826
+ if (doc.datasource) {
827
+ const ds = doc.datasource;
828
+ const url = ds.url.kind === "env" ? `env("${ds.url.value}")` : `"${ds.url.value}"`;
829
+ blocks.push(
830
+ `datasource ${ds.name} {
831
+ provider = "${ds.provider}"
832
+ url = ${url}
833
+ }`
834
+ );
835
+ }
836
+ for (const gen of doc.generators) {
837
+ const lines = [` provider = "${gen.provider}"`];
838
+ if (gen.output) lines.push(` output = "${gen.output}"`);
839
+ for (const [k, v] of Object.entries(gen.config)) {
840
+ if (k === "provider" || k === "output") continue;
841
+ lines.push(` ${k} = "${v}"`);
842
+ }
843
+ blocks.push(`generator ${gen.name} {
844
+ ${lines.join("\n")}
845
+ }`);
846
+ }
847
+ for (const enumNode of doc.enums) {
848
+ blocks.push(printEnum(enumNode));
849
+ }
850
+ for (const model of doc.models) {
851
+ blocks.push(printModel(model));
852
+ }
853
+ return blocks.join("\n\n") + "\n";
854
+ }
855
+ function printEnum(node) {
856
+ const lines = [];
857
+ if (node.documentation) lines.push(...docLines(node.documentation));
858
+ lines.push(`enum ${node.name} {`);
859
+ for (const v of node.values) {
860
+ lines.push(` ${v.name}${v.dbName ? ` @map("${v.dbName}")` : ""}`);
861
+ }
862
+ if (node.dbName) lines.push(`
863
+ @@map("${node.dbName}")`);
864
+ lines.push(`}`);
865
+ return lines.join("\n");
866
+ }
867
+ function printModel(model) {
868
+ const lines = [];
869
+ if (model.documentation) lines.push(...docLines(model.documentation));
870
+ lines.push(`model ${model.name} {`);
871
+ const nameWidth = Math.max(...model.fields.map((f) => f.name.length), 0);
872
+ const typeWidth = Math.max(...model.fields.map((f) => fieldType(f).length), 0);
873
+ for (const field of model.fields) {
874
+ if (field.documentation) {
875
+ lines.push(...docLines(field.documentation).map((l) => ` ${l}`));
876
+ }
877
+ const attrs = fieldAttributes(field);
878
+ const name = field.name.padEnd(nameWidth);
879
+ const type = fieldType(field).padEnd(typeWidth);
880
+ lines.push(` ${name} ${type}${attrs ? ` ${attrs}` : ""}`.trimEnd());
881
+ }
882
+ const blockAttrs = modelBlockAttributes(model);
883
+ if (blockAttrs.length > 0) {
884
+ lines.push("");
885
+ for (const a of blockAttrs) lines.push(` ${a}`);
886
+ }
887
+ lines.push(`}`);
888
+ return lines.join("\n");
889
+ }
890
+ function fieldType(field) {
891
+ let t = field.type;
892
+ if (field.isList) t += "[]";
893
+ else if (!field.isRequired) t += "?";
894
+ return t;
895
+ }
896
+ function fieldAttributes(field) {
897
+ const parts = [];
898
+ if (field.isId) parts.push("@id");
899
+ if (field.isUnique) parts.push("@unique");
900
+ if (field.default) parts.push(`@default(${printDefault(field.default)})`);
901
+ if (field.isUpdatedAt) parts.push("@updatedAt");
902
+ if (field.relation) {
903
+ const rel2 = field.relation;
904
+ const args = [];
905
+ if (rel2.name) args.push(`"${rel2.name}"`);
906
+ if (rel2.fields?.length) args.push(`fields: [${rel2.fields.join(", ")}]`);
907
+ if (rel2.references?.length)
908
+ args.push(`references: [${rel2.references.join(", ")}]`);
909
+ if (rel2.onDelete) args.push(`onDelete: ${rel2.onDelete}`);
910
+ if (rel2.onUpdate) args.push(`onUpdate: ${rel2.onUpdate}`);
911
+ parts.push(args.length ? `@relation(${args.join(", ")})` : "@relation");
912
+ }
913
+ if (field.nativeType) {
914
+ const a = field.nativeType.args.length ? `(${field.nativeType.args.join(", ")})` : "";
915
+ parts.push(`@db.${field.nativeType.name}${a}`);
916
+ }
917
+ if (field.dbName) parts.push(`@map("${field.dbName}")`);
918
+ return parts.join(" ");
919
+ }
920
+ function printDefault(def) {
921
+ if (def.function) {
922
+ return `${def.function.name}(${def.function.args.map(printArgValue).join(", ")})`;
923
+ }
924
+ if (typeof def.literal === "string") {
925
+ return /^[A-Za-z_][A-Za-z0-9_]*$/.test(def.literal) ? def.literal : `"${def.literal}"`;
926
+ }
927
+ return String(def.literal);
928
+ }
929
+ function printArgValue(v) {
930
+ switch (v.kind) {
931
+ case "string":
932
+ return `"${v.value}"`;
933
+ case "number":
934
+ return String(v.value);
935
+ case "boolean":
936
+ return String(v.value);
937
+ case "ref":
938
+ return v.value;
939
+ case "array":
940
+ return `[${v.items.map(printArgValue).join(", ")}]`;
941
+ case "function":
942
+ return `${v.name}(${v.args.map(printArgValue).join(", ")})`;
943
+ }
944
+ }
945
+ function modelBlockAttributes(model) {
946
+ const out = [];
947
+ const inlineId = model.primaryKey.length === 1 && model.fields.find((f) => f.name === model.primaryKey[0])?.isId;
948
+ if (model.primaryKey.length > 0 && !inlineId) {
949
+ out.push(`@@id([${model.primaryKey.join(", ")}])`);
950
+ }
951
+ for (const u of model.uniqueIndexes) {
952
+ out.push(
953
+ `@@unique([${u.fields.join(", ")}]${u.name ? `, map: "${u.name}"` : ""})`
954
+ );
955
+ }
956
+ for (const i of model.indexes) {
957
+ out.push(
958
+ `@@index([${i.fields.join(", ")}]${i.name ? `, map: "${i.name}"` : ""})`
959
+ );
960
+ }
961
+ if (model.dbName) out.push(`@@map("${model.dbName}")`);
962
+ return out;
963
+ }
964
+ function docLines(documentation) {
965
+ return documentation.split("\n").map((l) => `/// ${l}`);
966
+ }
967
+
968
+ // src/utils/index.ts
969
+ function uniq(items) {
970
+ return [...new Set(items)];
971
+ }
972
+ function pascalCase(input) {
973
+ return input.toLowerCase().replace(
974
+ /[^a-zA-Z0-9]+(.)?/g,
975
+ (_, c) => c ? c.toUpperCase() : ""
976
+ ).replace(/^(.)/, (_, c) => c.toUpperCase());
977
+ }
978
+ function camelCase(input) {
979
+ const pascal = pascalCase(input);
980
+ return pascal.charAt(0).toLowerCase() + pascal.slice(1);
981
+ }
982
+ function pluralize(word) {
983
+ if (/[^aeiou]y$/i.test(word)) return word.replace(/y$/i, "ies");
984
+ if (/(s|x|z|ch|sh)$/i.test(word)) return `${word}es`;
985
+ return `${word}s`;
986
+ }
987
+ function lowerFirst(input) {
988
+ return input.charAt(0).toLowerCase() + input.slice(1);
989
+ }
990
+
991
+ // src/schema/relations-complete.ts
992
+ function completeRelations(doc) {
993
+ const original = [];
994
+ for (const model of doc.models) {
995
+ for (const field of model.fields) {
996
+ if (field.kind === "object") original.push({ model, field });
997
+ }
998
+ }
999
+ for (const { model, field } of original) {
1000
+ const related = findModel(doc, field.type);
1001
+ if (!related) continue;
1002
+ if (hasPartner(related, model, field)) continue;
1003
+ if (field.isList) {
1004
+ addOwningSide(related, model, field);
1005
+ } else if (field.relation?.fields?.length) {
1006
+ addBackRelation(related, model, field);
1007
+ } else {
1008
+ upgradeBareToOne(doc, model, field);
1009
+ }
1010
+ }
1011
+ return doc;
1012
+ }
1013
+ function hasPartner(related, model, field) {
1014
+ return related.fields.some(
1015
+ (f) => f !== field && f.kind === "object" && f.type === model.name && relationNamesMatch(f, field)
1016
+ );
1017
+ }
1018
+ function relationNamesMatch(a, b) {
1019
+ const an = a.relation?.name;
1020
+ const bn = b.relation?.name;
1021
+ if (an && bn) return an === bn;
1022
+ if (!an && !bn) return true;
1023
+ return false;
1024
+ }
1025
+ function addBackRelation(related, owner, owning) {
1026
+ const fkFields = (owning.relation?.fields ?? []).map((n) => owner.fields.find((f) => f.name === n)).filter((f) => !!f);
1027
+ const isOneToOne = fkFields.length > 0 && fkFields.every((f) => f.isUnique);
1028
+ const baseName = isOneToOne ? camelCase(owner.name) : camelCase(pluralize(owner.name));
1029
+ const name = uniqueFieldName(related, baseName);
1030
+ related.fields.push({
1031
+ name,
1032
+ type: owner.name,
1033
+ kind: "object",
1034
+ isList: !isOneToOne,
1035
+ isRequired: false,
1036
+ isId: false,
1037
+ isUnique: false,
1038
+ isUpdatedAt: false,
1039
+ // Only emit @relation when the relation is named; an unnamed back-relation
1040
+ // carries no attribute (matches Prisma's formatter output).
1041
+ ...owning.relation?.name ? { relation: { name: owning.relation.name } } : {}
1042
+ });
1043
+ }
1044
+ function addOwningSide(related, parent, listField) {
1045
+ const ref = idFields(parent)[0];
1046
+ if (!ref) return;
1047
+ const relationField = uniqueFieldName(related, camelCase(parent.name));
1048
+ const fkName = uniqueFieldName(
1049
+ related,
1050
+ `${camelCase(parent.name)}${pascalCase(ref.name)}`
1051
+ );
1052
+ related.fields.push({
1053
+ name: fkName,
1054
+ type: ref.type,
1055
+ kind: "scalar",
1056
+ isList: false,
1057
+ isRequired: true,
1058
+ isId: false,
1059
+ isUnique: false,
1060
+ isUpdatedAt: false,
1061
+ ...ref.nativeType ? { nativeType: ref.nativeType } : {}
1062
+ });
1063
+ related.fields.push({
1064
+ name: relationField,
1065
+ type: parent.name,
1066
+ kind: "object",
1067
+ isList: false,
1068
+ isRequired: true,
1069
+ isId: false,
1070
+ isUnique: false,
1071
+ isUpdatedAt: false,
1072
+ relation: {
1073
+ ...listField.relation?.name ? { name: listField.relation.name } : {},
1074
+ fields: [fkName],
1075
+ references: [ref.name]
1076
+ }
1077
+ });
1078
+ }
1079
+ function upgradeBareToOne(doc, model, field) {
1080
+ const related = findModel(doc, field.type);
1081
+ if (!related) return;
1082
+ const ref = idFields(related)[0];
1083
+ if (!ref) return;
1084
+ const fkName = uniqueFieldName(model, `${field.name}${pascalCase(ref.name)}`);
1085
+ const idx = model.fields.indexOf(field);
1086
+ model.fields.splice(idx, 0, {
1087
+ name: fkName,
1088
+ type: ref.type,
1089
+ kind: "scalar",
1090
+ isList: false,
1091
+ isRequired: field.isRequired,
1092
+ isId: false,
1093
+ isUnique: false,
1094
+ isUpdatedAt: false,
1095
+ ...ref.nativeType ? { nativeType: ref.nativeType } : {}
1096
+ });
1097
+ field.relation = {
1098
+ ...field.relation ?? {},
1099
+ fields: [fkName],
1100
+ references: [ref.name]
1101
+ };
1102
+ addBackRelation(related, model, field);
1103
+ }
1104
+ function uniqueFieldName(model, base) {
1105
+ let name = base || "relation";
1106
+ let i = 1;
1107
+ while (model.fields.some((f) => f.name === name)) name = `${base}_${++i}`;
1108
+ return name;
1109
+ }
1110
+
1111
+ // src/schema/index.ts
1112
+ function formatSchema(source, file) {
1113
+ const doc = completeRelations(parseSchema(source, file));
1114
+ validateSchema(doc);
1115
+ return printSchema(doc);
1116
+ }
1117
+ function parseSchema(source, file) {
1118
+ return new Parser(source, file).parse();
1119
+ }
1120
+ function parseAndValidate(source, file) {
1121
+ const doc = parseSchema(source, file);
1122
+ validateSchema(doc);
1123
+ return doc;
1124
+ }
1125
+ var DEFAULT_SCHEMA_PATHS = [
1126
+ "ember/schema.ember",
1127
+ "schema.ember",
1128
+ "prisma/schema.ember"
1129
+ ];
1130
+ function findSchemaPath(base = process.cwd(), explicit) {
1131
+ if (explicit) {
1132
+ const p = (0, import_node_path.resolve)(base, explicit);
1133
+ return (0, import_node_fs.existsSync)(p) ? p : void 0;
1134
+ }
1135
+ for (const candidate of DEFAULT_SCHEMA_PATHS) {
1136
+ const p = (0, import_node_path.resolve)(base, candidate);
1137
+ if ((0, import_node_fs.existsSync)(p)) return p;
1138
+ }
1139
+ return void 0;
1140
+ }
1141
+ function loadSchema(path) {
1142
+ const source = (0, import_node_fs.readFileSync)(path, "utf8");
1143
+ const document = parseAndValidate(source, path);
1144
+ return {
1145
+ document,
1146
+ path,
1147
+ databaseUrl: resolveDatasourceUrl(document, (0, import_node_path.dirname)(path))
1148
+ };
1149
+ }
1150
+ function resolveDatasourceUrl(doc, _base) {
1151
+ const ds = doc.datasource;
1152
+ if (!ds) return void 0;
1153
+ if (ds.url.kind === "literal") return ds.url.value;
1154
+ return process.env[ds.url.value];
1155
+ }
1156
+
1157
+ // src/driver/firebird-driver.ts
1158
+ var import_node_async_hooks = require("async_hooks");
1159
+ var import_node_firebird = __toESM(require("node-firebird"), 1);
1160
+ var fb = import_node_firebird.default;
1161
+ function isolationConstant(level) {
1162
+ switch (level) {
1163
+ case "READ_COMMITTED_READ_ONLY":
1164
+ return fb.ISOLATION_READ_COMMITTED_READ_ONLY;
1165
+ case "REPEATABLE_READ":
1166
+ return fb.ISOLATION_REPEATABLE_READ;
1167
+ case "SERIALIZABLE":
1168
+ return fb.ISOLATION_SERIALIZABLE;
1169
+ case "READ_COMMITTED":
1170
+ default:
1171
+ return fb.ISOLATION_READ_COMMITTED;
1172
+ }
1173
+ }
1174
+ var FirebirdDriver = class {
1175
+ pool = null;
1176
+ options;
1177
+ poolMax;
1178
+ onQuery;
1179
+ activeTx = new import_node_async_hooks.AsyncLocalStorage();
1180
+ constructor(config, driverOptions) {
1181
+ this.poolMax = config.poolMax ?? 5;
1182
+ this.onQuery = driverOptions?.onQuery;
1183
+ this.options = {
1184
+ host: config.host,
1185
+ port: config.port,
1186
+ database: config.database,
1187
+ user: config.user,
1188
+ password: config.password,
1189
+ role: config.role ?? "",
1190
+ pageSize: config.pageSize ?? 4096,
1191
+ encoding: config.encoding ?? "UTF8",
1192
+ blobAsText: config.blobAsText ?? true,
1193
+ lowercase_keys: config.lowercaseKeys ?? false,
1194
+ retryConnectionInterval: 1e3,
1195
+ // FB3+ secure auth (Srp) is negotiated by default; set explicitly to force
1196
+ // a plugin, or "Legacy_Auth" for Firebird 2.1/2.5 servers.
1197
+ ...config.authPlugin ? { pluginName: config.authPlugin } : {},
1198
+ ...config.wireCompression != null ? { wireCompression: config.wireCompression } : {}
1199
+ };
1200
+ }
1201
+ async connect() {
1202
+ if (this.pool) return;
1203
+ this.pool = fb.pool(this.poolMax, this.options);
1204
+ }
1205
+ async disconnect() {
1206
+ if (!this.pool) return;
1207
+ this.pool.destroy();
1208
+ this.pool = null;
1209
+ }
1210
+ async transaction(fn, options) {
1211
+ const existing = this.activeTx.getStore();
1212
+ if (existing) {
1213
+ return fn(existing);
1214
+ }
1215
+ await this.connect();
1216
+ const db = await this.acquire();
1217
+ const tr = await this.begin(db, options?.isolation);
1218
+ const ctx = {
1219
+ query: (sql, params) => this.runOnTransaction(tr, sql, params)
1220
+ };
1221
+ try {
1222
+ const result = await this.activeTx.run(ctx, () => fn(ctx));
1223
+ await this.commit(tr);
1224
+ return result;
1225
+ } catch (err) {
1226
+ await this.safeRollback(tr);
1227
+ throw err;
1228
+ } finally {
1229
+ db.detach();
1230
+ }
1231
+ }
1232
+ // ---- promise wrappers over node-firebird -------------------------------
1233
+ acquire() {
1234
+ return new Promise((resolve4, reject) => {
1235
+ this.pool.get((err, db) => {
1236
+ if (err) return reject(wrap(err, "Failed to acquire connection"));
1237
+ resolve4(db);
1238
+ });
1239
+ });
1240
+ }
1241
+ begin(db, isolation) {
1242
+ return new Promise((resolve4, reject) => {
1243
+ db.transaction(isolationConstant(isolation), (err, tr) => {
1244
+ if (err) return reject(wrap(err, "Failed to start transaction"));
1245
+ resolve4(tr);
1246
+ });
1247
+ });
1248
+ }
1249
+ runOnTransaction(tr, sql, params = []) {
1250
+ const start = this.onQuery ? performance.now() : 0;
1251
+ return new Promise((resolve4, reject) => {
1252
+ tr.query(sql, [...params], (err, result) => {
1253
+ if (err) return reject(wrap(err, "Query failed", sql));
1254
+ const rows = normalizeRows(result);
1255
+ if (this.onQuery) {
1256
+ this.onQuery({
1257
+ sql,
1258
+ params,
1259
+ durationMs: performance.now() - start,
1260
+ rowCount: rows.length
1261
+ });
1262
+ }
1263
+ resolve4(rows);
1264
+ });
1265
+ });
1266
+ }
1267
+ commit(tr) {
1268
+ return new Promise((resolve4, reject) => {
1269
+ tr.commit((err) => {
1270
+ if (err) return reject(wrap(err, "Failed to commit transaction"));
1271
+ resolve4();
1272
+ });
1273
+ });
1274
+ }
1275
+ safeRollback(tr) {
1276
+ return new Promise((resolve4) => {
1277
+ tr.rollback(() => resolve4());
1278
+ });
1279
+ }
1280
+ };
1281
+ function normalizeRows(result) {
1282
+ if (Array.isArray(result)) return result;
1283
+ if (result === void 0 || result === null) return [];
1284
+ return [result];
1285
+ }
1286
+ function wrap(err, message, sql) {
1287
+ const detail = err && typeof err === "object" && "message" in err ? String(err.message) : String(err);
1288
+ return new DatabaseError(`${message}: ${detail}`, err, sql);
1289
+ }
1290
+
1291
+ // src/driver/url.ts
1292
+ function parseConnectionUrl(url) {
1293
+ let parsed;
1294
+ try {
1295
+ parsed = new URL(url);
1296
+ } catch {
1297
+ throw new EmberError(`Invalid Firebird connection URL: ${url}`);
1298
+ }
1299
+ if (!/^firebird:?$/.test(parsed.protocol.replace(":", "") + ":")) {
1300
+ if (parsed.protocol !== "firebird:") {
1301
+ throw new EmberError(
1302
+ `Unsupported protocol '${parsed.protocol}'. Expected 'firebird:'.`
1303
+ );
1304
+ }
1305
+ }
1306
+ const database = normalizeDatabasePath(parsed.pathname);
1307
+ if (!database) {
1308
+ throw new EmberError(`Connection URL is missing a database path: ${url}`);
1309
+ }
1310
+ const params = parsed.searchParams;
1311
+ const config = {
1312
+ host: parsed.hostname || "127.0.0.1",
1313
+ port: parsed.port ? Number(parsed.port) : 3050,
1314
+ database,
1315
+ user: decodeURIComponent(parsed.username || "SYSDBA"),
1316
+ password: decodeURIComponent(parsed.password || "masterkey"),
1317
+ encoding: params.get("encoding") ?? "UTF8"
1318
+ };
1319
+ const role = params.get("role");
1320
+ if (role) config.role = role;
1321
+ const poolMax = params.get("poolMax") ?? params.get("connection_limit");
1322
+ if (poolMax) config.poolMax = Number(poolMax);
1323
+ const pageSize = params.get("pageSize");
1324
+ if (pageSize) config.pageSize = Number(pageSize);
1325
+ const auth = (params.get("authPlugin") ?? params.get("auth"))?.toLowerCase();
1326
+ if (auth === "legacy" || auth === "legacy_auth") config.authPlugin = "Legacy_Auth";
1327
+ else if (auth === "srp") config.authPlugin = "Srp";
1328
+ const wireCompression = params.get("wireCompression");
1329
+ if (wireCompression != null) {
1330
+ config.wireCompression = wireCompression !== "false" && wireCompression !== "0";
1331
+ }
1332
+ const version = params.get("version");
1333
+ if (version) config.version = normalizeVersion(version);
1334
+ return config;
1335
+ }
1336
+ var VERSIONS = /* @__PURE__ */ new Set(["2.1", "2.5", "3", "4", "5"]);
1337
+ function normalizeVersion(raw) {
1338
+ const trimmed = raw.trim();
1339
+ if (VERSIONS.has(trimmed)) return trimmed;
1340
+ if (/^2\.1/.test(trimmed)) return "2.1";
1341
+ if (/^2\.5/.test(trimmed)) return "2.5";
1342
+ const major = trimmed.split(".")[0];
1343
+ if (major && VERSIONS.has(major)) return major;
1344
+ return void 0;
1345
+ }
1346
+ function normalizeDatabasePath(pathname) {
1347
+ let p = decodeURIComponent(pathname);
1348
+ if (p.startsWith("//")) p = p.slice(1);
1349
+ if (/^\/[A-Za-z]:\//.test(p)) p = p.slice(1);
1350
+ return p;
1351
+ }
1352
+
1353
+ // src/driver/index.ts
1354
+ function createDriver(source, options) {
1355
+ const config = typeof source === "string" ? parseConnectionUrl(source) : source;
1356
+ return new FirebirdDriver(config, options);
1357
+ }
1358
+
1359
+ // src/sql/dialect.ts
1360
+ var FirebirdDialect = class {
1361
+ supportsReturning = true;
1362
+ version;
1363
+ supportsBooleanType;
1364
+ supportsIdentity;
1365
+ supportsWindowFunctions;
1366
+ constructor(options = {}) {
1367
+ this.version = options.version ?? "3";
1368
+ const rank = versionRank(this.version);
1369
+ this.supportsBooleanType = rank >= 30;
1370
+ this.supportsIdentity = rank >= 30;
1371
+ this.supportsWindowFunctions = rank >= 30;
1372
+ }
1373
+ booleanColumnType() {
1374
+ return this.supportsBooleanType ? "BOOLEAN" : "SMALLINT";
1375
+ }
1376
+ quoteId(name) {
1377
+ return `"${name.replace(/"/g, '""')}"`;
1378
+ }
1379
+ quoteRef(table, column) {
1380
+ return `${this.quoteId(table)}.${this.quoteId(column)}`;
1381
+ }
1382
+ paginationClause(take, skip) {
1383
+ const parts = [];
1384
+ if (typeof take === "number" && take >= 0) {
1385
+ parts.push(`FIRST ${Math.trunc(take)}`);
1386
+ }
1387
+ if (typeof skip === "number" && skip > 0) {
1388
+ parts.push(`SKIP ${Math.trunc(skip)}`);
1389
+ }
1390
+ return parts.join(" ");
1391
+ }
1392
+ caseInsensitive(expr) {
1393
+ return `UPPER(${expr})`;
1394
+ }
1395
+ defaultFunctionSql(name) {
1396
+ switch (name) {
1397
+ case "now":
1398
+ return "CURRENT_TIMESTAMP";
1399
+ case "uuid":
1400
+ case "cuid":
1401
+ return "UUID_TO_CHAR(GEN_UUID())";
1402
+ case "autoincrement":
1403
+ return null;
1404
+ // handled via generators/identity, not an inline default
1405
+ default:
1406
+ return null;
1407
+ }
1408
+ }
1409
+ coerceValue(value) {
1410
+ if (value === null || value === void 0) return null;
1411
+ if (value instanceof Date) return value;
1412
+ if (Buffer.isBuffer(value)) return value;
1413
+ if (typeof value === "boolean") {
1414
+ return this.supportsBooleanType ? value : value ? 1 : 0;
1415
+ }
1416
+ if (typeof value === "bigint") return value;
1417
+ if (typeof value === "number") return value;
1418
+ if (typeof value === "string") return value;
1419
+ return JSON.stringify(value);
1420
+ }
1421
+ };
1422
+ function versionRank(version) {
1423
+ switch (version) {
1424
+ case "2.1":
1425
+ return 21;
1426
+ case "2.5":
1427
+ return 25;
1428
+ case "3":
1429
+ return 30;
1430
+ case "4":
1431
+ return 40;
1432
+ case "5":
1433
+ return 50;
1434
+ default:
1435
+ return 30;
1436
+ }
1437
+ }
1438
+
1439
+ // src/introspect/firebird-meta.ts
1440
+ var trim = (v) => v == null ? "" : String(v).trim();
1441
+ var numOrNull = (v) => v == null ? null : Number(v);
1442
+ var FirebirdMetadataReader = class {
1443
+ constructor(tx) {
1444
+ this.tx = tx;
1445
+ }
1446
+ tx;
1447
+ async tables() {
1448
+ const rows = await this.tx.query(
1449
+ `SELECT RDB$RELATION_NAME
1450
+ FROM RDB$RELATIONS
1451
+ WHERE RDB$VIEW_BLR IS NULL
1452
+ AND (RDB$SYSTEM_FLAG IS NULL OR RDB$SYSTEM_FLAG = 0)
1453
+ ORDER BY RDB$RELATION_NAME`
1454
+ );
1455
+ return rows.map((r) => trim(r["RDB$RELATION_NAME"]));
1456
+ }
1457
+ async columns() {
1458
+ const rows = await this.tx.query(
1459
+ `SELECT
1460
+ rf.RDB$RELATION_NAME AS TABLE_NAME,
1461
+ rf.RDB$FIELD_NAME AS FIELD_NAME,
1462
+ rf.RDB$FIELD_POSITION AS FIELD_POSITION,
1463
+ rf.RDB$NULL_FLAG AS NULL_FLAG,
1464
+ rf.RDB$DEFAULT_SOURCE AS DEFAULT_SOURCE,
1465
+ rf.RDB$IDENTITY_TYPE AS IDENTITY_TYPE,
1466
+ f.RDB$FIELD_TYPE AS FIELD_TYPE,
1467
+ f.RDB$FIELD_SUB_TYPE AS FIELD_SUB_TYPE,
1468
+ f.RDB$FIELD_LENGTH AS FIELD_LENGTH,
1469
+ f.RDB$CHARACTER_LENGTH AS CHAR_LENGTH,
1470
+ f.RDB$FIELD_PRECISION AS FIELD_PRECISION,
1471
+ f.RDB$FIELD_SCALE AS FIELD_SCALE
1472
+ FROM RDB$RELATION_FIELDS rf
1473
+ JOIN RDB$FIELDS f ON f.RDB$FIELD_NAME = rf.RDB$FIELD_SOURCE
1474
+ JOIN RDB$RELATIONS r ON r.RDB$RELATION_NAME = rf.RDB$RELATION_NAME
1475
+ WHERE r.RDB$VIEW_BLR IS NULL
1476
+ AND (r.RDB$SYSTEM_FLAG IS NULL OR r.RDB$SYSTEM_FLAG = 0)
1477
+ ORDER BY rf.RDB$RELATION_NAME, rf.RDB$FIELD_POSITION`
1478
+ );
1479
+ return rows.map((r) => ({
1480
+ table: trim(r.TABLE_NAME),
1481
+ name: trim(r.FIELD_NAME),
1482
+ position: Number(r.FIELD_POSITION ?? 0),
1483
+ fieldType: Number(r.FIELD_TYPE ?? 0),
1484
+ fieldSubType: numOrNull(r.FIELD_SUB_TYPE),
1485
+ length: numOrNull(r.FIELD_LENGTH),
1486
+ charLength: numOrNull(r.CHAR_LENGTH),
1487
+ precision: numOrNull(r.FIELD_PRECISION),
1488
+ scale: numOrNull(r.FIELD_SCALE),
1489
+ notNull: r.NULL_FLAG != null && Number(r.NULL_FLAG) === 1,
1490
+ defaultSource: r.DEFAULT_SOURCE == null ? null : trim(r.DEFAULT_SOURCE),
1491
+ isIdentity: r.IDENTITY_TYPE != null
1492
+ }));
1493
+ }
1494
+ async constraints() {
1495
+ const rows = await this.tx.query(
1496
+ `SELECT
1497
+ rc.RDB$CONSTRAINT_NAME AS CONSTRAINT_NAME,
1498
+ rc.RDB$CONSTRAINT_TYPE AS CONSTRAINT_TYPE,
1499
+ rc.RDB$RELATION_NAME AS TABLE_NAME,
1500
+ rc.RDB$INDEX_NAME AS INDEX_NAME,
1501
+ seg.RDB$FIELD_NAME AS FIELD_NAME,
1502
+ seg.RDB$FIELD_POSITION AS SEG_POSITION,
1503
+ refc.RDB$UPDATE_RULE AS UPDATE_RULE,
1504
+ refc.RDB$DELETE_RULE AS DELETE_RULE,
1505
+ refc.RDB$CONST_NAME_UQ AS UQ_NAME
1506
+ FROM RDB$RELATION_CONSTRAINTS rc
1507
+ JOIN RDB$INDEX_SEGMENTS seg ON seg.RDB$INDEX_NAME = rc.RDB$INDEX_NAME
1508
+ LEFT JOIN RDB$REF_CONSTRAINTS refc ON refc.RDB$CONSTRAINT_NAME = rc.RDB$CONSTRAINT_NAME
1509
+ WHERE rc.RDB$CONSTRAINT_TYPE IN ('PRIMARY KEY', 'UNIQUE', 'FOREIGN KEY')
1510
+ ORDER BY rc.RDB$CONSTRAINT_NAME, seg.RDB$FIELD_POSITION`
1511
+ );
1512
+ const byName = /* @__PURE__ */ new Map();
1513
+ const uqByName = /* @__PURE__ */ new Map();
1514
+ for (const r of rows) {
1515
+ const name = trim(r.CONSTRAINT_NAME);
1516
+ let c = byName.get(name);
1517
+ if (!c) {
1518
+ c = {
1519
+ table: trim(r.TABLE_NAME),
1520
+ type: trim(r.CONSTRAINT_TYPE),
1521
+ name,
1522
+ indexName: r.INDEX_NAME == null ? null : trim(r.INDEX_NAME),
1523
+ columns: []
1524
+ };
1525
+ if (c.type === "FOREIGN KEY") {
1526
+ c.updateRule = trim(r.UPDATE_RULE) || void 0;
1527
+ c.deleteRule = trim(r.DELETE_RULE) || void 0;
1528
+ uqByName.set(name, trim(r.UQ_NAME));
1529
+ }
1530
+ byName.set(name, c);
1531
+ }
1532
+ const col = trim(r.FIELD_NAME);
1533
+ if (col && !c.columns.includes(col)) c.columns.push(col);
1534
+ }
1535
+ for (const c of byName.values()) {
1536
+ if (c.type !== "FOREIGN KEY") continue;
1537
+ const uqName = uqByName.get(c.name);
1538
+ if (!uqName) continue;
1539
+ const target = byName.get(uqName);
1540
+ if (target) {
1541
+ c.references = { table: target.table, columns: [...target.columns] };
1542
+ } else {
1543
+ const resolved = await this.resolveConstraintColumns(uqName);
1544
+ if (resolved) c.references = resolved;
1545
+ }
1546
+ }
1547
+ return [...byName.values()];
1548
+ }
1549
+ async resolveConstraintColumns(constraintName2) {
1550
+ const rows = await this.tx.query(
1551
+ `SELECT rc.RDB$RELATION_NAME AS TABLE_NAME, seg.RDB$FIELD_NAME AS FIELD_NAME
1552
+ FROM RDB$RELATION_CONSTRAINTS rc
1553
+ JOIN RDB$INDEX_SEGMENTS seg ON seg.RDB$INDEX_NAME = rc.RDB$INDEX_NAME
1554
+ WHERE rc.RDB$CONSTRAINT_NAME = ?
1555
+ ORDER BY seg.RDB$FIELD_POSITION`,
1556
+ [constraintName2]
1557
+ );
1558
+ if (rows.length === 0) return null;
1559
+ return {
1560
+ table: trim(rows[0].TABLE_NAME),
1561
+ columns: rows.map((r) => trim(r.FIELD_NAME))
1562
+ };
1563
+ }
1564
+ };
1565
+
1566
+ // src/introspect/type-map.ts
1567
+ var FB = {
1568
+ SMALLINT: 7,
1569
+ INTEGER: 8,
1570
+ QUAD: 9,
1571
+ FLOAT: 10,
1572
+ DATE_LEGACY: 11,
1573
+ DATE: 12,
1574
+ TIME: 13,
1575
+ CHAR: 14,
1576
+ INT64: 16,
1577
+ // BIGINT / numeric
1578
+ BOOLEAN: 23,
1579
+ DOUBLE: 27,
1580
+ TIMESTAMP: 35,
1581
+ VARCHAR: 37,
1582
+ BLOB: 261
1583
+ };
1584
+ function mapColumnType(col) {
1585
+ const scale = col.scale ?? 0;
1586
+ switch (col.fieldType) {
1587
+ case FB.SMALLINT:
1588
+ return scale < 0 ? decimal(col) : { scalar: "Int", native: { name: "SmallInt", args: [] } };
1589
+ case FB.INTEGER:
1590
+ return scale < 0 ? decimal(col) : { scalar: "Int", native: { name: "Integer", args: [] } };
1591
+ case FB.INT64:
1592
+ return scale < 0 ? decimal(col) : { scalar: "BigInt", native: { name: "BigInt", args: [] } };
1593
+ case FB.FLOAT:
1594
+ return { scalar: "Float", native: { name: "Float", args: [] } };
1595
+ case FB.DOUBLE:
1596
+ return { scalar: "Float", native: { name: "DoublePrecision", args: [] } };
1597
+ case FB.BOOLEAN:
1598
+ return { scalar: "Boolean", native: { name: "Boolean", args: [] } };
1599
+ case FB.CHAR:
1600
+ return {
1601
+ scalar: "String",
1602
+ native: { name: "Char", args: col.charLength ? [col.charLength] : [] }
1603
+ };
1604
+ case FB.VARCHAR:
1605
+ return {
1606
+ scalar: "String",
1607
+ native: { name: "VarChar", args: col.charLength ? [col.charLength] : [] }
1608
+ };
1609
+ case FB.DATE:
1610
+ case FB.DATE_LEGACY:
1611
+ return { scalar: "DateTime", native: { name: "Date", args: [] } };
1612
+ case FB.TIME:
1613
+ return { scalar: "DateTime", native: { name: "Time", args: [] } };
1614
+ case FB.TIMESTAMP:
1615
+ return { scalar: "DateTime", native: { name: "Timestamp", args: [] } };
1616
+ case FB.BLOB:
1617
+ return col.fieldSubType === 1 ? { scalar: "String", native: { name: "Text", args: [] } } : { scalar: "Bytes", native: { name: "Blob", args: [] } };
1618
+ default:
1619
+ return { scalar: "String" };
1620
+ }
1621
+ }
1622
+ function decimal(col) {
1623
+ const precision = col.precision ?? 18;
1624
+ const scaleAbs = Math.abs(col.scale ?? 0);
1625
+ return {
1626
+ scalar: "Decimal",
1627
+ native: { name: "Decimal", args: [precision, scaleAbs] }
1628
+ };
1629
+ }
1630
+
1631
+ // src/introspect/index.ts
1632
+ var Introspector = class {
1633
+ constructor(driver) {
1634
+ this.driver = driver;
1635
+ }
1636
+ driver;
1637
+ async introspect(options = {}) {
1638
+ const { tables, columns, constraints } = await this.driver.transaction(
1639
+ async (tx) => {
1640
+ const reader = new FirebirdMetadataReader(tx);
1641
+ return {
1642
+ tables: await reader.tables(),
1643
+ columns: await reader.columns(),
1644
+ constraints: await reader.constraints()
1645
+ };
1646
+ },
1647
+ { isolation: "READ_COMMITTED_READ_ONLY" }
1648
+ );
1649
+ const columnsByTable = groupBy(columns, (c) => c.table);
1650
+ const constraintsByTable = groupBy(constraints, (c) => c.table);
1651
+ const modelNames = buildNameMap(tables);
1652
+ const models = tables.map(
1653
+ (table) => this.buildModel(
1654
+ table,
1655
+ modelNames,
1656
+ columnsByTable.get(table) ?? [],
1657
+ constraintsByTable.get(table) ?? []
1658
+ )
1659
+ );
1660
+ addRelations(models, modelNames, constraints);
1661
+ const enums = [];
1662
+ const doc = {
1663
+ generators: [
1664
+ { name: "client", provider: "ember-client-js", output: "../generated", config: {} }
1665
+ ],
1666
+ models,
1667
+ enums
1668
+ };
1669
+ if (options.datasource) {
1670
+ doc.datasource = {
1671
+ name: options.datasource.name,
1672
+ provider: options.datasource.provider,
1673
+ url: options.datasource.envVar ? { kind: "env", value: options.datasource.envVar } : { kind: "literal", value: options.datasource.url ?? "" }
1674
+ };
1675
+ }
1676
+ return doc;
1677
+ }
1678
+ buildModel(table, modelNames, columns, constraints) {
1679
+ const modelName = modelNames.get(table);
1680
+ const pk = constraints.find((c) => c.type === "PRIMARY KEY");
1681
+ const uniques = constraints.filter((c) => c.type === "UNIQUE");
1682
+ const fieldNameMap = buildNameMap(columns.map((c) => c.name), camelCase);
1683
+ const fields = columns.map(
1684
+ (col) => buildField(col, fieldNameMap, pk, uniques)
1685
+ );
1686
+ const model = {
1687
+ name: modelName,
1688
+ dbName: modelName !== table ? table : void 0,
1689
+ fields,
1690
+ primaryKey: pk && pk.columns.length > 1 ? pk.columns.map((c) => fieldNameMap.get(c)) : [],
1691
+ uniqueIndexes: uniques.filter((u) => u.columns.length > 1).map((u) => ({ fields: u.columns.map((c) => fieldNameMap.get(c)) })),
1692
+ indexes: []
1693
+ };
1694
+ return model;
1695
+ }
1696
+ };
1697
+ function buildField(col, fieldNameMap, pk, uniques) {
1698
+ const fieldName = fieldNameMap.get(col.name);
1699
+ const mapped = mapColumnType(col);
1700
+ const isSingleIdCol = !!pk && pk.columns.length === 1 && pk.columns[0] === col.name;
1701
+ const isSingleUnique = uniques.some(
1702
+ (u) => u.columns.length === 1 && u.columns[0] === col.name
1703
+ );
1704
+ const field = {
1705
+ name: fieldName,
1706
+ type: mapped.scalar,
1707
+ kind: "scalar",
1708
+ isList: false,
1709
+ isRequired: col.notNull || isSingleIdCol,
1710
+ isId: isSingleIdCol,
1711
+ isUnique: isSingleUnique,
1712
+ isUpdatedAt: false,
1713
+ dbName: fieldName !== col.name ? col.name : void 0,
1714
+ nativeType: mapped.native,
1715
+ default: parseDefault(col)
1716
+ };
1717
+ return field;
1718
+ }
1719
+ function parseDefault(col) {
1720
+ if (col.isIdentity) return { function: { name: "autoincrement", args: [] } };
1721
+ if (!col.defaultSource) return void 0;
1722
+ const src = col.defaultSource.replace(/^DEFAULT\s+/i, "").trim();
1723
+ if (/^CURRENT_TIMESTAMP/i.test(src)) return { function: { name: "now", args: [] } };
1724
+ if (/^(TRUE|FALSE)$/i.test(src)) return { literal: /^TRUE$/i.test(src) };
1725
+ if (/^-?\d+(\.\d+)?$/.test(src)) return { literal: Number(src) };
1726
+ const str = /^'(.*)'$/.exec(src);
1727
+ if (str) return { literal: str[1] };
1728
+ return void 0;
1729
+ }
1730
+ function addRelations(models, modelNames, constraints) {
1731
+ const byName = new Map(models.map((m) => [m.name, m]));
1732
+ for (const fk of constraints) {
1733
+ if (fk.type !== "FOREIGN KEY" || !fk.references) continue;
1734
+ const childModel = byName.get(modelNames.get(fk.table));
1735
+ const parentModel = byName.get(modelNames.get(fk.references.table));
1736
+ if (!childModel || !parentModel) continue;
1737
+ const localFieldNames = fk.columns.map((c) => columnToField(childModel, c));
1738
+ const refFieldNames = fk.references.columns.map(
1739
+ (c) => columnToField(parentModel, c)
1740
+ );
1741
+ const childFieldName = uniqueFieldName2(childModel, lowerFirst(parentModel.name));
1742
+ childModel.fields.push({
1743
+ name: childFieldName,
1744
+ type: parentModel.name,
1745
+ kind: "object",
1746
+ isList: false,
1747
+ isRequired: localFieldNames.every(
1748
+ (n) => childModel.fields.find((f) => f.name === n)?.isRequired
1749
+ ),
1750
+ isId: false,
1751
+ isUnique: false,
1752
+ isUpdatedAt: false,
1753
+ relation: {
1754
+ fields: localFieldNames,
1755
+ references: refFieldNames,
1756
+ onDelete: mapRule(fk.deleteRule),
1757
+ onUpdate: mapRule(fk.updateRule)
1758
+ }
1759
+ });
1760
+ const backName = uniqueFieldName2(
1761
+ parentModel,
1762
+ lowerFirst(pluralize(childModel.name))
1763
+ );
1764
+ parentModel.fields.push({
1765
+ name: backName,
1766
+ type: childModel.name,
1767
+ kind: "object",
1768
+ isList: true,
1769
+ isRequired: false,
1770
+ isId: false,
1771
+ isUnique: false,
1772
+ isUpdatedAt: false
1773
+ });
1774
+ }
1775
+ }
1776
+ function columnToField(model, column) {
1777
+ const f = model.fields.find((x) => (x.dbName ?? x.name) === column);
1778
+ return f ? f.name : camelCase(column);
1779
+ }
1780
+ function uniqueFieldName2(model, base) {
1781
+ let name = base;
1782
+ let i = 1;
1783
+ while (model.fields.some((f) => f.name === name)) name = `${base}_${++i}`;
1784
+ return name;
1785
+ }
1786
+ function mapRule(rule) {
1787
+ switch ((rule ?? "").toUpperCase()) {
1788
+ case "CASCADE":
1789
+ return "Cascade";
1790
+ case "SET NULL":
1791
+ return "SetNull";
1792
+ case "SET DEFAULT":
1793
+ return "SetDefault";
1794
+ case "NO ACTION":
1795
+ return "NoAction";
1796
+ case "RESTRICT":
1797
+ return "Restrict";
1798
+ default:
1799
+ return void 0;
1800
+ }
1801
+ }
1802
+ function buildNameMap(dbNames, transform = pascalCase) {
1803
+ const map = /* @__PURE__ */ new Map();
1804
+ const used = /* @__PURE__ */ new Set();
1805
+ for (const dbName of uniq(dbNames)) {
1806
+ let name = transform(dbName);
1807
+ if (!name) name = dbName;
1808
+ let candidate = name;
1809
+ let i = 1;
1810
+ while (used.has(candidate)) candidate = `${name}_${++i}`;
1811
+ used.add(candidate);
1812
+ map.set(dbName, candidate);
1813
+ }
1814
+ return map;
1815
+ }
1816
+ function groupBy(items, key) {
1817
+ const map = /* @__PURE__ */ new Map();
1818
+ for (const item of items) {
1819
+ const k = key(item);
1820
+ const list = map.get(k) ?? [];
1821
+ list.push(item);
1822
+ map.set(k, list);
1823
+ }
1824
+ return map;
1825
+ }
1826
+
1827
+ // src/generator/index.ts
1828
+ var import_node_fs2 = require("fs");
1829
+ var import_node_path2 = require("path");
1830
+
1831
+ // src/generator/ts-types.ts
1832
+ var SCALAR_TS = {
1833
+ String: "string",
1834
+ Boolean: "boolean",
1835
+ Int: "number",
1836
+ BigInt: "bigint",
1837
+ Float: "number",
1838
+ Decimal: "number",
1839
+ DateTime: "Date",
1840
+ Bytes: "Buffer",
1841
+ Json: "JsonValue"
1842
+ };
1843
+ function baseTsType(field, schema) {
1844
+ if (field.kind === "enum") return field.type;
1845
+ if (field.kind === "object") return field.type;
1846
+ return SCALAR_TS[field.type] ?? "unknown";
1847
+ }
1848
+
1849
+ // src/generator/index.ts
1850
+ var ClientGenerator = class {
1851
+ constructor(schema) {
1852
+ this.schema = schema;
1853
+ }
1854
+ schema;
1855
+ generate() {
1856
+ const parts = [];
1857
+ parts.push(this.header());
1858
+ parts.push(this.enums());
1859
+ parts.push(this.modelTypes());
1860
+ parts.push(this.registry());
1861
+ parts.push(PAYLOAD_RESOLVER);
1862
+ parts.push(this.namespace());
1863
+ parts.push(this.clientClass());
1864
+ return parts.filter(Boolean).join("\n\n") + "\n";
1865
+ }
1866
+ // ---- header & shared ----------------------------------------------------
1867
+ header() {
1868
+ return `// AUTO-GENERATED by EmberORM. Do not edit by hand.
1869
+ /* eslint-disable */
1870
+ import { EmberClientBase } from "ember-orm/client";
1871
+ import type { ClientOptions } from "ember-orm/client";
1872
+
1873
+ export type JsonValue =
1874
+ | string
1875
+ | number
1876
+ | boolean
1877
+ | null
1878
+ | { [key: string]: JsonValue }
1879
+ | JsonValue[];
1880
+
1881
+ export type SortOrder = "asc" | "desc";
1882
+ export type QueryMode = "default" | "insensitive";
1883
+
1884
+ const schemaDocument = ${JSON.stringify(this.schema)} as unknown as ClientOptions["schema"];`;
1885
+ }
1886
+ enums() {
1887
+ return this.schema.enums.map((e) => this.enumType(e)).join("\n\n");
1888
+ }
1889
+ enumType(node) {
1890
+ const values = node.values.map((v) => ` ${v.name}: "${v.name}"`).join(",\n");
1891
+ return `export const ${node.name} = {
1892
+ ${values}
1893
+ } as const;
1894
+ export type ${node.name} = (typeof ${node.name})[keyof typeof ${node.name}];`;
1895
+ }
1896
+ modelTypes() {
1897
+ return this.schema.models.map((m) => this.modelType(m)).join("\n\n");
1898
+ }
1899
+ modelType(model) {
1900
+ const scalars = scalarFields(model).map((f) => ` ${f.name}: ${this.scalarType(f)};`).join("\n");
1901
+ const lines = [];
1902
+ lines.push(`export type ${model.name} = {
1903
+ ${scalars}
1904
+ };`);
1905
+ lines.push(`export type ${model.name}GetPayload<A> = $Payload<"${model.name}", A>;`);
1906
+ lines.push(this.fluentType(model));
1907
+ return lines.join("\n");
1908
+ }
1909
+ /**
1910
+ * Fluent-API return type for single-record reads: a Promise of the payload
1911
+ * that also exposes a method per relation (to-many -> array; to-one ->
1912
+ * chainable fluent).
1913
+ */
1914
+ fluentType(model) {
1915
+ const n = model.name;
1916
+ const methods = relationFields(model).map((f) => {
1917
+ if (f.isList) {
1918
+ return ` ${f.name}<S extends Prisma.${f.type}FindManyArgs = {}>(args?: S): Promise<${f.type}GetPayload<S>[]>;`;
1919
+ }
1920
+ return ` ${f.name}<S extends Prisma.${f.type}FindFirstArgs = {}>(args?: S): ${f.type}Fluent<S, null>;`;
1921
+ });
1922
+ return `export type ${n}Fluent<A, Null = null> = Promise<${n}GetPayload<A> | Null> & {
1923
+ ${methods.join("\n")}
1924
+ };`;
1925
+ }
1926
+ /**
1927
+ * Type-level registry powering recursive payload resolution: a model-name
1928
+ * union, a scalar-payload map, and a relation map carrying each relation's
1929
+ * target model, list-ness and nullability.
1930
+ */
1931
+ registry() {
1932
+ const names = this.schema.models.map((m) => `"${m.name}"`).join(" | ");
1933
+ const scalarEntries = this.schema.models.map((m) => ` ${m.name}: ${m.name};`).join("\n");
1934
+ const relationEntries = this.schema.models.map((m) => {
1935
+ const rels = relationFields(m).map((f) => {
1936
+ const nullable = !f.isList && !f.isRequired ? "; isNullable: true" : "";
1937
+ return ` ${f.name}: { model: "${f.type}"; isList: ${f.isList}${nullable} }`;
1938
+ }).join(";\n");
1939
+ return ` ${m.name}: {${rels ? `
1940
+ ${rels}
1941
+ ` : ""}};`;
1942
+ }).join("\n");
1943
+ return `export type $ModelName = ${names || "never"};
1944
+
1945
+ export interface $ScalarPayload {
1946
+ ${scalarEntries}
1947
+ }
1948
+
1949
+ export interface $RelationMap {
1950
+ ${relationEntries}
1951
+ }`;
1952
+ }
1953
+ scalarType(field) {
1954
+ const base = baseTsType(field, this.schema);
1955
+ if (field.isList) return `${base}[]`;
1956
+ return field.isRequired ? base : `${base} | null`;
1957
+ }
1958
+ // ---- input types (Prisma namespace) -------------------------------------
1959
+ namespace() {
1960
+ const blocks = [SHARED_FILTERS];
1961
+ for (const model of this.schema.models) {
1962
+ blocks.push(this.whereInput(model));
1963
+ blocks.push(this.orderByInput(model));
1964
+ blocks.push(this.selectInclude(model));
1965
+ blocks.push(this.dataInputs(model));
1966
+ blocks.push(this.argsTypes(model));
1967
+ blocks.push(this.delegateInterface(model));
1968
+ }
1969
+ return `export namespace Prisma {
1970
+ ${indentBlock(blocks.join("\n\n"))}
1971
+ }`;
1972
+ }
1973
+ whereInput(model) {
1974
+ const fields = [];
1975
+ for (const f of model.fields) {
1976
+ if (f.kind === "object") {
1977
+ fields.push(` ${f.name}?: ${this.relationFilter(f)};`);
1978
+ } else {
1979
+ fields.push(` ${f.name}?: ${this.fieldFilter(f)};`);
1980
+ }
1981
+ }
1982
+ return `export type ${model.name}WhereInput = {
1983
+ AND?: ${model.name}WhereInput | ${model.name}WhereInput[];
1984
+ OR?: ${model.name}WhereInput[];
1985
+ NOT?: ${model.name}WhereInput | ${model.name}WhereInput[];
1986
+ ${fields.join("\n")}
1987
+ };
1988
+
1989
+ export type ${model.name}WhereUniqueInput = ${this.whereUnique(model)};`;
1990
+ }
1991
+ whereUnique(model) {
1992
+ const uniques = /* @__PURE__ */ new Set();
1993
+ for (const f of model.fields) if (f.isId || f.isUnique) uniques.add(f.name);
1994
+ for (const f of model.primaryKey) uniques.add(f);
1995
+ const parts = [...uniques].map((name) => {
1996
+ const field = model.fields.find((x) => x.name === name);
1997
+ return `{ ${name}: ${baseTsType(field, this.schema)} }`;
1998
+ });
1999
+ const base = `Partial<${model.name}WhereInput>`;
2000
+ return parts.length ? `(${parts.join(" | ")}) & ${base}` : base;
2001
+ }
2002
+ fieldFilter(field) {
2003
+ const base = baseTsType(field, this.schema);
2004
+ const filterName = FILTER_FOR_SCALAR[field.type] ?? null;
2005
+ if (field.kind === "enum") {
2006
+ return `${base} | { equals?: ${base}; in?: ${base}[]; notIn?: ${base}[]; not?: ${base} }`;
2007
+ }
2008
+ if (filterName) return `${base} | ${filterName}`;
2009
+ return `${base}`;
2010
+ }
2011
+ relationFilter(field) {
2012
+ if (field.isList) {
2013
+ return `{ some?: ${field.type}WhereInput; every?: ${field.type}WhereInput; none?: ${field.type}WhereInput }`;
2014
+ }
2015
+ return `${field.type}WhereInput | { is?: ${field.type}WhereInput | null; isNot?: ${field.type}WhereInput | null } | null`;
2016
+ }
2017
+ orderByInput(model) {
2018
+ const fields = scalarFields(model).map((f) => ` ${f.name}?: SortOrder;`).join("\n");
2019
+ return `export type ${model.name}OrderByInput = {
2020
+ ${fields}
2021
+ };`;
2022
+ }
2023
+ selectInclude(model) {
2024
+ const scalarSel = scalarFields(model).map((f) => ` ${f.name}?: boolean;`).join("\n");
2025
+ const relSel = relationFields(model).map((f) => ` ${f.name}?: boolean | ${f.type}FindManyArgs;`).join("\n");
2026
+ const include = relationFields(model).map((f) => ` ${f.name}?: boolean | ${f.type}FindManyArgs;`).join("\n");
2027
+ const listRelations = relationFields(model).filter((f) => f.isList);
2028
+ const countLine = listRelations.length ? `
2029
+ _count?: boolean | { select?: { ${listRelations.map((f) => `${f.name}?: boolean`).join("; ")} } };` : "";
2030
+ return `export type ${model.name}Select = {
2031
+ ${scalarSel}${relSel ? "\n" + relSel : ""}${countLine}
2032
+ };
2033
+
2034
+ export type ${model.name}Include = {
2035
+ ${include || " [k: string]: never;"}${countLine}
2036
+ };`;
2037
+ }
2038
+ dataInputs(model) {
2039
+ const foreignKeys2 = /* @__PURE__ */ new Set();
2040
+ for (const rel2 of relationFields(model)) {
2041
+ for (const fk of rel2.relation?.fields ?? []) foreignKeys2.add(fk);
2042
+ }
2043
+ const createScalars = scalarFields(model).filter((f) => !isGeneratedOnCreate(f)).map((f) => {
2044
+ const optional = isOptionalOnCreate(f) || foreignKeys2.has(f.name) ? "?" : "";
2045
+ return ` ${f.name}${optional}: ${this.inputScalarType(f)};`;
2046
+ });
2047
+ const createRelations = relationFields(model).map(
2048
+ (f) => ` ${f.name}?: ${this.nestedCreate(f)};`
2049
+ );
2050
+ const updateScalars = scalarFields(model).map(
2051
+ (f) => ` ${f.name}?: ${this.updateScalarType(f)};`
2052
+ );
2053
+ const updateRelations = relationFields(model).map(
2054
+ (f) => ` ${f.name}?: ${this.nestedUpdate(f)};`
2055
+ );
2056
+ return `export type ${model.name}CreateInput = {
2057
+ ${[...createScalars, ...createRelations].join("\n")}
2058
+ };
2059
+
2060
+ export type ${model.name}UpdateInput = {
2061
+ ${[...updateScalars, ...updateRelations].join("\n")}
2062
+ };`;
2063
+ }
2064
+ inputScalarType(field) {
2065
+ const base = baseTsType(field, this.schema);
2066
+ if (field.isList) return `${base}[]`;
2067
+ return field.isRequired ? base : `${base} | null`;
2068
+ }
2069
+ /**
2070
+ * Update value for a scalar field: a bare value, `{ set }`, and — for numeric
2071
+ * fields — the atomic operators increment/decrement/multiply/divide.
2072
+ */
2073
+ updateScalarType(field) {
2074
+ const value = this.inputScalarType(field);
2075
+ if (field.isList) return value;
2076
+ const base = baseTsType(field, this.schema);
2077
+ if (NUMERIC_SCALARS.has(field.type)) {
2078
+ const nullable = field.isRequired ? "" : " | null";
2079
+ return `${value} | { set?: ${base}${nullable}; increment?: ${base}; decrement?: ${base}; multiply?: ${base}; divide?: ${base} }`;
2080
+ }
2081
+ return `${value} | { set?: ${value} }`;
2082
+ }
2083
+ nestedCreate(field) {
2084
+ const t = field.type;
2085
+ if (field.isList) {
2086
+ return `{ create?: ${t}CreateInput | ${t}CreateInput[]; connect?: ${t}WhereUniqueInput | ${t}WhereUniqueInput[]; connectOrCreate?: { where: ${t}WhereUniqueInput; create: ${t}CreateInput } | { where: ${t}WhereUniqueInput; create: ${t}CreateInput }[] }`;
2087
+ }
2088
+ return `{ create?: ${t}CreateInput; connect?: ${t}WhereUniqueInput; connectOrCreate?: { where: ${t}WhereUniqueInput; create: ${t}CreateInput } }`;
2089
+ }
2090
+ nestedUpdate(field) {
2091
+ const t = field.type;
2092
+ if (field.isList) {
2093
+ 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[] }`;
2094
+ }
2095
+ return `{ create?: ${t}CreateInput; connect?: ${t}WhereUniqueInput; disconnect?: boolean }`;
2096
+ }
2097
+ argsTypes(model) {
2098
+ const n = model.name;
2099
+ return `export type ${n}Omit = Partial<Record<keyof ${n}, boolean>>;
2100
+
2101
+ export type ${n}FindManyArgs = {
2102
+ where?: ${n}WhereInput;
2103
+ orderBy?: ${n}OrderByInput | ${n}OrderByInput[];
2104
+ select?: ${n}Select;
2105
+ include?: ${n}Include;
2106
+ omit?: ${n}Omit;
2107
+ take?: number;
2108
+ skip?: number;
2109
+ cursor?: ${n}WhereUniqueInput;
2110
+ distinct?: (keyof ${n})[];
2111
+ };
2112
+
2113
+ export type ${n}FindFirstArgs = ${n}FindManyArgs;
2114
+
2115
+ export type ${n}FindUniqueArgs = {
2116
+ where: ${n}WhereUniqueInput;
2117
+ select?: ${n}Select;
2118
+ include?: ${n}Include;
2119
+ omit?: ${n}Omit;
2120
+ };
2121
+
2122
+ export type ${n}CreateArgs = {
2123
+ data: ${n}CreateInput;
2124
+ select?: ${n}Select;
2125
+ include?: ${n}Include;
2126
+ omit?: ${n}Omit;
2127
+ };
2128
+
2129
+ export type ${n}CreateManyArgs = { data: ${n}CreateInput[]; skipDuplicates?: boolean };
2130
+
2131
+ export type ${n}CreateManyAndReturnArgs = {
2132
+ data: ${n}CreateInput[];
2133
+ select?: ${n}Select;
2134
+ omit?: ${n}Omit;
2135
+ skipDuplicates?: boolean;
2136
+ };
2137
+
2138
+ export type ${n}UpdateArgs = {
2139
+ where: ${n}WhereUniqueInput;
2140
+ data: ${n}UpdateInput;
2141
+ select?: ${n}Select;
2142
+ include?: ${n}Include;
2143
+ omit?: ${n}Omit;
2144
+ };
2145
+
2146
+ export type ${n}UpdateManyArgs = { where?: ${n}WhereInput; data: ${n}UpdateInput };
2147
+
2148
+ export type ${n}UpsertArgs = {
2149
+ where: ${n}WhereUniqueInput;
2150
+ create: ${n}CreateInput;
2151
+ update: ${n}UpdateInput;
2152
+ select?: ${n}Select;
2153
+ include?: ${n}Include;
2154
+ omit?: ${n}Omit;
2155
+ };
2156
+
2157
+ export type ${n}DeleteArgs = { where: ${n}WhereUniqueInput; select?: ${n}Select; include?: ${n}Include };
2158
+ export type ${n}DeleteManyArgs = { where?: ${n}WhereInput };
2159
+
2160
+ export type ${n}CountArgs = { where?: ${n}WhereInput; take?: number; skip?: number };
2161
+
2162
+ export type ${n}AggregateArgs = {
2163
+ where?: ${n}WhereInput;
2164
+ _count?: true | Partial<Record<keyof ${n}, boolean>>;
2165
+ _avg?: Partial<Record<keyof ${n}, boolean>>;
2166
+ _sum?: Partial<Record<keyof ${n}, boolean>>;
2167
+ _min?: Partial<Record<keyof ${n}, boolean>>;
2168
+ _max?: Partial<Record<keyof ${n}, boolean>>;
2169
+ };
2170
+
2171
+ export type ${n}GroupByArgs = ${n}AggregateArgs & {
2172
+ by: (keyof ${n})[];
2173
+ having?: ${n}WhereInput;
2174
+ orderBy?: ${n}OrderByInput | ${n}OrderByInput[];
2175
+ };`;
2176
+ }
2177
+ delegateInterface(model) {
2178
+ const n = model.name;
2179
+ return `export interface ${n}Delegate {
2180
+ findMany<A extends ${n}FindManyArgs>(args?: A): Promise<${n}GetPayload<A>[]>;
2181
+ findFirst<A extends ${n}FindFirstArgs>(args?: A): ${n}Fluent<A, null>;
2182
+ findFirstOrThrow<A extends ${n}FindFirstArgs>(args?: A): ${n}Fluent<A, never>;
2183
+ findUnique<A extends ${n}FindUniqueArgs>(args: A): ${n}Fluent<A, null>;
2184
+ findUniqueOrThrow<A extends ${n}FindUniqueArgs>(args: A): ${n}Fluent<A, never>;
2185
+ create<A extends ${n}CreateArgs>(args: A): Promise<${n}GetPayload<A>>;
2186
+ createMany(args: ${n}CreateManyArgs): Promise<{ count: number }>;
2187
+ createManyAndReturn<A extends ${n}CreateManyAndReturnArgs>(args: A): Promise<${n}GetPayload<A>[]>;
2188
+ update<A extends ${n}UpdateArgs>(args: A): Promise<${n}GetPayload<A>>;
2189
+ updateMany(args: ${n}UpdateManyArgs): Promise<{ count: number }>;
2190
+ upsert<A extends ${n}UpsertArgs>(args: A): Promise<${n}GetPayload<A>>;
2191
+ delete<A extends ${n}DeleteArgs>(args: A): Promise<${n}GetPayload<A>>;
2192
+ deleteMany(args?: ${n}DeleteManyArgs): Promise<{ count: number }>;
2193
+ count(args?: ${n}CountArgs): Promise<number>;
2194
+ aggregate(args?: ${n}AggregateArgs): Promise<Record<string, any>>;
2195
+ groupBy(args: ${n}GroupByArgs): Promise<Record<string, any>[]>;
2196
+ }`;
2197
+ }
2198
+ // ---- client class -------------------------------------------------------
2199
+ clientClass() {
2200
+ const delegates = this.schema.models.map((m) => ` declare readonly ${lowerFirst(m.name)}: Prisma.${m.name}Delegate;`).join("\n");
2201
+ return `export type EmberClientOptions = {
2202
+ datasourceUrl?: string;
2203
+ datasource?: ClientOptions["datasource"];
2204
+ log?: ClientOptions["log"];
2205
+ };
2206
+
2207
+ export class EmberClient extends EmberClientBase {
2208
+ constructor(options: EmberClientOptions = {}) {
2209
+ super({ schema: schemaDocument, ...options });
2210
+ }
2211
+
2212
+ ${delegates}
2213
+ }`;
2214
+ }
2215
+ };
2216
+ function generateClientSource(schema) {
2217
+ return new ClientGenerator(schema).generate();
2218
+ }
2219
+ function writeClient(schema, outDir) {
2220
+ const source = generateClientSource(schema);
2221
+ const file = (0, import_node_path2.resolve)(outDir, "index.ts");
2222
+ (0, import_node_fs2.mkdirSync)((0, import_node_path2.dirname)(file), { recursive: true });
2223
+ (0, import_node_fs2.writeFileSync)(file, source, "utf8");
2224
+ return file;
2225
+ }
2226
+ var PAYLOAD_RESOLVER = `type $Scalar<M extends $ModelName> = $ScalarPayload[M];
2227
+ type $Relations<M extends $ModelName> = M extends keyof $RelationMap
2228
+ ? $RelationMap[M]
2229
+ : {};
2230
+
2231
+ // A relation sub-arg may be \`true\` (full payload) or a nested args object.
2232
+ type $NormalizeArgs<Sub> = Sub extends object ? Sub : {};
2233
+
2234
+ type $RelationResult<Info, Sub> = Info extends {
2235
+ model: infer RM extends $ModelName;
2236
+ isList: infer L;
2237
+ }
2238
+ ? L extends true
2239
+ ? $Payload<RM, $NormalizeArgs<Sub>>[]
2240
+ : Info extends { isNullable: true }
2241
+ ? $Payload<RM, $NormalizeArgs<Sub>> | null
2242
+ : $Payload<RM, $NormalizeArgs<Sub>>
2243
+ : never;
2244
+
2245
+ // To-many relation keys of a model (eligible for _count).
2246
+ type $ListRelations<M extends $ModelName> = {
2247
+ [K in keyof $Relations<M>]: $Relations<M>[K] extends { isList: true }
2248
+ ? K
2249
+ : never;
2250
+ }[keyof $Relations<M>];
2251
+
2252
+ type $CountResult<M extends $ModelName, C> = C extends { select: infer SC }
2253
+ ? { [K in keyof SC & $ListRelations<M>]: number }
2254
+ : { [K in $ListRelations<M> & string]: number };
2255
+
2256
+ type $CountPart<M extends $ModelName, A> = A extends { _count: infer C }
2257
+ ? { _count: $CountResult<M, C> }
2258
+ : {};
2259
+
2260
+ type $SelectResult<M extends $ModelName, S> = {
2261
+ [K in keyof S & keyof $Scalar<M>]: $Scalar<M>[K];
2262
+ } & {
2263
+ [K in keyof S & keyof $Relations<M>]: $RelationResult<$Relations<M>[K], S[K]>;
2264
+ } & $CountPart<M, S>;
2265
+
2266
+ type $IncludeResult<M extends $ModelName, I> = {
2267
+ [K in keyof I & keyof $Relations<M>]: $RelationResult<$Relations<M>[K], I[K]>;
2268
+ } & $CountPart<M, I>;
2269
+
2270
+ export type $Payload<M extends $ModelName, A> = A extends { select: infer S }
2271
+ ? $SelectResult<M, S>
2272
+ : A extends { include: infer I }
2273
+ ? $Scalar<M> & $IncludeResult<M, I>
2274
+ : $Scalar<M>;`;
2275
+ var NUMERIC_SCALARS = /* @__PURE__ */ new Set(["Int", "BigInt", "Float", "Decimal"]);
2276
+ var FILTER_FOR_SCALAR = {
2277
+ String: "StringFilter",
2278
+ Int: "IntFilter",
2279
+ BigInt: "BigIntFilter",
2280
+ Float: "FloatFilter",
2281
+ Decimal: "FloatFilter",
2282
+ Boolean: "BoolFilter",
2283
+ DateTime: "DateTimeFilter",
2284
+ Bytes: "BytesFilter",
2285
+ Json: "JsonFilter"
2286
+ };
2287
+ var SHARED_FILTERS = `export type StringFilter = {
2288
+ equals?: string;
2289
+ not?: string | StringFilter;
2290
+ in?: string[];
2291
+ notIn?: string[];
2292
+ lt?: string;
2293
+ lte?: string;
2294
+ gt?: string;
2295
+ gte?: string;
2296
+ contains?: string;
2297
+ startsWith?: string;
2298
+ endsWith?: string;
2299
+ mode?: QueryMode;
2300
+ };
2301
+
2302
+ export type IntFilter = {
2303
+ equals?: number;
2304
+ not?: number | IntFilter;
2305
+ in?: number[];
2306
+ notIn?: number[];
2307
+ lt?: number;
2308
+ lte?: number;
2309
+ gt?: number;
2310
+ gte?: number;
2311
+ };
2312
+
2313
+ export type FloatFilter = IntFilter;
2314
+ export type BigIntFilter = {
2315
+ equals?: bigint;
2316
+ not?: bigint | BigIntFilter;
2317
+ in?: bigint[];
2318
+ notIn?: bigint[];
2319
+ lt?: bigint;
2320
+ lte?: bigint;
2321
+ gt?: bigint;
2322
+ gte?: bigint;
2323
+ };
2324
+
2325
+ export type BoolFilter = { equals?: boolean; not?: boolean };
2326
+
2327
+ export type DateTimeFilter = {
2328
+ equals?: Date;
2329
+ not?: Date | DateTimeFilter;
2330
+ in?: Date[];
2331
+ notIn?: Date[];
2332
+ lt?: Date;
2333
+ lte?: Date;
2334
+ gt?: Date;
2335
+ gte?: Date;
2336
+ };
2337
+
2338
+ export type BytesFilter = { equals?: Buffer; not?: Buffer };
2339
+
2340
+ // Firebird has no JSON SQL functions: filters operate on the serialized text.
2341
+ // 'path' is accepted by the type but rejected at runtime on Firebird.
2342
+ export type JsonFilter = {
2343
+ equals?: JsonValue;
2344
+ not?: JsonValue;
2345
+ string_contains?: string;
2346
+ string_starts_with?: string;
2347
+ string_ends_with?: string;
2348
+ path?: string[];
2349
+ };`;
2350
+ function isGeneratedOnCreate(field) {
2351
+ return field.default?.function?.name === "autoincrement";
2352
+ }
2353
+ function isOptionalOnCreate(field) {
2354
+ return !field.isRequired || !!field.default || field.isUpdatedAt || field.isList;
2355
+ }
2356
+ function indentBlock(text) {
2357
+ return text.split("\n").map((l) => l ? " " + l : l).join("\n");
2358
+ }
2359
+
2360
+ // src/migrate/index.ts
2361
+ var import_node_fs4 = require("fs");
2362
+ var import_node_path4 = require("path");
2363
+
2364
+ // src/migrate/diff.ts
2365
+ function diffSchemas(desired, current) {
2366
+ const currentByTable = new Map(
2367
+ current.models.map((m) => [modelTable(m), m])
2368
+ );
2369
+ const desiredTables = new Set(desired.models.map((m) => modelTable(m)));
2370
+ const createdModels = [];
2371
+ const modelChanges = [];
2372
+ for (const model of desired.models) {
2373
+ const table = modelTable(model);
2374
+ const currentModel = currentByTable.get(table);
2375
+ if (!currentModel) {
2376
+ createdModels.push(model);
2377
+ continue;
2378
+ }
2379
+ const change = diffModel(desired, model, currentModel, table);
2380
+ if (hasModelChanges(change)) modelChanges.push(change);
2381
+ }
2382
+ const droppedTables = current.models.map((m) => modelTable(m)).filter((t) => !desiredTables.has(t));
2383
+ return { createdModels, droppedTables, modelChanges };
2384
+ }
2385
+ function diffModel(desired, model, current, table) {
2386
+ const desiredCols = new Map(
2387
+ scalarFields(model).map((f) => [fieldColumn(f), f])
2388
+ );
2389
+ const currentCols = new Map(
2390
+ scalarFields(current).map((f) => [fieldColumn(f), f])
2391
+ );
2392
+ const addedColumns = [];
2393
+ const changedColumns = [];
2394
+ for (const [col, field] of desiredCols) {
2395
+ const existing = currentCols.get(col);
2396
+ if (!existing) {
2397
+ addedColumns.push(field);
2398
+ continue;
2399
+ }
2400
+ const typeChanged = !sameColumnType(field, existing);
2401
+ const nullabilityChanged = field.isRequired !== existing.isRequired;
2402
+ if (typeChanged || nullabilityChanged) {
2403
+ changedColumns.push({ field, table, typeChanged, nullabilityChanged });
2404
+ }
2405
+ }
2406
+ const droppedColumns = [...currentCols.keys()].filter(
2407
+ (c) => !desiredCols.has(c)
2408
+ );
2409
+ const currentUniqueSets = uniqueColumnSets(current).map(setKey);
2410
+ const addedUniques = uniqueColumnSets(model).filter((cols) => !currentUniqueSets.includes(setKey(cols))).map((cols) => ({ name: constraintName("UQ", table, cols), columns: cols }));
2411
+ const currentFkSets = foreignKeys(desired, current).map(fkKey);
2412
+ const addedForeignKeys = foreignKeys(desired, model).filter((fk) => !currentFkSets.includes(fkKey(fk))).map((fk) => ({ ...fk, name: constraintName("FK", table, fk.columns) }));
2413
+ return {
2414
+ model,
2415
+ table,
2416
+ addedColumns,
2417
+ droppedColumns,
2418
+ changedColumns,
2419
+ addedUniques,
2420
+ addedForeignKeys
2421
+ };
2422
+ }
2423
+ function hasModelChanges(c) {
2424
+ return c.addedColumns.length > 0 || c.droppedColumns.length > 0 || c.changedColumns.length > 0 || c.addedUniques.length > 0 || c.addedForeignKeys.length > 0;
2425
+ }
2426
+ function uniqueColumnSets(model) {
2427
+ const sets = [];
2428
+ for (const f of scalarFields(model)) {
2429
+ if (f.isUnique) sets.push([fieldColumn(f)]);
2430
+ }
2431
+ for (const u of model.uniqueIndexes) {
2432
+ sets.push(u.fields.map((name) => columnFor(model, name)));
2433
+ }
2434
+ return sets;
2435
+ }
2436
+ function indexSpecs(model) {
2437
+ return model.indexes.map((i) => ({
2438
+ name: i.name ?? constraintName("IDX", modelTable(model), i.fields.map((n) => columnFor(model, n))),
2439
+ columns: i.fields.map((name) => columnFor(model, name)),
2440
+ unique: i.unique
2441
+ }));
2442
+ }
2443
+ function foreignKeys(schema, model) {
2444
+ const out = [];
2445
+ for (const f of relationFields(model)) {
2446
+ const rel2 = f.relation;
2447
+ if (!rel2?.fields?.length) continue;
2448
+ const refModel = schema.models.find((m) => m.name === f.type);
2449
+ if (!refModel) continue;
2450
+ out.push({
2451
+ columns: rel2.fields.map((name) => columnFor(model, name)),
2452
+ refTable: modelTable(refModel),
2453
+ refColumns: (rel2.references ?? []).map((name) => columnFor(refModel, name)),
2454
+ onDelete: rel2.onDelete,
2455
+ onUpdate: rel2.onUpdate
2456
+ });
2457
+ }
2458
+ return out;
2459
+ }
2460
+ function columnFor(model, fieldName) {
2461
+ const f = model.fields.find((x) => x.name === fieldName);
2462
+ return f ? fieldColumn(f) : fieldName.toUpperCase();
2463
+ }
2464
+ function sameColumnType(a, b) {
2465
+ if (a.type !== b.type) return false;
2466
+ return sameNative(a.nativeType, b.nativeType);
2467
+ }
2468
+ function sameNative(a, b) {
2469
+ if (!a && !b) return true;
2470
+ if (!a || !b) return true;
2471
+ return a.name === b.name && a.args.join(",") === b.args.join(",");
2472
+ }
2473
+ function setKey(cols) {
2474
+ return [...cols].sort().join(",");
2475
+ }
2476
+ function fkKey(fk) {
2477
+ return `${setKey(fk.columns)}->${fk.refTable}(${setKey(fk.refColumns)})`;
2478
+ }
2479
+ function constraintName(prefix, table, columns) {
2480
+ const base = `${prefix}_${table}_${columns.join("_")}`.replace(/[^A-Za-z0-9_]/g, "_");
2481
+ if (base.length <= 31) return base;
2482
+ let hash = 0;
2483
+ for (let i = 0; i < base.length; i++) hash = hash * 31 + base.charCodeAt(i) | 0;
2484
+ const suffix = Math.abs(hash).toString(36).slice(0, 6);
2485
+ return `${base.slice(0, 24)}_${suffix}`;
2486
+ }
2487
+
2488
+ // src/migrate/ddl.ts
2489
+ var FirebirdDdl = class {
2490
+ constructor(d) {
2491
+ this.d = d;
2492
+ }
2493
+ d;
2494
+ // ---- column types -------------------------------------------------------
2495
+ /** Firebird column type for a field, honoring its `@db.*` native type. */
2496
+ columnType(field) {
2497
+ const native = field.nativeType;
2498
+ if (native) {
2499
+ const args = native.args.length ? `(${native.args.join(", ")})` : "";
2500
+ switch (native.name) {
2501
+ case "VarChar":
2502
+ return `VARCHAR${args || "(255)"}`;
2503
+ case "Char":
2504
+ return `CHAR${args || "(1)"}`;
2505
+ case "Text":
2506
+ return "BLOB SUB_TYPE TEXT";
2507
+ case "SmallInt":
2508
+ return "SMALLINT";
2509
+ case "Integer":
2510
+ return "INTEGER";
2511
+ case "BigInt":
2512
+ return "BIGINT";
2513
+ case "Float":
2514
+ return "FLOAT";
2515
+ case "DoublePrecision":
2516
+ return "DOUBLE PRECISION";
2517
+ case "Decimal":
2518
+ return `DECIMAL${args || "(18, 4)"}`;
2519
+ case "Boolean":
2520
+ return this.d.booleanColumnType();
2521
+ case "Date":
2522
+ return "DATE";
2523
+ case "Time":
2524
+ return "TIME";
2525
+ case "Timestamp":
2526
+ return "TIMESTAMP";
2527
+ case "Blob":
2528
+ return "BLOB SUB_TYPE BINARY";
2529
+ }
2530
+ }
2531
+ if (field.type === "Boolean") return this.d.booleanColumnType();
2532
+ return DEFAULT_DDL_TYPE[field.type] ?? "VARCHAR(255)";
2533
+ }
2534
+ /** `"COL" <type> [GENERATED ...] [DEFAULT ...] [NOT NULL]`. */
2535
+ columnDefinition(field) {
2536
+ const parts = [this.d.quoteId(fieldColumn(field)), this.columnType(field)];
2537
+ if (isIdentity(field) && this.d.supportsIdentity) {
2538
+ parts.push("GENERATED BY DEFAULT AS IDENTITY");
2539
+ } else if (!isIdentity(field)) {
2540
+ const def = defaultClause(field.default);
2541
+ if (def) parts.push(def);
2542
+ }
2543
+ if (field.isRequired) parts.push("NOT NULL");
2544
+ return parts.join(" ");
2545
+ }
2546
+ /**
2547
+ * On Firebird 2.1/2.5 (no IDENTITY), emulate autoincrement with a SEQUENCE
2548
+ * and a BEFORE INSERT trigger. Returns the extra DDL objects to create after
2549
+ * the table; an empty array when IDENTITY is supported.
2550
+ */
2551
+ autoIncrementObjects(model) {
2552
+ if (this.d.supportsIdentity) return [];
2553
+ const table = modelTable(model);
2554
+ const out = [];
2555
+ for (const field of scalarFields(model)) {
2556
+ if (isIdentity(field)) {
2557
+ out.push(...this.autoIncrementForColumn(table, fieldColumn(field)));
2558
+ }
2559
+ }
2560
+ return out;
2561
+ }
2562
+ autoIncrementForColumn(table, column) {
2563
+ if (this.d.supportsIdentity) return [];
2564
+ const seq = capName(`GEN_${table}_${column}`);
2565
+ const trig = capName(`${table}_BI_${column}`);
2566
+ const qTable = this.d.quoteId(table);
2567
+ const qCol = this.d.quoteId(column);
2568
+ const qSeq = this.d.quoteId(seq);
2569
+ return [
2570
+ `CREATE SEQUENCE ${qSeq}`,
2571
+ `CREATE TRIGGER ${this.d.quoteId(trig)} FOR ${qTable} ACTIVE BEFORE INSERT POSITION 0 AS
2572
+ BEGIN
2573
+ IF (NEW.${qCol} IS NULL) THEN NEW.${qCol} = GEN_ID(${qSeq}, 1);
2574
+ END`
2575
+ ];
2576
+ }
2577
+ // ---- table-level --------------------------------------------------------
2578
+ createTable(model) {
2579
+ const cols = scalarFields(model).map((f) => " " + this.columnDefinition(f));
2580
+ const pk = idFields(model);
2581
+ const lines = [...cols];
2582
+ if (pk.length > 0) {
2583
+ lines.push(
2584
+ ` PRIMARY KEY (${pk.map((f) => this.d.quoteId(fieldColumn(f))).join(", ")})`
2585
+ );
2586
+ }
2587
+ return `CREATE TABLE ${this.d.quoteId(modelTable(model))} (
2588
+ ${lines.join(",\n")}
2589
+ )`;
2590
+ }
2591
+ dropTable(table) {
2592
+ return `DROP TABLE ${this.d.quoteId(table)}`;
2593
+ }
2594
+ addColumn(table, field) {
2595
+ return `ALTER TABLE ${this.d.quoteId(table)} ADD ${this.columnDefinition(field)}`;
2596
+ }
2597
+ dropColumn(table, column) {
2598
+ return `ALTER TABLE ${this.d.quoteId(table)} DROP ${this.d.quoteId(column)}`;
2599
+ }
2600
+ /** Change a column's data type (Firebird: ALTER COLUMN ... TYPE ...). */
2601
+ alterColumnType(table, field) {
2602
+ return `ALTER TABLE ${this.d.quoteId(table)} ALTER COLUMN ${this.d.quoteId(
2603
+ fieldColumn(field)
2604
+ )} TYPE ${this.columnType(field)}`;
2605
+ }
2606
+ setNotNull(table, column, notNull) {
2607
+ const action = notNull ? "SET NOT NULL" : "DROP NOT NULL";
2608
+ return `ALTER TABLE ${this.d.quoteId(table)} ALTER COLUMN ${this.d.quoteId(
2609
+ column
2610
+ )} ${action}`;
2611
+ }
2612
+ // ---- constraints & indexes ---------------------------------------------
2613
+ addUnique(table, name, columns) {
2614
+ return `ALTER TABLE ${this.d.quoteId(table)} ADD CONSTRAINT ${this.d.quoteId(
2615
+ name
2616
+ )} UNIQUE (${columns.map((c) => this.d.quoteId(c)).join(", ")})`;
2617
+ }
2618
+ dropConstraint(table, name) {
2619
+ return `ALTER TABLE ${this.d.quoteId(table)} DROP CONSTRAINT ${this.d.quoteId(name)}`;
2620
+ }
2621
+ addForeignKey(table, name, columns, refTable, refColumns, onDelete, onUpdate) {
2622
+ let sql = `ALTER TABLE ${this.d.quoteId(table)} ADD CONSTRAINT ${this.d.quoteId(
2623
+ name
2624
+ )} FOREIGN KEY (${columns.map((c) => this.d.quoteId(c)).join(", ")}) REFERENCES ${this.d.quoteId(
2625
+ refTable
2626
+ )} (${refColumns.map((c) => this.d.quoteId(c)).join(", ")})`;
2627
+ if (onUpdate) sql += ` ON UPDATE ${referentialSql(onUpdate)}`;
2628
+ if (onDelete) sql += ` ON DELETE ${referentialSql(onDelete)}`;
2629
+ return sql;
2630
+ }
2631
+ createIndex(table, name, columns, unique) {
2632
+ return `CREATE ${unique ? "UNIQUE " : ""}INDEX ${this.d.quoteId(name)} ON ${this.d.quoteId(
2633
+ table
2634
+ )} (${columns.map((c) => this.d.quoteId(c)).join(", ")})`;
2635
+ }
2636
+ dropIndex(name) {
2637
+ return `DROP INDEX ${this.d.quoteId(name)}`;
2638
+ }
2639
+ };
2640
+ var DEFAULT_DDL_TYPE = {
2641
+ String: "VARCHAR(255)",
2642
+ Boolean: "BOOLEAN",
2643
+ Int: "INTEGER",
2644
+ BigInt: "BIGINT",
2645
+ Float: "DOUBLE PRECISION",
2646
+ Decimal: "DECIMAL(18, 4)",
2647
+ DateTime: "TIMESTAMP",
2648
+ Bytes: "BLOB SUB_TYPE BINARY",
2649
+ Json: "BLOB SUB_TYPE TEXT"
2650
+ };
2651
+ function isIdentity(field) {
2652
+ return field.default?.function?.name === "autoincrement";
2653
+ }
2654
+ function capName(name) {
2655
+ const safe = name.replace(/[^A-Za-z0-9_]/g, "_");
2656
+ if (safe.length <= 31) return safe;
2657
+ let hash = 0;
2658
+ for (let i = 0; i < safe.length; i++) hash = hash * 31 + safe.charCodeAt(i) | 0;
2659
+ return `${safe.slice(0, 24)}_${Math.abs(hash).toString(36).slice(0, 6)}`;
2660
+ }
2661
+ function defaultClause(def) {
2662
+ if (!def) return null;
2663
+ if (def.function) {
2664
+ switch (def.function.name) {
2665
+ case "now":
2666
+ return "DEFAULT CURRENT_TIMESTAMP";
2667
+ case "uuid":
2668
+ case "cuid":
2669
+ return null;
2670
+ // generated app-side
2671
+ default:
2672
+ return null;
2673
+ }
2674
+ }
2675
+ if (def.literal === void 0) return null;
2676
+ if (typeof def.literal === "boolean") {
2677
+ return `DEFAULT ${def.literal ? "TRUE" : "FALSE"}`;
2678
+ }
2679
+ if (typeof def.literal === "number") return `DEFAULT ${def.literal}`;
2680
+ return `DEFAULT '${String(def.literal).replace(/'/g, "''")}'`;
2681
+ }
2682
+ function referentialSql(action) {
2683
+ switch (action) {
2684
+ case "Cascade":
2685
+ return "CASCADE";
2686
+ case "SetNull":
2687
+ return "SET NULL";
2688
+ case "SetDefault":
2689
+ return "SET DEFAULT";
2690
+ case "NoAction":
2691
+ return "NO ACTION";
2692
+ case "Restrict":
2693
+ return "NO ACTION";
2694
+ // Firebird has no RESTRICT; NO ACTION is closest
2695
+ default:
2696
+ return "NO ACTION";
2697
+ }
2698
+ }
2699
+
2700
+ // src/migrate/planner.ts
2701
+ function planMigration(diff, desired, dialect) {
2702
+ const ddl = new FirebirdDdl(dialect);
2703
+ const drops = [];
2704
+ const creates = [];
2705
+ const alters = [];
2706
+ const constraints = [];
2707
+ const indexes = [];
2708
+ const foreignKeyStmts = [];
2709
+ for (const change of diff.modelChanges) {
2710
+ for (const col of change.droppedColumns) {
2711
+ drops.push(ddl.dropColumn(change.table, col));
2712
+ }
2713
+ }
2714
+ for (const table of diff.droppedTables) {
2715
+ drops.push(ddl.dropTable(table));
2716
+ }
2717
+ for (const model of diff.createdModels) {
2718
+ const table = modelTable(model);
2719
+ creates.push(ddl.createTable(model));
2720
+ creates.push(...ddl.autoIncrementObjects(model));
2721
+ for (const cols of uniqueColumnSets(model)) {
2722
+ if (cols.length === 1) continue;
2723
+ constraints.push(
2724
+ ddl.addUnique(table, constraintName("UQ", table, cols), cols)
2725
+ );
2726
+ }
2727
+ for (const idx of indexSpecs(model)) {
2728
+ indexes.push(ddl.createIndex(table, idx.name, idx.columns, idx.unique));
2729
+ }
2730
+ for (const fk of foreignKeys(desired, model)) {
2731
+ foreignKeyStmts.push(
2732
+ ddl.addForeignKey(
2733
+ table,
2734
+ constraintName("FK", table, fk.columns),
2735
+ fk.columns,
2736
+ fk.refTable,
2737
+ fk.refColumns,
2738
+ fk.onDelete,
2739
+ fk.onUpdate
2740
+ )
2741
+ );
2742
+ }
2743
+ }
2744
+ for (const change of diff.modelChanges) {
2745
+ for (const field of change.addedColumns) {
2746
+ alters.push(ddl.addColumn(change.table, field));
2747
+ if (isIdentity(field)) {
2748
+ creates.push(
2749
+ ...ddl.autoIncrementForColumn(change.table, fieldColumn(field))
2750
+ );
2751
+ }
2752
+ }
2753
+ for (const cc of change.changedColumns) {
2754
+ if (cc.typeChanged) alters.push(ddl.alterColumnType(cc.table, cc.field));
2755
+ if (cc.nullabilityChanged) {
2756
+ alters.push(
2757
+ ddl.setNotNull(cc.table, columnName(cc), cc.field.isRequired)
2758
+ );
2759
+ }
2760
+ }
2761
+ for (const uq of change.addedUniques) {
2762
+ if (uq.columns.length === 1) {
2763
+ constraints.push(ddl.addUnique(change.table, uq.name, uq.columns));
2764
+ } else {
2765
+ constraints.push(ddl.addUnique(change.table, uq.name, uq.columns));
2766
+ }
2767
+ }
2768
+ for (const fk of change.addedForeignKeys) {
2769
+ foreignKeyStmts.push(
2770
+ ddl.addForeignKey(
2771
+ change.table,
2772
+ fk.name,
2773
+ fk.columns,
2774
+ fk.refTable,
2775
+ fk.refColumns,
2776
+ fk.onDelete,
2777
+ fk.onUpdate
2778
+ )
2779
+ );
2780
+ }
2781
+ }
2782
+ return [
2783
+ ...drops,
2784
+ ...creates,
2785
+ ...alters,
2786
+ ...constraints,
2787
+ ...indexes,
2788
+ ...foreignKeyStmts
2789
+ ];
2790
+ }
2791
+ var BREAKPOINT = "--> statement-breakpoint";
2792
+ function renderMigrationSql(statements) {
2793
+ if (statements.length === 0) return "-- This migration is empty.\n";
2794
+ return statements.join(`
2795
+ ${BREAKPOINT}
2796
+ `) + "\n";
2797
+ }
2798
+ function splitStatements(sql) {
2799
+ return sql.split(BREAKPOINT).map((s) => stripComments(s).trim().replace(/;\s*$/, "").trim()).filter((s) => s.length > 0);
2800
+ }
2801
+ function stripComments(sql) {
2802
+ return sql.split("\n").filter((line) => !line.trim().startsWith("--")).join("\n");
2803
+ }
2804
+ function columnName(cc) {
2805
+ return cc.field.dbName ?? cc.field.name.toUpperCase();
2806
+ }
2807
+
2808
+ // src/migrate/history.ts
2809
+ var import_node_fs3 = require("fs");
2810
+ var import_node_path3 = require("path");
2811
+ var HISTORY_TABLE = "_EMBER_MIGRATIONS";
2812
+ async function ensureHistoryTable(tx, dialect) {
2813
+ const exists = await tx.query(
2814
+ `SELECT COUNT(*) AS N FROM RDB$RELATIONS WHERE RDB$RELATION_NAME = ?`,
2815
+ [HISTORY_TABLE]
2816
+ );
2817
+ if (Number(exists[0]?.N ?? 0) > 0) return;
2818
+ const t = dialect.quoteId(HISTORY_TABLE);
2819
+ await tx.query(
2820
+ `CREATE TABLE ${t} (
2821
+ ${dialect.quoteId("ID")} VARCHAR(128) NOT NULL PRIMARY KEY,
2822
+ ${dialect.quoteId("CHECKSUM")} VARCHAR(64),
2823
+ ${dialect.quoteId("STEPS")} INTEGER,
2824
+ ${dialect.quoteId("APPLIED_AT")} TIMESTAMP DEFAULT CURRENT_TIMESTAMP
2825
+ )`
2826
+ );
2827
+ }
2828
+ async function appliedMigrations(tx, dialect) {
2829
+ const t = dialect.quoteId(HISTORY_TABLE);
2830
+ const rows = await tx.query(
2831
+ `SELECT ${dialect.quoteId("ID")} AS "id", ${dialect.quoteId("CHECKSUM")} AS "checksum", ${dialect.quoteId("STEPS")} AS "steps" FROM ${t} ORDER BY ${dialect.quoteId("ID")}`
2832
+ );
2833
+ return rows.map((r) => ({
2834
+ id: String(r.id).trim(),
2835
+ checksum: r.checksum == null ? "" : String(r.checksum).trim(),
2836
+ steps: Number(r.steps ?? 0)
2837
+ }));
2838
+ }
2839
+ async function recordMigration(tx, dialect, migration) {
2840
+ const t = dialect.quoteId(HISTORY_TABLE);
2841
+ await tx.query(
2842
+ `INSERT INTO ${t} (${dialect.quoteId("ID")}, ${dialect.quoteId("CHECKSUM")}, ${dialect.quoteId("STEPS")}) VALUES (?, ?, ?)`,
2843
+ [migration.id, migration.checksum, migration.steps]
2844
+ );
2845
+ }
2846
+ function listLocalMigrations(migrationsDir) {
2847
+ if (!(0, import_node_fs3.existsSync)(migrationsDir)) return [];
2848
+ return (0, import_node_fs3.readdirSync)(migrationsDir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name).sort().map((id) => {
2849
+ const dir = (0, import_node_path3.join)(migrationsDir, id);
2850
+ const file = (0, import_node_path3.join)(dir, "migration.sql");
2851
+ return {
2852
+ id,
2853
+ dir,
2854
+ sql: (0, import_node_fs3.existsSync)(file) ? (0, import_node_fs3.readFileSync)(file, "utf8") : ""
2855
+ };
2856
+ }).filter((m) => m.sql.length > 0);
2857
+ }
2858
+ function checksum(sql) {
2859
+ let h = 2166136261;
2860
+ for (let i = 0; i < sql.length; i++) {
2861
+ h ^= sql.charCodeAt(i);
2862
+ h = Math.imul(h, 16777619);
2863
+ }
2864
+ return (h >>> 0).toString(16).padStart(8, "0");
2865
+ }
2866
+
2867
+ // src/migrate/index.ts
2868
+ var Migrator = class {
2869
+ constructor(driver, desired, migrationsDir, dialect) {
2870
+ this.driver = driver;
2871
+ this.desired = desired;
2872
+ this.migrationsDir = migrationsDir;
2873
+ this.dialect = dialect ?? new FirebirdDialect();
2874
+ }
2875
+ driver;
2876
+ desired;
2877
+ migrationsDir;
2878
+ dialect;
2879
+ /** Compute the diff between the desired schema and the live database. */
2880
+ async diff() {
2881
+ const current = await this.currentSchema();
2882
+ return diffSchemas(this.desired, current);
2883
+ }
2884
+ /** Plan (but do not apply) the DDL needed to reach the desired schema. */
2885
+ async plan() {
2886
+ const diff = await this.diff();
2887
+ return planMigration(diff, this.desired, this.dialect);
2888
+ }
2889
+ /**
2890
+ * `migrate dev`: create a timestamped migration from the current diff, apply
2891
+ * it, and record it in history.
2892
+ */
2893
+ async dev(name = "migration") {
2894
+ const statements = await this.plan();
2895
+ if (statements.length === 0) return { empty: true, statements: [] };
2896
+ const id = `${timestamp()}_${slug(name)}`;
2897
+ const dir = (0, import_node_path4.join)(this.migrationsDir, id);
2898
+ const body = renderMigrationSql(statements);
2899
+ (0, import_node_fs4.mkdirSync)(dir, { recursive: true });
2900
+ (0, import_node_fs4.writeFileSync)((0, import_node_path4.join)(dir, "migration.sql"), body, "utf8");
2901
+ await this.driver.transaction(async (tx) => {
2902
+ await ensureHistoryTable(tx, this.dialect);
2903
+ for (const stmt of statements) await tx.query(stmt);
2904
+ await recordMigration(tx, this.dialect, {
2905
+ id,
2906
+ checksum: checksum(body),
2907
+ steps: statements.length
2908
+ });
2909
+ });
2910
+ return { empty: false, id, dir, statements };
2911
+ }
2912
+ /**
2913
+ * `db push`: apply the diff directly to the database without writing a
2914
+ * migration file (prototyping flow).
2915
+ */
2916
+ async push() {
2917
+ const statements = await this.plan();
2918
+ if (statements.length === 0) return { statements: [] };
2919
+ await this.driver.transaction(async (tx) => {
2920
+ for (const stmt of statements) await tx.query(stmt);
2921
+ });
2922
+ return { statements };
2923
+ }
2924
+ /** `migrate deploy`: apply every on-disk migration not yet recorded. */
2925
+ async deploy() {
2926
+ const local = listLocalMigrations(this.migrationsDir);
2927
+ const applied = [];
2928
+ const known = await this.driver.transaction(async (tx) => {
2929
+ await ensureHistoryTable(tx, this.dialect);
2930
+ return new Set((await appliedMigrations(tx, this.dialect)).map((m) => m.id));
2931
+ });
2932
+ for (const migration of local) {
2933
+ if (known.has(migration.id)) continue;
2934
+ const statements = splitStatements(migration.sql);
2935
+ await this.driver.transaction(async (tx) => {
2936
+ for (const stmt of statements) await tx.query(stmt);
2937
+ await recordMigration(tx, this.dialect, {
2938
+ id: migration.id,
2939
+ checksum: checksum(migration.sql),
2940
+ steps: statements.length
2941
+ });
2942
+ });
2943
+ applied.push({ id: migration.id, steps: statements.length });
2944
+ }
2945
+ return { applied };
2946
+ }
2947
+ /** `migrate status`: list applied vs pending migrations. */
2948
+ async status() {
2949
+ const local = listLocalMigrations(this.migrationsDir).map((m) => m.id);
2950
+ const applied = await this.driver.transaction(async (tx) => {
2951
+ await ensureHistoryTable(tx, this.dialect);
2952
+ return (await appliedMigrations(tx, this.dialect)).map((m) => m.id);
2953
+ });
2954
+ const appliedSet = new Set(applied);
2955
+ return {
2956
+ applied,
2957
+ pending: local.filter((id) => !appliedSet.has(id))
2958
+ };
2959
+ }
2960
+ async currentSchema() {
2961
+ const introspector = new Introspector(this.driver);
2962
+ const current = await introspector.introspect();
2963
+ current.models = current.models.filter(
2964
+ (m) => modelTable(m) !== HISTORY_TABLE
2965
+ );
2966
+ return current;
2967
+ }
2968
+ };
2969
+ function timestamp() {
2970
+ const d = /* @__PURE__ */ new Date();
2971
+ const p = (n, w = 2) => String(n).padStart(w, "0");
2972
+ return `${d.getUTCFullYear()}${p(d.getUTCMonth() + 1)}${p(d.getUTCDate())}${p(d.getUTCHours())}${p(d.getUTCMinutes())}${p(d.getUTCSeconds())}`;
2973
+ }
2974
+ function slug(name) {
2975
+ return name.toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_|_$/g, "") || "migration";
2976
+ }
2977
+
2978
+ // src/cli/commands.ts
2979
+ var import_node_fs6 = require("fs");
2980
+ var DEFAULT_SCHEMA = "ember/schema.ember";
2981
+ var STARTER_SCHEMA = `datasource db {
2982
+ provider = "firebird"
2983
+ url = env("DATABASE_URL")
2984
+ }
2985
+
2986
+ generator client {
2987
+ provider = "ember-client-js"
2988
+ output = "../generated"
2989
+ }
2990
+
2991
+ /// Example model \u2014 replace with your own or run \`ember db pull\`.
2992
+ model User {
2993
+ id Int @id @default(autoincrement())
2994
+ email String @unique
2995
+ name String?
2996
+ createdAt DateTime @default(now())
2997
+ }
2998
+ `;
2999
+ function init(ctx) {
3000
+ const target = (0, import_node_path5.resolve)(ctx.cwd, DEFAULT_SCHEMA);
3001
+ if ((0, import_node_fs5.existsSync)(target)) {
3002
+ ctx.error(`Schema already exists at ${target}`);
3003
+ return 1;
3004
+ }
3005
+ (0, import_node_fs5.mkdirSync)((0, import_node_path5.dirname)(target), { recursive: true });
3006
+ (0, import_node_fs5.writeFileSync)(target, STARTER_SCHEMA, "utf8");
3007
+ ctx.log(`Created ${rel(ctx.cwd, target)}`);
3008
+ ctx.log("Set DATABASE_URL and run `ember db pull` or `ember generate`.");
3009
+ return 0;
3010
+ }
3011
+ function validate(ctx, schemaPath) {
3012
+ const path = requireSchema(ctx, schemaPath);
3013
+ if (!path) return 1;
3014
+ loadSchema(path);
3015
+ ctx.log(`Schema at ${rel(ctx.cwd, path)} is valid.`);
3016
+ return 0;
3017
+ }
3018
+ function format(ctx, schemaPath) {
3019
+ const path = requireSchema(ctx, schemaPath);
3020
+ if (!path) return 1;
3021
+ const source = (0, import_node_fs6.readFileSync)(path, "utf8");
3022
+ (0, import_node_fs5.writeFileSync)(path, formatSchema(source, path), "utf8");
3023
+ ctx.log(`Formatted ${rel(ctx.cwd, path)}`);
3024
+ return 0;
3025
+ }
3026
+ function generate(ctx, schemaPath) {
3027
+ const path = requireSchema(ctx, schemaPath);
3028
+ if (!path) return 1;
3029
+ const { document } = loadSchema(path);
3030
+ const gen = document.generators[0];
3031
+ const out = (0, import_node_path5.resolve)((0, import_node_path5.dirname)(path), gen?.output ?? "../generated");
3032
+ const file = writeClient(document, out);
3033
+ ctx.log(`Generated client at ${rel(ctx.cwd, file)}`);
3034
+ return 0;
3035
+ }
3036
+ async function dbPull(ctx, options) {
3037
+ const path = findSchemaPath(ctx.cwd, options.schemaPath) ?? (0, import_node_path5.resolve)(ctx.cwd, DEFAULT_SCHEMA);
3038
+ let url = options.url;
3039
+ let envVar = "DATABASE_URL";
3040
+ if (!url && (0, import_node_fs5.existsSync)(path)) {
3041
+ const { document } = loadSchema(path);
3042
+ url = resolveDatasourceUrl(document, (0, import_node_path5.dirname)(path));
3043
+ if (document.datasource?.url.kind === "env") {
3044
+ envVar = document.datasource.url.value;
3045
+ }
3046
+ }
3047
+ url ??= process.env.DATABASE_URL;
3048
+ if (!url) {
3049
+ ctx.error(
3050
+ "No database URL. Set DATABASE_URL, pass --url, or add a datasource block."
3051
+ );
3052
+ return 1;
3053
+ }
3054
+ const driver = createDriver(url);
3055
+ try {
3056
+ await driver.connect();
3057
+ const introspector = new Introspector(driver);
3058
+ const document = await introspector.introspect({
3059
+ datasource: { name: "db", provider: "firebird", envVar }
3060
+ });
3061
+ (0, import_node_fs5.mkdirSync)((0, import_node_path5.dirname)(path), { recursive: true });
3062
+ (0, import_node_fs5.writeFileSync)(path, printSchema(document), "utf8");
3063
+ ctx.log(
3064
+ `Introspected ${document.models.length} model(s) into ${rel(ctx.cwd, path)}`
3065
+ );
3066
+ return 0;
3067
+ } finally {
3068
+ await driver.disconnect();
3069
+ }
3070
+ }
3071
+ async function migrateDev(ctx, options) {
3072
+ return withMigrator(ctx, options, async (migrator) => {
3073
+ const result = await migrator.dev(options.name ?? "migration");
3074
+ if (result.empty) {
3075
+ ctx.log("No schema changes \u2014 database already in sync.");
3076
+ return 0;
3077
+ }
3078
+ ctx.log(`Created and applied migration ${result.id} (${result.statements.length} step(s)).`);
3079
+ return 0;
3080
+ });
3081
+ }
3082
+ async function migrateDeploy(ctx, options) {
3083
+ return withMigrator(ctx, options, async (migrator) => {
3084
+ const { applied } = await migrator.deploy();
3085
+ if (applied.length === 0) {
3086
+ ctx.log("No pending migrations.");
3087
+ return 0;
3088
+ }
3089
+ for (const m of applied) ctx.log(`Applied ${m.id} (${m.steps} step(s)).`);
3090
+ return 0;
3091
+ });
3092
+ }
3093
+ async function migrateStatus(ctx, options) {
3094
+ return withMigrator(ctx, options, async (migrator) => {
3095
+ const { applied, pending } = await migrator.status();
3096
+ ctx.log(`Applied (${applied.length}):`);
3097
+ for (const id of applied) ctx.log(` \u2713 ${id}`);
3098
+ ctx.log(`Pending (${pending.length}):`);
3099
+ for (const id of pending) ctx.log(` \u2022 ${id}`);
3100
+ return 0;
3101
+ });
3102
+ }
3103
+ async function dbPush(ctx, options) {
3104
+ return withMigrator(ctx, options, async (migrator) => {
3105
+ const { statements } = await migrator.push();
3106
+ if (statements.length === 0) {
3107
+ ctx.log("No schema changes \u2014 database already in sync.");
3108
+ return 0;
3109
+ }
3110
+ ctx.log(`Applied ${statements.length} statement(s) to the database.`);
3111
+ return 0;
3112
+ });
3113
+ }
3114
+ async function withMigrator(ctx, options, fn) {
3115
+ const path = requireSchema(ctx, options.schemaPath);
3116
+ if (!path) return 1;
3117
+ const { document } = loadSchema(path);
3118
+ const url = options.url ?? resolveDatasourceUrl(document, (0, import_node_path5.dirname)(path)) ?? process.env.DATABASE_URL;
3119
+ if (!url) {
3120
+ ctx.error(
3121
+ "No database URL. Set DATABASE_URL, pass --url, or add a datasource block."
3122
+ );
3123
+ return 1;
3124
+ }
3125
+ const migrationsDir = (0, import_node_path5.resolve)((0, import_node_path5.dirname)(path), "migrations");
3126
+ const config = parseConnectionUrl(url);
3127
+ const dialect = new FirebirdDialect({ version: config.version });
3128
+ const driver = createDriver(config);
3129
+ try {
3130
+ await driver.connect();
3131
+ const migrator = new Migrator(driver, document, migrationsDir, dialect);
3132
+ return await fn(migrator);
3133
+ } finally {
3134
+ await driver.disconnect();
3135
+ }
3136
+ }
3137
+ function requireSchema(ctx, schemaPath) {
3138
+ const path = findSchemaPath(ctx.cwd, schemaPath);
3139
+ if (!path) {
3140
+ ctx.error(
3141
+ `Schema not found. Expected ${DEFAULT_SCHEMA} (or pass --schema). Run \`ember init\` to create one.`
3142
+ );
3143
+ return null;
3144
+ }
3145
+ return path;
3146
+ }
3147
+ function rel(cwd, target) {
3148
+ return target.startsWith(cwd) ? target.slice(cwd.length + 1) : target;
3149
+ }
3150
+
3151
+ // src/cli/bin.ts
3152
+ var import_meta = {};
3153
+ var HELP = `EmberORM \u2014 Prisma-like ORM for Firebird
3154
+
3155
+ Usage: ember <command> [options]
3156
+
3157
+ Commands:
3158
+ init Scaffold ember/schema.ember
3159
+ db pull Introspect the database into your schema
3160
+ db push Apply schema changes directly (no migration file)
3161
+ generate Generate the typed client from the schema
3162
+ migrate dev Diff, create a migration file, and apply it
3163
+ migrate deploy Apply all pending migration files
3164
+ migrate status Show applied vs pending migrations
3165
+ format Re-print the schema with canonical formatting
3166
+ validate Parse and validate the schema
3167
+
3168
+ Options:
3169
+ --schema <path> Path to the schema file
3170
+ --url <url> Firebird connection URL (overrides datasource/env)
3171
+ --name <name> Migration name (migrate dev)
3172
+ -h, --help Show this help
3173
+ -v, --version Show version
3174
+ `;
3175
+ function parseArgs(argv) {
3176
+ const command = [];
3177
+ const flags = {};
3178
+ for (let i = 0; i < argv.length; i++) {
3179
+ const arg = argv[i];
3180
+ if (arg.startsWith("--")) {
3181
+ const key = arg.slice(2);
3182
+ const next = argv[i + 1];
3183
+ if (next && !next.startsWith("-")) {
3184
+ flags[key] = next;
3185
+ i++;
3186
+ } else {
3187
+ flags[key] = true;
3188
+ }
3189
+ } else if (arg === "-h") {
3190
+ flags.help = true;
3191
+ } else if (arg === "-v") {
3192
+ flags.version = true;
3193
+ } else {
3194
+ command.push(arg);
3195
+ }
3196
+ }
3197
+ return { command, flags };
3198
+ }
3199
+ async function main() {
3200
+ const { command, flags } = parseArgs(process.argv.slice(2));
3201
+ const ctx = {
3202
+ cwd: process.cwd(),
3203
+ log: (m) => process.stdout.write(m + "\n"),
3204
+ error: (m) => process.stderr.write(`error: ${m}
3205
+ `)
3206
+ };
3207
+ if (flags.version) {
3208
+ ctx.log(readVersion());
3209
+ return 0;
3210
+ }
3211
+ if (flags.help || command.length === 0) {
3212
+ ctx.log(HELP);
3213
+ return command.length === 0 ? 1 : 0;
3214
+ }
3215
+ const schemaPath = typeof flags.schema === "string" ? flags.schema : void 0;
3216
+ const url = typeof flags.url === "string" ? flags.url : void 0;
3217
+ const name = typeof flags.name === "string" ? flags.name : void 0;
3218
+ const [first, second] = command;
3219
+ switch (first) {
3220
+ case "init":
3221
+ return init(ctx);
3222
+ case "validate":
3223
+ return validate(ctx, schemaPath);
3224
+ case "format":
3225
+ return format(ctx, schemaPath);
3226
+ case "generate":
3227
+ return generate(ctx, schemaPath);
3228
+ case "db":
3229
+ if (second === "pull") return dbPull(ctx, { schemaPath, url });
3230
+ if (second === "push") return dbPush(ctx, { schemaPath, url });
3231
+ ctx.error(`Unknown db subcommand '${second ?? ""}'.`);
3232
+ return 1;
3233
+ case "migrate":
3234
+ if (second === "dev") return migrateDev(ctx, { schemaPath, url, name });
3235
+ if (second === "deploy") return migrateDeploy(ctx, { schemaPath, url });
3236
+ if (second === "status") return migrateStatus(ctx, { schemaPath, url });
3237
+ ctx.error(`Unknown migrate subcommand '${second ?? ""}'.`);
3238
+ return 1;
3239
+ default:
3240
+ ctx.error(`Unknown command '${first}'. Run 'ember --help'.`);
3241
+ return 1;
3242
+ }
3243
+ }
3244
+ function readVersion() {
3245
+ for (const candidate of ["../../package.json", "../package.json"]) {
3246
+ try {
3247
+ const url = new URL(candidate, import_meta.url);
3248
+ const pkg = JSON.parse((0, import_node_fs7.readFileSync)((0, import_node_url.fileURLToPath)(url), "utf8"));
3249
+ if (pkg?.version) return `ember ${pkg.version}`;
3250
+ } catch {
3251
+ }
3252
+ }
3253
+ return "ember (unknown version)";
3254
+ }
3255
+ main().then((code) => process.exit(code)).catch((err) => {
3256
+ if (err instanceof EmberError) {
3257
+ process.stderr.write(`error: ${err.message}
3258
+ `);
3259
+ } else {
3260
+ process.stderr.write(`unexpected error: ${err.stack ?? err}
3261
+ `);
3262
+ }
3263
+ process.exit(1);
3264
+ });
3265
+ //# sourceMappingURL=bin.cjs.map