@casekit/orm2 0.0.0-20250322230249

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 (275) hide show
  1. package/build/builders/buildCount.d.ts +23 -0
  2. package/build/builders/buildCount.js +63 -0
  3. package/build/builders/buildCount.test.d.ts +1 -0
  4. package/build/builders/buildCount.test.js +144 -0
  5. package/build/builders/buildCreate.d.ts +5 -0
  6. package/build/builders/buildCreate.js +28 -0
  7. package/build/builders/buildCreate.test.d.ts +1 -0
  8. package/build/builders/buildCreate.test.js +47 -0
  9. package/build/builders/buildDelete.d.ts +5 -0
  10. package/build/builders/buildDelete.js +28 -0
  11. package/build/builders/buildDelete.test.d.ts +1 -0
  12. package/build/builders/buildDelete.test.js +108 -0
  13. package/build/builders/buildFind.d.ts +8 -0
  14. package/build/builders/buildFind.js +185 -0
  15. package/build/builders/buildFind.test.d.ts +1 -0
  16. package/build/builders/buildFind.test.js +263 -0
  17. package/build/builders/buildUpdate.d.ts +5 -0
  18. package/build/builders/buildUpdate.js +34 -0
  19. package/build/builders/buildUpdate.test.d.ts +1 -0
  20. package/build/builders/buildUpdate.test.js +130 -0
  21. package/build/builders/buildWhere.d.ts +6 -0
  22. package/build/builders/buildWhere.js +63 -0
  23. package/build/builders/buildWhere.test.d.ts +1 -0
  24. package/build/builders/buildWhere.test.js +154 -0
  25. package/build/builders/types.d.ts +87 -0
  26. package/build/builders/types.js +1 -0
  27. package/build/connection.d.ts +31 -0
  28. package/build/connection.js +206 -0
  29. package/build/index.d.ts +10 -0
  30. package/build/index.js +5 -0
  31. package/build/operators.d.ts +59 -0
  32. package/build/operators.js +44 -0
  33. package/build/orm.count.d.ts +14 -0
  34. package/build/orm.count.js +22 -0
  35. package/build/orm.createMany.d.ts +5 -0
  36. package/build/orm.createMany.js +26 -0
  37. package/build/orm.createOne.d.ts +5 -0
  38. package/build/orm.createOne.js +34 -0
  39. package/build/orm.d.ts +81 -0
  40. package/build/orm.deleteMany.d.ts +5 -0
  41. package/build/orm.deleteMany.js +26 -0
  42. package/build/orm.deleteOne.d.ts +5 -0
  43. package/build/orm.deleteOne.js +32 -0
  44. package/build/orm.findMany.d.ts +8 -0
  45. package/build/orm.findMany.js +64 -0
  46. package/build/orm.findOne.d.ts +5 -0
  47. package/build/orm.findOne.js +20 -0
  48. package/build/orm.js +243 -0
  49. package/build/orm.restrict.d.ts +6 -0
  50. package/build/orm.restrict.js +52 -0
  51. package/build/orm.updateMany.d.ts +5 -0
  52. package/build/orm.updateMany.js +29 -0
  53. package/build/orm.updateOne.d.ts +5 -0
  54. package/build/orm.updateOne.js +35 -0
  55. package/build/sql/countToSql.d.ts +3 -0
  56. package/build/sql/countToSql.js +11 -0
  57. package/build/sql/countToSql.test.d.ts +1 -0
  58. package/build/sql/countToSql.test.js +218 -0
  59. package/build/sql/createToSql.d.ts +3 -0
  60. package/build/sql/createToSql.js +27 -0
  61. package/build/sql/createToSql.test.d.ts +1 -0
  62. package/build/sql/createToSql.test.js +186 -0
  63. package/build/sql/deleteToSql.d.ts +3 -0
  64. package/build/sql/deleteToSql.js +15 -0
  65. package/build/sql/deleteToSql.test.d.ts +1 -0
  66. package/build/sql/deleteToSql.test.js +93 -0
  67. package/build/sql/findToSql.d.ts +3 -0
  68. package/build/sql/findToSql.js +33 -0
  69. package/build/sql/findToSql.test.d.ts +1 -0
  70. package/build/sql/findToSql.test.js +409 -0
  71. package/build/sql/updateToSql.d.ts +3 -0
  72. package/build/sql/updateToSql.js +16 -0
  73. package/build/sql/updateToSql.test.d.ts +1 -0
  74. package/build/sql/updateToSql.test.js +165 -0
  75. package/build/sql/util.d.ts +11 -0
  76. package/build/sql/util.js +36 -0
  77. package/build/sql/util.test.d.ts +1 -0
  78. package/build/sql/util.test.js +163 -0
  79. package/build/tests/connection.test.d.ts +1 -0
  80. package/build/tests/connection.test.js +304 -0
  81. package/build/tests/datatypes.test.d.ts +1 -0
  82. package/build/tests/datatypes.test.js +239 -0
  83. package/build/tests/operators.test.d.ts +1 -0
  84. package/build/tests/operators.test.js +125 -0
  85. package/build/tests/orm.count.middleware.test.d.ts +1 -0
  86. package/build/tests/orm.count.middleware.test.js +132 -0
  87. package/build/tests/orm.count.test-d.d.ts +1 -0
  88. package/build/tests/orm.count.test-d.js +60 -0
  89. package/build/tests/orm.count.test.d.ts +1 -0
  90. package/build/tests/orm.count.test.js +151 -0
  91. package/build/tests/orm.createMany.middleware.test.d.ts +1 -0
  92. package/build/tests/orm.createMany.middleware.test.js +63 -0
  93. package/build/tests/orm.createMany.test-d.d.ts +1 -0
  94. package/build/tests/orm.createMany.test-d.js +131 -0
  95. package/build/tests/orm.createMany.test.d.ts +1 -0
  96. package/build/tests/orm.createMany.test.js +392 -0
  97. package/build/tests/orm.createOne.middleware.test.d.ts +1 -0
  98. package/build/tests/orm.createOne.middleware.test.js +54 -0
  99. package/build/tests/orm.createOne.test-d.d.ts +1 -0
  100. package/build/tests/orm.createOne.test-d.js +113 -0
  101. package/build/tests/orm.createOne.test.d.ts +1 -0
  102. package/build/tests/orm.createOne.test.js +268 -0
  103. package/build/tests/orm.deleteMany.middleware.test.d.ts +1 -0
  104. package/build/tests/orm.deleteMany.middleware.test.js +77 -0
  105. package/build/tests/orm.deleteMany.test-d.d.ts +1 -0
  106. package/build/tests/orm.deleteMany.test-d.js +179 -0
  107. package/build/tests/orm.deleteMany.test.d.ts +1 -0
  108. package/build/tests/orm.deleteMany.test.js +394 -0
  109. package/build/tests/orm.deleteOne.middleware.test.d.ts +1 -0
  110. package/build/tests/orm.deleteOne.middleware.test.js +61 -0
  111. package/build/tests/orm.deleteOne.test-d.d.ts +1 -0
  112. package/build/tests/orm.deleteOne.test-d.js +179 -0
  113. package/build/tests/orm.deleteOne.test.d.ts +1 -0
  114. package/build/tests/orm.deleteOne.test.js +360 -0
  115. package/build/tests/orm.findMany.includeManyToMany.test.d.ts +1 -0
  116. package/build/tests/orm.findMany.includeManyToMany.test.js +335 -0
  117. package/build/tests/orm.findMany.includeManyToOne.test.d.ts +1 -0
  118. package/build/tests/orm.findMany.includeManyToOne.test.js +286 -0
  119. package/build/tests/orm.findMany.includeOneToMany.test.d.ts +1 -0
  120. package/build/tests/orm.findMany.includeOneToMany.test.js +530 -0
  121. package/build/tests/orm.findMany.middleware.test.d.ts +1 -0
  122. package/build/tests/orm.findMany.middleware.test.js +66 -0
  123. package/build/tests/orm.findMany.offsetLimit.test.d.ts +1 -0
  124. package/build/tests/orm.findMany.offsetLimit.test.js +108 -0
  125. package/build/tests/orm.findMany.orderBy.test.d.ts +1 -0
  126. package/build/tests/orm.findMany.orderBy.test.js +304 -0
  127. package/build/tests/orm.findMany.select.test.d.ts +1 -0
  128. package/build/tests/orm.findMany.select.test.js +278 -0
  129. package/build/tests/orm.findMany.test-d.d.ts +1 -0
  130. package/build/tests/orm.findMany.test-d.js +374 -0
  131. package/build/tests/orm.findMany.where.test.d.ts +1 -0
  132. package/build/tests/orm.findMany.where.test.js +383 -0
  133. package/build/tests/orm.findOne.middleware.test.d.ts +1 -0
  134. package/build/tests/orm.findOne.middleware.test.js +57 -0
  135. package/build/tests/orm.findOne.test-d.d.ts +1 -0
  136. package/build/tests/orm.findOne.test-d.js +377 -0
  137. package/build/tests/orm.findOne.test.d.ts +1 -0
  138. package/build/tests/orm.findOne.test.js +247 -0
  139. package/build/tests/orm.restrict.test-d.d.ts +1 -0
  140. package/build/tests/orm.restrict.test-d.js +105 -0
  141. package/build/tests/orm.restrict.test.d.ts +1 -0
  142. package/build/tests/orm.restrict.test.js +259 -0
  143. package/build/tests/orm.transact.test.d.ts +1 -0
  144. package/build/tests/orm.transact.test.js +48 -0
  145. package/build/tests/orm.updateMany.middleware.test.d.ts +1 -0
  146. package/build/tests/orm.updateMany.middleware.test.js +72 -0
  147. package/build/tests/orm.updateMany.test.d.ts +1 -0
  148. package/build/tests/orm.updateMany.test.js +210 -0
  149. package/build/tests/orm.updateOne.middleware.test.d.ts +1 -0
  150. package/build/tests/orm.updateOne.middleware.test.js +62 -0
  151. package/build/tests/orm.updateOne.test.d.ts +1 -0
  152. package/build/tests/orm.updateOne.test.js +209 -0
  153. package/build/tests/util/db.d.ts +1571 -0
  154. package/build/tests/util/db.js +10 -0
  155. package/build/tests/util/logger.d.ts +19 -0
  156. package/build/tests/util/logger.js +40 -0
  157. package/build/types/BaseFindParams.d.ts +1 -0
  158. package/build/types/BaseFindParams.js +1 -0
  159. package/build/types/CountParams.d.ts +16 -0
  160. package/build/types/CountParams.js +1 -0
  161. package/build/types/CountParams.test-d.d.ts +1 -0
  162. package/build/types/CountParams.test-d.js +89 -0
  163. package/build/types/CreateManyParams.d.ts +11 -0
  164. package/build/types/CreateManyParams.js +1 -0
  165. package/build/types/CreateManyParams.test-d.d.ts +1 -0
  166. package/build/types/CreateManyParams.test-d.js +83 -0
  167. package/build/types/CreateManyResult.d.ts +5 -0
  168. package/build/types/CreateManyResult.js +1 -0
  169. package/build/types/CreateManyResult.test-d.d.ts +1 -0
  170. package/build/types/CreateManyResult.test-d.js +22 -0
  171. package/build/types/CreateOneParams.d.ts +11 -0
  172. package/build/types/CreateOneParams.js +1 -0
  173. package/build/types/CreateOneParams.test-d.d.ts +1 -0
  174. package/build/types/CreateOneParams.test-d.js +56 -0
  175. package/build/types/CreateOneResult.d.ts +5 -0
  176. package/build/types/CreateOneResult.js +1 -0
  177. package/build/types/CreateOneResult.test-d.d.ts +1 -0
  178. package/build/types/CreateOneResult.test-d.js +23 -0
  179. package/build/types/CreateValues.d.ts +6 -0
  180. package/build/types/CreateValues.js +1 -0
  181. package/build/types/CreateValues.test-d.d.ts +1 -0
  182. package/build/types/CreateValues.test-d.js +60 -0
  183. package/build/types/DeleteManyResult.d.ts +5 -0
  184. package/build/types/DeleteManyResult.js +1 -0
  185. package/build/types/DeleteManyResult.test-d.d.ts +1 -0
  186. package/build/types/DeleteManyResult.test-d.js +23 -0
  187. package/build/types/DeleteOneResult.d.ts +5 -0
  188. package/build/types/DeleteOneResult.js +1 -0
  189. package/build/types/DeleteOneResult.test-d.d.ts +1 -0
  190. package/build/types/DeleteOneResult.test-d.js +23 -0
  191. package/build/types/DeleteParams.d.ts +7 -0
  192. package/build/types/DeleteParams.js +1 -0
  193. package/build/types/DeleteParams.test-d.d.ts +1 -0
  194. package/build/types/DeleteParams.test-d.js +46 -0
  195. package/build/types/FindParams.d.ts +22 -0
  196. package/build/types/FindParams.js +1 -0
  197. package/build/types/FindParams.test-d.d.ts +1 -0
  198. package/build/types/FindParams.test-d.js +107 -0
  199. package/build/types/FindResult.d.ts +13 -0
  200. package/build/types/FindResult.js +1 -0
  201. package/build/types/FindResult.test-d.d.ts +1 -0
  202. package/build/types/FindResult.test-d.js +30 -0
  203. package/build/types/IncludeClause.d.ts +5 -0
  204. package/build/types/IncludeClause.js +1 -0
  205. package/build/types/IncludeClause.test-d.d.ts +1 -0
  206. package/build/types/IncludeClause.test-d.js +69 -0
  207. package/build/types/Middleware.d.ts +51 -0
  208. package/build/types/Middleware.js +37 -0
  209. package/build/types/OptionalValues.d.ts +4 -0
  210. package/build/types/OptionalValues.js +1 -0
  211. package/build/types/OptionalValues.test-d.d.ts +1 -0
  212. package/build/types/OptionalValues.test-d.js +12 -0
  213. package/build/types/OrderByClause.d.ts +9 -0
  214. package/build/types/OrderByClause.js +1 -0
  215. package/build/types/OrderByClause.test-d.d.ts +1 -0
  216. package/build/types/OrderByClause.test-d.js +44 -0
  217. package/build/types/RequiredValues.d.ts +4 -0
  218. package/build/types/RequiredValues.js +1 -0
  219. package/build/types/RequiredValues.test-d.d.ts +1 -0
  220. package/build/types/RequiredValues.test-d.js +22 -0
  221. package/build/types/RestrictModels.d.ts +23 -0
  222. package/build/types/RestrictModels.js +1 -0
  223. package/build/types/RestrictModels.test-d.d.ts +1 -0
  224. package/build/types/RestrictModels.test-d.js +44 -0
  225. package/build/types/ReturningClause.d.ts +3 -0
  226. package/build/types/ReturningClause.js +1 -0
  227. package/build/types/ReturningClause.test-d.d.ts +1 -0
  228. package/build/types/ReturningClause.test-d.js +15 -0
  229. package/build/types/SelectClause.d.ts +3 -0
  230. package/build/types/SelectClause.js +1 -0
  231. package/build/types/SelectClause.test-d.d.ts +1 -0
  232. package/build/types/SelectClause.test-d.js +15 -0
  233. package/build/types/UpdateManyResult.d.ts +5 -0
  234. package/build/types/UpdateManyResult.js +1 -0
  235. package/build/types/UpdateManyResult.test-d.d.ts +1 -0
  236. package/build/types/UpdateManyResult.test-d.js +15 -0
  237. package/build/types/UpdateOneResult.d.ts +5 -0
  238. package/build/types/UpdateOneResult.js +1 -0
  239. package/build/types/UpdateOneResult.test-d.d.ts +1 -0
  240. package/build/types/UpdateOneResult.test-d.js +15 -0
  241. package/build/types/UpdateParams.d.ts +9 -0
  242. package/build/types/UpdateParams.js +1 -0
  243. package/build/types/UpdateParams.test-d.d.ts +1 -0
  244. package/build/types/UpdateParams.test-d.js +29 -0
  245. package/build/types/UpdateValues.d.ts +6 -0
  246. package/build/types/UpdateValues.js +1 -0
  247. package/build/types/UpdateValues.test-d.d.ts +1 -0
  248. package/build/types/UpdateValues.test-d.js +33 -0
  249. package/build/types/WhereClause.d.ts +29 -0
  250. package/build/types/WhereClause.js +1 -0
  251. package/build/types/WhereClause.test-d.d.ts +1 -0
  252. package/build/types/WhereClause.test-d.js +137 -0
  253. package/build/util/getIncludedToManySubqueries.d.ts +12 -0
  254. package/build/util/getIncludedToManySubqueries.js +63 -0
  255. package/build/util/getIncludedToManySubqueries.test.d.ts +1 -0
  256. package/build/util/getIncludedToManySubqueries.test.js +121 -0
  257. package/build/util/getLateralJoinValues.d.ts +4 -0
  258. package/build/util/getLateralJoinValues.js +30 -0
  259. package/build/util/getLateralJoinValues.test.d.ts +1 -0
  260. package/build/util/getLateralJoinValues.test.js +99 -0
  261. package/build/util/hasClauses.d.ts +1 -0
  262. package/build/util/hasClauses.js +9 -0
  263. package/build/util/hasClauses.test.d.ts +1 -0
  264. package/build/util/hasClauses.test.js +15 -0
  265. package/build/util/makeTableAlias.d.ts +1 -0
  266. package/build/util/makeTableAlias.js +10 -0
  267. package/build/util/makeTableAlias.test.d.ts +1 -0
  268. package/build/util/makeTableAlias.test.js +18 -0
  269. package/build/util/resultSchema.d.ts +10 -0
  270. package/build/util/resultSchema.js +42 -0
  271. package/build/util/rowToObject.d.ts +8 -0
  272. package/build/util/rowToObject.js +10 -0
  273. package/build/util/rowToObject.test.d.ts +1 -0
  274. package/build/util/rowToObject.test.js +68 -0
  275. package/package.json +67 -0
