@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.
- package/README.md +62 -471
- package/dist/column.d.ts +150 -0
- package/dist/column.d.ts.map +1 -0
- package/dist/enum.d.ts +27 -0
- package/dist/enum.d.ts.map +1 -0
- package/dist/generator/index.d.ts +5 -0
- package/dist/generator/index.d.ts.map +1 -0
- package/dist/generator/prisma.d.ts +19 -0
- package/dist/generator/prisma.d.ts.map +1 -0
- package/dist/index.d.ts +116 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1546 -0
- package/dist/index.js.map +22 -0
- package/dist/model.d.ts +44 -0
- package/dist/model.d.ts.map +1 -0
- package/dist/naming.d.ts +23 -0
- package/dist/naming.d.ts.map +1 -0
- package/dist/relation-plan.d.ts +101 -0
- package/dist/relation-plan.d.ts.map +1 -0
- package/dist/relation.d.ts +23 -0
- package/dist/relation.d.ts.map +1 -0
- package/dist/relations.d.ts +29 -0
- package/dist/relations.d.ts.map +1 -0
- package/dist/schema.d.ts +38 -0
- package/dist/schema.d.ts.map +1 -0
- package/dist/templates.d.ts +79 -0
- package/dist/templates.d.ts.map +1 -0
- package/dist/to-ir.d.ts +41 -0
- package/dist/to-ir.d.ts.map +1 -0
- package/dist/types.d.ts +286 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/validate.d.ts +22 -0
- package/dist/validate.d.ts.map +1 -0
- package/package.json +29 -26
- package/src/column.ts +0 -389
- package/src/enum.ts +0 -71
- package/src/generator/index.ts +0 -5
- package/src/generator/prisma.ts +0 -728
- package/src/index.ts +0 -247
- package/src/model.ts +0 -330
- package/src/relation.ts +0 -75
- package/src/relations.ts +0 -293
- package/src/schema.ts +0 -184
- package/src/templates.ts +0 -134
- package/src/types.ts +0 -381
- package/src/validate.ts +0 -141
package/src/index.ts
DELETED
|
@@ -1,247 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @vibeorm/dsl - TypeScript DSL for database schema management
|
|
3
|
-
*
|
|
4
|
-
* Relations are defined separately via dsl.defineRelations()
|
|
5
|
-
*
|
|
6
|
-
* @example
|
|
7
|
-
* import { dsl } from "@vibeorm/dsl";
|
|
8
|
-
*
|
|
9
|
-
* // Models - just columns (FK columns declared explicitly)
|
|
10
|
-
* const Workspace = dsl.create({
|
|
11
|
-
* name: "Workspace",
|
|
12
|
-
* schema: {
|
|
13
|
-
* ...dsl.templates.base,
|
|
14
|
-
* name: dsl.string(),
|
|
15
|
-
* },
|
|
16
|
-
* });
|
|
17
|
-
*
|
|
18
|
-
* const User = dsl.create({
|
|
19
|
-
* name: "User",
|
|
20
|
-
* schema: {
|
|
21
|
-
* ...dsl.templates.base,
|
|
22
|
-
* email: dsl.string({ unique: true }),
|
|
23
|
-
* workspaceId: dsl.string({ index: true }),
|
|
24
|
-
* },
|
|
25
|
-
* indexes: (i) => [
|
|
26
|
-
* i.unique("workspaceId", "email"),
|
|
27
|
-
* ],
|
|
28
|
-
* });
|
|
29
|
-
*
|
|
30
|
-
* // Relations - centralized and type-safe
|
|
31
|
-
* const relations = dsl.defineRelations({ Workspace, User }, (r) => ({
|
|
32
|
-
* Workspace: {
|
|
33
|
-
* users: r.many.User({
|
|
34
|
-
* from: r.Workspace.id,
|
|
35
|
-
* to: r.User.workspaceId,
|
|
36
|
-
* }),
|
|
37
|
-
* },
|
|
38
|
-
* User: {
|
|
39
|
-
* workspace: r.one.Workspace({
|
|
40
|
-
* from: r.User.workspaceId,
|
|
41
|
-
* to: r.Workspace.id,
|
|
42
|
-
* }),
|
|
43
|
-
* },
|
|
44
|
-
* }));
|
|
45
|
-
*
|
|
46
|
-
* // Schema with relations
|
|
47
|
-
* const schema = dsl.schema({ User, Workspace }, { relations });
|
|
48
|
-
* const prisma = dsl.generate.prisma(schema);
|
|
49
|
-
*/
|
|
50
|
-
|
|
51
|
-
// ─── Column Imports ─────────────────────────────────
|
|
52
|
-
|
|
53
|
-
import {
|
|
54
|
-
bigint,
|
|
55
|
-
boolean,
|
|
56
|
-
bytes,
|
|
57
|
-
ChainableColumn,
|
|
58
|
-
datetime,
|
|
59
|
-
decimal,
|
|
60
|
-
enumCol,
|
|
61
|
-
float,
|
|
62
|
-
id,
|
|
63
|
-
int,
|
|
64
|
-
json,
|
|
65
|
-
string,
|
|
66
|
-
unsupported,
|
|
67
|
-
} from "./column.ts";
|
|
68
|
-
|
|
69
|
-
// ─── Enum Imports ─────────────────────────────────
|
|
70
|
-
|
|
71
|
-
import { createEnum } from "./enum.ts";
|
|
72
|
-
import type { StandaloneEnumConfig } from "./enum.ts";
|
|
73
|
-
|
|
74
|
-
// ─── Model Imports ─────────────────────────────────
|
|
75
|
-
|
|
76
|
-
import { create } from "./model.ts";
|
|
77
|
-
|
|
78
|
-
// ─── Relations Imports ─────────────────────────────────
|
|
79
|
-
|
|
80
|
-
import { defineRelations, defineRelationsPart, mergeRelations } from "./relations.ts";
|
|
81
|
-
|
|
82
|
-
// ─── Schema Imports ─────────────────────────────────
|
|
83
|
-
|
|
84
|
-
import { domain, schema } from "./schema.ts";
|
|
85
|
-
|
|
86
|
-
// ─── Generator Imports ─────────────────────────────────
|
|
87
|
-
|
|
88
|
-
import { generatePrisma } from "./generator/prisma.ts";
|
|
89
|
-
|
|
90
|
-
// ─── Validation Imports ─────────────────────────────────
|
|
91
|
-
|
|
92
|
-
import { validateSchema } from "./validate.ts";
|
|
93
|
-
|
|
94
|
-
// ─── Template Imports ─────────────────────────────────
|
|
95
|
-
|
|
96
|
-
import {
|
|
97
|
-
base,
|
|
98
|
-
baseWithWorkspace,
|
|
99
|
-
fields,
|
|
100
|
-
templates,
|
|
101
|
-
withActive,
|
|
102
|
-
withAudit,
|
|
103
|
-
withExternalId,
|
|
104
|
-
withMetadata,
|
|
105
|
-
withNameDescription,
|
|
106
|
-
} from "./templates.ts";
|
|
107
|
-
|
|
108
|
-
// ─── Type Imports ─────────────────────────────────
|
|
109
|
-
|
|
110
|
-
import type {
|
|
111
|
-
ColumnDef,
|
|
112
|
-
EnumDef,
|
|
113
|
-
InlineEnumConfig,
|
|
114
|
-
SchemaDef,
|
|
115
|
-
} from "./types.ts";
|
|
116
|
-
|
|
117
|
-
// ─── Type Exports ─────────────────────────────────
|
|
118
|
-
|
|
119
|
-
export type {
|
|
120
|
-
// Cascade action types
|
|
121
|
-
CascadeAction,
|
|
122
|
-
// Children types
|
|
123
|
-
ChildrenBuilder,
|
|
124
|
-
ChildrenDef,
|
|
125
|
-
ChildrenRelationConfig,
|
|
126
|
-
// Column types
|
|
127
|
-
ColumnDef,
|
|
128
|
-
ColumnRef,
|
|
129
|
-
// Enum types
|
|
130
|
-
EnumDef,
|
|
131
|
-
EnumFieldConfig,
|
|
132
|
-
// Generator types
|
|
133
|
-
GeneratorConfig,
|
|
134
|
-
GeneratorOutput,
|
|
135
|
-
// Index types
|
|
136
|
-
IndexBuilder,
|
|
137
|
-
IndexCallback,
|
|
138
|
-
IndexDef,
|
|
139
|
-
IndexOptions,
|
|
140
|
-
IndexType,
|
|
141
|
-
InlineEnumConfig,
|
|
142
|
-
// Model types
|
|
143
|
-
ModelDef,
|
|
144
|
-
// Prisma types
|
|
145
|
-
PrismaGeneratorConfig,
|
|
146
|
-
// Relations types
|
|
147
|
-
ManyRelationDefConfig,
|
|
148
|
-
OneRelationDefConfig,
|
|
149
|
-
RelationsBuilder,
|
|
150
|
-
RelationsMap,
|
|
151
|
-
RelationsOutput,
|
|
152
|
-
StoredRelationDef,
|
|
153
|
-
ThroughRef,
|
|
154
|
-
// Schema types
|
|
155
|
-
SchemaDef,
|
|
156
|
-
SchemaInput,
|
|
157
|
-
} from "./types.ts";
|
|
158
|
-
|
|
159
|
-
export type { ValidationError } from "./validate.ts";
|
|
160
|
-
|
|
161
|
-
// ─── Enum Factory ─────────────────────────────────
|
|
162
|
-
|
|
163
|
-
/**
|
|
164
|
-
* Creates an enum - either standalone (reusable) or inline (one-off)
|
|
165
|
-
*
|
|
166
|
-
* @example
|
|
167
|
-
* // Standalone enum (reusable across models)
|
|
168
|
-
* const Status = dsl.enum({ name: "Status", options: ["active", "inactive"] });
|
|
169
|
-
* // Use in model: status: Status.field({ default: "active" })
|
|
170
|
-
*
|
|
171
|
-
* // Inline enum (one-off use)
|
|
172
|
-
* priority: dsl.enum({ options: ["low", "medium", "high"], default: "medium" })
|
|
173
|
-
*/
|
|
174
|
-
function enumFactory<const T extends readonly string[]>(
|
|
175
|
-
config: StandaloneEnumConfig<T>
|
|
176
|
-
): EnumDef<T>;
|
|
177
|
-
function enumFactory<const T extends readonly string[]>(config: InlineEnumConfig<T>): ChainableColumn<"enum">;
|
|
178
|
-
function enumFactory<const T extends readonly string[]>(
|
|
179
|
-
config: StandaloneEnumConfig<T> | InlineEnumConfig<T>
|
|
180
|
-
): EnumDef<T> | ChainableColumn<"enum"> {
|
|
181
|
-
// Standalone enum has 'name' property
|
|
182
|
-
if ("name" in config && typeof config.name === "string") {
|
|
183
|
-
return createEnum(config as StandaloneEnumConfig<T>);
|
|
184
|
-
}
|
|
185
|
-
// Inline enum
|
|
186
|
-
return enumCol(config as InlineEnumConfig<T>);
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
// ─── Main DSL Namespace ─────────────────────────────────
|
|
190
|
-
|
|
191
|
-
/**
|
|
192
|
-
* Main database schema DSL namespace
|
|
193
|
-
*/
|
|
194
|
-
export const dsl = {
|
|
195
|
-
// Column types
|
|
196
|
-
string,
|
|
197
|
-
int,
|
|
198
|
-
bigint,
|
|
199
|
-
float,
|
|
200
|
-
decimal,
|
|
201
|
-
boolean,
|
|
202
|
-
datetime,
|
|
203
|
-
json,
|
|
204
|
-
id,
|
|
205
|
-
bytes,
|
|
206
|
-
unsupported,
|
|
207
|
-
enum: enumFactory,
|
|
208
|
-
|
|
209
|
-
// Model creation
|
|
210
|
-
create,
|
|
211
|
-
|
|
212
|
-
// Relations
|
|
213
|
-
defineRelations,
|
|
214
|
-
defineRelationsPart,
|
|
215
|
-
mergeRelations,
|
|
216
|
-
|
|
217
|
-
// Schema composition
|
|
218
|
-
schema,
|
|
219
|
-
domain,
|
|
220
|
-
|
|
221
|
-
// Validation
|
|
222
|
-
validateSchema,
|
|
223
|
-
|
|
224
|
-
// Generators
|
|
225
|
-
generate: Object.assign(
|
|
226
|
-
function generate(schemaDef: SchemaDef): { prisma?: string } {
|
|
227
|
-
return {
|
|
228
|
-
prisma: generatePrisma(schemaDef),
|
|
229
|
-
};
|
|
230
|
-
},
|
|
231
|
-
{
|
|
232
|
-
prisma: generatePrisma,
|
|
233
|
-
}
|
|
234
|
-
),
|
|
235
|
-
|
|
236
|
-
// Templates
|
|
237
|
-
templates: {
|
|
238
|
-
base,
|
|
239
|
-
baseWithWorkspace,
|
|
240
|
-
withMetadata,
|
|
241
|
-
withActive,
|
|
242
|
-
withAudit,
|
|
243
|
-
withExternalId,
|
|
244
|
-
withNameDescription,
|
|
245
|
-
fields,
|
|
246
|
-
},
|
|
247
|
-
};
|
package/src/model.ts
DELETED
|
@@ -1,330 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Model builder using dsl.create({ name, schema, indexes })
|
|
3
|
-
*
|
|
4
|
-
* v3: Models define ONLY columns (no inline relations)
|
|
5
|
-
* Relations are defined separately via dsl.defineRelations()
|
|
6
|
-
* Children (inline nested models) are still supported via schema callback
|
|
7
|
-
*
|
|
8
|
-
* @example
|
|
9
|
-
* // Simple model - just columns
|
|
10
|
-
* const Workspace = dsl.create({
|
|
11
|
-
* name: "Workspace",
|
|
12
|
-
* schema: {
|
|
13
|
-
* ...dsl.base,
|
|
14
|
-
* name: dsl.string(),
|
|
15
|
-
* slug: dsl.string({ unique: true }),
|
|
16
|
-
* },
|
|
17
|
-
* });
|
|
18
|
-
*
|
|
19
|
-
* // Model with FK column (explicit) and type-safe indexes
|
|
20
|
-
* const Printer = dsl.create({
|
|
21
|
-
* name: "Printer",
|
|
22
|
-
* schema: {
|
|
23
|
-
* ...dsl.base,
|
|
24
|
-
* name: dsl.string(),
|
|
25
|
-
* workspaceId: dsl.string({ index: true }), // FK declared explicitly
|
|
26
|
-
* },
|
|
27
|
-
* // TypeScript suggests valid column names
|
|
28
|
-
* indexes: (i) => [
|
|
29
|
-
* i.unique("workspaceId", "name"),
|
|
30
|
-
* ],
|
|
31
|
-
* });
|
|
32
|
-
*
|
|
33
|
-
* // Model with children (inline nested models)
|
|
34
|
-
* const Order = dsl.create({
|
|
35
|
-
* name: "Order",
|
|
36
|
-
* schema: (s) => ({
|
|
37
|
-
* ...dsl.base,
|
|
38
|
-
* total: dsl.int(),
|
|
39
|
-
* Items: s.children({
|
|
40
|
-
* name: "OrderItem",
|
|
41
|
-
* schema: { id: dsl.id(), quantity: dsl.int() },
|
|
42
|
-
* }),
|
|
43
|
-
* }),
|
|
44
|
-
* });
|
|
45
|
-
*/
|
|
46
|
-
|
|
47
|
-
import type {
|
|
48
|
-
ChildrenBuilder,
|
|
49
|
-
ChildrenDef,
|
|
50
|
-
ChildrenSchemaCallback,
|
|
51
|
-
ColumnDef,
|
|
52
|
-
IndexBuilder,
|
|
53
|
-
IndexCallback,
|
|
54
|
-
IndexDef,
|
|
55
|
-
IndexOptions,
|
|
56
|
-
IndexType,
|
|
57
|
-
ModelDef,
|
|
58
|
-
SchemaDefinition,
|
|
59
|
-
} from "./types.ts";
|
|
60
|
-
import { cloneColumnDef } from "./column.ts";
|
|
61
|
-
import { createChildrenBuilder } from "./relation.ts";
|
|
62
|
-
|
|
63
|
-
// ─── Type Guards ─────────────────────────
|
|
64
|
-
|
|
65
|
-
/**
|
|
66
|
-
* Checks if a value is a ColumnDef
|
|
67
|
-
*/
|
|
68
|
-
function isColumnDef(value: unknown): value is ColumnDef {
|
|
69
|
-
return (
|
|
70
|
-
typeof value === "object" &&
|
|
71
|
-
value !== null &&
|
|
72
|
-
"__type" in value &&
|
|
73
|
-
value.__type === "column"
|
|
74
|
-
);
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
/**
|
|
78
|
-
* Checks if a value is a ChildrenDef
|
|
79
|
-
*/
|
|
80
|
-
function isChildrenDef(value: unknown): value is ChildrenDef {
|
|
81
|
-
return (
|
|
82
|
-
typeof value === "object" &&
|
|
83
|
-
value !== null &&
|
|
84
|
-
"__type" in value &&
|
|
85
|
-
value.__type === "children"
|
|
86
|
-
);
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
// ─── Helpers ─────────────────────────────
|
|
90
|
-
|
|
91
|
-
/**
|
|
92
|
-
* Converts a model name to a FK column name
|
|
93
|
-
* e.g., "Order" -> "orderId"
|
|
94
|
-
*/
|
|
95
|
-
function toFkColumnName(modelName: string): string {
|
|
96
|
-
return modelName.charAt(0).toLowerCase() + modelName.slice(1) + "Id";
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
// ─── Index Builder ───────────────────────
|
|
100
|
-
|
|
101
|
-
/**
|
|
102
|
-
* Creates an IndexBuilder with type-safe column references
|
|
103
|
-
*
|
|
104
|
-
* @example
|
|
105
|
-
* indexes: (i) => [
|
|
106
|
-
* i.on("workspaceId", "name"), // Composite index
|
|
107
|
-
* i.unique("workspaceId", "email"), // Unique constraint
|
|
108
|
-
* i.on("status"), // Single column index
|
|
109
|
-
* i.gin("tags"), // GIN index for JSON/arrays
|
|
110
|
-
* i.on(["content"], { type: "Gist" }), // Index with type option
|
|
111
|
-
* ]
|
|
112
|
-
*/
|
|
113
|
-
export function createIndexBuilder<T extends string>(): IndexBuilder<T> {
|
|
114
|
-
// Helper to create index with optional type
|
|
115
|
-
const createIndex = (columns: string[], type?: IndexType): IndexDef => ({
|
|
116
|
-
__type: "index",
|
|
117
|
-
__columns: columns,
|
|
118
|
-
__indexType: type,
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
return {
|
|
122
|
-
/**
|
|
123
|
-
* Creates a composite index on the specified columns
|
|
124
|
-
* Optionally accepts an options object with index type
|
|
125
|
-
*/
|
|
126
|
-
on<C extends T>(...args: C[] | [C[], IndexOptions]): IndexDef {
|
|
127
|
-
// Check if last arg is options object
|
|
128
|
-
if (args.length === 2 && Array.isArray(args[0]) && typeof args[1] === "object" && !Array.isArray(args[1])) {
|
|
129
|
-
const [columns, options] = args as [C[], IndexOptions];
|
|
130
|
-
return createIndex(columns, options.type);
|
|
131
|
-
}
|
|
132
|
-
// Normal variadic columns
|
|
133
|
-
return createIndex(args as string[]);
|
|
134
|
-
},
|
|
135
|
-
|
|
136
|
-
/**
|
|
137
|
-
* Creates a unique constraint on the specified columns
|
|
138
|
-
*/
|
|
139
|
-
unique<C extends T>(...columns: C[]): IndexDef {
|
|
140
|
-
return {
|
|
141
|
-
__type: "unique",
|
|
142
|
-
__columns: columns,
|
|
143
|
-
};
|
|
144
|
-
},
|
|
145
|
-
|
|
146
|
-
/**
|
|
147
|
-
* Creates a GIN index (PostgreSQL) - good for JSON, arrays, full-text search
|
|
148
|
-
*/
|
|
149
|
-
gin<C extends T>(...columns: C[]): IndexDef {
|
|
150
|
-
return createIndex(columns, "Gin");
|
|
151
|
-
},
|
|
152
|
-
|
|
153
|
-
/**
|
|
154
|
-
* Creates a GiST index (PostgreSQL) - good for geometric, range, and full-text data
|
|
155
|
-
*/
|
|
156
|
-
gist<C extends T>(...columns: C[]): IndexDef {
|
|
157
|
-
return createIndex(columns, "Gist");
|
|
158
|
-
},
|
|
159
|
-
|
|
160
|
-
/**
|
|
161
|
-
* Creates a Hash index (PostgreSQL) - good for equality comparisons
|
|
162
|
-
*/
|
|
163
|
-
hash<C extends T>(...columns: C[]): IndexDef {
|
|
164
|
-
return createIndex(columns, "Hash");
|
|
165
|
-
},
|
|
166
|
-
|
|
167
|
-
/**
|
|
168
|
-
* Creates a BRIN index (PostgreSQL) - good for large tables with naturally ordered data
|
|
169
|
-
*/
|
|
170
|
-
brin<C extends T>(...columns: C[]): IndexDef {
|
|
171
|
-
return createIndex(columns, "Brin");
|
|
172
|
-
},
|
|
173
|
-
};
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
// ─── Schema Processing ───────────────────
|
|
177
|
-
|
|
178
|
-
/**
|
|
179
|
-
* Processes a schema definition and extracts columns and children
|
|
180
|
-
* Note: Relations are no longer processed here - use defineRelations() instead
|
|
181
|
-
*/
|
|
182
|
-
function processSchema(
|
|
183
|
-
modelName: string,
|
|
184
|
-
schema: SchemaDefinition | ChildrenSchemaCallback
|
|
185
|
-
): {
|
|
186
|
-
columns: Map<string, ColumnDef>;
|
|
187
|
-
children: Map<string, ModelDef>;
|
|
188
|
-
resolvedSchema: SchemaDefinition;
|
|
189
|
-
} {
|
|
190
|
-
// Resolve schema if it's a callback (for children support)
|
|
191
|
-
const resolvedSchema =
|
|
192
|
-
typeof schema === "function" ? schema(createChildrenBuilder()) : schema;
|
|
193
|
-
|
|
194
|
-
const columns = new Map<string, ColumnDef>();
|
|
195
|
-
const children = new Map<string, ModelDef>();
|
|
196
|
-
|
|
197
|
-
for (const [key, value] of Object.entries(resolvedSchema)) {
|
|
198
|
-
if (isColumnDef(value)) {
|
|
199
|
-
// Clone to avoid mutating shared instances (e.g., from templates)
|
|
200
|
-
const cloned = cloneColumnDef(value);
|
|
201
|
-
cloned.__name = key;
|
|
202
|
-
columns.set(key, cloned);
|
|
203
|
-
} else if (isChildrenDef(value)) {
|
|
204
|
-
// Create child model from children definition
|
|
205
|
-
const childModel = create({
|
|
206
|
-
name: value.__name,
|
|
207
|
-
schema: value.__schema,
|
|
208
|
-
});
|
|
209
|
-
|
|
210
|
-
// Add parent FK to child model
|
|
211
|
-
const parentFkName = toFkColumnName(modelName);
|
|
212
|
-
childModel.__columns.set(parentFkName, {
|
|
213
|
-
__type: "column",
|
|
214
|
-
__columnType: "string",
|
|
215
|
-
__name: parentFkName,
|
|
216
|
-
__config: {
|
|
217
|
-
index: true,
|
|
218
|
-
},
|
|
219
|
-
});
|
|
220
|
-
|
|
221
|
-
children.set(key, childModel);
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
return { columns, children, resolvedSchema };
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
// ─── Type Helpers ────────────────────────
|
|
229
|
-
|
|
230
|
-
/**
|
|
231
|
-
* Extract keys from schema where value is a ColumnDef (excludes ChildrenDef)
|
|
232
|
-
*/
|
|
233
|
-
export type ColumnKeys<T> = {
|
|
234
|
-
[K in keyof T]: T[K] extends ColumnDef ? K : never;
|
|
235
|
-
}[keyof T];
|
|
236
|
-
|
|
237
|
-
/**
|
|
238
|
-
* All valid column names for indexes
|
|
239
|
-
*/
|
|
240
|
-
export type SchemaColumnNames<T> = Extract<ColumnKeys<T>, string>;
|
|
241
|
-
|
|
242
|
-
// ─── Model Config ────────────────────────
|
|
243
|
-
|
|
244
|
-
/**
|
|
245
|
-
* Config for dsl.create() with type-safe indexes
|
|
246
|
-
*/
|
|
247
|
-
export type CreateModelConfig<
|
|
248
|
-
TSchema extends SchemaDefinition,
|
|
249
|
-
TColumns extends string = SchemaColumnNames<TSchema>,
|
|
250
|
-
> = {
|
|
251
|
-
name: string;
|
|
252
|
-
schema: TSchema | ((s: ChildrenBuilder) => TSchema);
|
|
253
|
-
indexes?: IndexCallback<TColumns>;
|
|
254
|
-
/** Map to a different database table name - generates @@map("table_name") */
|
|
255
|
-
map?: string;
|
|
256
|
-
/** Compound primary key columns - generates @@id([col1, col2]) */
|
|
257
|
-
primaryKey?: TColumns[];
|
|
258
|
-
};
|
|
259
|
-
|
|
260
|
-
// ─── Model Factory ───────────────────────
|
|
261
|
-
|
|
262
|
-
/**
|
|
263
|
-
* Creates a model definition with type-safe indexes
|
|
264
|
-
*
|
|
265
|
-
* v3: Models only contain columns and children.
|
|
266
|
-
* Relations are defined separately via dsl.defineRelations()
|
|
267
|
-
*
|
|
268
|
-
* @example
|
|
269
|
-
* // Simple model
|
|
270
|
-
* const User = dsl.create({
|
|
271
|
-
* name: "User",
|
|
272
|
-
* schema: {
|
|
273
|
-
* ...dsl.base,
|
|
274
|
-
* email: dsl.string({ unique: true }),
|
|
275
|
-
* workspaceId: dsl.string({ index: true }), // FK declared explicitly
|
|
276
|
-
* },
|
|
277
|
-
* });
|
|
278
|
-
*
|
|
279
|
-
* // Model with children
|
|
280
|
-
* const Order = dsl.create({
|
|
281
|
-
* name: "Order",
|
|
282
|
-
* schema: (s) => ({
|
|
283
|
-
* ...dsl.base,
|
|
284
|
-
* Items: s.children({
|
|
285
|
-
* name: "OrderItem",
|
|
286
|
-
* schema: { id: dsl.id(), qty: dsl.int() },
|
|
287
|
-
* }),
|
|
288
|
-
* }),
|
|
289
|
-
* });
|
|
290
|
-
*/
|
|
291
|
-
export function create<
|
|
292
|
-
const TSchema extends SchemaDefinition,
|
|
293
|
-
TColumns extends string = SchemaColumnNames<TSchema>,
|
|
294
|
-
>(config: CreateModelConfig<TSchema, TColumns>): ModelDef<TSchema> {
|
|
295
|
-
const { columns, children } = processSchema(config.name, config.schema);
|
|
296
|
-
|
|
297
|
-
const model: ModelDef<TSchema> = {
|
|
298
|
-
__type: "model",
|
|
299
|
-
__name: config.name,
|
|
300
|
-
__schema: config.schema,
|
|
301
|
-
__indexes: config.indexes as IndexCallback | undefined,
|
|
302
|
-
__columns: columns,
|
|
303
|
-
__children: children,
|
|
304
|
-
__map: config.map,
|
|
305
|
-
__primaryKey: config.primaryKey as string[] | undefined,
|
|
306
|
-
};
|
|
307
|
-
|
|
308
|
-
return model;
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
// ─── Model Utilities ─────────────────────
|
|
312
|
-
|
|
313
|
-
/**
|
|
314
|
-
* Gets all column names from a model (for index builder)
|
|
315
|
-
*/
|
|
316
|
-
export function getColumnNames(model: ModelDef): string[] {
|
|
317
|
-
return Array.from(model.__columns.keys());
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
/**
|
|
321
|
-
* Evaluates index definitions for a model
|
|
322
|
-
*/
|
|
323
|
-
export function evaluateIndexes(model: ModelDef): IndexDef[] {
|
|
324
|
-
if (!model.__indexes) {
|
|
325
|
-
return [];
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
const indexBuilder = createIndexBuilder<string>();
|
|
329
|
-
return model.__indexes(indexBuilder);
|
|
330
|
-
}
|
package/src/relation.ts
DELETED
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Children builder for inline nested models
|
|
3
|
-
*
|
|
4
|
-
* v3: Relations are now defined separately via dsl.defineRelations()
|
|
5
|
-
* This module only provides the children() builder for inline nested models
|
|
6
|
-
*
|
|
7
|
-
* @example
|
|
8
|
-
* const Order = dsl.create({
|
|
9
|
-
* name: "Order",
|
|
10
|
-
* schema: (s) => ({
|
|
11
|
-
* ...dsl.base,
|
|
12
|
-
* total: dsl.int(),
|
|
13
|
-
* Items: s.children({
|
|
14
|
-
* name: "OrderItem",
|
|
15
|
-
* schema: {
|
|
16
|
-
* ...dsl.base,
|
|
17
|
-
* productId: dsl.string(),
|
|
18
|
-
* quantity: dsl.int(),
|
|
19
|
-
* },
|
|
20
|
-
* }),
|
|
21
|
-
* }),
|
|
22
|
-
* });
|
|
23
|
-
*/
|
|
24
|
-
|
|
25
|
-
import type {
|
|
26
|
-
ChildrenBuilder,
|
|
27
|
-
ChildrenDef,
|
|
28
|
-
ChildrenRelationConfig,
|
|
29
|
-
} from "./types.ts";
|
|
30
|
-
|
|
31
|
-
// ─── Children Builder ─────────────────────
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* Creates an inline child model
|
|
35
|
-
* Generates a separate model with automatic FK back to parent
|
|
36
|
-
*
|
|
37
|
-
* @example
|
|
38
|
-
* Files: s.children({
|
|
39
|
-
* name: "PrintJobFile",
|
|
40
|
-
* schema: {
|
|
41
|
-
* ...dsl.base,
|
|
42
|
-
* fileUrl: dsl.string(),
|
|
43
|
-
* fileName: dsl.string(),
|
|
44
|
-
* },
|
|
45
|
-
* })
|
|
46
|
-
*
|
|
47
|
-
* // Or with nested children:
|
|
48
|
-
* Activities: s.children({
|
|
49
|
-
* name: "PrintJobActivity",
|
|
50
|
-
* schema: (s2) => ({
|
|
51
|
-
* ...dsl.base,
|
|
52
|
-
* type: dsl.string(),
|
|
53
|
-
* Logs: s2.children({
|
|
54
|
-
* name: "ActivityLog",
|
|
55
|
-
* schema: { id: dsl.id(), message: dsl.string() },
|
|
56
|
-
* }),
|
|
57
|
-
* }),
|
|
58
|
-
* })
|
|
59
|
-
*/
|
|
60
|
-
export function children(config: ChildrenRelationConfig): ChildrenDef {
|
|
61
|
-
return {
|
|
62
|
-
__type: "children",
|
|
63
|
-
__name: config.name,
|
|
64
|
-
__schema: config.schema,
|
|
65
|
-
};
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
/**
|
|
69
|
-
* Creates a ChildrenBuilder for use in schema callbacks
|
|
70
|
-
*/
|
|
71
|
-
export function createChildrenBuilder(): ChildrenBuilder {
|
|
72
|
-
return {
|
|
73
|
-
children,
|
|
74
|
-
};
|
|
75
|
-
}
|