@casekit/orm2-config 1.0.0 → 1.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/{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
|
@@ -6,12 +6,6 @@ export type { NormalizedFieldDefinition } from "./types/NormalizedFieldDefinitio
|
|
|
6
6
|
export type { NormalizedForeignKeyDefinition } from "./types/NormalizedForeignKeyDefinition.js";
|
|
7
7
|
export type { NormalizedModelDefinition } from "./types/NormalizedModelDefinition.js";
|
|
8
8
|
export type { NormalizedPrimaryKey } from "./types/NormalizedPrimaryKey.js";
|
|
9
|
-
export type {
|
|
10
|
-
NormalizedManyToManyRelationDefinition,
|
|
11
|
-
NormalizedManyToOneRelationDefinition,
|
|
12
|
-
NormalizedOneToManyRelationDefinition,
|
|
13
|
-
NormalizedRelationDefinition,
|
|
14
|
-
} from "./types/NormalizedRelationDefinition.js";
|
|
9
|
+
export type { NormalizedManyToManyRelationDefinition, NormalizedManyToOneRelationDefinition, NormalizedOneToManyRelationDefinition, NormalizedRelationDefinition, } from "./types/NormalizedRelationDefinition.js";
|
|
15
10
|
export type { NormalizedUniqueConstraintDefinition } from "./types/NormalizedUniqueConstraintDefinition.js";
|
|
16
|
-
|
|
17
11
|
export { getField, getModel, getRelation } from "./util.js";
|
package/build/index.js
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { normalizeConfig } from "./normalize/normalizeConfig.js";
|
|
2
|
+
export { normalizeModel } from "./normalize/normalizeModel.js";
|
|
3
|
+
export { normalizeUniqueConstraint } from "./normalize/normalizeUniqueConstraints.js";
|
|
4
|
+
export { getField, getModel, getRelation } from "./util.js";
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
/**
|
|
3
|
+
* WARNING!!! The schemas in this file must be kept in sync
|
|
4
|
+
* with DefaultFieldType in packages/orm-schema/src/helper/DefaultFieldType.ts.
|
|
5
|
+
* If you make a change here, make sure to update the
|
|
6
|
+
* corresponding type.
|
|
7
|
+
*/
|
|
8
|
+
export declare const defaultZodSchema: (type: string) => z.ZodType;
|
|
@@ -1,26 +1,37 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
|
|
3
2
|
/**
|
|
4
3
|
* WARNING!!! The schemas in this file must be kept in sync
|
|
5
4
|
* with DefaultFieldType in packages/orm-schema/src/helper/DefaultFieldType.ts.
|
|
6
5
|
* If you make a change here, make sure to update the
|
|
7
6
|
* corresponding type.
|
|
8
7
|
*/
|
|
9
|
-
export const defaultZodSchema = (type
|
|
8
|
+
export const defaultZodSchema = (type) => {
|
|
10
9
|
if (type.endsWith("[]"))
|
|
11
10
|
return z.array(defaultZodSchema(type.slice(0, -2)));
|
|
12
|
-
if (type.startsWith("bit "))
|
|
13
|
-
|
|
14
|
-
if (type.startsWith("
|
|
15
|
-
|
|
16
|
-
if (type.startsWith("
|
|
17
|
-
|
|
18
|
-
if (type.startsWith("
|
|
19
|
-
|
|
20
|
-
if (type.startsWith("
|
|
21
|
-
|
|
22
|
-
if (type.startsWith("
|
|
23
|
-
|
|
11
|
+
if (type.startsWith("bit "))
|
|
12
|
+
return z.string();
|
|
13
|
+
if (type.startsWith("bit("))
|
|
14
|
+
return z.string();
|
|
15
|
+
if (type.startsWith("character varying"))
|
|
16
|
+
return z.string();
|
|
17
|
+
if (type.startsWith("character"))
|
|
18
|
+
return z.string();
|
|
19
|
+
if (type.startsWith("numeric "))
|
|
20
|
+
return z.string();
|
|
21
|
+
if (type.startsWith("numeric("))
|
|
22
|
+
return z.string();
|
|
23
|
+
if (type.startsWith("timestamp "))
|
|
24
|
+
return z.date();
|
|
25
|
+
if (type.startsWith("timestamp("))
|
|
26
|
+
return z.date();
|
|
27
|
+
if (type.startsWith("time "))
|
|
28
|
+
return z.string();
|
|
29
|
+
if (type.startsWith("time("))
|
|
30
|
+
return z.string();
|
|
31
|
+
if (type.startsWith("varchar "))
|
|
32
|
+
return z.string();
|
|
33
|
+
if (type.startsWith("varchar("))
|
|
34
|
+
return z.string();
|
|
24
35
|
// if (type.startsWith("interval"))
|
|
25
36
|
// return z.object({
|
|
26
37
|
// years: z.number().optional(),
|
|
@@ -31,7 +42,6 @@ export const defaultZodSchema = (type: string): z.ZodType => {
|
|
|
31
42
|
// seconds: z.number().optional(),
|
|
32
43
|
// milliseconds: z.number().optional(),
|
|
33
44
|
// });
|
|
34
|
-
|
|
35
45
|
switch (type) {
|
|
36
46
|
case "bigint":
|
|
37
47
|
case "bigserial":
|
|
@@ -110,10 +120,8 @@ export const defaultZodSchema = (type: string): z.ZodType => {
|
|
|
110
120
|
case "timestamptz":
|
|
111
121
|
return z.date();
|
|
112
122
|
default:
|
|
113
|
-
throw new Error(
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
" - please specify a zod schema in the field definition",
|
|
117
|
-
);
|
|
123
|
+
throw new Error("Unsupported type: " +
|
|
124
|
+
type +
|
|
125
|
+
" - please specify a zod schema in the field definition");
|
|
118
126
|
}
|
|
119
127
|
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1,24 +1,18 @@
|
|
|
1
1
|
import pg from "pg";
|
|
2
2
|
import { afterAll, afterEach, beforeAll, describe, expect, test } from "vitest";
|
|
3
|
-
|
|
4
3
|
import { defaultZodSchema } from "./defaultZodSchema.js";
|
|
5
|
-
|
|
6
4
|
describe("defaultZodSchema", () => {
|
|
7
|
-
let db
|
|
8
|
-
|
|
5
|
+
let db;
|
|
9
6
|
beforeAll(async () => {
|
|
10
7
|
db = new pg.Client();
|
|
11
8
|
await db.connect();
|
|
12
9
|
});
|
|
13
|
-
|
|
14
10
|
afterEach(async () => {
|
|
15
11
|
await db.query("ROLLBACK");
|
|
16
12
|
});
|
|
17
|
-
|
|
18
13
|
afterAll(async () => {
|
|
19
14
|
await db.end();
|
|
20
15
|
});
|
|
21
|
-
|
|
22
16
|
test.for([
|
|
23
17
|
["char", "a", "a"],
|
|
24
18
|
["character", "a", "a"],
|
|
@@ -141,20 +135,18 @@ describe("defaultZodSchema", () => {
|
|
|
141
135
|
],
|
|
142
136
|
],
|
|
143
137
|
],
|
|
144
|
-
]
|
|
138
|
+
])("%s columns", async ([datatype, value, expected = value]) => {
|
|
145
139
|
await db.query("BEGIN TRANSACTION");
|
|
146
140
|
await db.query(`CREATE TABLE foo (value ${datatype})`);
|
|
147
|
-
|
|
148
141
|
if (datatype.endsWith("serial")) {
|
|
149
142
|
await db.query("INSERT INTO foo DEFAULT VALUES");
|
|
150
|
-
}
|
|
143
|
+
}
|
|
144
|
+
else {
|
|
151
145
|
await db.query(`INSERT INTO foo (value) VALUES ($1::${datatype})`, [
|
|
152
146
|
value,
|
|
153
147
|
]);
|
|
154
148
|
}
|
|
155
|
-
|
|
156
|
-
const result = await db.query<{ value: unknown }>(`SELECT * FROM foo`);
|
|
157
|
-
|
|
149
|
+
const result = await db.query(`SELECT * FROM foo`);
|
|
158
150
|
const parsed = defaultZodSchema(datatype).parse(result.rows[0]?.value);
|
|
159
151
|
expect(parsed).toEqual(expected);
|
|
160
152
|
});
|
|
@@ -1,9 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
export const getColumns = (
|
|
4
|
-
model: PopulatedModelDefinition,
|
|
5
|
-
fields: string[],
|
|
6
|
-
): string[] => {
|
|
1
|
+
export const getColumns = (model, fields) => {
|
|
7
2
|
return fields.map((f) => {
|
|
8
3
|
if (!model.fields[f]) {
|
|
9
4
|
throw new Error(`Field "${f}" not found in model "${model.name}"`);
|
|
@@ -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
|
-
|
|
4
3
|
import { getColumns } from "./getColumns.js";
|
|
5
4
|
import { populateModels } from "./populateModels.js";
|
|
6
|
-
|
|
7
5
|
describe("getColumns", () => {
|
|
8
6
|
test("returns column names for given fields", () => {
|
|
9
7
|
const models = populateModels({
|
|
@@ -18,12 +16,8 @@ describe("getColumns", () => {
|
|
|
18
16
|
},
|
|
19
17
|
},
|
|
20
18
|
});
|
|
21
|
-
|
|
22
|
-
expect(
|
|
23
|
-
getColumns(models["user"]!, ["id", "fullName", "emailAddress"]),
|
|
24
|
-
).toEqual(["id", "full_name", "email_address"]);
|
|
19
|
+
expect(getColumns(models["user"], ["id", "fullName", "emailAddress"])).toEqual(["id", "full_name", "email_address"]);
|
|
25
20
|
});
|
|
26
|
-
|
|
27
21
|
test("throws error for non-existent field", () => {
|
|
28
22
|
const models = populateModels({
|
|
29
23
|
naming: { column: snakeCase },
|
|
@@ -35,12 +29,8 @@ describe("getColumns", () => {
|
|
|
35
29
|
},
|
|
36
30
|
},
|
|
37
31
|
});
|
|
38
|
-
|
|
39
|
-
expect(() =>
|
|
40
|
-
getColumns(models["user"]!, ["id", "nonexistent"]),
|
|
41
|
-
).toThrow('Field "nonexistent" not found in model "user"');
|
|
32
|
+
expect(() => getColumns(models["user"], ["id", "nonexistent"])).toThrow('Field "nonexistent" not found in model "user"');
|
|
42
33
|
});
|
|
43
|
-
|
|
44
34
|
test("returns empty array for empty fields array", () => {
|
|
45
35
|
const models = populateModels({
|
|
46
36
|
naming: { column: snakeCase },
|
|
@@ -52,10 +42,8 @@ describe("getColumns", () => {
|
|
|
52
42
|
},
|
|
53
43
|
},
|
|
54
44
|
});
|
|
55
|
-
|
|
56
|
-
expect(getColumns(models["user"]!, [])).toEqual([]);
|
|
45
|
+
expect(getColumns(models["user"], [])).toEqual([]);
|
|
57
46
|
});
|
|
58
|
-
|
|
59
47
|
test("handles custom column names", () => {
|
|
60
48
|
const models = populateModels({
|
|
61
49
|
models: {
|
|
@@ -67,8 +55,7 @@ describe("getColumns", () => {
|
|
|
67
55
|
},
|
|
68
56
|
},
|
|
69
57
|
});
|
|
70
|
-
|
|
71
|
-
expect(getColumns(models["user"]!, ["id", "name"])).toEqual([
|
|
58
|
+
expect(getColumns(models["user"], ["id", "name"])).toEqual([
|
|
72
59
|
"id",
|
|
73
60
|
"user_full_name",
|
|
74
61
|
]);
|
|
@@ -1,12 +1,7 @@
|
|
|
1
1
|
import { identity, mapValues } from "es-toolkit";
|
|
2
|
-
|
|
3
|
-
import { Config } from "@casekit/orm2-schema";
|
|
4
|
-
|
|
5
|
-
import { NormalizedConfig } from "#types/NormalizedConfig.js";
|
|
6
2
|
import { normalizeModel } from "./normalizeModel.js";
|
|
7
3
|
import { populateModels } from "./populateModels.js";
|
|
8
|
-
|
|
9
|
-
export const normalizeConfig = (config: Config): NormalizedConfig => {
|
|
4
|
+
export const normalizeConfig = (config) => {
|
|
10
5
|
const models = populateModels(config);
|
|
11
6
|
return {
|
|
12
7
|
...config,
|
|
@@ -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
3
|
import { z } from "zod";
|
|
4
|
-
|
|
5
4
|
import { sql } from "@casekit/sql";
|
|
6
|
-
|
|
7
5
|
import { normalizeConfig } from "./normalizeConfig.js";
|
|
8
|
-
|
|
9
6
|
describe("normalizeConfig", () => {
|
|
10
7
|
test("normalizes minimal config with defaults", () => {
|
|
11
8
|
const config = {
|
|
@@ -17,10 +14,8 @@ describe("normalizeConfig", () => {
|
|
|
17
14
|
},
|
|
18
15
|
},
|
|
19
16
|
},
|
|
20
|
-
}
|
|
21
|
-
|
|
17
|
+
};
|
|
22
18
|
const result = normalizeConfig(config);
|
|
23
|
-
|
|
24
19
|
expect(result.schema).toBe("public");
|
|
25
20
|
expect(result.operators).toEqual({ where: {} });
|
|
26
21
|
expect(result.extensions).toEqual([]);
|
|
@@ -29,12 +24,10 @@ describe("normalizeConfig", () => {
|
|
|
29
24
|
expect(result.logger).toBe(console);
|
|
30
25
|
expect(typeof result.naming.column).toBe("function");
|
|
31
26
|
expect(typeof result.naming.table).toBe("function");
|
|
32
|
-
|
|
33
27
|
// Test that default identity functions don't transform names
|
|
34
28
|
expect(result.naming.column("userName")).toBe("userName");
|
|
35
29
|
expect(result.naming.table("UserProfile")).toBe("UserProfile");
|
|
36
30
|
});
|
|
37
|
-
|
|
38
31
|
test("preserves provided config values", () => {
|
|
39
32
|
const customLogger = {
|
|
40
33
|
debug: () => {
|
|
@@ -50,9 +43,7 @@ describe("normalizeConfig", () => {
|
|
|
50
43
|
/* empty */
|
|
51
44
|
},
|
|
52
45
|
};
|
|
53
|
-
|
|
54
46
|
const $contains = Symbol("contains");
|
|
55
|
-
|
|
56
47
|
const config = {
|
|
57
48
|
schema: "custom",
|
|
58
49
|
models: {
|
|
@@ -64,7 +55,7 @@ describe("normalizeConfig", () => {
|
|
|
64
55
|
},
|
|
65
56
|
operators: {
|
|
66
57
|
where: {
|
|
67
|
-
[$contains]: () => sql``,
|
|
58
|
+
[$contains]: () => sql ``,
|
|
68
59
|
},
|
|
69
60
|
},
|
|
70
61
|
extensions: ["uuid-ossp"],
|
|
@@ -78,10 +69,8 @@ describe("normalizeConfig", () => {
|
|
|
78
69
|
column: snakeCase,
|
|
79
70
|
table: snakeCase,
|
|
80
71
|
},
|
|
81
|
-
}
|
|
82
|
-
|
|
72
|
+
};
|
|
83
73
|
const result = normalizeConfig(config);
|
|
84
|
-
|
|
85
74
|
expect(result.schema).toBe("custom");
|
|
86
75
|
expect(result.operators).toBe(config.operators);
|
|
87
76
|
expect(result.extensions).toEqual(["uuid-ossp"]);
|
|
@@ -91,7 +80,6 @@ describe("normalizeConfig", () => {
|
|
|
91
80
|
expect(result.naming.column).toBe(snakeCase);
|
|
92
81
|
expect(result.naming.table).toBe(snakeCase);
|
|
93
82
|
});
|
|
94
|
-
|
|
95
83
|
test("normalizes models with relationships", () => {
|
|
96
84
|
const config = {
|
|
97
85
|
models: {
|
|
@@ -102,7 +90,7 @@ describe("normalizeConfig", () => {
|
|
|
102
90
|
},
|
|
103
91
|
relations: {
|
|
104
92
|
posts: {
|
|
105
|
-
type: "1:N"
|
|
93
|
+
type: "1:N",
|
|
106
94
|
model: "post",
|
|
107
95
|
fromField: "id",
|
|
108
96
|
toField: "authorId",
|
|
@@ -116,11 +104,9 @@ describe("normalizeConfig", () => {
|
|
|
116
104
|
},
|
|
117
105
|
},
|
|
118
106
|
},
|
|
119
|
-
}
|
|
120
|
-
|
|
107
|
+
};
|
|
121
108
|
const result = normalizeConfig(config);
|
|
122
|
-
|
|
123
|
-
expect(result.models["user"]!.relations["posts"]!).toEqual({
|
|
109
|
+
expect(result.models["user"].relations["posts"]).toEqual({
|
|
124
110
|
name: "posts",
|
|
125
111
|
type: "1:N",
|
|
126
112
|
model: "post",
|
|
@@ -135,7 +121,6 @@ describe("normalizeConfig", () => {
|
|
|
135
121
|
},
|
|
136
122
|
});
|
|
137
123
|
});
|
|
138
|
-
|
|
139
124
|
test("applies naming functions to all models", () => {
|
|
140
125
|
const config = {
|
|
141
126
|
naming: {
|
|
@@ -158,37 +143,20 @@ describe("normalizeConfig", () => {
|
|
|
158
143
|
},
|
|
159
144
|
},
|
|
160
145
|
},
|
|
161
|
-
}
|
|
162
|
-
|
|
146
|
+
};
|
|
163
147
|
const result = normalizeConfig(config);
|
|
164
|
-
|
|
165
148
|
// Check table names
|
|
166
|
-
expect(result.models["userProfile"]
|
|
167
|
-
expect(result.models["orderItem"]
|
|
168
|
-
|
|
149
|
+
expect(result.models["userProfile"].table).toBe("user_profile");
|
|
150
|
+
expect(result.models["orderItem"].table).toBe("order_item");
|
|
169
151
|
// Check column names in first model
|
|
170
|
-
expect(result.models["userProfile"]
|
|
171
|
-
|
|
172
|
-
);
|
|
173
|
-
expect(result.models["userProfile"]!.fields["firstName"]!.column).toBe(
|
|
174
|
-
"first_name",
|
|
175
|
-
);
|
|
176
|
-
expect(result.models["userProfile"]!.fields["lastName"]!.column).toBe(
|
|
177
|
-
"last_name",
|
|
178
|
-
);
|
|
179
|
-
|
|
152
|
+
expect(result.models["userProfile"].fields["userId"].column).toBe("user_id");
|
|
153
|
+
expect(result.models["userProfile"].fields["firstName"].column).toBe("first_name");
|
|
154
|
+
expect(result.models["userProfile"].fields["lastName"].column).toBe("last_name");
|
|
180
155
|
// Check column names in second model
|
|
181
|
-
expect(result.models["orderItem"]
|
|
182
|
-
|
|
183
|
-
);
|
|
184
|
-
expect(result.models["orderItem"]!.fields["productId"]!.column).toBe(
|
|
185
|
-
"product_id",
|
|
186
|
-
);
|
|
187
|
-
expect(result.models["orderItem"]!.fields["unitPrice"]!.column).toBe(
|
|
188
|
-
"unit_price",
|
|
189
|
-
);
|
|
156
|
+
expect(result.models["orderItem"].fields["orderId"].column).toBe("order_id");
|
|
157
|
+
expect(result.models["orderItem"].fields["productId"].column).toBe("product_id");
|
|
158
|
+
expect(result.models["orderItem"].fields["unitPrice"].column).toBe("unit_price");
|
|
190
159
|
});
|
|
191
|
-
|
|
192
160
|
test("normalizes custom Zod schemas in models", () => {
|
|
193
161
|
const config = {
|
|
194
162
|
models: {
|
|
@@ -206,19 +174,12 @@ describe("normalizeConfig", () => {
|
|
|
206
174
|
},
|
|
207
175
|
},
|
|
208
176
|
},
|
|
209
|
-
}
|
|
210
|
-
|
|
177
|
+
};
|
|
211
178
|
const result = normalizeConfig(config);
|
|
212
|
-
|
|
213
179
|
// Verify that Zod schemas are preserved
|
|
214
|
-
expect(
|
|
215
|
-
|
|
216
|
-
).toBeInstanceOf(z.ZodEmail);
|
|
217
|
-
expect(result.models["user"]!.fields["age"]!.zodSchema).toBeInstanceOf(
|
|
218
|
-
z.ZodNumber,
|
|
219
|
-
);
|
|
180
|
+
expect(result.models["user"].fields["email"].zodSchema).toBeInstanceOf(z.ZodEmail);
|
|
181
|
+
expect(result.models["user"].fields["age"].zodSchema).toBeInstanceOf(z.ZodNumber);
|
|
220
182
|
});
|
|
221
|
-
|
|
222
183
|
test("handles partial naming configuration", () => {
|
|
223
184
|
const config = {
|
|
224
185
|
naming: {
|
|
@@ -232,18 +193,13 @@ describe("normalizeConfig", () => {
|
|
|
232
193
|
},
|
|
233
194
|
},
|
|
234
195
|
},
|
|
235
|
-
}
|
|
236
|
-
|
|
196
|
+
};
|
|
237
197
|
const result = normalizeConfig(config);
|
|
238
|
-
|
|
239
198
|
// Column should be transformed
|
|
240
|
-
expect(result.models["userProfile"]
|
|
241
|
-
"user_id",
|
|
242
|
-
);
|
|
199
|
+
expect(result.models["userProfile"].fields["userId"].column).toBe("user_id");
|
|
243
200
|
// Table should remain unchanged due to default identity function
|
|
244
|
-
expect(result.models["userProfile"]
|
|
201
|
+
expect(result.models["userProfile"].table).toBe("userProfile");
|
|
245
202
|
});
|
|
246
|
-
|
|
247
203
|
test("handles empty extensions array", () => {
|
|
248
204
|
const config = {
|
|
249
205
|
models: {
|
|
@@ -254,10 +210,8 @@ describe("normalizeConfig", () => {
|
|
|
254
210
|
},
|
|
255
211
|
},
|
|
256
212
|
extensions: [],
|
|
257
|
-
}
|
|
258
|
-
|
|
213
|
+
};
|
|
259
214
|
const result = normalizeConfig(config);
|
|
260
|
-
|
|
261
215
|
expect(result.extensions).toEqual([]);
|
|
262
216
|
});
|
|
263
217
|
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1,10 +1,8 @@
|
|
|
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 { normalizeField } from "./normalizeField.js";
|
|
6
5
|
import { populateModels } from "./populateModels.js";
|
|
7
|
-
|
|
8
6
|
describe("normalizeField", () => {
|
|
9
7
|
test("strips out references, unique, and primary key properties", () => {
|
|
10
8
|
const models = populateModels({
|
|
@@ -30,8 +28,7 @@ describe("normalizeField", () => {
|
|
|
30
28
|
},
|
|
31
29
|
},
|
|
32
30
|
});
|
|
33
|
-
|
|
34
|
-
expect(normalizeField(models["post"]!.fields["id"]!)).toEqual({
|
|
31
|
+
expect(normalizeField(models["post"].fields["id"])).toEqual({
|
|
35
32
|
name: "id",
|
|
36
33
|
column: "id",
|
|
37
34
|
type: "serial",
|
|
@@ -40,8 +37,7 @@ describe("normalizeField", () => {
|
|
|
40
37
|
default: null,
|
|
41
38
|
provided: false,
|
|
42
39
|
});
|
|
43
|
-
|
|
44
|
-
expect(normalizeField(models["post"]!.fields["slug"]!)).toEqual({
|
|
40
|
+
expect(normalizeField(models["post"].fields["slug"])).toEqual({
|
|
45
41
|
name: "slug",
|
|
46
42
|
column: "slug",
|
|
47
43
|
type: "text",
|
|
@@ -50,8 +46,7 @@ describe("normalizeField", () => {
|
|
|
50
46
|
default: null,
|
|
51
47
|
provided: false,
|
|
52
48
|
});
|
|
53
|
-
|
|
54
|
-
expect(normalizeField(models["post"]!.fields["authorId"]!)).toEqual({
|
|
49
|
+
expect(normalizeField(models["post"].fields["authorId"])).toEqual({
|
|
55
50
|
name: "authorId",
|
|
56
51
|
column: "author_id",
|
|
57
52
|
type: "integer",
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { ForeignKeyDefinition } from "@casekit/orm2-schema";
|
|
2
|
+
import { NormalizedForeignKeyDefinition } from "#types/NormalizedForeignKeyDefinition.js";
|
|
3
|
+
import { PopulatedModelDefinition } from "#types/PopulatedModelDefinition.js";
|
|
4
|
+
export declare const normalizeForeignKeys: (models: Record<string, PopulatedModelDefinition>, model: PopulatedModelDefinition) => NormalizedForeignKeyDefinition[];
|
|
5
|
+
export declare const normalizeForeignKey: (models: Record<string, PopulatedModelDefinition>, model: PopulatedModelDefinition, fk: ForeignKeyDefinition) => NormalizedForeignKeyDefinition;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { isEqual } from "es-toolkit";
|
|
2
|
+
import { getColumns } from "./getColumns.js";
|
|
3
|
+
export const normalizeForeignKeys = (models, model) => {
|
|
4
|
+
const columnLevelForeignKeys = Object.values(model.fields)
|
|
5
|
+
.filter(hasReference)
|
|
6
|
+
.map(referenceToForeignKey)
|
|
7
|
+
.map((fk) => normalizeForeignKey(models, model, fk));
|
|
8
|
+
const modelLevelForeignKeys = model.foreignKeys.map((fk) => normalizeForeignKey(models, model, fk));
|
|
9
|
+
for (const fk of columnLevelForeignKeys) {
|
|
10
|
+
if (modelLevelForeignKeys.some((other) => isEqual(fk.columns, other.columns))) {
|
|
11
|
+
throw new Error(`Duplicate foreign key defined in model "${model.name}"`);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
return [...columnLevelForeignKeys, ...modelLevelForeignKeys];
|
|
15
|
+
};
|
|
16
|
+
export const normalizeForeignKey = (models, model, fk) => {
|
|
17
|
+
const referencedModel = models[fk.references.model];
|
|
18
|
+
if (!referencedModel) {
|
|
19
|
+
throw new Error(`Referenced model "${fk.references.model}" not found in models`);
|
|
20
|
+
}
|
|
21
|
+
const columns = getColumns(model, fk.fields);
|
|
22
|
+
return {
|
|
23
|
+
name: fk.name ?? [model.table, ...columns, "fkey"].join("_"),
|
|
24
|
+
fields: fk.fields,
|
|
25
|
+
columns: columns,
|
|
26
|
+
references: {
|
|
27
|
+
model: fk.references.model,
|
|
28
|
+
fields: fk.references.fields,
|
|
29
|
+
schema: referencedModel.schema,
|
|
30
|
+
table: referencedModel.table,
|
|
31
|
+
columns: getColumns(referencedModel, fk.references.fields),
|
|
32
|
+
},
|
|
33
|
+
onUpdate: fk.onUpdate ?? null,
|
|
34
|
+
onDelete: fk.onDelete ?? null,
|
|
35
|
+
};
|
|
36
|
+
};
|
|
37
|
+
const referenceToForeignKey = (field) => {
|
|
38
|
+
return {
|
|
39
|
+
fields: [field.name],
|
|
40
|
+
references: {
|
|
41
|
+
model: field.references.model,
|
|
42
|
+
fields: [field.references.field],
|
|
43
|
+
},
|
|
44
|
+
onUpdate: field.references.onUpdate,
|
|
45
|
+
onDelete: field.references.onDelete,
|
|
46
|
+
};
|
|
47
|
+
};
|
|
48
|
+
const hasReference = (field) => {
|
|
49
|
+
return !!field.references;
|
|
50
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|