@vibeorm/dsl 0.3.0 → 2.0.0-alpha.1

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.
Files changed (46) hide show
  1. package/README.md +62 -471
  2. package/dist/column.d.ts +150 -0
  3. package/dist/column.d.ts.map +1 -0
  4. package/dist/enum.d.ts +27 -0
  5. package/dist/enum.d.ts.map +1 -0
  6. package/dist/generator/index.d.ts +5 -0
  7. package/dist/generator/index.d.ts.map +1 -0
  8. package/dist/generator/prisma.d.ts +19 -0
  9. package/dist/generator/prisma.d.ts.map +1 -0
  10. package/dist/index.d.ts +116 -0
  11. package/dist/index.d.ts.map +1 -0
  12. package/dist/index.js +1546 -0
  13. package/dist/index.js.map +22 -0
  14. package/dist/model.d.ts +44 -0
  15. package/dist/model.d.ts.map +1 -0
  16. package/dist/naming.d.ts +23 -0
  17. package/dist/naming.d.ts.map +1 -0
  18. package/dist/relation-plan.d.ts +101 -0
  19. package/dist/relation-plan.d.ts.map +1 -0
  20. package/dist/relation.d.ts +23 -0
  21. package/dist/relation.d.ts.map +1 -0
  22. package/dist/relations.d.ts +29 -0
  23. package/dist/relations.d.ts.map +1 -0
  24. package/dist/schema.d.ts +38 -0
  25. package/dist/schema.d.ts.map +1 -0
  26. package/dist/templates.d.ts +79 -0
  27. package/dist/templates.d.ts.map +1 -0
  28. package/dist/to-ir.d.ts +41 -0
  29. package/dist/to-ir.d.ts.map +1 -0
  30. package/dist/types.d.ts +286 -0
  31. package/dist/types.d.ts.map +1 -0
  32. package/dist/validate.d.ts +22 -0
  33. package/dist/validate.d.ts.map +1 -0
  34. package/package.json +29 -26
  35. package/src/column.ts +0 -389
  36. package/src/enum.ts +0 -71
  37. package/src/generator/index.ts +0 -5
  38. package/src/generator/prisma.ts +0 -728
  39. package/src/index.ts +0 -247
  40. package/src/model.ts +0 -330
  41. package/src/relation.ts +0 -75
  42. package/src/relations.ts +0 -293
  43. package/src/schema.ts +0 -184
  44. package/src/templates.ts +0 -134
  45. package/src/types.ts +0 -381
  46. package/src/validate.ts +0 -141