@@ -0,0 +1,5 @@
1
+ import { FieldType, ModelDefinitions, ModelName, OperatorDefinitions } from "@casekit/orm2-schema";
2
+ import { UpdateParams } from "./UpdateParams.js";
3
+ export type UpdateOneResult<Models extends ModelDefinitions, Operators extends OperatorDefinitions, M extends ModelName<Models>, Q extends UpdateParams<Models, Operators, M>> = Q["returning"] extends NonNullable<Q["returning"]> ? {
4
+ [C in Q["returning"][number]]: FieldType<Models[M], C>;
5
+ } : number;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,15 @@
1
+ import { describe, expectTypeOf, test } from "vitest";
2
+ describe("UpdateOneResult", () => {
3
+ test("returns count when no returning clause", () => {
4
+ expectTypeOf().toEqualTypeOf();
5
+ });
6
+ test("returns array of selected fields when returning clause present", () => {
7
+ expectTypeOf().toEqualTypeOf();
8
+ });
9
+ test("handles complex field types", () => {
10
+ expectTypeOf().toEqualTypeOf();
11
+ });
12
+ test("does not allow invalid field names in returning clause", () => {
13
+ let _;
14
+ });
15
+ });
@@ -0,0 +1,9 @@
1
+ import { ModelDefinitions, ModelName, OperatorDefinitions } from "@casekit/orm2-schema";
2
+ import { ReturningClause } from "./ReturningClause.js";
3
+ import { UpdateValues } from "./UpdateValues.js";
4
+ import { WhereClause } from "./WhereClause.js";
5
+ export type UpdateParams<Models extends ModelDefinitions, Operators extends OperatorDefinitions, M extends ModelName<Models>> = {
6
+ set: UpdateValues<Models[M]>;
7
+ where: WhereClause<Models, Operators, M>;
8
+ returning?: ReturningClause<Models[M]>;
9
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,29 @@
1
+ import { describe, test } from "vitest";
2
+ describe("UpdateParams", () => {
3
+ test("basic structure", () => {
4
+ const _ = {
5
+ set: { title: "hello world" },
6
+ where: { authorId: 1 },
7
+ returning: ["id", "title"],
8
+ };
9
+ });
10
+ test("returning clause is optional", () => {
11
+ const _ = {
12
+ set: { content: "hello" },
13
+ where: { id: 1 },
14
+ };
15
+ });
16
+ test("handles complex field types", () => {
17
+ const _ = {
18
+ set: { tags: ["hello", "world"], deletedAt: null },
19
+ where: { title: "hello" },
20
+ returning: ["id", "tags", "deletedAt"],
21
+ };
22
+ });
23
+ test("rejects invalid field names", () => {
24
+ const _ = {
25
+ // @ts-expect-error invalid field
26
+ invalid: "wrong",
27
+ };
28
+ });
29
+ });
@@ -0,0 +1,6 @@
1
+ import { DeepReadonly } from "ts-essentials";
2
+ import { FieldName, FieldType, ModelDefinition } from "@casekit/orm2-schema";
3
+ import { Simplify } from "@casekit/toolbox";
4
+ export type UpdateValues<Model extends ModelDefinition> = DeepReadonly<Simplify<{
5
+ [K in FieldName<Model>]?: FieldType<Model, K>;
6
+ }>>;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,33 @@
1
+ import { describe, test } from "vitest";
2
+ describe("UpdateValues", () => {
3
+ test("all fields are optional", () => {
4
+ const _ = {};
5
+ });
6
+ test("handles nullable fields", () => {
7
+ const _ = {
8
+ backgroundColorValue: null,
9
+ };
10
+ });
11
+ test("handles arrays", () => {
12
+ const _ = {
13
+ tags: ["a", "b", "c"],
14
+ };
15
+ });
16
+ test("handles jsonb fields", () => {
17
+ const _ = {
18
+ metadata: {
19
+ foo: "a",
20
+ bar: [
21
+ { baz: "good", quux: true },
22
+ { baz: "bad", quux: false },
23
+ ],
24
+ },
25
+ };
26
+ });
27
+ test("rejects invalid field names", () => {
28
+ const _ = {
29
+ // @ts-expect-error invalid field
30
+ invalid: "wrong",
31
+ };
32
+ });
33
+ });
@@ -0,0 +1,29 @@
1
+ import { DeepReadonly } from "ts-essentials";
2
+ import { FieldName, FieldType, ModelDefinition, ModelDefinitions, ModelName, OperatorDefinitions } from "@casekit/orm2-schema";
3
+ import { $and, $eq, $gt, $gte, $ilike, $in, $is, $like, $lt, $lte, $ne, $not, $or } from "#operators.js";
4
+ export interface DefaultOperators<T> {
5
+ [$eq]?: T;
6
+ [$ne]?: T;
7
+ [$not]?: null | true | false;
8
+ [$is]?: null | true | false;
9
+ [$gt]?: T;
10
+ [$gte]?: T;
11
+ [$lt]?: T;
12
+ [$lte]?: T;
13
+ [$in]?: T[];
14
+ [$like]?: string;
15
+ [$ilike]?: string;
16
+ }
17
+ export type ReadonlyArrays<T> = T extends (infer E)[] ? readonly E[] : T extends Date ? Date : T extends object ? {
18
+ readonly [K in keyof T]: ReadonlyArrays<T[K]>;
19
+ } : T;
20
+ export type WhereClauseValue<Model extends ModelDefinition, Operators extends OperatorDefinitions, C extends FieldName<Model>, T = DeepReadonly<FieldType<Model, C>>> = T | null | DefaultOperators<T> | (Operators["where"] extends never ? never : {
21
+ [K in Extract<keyof Operators["where"], symbol>]?: Parameters<NonNullable<Operators["where"]>[K]>[1];
22
+ });
23
+ export type WhereClause<Models extends ModelDefinitions, Operators extends OperatorDefinitions, M extends ModelName<Models>, _Model extends ModelDefinition = Models[M]> = {
24
+ [C in FieldName<_Model>]?: WhereClauseValue<_Model, Operators, C>;
25
+ } & {
26
+ [$and]?: Array<WhereClause<Models, Operators, M>>;
27
+ [$or]?: Array<WhereClause<Models, Operators, M>>;
28
+ [$not]?: WhereClause<Models, Operators, M>;
29
+ };
@@ -0,0 +1 @@
1
+ import { $and, $eq, $gt, $gte, $ilike, $in, $is, $like, $lt, $lte, $ne, $not, $or, } from "#operators.js";
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,137 @@
1
+ import { describe, test } from "vitest";
2
+ import { sql } from "@casekit/sql";
3
+ import { $and, $gt, $gte, $ilike, $in, $is, $like, $lt, $not, $or, } from "#operators.js";
4
+ describe("WhereClause", () => {
5
+ test("basic value comparisons", () => {
6
+ const _ = {
7
+ name: "John",
8
+ createdAt: { [$gt]: new Date("2000-01-01") },
9
+ };
10
+ });
11
+ test("logical operators", () => {
12
+ const _ = {
13
+ [$or]: [
14
+ { name: { [$like]: "John%" } },
15
+ {
16
+ [$and]: [
17
+ { createdAt: { [$gte]: new Date("2000-01-01") } },
18
+ { createdAt: { [$lt]: new Date("2024-01-01") } },
19
+ ],
20
+ },
21
+ ],
22
+ };
23
+ });
24
+ test("null checks with $is", () => {
25
+ const _ = {
26
+ name: { [$is]: null },
27
+ createdAt: { [$is]: null },
28
+ };
29
+ });
30
+ test("null checks with $not", () => {
31
+ const _ = {
32
+ name: { [$is]: null },
33
+ createdAt: { [$not]: null },
34
+ };
35
+ });
36
+ test("boolean checks with $is", () => {
37
+ const _ = {
38
+ name: { [$is]: null },
39
+ createdAt: { [$is]: false },
40
+ };
41
+ });
42
+ test("boolean checks with $not", () => {
43
+ const _ = {
44
+ name: { [$is]: null },
45
+ createdAt: { [$not]: true },
46
+ };
47
+ });
48
+ test("string operations", () => {
49
+ const _ = {
50
+ name: {
51
+ [$like]: "%doe%",
52
+ [$ilike]: "JOHN%",
53
+ },
54
+ email: { [$in]: ["john@example.com", "jane@example.com"] },
55
+ };
56
+ });
57
+ test("compound conditions", () => {
58
+ const _ = {
59
+ [$and]: [
60
+ {
61
+ createdAt: {
62
+ [$gte]: new Date("2000-01-01"),
63
+ [$lt]: new Date("2024-01-01"),
64
+ },
65
+ },
66
+ {
67
+ [$or]: [
68
+ { email: { [$like]: "%@company.com" } },
69
+ { role: { [$in]: ["admin", "user"] } },
70
+ ],
71
+ },
72
+ ],
73
+ };
74
+ });
75
+ test("custom operators", () => {
76
+ const $startswith = Symbol("startswith");
77
+ const operators = {
78
+ where: {
79
+ [$startswith]: ({ table, column }, value) => sql `${table}.${column} LIKE ${value}%`,
80
+ },
81
+ };
82
+ const _ = {
83
+ name: { [$startswith]: "John" },
84
+ };
85
+ });
86
+ describe("invalid queries", () => {
87
+ test("wrong type", () => {
88
+ const _ = {
89
+ // @ts-expect-error name should be string
90
+ name: 621,
91
+ };
92
+ });
93
+ test("non existent column", () => {
94
+ const _ = {
95
+ // @ts-expect-error non existent column
96
+ wrong: "x",
97
+ };
98
+ });
99
+ test("invalid $in value", () => {
100
+ const _ = {
101
+ role: {
102
+ // @ts-expect-error invalid $in value
103
+ [$in]: ["admin", "user", "wrong"],
104
+ },
105
+ };
106
+ });
107
+ test("invalid $is value", () => {
108
+ const _ = {
109
+ name: {
110
+ // @ts-expect-error $is only works with null or boolean
111
+ [$is]: "test",
112
+ },
113
+ };
114
+ });
115
+ test("invalid $like value", () => {
116
+ const _ = {
117
+ counter: {
118
+ // @ts-expect-error $like only works with string
119
+ [$like]: 123,
120
+ },
121
+ };
122
+ });
123
+ test("invalid $and value", () => {
124
+ const _ = {
125
+ [$and]:
126
+ // @ts-expect-error should be an array
127
+ { createdAt: { [$gt]: Date } },
128
+ };
129
+ });
130
+ test("invalid $not value", () => {
131
+ const _ = {
132
+ // @ts-expect-error $not should take an object, not an array
133
+ [$not]: [{ age: 21 }],
134
+ };
135
+ });
136
+ });
137
+ });
@@ -0,0 +1,12 @@
1
+ import { NormalizedConfig } from "@casekit/orm2-config";
2
+ import { ModelDefinition, OperatorDefinitions } from "@casekit/orm2-schema";
3
+ import { FindParams } from "../types/FindParams.js";
4
+ export type ToManyRelationSubquery = {
5
+ query: FindParams<Record<string, Required<ModelDefinition>>, OperatorDefinitions, string>;
6
+ modelName: string;
7
+ from: string[];
8
+ to: string[];
9
+ path: string[];
10
+ extract: (rows: Record<string, unknown>[]) => unknown[];
11
+ };
12
+ export declare const getIncludedToManySubqueries: (config: NormalizedConfig, modelName: string, query: FindParams<Record<string, Required<ModelDefinition>>, OperatorDefinitions, string>, path?: string[]) => ToManyRelationSubquery[];
@@ -0,0 +1,63 @@
1
+ import { identity } from "es-toolkit";
2
+ import { getModel, getRelation } from "@casekit/orm2-config";
3
+ export const getIncludedToManySubqueries = (config, modelName, query, path = []) => {
4
+ const relationQueries = [];
5
+ for (const [relationName, subquery] of Object.entries(query.include ?? {})) {
6
+ const model = getModel(config.models, modelName);
7
+ const relation = getRelation(model, relationName);
8
+ if (relation.type === "1:N") {
9
+ relationQueries.push({
10
+ modelName: relation.model,
11
+ query: subquery,
12
+ from: relation.from.fields,
13
+ to: relation.to.fields,
14
+ extract: identity,
15
+ path: [...path, relation.name],
16
+ });
17
+ }
18
+ else if (relation.type === "N:N") {
19
+ // for N:N relations, we create a lateral join from the parent model
20
+ // to the join model via its 'fromRelation', rather than directly tothe relation
21
+ // model itself.
22
+ const throughModel = getModel(config.models, relation.through.model);
23
+ const fromRelation = getRelation(throughModel, relation.through.fromRelation);
24
+ if (fromRelation.type !== "N:1") {
25
+ throw new Error(`Expected relation ${relation.through.fromRelation} to be a N:1 relation, but got ${fromRelation.type}`);
26
+ }
27
+ const toRelation = getRelation(throughModel, relation.through.toRelation);
28
+ if (toRelation.type !== "N:1") {
29
+ throw new Error(`Expected relation ${relation.through.toRelation} to be a N:1 relation, but got ${toRelation.type}`);
30
+ }
31
+ const from = fromRelation.to.fields;
32
+ const to = fromRelation.from.fields;
33
+ // instead of querying directly for the relation model,
34
+ // we wrap the query in a query of the join model and
35
+ // include the query of the relation model as a 1:N
36
+ // subquery
37
+ const query = {
38
+ select: throughModel.primaryKey.map((k) => k.field),
39
+ include: {
40
+ [toRelation.name]: subquery,
41
+ },
42
+ };
43
+ // because we're going to be running a query on the join model
44
+ // rather than directly on the relation model, the results
45
+ // will be for the join model, so we need to map over them
46
+ // and extract the embedded relation model results.
47
+ const extract = (rows) => rows.map((r) => r[toRelation.name]);
48
+ relationQueries.push({
49
+ modelName: relation.through.model,
50
+ query,
51
+ path: [...path, relation.name],
52
+ from,
53
+ to,
54
+ extract,
55
+ });
56
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
57
+ }
58
+ else if (relation.type === "N:1") {
59
+ relationQueries.push(...getIncludedToManySubqueries(config, relation.model, subquery, [...path, relation.name]));
60
+ }
61
+ }
62
+ return relationQueries;
63
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,121 @@
1
+ import { identity } from "es-toolkit";
2
+ import { describe, expect, test } from "vitest";
3
+ import { createTestDB } from "../tests/util/db.js";
4
+ import { getIncludedToManySubqueries } from "./getIncludedToManySubqueries.js";
5
+ describe("getIncludedToManySubqueries", () => {
6
+ const { db } = createTestDB();
7
+ test("returns empty array when no includes specified", () => {
8
+ const result = getIncludedToManySubqueries(db.config, "user", {
9
+ select: ["id", "name"],
10
+ });
11
+ expect(result).toEqual([]);
12
+ });
13
+ test("processes 1:N relations correctly", () => {
14
+ const result = getIncludedToManySubqueries(db.config, "user", {
15
+ select: ["id", "name"],
16
+ include: {
17
+ posts: {
18
+ select: ["id", "title"],
19
+ where: { deletedAt: null },
20
+ },
21
+ },
22
+ });
23
+ expect(result).toHaveLength(1);
24
+ expect(result[0]).toMatchObject({
25
+ modelName: "post",
26
+ from: ["id"],
27
+ to: ["authorId"],
28
+ path: ["posts"],
29
+ query: {
30
+ select: ["id", "title"],
31
+ where: { deletedAt: null },
32
+ },
33
+ });
34
+ expect(result[0].extract).toBe(identity);
35
+ });
36
+ test("processes N:N relations correctly", () => {
37
+ const result = getIncludedToManySubqueries(db.config, "user", {
38
+ select: ["id", "name"],
39
+ include: {
40
+ friends: {
41
+ select: ["id", "name"],
42
+ },
43
+ },
44
+ });
45
+ expect(result).toHaveLength(1);
46
+ expect(result[0]).toMatchObject({
47
+ modelName: "friendship",
48
+ from: ["id"],
49
+ to: ["userId"],
50
+ path: ["friends"],
51
+ query: {
52
+ select: ["userId", "friendId"],
53
+ include: {
54
+ friend: {
55
+ select: ["id", "name"],
56
+ },
57
+ },
58
+ },
59
+ });
60
+ const mockRows = [
61
+ { friend: { id: 1, name: "Friend 1" } },
62
+ { friend: { id: 2, name: "Friend 2" } },
63
+ ];
64
+ expect(result[0].extract(mockRows)).toEqual([
65
+ { id: 1, name: "Friend 1" },
66
+ { id: 2, name: "Friend 2" },
67
+ ]);
68
+ });
69
+ test("processes nested N:1 relations with to-many relations correctly", () => {
70
+ const result = getIncludedToManySubqueries(db.config, "like", {
71
+ select: ["id", "userId"],
72
+ include: {
73
+ post: {
74
+ select: ["id"],
75
+ include: {
76
+ likes: {
77
+ select: ["id", "userId"],
78
+ },
79
+ },
80
+ },
81
+ },
82
+ });
83
+ expect(result).toHaveLength(1);
84
+ expect(result[0]).toMatchObject({
85
+ modelName: "like",
86
+ path: ["post", "likes"],
87
+ query: {
88
+ select: ["id", "userId"],
89
+ },
90
+ });
91
+ });
92
+ test("handles multiple to-many relations at the same level", () => {
93
+ const result = getIncludedToManySubqueries(db.config, "post", {
94
+ select: ["id", "title"],
95
+ include: {
96
+ likes: {
97
+ select: ["id", "userId"],
98
+ },
99
+ author: {
100
+ select: ["id"],
101
+ include: {
102
+ posts: {
103
+ select: ["id", "title"],
104
+ },
105
+ },
106
+ },
107
+ },
108
+ });
109
+ expect(result).toHaveLength(2);
110
+ expect(result).toEqual(expect.arrayContaining([
111
+ expect.objectContaining({
112
+ modelName: "like",
113
+ path: ["likes"],
114
+ }),
115
+ expect.objectContaining({
116
+ modelName: "post",
117
+ path: ["author", "posts"],
118
+ }),
119
+ ]));
120
+ });
121
+ });
@@ -0,0 +1,4 @@
1
+ export declare const getLateralJoinValues: (results: Record<string, unknown>[], path: string[], from: string[], to: string[]) => {
2
+ field: string;
3
+ values: unknown[];
4
+ }[];
@@ -0,0 +1,30 @@
1
+ import { isEqual, isNil, uniqWith } from "es-toolkit";
2
+ import { get } from "es-toolkit/compat";
3
+ export const getLateralJoinValues = (results, path, from, to) => {
4
+ const all = results.flatMap((result) => {
5
+ const parent = path.length === 0
6
+ ? result
7
+ : get(result, path);
8
+ if (isNil(parent)) {
9
+ // if the relation is a join from an optional N:1 relation of
10
+ // the query's top level model, then the value being joined from
11
+ // make not exist on the result. in that case we can't do a lateral
12
+ // join so we ignore it.
13
+ return [];
14
+ }
15
+ return [
16
+ Object.fromEntries(to.map((f, index) => [f, parent[from[index]]])),
17
+ ];
18
+ });
19
+ const unique = uniqWith(all, isEqual);
20
+ const values = Object.fromEntries(to.map((f) => [f, []]));
21
+ for (const v of unique) {
22
+ for (const f of to) {
23
+ values[f].push(v[f]);
24
+ }
25
+ }
26
+ return to.map((f) => ({
27
+ field: f,
28
+ values: values[f] ?? [],
29
+ }));
30
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,99 @@
1
+ import { describe, expect, test } from "vitest";
2
+ import { getLateralJoinValues } from "./getLateralJoinValues.js";
3
+ describe("getLateralJoinValues", () => {
4
+ test("should handle flat objects with direct mapping", () => {
5
+ const results = [
6
+ { id: 1, name: "John" },
7
+ { id: 2, name: "Jane" },
8
+ { id: 2, name: "Jane" }, // Duplicate to test uniqueness
9
+ ];
10
+ const result = getLateralJoinValues(results, [], // empty path for top-level properties
11
+ ["id", "name"], ["userId", "userName"]);
12
+ expect(result).toEqual([
13
+ {
14
+ field: "userId",
15
+ values: [1, 2],
16
+ },
17
+ {
18
+ field: "userName",
19
+ values: ["John", "Jane"],
20
+ },
21
+ ]);
22
+ });
23
+ test("should handle nested objects with path", () => {
24
+ const results = [
25
+ { user: { id: 1, name: "John" } },
26
+ { user: { id: 2, name: "Jane" } },
27
+ ];
28
+ const result = getLateralJoinValues(results, ["user"], ["id", "name"], ["userId", "userName"]);
29
+ expect(result).toEqual([
30
+ {
31
+ field: "userId",
32
+ values: [1, 2],
33
+ },
34
+ {
35
+ field: "userName",
36
+ values: ["John", "Jane"],
37
+ },
38
+ ]);
39
+ });
40
+ test("should handle missing nested objects", () => {
41
+ const results = [
42
+ { user: { id: 1, name: "John" } },
43
+ { user: null },
44
+ { user: { id: 2, name: "Jane" } },
45
+ ];
46
+ const result = getLateralJoinValues(results, ["user"], ["id", "name"], ["userId", "userName"]);
47
+ expect(result).toEqual([
48
+ {
49
+ field: "userId",
50
+ values: [1, 2],
51
+ },
52
+ {
53
+ field: "userName",
54
+ values: ["John", "Jane"],
55
+ },
56
+ ]);
57
+ });
58
+ test(" handle deeply nested paths", () => {
59
+ const results = [
60
+ { data: { user: { profile: { id: 1, name: "John" } } } },
61
+ { data: { user: { profile: { id: 2, name: "Jane" } } } },
62
+ ];
63
+ const result = getLateralJoinValues(results, ["data", "user", "profile"], ["id", "name"], ["userId", "userName"]);
64
+ expect(result).toEqual([
65
+ {
66
+ field: "userId",
67
+ values: [1, 2],
68
+ },
69
+ {
70
+ field: "userName",
71
+ values: ["John", "Jane"],
72
+ },
73
+ ]);
74
+ });
75
+ test("should handle empty results array", () => {
76
+ const results = [];
77
+ const result = getLateralJoinValues(results, ["user"], ["id", "name"], ["userId", "userName"]);
78
+ expect(result).toEqual([
79
+ {
80
+ field: "userId",
81
+ values: [],
82
+ },
83
+ {
84
+ field: "userName",
85
+ values: [],
86
+ },
87
+ ]);
88
+ });
89
+ test("should handle single field mapping", () => {
90
+ const results = [{ id: 1 }, { id: 2 }];
91
+ const result = getLateralJoinValues(results, [], ["id"], ["userId"]);
92
+ expect(result).toEqual([
93
+ {
94
+ field: "userId",
95
+ values: [1, 2],
96
+ },
97
+ ]);
98
+ });
99
+ });
@@ -0,0 +1 @@
1
+ export declare const hasClauses: <T>(clause: T) => clause is NonNullable<T>;
@@ -0,0 +1,9 @@
1
+ export const hasClauses = (clause) => {
2
+ if (clause === null || clause === undefined || typeof clause !== "object")
3
+ return false;
4
+ const keys = [
5
+ ...Object.keys(clause),
6
+ ...Object.getOwnPropertySymbols(clause),
7
+ ];
8
+ return keys.length > 0;
9
+ };
@@ -0,0 +1 @@
1
+ export {};