@casekit/orm 0.0.1-alpha.14 → 0.0.1-alpha.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (215) hide show
  1. package/lib/orm.query.test.d.ts +2 -0
  2. package/lib/orm.query.test.d.ts.map +1 -0
  3. package/lib/orm.query.test.js +22 -0
  4. package/lib/orm.query.test.js.map +1 -0
  5. package/package.json +3 -6
  6. package/src/Connection.ts +65 -0
  7. package/src/errors.ts +18 -0
  8. package/src/index.ts +16 -0
  9. package/src/logger.ts +3 -0
  10. package/src/migrate/commands/implode.ts +46 -0
  11. package/src/migrate/index.ts +1 -0
  12. package/src/migrate/migrator.ts +24 -0
  13. package/src/migrate/sql/createExtensionsSql.test.ts +26 -0
  14. package/src/migrate/sql/createExtensionsSql.ts +16 -0
  15. package/src/migrate/sql/createForeignKeyConstraintSql.test.ts +50 -0
  16. package/src/migrate/sql/createForeignKeyConstraintSql.ts +44 -0
  17. package/src/migrate/sql/createSchemasSql.test.ts +81 -0
  18. package/src/migrate/sql/createSchemasSql.ts +15 -0
  19. package/src/migrate/sql/createTableSql.properties.ts +38 -0
  20. package/src/migrate/sql/createTableSql.test.ts +74 -0
  21. package/src/migrate/sql/createTableSql.ts +53 -0
  22. package/src/migrate/sql/createUniqueConstraintSql.ts +27 -0
  23. package/src/migrate/sql/dropSchemasSql.ts +15 -0
  24. package/src/migrate/sql/dropTableSql.ts +13 -0
  25. package/src/orm.query.test.ts +28 -0
  26. package/src/orm.ts +370 -0
  27. package/src/pull/index.ts +1 -0
  28. package/src/pull/introspect/getForeignKeys.ts +64 -0
  29. package/src/pull/introspect/getPrimaryKeys.ts +26 -0
  30. package/src/pull/introspect/getTables.ts +51 -0
  31. package/src/pull/introspect/getUniqueConstraints.ts +39 -0
  32. package/src/pull/parse/parseCreateUniqueIndexStatement.test.ts +14 -0
  33. package/src/pull/parse/parseCreateUniqueIndexStatement.ts +19 -0
  34. package/src/pull/pull.ts +78 -0
  35. package/src/pull/render/renderModel.test.ts +144 -0
  36. package/src/pull/render/renderModel.ts +141 -0
  37. package/src/pull/render/renderModelsIndex.ts +24 -0
  38. package/src/pull/render/renderRelations.ts +77 -0
  39. package/src/pull/render/renderRelationsIndex.ts +24 -0
  40. package/src/pull/types/ColumnMeta.ts +10 -0
  41. package/src/pull/types/ForeignKey.ts +6 -0
  42. package/src/pull/types/PrimaryKey.ts +6 -0
  43. package/src/pull/types/UniqueConstraint.ts +8 -0
  44. package/src/pull/util/format.ts +17 -0
  45. package/src/pull/util/quote.ts +7 -0
  46. package/src/pull/util/unquote.ts +9 -0
  47. package/src/queries/clauses/IncludeClause.ts +39 -0
  48. package/src/queries/clauses/LateralByClause.ts +4 -0
  49. package/src/queries/clauses/ReturningClause.ts +7 -0
  50. package/src/queries/clauses/SelectClause.ts +7 -0
  51. package/src/queries/clauses/WhereClause.ts +16 -0
  52. package/src/queries/clauses/helpers/OptionalColumn.ts +18 -0
  53. package/src/queries/clauses/helpers/OptionalParams.ts +14 -0
  54. package/src/queries/clauses/helpers/RequiredColumn.ts +8 -0
  55. package/src/queries/clauses/helpers/RequiredParams.ts +14 -0
  56. package/src/queries/clauses/include/IncludedRelationModel.ts +13 -0
  57. package/src/queries/clauses/include/IncludedRelationName.ts +11 -0
  58. package/src/queries/clauses/include/IncludedRelationQuery.ts +20 -0
  59. package/src/queries/clauses/where/buildWhereClause.ts +121 -0
  60. package/src/queries/clauses/where/buildWhereClauses.test.ts +145 -0
  61. package/src/queries/clauses/where/buildWhereClauses.ts +45 -0
  62. package/src/queries/clauses/where/operators.ts +13 -0
  63. package/src/queries/clauses/where/types/WhereClauseValue.ts +39 -0
  64. package/src/queries/count/buildCount.ts +77 -0
  65. package/src/queries/count/countToSql.ts +66 -0
  66. package/src/queries/count/tests/count.test.ts +35 -0
  67. package/src/queries/count/types/BaseCountParams.ts +11 -0
  68. package/src/queries/count/types/CountBuilder.ts +45 -0
  69. package/src/queries/count/types/CountParams.ts +27 -0
  70. package/src/queries/count.ts +33 -0
  71. package/src/queries/create/buildCreate.ts +67 -0
  72. package/src/queries/create/createResultSchema.ts +24 -0
  73. package/src/queries/create/createToSql.ts +44 -0
  74. package/src/queries/create/tests/createMany.varied-keys.test.ts +28 -0
  75. package/src/queries/create/tests/createOne.test-d.ts +116 -0
  76. package/src/queries/create/tests/createOne.test.ts +197 -0
  77. package/src/queries/create/types/BaseCreateManyParams.ts +7 -0
  78. package/src/queries/create/types/BaseCreateOneParams.ts +7 -0
  79. package/src/queries/create/types/CreateManyParams.ts +15 -0
  80. package/src/queries/create/types/CreateManyResult.ts +17 -0
  81. package/src/queries/create/types/CreateOneParams.ts +22 -0
  82. package/src/queries/create/types/CreateOneResult.ts +17 -0
  83. package/src/queries/createMany.ts +38 -0
  84. package/src/queries/createOne.ts +27 -0
  85. package/src/queries/delete/buildDelete.ts +56 -0
  86. package/src/queries/delete/deleteResultSchema.ts +23 -0
  87. package/src/queries/delete/deleteToSql.ts +48 -0
  88. package/src/queries/delete/tests/deleteOne.test.ts +108 -0
  89. package/src/queries/delete/types/BaseDeleteParams.ts +9 -0
  90. package/src/queries/delete/types/DeleteManyResult.ts +16 -0
  91. package/src/queries/delete/types/DeleteOneResult.ts +16 -0
  92. package/src/queries/delete/types/DeleteParams.ts +12 -0
  93. package/src/queries/deleteMany.ts +33 -0
  94. package/src/queries/deleteOne.ts +32 -0
  95. package/src/queries/find/buildFind.ts +138 -0
  96. package/src/queries/find/findResultSchema.ts +32 -0
  97. package/src/queries/find/findToSql.test.ts +123 -0
  98. package/src/queries/find/findToSql.ts +141 -0
  99. package/src/queries/find/getIncludedManyToManyRelations.ts +49 -0
  100. package/src/queries/find/getIncludedOneToManyRelations.ts +44 -0
  101. package/src/queries/find/tests/findMany.include.test.ts +107 -0
  102. package/src/queries/find/tests/findMany.limit.test-d.ts +75 -0
  103. package/src/queries/find/tests/findMany.limit.test.ts +176 -0
  104. package/src/queries/find/tests/findMany.nullable-relations.test.ts +127 -0
  105. package/src/queries/find/tests/findMany.orderBy.test-d.ts +84 -0
  106. package/src/queries/find/tests/findMany.orderBy.test.ts +184 -0
  107. package/src/queries/find/tests/findMany.select.test-d.ts +117 -0
  108. package/src/queries/find/tests/findMany.select.test.ts +188 -0
  109. package/src/queries/find/tests/findMany.too-deep.test-d.ts +154 -0
  110. package/src/queries/find/tests/findMany.where.test-d.ts +85 -0
  111. package/src/queries/find/tests/findMany.where.test.ts +76 -0
  112. package/src/queries/find/tests/middleware.find.where.test.ts +467 -0
  113. package/src/queries/find/types/BaseFindParams.ts +18 -0
  114. package/src/queries/find/types/FindBuilder.ts +73 -0
  115. package/src/queries/find/types/FindManyParams.ts +24 -0
  116. package/src/queries/find/types/FindManyResult.ts +13 -0
  117. package/src/queries/find/types/FindOneParams.ts +17 -0
  118. package/src/queries/find/types/FindOneResult.ts +50 -0
  119. package/src/queries/findMany.ts +134 -0
  120. package/src/queries/findOne.ts +30 -0
  121. package/src/queries/middleware/Middleware.ts +53 -0
  122. package/src/queries/middleware/ValuesMiddleware.ts +9 -0
  123. package/src/queries/middleware/WhereMiddleware.ts +16 -0
  124. package/src/queries/update/buildUpdate.ts +76 -0
  125. package/src/queries/update/tests/updateOne.test.ts +118 -0
  126. package/src/queries/update/types/BaseUpdateParams.ts +10 -0
  127. package/src/queries/update/types/UpdateManyResult.ts +16 -0
  128. package/src/queries/update/types/UpdateOneResult.ts +16 -0
  129. package/src/queries/update/types/UpdateParams.ts +14 -0
  130. package/src/queries/update/types/UpdateValues.ts +12 -0
  131. package/src/queries/update/updateResultSchema.ts +23 -0
  132. package/src/queries/update/updateToSql.ts +43 -0
  133. package/src/queries/updateMany.ts +33 -0
  134. package/src/queries/updateOne.ts +33 -0
  135. package/src/queries/util/hasConditions.test.ts +36 -0
  136. package/src/queries/util/hasConditions.ts +14 -0
  137. package/src/queries/util/rowToObject.test.ts +76 -0
  138. package/src/queries/util/rowToObject.ts +13 -0
  139. package/src/queries/util/tableAlias.test.ts +20 -0
  140. package/src/queries/util/tableAlias.ts +10 -0
  141. package/src/schema/populate/composeMiddleware.ts +51 -0
  142. package/src/schema/populate/populateConfiguration.ts +48 -0
  143. package/src/schema/populate/populateModel.ts +98 -0
  144. package/src/schema/populate/suggestedColumnSchema.ts +62 -0
  145. package/src/schema/types/base/BaseColumn.ts +10 -0
  146. package/src/schema/types/base/BaseConfiguration.ts +14 -0
  147. package/src/schema/types/base/BaseModel.ts +12 -0
  148. package/src/schema/types/base/BaseModels.ts +3 -0
  149. package/src/schema/types/base/BaseOrm.ts +8 -0
  150. package/src/schema/types/base/BaseRelation.ts +19 -0
  151. package/src/schema/types/base/BaseRelations.ts +3 -0
  152. package/src/schema/types/constraints/ForeignKey.ts +13 -0
  153. package/src/schema/types/constraints/UniqueConstraint.ts +9 -0
  154. package/src/schema/types/helpers/ColumnName.ts +6 -0
  155. package/src/schema/types/helpers/ColumnType.ts +23 -0
  156. package/src/schema/types/helpers/Columns.ts +3 -0
  157. package/src/schema/types/helpers/HasDefault.ts +11 -0
  158. package/src/schema/types/helpers/IsNullable.ts +7 -0
  159. package/src/schema/types/helpers/IsProvided.ts +8 -0
  160. package/src/schema/types/helpers/IsSerial.ts +10 -0
  161. package/src/schema/types/helpers/ModelName.ts +6 -0
  162. package/src/schema/types/loose/LooseColumnDefinition.ts +80 -0
  163. package/src/schema/types/loose/LooseModelDefinition.ts +47 -0
  164. package/src/schema/types/loose/LooseModelDefinitions.ts +3 -0
  165. package/src/schema/types/loose/LooseRelationDefinition.ts +10 -0
  166. package/src/schema/types/loose/LooseRelationsDefinition.ts +8 -0
  167. package/src/schema/types/loose/LooseRelationsDefinitions.ts +7 -0
  168. package/src/schema/types/postgres/DataType.ts +65 -0
  169. package/src/schema/types/relations/ManyToManyRelation.ts +15 -0
  170. package/src/schema/types/relations/ManyToOneRelation.ts +14 -0
  171. package/src/schema/types/relations/OneToManyRelation.ts +12 -0
  172. package/src/schema/types/strict/ColumnDefinition.ts +81 -0
  173. package/src/schema/types/strict/ModelDefinition.ts +47 -0
  174. package/src/schema/types/strict/ModelDefinitions.ts +3 -0
  175. package/src/schema/types/strict/RelationDefinition.ts +13 -0
  176. package/src/schema/types/strict/RelationsDefinition.ts +8 -0
  177. package/src/schema/types/strict/RelationsDefinitions.ts +7 -0
  178. package/src/schema/validate/validateConfiguration.ts +9 -0
  179. package/src/schema/validate/validateModel.ts +32 -0
  180. package/src/sql/SQLStatement.test.ts +112 -0
  181. package/src/sql/SQLStatement.ts +86 -0
  182. package/src/sql/index.ts +2 -0
  183. package/src/sql/sql.ts +47 -0
  184. package/src/test/db/index.ts +27 -0
  185. package/src/test/db/models/foo.model.ts +24 -0
  186. package/src/test/db/models/foo.relations.ts +4 -0
  187. package/src/test/db/models/post.model.ts +34 -0
  188. package/src/test/db/models/post.relations.ts +21 -0
  189. package/src/test/db/models/tenant.model.ts +14 -0
  190. package/src/test/db/models/tenant.relations.ts +22 -0
  191. package/src/test/db/models/tenantUser.model.ts +23 -0
  192. package/src/test/db/models/tenantUser.relations.ts +15 -0
  193. package/src/test/db/models/user.model.ts +29 -0
  194. package/src/test/db/models/user.relations.ts +38 -0
  195. package/src/test/db/models.ts +15 -0
  196. package/src/test/db/relations.ts +13 -0
  197. package/src/test/gen/column.ts +39 -0
  198. package/src/test/gen/index.ts +2 -0
  199. package/src/test/gen/model.ts +58 -0
  200. package/src/test/gen/sqldate.ts +8 -0
  201. package/src/test/globalSetup.ts +6 -0
  202. package/src/test/seed/index.ts +75 -0
  203. package/src/test/util/withRollback.ts +18 -0
  204. package/src/test/util/withTransaction.ts +19 -0
  205. package/src/types/ColumnName.ts +6 -0
  206. package/src/types/ColumnType.ts +20 -0
  207. package/src/types/Configuration.ts +21 -0
  208. package/src/types/ModelType.ts +20 -0
  209. package/src/types/util/DeepRequired.ts +7 -0
  210. package/src/types/util/DisallowExtraKeys.ts +5 -0
  211. package/src/types/util/NonEmptyArray.ts +1 -0
  212. package/src/types/util/Simplify.ts +8 -0
  213. package/src/util/ensureArray.ts +4 -0
  214. package/src/util/interleave.test.ts +35 -0
  215. package/src/util/interleave.ts +8 -0