package/dist/index.js ADDED
@@ -0,0 +1,1546 @@
1
+ // @bun
2
+ // packages/dsl/src/column.ts
3
+ function cloneColumnDef(params) {
4
+ const { column } = params;
5
+ return {
6
+ __type: "column",
7
+ __columnType: column.__columnType,
8
+ __name: column.__name,
9
+ __config: { ...column.__config },
10
+ __enumValues: column.__enumValues,
11
+ __enumName: column.__enumName
12
+ };
13
+ }
14
+
15
+ class ChainableColumn {
16
+ __type = "column";
17
+ __columnType;
18
+ __config;
19
+ __name;
20
+ __enumValues;
21
+ __enumName;
22
+ constructor(columnType, config = {}) {
23
+ this.__columnType = columnType;
24
+ this.__config = { ...config };
25
+ }
26
+ optional() {
27
+ this.__config.optional = true;
28
+ return this;
29
+ }
30
+ unique() {
31
+ this.__config.unique = true;
32
+ return this;
33
+ }
34
+ index() {
35
+ this.__config.index = true;
36
+ return this;
37
+ }
38
+ array() {
39
+ this.__config.array = true;
40
+ return this;
41
+ }
42
+ default(value) {
43
+ this.__config.default = value;
44
+ return this;
45
+ }
46
+ map(dbColumnName) {
47
+ this.__config.map = dbColumnName;
48
+ return this;
49
+ }
50
+ dbgenerated(expression) {
51
+ this.__config.dbgenerated = expression;
52
+ return this;
53
+ }
54
+ comment(text) {
55
+ this.__config.comment = text;
56
+ return this;
57
+ }
58
+ }
59
+
60
+ class ChainableStringColumn extends ChainableColumn {
61
+ constructor(config = {}) {
62
+ super("string", config);
63
+ }
64
+ maxLength(n) {
65
+ this.__config.maxLength = n;
66
+ return this;
67
+ }
68
+ text() {
69
+ this.__config.text = true;
70
+ return this;
71
+ }
72
+ }
73
+
74
+ class ChainableDecimalColumn extends ChainableColumn {
75
+ constructor(config = {}) {
76
+ super("decimal", config);
77
+ }
78
+ precision(p, s) {
79
+ this.__config.precision = p;
80
+ if (s !== undefined) {
81
+ this.__config.scale = s;
82
+ }
83
+ return this;
84
+ }
85
+ }
86
+
87
+ class ChainableDatetimeColumn extends ChainableColumn {
88
+ constructor(config = {}) {
89
+ super("datetime", config);
90
+ }
91
+ tz() {
92
+ this.__config.tz = true;
93
+ return this;
94
+ }
95
+ updatedAt() {
96
+ this.__config.updatedAt = true;
97
+ return this;
98
+ }
99
+ defaultNow() {
100
+ this.__config.default = "now";
101
+ return this;
102
+ }
103
+ }
104
+
105
+ class ChainableIdColumn extends ChainableColumn {
106
+ constructor(config = {}) {
107
+ super("id", { ...config, type: config.type ?? "ulid" });
108
+ }
109
+ type(idType) {
110
+ this.__config.type = idType;
111
+ return this;
112
+ }
113
+ }
114
+
115
+ class ChainableEnumColumn extends ChainableColumn {
116
+ constructor(params) {
117
+ super("enum", params.config ?? {});
118
+ this.__enumValues = params.options;
119
+ if (params.enumName !== undefined) {
120
+ this.__enumName = params.enumName;
121
+ }
122
+ }
123
+ default(value) {
124
+ this.__config.default = value;
125
+ return this;
126
+ }
127
+ }
128
+ function string(config = {}) {
129
+ return new ChainableStringColumn(config);
130
+ }
131
+ function int(config = {}) {
132
+ return new ChainableColumn("int", config);
133
+ }
134
+ function float(config = {}) {
135
+ return new ChainableColumn("float", config);
136
+ }
137
+ function decimal(config = {}) {
138
+ return new ChainableDecimalColumn(config);
139
+ }
140
+ function boolean(config = {}) {
141
+ return new ChainableColumn("boolean", config);
142
+ }
143
+ function datetime(config = {}) {
144
+ return new ChainableDatetimeColumn(config);
145
+ }
146
+ function json(config = {}) {
147
+ return new ChainableColumn("json", config);
148
+ }
149
+ function id(config = {}) {
150
+ return new ChainableIdColumn(config);
151
+ }
152
+ function bytes(config = {}) {
153
+ return new ChainableColumn("bytes", config);
154
+ }
155
+ function bigint(config = {}) {
156
+ return new ChainableColumn("bigint", config);
157
+ }
158
+ function unsupported(dbType) {
159
+ const config = { dbType };
160
+ return new ChainableColumn("unsupported", config);
161
+ }
162
+ function extensionType(config) {
163
+ const { type, args, ...rest } = config;
164
+ return new ChainableColumn("unsupported", {
165
+ ...rest,
166
+ dbType: "",
167
+ extensionType: type,
168
+ extensionArgs: [...args ?? []]
169
+ });
170
+ }
171
+ function enumCol(config) {
172
+ const { options, ...restConfig } = config;
173
+ return new ChainableEnumColumn({ options, config: restConfig });
174
+ }
175
+
176
+ // packages/dsl/src/enum.ts
177
+ function createEnum(config) {
178
+ const { name, options } = config;
179
+ return {
180
+ __type: "enum",
181
+ __name: name,
182
+ __options: options,
183
+ field(fieldConfig = {}) {
184
+ return new ChainableEnumColumn({ options, config: fieldConfig, enumName: name });
185
+ }
186
+ };
187
+ }
188
+
189
+ // packages/dsl/src/generator/prisma.ts
190
+ import { internalError } from "@vibeorm/schema";
191
+
192
+ // packages/dsl/src/naming.ts
193
+ function capitalize(params) {
194
+ const { value } = params;
195
+ return value.charAt(0).toUpperCase() + value.slice(1);
196
+ }
197
+ function uncapitalize(params) {
198
+ const { value } = params;
199
+ return value.charAt(0).toLowerCase() + value.slice(1);
200
+ }
201
+ function toFkColumnName(params) {
202
+ return `${uncapitalize({ value: params.modelName })}Id`;
203
+ }
204
+ function inlineEnumName(params) {
205
+ const { modelName, columnName } = params;
206
+ return `${modelName}${capitalize({ value: columnName })}`;
207
+ }
208
+
209
+ // packages/dsl/src/relation.ts
210
+ function children(config) {
211
+ return {
212
+ __type: "children",
213
+ __name: config.name,
214
+ __schema: config.schema
215
+ };
216
+ }
217
+ function createChildrenBuilder() {
218
+ return { children };
219
+ }
220
+
221
+ // packages/dsl/src/model.ts
222
+ function isColumnDef(value) {
223
+ return typeof value === "object" && value !== null && "__type" in value && value.__type === "column";
224
+ }
225
+ function isChildrenDef(value) {
226
+ return typeof value === "object" && value !== null && "__type" in value && value.__type === "children";
227
+ }
228
+ function createIndexBuilder() {
229
+ const createIndex = (columns, type) => ({
230
+ __type: "index",
231
+ __columns: [...columns],
232
+ __indexType: type
233
+ });
234
+ return {
235
+ on(...args) {
236
+ const [first, second] = args;
237
+ if (args.length === 2 && Array.isArray(first) && typeof second === "object" && !Array.isArray(second)) {
238
+ return createIndex(first, second.type);
239
+ }
240
+ return createIndex(args);
241
+ },
242
+ index(...columns) {
243
+ return createIndex(columns);
244
+ },
245
+ unique(...columns) {
246
+ return { __type: "unique", __columns: [...columns] };
247
+ },
248
+ gin(...columns) {
249
+ return createIndex(columns, "Gin");
250
+ },
251
+ gist(...columns) {
252
+ return createIndex(columns, "Gist");
253
+ },
254
+ hash(...columns) {
255
+ return createIndex(columns, "Hash");
256
+ },
257
+ brin(...columns) {
258
+ return createIndex(columns, "Brin");
259
+ }
260
+ };
261
+ }
262
+ function processSchema(params) {
263
+ const { modelName, schema } = params;
264
+ const resolvedSchema = typeof schema === "function" ? schema(createChildrenBuilder()) : schema;
265
+ const columns = new Map;
266
+ const children2 = new Map;
267
+ for (const [key, value] of Object.entries(resolvedSchema)) {
268
+ if (isColumnDef(value)) {
269
+ const cloned = cloneColumnDef({ column: value });
270
+ cloned.__name = key;
271
+ columns.set(key, cloned);
272
+ } else if (isChildrenDef(value)) {
273
+ const childModel = create({ name: value.__name, schema: value.__schema });
274
+ const parentFkName = toFkColumnName({ modelName });
275
+ childModel.__columns.set(parentFkName, {
276
+ __type: "column",
277
+ __columnType: "string",
278
+ __name: parentFkName,
279
+ __config: { index: true }
280
+ });
281
+ children2.set(key, childModel);
282
+ }
283
+ }
284
+ return { columns, children: children2 };
285
+ }
286
+ function create(config) {
287
+ const { columns, children: children2 } = processSchema({
288
+ modelName: config.name,
289
+ schema: config.schema
290
+ });
291
+ return {
292
+ __type: "model",
293
+ __name: config.name,
294
+ __schema: config.schema,
295
+ __indexes: config.indexes,
296
+ __columns: columns,
297
+ __children: children2,
298
+ __map: config.map,
299
+ __primaryKey: config.primaryKey,
300
+ __extensions: config.extensions
301
+ };
302
+ }
303
+ function getColumnNames(params) {
304
+ return Array.from(params.model.__columns.keys());
305
+ }
306
+ function evaluateIndexes(params) {
307
+ const { model } = params;
308
+ if (model.__indexes === undefined) {
309
+ return [];
310
+ }
311
+ return model.__indexes(createIndexBuilder());
312
+ }
313
+
314
+ // packages/dsl/src/relation-plan.ts
315
+ function detectOneToOnePairs(params) {
316
+ const { relations, models } = params;
317
+ const mergeMap = new Map;
318
+ const allOnes = [];
319
+ for (const [modelName, modelRelations] of Object.entries(relations)) {
320
+ for (const [relName, rel] of Object.entries(modelRelations)) {
321
+ if (rel.__relationType === "one") {
322
+ allOnes.push({ modelName, relName, rel });
323
+ }
324
+ }
325
+ }
326
+ const paired = new Set;
327
+ for (let i = 0;i < allOnes.length; i++) {
328
+ const a = allOnes[i];
329
+ if (a === undefined)
330
+ continue;
331
+ const keyA = `${a.modelName}:${a.relName}`;
332
+ if (paired.has(keyA))
333
+ continue;
334
+ for (let j = i + 1;j < allOnes.length; j++) {
335
+ const b = allOnes[j];
336
+ if (b === undefined)
337
+ continue;
338
+ const keyB = `${b.modelName}:${b.relName}`;
339
+ if (paired.has(keyB))
340
+ continue;
341
+ const sameColumnPair = a.rel.__fromModel === b.rel.__toModel && a.rel.__fromColumn === b.rel.__toColumn && a.rel.__toModel === b.rel.__fromModel && a.rel.__toColumn === b.rel.__fromColumn;
342
+ if (!sameColumnPair)
343
+ continue;
344
+ const { fkOwner, backRelation } = resolveFkOwner({ a, b, models });
345
+ const onDelete = fkOwner.rel.__onDelete ?? backRelation.rel.__onDelete;
346
+ const onUpdate = fkOwner.rel.__onUpdate ?? backRelation.rel.__onUpdate;
347
+ const fkKey = `${fkOwner.modelName}:${fkOwner.relName}`;
348
+ const backKey = `${backRelation.modelName}:${backRelation.relName}`;
349
+ mergeMap.set(fkKey, {
350
+ role: "fkOwner",
351
+ pairedRelName: backRelation.relName,
352
+ pairedModelName: backRelation.modelName,
353
+ onDelete,
354
+ onUpdate
355
+ });
356
+ mergeMap.set(backKey, {
357
+ role: "backRelation",
358
+ pairedRelName: fkOwner.relName,
359
+ pairedModelName: fkOwner.modelName,
360
+ onDelete,
361
+ onUpdate
362
+ });
363
+ paired.add(fkKey);
364
+ paired.add(backKey);
365
+ break;
366
+ }
367
+ }
368
+ return mergeMap;
369
+ }
370
+ function resolveFkOwner(params) {
371
+ const { a, b, models } = params;
372
+ const aFromIsId = models.get(a.modelName)?.__columns.get(a.rel.__fromColumn)?.__columnType === "id";
373
+ const bFromIsId = models.get(b.modelName)?.__columns.get(b.rel.__fromColumn)?.__columnType === "id";
374
+ if (aFromIsId && !bFromIsId)
375
+ return { fkOwner: b, backRelation: a };
376
+ if (!aFromIsId && bFromIsId)
377
+ return { fkOwner: a, backRelation: b };
378
+ if (a.rel.__onDelete !== undefined || a.rel.__onUpdate !== undefined) {
379
+ return { fkOwner: a, backRelation: b };
380
+ }
381
+ if (b.rel.__onDelete !== undefined || b.rel.__onUpdate !== undefined) {
382
+ return { fkOwner: b, backRelation: a };
383
+ }
384
+ const aLooksFk = looksLikeForeignKey({ column: a.rel.__fromColumn });
385
+ const bLooksFk = looksLikeForeignKey({ column: b.rel.__fromColumn });
386
+ return aLooksFk && !bLooksFk ? { fkOwner: a, backRelation: b } : { fkOwner: b, backRelation: a };
387
+ }
388
+ function looksLikeForeignKey(params) {
389
+ const { column } = params;
390
+ return column.toLowerCase().includes("id") && column !== "id";
391
+ }
392
+ function planExplicitRelations(params) {
393
+ const { modelName, model, relations, mergeMap } = params;
394
+ const planned = [];
395
+ for (const [relName, rel] of Object.entries(relations[modelName] ?? {})) {
396
+ const name = relName;
397
+ const base = {
398
+ model: modelName,
399
+ name,
400
+ targetModel: rel.__targetModel,
401
+ relationName: rel.__alias
402
+ };
403
+ if (rel.__relationType === "many") {
404
+ planned.push({ ...base, kind: "list", isList: true, isOptional: false });
405
+ continue;
406
+ }
407
+ const mergeInfo = mergeMap.get(`${modelName}:${relName}`);
408
+ if (mergeInfo?.role === "backRelation") {
409
+ planned.push({ ...base, kind: "backOne", isList: false, isOptional: true });
410
+ continue;
411
+ }
412
+ const ownsFk = mergeInfo?.role === "fkOwner" || rel.__fromModel === modelName;
413
+ if (!ownsFk) {
414
+ planned.push({ ...base, kind: "backOne", isList: false, isOptional: rel.__optional });
415
+ continue;
416
+ }
417
+ const fkColumn = model.__columns.get(rel.__fromColumn);
418
+ planned.push({
419
+ ...base,
420
+ kind: "fkOwner",
421
+ isList: false,
422
+ isOptional: fkColumn?.__config.optional === true || rel.__optional,
423
+ fields: [rel.__fromColumn],
424
+ references: [rel.__toColumn],
425
+ onDelete: mergeInfo?.onDelete ?? rel.__onDelete,
426
+ onUpdate: mergeInfo?.onUpdate ?? rel.__onUpdate
427
+ });
428
+ }
429
+ return planned;
430
+ }
431
+ function planMissingReverseRelations(params) {
432
+ const { modelName, relations, mergeMap, existing } = params;
433
+ const planned = [];
434
+ const existingLists = new Set;
435
+ const existingNames = new Set;
436
+ for (const relation of existing) {
437
+ existingNames.add(relation.name);
438
+ if (relation.isList) {
439
+ existingLists.add(`${relation.targetModel}:${relation.relationName ?? ""}`);
440
+ }
441
+ }
442
+ for (const [sourceModelName, sourceRelations] of Object.entries(relations)) {
443
+ for (const [relName, rel] of Object.entries(sourceRelations)) {
444
+ if (rel.__targetModel !== modelName || rel.__relationType !== "one")
445
+ continue;
446
+ if (mergeMap.has(`${sourceModelName}:${relName}`))
447
+ continue;
448
+ const reverseKey = `${sourceModelName}:${rel.__alias ?? ""}`;
449
+ if (existingLists.has(reverseKey))
450
+ continue;
451
+ if (sourceModelName === modelName) {
452
+ const hasExplicitReverse = Object.values(sourceRelations).some((other) => other.__relationType === "many" && other.__targetModel === modelName && other.__alias === rel.__alias);
453
+ if (hasExplicitReverse)
454
+ continue;
455
+ }
456
+ const baseName = rel.__alias ?? sourceModelName;
457
+ let name = baseName;
458
+ let counter = 1;
459
+ while (existingNames.has(name)) {
460
+ counter++;
461
+ name = `${baseName}${counter}`;
462
+ }
463
+ planned.push({
464
+ model: modelName,
465
+ name,
466
+ targetModel: sourceModelName,
467
+ kind: "list",
468
+ isList: true,
469
+ isOptional: false,
470
+ relationName: rel.__alias
471
+ });
472
+ existingLists.add(reverseKey);
473
+ existingNames.add(name);
474
+ }
475
+ }
476
+ return planned;
477
+ }
478
+ function planChildRelations(params) {
479
+ const { modelName, model, models } = params;
480
+ const planned = [];
481
+ for (const [childKey, childModel] of model.__children) {
482
+ planned.push({
483
+ model: modelName,
484
+ name: childKey,
485
+ targetModel: childModel.__name,
486
+ kind: "list",
487
+ isList: true,
488
+ isOptional: false
489
+ });
490
+ }
491
+ for (const [parentModelName, parentModel] of models) {
492
+ for (const childModel of parentModel.__children.values()) {
493
+ if (childModel.__name !== modelName)
494
+ continue;
495
+ const parentPk = parentModel.__primaryKey?.[0] ?? Array.from(parentModel.__columns.entries()).find(([, column]) => column.__columnType === "id")?.[0] ?? "id";
496
+ planned.push({
497
+ model: modelName,
498
+ name: parentModelName,
499
+ targetModel: parentModelName,
500
+ kind: "fkOwner",
501
+ isList: false,
502
+ isOptional: false,
503
+ fields: [toFkColumnName({ modelName: parentModelName })],
504
+ references: [parentPk]
505
+ });
506
+ }
507
+ }
508
+ return planned;
509
+ }
510
+ function planIndexes(params) {
511
+ const { model } = params;
512
+ const byKey = new Map;
513
+ for (const [columnName, column] of model.__columns) {
514
+ if (column.__columnType === "id" || column.__config.unique === true)
515
+ continue;
516
+ if (column.__config.index === true) {
517
+ byKey.set(columnName, { isUnique: false, columns: [columnName] });
518
+ }
519
+ }
520
+ for (const index of evaluateIndexes({ model })) {
521
+ const key = index.__columns.join(",");
522
+ const existing = byKey.get(key);
523
+ const isUnique = index.__type === "unique";
524
+ if (existing === undefined || isUnique) {
525
+ byKey.set(key, {
526
+ isUnique,
527
+ columns: [...index.__columns],
528
+ indexType: index.__indexType
529
+ });
530
+ } else if (index.__indexType !== undefined && existing.indexType === undefined) {
531
+ byKey.set(key, { ...existing, indexType: index.__indexType });
532
+ }
533
+ }
534
+ return Array.from(byKey.values());
535
+ }
536
+ function planSchema(params) {
537
+ const { schema } = params;
538
+ const mergeMap = detectOneToOnePairs({
539
+ relations: schema.__relations,
540
+ models: schema.__models
541
+ });
542
+ const relations = new Map;
543
+ const indexes = new Map;
544
+ for (const [modelName, model] of schema.__models) {
545
+ const explicit = planExplicitRelations({
546
+ modelName,
547
+ model,
548
+ relations: schema.__relations,
549
+ mergeMap
550
+ });
551
+ const reverses = planMissingReverseRelations({
552
+ modelName,
553
+ relations: schema.__relations,
554
+ mergeMap,
555
+ existing: explicit
556
+ });
557
+ const children2 = planChildRelations({ modelName, model, models: schema.__models });
558
+ relations.set(modelName, [...explicit, ...reverses, ...children2]);
559
+ indexes.set(modelName, planIndexes({ model }));
560
+ }
561
+ return { relations, indexes };
562
+ }
563
+ function resolveRelationType(params) {
564
+ const { relation, plan } = params;
565
+ const counterparts = findCounterparts({ relation, plan });
566
+ if (relation.isList) {
567
+ if (counterparts.some((candidate) => !candidate.isList))
568
+ return "oneToMany";
569
+ return counterparts.some((candidate) => candidate.isList) ? "manyToMany" : "oneToMany";
570
+ }
571
+ if (relation.kind === "fkOwner") {
572
+ const pairedWithSingular = counterparts.length > 0 && counterparts.every((candidate) => !candidate.isList);
573
+ return pairedWithSingular ? "oneToOne" : "manyToOne";
574
+ }
575
+ return "oneToOne";
576
+ }
577
+ function findCounterparts(params) {
578
+ const { relation, plan } = params;
579
+ const nameKey = relation.relationName ?? "";
580
+ return (plan.relations.get(relation.targetModel) ?? []).filter((candidate) => candidate !== relation && candidate.targetModel === relation.model && (candidate.relationName ?? "") === nameKey);
581
+ }
582
+
583
+ // packages/dsl/src/generator/prisma.ts
584
+ function prismaType(params) {
585
+ const { column } = params;
586
+ const list = column.__config.array === true ? "[]" : "";
587
+ switch (column.__columnType) {
588
+ case "string":
589
+ return `String${list}`;
590
+ case "int":
591
+ return `Int${list}`;
592
+ case "bigint":
593
+ return `BigInt${list}`;
594
+ case "float":
595
+ return `Float${list}`;
596
+ case "decimal":
597
+ return `Decimal${list}`;
598
+ case "boolean":
599
+ return `Boolean${list}`;
600
+ case "datetime":
601
+ return `DateTime${list}`;
602
+ case "json":
603
+ return `Json${list}`;
604
+ case "bytes":
605
+ return `Bytes${list}`;
606
+ case "id":
607
+ return column.__config.type === "autoincrement" ? "Int" : "String";
608
+ case "enum":
609
+ return column.__enumName === undefined ? `String${list}` : `${column.__enumName}${list}`;
610
+ case "unsupported": {
611
+ const extensionTypeName = column.__config["extensionType"];
612
+ if (typeof extensionTypeName === "string") {
613
+ const rawArgs = column.__config["extensionArgs"];
614
+ const args = Array.isArray(rawArgs) ? rawArgs.map(String).join(", ") : "";
615
+ return `${extensionTypeName}${args.length > 0 ? `(${args})` : ""}${list}`;
616
+ }
617
+ return `Unsupported("${String(column.__config.dbType)}")${list}`;
618
+ }
619
+ }
620
+ }
621
+ function idDefaultAttribute(params) {
622
+ const idType = params.column.__config.type;
623
+ switch (idType) {
624
+ case "ulid":
625
+ case "cuid":
626
+ case "uuid":
627
+ case "nanoid":
628
+ case "autoincrement":
629
+ return `@default(${idType}())`;
630
+ default:
631
+ return;
632
+ }
633
+ }
634
+ function defaultAttribute(params) {
635
+ const { column } = params;
636
+ const { dbgenerated, default: value } = column.__config;
637
+ if (typeof dbgenerated === "string") {
638
+ return `@default(dbgenerated("${dbgenerated}"))`;
639
+ }
640
+ if (value === undefined)
641
+ return;
642
+ if (column.__columnType === "datetime" && value === "now") {
643
+ return "@default(now())";
644
+ }
645
+ if (column.__columnType === "enum" && typeof value === "string") {
646
+ return `@default(${value})`;
647
+ }
648
+ if (typeof value === "string")
649
+ return `@default("${value}")`;
650
+ if (typeof value === "boolean" || typeof value === "number")
651
+ return `@default(${value})`;
652
+ if (typeof value === "bigint")
653
+ return `@default(${value.toString()})`;
654
+ return;
655
+ }
656
+ function nativeTypeAttribute(params) {
657
+ const { column } = params;
658
+ if (column.__columnType === "string") {
659
+ const { maxLength, text } = column.__config;
660
+ if (typeof maxLength === "number")
661
+ return `@db.VarChar(${maxLength})`;
662
+ if (text === true)
663
+ return "@db.Text";
664
+ return;
665
+ }
666
+ if (column.__columnType === "decimal") {
667
+ const { precision, scale } = column.__config;
668
+ if (typeof precision === "number")
669
+ return `@db.Decimal(${precision}, ${scale ?? 0})`;
670
+ return;
671
+ }
672
+ if (column.__columnType === "datetime" && column.__config.tz === true) {
673
+ return "@db.Timestamptz";
674
+ }
675
+ return;
676
+ }
677
+ function formatColumn(params) {
678
+ const { name, column } = params;
679
+ const parts = [` ${name} ${prismaType({ column })}${column.__config.optional === true ? "?" : ""}`];
680
+ if (column.__columnType === "id") {
681
+ parts.push("@id");
682
+ const idDefault = idDefaultAttribute({ column });
683
+ if (idDefault !== undefined)
684
+ parts.push(idDefault);
685
+ } else {
686
+ const columnDefault = defaultAttribute({ column });
687
+ if (columnDefault !== undefined)
688
+ parts.push(columnDefault);
689
+ }
690
+ if (column.__columnType === "datetime" && column.__config.updatedAt === true) {
691
+ parts.push("@updatedAt");
692
+ }
693
+ if (column.__config.unique === true) {
694
+ parts.push("@unique");
695
+ }
696
+ if (typeof column.__config.map === "string") {
697
+ parts.push(`@map("${column.__config.map}")`);
698
+ }
699
+ const nativeType = nativeTypeAttribute({ column });
700
+ if (nativeType !== undefined)
701
+ parts.push(nativeType);
702
+ const columnLine = parts.join(" ");
703
+ const comment = column.__config.comment;
704
+ if (typeof comment === "string") {
705
+ const commentLines = comment.split(`
706
+ `).map((line) => ` /// ${line}`);
707
+ return [...commentLines, columnLine].join(`
708
+ `);
709
+ }
710
+ return columnLine;
711
+ }
712
+ function formatRelation(params) {
713
+ const { relation } = params;
714
+ const { name, targetModel, relationName } = relation;
715
+ if (relation.kind === "list") {
716
+ const attribute = relationName === undefined ? "" : ` @relation("${relationName}")`;
717
+ return ` ${name} ${targetModel}[]${attribute}`;
718
+ }
719
+ const optional = relation.isOptional ? "?" : "";
720
+ if (relation.kind === "backOne") {
721
+ const attribute = relationName === undefined ? "" : ` @relation("${relationName}")`;
722
+ return ` ${name} ${targetModel}${optional}${attribute}`;
723
+ }
724
+ const args = [];
725
+ if (relationName !== undefined)
726
+ args.push(`"${relationName}"`);
727
+ if (relation.fields === undefined || relation.references === undefined) {
728
+ throw internalError({
729
+ message: `planned FK relation "${relation.model}.${name}" has no fields/references`,
730
+ meta: { model: relation.model, relation: name }
731
+ });
732
+ }
733
+ args.push(`fields: [${relation.fields.join(", ")}]`);
734
+ args.push(`references: [${relation.references.join(", ")}]`);
735
+ if (relation.onDelete !== undefined)
736
+ args.push(`onDelete: ${relation.onDelete}`);
737
+ if (relation.onUpdate !== undefined)
738
+ args.push(`onUpdate: ${relation.onUpdate}`);
739
+ return ` ${name} ${targetModel}${optional} @relation(${args.join(", ")})`;
740
+ }
741
+ function formatIndex(params) {
742
+ const { index } = params;
743
+ const columns = index.columns.join(", ");
744
+ if (index.isUnique) {
745
+ return ` @@unique([${columns}])`;
746
+ }
747
+ const typeArg = index.indexType === undefined ? "" : `, type: ${index.indexType}`;
748
+ return ` @@index([${columns}]${typeArg})`;
749
+ }
750
+ function formatExtensionValue(params) {
751
+ const { value } = params;
752
+ if (typeof value === "string")
753
+ return `"${value}"`;
754
+ if (Array.isArray(value)) {
755
+ return `[${value.map((entry) => formatExtensionValue({ value: entry })).join(", ")}]`;
756
+ }
757
+ return String(value);
758
+ }
759
+ function formatModel(params) {
760
+ const { model, plan } = params;
761
+ const lines = [`model ${model.__name} {`];
762
+ lines.push(" //> Columns");
763
+ for (const [name, column] of model.__columns) {
764
+ lines.push(formatColumn({ name, column }));
765
+ }
766
+ lines.push("");
767
+ lines.push(" //> Relations");
768
+ for (const relation of plan.relations.get(model.__name) ?? []) {
769
+ lines.push(formatRelation({ relation }));
770
+ }
771
+ lines.push("");
772
+ lines.push(" //> Indexes");
773
+ lines.push("");
774
+ for (const index of plan.indexes.get(model.__name) ?? []) {
775
+ lines.push(formatIndex({ index }));
776
+ }
777
+ for (const use of model.__extensions ?? []) {
778
+ const args = [];
779
+ if (use.fields !== undefined)
780
+ args.push(`[${use.fields.join(", ")}]`);
781
+ for (const [key, value] of Object.entries(use.args ?? {})) {
782
+ args.push(`${key}: ${formatExtensionValue({ value })}`);
783
+ }
784
+ lines.push(` @@${use.attribute}(${args.join(", ")})`);
785
+ }
786
+ if (model.__primaryKey !== undefined && model.__primaryKey.length > 0) {
787
+ lines.push(` @@id([${model.__primaryKey.join(", ")}])`);
788
+ }
789
+ if (typeof model.__map === "string") {
790
+ lines.push(` @@map("${model.__map}")`);
791
+ }
792
+ lines.push("}");
793
+ return lines.join(`
794
+ `);
795
+ }
796
+ function formatEnum(params) {
797
+ const { name, values } = params;
798
+ return [`enum ${name} {`, ...values.map((value) => ` ${value}`), "}"].join(`
799
+ `);
800
+ }
801
+ function generatePrisma(schema, _config = {}) {
802
+ const plan = planSchema({ schema });
803
+ const sections = [];
804
+ if (schema.__enums.size > 0) {
805
+ sections.push("// ============================================");
806
+ sections.push("// Enums");
807
+ sections.push("// ============================================");
808
+ sections.push("");
809
+ for (const [name, values] of schema.__enums) {
810
+ sections.push(formatEnum({ name, values }));
811
+ sections.push("");
812
+ }
813
+ }
814
+ sections.push("// ============================================");
815
+ sections.push("// Models");
816
+ sections.push("// ============================================");
817
+ sections.push("");
818
+ for (const model of schema.__models.values()) {
819
+ sections.push(formatModel({ model, plan }));
820
+ sections.push("");
821
+ }
822
+ return sections.join(`
823
+ `).trim();
824
+ }
825
+
826
+ // packages/dsl/src/relations.ts
827
+ function createColumnRef(params) {
828
+ const { model, column } = params;
829
+ return {
830
+ __type: "columnRef",
831
+ __model: model,
832
+ __column: column,
833
+ through(junction) {
834
+ return {
835
+ __type: "throughRef",
836
+ __model: model,
837
+ __column: column,
838
+ __junction: junction.__model,
839
+ __junctionColumn: junction.__column
840
+ };
841
+ }
842
+ };
843
+ }
844
+ function extractRefInfo(params) {
845
+ const { ref } = params;
846
+ return { model: ref.__model, column: ref.__column };
847
+ }
848
+ function createRelationFactory(params) {
849
+ const { relationType, targetModelName } = params;
850
+ return (config) => {
851
+ const from = extractRefInfo({ ref: config.from });
852
+ const to = extractRefInfo({ ref: config.to });
853
+ let throughJunction;
854
+ let throughFromCol;
855
+ let throughToCol;
856
+ if (config.from.__type === "throughRef") {
857
+ throughJunction = config.from.__junction;
858
+ throughFromCol = config.from.__junctionColumn;
859
+ }
860
+ if (config.to.__type === "throughRef") {
861
+ throughJunction = throughJunction ?? config.to.__junction;
862
+ throughToCol = config.to.__junctionColumn;
863
+ }
864
+ return {
865
+ __type: "storedRelation",
866
+ __relationType: relationType,
867
+ __targetModel: targetModelName,
868
+ __fromModel: from.model,
869
+ __fromColumn: from.column,
870
+ __toModel: to.model,
871
+ __toColumn: to.column,
872
+ __throughJunction: throughJunction,
873
+ __throughFromCol: throughFromCol,
874
+ __throughToCol: throughToCol,
875
+ __optional: config.optional ?? relationType === "many",
876
+ __alias: config.alias,
877
+ __onDelete: config.onDelete,
878
+ __onUpdate: config.onUpdate
879
+ };
880
+ };
881
+ }
882
+ function createRelationsBuilder(params) {
883
+ const { models } = params;
884
+ const builder = {};
885
+ const oneObj = {};
886
+ const manyObj = {};
887
+ for (const modelName of Object.keys(models)) {
888
+ oneObj[modelName] = createRelationFactory({ relationType: "one", targetModelName: modelName });
889
+ manyObj[modelName] = createRelationFactory({
890
+ relationType: "many",
891
+ targetModelName: modelName
892
+ });
893
+ const model = models[modelName];
894
+ if (model === undefined)
895
+ continue;
896
+ const columnRefs = {};
897
+ for (const columnName of model.__columns.keys()) {
898
+ columnRefs[columnName] = createColumnRef({ model: modelName, column: columnName });
899
+ }
900
+ builder[modelName] = columnRefs;
901
+ }
902
+ builder.one = oneObj;
903
+ builder.many = manyObj;
904
+ return builder;
905
+ }
906
+ function processRelationsOutput(params) {
907
+ const result = {};
908
+ for (const [modelName, relations] of Object.entries(params.output)) {
909
+ if (relations) {
910
+ result[modelName] = relations;
911
+ }
912
+ }
913
+ return result;
914
+ }
915
+ function defineRelations(models, callback) {
916
+ const builder = createRelationsBuilder({ models });
917
+ return processRelationsOutput({ output: callback(builder) });
918
+ }
919
+ function defineRelationsPart(models, callback) {
920
+ const builder = createRelationsBuilder({ models });
921
+ return processRelationsOutput({ output: callback(builder) });
922
+ }
923
+ function mergeRelations(...parts) {
924
+ const result = {};
925
+ for (const part of parts) {
926
+ for (const [modelName, relations] of Object.entries(part)) {
927
+ const existing = result[modelName] ?? {};
928
+ result[modelName] = { ...existing, ...relations };
929
+ }
930
+ }
931
+ return result;
932
+ }
933
+
934
+ // packages/dsl/src/schema.ts
935
+ function isModelDef(value) {
936
+ return typeof value === "object" && value !== null && "__type" in value && value.__type === "model";
937
+ }
938
+ function isEnumDef(value) {
939
+ return typeof value === "object" && value !== null && "__type" in value && value.__type === "enum";
940
+ }
941
+ function addModelTree(params) {
942
+ const { model, models } = params;
943
+ models.set(model.__name, model);
944
+ for (const childModel of model.__children.values()) {
945
+ addModelTree({ model: childModel, models });
946
+ }
947
+ }
948
+ function extractModelsAndEnums(params) {
949
+ const { input, models, standaloneEnums } = params;
950
+ for (const value of Object.values(input)) {
951
+ if (isModelDef(value)) {
952
+ addModelTree({ model: value, models });
953
+ } else if (isEnumDef(value)) {
954
+ standaloneEnums.set(value.__name, value.__options);
955
+ } else if (typeof value === "object" && value !== null) {
956
+ extractModelsAndEnums({ input: value, models, standaloneEnums });
957
+ }
958
+ }
959
+ }
960
+ function resolveEnums(params) {
961
+ const { models, standaloneEnums } = params;
962
+ const enums = new Map(standaloneEnums);
963
+ for (const [modelName, model] of models) {
964
+ for (const [columnName, column] of model.__columns) {
965
+ if (column.__columnType !== "enum" || column.__enumValues === undefined)
966
+ continue;
967
+ if (column.__enumName !== undefined) {
968
+ if (!enums.has(column.__enumName)) {
969
+ enums.set(column.__enumName, column.__enumValues);
970
+ }
971
+ continue;
972
+ }
973
+ const name = inlineEnumName({ modelName, columnName });
974
+ enums.set(name, column.__enumValues);
975
+ column.__enumName = name;
976
+ }
977
+ }
978
+ return enums;
979
+ }
980
+ function schema(input, options) {
981
+ const models = new Map;
982
+ const standaloneEnums = new Map;
983
+ extractModelsAndEnums({ input, models, standaloneEnums });
984
+ return {
985
+ __type: "schema",
986
+ __models: models,
987
+ __enums: resolveEnums({ models, standaloneEnums }),
988
+ __relations: options?.relations ?? {}
989
+ };
990
+ }
991
+ function domain(params) {
992
+ const { name, models } = params;
993
+ return { name, models };
994
+ }
995
+
996
+ // packages/dsl/src/templates.ts
997
+ var base = {
998
+ get id() {
999
+ return id({ type: "ulid" });
1000
+ },
1001
+ get createdAt() {
1002
+ return datetime({ default: "now", tz: true, index: true });
1003
+ },
1004
+ get updatedAt() {
1005
+ return datetime({ default: "now", updatedAt: true, tz: true, index: true });
1006
+ },
1007
+ get deletedAt() {
1008
+ return datetime({ optional: true, index: true });
1009
+ }
1010
+ };
1011
+ var baseWithWorkspace = {
1012
+ get id() {
1013
+ return id({ type: "ulid" });
1014
+ },
1015
+ get createdAt() {
1016
+ return datetime({ default: "now", tz: true, index: true });
1017
+ },
1018
+ get updatedAt() {
1019
+ return datetime({ default: "now", updatedAt: true, tz: true, index: true });
1020
+ },
1021
+ get deletedAt() {
1022
+ return datetime({ optional: true, index: true });
1023
+ },
1024
+ get workspaceId() {
1025
+ return string({ index: true });
1026
+ }
1027
+ };
1028
+ var withMetadata = {
1029
+ get metadata() {
1030
+ return json({ optional: true });
1031
+ },
1032
+ get settings() {
1033
+ return json({ optional: true });
1034
+ }
1035
+ };
1036
+ var withActive = {
1037
+ get isActive() {
1038
+ return boolean({ default: true });
1039
+ }
1040
+ };
1041
+ var withAudit = {
1042
+ get createdById() {
1043
+ return string({ optional: true, index: true });
1044
+ },
1045
+ get updatedById() {
1046
+ return string({ optional: true, index: true });
1047
+ }
1048
+ };
1049
+ var withExternalId = {
1050
+ get externalId() {
1051
+ return string({ index: true });
1052
+ }
1053
+ };
1054
+ var withNameDescription = {
1055
+ get name() {
1056
+ return string();
1057
+ },
1058
+ get description() {
1059
+ return string({ optional: true });
1060
+ }
1061
+ };
1062
+ var fields = {
1063
+ get datetime() {
1064
+ return datetime({ default: "now", updatedAt: true, tz: true, index: true });
1065
+ },
1066
+ get id() {
1067
+ return id({ type: "ulid" });
1068
+ },
1069
+ get title() {
1070
+ return string();
1071
+ },
1072
+ get description() {
1073
+ return string({ optional: true });
1074
+ },
1075
+ get externalId() {
1076
+ return string({ index: true });
1077
+ },
1078
+ get metadata() {
1079
+ return json({ optional: true });
1080
+ },
1081
+ get settings() {
1082
+ return json({ optional: true });
1083
+ },
1084
+ get isActive() {
1085
+ return boolean({ default: true });
1086
+ }
1087
+ };
1088
+ var templates = {
1089
+ base,
1090
+ baseWithWorkspace,
1091
+ withMetadata,
1092
+ withActive,
1093
+ withAudit,
1094
+ withExternalId,
1095
+ withNameDescription,
1096
+ fields
1097
+ };
1098
+
1099
+ // packages/dsl/src/to-ir.ts
1100
+ import {
1101
+ EMPTY_EXTENSION_REGISTRY,
1102
+ VibeError,
1103
+ assertValidSchemaIR,
1104
+ mergeExtensionDeclarations
1105
+ } from "@vibeorm/schema";
1106
+ function fieldType(params) {
1107
+ const { column } = params;
1108
+ switch (column.__columnType) {
1109
+ case "string":
1110
+ return "String";
1111
+ case "int":
1112
+ return "Int";
1113
+ case "bigint":
1114
+ return "BigInt";
1115
+ case "float":
1116
+ return "Float";
1117
+ case "decimal":
1118
+ return "Decimal";
1119
+ case "boolean":
1120
+ return "Boolean";
1121
+ case "datetime":
1122
+ return "DateTime";
1123
+ case "json":
1124
+ return "Json";
1125
+ case "bytes":
1126
+ return "Bytes";
1127
+ case "id":
1128
+ return column.__config.type === "autoincrement" ? "Int" : "String";
1129
+ case "enum":
1130
+ return column.__enumName === undefined ? "String" : { enum: column.__enumName };
1131
+ case "unsupported":
1132
+ return "String";
1133
+ }
1134
+ }
1135
+ function idDefault(params) {
1136
+ switch (params.column.__config.type) {
1137
+ case "ulid":
1138
+ return { kind: "ulid" };
1139
+ case "cuid":
1140
+ return { kind: "cuid" };
1141
+ case "uuid":
1142
+ return { kind: "uuid" };
1143
+ case "nanoid":
1144
+ return { kind: "nanoid" };
1145
+ case "autoincrement":
1146
+ return { kind: "autoincrement" };
1147
+ default:
1148
+ return;
1149
+ }
1150
+ }
1151
+ function fieldDefault(params) {
1152
+ const { column } = params;
1153
+ if (column.__columnType === "id")
1154
+ return idDefault({ column });
1155
+ const { dbgenerated, default: value } = column.__config;
1156
+ if (typeof dbgenerated === "string")
1157
+ return { kind: "dbgenerated", sql: dbgenerated };
1158
+ if (value === undefined)
1159
+ return;
1160
+ if (column.__columnType === "datetime" && value === "now")
1161
+ return { kind: "now" };
1162
+ if (column.__columnType === "enum" && typeof value === "string") {
1163
+ return { kind: "enumValue", value };
1164
+ }
1165
+ if (typeof value === "string" || typeof value === "boolean" || typeof value === "number") {
1166
+ return { kind: "literal", value };
1167
+ }
1168
+ if (typeof value === "bigint")
1169
+ return { kind: "literal", value: Number(value) };
1170
+ return;
1171
+ }
1172
+ function nativeType(params) {
1173
+ const { column } = params;
1174
+ if (column.__columnType === "string") {
1175
+ const { maxLength, text } = column.__config;
1176
+ if (typeof maxLength === "number")
1177
+ return `VarChar(${maxLength})`;
1178
+ if (text === true)
1179
+ return "Text";
1180
+ return;
1181
+ }
1182
+ if (column.__columnType === "decimal") {
1183
+ const { precision, scale } = column.__config;
1184
+ if (typeof precision === "number")
1185
+ return `Decimal(${precision}, ${scale ?? 0})`;
1186
+ return;
1187
+ }
1188
+ if (column.__columnType === "datetime" && column.__config.tz === true) {
1189
+ return "Timestamptz";
1190
+ }
1191
+ if (column.__columnType === "unsupported") {
1192
+ return String(column.__config.dbType);
1193
+ }
1194
+ return;
1195
+ }
1196
+ function buildField(params) {
1197
+ const { name, column, extensionNativeType } = params;
1198
+ const config = column.__config;
1199
+ const isList = column.__columnType !== "id" && config.array === true;
1200
+ const dbName = typeof config.map === "string" ? config.map : undefined;
1201
+ const documentation = typeof config.comment === "string" ? config.comment : undefined;
1202
+ const defaultValue = fieldDefault({ column });
1203
+ const native = extensionNativeType ?? nativeType({ column });
1204
+ return {
1205
+ name,
1206
+ type: fieldType({ column }),
1207
+ isList,
1208
+ isOptional: config.optional === true,
1209
+ isId: column.__columnType === "id",
1210
+ isUnique: config.unique === true,
1211
+ isUpdatedAt: column.__columnType === "datetime" && config.updatedAt === true,
1212
+ ...dbName === undefined ? {} : { dbName },
1213
+ ...defaultValue === undefined ? {} : { default: defaultValue },
1214
+ ...native === undefined ? {} : { nativeType: native },
1215
+ ...column.__columnType === "unsupported" ? { isUnsupported: true } : {},
1216
+ ...documentation === undefined ? {} : { documentation }
1217
+ };
1218
+ }
1219
+ function buildRelation(params) {
1220
+ const { relation, plan } = params;
1221
+ return {
1222
+ name: relation.name,
1223
+ model: relation.model,
1224
+ targetModel: relation.targetModel,
1225
+ type: resolveRelationType({ relation, plan }),
1226
+ isList: relation.isList,
1227
+ isOptional: relation.isOptional,
1228
+ ...relation.relationName === undefined ? {} : { relationName: relation.relationName },
1229
+ ...relation.fields === undefined ? {} : { fields: [...relation.fields] },
1230
+ ...relation.references === undefined ? {} : { references: [...relation.references] },
1231
+ ...relation.onDelete === undefined ? {} : { onDelete: relation.onDelete },
1232
+ ...relation.onUpdate === undefined ? {} : { onUpdate: relation.onUpdate }
1233
+ };
1234
+ }
1235
+ var INDEX_KINDS = {
1236
+ BTree: "btree",
1237
+ Hash: "hash",
1238
+ Gin: "gin",
1239
+ Gist: "gist",
1240
+ SpGist: "spgist",
1241
+ Brin: "brin"
1242
+ };
1243
+ function buildIndex(params) {
1244
+ const { index } = params;
1245
+ const kind = index.isUnique ? "unique" : index.indexType === undefined ? "btree" : INDEX_KINDS[index.indexType];
1246
+ return {
1247
+ kind,
1248
+ fields: index.columns.map((name) => ({ name }))
1249
+ };
1250
+ }
1251
+ function buildModel(params) {
1252
+ const { model, plan, extensions, declarations, issues } = params;
1253
+ const primaryKey = model.__primaryKey !== undefined && model.__primaryKey.length > 0 ? { fields: [...model.__primaryKey] } : undefined;
1254
+ const fields2 = [];
1255
+ for (const [name, column] of model.__columns) {
1256
+ const extensionTypeName = column.__config["extensionType"];
1257
+ if (typeof extensionTypeName !== "string") {
1258
+ fields2.push(buildField({ name, column }));
1259
+ continue;
1260
+ }
1261
+ const registered = extensions.fieldTypes.get(extensionTypeName);
1262
+ if (registered === undefined) {
1263
+ issues.push({
1264
+ rule: "extension-attribute",
1265
+ message: `Field "${model.__name}.${name}" uses extension type "${extensionTypeName}" but no configured extension provides it`,
1266
+ model: model.__name,
1267
+ field: name
1268
+ });
1269
+ continue;
1270
+ }
1271
+ const rawArgs = column.__config["extensionArgs"];
1272
+ const lowered = registered.fieldType.lower({
1273
+ model: model.__name,
1274
+ field: name,
1275
+ args: Array.isArray(rawArgs) ? rawArgs.filter((entry) => typeof entry === "string" || typeof entry === "number") : []
1276
+ });
1277
+ if ("error" in lowered) {
1278
+ issues.push({
1279
+ rule: "extension-attribute",
1280
+ message: `Field "${model.__name}.${name}": ${lowered.error}`,
1281
+ model: model.__name,
1282
+ field: name
1283
+ });
1284
+ continue;
1285
+ }
1286
+ fields2.push(buildField({ name, column, extensionNativeType: lowered.nativeType }));
1287
+ if (lowered.declaration !== undefined) {
1288
+ declarations.add({
1289
+ extension: registered.extension.name,
1290
+ model: model.__name,
1291
+ declaration: lowered.declaration
1292
+ });
1293
+ }
1294
+ }
1295
+ for (const use of model.__extensions ?? []) {
1296
+ const registered = extensions.modelAttributes.get(use.attribute);
1297
+ if (registered === undefined) {
1298
+ issues.push({
1299
+ rule: "unknown-attribute",
1300
+ message: `Unknown block attribute "@@${use.attribute}" on model "${model.__name}" \u2014 no extension provides it`,
1301
+ model: model.__name
1302
+ });
1303
+ continue;
1304
+ }
1305
+ const parsed = registered.attribute.parse({
1306
+ model: model.__name,
1307
+ args: {
1308
+ named: { ...use.args ?? {} },
1309
+ ...use.fields === undefined ? {} : { fields: use.fields }
1310
+ },
1311
+ fieldNames: fields2.map((field) => field.name)
1312
+ });
1313
+ if ("error" in parsed) {
1314
+ issues.push({
1315
+ rule: "extension-attribute",
1316
+ message: `@@${use.attribute} on "${model.__name}": ${parsed.error}`,
1317
+ model: model.__name
1318
+ });
1319
+ continue;
1320
+ }
1321
+ declarations.add({
1322
+ extension: registered.extension.name,
1323
+ model: model.__name,
1324
+ declaration: parsed.declaration
1325
+ });
1326
+ }
1327
+ return {
1328
+ name: model.__name,
1329
+ ...model.__map === undefined ? {} : { dbName: model.__map },
1330
+ fields: fields2,
1331
+ relations: (plan.relations.get(model.__name) ?? []).map((relation) => buildRelation({ relation, plan })),
1332
+ indexes: (plan.indexes.get(model.__name) ?? []).map((index) => buildIndex({ index })),
1333
+ ...primaryKey === undefined ? {} : { primaryKey },
1334
+ isView: false
1335
+ };
1336
+ }
1337
+
1338
+ class ExtensionDeclarationCollector {
1339
+ byExtension = new Map;
1340
+ add(params) {
1341
+ const perModel = this.byExtension.get(params.extension) ?? new Map;
1342
+ this.byExtension.set(params.extension, perModel);
1343
+ const existing = perModel.get(params.model) ?? {};
1344
+ perModel.set(params.model, mergeExtensionDeclarations({ base: existing, next: params.declaration }));
1345
+ }
1346
+ toIR() {
1347
+ return [...this.byExtension.entries()].sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map(([name, perModel]) => ({
1348
+ name,
1349
+ options: {
1350
+ models: Object.fromEntries([...perModel.entries()].sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0))
1351
+ }
1352
+ }));
1353
+ }
1354
+ }
1355
+ function toIR(params) {
1356
+ const { schema: schema2, provider = "postgresql" } = params;
1357
+ const extensions = params.extensions ?? EMPTY_EXTENSION_REGISTRY;
1358
+ const plan = planSchema({ schema: schema2 });
1359
+ const declarations = new ExtensionDeclarationCollector;
1360
+ const issues = [];
1361
+ const models = Array.from(schema2.__models.values(), (model) => buildModel({ model, plan, extensions, declarations, issues }));
1362
+ const enums = Array.from(schema2.__enums, ([name, values]) => ({
1363
+ name,
1364
+ values: [...values]
1365
+ }));
1366
+ if (issues.length > 0) {
1367
+ throw new VibeError({
1368
+ code: "VIBE_SCHEMA_INVALID",
1369
+ message: `Schema validation failed with ${issues.length} issue(s): ${issues.map((issue) => issue.message).join("; ")}`,
1370
+ meta: { issues }
1371
+ });
1372
+ }
1373
+ const ir = {
1374
+ datasource: { provider },
1375
+ models,
1376
+ enums,
1377
+ extensions: declarations.toIR()
1378
+ };
1379
+ assertValidSchemaIR({ schema: ir });
1380
+ return ir;
1381
+ }
1382
+
1383
+ // packages/dsl/src/validate.ts
1384
+ function validateSchema(schema2) {
1385
+ const errors = [];
1386
+ const allNames = new Set;
1387
+ for (const name of schema2.__models.keys()) {
1388
+ if (allNames.has(name)) {
1389
+ errors.push({
1390
+ type: "error",
1391
+ message: `Duplicate name "${name}" \u2014 used by more than one model`,
1392
+ model: name
1393
+ });
1394
+ }
1395
+ allNames.add(name);
1396
+ }
1397
+ for (const name of schema2.__enums.keys()) {
1398
+ if (allNames.has(name)) {
1399
+ errors.push({
1400
+ type: "error",
1401
+ message: `Duplicate name "${name}" \u2014 used by both a model and an enum`
1402
+ });
1403
+ }
1404
+ allNames.add(name);
1405
+ }
1406
+ for (const [modelName, modelRelations] of Object.entries(schema2.__relations)) {
1407
+ const model = schema2.__models.get(modelName);
1408
+ if (model === undefined) {
1409
+ errors.push({
1410
+ type: "error",
1411
+ message: `Relations defined for unknown model "${modelName}"`,
1412
+ model: modelName
1413
+ });
1414
+ continue;
1415
+ }
1416
+ for (const [relationName, relation] of Object.entries(modelRelations)) {
1417
+ const targetModel = schema2.__models.get(relation.__targetModel);
1418
+ if (targetModel === undefined) {
1419
+ errors.push({
1420
+ type: "error",
1421
+ message: `Relation "${relationName}" references unknown model "${relation.__targetModel}"`,
1422
+ model: modelName,
1423
+ relation: relationName
1424
+ });
1425
+ continue;
1426
+ }
1427
+ if (relation.__relationType === "one" && relation.__fromModel === modelName) {
1428
+ if (!model.__columns.has(relation.__fromColumn)) {
1429
+ errors.push({
1430
+ type: "error",
1431
+ message: `Relation "${relationName}" references FK column "${relation.__fromColumn}" which doesn't exist on model "${modelName}"`,
1432
+ model: modelName,
1433
+ relation: relationName
1434
+ });
1435
+ }
1436
+ if (!targetModel.__columns.has(relation.__toColumn)) {
1437
+ errors.push({
1438
+ type: "error",
1439
+ message: `Relation "${relationName}" references column "${relation.__toColumn}" which doesn't exist on target model "${relation.__targetModel}"`,
1440
+ model: modelName,
1441
+ relation: relationName
1442
+ });
1443
+ }
1444
+ }
1445
+ if (relation.__targetModel === modelName && relation.__alias === undefined) {
1446
+ const otherSelfRelations = Object.values(modelRelations).filter((other) => other.__targetModel === modelName && other !== relation);
1447
+ if (otherSelfRelations.length > 0) {
1448
+ errors.push({
1449
+ type: "warning",
1450
+ message: `Self-referential relation "${relationName}" on "${modelName}" has no alias \u2014 this may cause Prisma ambiguity`,
1451
+ model: modelName,
1452
+ relation: relationName
1453
+ });
1454
+ }
1455
+ }
1456
+ }
1457
+ }
1458
+ for (const [modelName, model] of schema2.__models) {
1459
+ const hasIdColumn = Array.from(model.__columns.values()).some((column) => column.__columnType === "id");
1460
+ const hasPrimaryKey = model.__primaryKey !== undefined && model.__primaryKey.length > 0;
1461
+ if (!hasIdColumn && !hasPrimaryKey) {
1462
+ errors.push({
1463
+ type: "warning",
1464
+ message: `Model "${modelName}" has no id column and no compound primaryKey defined`,
1465
+ model: modelName
1466
+ });
1467
+ }
1468
+ }
1469
+ return errors;
1470
+ }
1471
+
1472
+ // packages/dsl/src/index.ts
1473
+ function isStandaloneEnumConfig(config) {
1474
+ return "name" in config && typeof config.name === "string";
1475
+ }
1476
+ function enumFactory(config) {
1477
+ if (isStandaloneEnumConfig(config)) {
1478
+ return createEnum(config);
1479
+ }
1480
+ return enumCol(config);
1481
+ }
1482
+ var generate = Object.assign((schemaDef) => ({ prisma: generatePrisma(schemaDef) }), { prisma: generatePrisma });
1483
+ var dsl = {
1484
+ string,
1485
+ int,
1486
+ bigint,
1487
+ float,
1488
+ decimal,
1489
+ boolean,
1490
+ datetime,
1491
+ json,
1492
+ id,
1493
+ bytes,
1494
+ unsupported,
1495
+ extensionType,
1496
+ enum: enumFactory,
1497
+ create,
1498
+ defineRelations,
1499
+ defineRelationsPart,
1500
+ mergeRelations,
1501
+ schema,
1502
+ domain,
1503
+ toIR,
1504
+ validateSchema,
1505
+ generate,
1506
+ templates
1507
+ };
1508
+ export {
1509
+ withNameDescription,
1510
+ withMetadata,
1511
+ withExternalId,
1512
+ withAudit,
1513
+ withActive,
1514
+ validateSchema,
1515
+ toIR,
1516
+ templates,
1517
+ schema,
1518
+ resolveRelationType,
1519
+ planSchema,
1520
+ mergeRelations,
1521
+ getColumnNames,
1522
+ generatePrisma,
1523
+ fields,
1524
+ evaluateIndexes,
1525
+ dsl,
1526
+ domain,
1527
+ detectOneToOnePairs,
1528
+ defineRelationsPart,
1529
+ defineRelations,
1530
+ createIndexBuilder,
1531
+ createEnum,
1532
+ createChildrenBuilder,
1533
+ create,
1534
+ cloneColumnDef,
1535
+ children,
1536
+ baseWithWorkspace,
1537
+ base,
1538
+ ChainableStringColumn,
1539
+ ChainableIdColumn,
1540
+ ChainableEnumColumn,
1541
+ ChainableDecimalColumn,
1542
+ ChainableDatetimeColumn,
1543
+ ChainableColumn
1544
+ };
1545
+
1546
+ //# debugId=536E52A51C12A18264756E2164756E21