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