@@ -0,0 +1,78 @@
1
+ import fs from "fs";
2
+ import { camelCase } from "lodash-es";
3
+ import path from "path";
4
+ import pg from "pg";
5
+
6
+ import { getForeignKeys } from "./introspect/getForeignKeys";
7
+ import { getPrimaryKeys } from "./introspect/getPrimaryKeys";
8
+ import { getTables } from "./introspect/getTables";
9
+ import { getUniqueConstraints } from "./introspect/getUniqueConstraints";
10
+ import { renderModel } from "./render/renderModel";
11
+ import { renderModelsIndex } from "./render/renderModelsIndex";
12
+ import { renderRelations } from "./render/renderRelations";
13
+ import { renderRelationsIndex } from "./render/renderRelationsIndex";
14
+ import { format } from "./util/format";
15
+
16
+ export const pull = async (
17
+ client: pg.Client,
18
+ opts: { schema: string; outDir: string },
19
+ ) => {
20
+ const tables = await getTables(client, opts.schema);
21
+ const primaryKeys = await getPrimaryKeys(client, opts.schema);
22
+ const uniqueConstraints = await getUniqueConstraints(client, opts.schema);
23
+ const foreignKeys = await getForeignKeys(client, opts.schema);
24
+
25
+ fs.mkdirSync(path.resolve(opts.outDir, "models"), { recursive: true });
26
+
27
+ for (const [table, columns] of Object.entries(tables)) {
28
+ fs.writeFileSync(
29
+ path.resolve(opts.outDir, "models", `${camelCase(table)}.model.ts`),
30
+ await renderModel({
31
+ table,
32
+ columns,
33
+ primaryKey: primaryKeys[table] ?? [],
34
+ uniqueConstraints: uniqueConstraints[table] ?? [],
35
+ foreignKeys: foreignKeys[table] ?? [],
36
+ }),
37
+ { encoding: "utf-8" },
38
+ );
39
+ }
40
+
41
+ fs.writeFileSync(
42
+ path.resolve(opts.outDir, "models.ts"),
43
+ await renderModelsIndex(Object.keys(tables)),
44
+ { encoding: "utf-8" },
45
+ );
46
+
47
+ for (const table of Object.keys(tables)) {
48
+ fs.writeFileSync(
49
+ path.resolve(
50
+ opts.outDir,
51
+ "models",
52
+ `${camelCase(table)}.relations.ts`,
53
+ ),
54
+ await renderRelations({
55
+ table,
56
+ foreignKeys: foreignKeys,
57
+ }),
58
+ { encoding: "utf-8" },
59
+ );
60
+ }
61
+
62
+ fs.writeFileSync(
63
+ path.resolve(opts.outDir, "relations.ts"),
64
+ await renderRelationsIndex(Object.keys(tables)),
65
+ { encoding: "utf-8" },
66
+ );
67
+
68
+ if (!fs.existsSync(path.resolve(opts.outDir, "index.ts"))) {
69
+ fs.writeFileSync(
70
+ path.resolve(opts.outDir, "index.ts"),
71
+ await format(`
72
+ export { models, type Models } from "./models";
73
+ export { relations, type Relations } from "./relations";
74
+ `),
75
+ { encoding: "utf-8" },
76
+ );
77
+ }
78
+ };
@@ -0,0 +1,144 @@
1
+ import { unindent } from "@casekit/unindent";
2
+
3
+ import { describe, expect, test } from "vitest";
4
+
5
+ import { renderModel } from "./renderModel";
6
+
7
+ describe("renderModel", () => {
8
+ test("it renders a valid model definition given introspection results", async () => {
9
+ expect(
10
+ await renderModel({
11
+ table: "my_table",
12
+ columns: [
13
+ {
14
+ name: "id",
15
+ type: "uuid",
16
+ default: "uuid_generate_v4()",
17
+ cardinality: 0,
18
+ elementtype: null,
19
+ table: "my_table",
20
+ ordinal: 0,
21
+ nullable: false,
22
+ },
23
+ {
24
+ name: "name",
25
+ type: "text",
26
+ default: null,
27
+ cardinality: 0,
28
+ elementtype: null,
29
+ table: "my_table",
30
+ ordinal: 1,
31
+ nullable: true,
32
+ },
33
+ ],
34
+ uniqueConstraints: [
35
+ {
36
+ name: "my_table_name_unique",
37
+ table: "my_table",
38
+ definition:
39
+ "CREATE UNIQUE INDEX ON my_table USING btree (name)",
40
+ columns: ["name"],
41
+ where: undefined,
42
+ },
43
+ ],
44
+ foreignKeys: [],
45
+ primaryKey: ["id"],
46
+ }),
47
+ ).toEqual(
48
+ unindent`
49
+ import { type ModelDefinition, type ModelType, sql } from "@casekit/orm";
50
+
51
+ export const myTable = {
52
+ table: "my_table",
53
+ columns: {
54
+ id: {
55
+ name: "id",
56
+ type: "uuid",
57
+ primaryKey: true,
58
+ default: sql\`uuid_generate_v4()\`,
59
+ },
60
+ name: { name: "name", type: "text", unique: true, nullable: true },
61
+ },
62
+ } as const satisfies ModelDefinition;
63
+
64
+ export type MyTable = ModelType<typeof myTable>;
65
+ ` + "\n",
66
+ );
67
+ });
68
+ test("it renders a valid model definition given introspection results with a column-local unique constraint", async () => {
69
+ expect(
70
+ await renderModel({
71
+ table: "my_table",
72
+ columns: [
73
+ {
74
+ name: "id",
75
+ type: "uuid",
76
+ default: "uuid_generate_v4()",
77
+ cardinality: 0,
78
+ elementtype: null,
79
+ table: "my_table",
80
+ ordinal: 0,
81
+ nullable: false,
82
+ },
83
+ {
84
+ name: "name",
85
+ type: "text",
86
+ default: null,
87
+ cardinality: 0,
88
+ elementtype: null,
89
+ table: "my_table",
90
+ ordinal: 1,
91
+ nullable: true,
92
+ },
93
+ {
94
+ name: "deleted_at",
95
+ type: "timestamp",
96
+ default: null,
97
+ cardinality: 0,
98
+ elementtype: null,
99
+ table: "my_table",
100
+ ordinal: 2,
101
+ nullable: true,
102
+ },
103
+ ],
104
+ uniqueConstraints: [
105
+ {
106
+ name: "my_table_name_unique",
107
+ table: "my_table",
108
+ definition:
109
+ "CREATE UNIQUE INDEX ON my_table USING btree (name)",
110
+ columns: ["name"],
111
+ where: "deleted_at IS NULL",
112
+ },
113
+ ],
114
+ primaryKey: ["id"],
115
+ foreignKeys: [],
116
+ }),
117
+ ).toEqual(
118
+ unindent`
119
+ import { type ModelDefinition, type ModelType, sql } from "@casekit/orm";
120
+
121
+ export const myTable = {
122
+ table: "my_table",
123
+ columns: {
124
+ id: {
125
+ name: "id",
126
+ type: "uuid",
127
+ primaryKey: true,
128
+ default: sql\`uuid_generate_v4()\`,
129
+ },
130
+ name: {
131
+ name: "name",
132
+ type: "text",
133
+ unique: { where: sql\`deleted_at IS NULL\` },
134
+ nullable: true,
135
+ },
136
+ deletedAt: { name: "deleted_at", type: "timestamp", nullable: true },
137
+ },
138
+ } as const satisfies ModelDefinition;
139
+
140
+ export type MyTable = ModelType<typeof myTable>;
141
+ ` + "\n",
142
+ );
143
+ });
144
+ });
@@ -0,0 +1,141 @@
1
+ import { camelCase, identity, times, upperFirst } from "lodash-es";
2
+
3
+ import { ColumnMeta } from "../types/ColumnMeta";
4
+ import { ForeignKey } from "../types/ForeignKey";
5
+ import { UniqueConstraint } from "../types/UniqueConstraint";
6
+ import { format } from "../util/format";
7
+ import { quote } from "../util/quote";
8
+
9
+ type Definition = {
10
+ table: string;
11
+ columns: ColumnMeta[];
12
+ primaryKey: string[];
13
+ uniqueConstraints: UniqueConstraint[];
14
+ foreignKeys: ForeignKey[];
15
+ };
16
+
17
+ const renderDefault = (d: string) => {
18
+ return d.match(/^\d+$/)
19
+ ? `${d}`
20
+ : d.match(/::text$/)
21
+ ? d.replace(/::text$/, "")
22
+ : ["true", "false"].includes(d)
23
+ ? d
24
+ : `sql\`${d}\``;
25
+ };
26
+
27
+ const renderType = (column: ColumnMeta) => {
28
+ if (column.type === "ARRAY") {
29
+ return (
30
+ column.elementtype +
31
+ times(column.cardinality)
32
+ .map(() => "[]")
33
+ .join("")
34
+ );
35
+ } else {
36
+ return column.type;
37
+ }
38
+ };
39
+
40
+ export const renderColumn = (def: Definition) => (column: ColumnMeta) => {
41
+ const primaryKey =
42
+ def.primaryKey.length === 1 && def.primaryKey[0] === column.name;
43
+
44
+ const unique = def.uniqueConstraints.find(
45
+ (uc) => uc.columns.length === 1 && uc.columns[0] === column.name,
46
+ );
47
+
48
+ const references = def.foreignKeys.find(
49
+ (fk) =>
50
+ fk.columnsFrom.length === 1 && fk.columnsFrom[0] === column.name,
51
+ );
52
+
53
+ const definition = [
54
+ `name: "${column.name}"`,
55
+ `type: "${renderType(column)}"`,
56
+ primaryKey ? "primaryKey: true" : null,
57
+ unique ? `unique: ${renderColumnUniqueConstraint(unique)}` : null,
58
+ column.nullable ? "nullable: true" : null,
59
+ column.default ? `default: ${renderDefault(column.default)}` : null,
60
+ references
61
+ ? `references: { table: ${quote(references.tableTo)}, column: ${quote(references.columnsTo[0])} }`
62
+ : null,
63
+ ]
64
+ .filter(identity)
65
+ .join(", ");
66
+
67
+ return `"${camelCase(column.name)}": { ${definition} }`;
68
+ };
69
+
70
+ export const renderColumnUniqueConstraint = (constraint: UniqueConstraint) => {
71
+ if (constraint.nullsNotDistinct || constraint.where) {
72
+ return `{${constraint.nullsNotDistinct ? " nullsNotDistinct: true," : ""}${constraint.where ? ` where: sql\`${constraint.where}\`}` : ""}`;
73
+ } else {
74
+ return "true";
75
+ }
76
+ };
77
+
78
+ export const renderUniqueConstraint = (constraint: UniqueConstraint) => {
79
+ return `{ columns: [${constraint.columns.map(quote).join(", ")}], ${constraint.where ? `where: sql\`${constraint.where}\`,` : ""} }`;
80
+ };
81
+
82
+ export const renderUniqueConstraints = (constraints: UniqueConstraint[]) => {
83
+ return `uniqueConstraints: [ ${constraints.map(renderUniqueConstraint).join(", ")} ]`;
84
+ };
85
+
86
+ export const renderForeignKey = (constraint: ForeignKey) => {
87
+ return `{
88
+ columns: [${constraint.columnsFrom.map(quote).join(", ")}],
89
+ references: {
90
+ table: ${quote(constraint.tableTo)},
91
+ columns: [${constraint.columnsTo.map(quote).join(", ")}]
92
+ }
93
+ }`;
94
+ };
95
+
96
+ export const renderForeignKeys = (constraints: ForeignKey[]) => {
97
+ return `foreignKeys: [ ${constraints.map(renderForeignKey).join(", ")} ]`;
98
+ };
99
+
100
+ export const renderModel = async (def: Definition) => {
101
+ const imports = ["type ModelDefinition", "type ModelType"];
102
+ if (def.columns.find((c) => c.default !== null)) {
103
+ imports.push("sql");
104
+ }
105
+
106
+ const lines: string[] = [];
107
+
108
+ lines.push(
109
+ `columns: { ${def.columns.map(renderColumn(def)).join(",\n")} }`,
110
+ );
111
+
112
+ if (def.primaryKey.length > 1) {
113
+ lines.push(`primaryKey: [${def.primaryKey.map(quote).join(", ")}]`);
114
+ }
115
+
116
+ const multiColumnUniqueConstraints = def.uniqueConstraints.filter(
117
+ (uc) => uc.columns.length > 1,
118
+ );
119
+
120
+ if (multiColumnUniqueConstraints.length > 0) {
121
+ lines.push(renderUniqueConstraints(multiColumnUniqueConstraints));
122
+ }
123
+
124
+ const multiColumnForeignKeys = def.foreignKeys.filter(
125
+ (fk) => fk.columnsFrom.length > 1,
126
+ );
127
+
128
+ if (multiColumnForeignKeys.length > 0) {
129
+ lines.push(renderForeignKeys(multiColumnForeignKeys));
130
+ }
131
+
132
+ return await format(`
133
+ import { ${imports.join(", ")} } from "@casekit/orm";
134
+
135
+ export const ${camelCase(def.table)} = {
136
+ table: "${def.table}",
137
+ ${lines.join(",\n")}
138
+ } as const satisfies ModelDefinition;
139
+
140
+ export type ${upperFirst(camelCase(def.table))} = ModelType<typeof ${camelCase(def.table)}>;`);
141
+ };
@@ -0,0 +1,24 @@
1
+ import { camelCase } from "lodash-es";
2
+
3
+ import { format } from "../util/format";
4
+
5
+ export const renderModelsIndex = async (tableNames: string[]) => {
6
+ const names = tableNames.map((t) => camelCase(t));
7
+
8
+ const imports = names.map(
9
+ (name) => `import { ${name} } from "./models/${name}.model";`,
10
+ );
11
+ const exports = names.map((name) => `${name},`);
12
+
13
+ return await format(
14
+ `
15
+ ${imports.join("\n")}
16
+
17
+ export const models = {
18
+ ${exports.join("\n")}
19
+ };
20
+
21
+ export type Models = typeof models;
22
+ `,
23
+ );
24
+ };
@@ -0,0 +1,77 @@
1
+ import { camelCase, upperFirst } from "lodash-es";
2
+ import pluralize from "pluralize";
3
+
4
+ import { ForeignKey } from "../types/ForeignKey";
5
+ import { format } from "../util/format";
6
+ import { unquote } from "../util/unquote";
7
+
8
+ type Definition = {
9
+ table: string;
10
+ foreignKeys: Record<string, ForeignKey[]>;
11
+ };
12
+
13
+ const guessManyToOneRelationName = (fk: ForeignKey) => {
14
+ if (fk.columnsFrom.length > 1) {
15
+ return camelCase(fk.columnsFrom.join("_"));
16
+ } else {
17
+ return camelCase(fk.columnsFrom[0]).replace(/Id$/, "");
18
+ }
19
+ };
20
+
21
+ const guessOneToManyRelationName = (fk: ForeignKey) => {
22
+ if (fk.columnsFrom.length > 1) {
23
+ return camelCase(fk.columnsFrom.join("_") + "_" + fk.tableFrom);
24
+ } else {
25
+ const foreignName = camelCase(fk.columnsFrom[0]).replace(/Id$/, "");
26
+ if (foreignName === camelCase(fk.tableTo)) {
27
+ return camelCase(pluralize(fk.tableFrom));
28
+ } else {
29
+ return camelCase(
30
+ upperFirst(camelCase(fk.columnsFrom[0]).replace(/Id$/, "")) +
31
+ " " +
32
+ pluralize(fk.tableFrom),
33
+ );
34
+ }
35
+ }
36
+ };
37
+
38
+ export const renderRelations = async (def: Definition) => {
39
+ const manyToOne = (def.foreignKeys[def.table] ?? []).map(
40
+ (fk) =>
41
+ `${guessManyToOneRelationName(fk)}:` +
42
+ JSON.stringify({
43
+ model: camelCase(fk.tableTo),
44
+ type: "N:1",
45
+ foreignKey:
46
+ fk.columnsFrom.length === 1
47
+ ? camelCase(fk.columnsFrom[0])
48
+ : fk.columnsFrom.map(camelCase),
49
+ }),
50
+ );
51
+
52
+ const oneToMany = Object.entries(def.foreignKeys).flatMap(([table, fks]) =>
53
+ fks
54
+ .filter((fk) => unquote(fk.tableTo) === unquote(def.table))
55
+ .map(
56
+ (fk) =>
57
+ `${guessOneToManyRelationName(fk)}:` +
58
+ JSON.stringify({
59
+ model: camelCase(table),
60
+ type: "1:N",
61
+ foreignKey:
62
+ fk.columnsFrom.length === 1
63
+ ? camelCase(fk.columnsFrom[0])
64
+ : fk.columnsFrom.map(camelCase),
65
+ }),
66
+ ),
67
+ );
68
+
69
+ return await format(`
70
+ import { RelationsDefinition } from "@casekit/orm";
71
+ import { type Models } from "../models";
72
+
73
+ export const ${camelCase(def.table)} = {
74
+ ${[...manyToOne, ...oneToMany].map((rel) => rel).join(",\n")}
75
+ } as const satisfies RelationsDefinition<Models, "${camelCase(def.table)}">;
76
+ `);
77
+ };
@@ -0,0 +1,24 @@
1
+ import { camelCase } from "lodash-es";
2
+
3
+ import { format } from "../util/format";
4
+
5
+ export const renderRelationsIndex = async (tableNames: string[]) => {
6
+ const names = tableNames.map((t) => camelCase(t));
7
+
8
+ const imports = names.map(
9
+ (name) => `import { ${name} } from "./models/${name}.relations";`,
10
+ );
11
+ const exports = names.map((name) => `${name},`);
12
+
13
+ return await format(
14
+ `
15
+ ${imports.join("\n")}
16
+
17
+ export const relations = {
18
+ ${exports.join("\n")}
19
+ };
20
+
21
+ export type Relations = typeof relations;
22
+ `,
23
+ );
24
+ };
@@ -0,0 +1,10 @@
1
+ export type ColumnMeta = {
2
+ table: string;
3
+ name: string;
4
+ ordinal: number;
5
+ default: string | null;
6
+ nullable: boolean;
7
+ type: string;
8
+ elementtype: string | null;
9
+ cardinality: number;
10
+ };
@@ -0,0 +1,6 @@
1
+ export type ForeignKey = {
2
+ tableFrom: string;
3
+ columnsFrom: string[];
4
+ tableTo: string;
5
+ columnsTo: string[];
6
+ };
@@ -0,0 +1,6 @@
1
+ export type PrimaryKey = {
2
+ table: string;
3
+ column: string;
4
+ name: string;
5
+ ordinal: number;
6
+ };
@@ -0,0 +1,8 @@
1
+ export type UniqueConstraint = {
2
+ table: string;
3
+ definition: string;
4
+ name: string;
5
+ columns: string[];
6
+ where?: string;
7
+ nullsNotDistinct?: boolean;
8
+ };
@@ -0,0 +1,17 @@
1
+ import prettier from "prettier";
2
+
3
+ export const format = async (source: string) => {
4
+ try {
5
+ return await prettier.format(source, {
6
+ parser: "typescript",
7
+ printWidth: 80,
8
+ tabWidth: 4,
9
+ trailingComma: "all",
10
+ singleQuote: false,
11
+ semi: true,
12
+ });
13
+ } catch (e) {
14
+ console.error(e);
15
+ return source;
16
+ }
17
+ };
@@ -0,0 +1,7 @@
1
+ import { unquote } from "./unquote";
2
+
3
+ export const quote = (s: string) => {
4
+ // we unquote the string first to avoid
5
+ // double quoting
6
+ return JSON.stringify(unquote(s));
7
+ };
@@ -0,0 +1,9 @@
1
+ export const unquote = (s: string) => {
2
+ // if we're given an quoted string,
3
+ // strip the quotes
4
+ if (s.startsWith('"') && s.endsWith('"')) {
5
+ return s.substring(1, s.length - 1);
6
+ } else {
7
+ return s;
8
+ }
9
+ };
@@ -0,0 +1,39 @@
1
+ import { ModelName } from "../../schema/types/helpers/ModelName";
2
+ import { LooseModelDefinitions } from "../../schema/types/loose/LooseModelDefinitions";
3
+ import { LooseRelationsDefinitions } from "../../schema/types/loose/LooseRelationsDefinitions";
4
+ import { FindManyParams } from "../find/types/FindManyParams";
5
+ import { FindOneParams } from "../find/types/FindOneParams";
6
+
7
+ export type IncludeClause<
8
+ Models extends LooseModelDefinitions,
9
+ Relations extends LooseRelationsDefinitions<Models>,
10
+ M extends ModelName<Models>,
11
+ > = {
12
+ [R in Extract<keyof Relations[M], string>]?: Relations[M][R] extends {
13
+ model: ModelName<Models>;
14
+ }
15
+ ? Relations[M][R] extends { type: "N:1" }
16
+ ? Omit<
17
+ FindOneParams<Models, Relations, Relations[M][R]["model"]>,
18
+ // for can only be applied to the top level query
19
+ "for"
20
+ >
21
+ : Relations[M][R] extends { type: "1:N" }
22
+ ? Omit<
23
+ FindManyParams<Models, Relations, Relations[M][R]["model"]>,
24
+ // for can only be applied to the top level query
25
+ "for"
26
+ >
27
+ : Relations[M][R] extends { type: "N:N" }
28
+ ? Omit<
29
+ FindManyParams<
30
+ Models,
31
+ Relations,
32
+ Relations[M][R]["model"]
33
+ >,
34
+ // for can only be applied to the top level query
35
+ "for"
36
+ >
37
+ : never
38
+ : never;
39
+ };
@@ -0,0 +1,4 @@
1
+ export type LateralByClause = {
2
+ column: string;
3
+ values: unknown[];
4
+ }[];
@@ -0,0 +1,7 @@
1
+ import { ColumnName } from "../../schema/types/helpers/ColumnName";
2
+ import { LooseModelDefinition } from "../../schema/types/loose/LooseModelDefinition";
3
+ import { NonEmptyArray } from "../../types/util/NonEmptyArray";
4
+
5
+ export type ReturningClause<Model extends LooseModelDefinition> = NonEmptyArray<
6
+ ColumnName<Model>
7
+ >;
@@ -0,0 +1,7 @@
1
+ import { ColumnName } from "../../schema/types/helpers/ColumnName";
2
+ import { LooseModelDefinition } from "../../schema/types/loose/LooseModelDefinition";
3
+ import { NonEmptyArray } from "../../types/util/NonEmptyArray";
4
+
5
+ export type SelectClause<Model extends LooseModelDefinition> = NonEmptyArray<
6
+ ColumnName<Model>
7
+ >;
@@ -0,0 +1,16 @@
1
+ import { ColumnName } from "../../schema/types/helpers/ColumnName";
2
+ import { ModelName } from "../../schema/types/helpers/ModelName";
3
+ import { LooseModelDefinitions } from "../../schema/types/loose/LooseModelDefinitions";
4
+ import { $and, $not, $or } from "../clauses/where/operators";
5
+ import { WhereClauseValue } from "./where/types/WhereClauseValue";
6
+
7
+ export type WhereClause<
8
+ Models extends LooseModelDefinitions,
9
+ M extends ModelName<Models>,
10
+ > = {
11
+ [C in ColumnName<Models[M]>]?: WhereClauseValue<Models, M, C>;
12
+ } & {
13
+ [$and]?: WhereClause<Models, M>[];
14
+ [$or]?: WhereClause<Models, M>[];
15
+ [$not]?: WhereClause<Models, M>;
16
+ };
@@ -0,0 +1,18 @@
1
+ import { ColumnName } from "../../../schema/types/helpers/ColumnName";
2
+ import { HasDefault } from "../../../schema/types/helpers/HasDefault";
3
+ import { IsNullable } from "../../../schema/types/helpers/IsNullable";
4
+ import { IsProvided } from "../../../schema/types/helpers/IsProvided";
5
+ import { IsSerial } from "../../../schema/types/helpers/IsSerial";
6
+ import { LooseModelDefinition } from "../../../schema/types/loose/LooseModelDefinition";
7
+
8
+ export type OptionalColumn<Model extends LooseModelDefinition> = {
9
+ [C in ColumnName<Model>]: IsNullable<Model, C> extends true
10
+ ? C
11
+ : IsSerial<Model, C> extends true
12
+ ? C
13
+ : HasDefault<Model, C> extends true
14
+ ? C
15
+ : IsProvided<Model, C> extends true
16
+ ? C
17
+ : never;
18
+ }[ColumnName<Model>];