@vibeorm/dsl 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +523 -0
- package/package.json +45 -0
- package/src/column.ts +389 -0
- package/src/enum.ts +71 -0
- package/src/generator/index.ts +5 -0
- package/src/generator/prisma.ts +728 -0
- package/src/index.ts +247 -0
- package/src/model.ts +330 -0
- package/src/relation.ts +75 -0
- package/src/relations.ts +293 -0
- package/src/schema.ts +184 -0
- package/src/templates.ts +134 -0
- package/src/types.ts +381 -0
- package/src/validate.ts +141 -0
package/README.md
ADDED
|
@@ -0,0 +1,523 @@
|
|
|
1
|
+
# @vibeorm/dsl
|
|
2
|
+
|
|
3
|
+
TypeScript DSL for defining Prisma schemas programmatically. Define models, columns, relations, and enums in TypeScript and generate valid `.prisma` schema files.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
bun add @vibeorm/dsl
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Quick Start
|
|
12
|
+
|
|
13
|
+
```typescript
|
|
14
|
+
import { dsl } from "@vibeorm/dsl";
|
|
15
|
+
|
|
16
|
+
// Define models
|
|
17
|
+
const Workspace = dsl.create({
|
|
18
|
+
name: "Workspace",
|
|
19
|
+
schema: {
|
|
20
|
+
...dsl.templates.base,
|
|
21
|
+
name: dsl.string(),
|
|
22
|
+
slug: dsl.string().unique(),
|
|
23
|
+
},
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
const User = dsl.create({
|
|
27
|
+
name: "User",
|
|
28
|
+
schema: {
|
|
29
|
+
...dsl.templates.base,
|
|
30
|
+
email: dsl.string().unique(),
|
|
31
|
+
workspaceId: dsl.string().index(),
|
|
32
|
+
},
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
// Define relations
|
|
36
|
+
const relations = dsl.defineRelations({ Workspace, User }, (r) => ({
|
|
37
|
+
Workspace: {
|
|
38
|
+
users: r.many.User({
|
|
39
|
+
from: r.Workspace.id,
|
|
40
|
+
to: r.User.workspaceId,
|
|
41
|
+
}),
|
|
42
|
+
},
|
|
43
|
+
User: {
|
|
44
|
+
workspace: r.one.Workspace({
|
|
45
|
+
from: r.User.workspaceId,
|
|
46
|
+
to: r.Workspace.id,
|
|
47
|
+
}),
|
|
48
|
+
},
|
|
49
|
+
}));
|
|
50
|
+
|
|
51
|
+
// Assemble schema and generate
|
|
52
|
+
const schema = dsl.schema({ Workspace, User }, { relations });
|
|
53
|
+
const prismaOutput = dsl.generate.prisma(schema);
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Column Types
|
|
57
|
+
|
|
58
|
+
Every column builder supports both **object syntax** and **chainable syntax**:
|
|
59
|
+
|
|
60
|
+
```typescript
|
|
61
|
+
// Object syntax
|
|
62
|
+
dsl.string({ optional: true, unique: true, default: "hello" })
|
|
63
|
+
|
|
64
|
+
// Chainable syntax (equivalent)
|
|
65
|
+
dsl.string().optional().unique().default("hello")
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### Available Types
|
|
69
|
+
|
|
70
|
+
| Builder | Prisma Type | Notes |
|
|
71
|
+
|---|---|---|
|
|
72
|
+
| `dsl.string()` | `String` | `.maxLength(n)` for `@db.VarChar(n)`, `.text()` for `@db.Text` |
|
|
73
|
+
| `dsl.int()` | `Int` | |
|
|
74
|
+
| `dsl.bigint()` | `BigInt` | |
|
|
75
|
+
| `dsl.float()` | `Float` | |
|
|
76
|
+
| `dsl.decimal()` | `Decimal` | `.precision(p, s)` for `@db.Decimal(p, s)` |
|
|
77
|
+
| `dsl.boolean()` | `Boolean` | |
|
|
78
|
+
| `dsl.datetime()` | `DateTime` | `.defaultNow()`, `.updatedAt()`, `.tz()` for `@db.Timestamptz()` |
|
|
79
|
+
| `dsl.json()` | `Json` | |
|
|
80
|
+
| `dsl.id()` | `String`/`Int` | `.type("ulid" \| "cuid" \| "uuid" \| "autoincrement")` |
|
|
81
|
+
| `dsl.bytes()` | `Bytes` | |
|
|
82
|
+
| `dsl.unsupported("tsvector")` | `Unsupported("tsvector")` | For database-native types without Prisma mapping |
|
|
83
|
+
|
|
84
|
+
### Common Modifiers
|
|
85
|
+
|
|
86
|
+
All column types support these chainable methods:
|
|
87
|
+
|
|
88
|
+
```typescript
|
|
89
|
+
dsl.string()
|
|
90
|
+
.optional() // Nullable (String?)
|
|
91
|
+
.unique() // @unique
|
|
92
|
+
.index() // @@index
|
|
93
|
+
.array() // String[]
|
|
94
|
+
.default("value") // @default("value")
|
|
95
|
+
.map("db_column_name") // @map("db_column_name")
|
|
96
|
+
.dbgenerated("expression") // @default(dbgenerated("expression"))
|
|
97
|
+
.comment("text") // /// text (triple-slash comment above column)
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### ID Types
|
|
101
|
+
|
|
102
|
+
```typescript
|
|
103
|
+
dsl.id() // String @id @default(ulid()) — default
|
|
104
|
+
dsl.id().type("cuid") // String @id @default(cuid())
|
|
105
|
+
dsl.id().type("uuid") // String @id @default(uuid())
|
|
106
|
+
dsl.id().type("autoincrement") // Int @id @default(autoincrement())
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### Datetime Shortcuts
|
|
110
|
+
|
|
111
|
+
```typescript
|
|
112
|
+
dsl.datetime().defaultNow() // @default(now())
|
|
113
|
+
dsl.datetime().updatedAt() // @updatedAt
|
|
114
|
+
dsl.datetime().tz() // @db.Timestamptz()
|
|
115
|
+
dsl.datetime().defaultNow().updatedAt().tz() // All combined
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
## Enums
|
|
119
|
+
|
|
120
|
+
### Standalone Enums (Reusable)
|
|
121
|
+
|
|
122
|
+
```typescript
|
|
123
|
+
const UserRole = dsl.enum({
|
|
124
|
+
name: "UserRole",
|
|
125
|
+
options: ["admin", "user", "guest"],
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
const User = dsl.create({
|
|
129
|
+
name: "User",
|
|
130
|
+
schema: {
|
|
131
|
+
id: dsl.id(),
|
|
132
|
+
role: UserRole.field().default("user"),
|
|
133
|
+
},
|
|
134
|
+
});
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
### Inline Enums (One-Off)
|
|
138
|
+
|
|
139
|
+
Auto-named as `ModelNameColumnName`:
|
|
140
|
+
|
|
141
|
+
```typescript
|
|
142
|
+
const Project = dsl.create({
|
|
143
|
+
name: "Project",
|
|
144
|
+
schema: {
|
|
145
|
+
id: dsl.id(),
|
|
146
|
+
// Generates: enum ProjectStatus { active, archived, deleted }
|
|
147
|
+
status: dsl.enum({ options: ["active", "archived", "deleted"] }).default("active"),
|
|
148
|
+
},
|
|
149
|
+
});
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
## Models
|
|
153
|
+
|
|
154
|
+
### Basic Model
|
|
155
|
+
|
|
156
|
+
```typescript
|
|
157
|
+
const User = dsl.create({
|
|
158
|
+
name: "User",
|
|
159
|
+
schema: {
|
|
160
|
+
id: dsl.id(),
|
|
161
|
+
email: dsl.string().unique(),
|
|
162
|
+
name: dsl.string(),
|
|
163
|
+
bio: dsl.string().optional(),
|
|
164
|
+
},
|
|
165
|
+
});
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
### With Indexes
|
|
169
|
+
|
|
170
|
+
The `indexes` callback provides a type-safe index builder:
|
|
171
|
+
|
|
172
|
+
```typescript
|
|
173
|
+
const User = dsl.create({
|
|
174
|
+
name: "User",
|
|
175
|
+
schema: {
|
|
176
|
+
id: dsl.id(),
|
|
177
|
+
email: dsl.string().index(), // Single-column: @@index([email])
|
|
178
|
+
organizationId: dsl.string().index(),
|
|
179
|
+
},
|
|
180
|
+
indexes: (i) => [
|
|
181
|
+
i.unique("organizationId", "email"), // @@unique([organizationId, email])
|
|
182
|
+
i.gin("tags"), // @@index([tags], type: Gin)
|
|
183
|
+
],
|
|
184
|
+
});
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
### With Compound Primary Key
|
|
188
|
+
|
|
189
|
+
```typescript
|
|
190
|
+
const UserGroup = dsl.create({
|
|
191
|
+
name: "UserGroup",
|
|
192
|
+
schema: {
|
|
193
|
+
userId: dsl.string(),
|
|
194
|
+
groupId: dsl.string(),
|
|
195
|
+
joinedAt: dsl.datetime().defaultNow(),
|
|
196
|
+
},
|
|
197
|
+
primaryKey: ["userId", "groupId"], // @@id([userId, groupId])
|
|
198
|
+
});
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
### With Table Mapping
|
|
202
|
+
|
|
203
|
+
```typescript
|
|
204
|
+
const User = dsl.create({
|
|
205
|
+
name: "User",
|
|
206
|
+
schema: {
|
|
207
|
+
id: dsl.id(),
|
|
208
|
+
firstName: dsl.string().map("first_name"),
|
|
209
|
+
},
|
|
210
|
+
map: "users", // @@map("users")
|
|
211
|
+
});
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
### With Children (Inline Nested Models)
|
|
215
|
+
|
|
216
|
+
Use a callback for `schema` to access the children builder:
|
|
217
|
+
|
|
218
|
+
```typescript
|
|
219
|
+
const Order = dsl.create({
|
|
220
|
+
name: "Order",
|
|
221
|
+
schema: (s) => ({
|
|
222
|
+
id: dsl.id(),
|
|
223
|
+
total: dsl.decimal(),
|
|
224
|
+
// Creates an OrderItem model with an auto-generated orderId FK
|
|
225
|
+
items: s.children({
|
|
226
|
+
name: "OrderItem",
|
|
227
|
+
schema: {
|
|
228
|
+
id: dsl.id(),
|
|
229
|
+
quantity: dsl.int(),
|
|
230
|
+
price: dsl.decimal(),
|
|
231
|
+
},
|
|
232
|
+
}),
|
|
233
|
+
}),
|
|
234
|
+
});
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
## Relations
|
|
238
|
+
|
|
239
|
+
Relations are defined separately from models using `dsl.defineRelations()`. This keeps models focused on columns and allows relations to reference each other.
|
|
240
|
+
|
|
241
|
+
### One-to-Many
|
|
242
|
+
|
|
243
|
+
```typescript
|
|
244
|
+
const relations = dsl.defineRelations({ Workspace, User }, (r) => ({
|
|
245
|
+
Workspace: {
|
|
246
|
+
users: r.many.User({
|
|
247
|
+
from: r.Workspace.id,
|
|
248
|
+
to: r.User.workspaceId,
|
|
249
|
+
}),
|
|
250
|
+
},
|
|
251
|
+
User: {
|
|
252
|
+
workspace: r.one.Workspace({
|
|
253
|
+
from: r.User.workspaceId,
|
|
254
|
+
to: r.Workspace.id,
|
|
255
|
+
}),
|
|
256
|
+
},
|
|
257
|
+
}));
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
### One-to-One
|
|
261
|
+
|
|
262
|
+
When both sides define `r.one`, the generator merges them into a single Prisma relation. The side with the FK column becomes the owning side:
|
|
263
|
+
|
|
264
|
+
```typescript
|
|
265
|
+
const relations = dsl.defineRelations({ User, UserProfile }, (r) => ({
|
|
266
|
+
User: {
|
|
267
|
+
profile: r.one.UserProfile({
|
|
268
|
+
from: r.User.id,
|
|
269
|
+
to: r.UserProfile.userId,
|
|
270
|
+
}),
|
|
271
|
+
},
|
|
272
|
+
UserProfile: {
|
|
273
|
+
user: r.one.User({
|
|
274
|
+
from: r.UserProfile.userId,
|
|
275
|
+
to: r.User.id,
|
|
276
|
+
onDelete: "Cascade",
|
|
277
|
+
}),
|
|
278
|
+
},
|
|
279
|
+
}));
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
Generates:
|
|
283
|
+
|
|
284
|
+
```prisma
|
|
285
|
+
model User {
|
|
286
|
+
// ...
|
|
287
|
+
Profile UserProfile?
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
model UserProfile {
|
|
291
|
+
// ...
|
|
292
|
+
User User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
293
|
+
}
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
### Self-Referential Relations
|
|
297
|
+
|
|
298
|
+
Use `alias` to disambiguate when a model references itself:
|
|
299
|
+
|
|
300
|
+
```typescript
|
|
301
|
+
Task: {
|
|
302
|
+
parentTask: r.one.Task({
|
|
303
|
+
from: r.Task.parentTaskId,
|
|
304
|
+
to: r.Task.id,
|
|
305
|
+
optional: true,
|
|
306
|
+
alias: "TaskHierarchy",
|
|
307
|
+
}),
|
|
308
|
+
childTasks: r.many.Task({
|
|
309
|
+
from: r.Task.id,
|
|
310
|
+
to: r.Task.parentTaskId,
|
|
311
|
+
alias: "TaskHierarchy",
|
|
312
|
+
}),
|
|
313
|
+
},
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
### Multiple Relations to Same Model
|
|
317
|
+
|
|
318
|
+
Use `alias` to distinguish different relations between the same models:
|
|
319
|
+
|
|
320
|
+
```typescript
|
|
321
|
+
Referral: {
|
|
322
|
+
referrer: r.one.User({
|
|
323
|
+
from: r.Referral.referrerId,
|
|
324
|
+
to: r.User.id,
|
|
325
|
+
alias: "ReferralReferrer",
|
|
326
|
+
}),
|
|
327
|
+
referredUser: r.one.User({
|
|
328
|
+
from: r.Referral.referredUserId,
|
|
329
|
+
to: r.User.id,
|
|
330
|
+
optional: true,
|
|
331
|
+
alias: "ReferralReferred",
|
|
332
|
+
}),
|
|
333
|
+
},
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
### Optional Relations
|
|
337
|
+
|
|
338
|
+
```typescript
|
|
339
|
+
User: {
|
|
340
|
+
workspace: r.one.Workspace({
|
|
341
|
+
from: r.User.workspaceId,
|
|
342
|
+
to: r.Workspace.id,
|
|
343
|
+
optional: true, // Workspace?
|
|
344
|
+
}),
|
|
345
|
+
},
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
### Cascade Actions
|
|
349
|
+
|
|
350
|
+
```typescript
|
|
351
|
+
UserProfile: {
|
|
352
|
+
user: r.one.User({
|
|
353
|
+
from: r.UserProfile.userId,
|
|
354
|
+
to: r.User.id,
|
|
355
|
+
onDelete: "Cascade", // onDelete: Cascade
|
|
356
|
+
onUpdate: "Cascade", // onUpdate: Cascade
|
|
357
|
+
}),
|
|
358
|
+
},
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
Supported actions: `"Cascade"`, `"SetNull"`, `"Restrict"`, `"NoAction"`, `"SetDefault"`.
|
|
362
|
+
|
|
363
|
+
### Splitting Relations Across Files
|
|
364
|
+
|
|
365
|
+
For large schemas, split relation definitions with `defineRelationsPart` and `mergeRelations`:
|
|
366
|
+
|
|
367
|
+
```typescript
|
|
368
|
+
// file: user-relations.ts
|
|
369
|
+
export const userRelations = dsl.defineRelationsPart({ User, Workspace }, (r) => ({
|
|
370
|
+
User: {
|
|
371
|
+
workspace: r.one.Workspace({
|
|
372
|
+
from: r.User.workspaceId,
|
|
373
|
+
to: r.Workspace.id,
|
|
374
|
+
}),
|
|
375
|
+
},
|
|
376
|
+
}));
|
|
377
|
+
|
|
378
|
+
// file: workspace-relations.ts
|
|
379
|
+
export const workspaceRelations = dsl.defineRelationsPart({ User, Workspace }, (r) => ({
|
|
380
|
+
Workspace: {
|
|
381
|
+
users: r.many.User({
|
|
382
|
+
from: r.Workspace.id,
|
|
383
|
+
to: r.User.workspaceId,
|
|
384
|
+
}),
|
|
385
|
+
},
|
|
386
|
+
}));
|
|
387
|
+
|
|
388
|
+
// file: schema.ts
|
|
389
|
+
const relations = dsl.mergeRelations(userRelations, workspaceRelations);
|
|
390
|
+
const schema = dsl.schema({ User, Workspace }, { relations });
|
|
391
|
+
```
|
|
392
|
+
|
|
393
|
+
## Templates
|
|
394
|
+
|
|
395
|
+
Built-in field templates for common patterns. Templates use getters, so every spread produces fresh column instances (no shared mutable state):
|
|
396
|
+
|
|
397
|
+
```typescript
|
|
398
|
+
const User = dsl.create({
|
|
399
|
+
name: "User",
|
|
400
|
+
schema: {
|
|
401
|
+
...dsl.templates.base, // id, createdAt, updatedAt, deletedAt
|
|
402
|
+
...dsl.templates.withMetadata, // metadata, settings (optional Json)
|
|
403
|
+
email: dsl.string().unique(),
|
|
404
|
+
},
|
|
405
|
+
});
|
|
406
|
+
```
|
|
407
|
+
|
|
408
|
+
### Available Templates
|
|
409
|
+
|
|
410
|
+
| Template | Fields |
|
|
411
|
+
|---|---|
|
|
412
|
+
| `dsl.templates.base` | `id` (ULID), `createdAt`, `updatedAt`, `deletedAt` (optional) |
|
|
413
|
+
| `dsl.templates.baseWithWorkspace` | Same as `base` + `workspaceId` (indexed String) |
|
|
414
|
+
| `dsl.templates.withMetadata` | `metadata`, `settings` (optional Json) |
|
|
415
|
+
| `dsl.templates.withActive` | `isActive` (Boolean, default `true`) |
|
|
416
|
+
| `dsl.templates.withAudit` | `createdById`, `updatedById` (optional, indexed) |
|
|
417
|
+
| `dsl.templates.withExternalId` | `externalId` (indexed String) |
|
|
418
|
+
| `dsl.templates.withNameDescription` | `name` (String), `description` (optional String) |
|
|
419
|
+
|
|
420
|
+
Individual fields are also available via `dsl.templates.fields`:
|
|
421
|
+
|
|
422
|
+
```typescript
|
|
423
|
+
dsl.templates.fields.id // ULID primary key
|
|
424
|
+
dsl.templates.fields.datetime // DateTime with now + updatedAt + tz
|
|
425
|
+
dsl.templates.fields.title // String
|
|
426
|
+
dsl.templates.fields.description // Optional String
|
|
427
|
+
dsl.templates.fields.metadata // Optional Json
|
|
428
|
+
dsl.templates.fields.settings // Optional Json
|
|
429
|
+
dsl.templates.fields.isActive // Boolean default true
|
|
430
|
+
dsl.templates.fields.externalId // Indexed String
|
|
431
|
+
```
|
|
432
|
+
|
|
433
|
+
## Schema Assembly
|
|
434
|
+
|
|
435
|
+
### Basic Schema
|
|
436
|
+
|
|
437
|
+
```typescript
|
|
438
|
+
const schema = dsl.schema({ User, Workspace }, { relations });
|
|
439
|
+
```
|
|
440
|
+
|
|
441
|
+
### With Standalone Enums
|
|
442
|
+
|
|
443
|
+
Pass standalone enums alongside models:
|
|
444
|
+
|
|
445
|
+
```typescript
|
|
446
|
+
const UserRole = dsl.enum({ name: "UserRole", options: ["admin", "user"] });
|
|
447
|
+
|
|
448
|
+
const schema = dsl.schema(
|
|
449
|
+
{ UserRole, User, Workspace },
|
|
450
|
+
{ relations }
|
|
451
|
+
);
|
|
452
|
+
```
|
|
453
|
+
|
|
454
|
+
### Grouped by Domain
|
|
455
|
+
|
|
456
|
+
```typescript
|
|
457
|
+
const schema = dsl.schema({
|
|
458
|
+
UserRole,
|
|
459
|
+
User,
|
|
460
|
+
shipping: { Carrier, CarrierService },
|
|
461
|
+
printing: { PrintAccount, PrintJob },
|
|
462
|
+
}, { relations });
|
|
463
|
+
```
|
|
464
|
+
|
|
465
|
+
## Generating Prisma Output
|
|
466
|
+
|
|
467
|
+
```typescript
|
|
468
|
+
const schema = dsl.schema({ User, Workspace }, { relations });
|
|
469
|
+
|
|
470
|
+
// Generate just the models + enums (no datasource/generator blocks)
|
|
471
|
+
const prismaBody = dsl.generate.prisma(schema);
|
|
472
|
+
```
|
|
473
|
+
|
|
474
|
+
The output contains only model and enum definitions. Add your own datasource and generator blocks:
|
|
475
|
+
|
|
476
|
+
```typescript
|
|
477
|
+
import { writeFileSync } from "fs";
|
|
478
|
+
|
|
479
|
+
const fullSchema = `datasource db {
|
|
480
|
+
provider = "postgresql"
|
|
481
|
+
url = env("DATABASE_URL")
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
generator client {
|
|
485
|
+
provider = "prisma-client"
|
|
486
|
+
output = "../src/generated/prisma"
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
${prismaBody}
|
|
490
|
+
`;
|
|
491
|
+
|
|
492
|
+
writeFileSync("prisma/schema.prisma", fullSchema);
|
|
493
|
+
```
|
|
494
|
+
|
|
495
|
+
## Validation
|
|
496
|
+
|
|
497
|
+
Check for common issues before generating:
|
|
498
|
+
|
|
499
|
+
```typescript
|
|
500
|
+
const errors = dsl.validateSchema(schema);
|
|
501
|
+
|
|
502
|
+
for (const error of errors) {
|
|
503
|
+
console.log(`[${error.type}] ${error.model}: ${error.message}`);
|
|
504
|
+
}
|
|
505
|
+
```
|
|
506
|
+
|
|
507
|
+
Checks for:
|
|
508
|
+
- Duplicate model/enum names
|
|
509
|
+
- Missing FK columns referenced in relations
|
|
510
|
+
- Self-referential relations without aliases
|
|
511
|
+
- Models without an `id` column or compound `primaryKey`
|
|
512
|
+
- Relations targeting non-existent models
|
|
513
|
+
|
|
514
|
+
## Full Example
|
|
515
|
+
|
|
516
|
+
See `tests/fixtures/advanced-schema.ts` for a comprehensive 11-model schema covering:
|
|
517
|
+
|
|
518
|
+
- Standalone and inline enums
|
|
519
|
+
- One-to-many, one-to-one, self-referential, and multi-FK relations
|
|
520
|
+
- Many-to-many via junction table
|
|
521
|
+
- Children (nested inline models)
|
|
522
|
+
- Array columns, optional FKs, composite indexes
|
|
523
|
+
- Relation aliases for disambiguation
|
package/package.json
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@vibeorm/dsl",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "TypeScript DSL for defining Prisma schemas with type-safe models, relations, and templates",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"keywords": [
|
|
7
|
+
"orm",
|
|
8
|
+
"dsl",
|
|
9
|
+
"prisma",
|
|
10
|
+
"schema",
|
|
11
|
+
"typescript",
|
|
12
|
+
"codegen",
|
|
13
|
+
"postgresql"
|
|
14
|
+
],
|
|
15
|
+
"type": "module",
|
|
16
|
+
"exports": {
|
|
17
|
+
".": {
|
|
18
|
+
"default": "./src/index.ts",
|
|
19
|
+
"types": "./src/index.ts"
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
"files": [
|
|
23
|
+
"src"
|
|
24
|
+
],
|
|
25
|
+
"repository": {
|
|
26
|
+
"type": "git",
|
|
27
|
+
"url": "https://github.com/vibeorm/vibeorm.git",
|
|
28
|
+
"directory": "packages/dsl"
|
|
29
|
+
},
|
|
30
|
+
"homepage": "https://github.com/vibeorm/vibeorm",
|
|
31
|
+
"bugs": {
|
|
32
|
+
"url": "https://github.com/vibeorm/vibeorm/issues"
|
|
33
|
+
},
|
|
34
|
+
"publishConfig": {
|
|
35
|
+
"access": "public"
|
|
36
|
+
},
|
|
37
|
+
"engines": {
|
|
38
|
+
"bun": ">=1.1.0"
|
|
39
|
+
},
|
|
40
|
+
"devDependencies": {
|
|
41
|
+
"@types/bun": "latest",
|
|
42
|
+
"prisma": "^7.3.0",
|
|
43
|
+
"typescript": "^5.9.3"
|
|
44
|
+
}
|
|
45
|
+
}
|