@casekit/orm2-config 1.0.0 → 1.0.2
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/{src/index.ts → build/index.d.ts} +1 -7
- package/build/index.js +4 -0
- package/build/normalize/defaultZodSchema.d.ts +8 -0
- package/{src/normalize/defaultZodSchema.ts → build/normalize/defaultZodSchema.js} +28 -20
- package/build/normalize/defaultZodSchema.test.d.ts +1 -0
- package/{src/normalize/defaultZodSchema.test.ts → build/normalize/defaultZodSchema.test.js} +5 -13
- package/build/normalize/getColumns.d.ts +2 -0
- package/{src/normalize/getColumns.ts → build/normalize/getColumns.js} +1 -6
- package/build/normalize/getColumns.test.d.ts +1 -0
- package/{src/normalize/getColumns.test.ts → build/normalize/getColumns.test.js} +4 -17
- package/build/normalize/normalizeConfig.d.ts +3 -0
- package/{src/normalize/normalizeConfig.ts → build/normalize/normalizeConfig.js} +1 -6
- package/build/normalize/normalizeConfig.test.d.ts +1 -0
- package/{src/normalize/normalizeConfig.test.ts → build/normalize/normalizeConfig.test.js} +22 -68
- package/build/normalize/normalizeField.d.ts +3 -0
- package/build/normalize/normalizeField.js +11 -0
- package/build/normalize/normalizeField.test.d.ts +1 -0
- package/{src/normalize/normalizeField.test.ts → build/normalize/normalizeField.test.js} +3 -8
- package/build/normalize/normalizeForeignKeys.d.ts +5 -0
- package/build/normalize/normalizeForeignKeys.js +50 -0
- package/build/normalize/normalizeForeignKeys.test.d.ts +1 -0
- package/{src/normalize/normalizeForeignKeys.test.ts → build/normalize/normalizeForeignKeys.test.js} +7 -26
- package/build/normalize/normalizeModel.d.ts +3 -0
- package/{src/normalize/normalizeModel.ts → build/normalize/normalizeModel.js} +1 -8
- package/build/normalize/normalizeModel.test.d.ts +1 -0
- package/{src/normalize/normalizeModel.test.ts → build/normalize/normalizeModel.test.js} +14 -38
- package/build/normalize/normalizePrimaryKey.d.ts +3 -0
- package/build/normalize/normalizePrimaryKey.js +18 -0
- package/build/normalize/normalizePrimaryKey.test.d.ts +1 -0
- package/{src/normalize/normalizePrimaryKey.test.ts → build/normalize/normalizePrimaryKey.test.js} +7 -31
- package/build/normalize/normalizeRelations.d.ts +3 -0
- package/{src/normalize/normalizeRelations.ts → build/normalize/normalizeRelations.js} +12 -34
- package/build/normalize/normalizeRelations.test.d.ts +1 -0
- package/{src/normalize/normalizeRelations.test.ts → build/normalize/normalizeRelations.test.js} +9 -38
- package/build/normalize/normalizeUniqueConstraints.d.ts +5 -0
- package/build/normalize/normalizeUniqueConstraints.js +29 -0
- package/build/normalize/normalizeUniqueConstraints.test.d.ts +1 -0
- package/{src/normalize/normalizeUniqueConstraints.test.ts → build/normalize/normalizeUniqueConstraints.test.js} +15 -46
- package/build/normalize/populateField.d.ts +3 -0
- package/{src/normalize/populateField.ts → build/normalize/populateField.js} +1 -9
- package/build/normalize/populateField.test.d.ts +1 -0
- package/build/normalize/populateField.test.js +198 -0
- package/build/normalize/populateModels.d.ts +3 -0
- package/{src/normalize/populateModels.ts → build/normalize/populateModels.js} +2 -11
- package/build/normalize/populateModels.test.d.ts +1 -0
- package/{src/normalize/populateModels.test.ts → build/normalize/populateModels.test.js} +11 -32
- package/{src/types/NormalizedConfig.ts → build/types/NormalizedConfig.d.ts} +1 -8
- package/build/types/NormalizedConfig.js +1 -0
- package/{src/types/NormalizedFieldDefinition.ts → build/types/NormalizedFieldDefinition.d.ts} +0 -1
- package/build/types/NormalizedFieldDefinition.js +1 -0
- package/build/types/NormalizedForeignKeyDefinition.js +1 -0
- package/{src/types/NormalizedModelDefinition.ts → build/types/NormalizedModelDefinition.d.ts} +0 -1
- package/build/types/NormalizedModelDefinition.js +1 -0
- package/build/types/NormalizedPrimaryKey.js +1 -0
- package/{src/types/NormalizedRelationDefinition.ts → build/types/NormalizedRelationDefinition.d.ts} +1 -7
- package/build/types/NormalizedRelationDefinition.js +1 -0
- package/{src/types/NormalizedUniqueConstraintDefinition.ts → build/types/NormalizedUniqueConstraintDefinition.d.ts} +0 -1
- package/build/types/NormalizedUniqueConstraintDefinition.js +1 -0
- package/{src/types/PopulatedFieldDefinition.ts → build/types/PopulatedFieldDefinition.d.ts} +0 -1
- package/build/types/PopulatedFieldDefinition.js +1 -0
- package/{src/types/PopulatedModelDefinition.ts → build/types/PopulatedModelDefinition.d.ts} +0 -2
- package/build/types/PopulatedModelDefinition.js +1 -0
- package/build/util.d.ts +6 -0
- package/build/util.js +21 -0
- package/package.json +10 -10
- package/src/normalize/normalizeField.ts +0 -16
- package/src/normalize/normalizeForeignKeys.ts +0 -89
- package/src/normalize/normalizePrimaryKey.ts +0 -30
- package/src/normalize/normalizeUniqueConstraints.ts +0 -58
- package/src/normalize/populateField.test.ts +0 -253
- package/src/util.ts +0 -38
- /package/{src/types/NormalizedForeignKeyDefinition.ts → build/types/NormalizedForeignKeyDefinition.d.ts} +0 -0
- /package/{src/types/NormalizedPrimaryKey.ts → build/types/NormalizedPrimaryKey.d.ts} +0 -0
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { UniqueConstraintDefinition } from "@casekit/orm2-schema";
|
|
2
|
+
import { NormalizedUniqueConstraintDefinition } from "#types/NormalizedUniqueConstraintDefinition.js";
|
|
3
|
+
import { PopulatedModelDefinition } from "#types/PopulatedModelDefinition.js";
|
|
4
|
+
export declare const normalizeUniqueConstraint: (model: PopulatedModelDefinition, constraint: UniqueConstraintDefinition) => NormalizedUniqueConstraintDefinition;
|
|
5
|
+
export declare const normalizeUniqueConstraints: (model: PopulatedModelDefinition) => NormalizedUniqueConstraintDefinition[];
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { getColumns } from "./getColumns.js";
|
|
2
|
+
export const normalizeUniqueConstraint = (model, constraint) => {
|
|
3
|
+
const columns = getColumns(model, constraint.fields);
|
|
4
|
+
return {
|
|
5
|
+
name: constraint.name ?? [model.table, ...columns, "ukey"].join("_"),
|
|
6
|
+
fields: constraint.fields,
|
|
7
|
+
columns,
|
|
8
|
+
where: constraint.where ?? null,
|
|
9
|
+
nullsNotDistinct: constraint.nullsNotDistinct ?? false,
|
|
10
|
+
};
|
|
11
|
+
};
|
|
12
|
+
export const normalizeUniqueConstraints = (model) => {
|
|
13
|
+
const columnLevelUniqueConstraints = Object.values(model.fields)
|
|
14
|
+
.filter((field) => field.unique)
|
|
15
|
+
.map((field) => normalizeUniqueConstraint(model, typeof field.unique === "boolean"
|
|
16
|
+
? {
|
|
17
|
+
fields: [field.name],
|
|
18
|
+
where: null,
|
|
19
|
+
nullsNotDistinct: false,
|
|
20
|
+
}
|
|
21
|
+
: { fields: [field.name], ...field.unique }));
|
|
22
|
+
const modelLevelUniqueConstraints = model.uniqueConstraints.map((constraint) => normalizeUniqueConstraint(model, constraint));
|
|
23
|
+
for (const constraint of columnLevelUniqueConstraints) {
|
|
24
|
+
if (modelLevelUniqueConstraints.some((other) => constraint.fields.every((field) => other.fields.includes(field)))) {
|
|
25
|
+
throw new Error(`Duplicate unique constraint defined in model "${model.name}"`);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return [...columnLevelUniqueConstraints, ...modelLevelUniqueConstraints];
|
|
29
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1,11 +1,8 @@
|
|
|
1
1
|
import { snakeCase } from "es-toolkit";
|
|
2
2
|
import { describe, expect, test } from "vitest";
|
|
3
|
-
|
|
4
3
|
import { sql } from "@casekit/sql";
|
|
5
|
-
|
|
6
4
|
import { normalizeUniqueConstraints } from "./normalizeUniqueConstraints.js";
|
|
7
5
|
import { populateModels } from "./populateModels.js";
|
|
8
|
-
|
|
9
6
|
describe("normalizeUniqueConstraints", () => {
|
|
10
7
|
test("handles both column-level and model-level unique constraints", () => {
|
|
11
8
|
const models = populateModels({
|
|
@@ -25,9 +22,7 @@ describe("normalizeUniqueConstraints", () => {
|
|
|
25
22
|
},
|
|
26
23
|
},
|
|
27
24
|
});
|
|
28
|
-
|
|
29
|
-
const result = normalizeUniqueConstraints(models["user"]!);
|
|
30
|
-
|
|
25
|
+
const result = normalizeUniqueConstraints(models["user"]);
|
|
31
26
|
expect(result).toEqual([
|
|
32
27
|
{
|
|
33
28
|
name: "user_email_ukey",
|
|
@@ -45,7 +40,6 @@ describe("normalizeUniqueConstraints", () => {
|
|
|
45
40
|
},
|
|
46
41
|
]);
|
|
47
42
|
});
|
|
48
|
-
|
|
49
43
|
test("handles complex column-level unique constraints", () => {
|
|
50
44
|
const models = populateModels({
|
|
51
45
|
naming: { column: snakeCase },
|
|
@@ -56,7 +50,7 @@ describe("normalizeUniqueConstraints", () => {
|
|
|
56
50
|
email: {
|
|
57
51
|
type: "text",
|
|
58
52
|
unique: {
|
|
59
|
-
where: sql`deleted_at IS NULL`,
|
|
53
|
+
where: sql `deleted_at IS NULL`,
|
|
60
54
|
nullsNotDistinct: true,
|
|
61
55
|
},
|
|
62
56
|
},
|
|
@@ -64,20 +58,17 @@ describe("normalizeUniqueConstraints", () => {
|
|
|
64
58
|
},
|
|
65
59
|
},
|
|
66
60
|
});
|
|
67
|
-
|
|
68
|
-
const result = normalizeUniqueConstraints(models["user"]!);
|
|
69
|
-
|
|
61
|
+
const result = normalizeUniqueConstraints(models["user"]);
|
|
70
62
|
expect(result).toEqual([
|
|
71
63
|
{
|
|
72
64
|
name: "user_email_ukey",
|
|
73
65
|
fields: ["email"],
|
|
74
66
|
columns: ["email"],
|
|
75
|
-
where: sql`deleted_at IS NULL`,
|
|
67
|
+
where: sql `deleted_at IS NULL`,
|
|
76
68
|
nullsNotDistinct: true,
|
|
77
69
|
},
|
|
78
70
|
]);
|
|
79
71
|
});
|
|
80
|
-
|
|
81
72
|
test("handles fields with unique: false", () => {
|
|
82
73
|
const models = populateModels({
|
|
83
74
|
models: {
|
|
@@ -89,12 +80,9 @@ describe("normalizeUniqueConstraints", () => {
|
|
|
89
80
|
},
|
|
90
81
|
},
|
|
91
82
|
});
|
|
92
|
-
|
|
93
|
-
const result = normalizeUniqueConstraints(models["user"]!);
|
|
94
|
-
|
|
83
|
+
const result = normalizeUniqueConstraints(models["user"]);
|
|
95
84
|
expect(result).toEqual([]);
|
|
96
85
|
});
|
|
97
|
-
|
|
98
86
|
test("handles multiple model-level unique constraints", () => {
|
|
99
87
|
const models = populateModels({
|
|
100
88
|
naming: { column: snakeCase },
|
|
@@ -114,15 +102,13 @@ describe("normalizeUniqueConstraints", () => {
|
|
|
114
102
|
{
|
|
115
103
|
name: "unique_email",
|
|
116
104
|
fields: ["email"],
|
|
117
|
-
where: sql`deleted_at IS NULL`,
|
|
105
|
+
where: sql `deleted_at IS NULL`,
|
|
118
106
|
},
|
|
119
107
|
],
|
|
120
108
|
},
|
|
121
109
|
},
|
|
122
110
|
});
|
|
123
|
-
|
|
124
|
-
const result = normalizeUniqueConstraints(models["user"]!);
|
|
125
|
-
|
|
111
|
+
const result = normalizeUniqueConstraints(models["user"]);
|
|
126
112
|
expect(result).toEqual([
|
|
127
113
|
{
|
|
128
114
|
name: "user_first_name_last_name_ukey",
|
|
@@ -135,12 +121,11 @@ describe("normalizeUniqueConstraints", () => {
|
|
|
135
121
|
name: "unique_email",
|
|
136
122
|
fields: ["email"],
|
|
137
123
|
columns: ["email"],
|
|
138
|
-
where: sql`deleted_at IS NULL`,
|
|
124
|
+
where: sql `deleted_at IS NULL`,
|
|
139
125
|
nullsNotDistinct: false,
|
|
140
126
|
},
|
|
141
127
|
]);
|
|
142
128
|
});
|
|
143
|
-
|
|
144
129
|
test("handles custom column names", () => {
|
|
145
130
|
const models = populateModels({
|
|
146
131
|
models: {
|
|
@@ -156,9 +141,7 @@ describe("normalizeUniqueConstraints", () => {
|
|
|
156
141
|
},
|
|
157
142
|
},
|
|
158
143
|
});
|
|
159
|
-
|
|
160
|
-
const result = normalizeUniqueConstraints(models["user"]!);
|
|
161
|
-
|
|
144
|
+
const result = normalizeUniqueConstraints(models["user"]);
|
|
162
145
|
expect(result).toEqual([
|
|
163
146
|
{
|
|
164
147
|
name: "user_user_email_ukey",
|
|
@@ -169,7 +152,6 @@ describe("normalizeUniqueConstraints", () => {
|
|
|
169
152
|
},
|
|
170
153
|
]);
|
|
171
154
|
});
|
|
172
|
-
|
|
173
155
|
test("returns empty array when no unique constraints exist", () => {
|
|
174
156
|
const models = populateModels({
|
|
175
157
|
models: {
|
|
@@ -181,12 +163,9 @@ describe("normalizeUniqueConstraints", () => {
|
|
|
181
163
|
},
|
|
182
164
|
},
|
|
183
165
|
});
|
|
184
|
-
|
|
185
|
-
const result = normalizeUniqueConstraints(models["user"]!);
|
|
186
|
-
|
|
166
|
+
const result = normalizeUniqueConstraints(models["user"]);
|
|
187
167
|
expect(result).toEqual([]);
|
|
188
168
|
});
|
|
189
|
-
|
|
190
169
|
test("throws error for non-existent fields in model-level constraints", () => {
|
|
191
170
|
const models = populateModels({
|
|
192
171
|
models: {
|
|
@@ -202,12 +181,8 @@ describe("normalizeUniqueConstraints", () => {
|
|
|
202
181
|
},
|
|
203
182
|
},
|
|
204
183
|
});
|
|
205
|
-
|
|
206
|
-
expect(() => normalizeUniqueConstraints(models["user"]!)).toThrow(
|
|
207
|
-
'Field "nonexistent" not found in model "user"',
|
|
208
|
-
);
|
|
184
|
+
expect(() => normalizeUniqueConstraints(models["user"])).toThrow('Field "nonexistent" not found in model "user"');
|
|
209
185
|
});
|
|
210
|
-
|
|
211
186
|
test("handles mix of boolean and object unique constraints at column level", () => {
|
|
212
187
|
const models = populateModels({
|
|
213
188
|
naming: { column: snakeCase },
|
|
@@ -219,7 +194,7 @@ describe("normalizeUniqueConstraints", () => {
|
|
|
219
194
|
username: {
|
|
220
195
|
type: "text",
|
|
221
196
|
unique: {
|
|
222
|
-
where: sql`active = true`,
|
|
197
|
+
where: sql `active = true`,
|
|
223
198
|
nullsNotDistinct: true,
|
|
224
199
|
},
|
|
225
200
|
},
|
|
@@ -227,9 +202,7 @@ describe("normalizeUniqueConstraints", () => {
|
|
|
227
202
|
},
|
|
228
203
|
},
|
|
229
204
|
});
|
|
230
|
-
|
|
231
|
-
const result = normalizeUniqueConstraints(models["user"]!);
|
|
232
|
-
|
|
205
|
+
const result = normalizeUniqueConstraints(models["user"]);
|
|
233
206
|
expect(result).toEqual([
|
|
234
207
|
{
|
|
235
208
|
name: "user_email_ukey",
|
|
@@ -242,12 +215,11 @@ describe("normalizeUniqueConstraints", () => {
|
|
|
242
215
|
name: "user_username_ukey",
|
|
243
216
|
fields: ["username"],
|
|
244
217
|
columns: ["username"],
|
|
245
|
-
where: sql`active = true`,
|
|
218
|
+
where: sql `active = true`,
|
|
246
219
|
nullsNotDistinct: true,
|
|
247
220
|
},
|
|
248
221
|
]);
|
|
249
222
|
});
|
|
250
|
-
|
|
251
223
|
test("throws error for duplicate unique constraints", () => {
|
|
252
224
|
const models = populateModels({
|
|
253
225
|
models: {
|
|
@@ -264,9 +236,6 @@ describe("normalizeUniqueConstraints", () => {
|
|
|
264
236
|
},
|
|
265
237
|
},
|
|
266
238
|
});
|
|
267
|
-
|
|
268
|
-
expect(() => normalizeUniqueConstraints(models["user"]!)).toThrow(
|
|
269
|
-
'Duplicate unique constraint defined in model "user"',
|
|
270
|
-
);
|
|
239
|
+
expect(() => normalizeUniqueConstraints(models["user"])).toThrow('Duplicate unique constraint defined in model "user"');
|
|
271
240
|
});
|
|
272
241
|
});
|
|
@@ -1,13 +1,5 @@
|
|
|
1
|
-
import { Config, FieldDefinition } from "@casekit/orm2-schema";
|
|
2
|
-
|
|
3
|
-
import { PopulatedFieldDefinition } from "#types/PopulatedFieldDefinition.js";
|
|
4
1
|
import { defaultZodSchema } from "./defaultZodSchema.js";
|
|
5
|
-
|
|
6
|
-
export const populateField = (
|
|
7
|
-
config: Config,
|
|
8
|
-
definition: FieldDefinition,
|
|
9
|
-
name: string,
|
|
10
|
-
): PopulatedFieldDefinition => {
|
|
2
|
+
export const populateField = (config, definition, name) => {
|
|
11
3
|
return {
|
|
12
4
|
name,
|
|
13
5
|
column: definition.column ?? config.naming?.column?.(name) ?? name,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
import { snakeCase } from "es-toolkit";
|
|
2
|
+
import { describe, expect, test } from "vitest";
|
|
3
|
+
import { ZodType, z } from "zod";
|
|
4
|
+
import { populateField } from "./populateField.js";
|
|
5
|
+
describe("populateField", () => {
|
|
6
|
+
test("populates minimal field definition with defaults", () => {
|
|
7
|
+
const result = populateField({
|
|
8
|
+
models: {
|
|
9
|
+
user: {
|
|
10
|
+
fields: {
|
|
11
|
+
name: { type: "text" },
|
|
12
|
+
},
|
|
13
|
+
},
|
|
14
|
+
},
|
|
15
|
+
}, { type: "text" }, "name");
|
|
16
|
+
expect(result).toEqual({
|
|
17
|
+
name: "name",
|
|
18
|
+
column: "name",
|
|
19
|
+
type: "text",
|
|
20
|
+
zodSchema: expect.any(ZodType),
|
|
21
|
+
default: null,
|
|
22
|
+
references: null,
|
|
23
|
+
nullable: false,
|
|
24
|
+
unique: false,
|
|
25
|
+
primaryKey: false,
|
|
26
|
+
provided: false,
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
test("applies naming function to column name when provided", () => {
|
|
30
|
+
const result = populateField({
|
|
31
|
+
naming: { column: snakeCase },
|
|
32
|
+
models: {
|
|
33
|
+
user: {
|
|
34
|
+
fields: {
|
|
35
|
+
fullName: { type: "text" },
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
}, { type: "text" }, "fullName");
|
|
40
|
+
expect(result.column).toBe("full_name");
|
|
41
|
+
});
|
|
42
|
+
test("uses explicit column name over naming function", () => {
|
|
43
|
+
const result = populateField({
|
|
44
|
+
naming: { column: snakeCase },
|
|
45
|
+
models: {
|
|
46
|
+
user: {
|
|
47
|
+
fields: {
|
|
48
|
+
fullName: { type: "text", column: "explicit_name" },
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
}, { type: "text", column: "explicit_name" }, "fullName");
|
|
53
|
+
expect(result.column).toBe("explicit_name");
|
|
54
|
+
});
|
|
55
|
+
test("preserves custom zodSchema", () => {
|
|
56
|
+
const customSchema = z.email();
|
|
57
|
+
const result = populateField({
|
|
58
|
+
models: {
|
|
59
|
+
user: {
|
|
60
|
+
fields: {
|
|
61
|
+
email: { type: "text", zodSchema: customSchema },
|
|
62
|
+
},
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
}, { type: "text", zodSchema: customSchema }, "email");
|
|
66
|
+
expect(result.zodSchema).toEqual(customSchema);
|
|
67
|
+
});
|
|
68
|
+
test("preserves all provided values", () => {
|
|
69
|
+
const result = populateField({
|
|
70
|
+
models: {
|
|
71
|
+
user: {
|
|
72
|
+
fields: {
|
|
73
|
+
id: {
|
|
74
|
+
type: "integer",
|
|
75
|
+
column: "custom_id",
|
|
76
|
+
zodSchema: z.number().min(1),
|
|
77
|
+
default: 42,
|
|
78
|
+
references: { model: "other", field: "id" },
|
|
79
|
+
nullable: true,
|
|
80
|
+
unique: true,
|
|
81
|
+
primaryKey: true,
|
|
82
|
+
provided: true,
|
|
83
|
+
},
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
other: {
|
|
87
|
+
fields: {
|
|
88
|
+
id: { type: "serial", primaryKey: true },
|
|
89
|
+
},
|
|
90
|
+
},
|
|
91
|
+
},
|
|
92
|
+
}, {
|
|
93
|
+
type: "integer",
|
|
94
|
+
column: "custom_id",
|
|
95
|
+
zodSchema: z.number().min(1),
|
|
96
|
+
default: 42,
|
|
97
|
+
references: { model: "other", field: "id" },
|
|
98
|
+
nullable: true,
|
|
99
|
+
unique: true,
|
|
100
|
+
primaryKey: true,
|
|
101
|
+
provided: true,
|
|
102
|
+
}, "id");
|
|
103
|
+
expect(result).toEqual({
|
|
104
|
+
name: "id",
|
|
105
|
+
type: "integer",
|
|
106
|
+
column: "custom_id",
|
|
107
|
+
zodSchema: expect.any(ZodType),
|
|
108
|
+
default: 42,
|
|
109
|
+
references: { model: "other", field: "id" },
|
|
110
|
+
nullable: true,
|
|
111
|
+
unique: true,
|
|
112
|
+
primaryKey: true,
|
|
113
|
+
provided: true,
|
|
114
|
+
});
|
|
115
|
+
});
|
|
116
|
+
test("handles array types", () => {
|
|
117
|
+
const result = populateField({
|
|
118
|
+
models: {
|
|
119
|
+
post: {
|
|
120
|
+
fields: {
|
|
121
|
+
tags: { type: "text[]" },
|
|
122
|
+
},
|
|
123
|
+
},
|
|
124
|
+
},
|
|
125
|
+
}, { type: "text[]" }, "tags");
|
|
126
|
+
expect(result).toEqual({
|
|
127
|
+
name: "tags",
|
|
128
|
+
column: "tags",
|
|
129
|
+
type: "text[]",
|
|
130
|
+
zodSchema: expect.any(ZodType),
|
|
131
|
+
default: null,
|
|
132
|
+
references: null,
|
|
133
|
+
nullable: false,
|
|
134
|
+
unique: false,
|
|
135
|
+
primaryKey: false,
|
|
136
|
+
provided: false,
|
|
137
|
+
});
|
|
138
|
+
});
|
|
139
|
+
test("handles nullable fields", () => {
|
|
140
|
+
const result = populateField({
|
|
141
|
+
models: {
|
|
142
|
+
post: {
|
|
143
|
+
fields: {
|
|
144
|
+
description: { type: "text", nullable: true },
|
|
145
|
+
},
|
|
146
|
+
},
|
|
147
|
+
},
|
|
148
|
+
}, { type: "text", nullable: true }, "description");
|
|
149
|
+
expect(result.nullable).toBe(true);
|
|
150
|
+
});
|
|
151
|
+
test("handles fields with defaults", () => {
|
|
152
|
+
const result = populateField({
|
|
153
|
+
models: {
|
|
154
|
+
user: {
|
|
155
|
+
fields: {
|
|
156
|
+
active: { type: "boolean", default: false },
|
|
157
|
+
},
|
|
158
|
+
},
|
|
159
|
+
},
|
|
160
|
+
}, { type: "boolean", default: false }, "active");
|
|
161
|
+
expect(result.default).toBe(false);
|
|
162
|
+
});
|
|
163
|
+
test("handles fields with foreign keys", () => {
|
|
164
|
+
const result = populateField({
|
|
165
|
+
models: {
|
|
166
|
+
user: {
|
|
167
|
+
fields: {
|
|
168
|
+
id: { type: "serial", primaryKey: true },
|
|
169
|
+
},
|
|
170
|
+
},
|
|
171
|
+
post: {
|
|
172
|
+
fields: {
|
|
173
|
+
userId: {
|
|
174
|
+
type: "integer",
|
|
175
|
+
references: {
|
|
176
|
+
model: "user",
|
|
177
|
+
field: "id",
|
|
178
|
+
onDelete: "CASCADE",
|
|
179
|
+
},
|
|
180
|
+
},
|
|
181
|
+
},
|
|
182
|
+
},
|
|
183
|
+
},
|
|
184
|
+
}, {
|
|
185
|
+
type: "integer",
|
|
186
|
+
references: {
|
|
187
|
+
model: "user",
|
|
188
|
+
field: "id",
|
|
189
|
+
onDelete: "CASCADE",
|
|
190
|
+
},
|
|
191
|
+
}, "userId");
|
|
192
|
+
expect(result.references).toEqual({
|
|
193
|
+
model: "user",
|
|
194
|
+
field: "id",
|
|
195
|
+
onDelete: "CASCADE",
|
|
196
|
+
});
|
|
197
|
+
});
|
|
198
|
+
});
|
|
@@ -1,13 +1,6 @@
|
|
|
1
1
|
import { mapValues } from "es-toolkit";
|
|
2
|
-
|
|
3
|
-
import { Config } from "@casekit/orm2-schema";
|
|
4
|
-
|
|
5
|
-
import { PopulatedModelDefinition } from "#types/PopulatedModelDefinition.js";
|
|
6
2
|
import { populateField } from "./populateField.js";
|
|
7
|
-
|
|
8
|
-
export const populateModels = (
|
|
9
|
-
config: Config,
|
|
10
|
-
): Record<string, PopulatedModelDefinition> => {
|
|
3
|
+
export const populateModels = (config) => {
|
|
11
4
|
return mapValues(config.models, (model, name) => {
|
|
12
5
|
return {
|
|
13
6
|
name,
|
|
@@ -17,9 +10,7 @@ export const populateModels = (
|
|
|
17
10
|
uniqueConstraints: model.uniqueConstraints ?? [],
|
|
18
11
|
foreignKeys: model.foreignKeys ?? [],
|
|
19
12
|
relations: model.relations ?? {},
|
|
20
|
-
fields: mapValues(model.fields, (field, name) =>
|
|
21
|
-
populateField(config, field, name),
|
|
22
|
-
),
|
|
13
|
+
fields: mapValues(model.fields, (field, name) => populateField(config, field, name)),
|
|
23
14
|
};
|
|
24
15
|
});
|
|
25
16
|
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
import { snakeCase } from "es-toolkit";
|
|
2
2
|
import { describe, expect, test } from "vitest";
|
|
3
3
|
import { ZodType } from "zod";
|
|
4
|
-
|
|
5
4
|
import { populateModels } from "./populateModels.js";
|
|
6
|
-
|
|
7
5
|
describe("populateModels", () => {
|
|
8
6
|
test("populates basic models with defaults", () => {
|
|
9
7
|
const result = populateModels({
|
|
@@ -16,7 +14,6 @@ describe("populateModels", () => {
|
|
|
16
14
|
},
|
|
17
15
|
},
|
|
18
16
|
});
|
|
19
|
-
|
|
20
17
|
expect(result).toEqual({
|
|
21
18
|
user: {
|
|
22
19
|
name: "user",
|
|
@@ -55,7 +52,6 @@ describe("populateModels", () => {
|
|
|
55
52
|
},
|
|
56
53
|
});
|
|
57
54
|
});
|
|
58
|
-
|
|
59
55
|
test("uses config schema when model schema not provided", () => {
|
|
60
56
|
const result = populateModels({
|
|
61
57
|
schema: "custom",
|
|
@@ -67,10 +63,8 @@ describe("populateModels", () => {
|
|
|
67
63
|
},
|
|
68
64
|
},
|
|
69
65
|
});
|
|
70
|
-
|
|
71
|
-
expect(result["user"]!.schema).toBe("custom");
|
|
66
|
+
expect(result["user"].schema).toBe("custom");
|
|
72
67
|
});
|
|
73
|
-
|
|
74
68
|
test("model schema overrides config schema", () => {
|
|
75
69
|
const result = populateModels({
|
|
76
70
|
schema: "config_schema",
|
|
@@ -83,10 +77,8 @@ describe("populateModels", () => {
|
|
|
83
77
|
},
|
|
84
78
|
},
|
|
85
79
|
});
|
|
86
|
-
|
|
87
|
-
expect(result["user"]!.schema).toBe("model_schema");
|
|
80
|
+
expect(result["user"].schema).toBe("model_schema");
|
|
88
81
|
});
|
|
89
|
-
|
|
90
82
|
test("applies table naming function when provided", () => {
|
|
91
83
|
const result = populateModels({
|
|
92
84
|
naming: { table: snakeCase },
|
|
@@ -98,10 +90,8 @@ describe("populateModels", () => {
|
|
|
98
90
|
},
|
|
99
91
|
},
|
|
100
92
|
});
|
|
101
|
-
|
|
102
|
-
expect(result["userProfile"]!.table).toBe("user_profile");
|
|
93
|
+
expect(result["userProfile"].table).toBe("user_profile");
|
|
103
94
|
});
|
|
104
|
-
|
|
105
95
|
test("explicit table name overrides naming function", () => {
|
|
106
96
|
const result = populateModels({
|
|
107
97
|
naming: { table: snakeCase },
|
|
@@ -114,10 +104,8 @@ describe("populateModels", () => {
|
|
|
114
104
|
},
|
|
115
105
|
},
|
|
116
106
|
});
|
|
117
|
-
|
|
118
|
-
expect(result["userProfile"]!.table).toBe("custom_table");
|
|
107
|
+
expect(result["userProfile"].table).toBe("custom_table");
|
|
119
108
|
});
|
|
120
|
-
|
|
121
109
|
test("uses explicit primary key when provided", () => {
|
|
122
110
|
const result = populateModels({
|
|
123
111
|
models: {
|
|
@@ -131,10 +119,8 @@ describe("populateModels", () => {
|
|
|
131
119
|
},
|
|
132
120
|
},
|
|
133
121
|
});
|
|
134
|
-
|
|
135
|
-
expect(result["post"]!.primaryKey).toEqual(["authorId", "slug"]);
|
|
122
|
+
expect(result["post"].primaryKey).toEqual(["authorId", "slug"]);
|
|
136
123
|
});
|
|
137
|
-
|
|
138
124
|
test("handles unique constraints", () => {
|
|
139
125
|
const result = populateModels({
|
|
140
126
|
models: {
|
|
@@ -152,15 +138,13 @@ describe("populateModels", () => {
|
|
|
152
138
|
},
|
|
153
139
|
},
|
|
154
140
|
});
|
|
155
|
-
|
|
156
|
-
expect(result["user"]!.uniqueConstraints).toEqual([
|
|
141
|
+
expect(result["user"].uniqueConstraints).toEqual([
|
|
157
142
|
{
|
|
158
143
|
name: "unique_email",
|
|
159
144
|
fields: ["email"],
|
|
160
145
|
},
|
|
161
146
|
]);
|
|
162
147
|
});
|
|
163
|
-
|
|
164
148
|
test("handles foreign keys", () => {
|
|
165
149
|
const result = populateModels({
|
|
166
150
|
models: {
|
|
@@ -185,8 +169,7 @@ describe("populateModels", () => {
|
|
|
185
169
|
},
|
|
186
170
|
},
|
|
187
171
|
});
|
|
188
|
-
|
|
189
|
-
expect(result["post"]!.foreignKeys).toEqual([
|
|
172
|
+
expect(result["post"].foreignKeys).toEqual([
|
|
190
173
|
{
|
|
191
174
|
fields: ["authorId"],
|
|
192
175
|
references: {
|
|
@@ -196,7 +179,6 @@ describe("populateModels", () => {
|
|
|
196
179
|
},
|
|
197
180
|
]);
|
|
198
181
|
});
|
|
199
|
-
|
|
200
182
|
test("handles relations", () => {
|
|
201
183
|
const result = populateModels({
|
|
202
184
|
models: {
|
|
@@ -221,8 +203,7 @@ describe("populateModels", () => {
|
|
|
221
203
|
},
|
|
222
204
|
},
|
|
223
205
|
});
|
|
224
|
-
|
|
225
|
-
expect(result["user"]!.relations).toEqual({
|
|
206
|
+
expect(result["user"].relations).toEqual({
|
|
226
207
|
posts: {
|
|
227
208
|
type: "1:N",
|
|
228
209
|
model: "post",
|
|
@@ -231,7 +212,6 @@ describe("populateModels", () => {
|
|
|
231
212
|
},
|
|
232
213
|
});
|
|
233
214
|
});
|
|
234
|
-
|
|
235
215
|
test("handles multiple models", () => {
|
|
236
216
|
const result = populateModels({
|
|
237
217
|
models: {
|
|
@@ -252,10 +232,9 @@ describe("populateModels", () => {
|
|
|
252
232
|
},
|
|
253
233
|
},
|
|
254
234
|
});
|
|
255
|
-
|
|
256
235
|
expect(Object.keys(result)).toEqual(["user", "post", "comment"]);
|
|
257
|
-
expect(result["user"]
|
|
258
|
-
expect(result["post"]
|
|
259
|
-
expect(result["comment"]
|
|
236
|
+
expect(result["user"].name).toBe("user");
|
|
237
|
+
expect(result["post"].name).toBe("post");
|
|
238
|
+
expect(result["comment"].name).toBe("comment");
|
|
260
239
|
});
|
|
261
240
|
});
|
|
@@ -1,19 +1,12 @@
|
|
|
1
1
|
import pg from "pg";
|
|
2
|
-
|
|
3
2
|
import { Logger, OperatorDefinitions } from "@casekit/orm2-schema";
|
|
4
|
-
|
|
5
3
|
import { NormalizedModelDefinition } from "./NormalizedModelDefinition.js";
|
|
6
|
-
|
|
7
4
|
export interface NormalizedConfig {
|
|
8
5
|
readonly schema: string;
|
|
9
6
|
readonly models: Record<string, NormalizedModelDefinition>;
|
|
10
7
|
readonly operators: OperatorDefinitions;
|
|
11
8
|
readonly extensions: readonly string[];
|
|
12
|
-
readonly connection:
|
|
13
|
-
| pg.ConnectionConfig
|
|
14
|
-
| pg.PoolConfig
|
|
15
|
-
| pg.PoolOptions
|
|
16
|
-
| null;
|
|
9
|
+
readonly connection: pg.ConnectionConfig | pg.PoolConfig | pg.PoolOptions | null;
|
|
17
10
|
readonly pool: boolean;
|
|
18
11
|
readonly logger: Logger;
|
|
19
12
|
readonly naming: {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|