@vibeorm/dsl 0.4.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/src/relations.ts DELETED
@@ -1,293 +0,0 @@
1
- /**
2
- * Drizzle-style centralized relations for @vibeorm/dsl
3
- *
4
- * @example
5
- * const relations = dsl.defineRelations({ User, Post }, (r) => ({
6
- * User: {
7
- * posts: r.many.Post({
8
- * from: r.User.id,
9
- * to: r.Post.authorId,
10
- * }),
11
- * },
12
- * Post: {
13
- * author: r.one.User({
14
- * from: r.Post.authorId,
15
- * to: r.User.id,
16
- * }),
17
- * },
18
- * }));
19
- */
20
-
21
- import type {
22
- ColumnRef,
23
- ManyRelationDefConfig,
24
- ModelDef,
25
- OneRelationDefConfig,
26
- RelationsBuilder,
27
- RelationsMap,
28
- RelationsOutput,
29
- StoredRelationDef,
30
- ThroughRef,
31
- } from "./types.ts";
32
-
33
- // ─── Column Ref Factory ─────────────────────
34
-
35
- /**
36
- * Creates a ColumnRef with the through() method for many-to-many relations
37
- */
38
- function createColumnRef<TModel extends string, TColumn extends string>(
39
- model: TModel,
40
- column: TColumn
41
- ): ColumnRef<TModel, TColumn> {
42
- return {
43
- __type: "columnRef",
44
- __model: model,
45
- __column: column,
46
- through<TJunction extends string, TJunctionCol extends string>(
47
- junction: ColumnRef<TJunction, TJunctionCol>
48
- ): ThroughRef<TModel, TColumn, TJunction, TJunctionCol> {
49
- return {
50
- __type: "throughRef",
51
- __model: model,
52
- __column: column,
53
- __junction: junction.__model,
54
- __junctionColumn: junction.__column,
55
- };
56
- },
57
- };
58
- }
59
-
60
- // ─── Ref Extraction ─────────────────────
61
-
62
- /**
63
- * Extracts info from a ColumnRef or ThroughRef
64
- */
65
- function extractRefInfo(ref: ColumnRef | ThroughRef): {
66
- model: string;
67
- column: string;
68
- junction?: string;
69
- junctionColumn?: string;
70
- } {
71
- if (ref.__type === "throughRef") {
72
- return {
73
- model: ref.__model,
74
- column: ref.__column,
75
- junction: ref.__junction,
76
- junctionColumn: ref.__junctionColumn,
77
- };
78
- }
79
- return {
80
- model: ref.__model,
81
- column: ref.__column,
82
- };
83
- }
84
-
85
- // ─── Relation Factory ─────────────────────
86
-
87
- /**
88
- * Creates a relation builder factory for one() or many()
89
- */
90
- function createRelationFactory(
91
- relationType: "one" | "many",
92
- targetModelName: string
93
- ): (config: OneRelationDefConfig | ManyRelationDefConfig) => StoredRelationDef {
94
- return (config) => {
95
- const from = extractRefInfo(config.from);
96
- const to = extractRefInfo(config.to);
97
-
98
- // Determine through junction info (for many-to-many)
99
- let throughJunction: string | undefined;
100
- let throughFromCol: string | undefined;
101
- let throughToCol: string | undefined;
102
-
103
- if (config.from.__type === "throughRef") {
104
- throughJunction = config.from.__junction;
105
- throughFromCol = config.from.__junctionColumn;
106
- }
107
- if (config.to.__type === "throughRef") {
108
- // If both have through, they should use the same junction
109
- if (!throughJunction) {
110
- throughJunction = config.to.__junction;
111
- }
112
- throughToCol = config.to.__junctionColumn;
113
- }
114
-
115
- return {
116
- __type: "storedRelation",
117
- __relationType: relationType,
118
- __targetModel: targetModelName,
119
- __fromModel: from.model,
120
- __fromColumn: from.column,
121
- __toModel: to.model,
122
- __toColumn: to.column,
123
- __throughJunction: throughJunction,
124
- __throughFromCol: throughFromCol,
125
- __throughToCol: throughToCol,
126
- __optional: config.optional ?? (relationType === "one" ? false : true),
127
- __alias: config.alias,
128
- __onDelete: config.onDelete,
129
- __onUpdate: config.onUpdate,
130
- };
131
- };
132
- }
133
-
134
- // ─── Relations Builder ─────────────────────
135
-
136
- /**
137
- * Creates the relations builder with r.one, r.many, and r.ModelName.columnName
138
- */
139
- function createRelationsBuilder<TSchema extends Record<string, ModelDef>>(
140
- schema: TSchema
141
- ): RelationsBuilder<TSchema> {
142
- const builder = {} as RelationsBuilder<TSchema>;
143
-
144
- // Create r.one and r.many objects
145
- const oneObj: Record<string, (config: OneRelationDefConfig) => StoredRelationDef> = {};
146
- const manyObj: Record<string, (config: ManyRelationDefConfig) => StoredRelationDef> = {};
147
-
148
- for (const modelName of Object.keys(schema)) {
149
- // r.one.ModelName
150
- oneObj[modelName] = createRelationFactory("one", modelName);
151
- // r.many.ModelName
152
- manyObj[modelName] = createRelationFactory("many", modelName);
153
-
154
- // r.ModelName.columnName - create column references
155
- const model = schema[modelName];
156
- if (!model) continue;
157
-
158
- const columnRefs: Record<string, ColumnRef> = {};
159
-
160
- // Get column names from the model's __columns Map
161
- for (const [colName] of model.__columns) {
162
- columnRefs[colName] = createColumnRef(modelName, colName);
163
- }
164
-
165
- // Also add column names from children if they have auto-generated FKs
166
- for (const [, childModel] of model.__children) {
167
- // Child models have parentId FK - we might want to expose this
168
- // For now, children are separate models so we don't add them here
169
- }
170
-
171
- // Assign column refs to builder (r.ModelName)
172
- (builder as Record<string, unknown>)[modelName] = columnRefs;
173
- }
174
-
175
- (builder as Record<string, unknown>).one = oneObj;
176
- (builder as Record<string, unknown>).many = manyObj;
177
-
178
- return builder;
179
- }
180
-
181
- // ─── Relations Processing ─────────────────────
182
-
183
- /**
184
- * Processes the callback output into a RelationsMap
185
- */
186
- function processRelationsOutput<TSchema extends Record<string, ModelDef>>(
187
- output: RelationsOutput<TSchema>
188
- ): RelationsMap {
189
- const result: RelationsMap = {};
190
-
191
- for (const [modelName, relations] of Object.entries(output)) {
192
- if (relations) {
193
- result[modelName] = relations;
194
- }
195
- }
196
-
197
- return result;
198
- }
199
-
200
- // ─── Public API ─────────────────────
201
-
202
- /**
203
- * Define all relations for a schema in one place
204
- *
205
- * @example
206
- * const relations = dsl.defineRelations({ User, Post, Comment }, (r) => ({
207
- * User: {
208
- * posts: r.many.Post({
209
- * from: r.User.id,
210
- * to: r.Post.authorId,
211
- * }),
212
- * },
213
- * Post: {
214
- * author: r.one.User({
215
- * from: r.Post.authorId,
216
- * to: r.User.id,
217
- * }),
218
- * comments: r.many.Comment({
219
- * from: r.Post.id,
220
- * to: r.Comment.postId,
221
- * }),
222
- * },
223
- * Comment: {
224
- * post: r.one.Post({
225
- * from: r.Comment.postId,
226
- * to: r.Post.id,
227
- * }),
228
- * },
229
- * }));
230
- */
231
- export function defineRelations<TSchema extends Record<string, ModelDef>>(
232
- schema: TSchema,
233
- callback: (r: RelationsBuilder<TSchema>) => RelationsOutput<TSchema>
234
- ): RelationsMap {
235
- const builder = createRelationsBuilder(schema);
236
- const output = callback(builder);
237
- return processRelationsOutput(output);
238
- }
239
-
240
- /**
241
- * Define a partial set of relations (for splitting into multiple files)
242
- *
243
- * @example
244
- * // user-relations.ts
245
- * export const userRelations = dsl.defineRelationsPart({ User, Post }, (r) => ({
246
- * User: {
247
- * posts: r.many.Post({ from: r.User.id, to: r.Post.authorId }),
248
- * },
249
- * }));
250
- *
251
- * // post-relations.ts
252
- * export const postRelations = dsl.defineRelationsPart({ User, Post }, (r) => ({
253
- * Post: {
254
- * author: r.one.User({ from: r.Post.authorId, to: r.User.id }),
255
- * },
256
- * }));
257
- *
258
- * // Merge in main file
259
- * const allRelations = { ...userRelations, ...postRelations };
260
- */
261
- export function defineRelationsPart<TSchema extends Record<string, ModelDef>>(
262
- schema: TSchema,
263
- callback: (r: RelationsBuilder<TSchema>) => Partial<RelationsOutput<TSchema>>
264
- ): RelationsMap {
265
- const builder = createRelationsBuilder(schema);
266
- const output = callback(builder);
267
- return processRelationsOutput(output as RelationsOutput<TSchema>);
268
- }
269
-
270
- /**
271
- * Merge multiple relation parts into a single RelationsMap
272
- *
273
- * @example
274
- * const allRelations = dsl.mergeRelations(
275
- * userRelations,
276
- * postRelations,
277
- * commentRelations,
278
- * );
279
- */
280
- export function mergeRelations(...parts: RelationsMap[]): RelationsMap {
281
- const result: RelationsMap = {};
282
-
283
- for (const part of parts) {
284
- for (const [modelName, relations] of Object.entries(part)) {
285
- if (!result[modelName]) {
286
- result[modelName] = {};
287
- }
288
- Object.assign(result[modelName], relations);
289
- }
290
- }
291
-
292
- return result;
293
- }
package/src/schema.ts DELETED
@@ -1,184 +0,0 @@
1
- /**
2
- * Schema composer for combining models and relations
3
- *
4
- * v3: Relations are now passed separately via options
5
- *
6
- * @example
7
- * // Define models
8
- * const models = { Workspace, User, Printer };
9
- *
10
- * // Define relations separately
11
- * const relations = dsl.defineRelations(models, (r) => ({
12
- * User: {
13
- * workspace: r.one.Workspace({ from: r.User.workspaceId, to: r.Workspace.id }),
14
- * },
15
- * }));
16
- *
17
- * // Create schema with relations
18
- * const schema = dsl.schema(models, { relations });
19
- *
20
- * @example
21
- * // Grouped by domain
22
- * const schema = dsl.schema({
23
- * Workspace,
24
- * shipping: { Carrier, CarrierService },
25
- * printing: { Account: PrintingAccount, Job: PrintJob },
26
- * }, { relations });
27
- *
28
- * @example
29
- * // With standalone enums
30
- * const Status = dsl.enum({ name: "Status", options: ["active", "inactive"] });
31
- * const schema = dsl.schema({
32
- * Status, // Standalone enums can be included
33
- * User,
34
- * }, { relations });
35
- */
36
-
37
- import type { EnumDef, ModelDef, RelationsMap, SchemaDef, SchemaInput } from "./types.ts";
38
-
39
- // ─── Types ─────────────────────────────────
40
-
41
- /**
42
- * Options for schema creation
43
- */
44
- export type SchemaOptions = {
45
- /** Relations defined via dsl.defineRelations() */
46
- relations?: RelationsMap;
47
- };
48
-
49
- // ─── Type Guards ─────────────────────────────────
50
-
51
- /**
52
- * Checks if a value is a ModelDef
53
- */
54
- function isModelDef(value: unknown): value is ModelDef {
55
- return (
56
- typeof value === "object" &&
57
- value !== null &&
58
- "__type" in value &&
59
- value.__type === "model"
60
- );
61
- }
62
-
63
- /**
64
- * Checks if a value is an EnumDef (standalone enum)
65
- */
66
- function isEnumDef(value: unknown): value is EnumDef {
67
- return (
68
- typeof value === "object" &&
69
- value !== null &&
70
- "__type" in value &&
71
- value.__type === "enum"
72
- );
73
- }
74
-
75
- // ─── Extraction Helpers ─────────────────────────────────
76
-
77
- /**
78
- * Recursively extracts all models and standalone enums from schema input
79
- */
80
- function extractModelsAndEnums(params: {
81
- input: SchemaInput;
82
- models: Map<string, ModelDef>;
83
- standaloneEnums: Map<string, readonly string[]>;
84
- }): void {
85
- const { input, models, standaloneEnums } = params;
86
-
87
- for (const [key, value] of Object.entries(input)) {
88
- if (isModelDef(value)) {
89
- models.set(value.__name, value);
90
-
91
- // Also add any child models
92
- for (const [childKey, childModel] of value.__children) {
93
- models.set(childModel.__name, childModel);
94
- }
95
- } else if (isEnumDef(value)) {
96
- // Standalone enum
97
- standaloneEnums.set(value.__name, value.__options);
98
- } else if (typeof value === "object" && value !== null) {
99
- // Nested group
100
- extractModelsAndEnums({
101
- input: value as SchemaInput,
102
- models,
103
- standaloneEnums,
104
- });
105
- }
106
- }
107
- }
108
-
109
- /**
110
- * Extracts all unique enum values from models (for inline enums)
111
- */
112
- function extractInlineEnums(params: {
113
- models: Map<string, ModelDef>;
114
- standaloneEnums: Map<string, readonly string[]>;
115
- }): Map<string, readonly string[]> {
116
- const { models, standaloneEnums } = params;
117
- const enums = new Map<string, readonly string[]>(standaloneEnums);
118
-
119
- for (const [modelName, model] of models) {
120
- for (const [colName, column] of model.__columns) {
121
- if (column.__columnType === "enum" && column.__enumValues) {
122
- // Check if this is a standalone enum reference
123
- if (column.__enumName) {
124
- // Already has a name (standalone enum) - ensure it's in the map
125
- if (!enums.has(column.__enumName)) {
126
- enums.set(column.__enumName, column.__enumValues);
127
- }
128
- } else {
129
- // Inline enum - generate name: ModelNameColumnName
130
- const enumName = `${modelName}${colName.charAt(0).toUpperCase()}${colName.slice(1)}`;
131
- enums.set(enumName, column.__enumValues);
132
-
133
- // Store enum name reference on column for generator
134
- column.__enumName = enumName;
135
- }
136
- }
137
- }
138
- }
139
-
140
- return enums;
141
- }
142
-
143
- // ─── Public API ─────────────────────────────────
144
-
145
- /**
146
- * Creates a schema from models and relations
147
- *
148
- * @param input - Models to include (can be nested/grouped)
149
- * @param options - Options including relations from defineRelations()
150
- */
151
- export function schema(input: SchemaInput, options?: SchemaOptions): SchemaDef {
152
- const models = new Map<string, ModelDef>();
153
- const standaloneEnums = new Map<string, readonly string[]>();
154
-
155
- extractModelsAndEnums({ input, models, standaloneEnums });
156
-
157
- const enums = extractInlineEnums({ models, standaloneEnums });
158
-
159
- return {
160
- __type: "schema",
161
- __models: models,
162
- __enums: enums,
163
- __relations: options?.relations ?? {},
164
- };
165
- }
166
-
167
- /**
168
- * Creates a domain group for organizing models
169
- *
170
- * @example
171
- * const shippingDomain = dsl.domain({
172
- * name: "Shipping",
173
- * models: { Carrier, CarrierService },
174
- * });
175
- */
176
- export function domain(params: {
177
- name: string;
178
- models: Record<string, ModelDef>;
179
- }): { name: string; models: Record<string, ModelDef> } {
180
- return {
181
- name: params.name,
182
- models: params.models,
183
- };
184
- }
package/src/templates.ts DELETED
@@ -1,134 +0,0 @@
1
- /**
2
- * Built-in templates for common schema patterns
3
- *
4
- * Templates use getters so that each spread produces fresh column instances,
5
- * preventing shared mutable state across models.
6
- *
7
- * @example
8
- * import { dsl } from "@vibeorm/dsl";
9
- *
10
- * const User = dsl.create({
11
- * name: "User",
12
- * schema: {
13
- * ...dsl.templates.base,
14
- * ...dsl.templates.withMetadata,
15
- * email: dsl.string({ unique: true }),
16
- * },
17
- * });
18
- */
19
-
20
- import { boolean, datetime, id, json, string } from "./column.ts";
21
-
22
- // ─── Base Templates ─────────────────────
23
-
24
- /**
25
- * Standard base fields for all models
26
- * - id: ULID primary key
27
- * - createdAt: Creation timestamp
28
- * - updatedAt: Last update timestamp
29
- * - deletedAt: Soft delete timestamp (optional)
30
- *
31
- * Uses getters to return fresh instances on each spread.
32
- */
33
- export const base = {
34
- get id() { return id({ type: "ulid" }); },
35
- get createdAt() { return datetime({ default: "now", tz: true, index: true }); },
36
- get updatedAt() { return datetime({ default: "now", updatedAt: true, tz: true, index: true }); },
37
- get deletedAt() { return datetime({ optional: true, index: true }); },
38
- };
39
-
40
- /**
41
- * Base fields with workspace association
42
- */
43
- export const baseWithWorkspace = {
44
- get id() { return id({ type: "ulid" }); },
45
- get createdAt() { return datetime({ default: "now", tz: true, index: true }); },
46
- get updatedAt() { return datetime({ default: "now", updatedAt: true, tz: true, index: true }); },
47
- get deletedAt() { return datetime({ optional: true, index: true }); },
48
- get workspaceId() { return string({ index: true }); },
49
- };
50
-
51
- // ─── Mixin Templates ─────────────────────
52
-
53
- /**
54
- * Metadata fields for storing arbitrary JSON data
55
- */
56
- export const withMetadata = {
57
- get metadata() { return json({ optional: true }); },
58
- get settings() { return json({ optional: true }); },
59
- };
60
-
61
- /**
62
- * Active/inactive status field
63
- */
64
- export const withActive = {
65
- get isActive() { return boolean({ default: true }); },
66
- };
67
-
68
- /**
69
- * Audit fields for tracking who created/updated
70
- */
71
- export const withAudit = {
72
- get createdById() { return string({ optional: true, index: true }); },
73
- get updatedById() { return string({ optional: true, index: true }); },
74
- };
75
-
76
- /**
77
- * External ID field for integrations
78
- */
79
- export const withExternalId = {
80
- get externalId() { return string({ index: true }); },
81
- };
82
-
83
- /**
84
- * Name and description fields
85
- */
86
- export const withNameDescription = {
87
- get name() { return string(); },
88
- get description() { return string({ optional: true }); },
89
- };
90
-
91
- // ─── Common Field Definitions ─────────────────────
92
-
93
- /**
94
- * Pre-defined field definitions for common use cases.
95
- * Uses getters for fresh instances.
96
- */
97
- export const fields = {
98
- /** Standard datetime with now default and timezone */
99
- get datetime() { return datetime({ default: "now", updatedAt: true, tz: true, index: true }); },
100
-
101
- /** ULID primary key */
102
- get id() { return id({ type: "ulid" }); },
103
-
104
- /** Standard title/name field */
105
- get title() { return string(); },
106
-
107
- /** Optional description field */
108
- get description() { return string({ optional: true }); },
109
-
110
- /** External ID with index */
111
- get externalId() { return string({ index: true }); },
112
-
113
- /** Optional metadata JSON */
114
- get metadata() { return json({ optional: true }); },
115
-
116
- /** Optional settings JSON */
117
- get settings() { return json({ optional: true }); },
118
-
119
- /** Active status with default true */
120
- get isActive() { return boolean({ default: true }); },
121
- };
122
-
123
- // ─── Template Namespace Export ─────────────────────
124
-
125
- export const templates = {
126
- base,
127
- baseWithWorkspace,
128
- withMetadata,
129
- withActive,
130
- withAudit,
131
- withExternalId,
132
- withNameDescription,
133
- fields,
134
- };