@casekit/orm2 0.0.0-20250331181319 → 0.0.0

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 (94) hide show
  1. package/build/builders/buildCount.d.ts +2 -1
  2. package/build/builders/buildCount.js +5 -8
  3. package/build/builders/buildCount.test.js +5 -5
  4. package/build/builders/buildCreate.d.ts +2 -1
  5. package/build/builders/buildCreate.js +6 -3
  6. package/build/builders/buildCreate.test.js +4 -4
  7. package/build/builders/buildDelete.d.ts +2 -1
  8. package/build/builders/buildDelete.js +2 -2
  9. package/build/builders/buildDelete.test.js +8 -8
  10. package/build/builders/buildFind.d.ts +2 -1
  11. package/build/builders/buildFind.js +5 -8
  12. package/build/builders/buildFind.test.js +4 -4
  13. package/build/builders/buildUpdate.d.ts +2 -1
  14. package/build/builders/buildUpdate.js +6 -3
  15. package/build/builders/buildUpdate.test.js +8 -8
  16. package/build/builders/buildWhere.d.ts +2 -1
  17. package/build/builders/buildWhere.js +30 -8
  18. package/build/builders/buildWhere.test.js +39 -24
  19. package/build/connection.d.ts +2 -2
  20. package/build/index.d.ts +1 -1
  21. package/build/index.js +1 -1
  22. package/build/orm.count.d.ts +2 -10
  23. package/build/orm.count.js +2 -11
  24. package/build/orm.createMany.d.ts +2 -1
  25. package/build/orm.createMany.js +2 -2
  26. package/build/orm.createOne.d.ts +2 -1
  27. package/build/orm.createOne.js +2 -2
  28. package/build/orm.d.ts +2 -2
  29. package/build/orm.deleteMany.d.ts +2 -1
  30. package/build/orm.deleteMany.js +2 -2
  31. package/build/orm.deleteOne.d.ts +2 -1
  32. package/build/orm.deleteOne.js +2 -2
  33. package/build/orm.findMany.d.ts +2 -1
  34. package/build/orm.findMany.js +3 -3
  35. package/build/orm.findOne.d.ts +2 -1
  36. package/build/orm.findOne.js +11 -19
  37. package/build/orm.js +13 -13
  38. package/build/orm.restrict.d.ts +2 -1
  39. package/build/orm.restrict.js +2 -2
  40. package/build/orm.updateMany.d.ts +2 -1
  41. package/build/orm.updateMany.js +3 -3
  42. package/build/orm.updateOne.d.ts +2 -1
  43. package/build/orm.updateOne.js +2 -2
  44. package/build/tests/orm.findMany.includeManyToOne.test.js +21 -5
  45. package/build/tests/orm.findOne.includeManyToMany.test.js +270 -0
  46. package/build/tests/orm.findOne.includeManyToOne.test.js +280 -0
  47. package/build/tests/orm.findOne.includeOneToMany.test.js +454 -0
  48. package/build/tests/orm.findOne.select.test.js +205 -0
  49. package/build/tests/orm.findOne.where.test.js +471 -0
  50. package/build/tests/orm.middleware.findOne.test.d.ts +1 -0
  51. package/build/tests/orm.middleware.set.test.d.ts +1 -0
  52. package/build/tests/orm.middleware.set.test.js +100 -0
  53. package/build/tests/orm.middleware.updateMany.test.d.ts +1 -0
  54. package/build/tests/orm.middleware.updateOne.test.d.ts +1 -0
  55. package/build/tests/orm.middleware.values.test.d.ts +1 -0
  56. package/build/tests/orm.middleware.values.test.js +126 -0
  57. package/build/tests/orm.middleware.where.test.d.ts +1 -0
  58. package/build/tests/orm.middleware.where.test.js +1054 -0
  59. package/build/tests/util/db.d.ts +52 -70
  60. package/build/types/CreateOneResult.d.ts +6 -2
  61. package/build/types/CreateOneResult.test-d.js +3 -0
  62. package/build/types/Middleware.d.ts +14 -0
  63. package/build/types/Middleware.js +37 -1
  64. package/build/types/WhereClause.d.ts +8 -8
  65. package/build/util/applySetMiddleware.d.ts +5 -0
  66. package/build/util/applySetMiddleware.js +7 -0
  67. package/build/util/applyValuesMiddleware.d.ts +5 -0
  68. package/build/util/applyValuesMiddleware.js +7 -0
  69. package/build/util/applyWhereMiddleware.d.ts +5 -0
  70. package/build/util/applyWhereMiddleware.js +7 -0
  71. package/build/util/applyWhereMiddleware.test.d.ts +1 -0
  72. package/build/util/applyWhereMiddleware.test.js +88 -0
  73. package/build/util/resultSchema.d.ts +6 -6
  74. package/package.json +23 -24
  75. /package/build/tests/{orm.count.middleware.test.d.ts → orm.findOne.includeManyToMany.test.d.ts} +0 -0
  76. /package/build/tests/{orm.createMany.middleware.test.d.ts → orm.findOne.includeManyToOne.test.d.ts} +0 -0
  77. /package/build/tests/{orm.createOne.middleware.test.d.ts → orm.findOne.includeOneToMany.test.d.ts} +0 -0
  78. /package/build/tests/{orm.deleteMany.middleware.test.d.ts → orm.findOne.select.test.d.ts} +0 -0
  79. /package/build/tests/{orm.deleteOne.middleware.test.d.ts → orm.findOne.where.test.d.ts} +0 -0
  80. /package/build/tests/{orm.findMany.middleware.test.d.ts → orm.middleware.count.test.d.ts} +0 -0
  81. /package/build/tests/{orm.count.middleware.test.js → orm.middleware.count.test.js} +0 -0
  82. /package/build/tests/{orm.findOne.middleware.test.d.ts → orm.middleware.createMany.test.d.ts} +0 -0
  83. /package/build/tests/{orm.createMany.middleware.test.js → orm.middleware.createMany.test.js} +0 -0
  84. /package/build/tests/{orm.updateMany.middleware.test.d.ts → orm.middleware.createOne.test.d.ts} +0 -0
  85. /package/build/tests/{orm.createOne.middleware.test.js → orm.middleware.createOne.test.js} +0 -0
  86. /package/build/tests/{orm.updateOne.middleware.test.d.ts → orm.middleware.deleteMany.test.d.ts} +0 -0
  87. /package/build/tests/{orm.deleteMany.middleware.test.js → orm.middleware.deleteMany.test.js} +0 -0
  88. /package/build/{types/BaseFindParams.d.ts → tests/orm.middleware.deleteOne.test.d.ts} +0 -0
  89. /package/build/tests/{orm.deleteOne.middleware.test.js → orm.middleware.deleteOne.test.js} +0 -0
  90. /package/build/{types/BaseFindParams.js → tests/orm.middleware.findMany.test.d.ts} +0 -0
  91. /package/build/tests/{orm.findMany.middleware.test.js → orm.middleware.findMany.test.js} +0 -0
  92. /package/build/tests/{orm.findOne.middleware.test.js → orm.middleware.findOne.test.js} +0 -0
  93. /package/build/tests/{orm.updateMany.middleware.test.js → orm.middleware.updateMany.test.js} +0 -0
  94. /package/build/tests/{orm.updateOne.middleware.test.js → orm.middleware.updateOne.test.js} +0 -0
@@ -12,6 +12,7 @@
12
12
  */
13
13
  import { NormalizedConfig } from "@casekit/orm2-config";
14
14
  import { ModelDefinitions, OperatorDefinitions } from "@casekit/orm2-schema";
15
+ import { Middleware } from "../types/Middleware.js";
15
16
  import { WhereClause } from "../types/WhereClause.js";
16
17
  import { CountBuilder } from "./types.js";
17
18
  type BaseCountParams = {
@@ -19,5 +20,5 @@ type BaseCountParams = {
19
20
  include?: Record<string, BaseCountParams>;
20
21
  for?: "update" | "no key update" | "share" | "key share";
21
22
  };
22
- export declare const buildCount: (config: NormalizedConfig, modelName: string, query: BaseCountParams, path?: string[], tableIndex?: number) => CountBuilder;
23
+ export declare const buildCount: (config: NormalizedConfig, middleware: Middleware[], modelName: string, query: BaseCountParams, path?: string[], tableIndex?: number) => CountBuilder;
23
24
  export {};
@@ -1,11 +1,10 @@
1
- import { hasClauses } from "#util/hasClauses.js";
2
1
  import { makeTableAlias } from "#util/makeTableAlias.js";
3
2
  import { buildWhere } from "./buildWhere.js";
4
3
  /**
5
4
  * Builds a base CountBuilder, with only columns from the main table.
6
5
  * Joins will be added later.
7
6
  */
8
- const buildBaseCount = (config, m, query, tableIndex = 0) => {
7
+ const buildBaseCount = (config, middleware, m, query, tableIndex = 0) => {
9
8
  const model = config.models[m];
10
9
  if (!model)
11
10
  throw new Error(`Model "${m}" not found`);
@@ -15,9 +14,7 @@ const buildBaseCount = (config, m, query, tableIndex = 0) => {
15
14
  alias: makeTableAlias(tableIndex++),
16
15
  model: m,
17
16
  };
18
- const where = hasClauses(query.where)
19
- ? buildWhere(config, table, query.where)
20
- : null;
17
+ const where = buildWhere(config, middleware, table, query.where);
21
18
  return {
22
19
  table,
23
20
  where,
@@ -26,17 +23,17 @@ const buildBaseCount = (config, m, query, tableIndex = 0) => {
26
23
  tableIndex,
27
24
  };
28
25
  };
29
- export const buildCount = (config, modelName, query, path = [], tableIndex = 0) => {
26
+ export const buildCount = (config, middleware, modelName, query, path = [], tableIndex = 0) => {
30
27
  const model = config.models[modelName];
31
28
  if (!model)
32
29
  throw new Error(`Model "${modelName}" not found`);
33
- const builder = buildBaseCount(config, modelName, query, tableIndex);
30
+ const builder = buildBaseCount(config, middleware, modelName, query, tableIndex);
34
31
  for (const [r, subquery] of Object.entries(query.include ?? {})) {
35
32
  const relation = model.relations[r];
36
33
  if (relation.type !== "N:1") {
37
34
  continue;
38
35
  }
39
- const joinBuilder = buildCount(config, relation.model, subquery, [...path, r], builder.tableIndex);
36
+ const joinBuilder = buildCount(config, middleware, relation.model, subquery, [...path, r], builder.tableIndex);
40
37
  builder.joins.push({
41
38
  path: [...path, r],
42
39
  relation: r,
@@ -5,7 +5,7 @@ import { buildCount } from "./buildCount.js";
5
5
  describe("buildCount", () => {
6
6
  const { db } = createTestDB();
7
7
  test("builds basic count query", () => {
8
- const result = buildCount(db.config, "post", {});
8
+ const result = buildCount(db.config, [], "post", {});
9
9
  expect(result).toEqual({
10
10
  table: {
11
11
  schema: "orm",
@@ -20,7 +20,7 @@ describe("buildCount", () => {
20
20
  });
21
21
  });
22
22
  test("builds count query with N:1 relation include", () => {
23
- const result = buildCount(db.config, "post", {
23
+ const result = buildCount(db.config, [], "post", {
24
24
  where: { id: 1 },
25
25
  include: {
26
26
  author: {
@@ -61,7 +61,7 @@ describe("buildCount", () => {
61
61
  });
62
62
  });
63
63
  test("builds count query with where clause", () => {
64
- const result = buildCount(db.config, "post", {
64
+ const result = buildCount(db.config, [], "post", {
65
65
  where: {
66
66
  title: "Test Post",
67
67
  },
@@ -80,7 +80,7 @@ describe("buildCount", () => {
80
80
  });
81
81
  });
82
82
  test("builds count query with nested includes and where clauses", () => {
83
- const result = buildCount(db.config, "post", {
83
+ const result = buildCount(db.config, [], "post", {
84
84
  where: {
85
85
  title: "Test Post",
86
86
  },
@@ -125,7 +125,7 @@ describe("buildCount", () => {
125
125
  });
126
126
  });
127
127
  test("builds count query with for parameter", () => {
128
- const result = buildCount(db.config, "post", {
128
+ const result = buildCount(db.config, [], "post", {
129
129
  for: "update",
130
130
  });
131
131
  expect(result).toEqual({
@@ -1,5 +1,6 @@
1
1
  import { NormalizedConfig } from "@casekit/orm2-config";
2
2
  import { ModelDefinitions, ModelName } from "@casekit/orm2-schema";
3
3
  import { CreateManyParams } from "#types/CreateManyParams.js";
4
+ import { Middleware } from "../types/Middleware.js";
4
5
  import { CreateBuilder } from "./types.js";
5
- export declare const buildCreate: (config: NormalizedConfig, modelName: string, query: CreateManyParams<ModelDefinitions, ModelName<ModelDefinitions>>, path?: string[], tableIndex?: number) => CreateBuilder;
6
+ export declare const buildCreate: (config: NormalizedConfig, middleware: Middleware[], modelName: string, query: CreateManyParams<ModelDefinitions, ModelName<ModelDefinitions>>, path?: string[], tableIndex?: number) => CreateBuilder;
@@ -1,10 +1,13 @@
1
1
  import { uniq } from "es-toolkit";
2
2
  import { makeTableAlias } from "#util/makeTableAlias.js";
3
- export const buildCreate = (config, modelName, query, path = [], tableIndex = 0) => {
3
+ import { applyValuesMiddleware } from "../util/applyValuesMiddleware.js";
4
+ export const buildCreate = (config, middleware, modelName, query, path = [], tableIndex = 0) => {
4
5
  const model = config.models[modelName];
5
6
  if (!model)
6
7
  throw new Error(`Model "${modelName}" not found`);
7
- const fields = uniq(query.values.flatMap(Object.keys));
8
+ // Apply values middleware to each value object
9
+ const processedValues = query.values.map((v) => applyValuesMiddleware(config, middleware, modelName, v));
10
+ const fields = uniq(processedValues.flatMap(Object.keys));
8
11
  const table = {
9
12
  schema: model.schema,
10
13
  name: model.table,
@@ -12,7 +15,7 @@ export const buildCreate = (config, modelName, query, path = [], tableIndex = 0)
12
15
  model: modelName,
13
16
  };
14
17
  const columns = fields.map((f) => model.fields[f].column);
15
- const values = query.values.map((v) => fields.map((f) => v[f] ?? null));
18
+ const values = processedValues.map((v) => fields.map((f) => v[f] ?? null));
16
19
  const returning = query.returning?.map((f, index) => ({
17
20
  name: model.fields[f].column,
18
21
  alias: `${table.alias}_${index}`,
@@ -4,7 +4,7 @@ import { buildCreate } from "./buildCreate.js";
4
4
  describe("buildCreate", () => {
5
5
  const { db } = createTestDB();
6
6
  test("should build a create query", () => {
7
- const result = buildCreate(db.config, "user", {
7
+ const result = buildCreate(db.config, [], "user", {
8
8
  values: [{ id: 1, name: "John Doe", email: "john@example.com" }],
9
9
  });
10
10
  expect(result).toEqual({
@@ -21,14 +21,14 @@ describe("buildCreate", () => {
21
21
  });
22
22
  });
23
23
  test("should throw an error if model is not found", () => {
24
- expect(() => buildCreate(db.config, "NonExistentModel", {
24
+ expect(() => buildCreate(db.config, [], "NonExistentModel", {
25
25
  values: [
26
26
  { id: 1, name: "John Doe", email: "john@example.com" },
27
27
  ],
28
28
  })).toThrow('Model "NonExistentModel" not found');
29
29
  });
30
30
  test("should handle returning fields", () => {
31
- const result = buildCreate(db.config, "user", {
31
+ const result = buildCreate(db.config, [], "user", {
32
32
  values: [{ id: 1, name: "John Doe", email: "john@example.com" }],
33
33
  returning: ["id", "name"],
34
34
  });
@@ -38,7 +38,7 @@ describe("buildCreate", () => {
38
38
  ]);
39
39
  });
40
40
  test("should handle onConflict", () => {
41
- const result = buildCreate(db.config, "user", {
41
+ const result = buildCreate(db.config, [], "user", {
42
42
  values: [{ id: 1, name: "John Doe", email: "john@example.com" }],
43
43
  onConflict: { do: "nothing" },
44
44
  });
@@ -1,5 +1,6 @@
1
1
  import { NormalizedConfig } from "@casekit/orm2-config";
2
2
  import { ModelDefinitions, ModelName, OperatorDefinitions } from "@casekit/orm2-schema";
3
3
  import { DeleteParams } from "#types/DeleteParams.js";
4
+ import { Middleware } from "../types/Middleware.js";
4
5
  import { DeleteBuilder } from "./types.js";
5
- export declare const buildDelete: (config: NormalizedConfig, modelName: string, query: DeleteParams<ModelDefinitions, OperatorDefinitions, ModelName<ModelDefinitions>>, path?: string[], tableIndex?: number) => DeleteBuilder;
6
+ export declare const buildDelete: (config: NormalizedConfig, middleware: Middleware[], modelName: string, query: DeleteParams<ModelDefinitions, OperatorDefinitions, ModelName<ModelDefinitions>>, path?: string[], tableIndex?: number) => DeleteBuilder;
@@ -1,7 +1,7 @@
1
1
  import { makeTableAlias } from "#util/makeTableAlias.js";
2
2
  import { hasClauses } from "../util/hasClauses.js";
3
3
  import { buildWhere } from "./buildWhere.js";
4
- export const buildDelete = (config, modelName, query, path = [], tableIndex = 0) => {
4
+ export const buildDelete = (config, middleware, modelName, query, path = [], tableIndex = 0) => {
5
5
  const model = config.models[modelName];
6
6
  if (!model)
7
7
  throw new Error(`Model "${modelName}" not found`);
@@ -14,7 +14,7 @@ export const buildDelete = (config, modelName, query, path = [], tableIndex = 0)
14
14
  if (!hasClauses(query.where)) {
15
15
  throw new Error("Delete queries must have a where clause");
16
16
  }
17
- const where = buildWhere(config, table, query.where);
17
+ const where = buildWhere(config, middleware, table, query.where);
18
18
  const returning = query.returning?.map((f, index) => ({
19
19
  name: model.fields[f].column,
20
20
  alias: `${table.alias}_${index}`,
@@ -5,7 +5,7 @@ import { buildDelete } from "./buildDelete.js";
5
5
  describe("buildDelete", () => {
6
6
  const { db } = createTestDB();
7
7
  test("builds basic delete query with where clause", () => {
8
- const result = buildDelete(db.config, "user", {
8
+ const result = buildDelete(db.config, [], "user", {
9
9
  where: { id: 1 },
10
10
  });
11
11
  expect(result).toEqual({
@@ -20,7 +20,7 @@ describe("buildDelete", () => {
20
20
  });
21
21
  });
22
22
  test("builds delete query with returning clause", () => {
23
- const result = buildDelete(db.config, "user", {
23
+ const result = buildDelete(db.config, [], "user", {
24
24
  where: { id: 1 },
25
25
  returning: ["id", "name"],
26
26
  });
@@ -47,23 +47,23 @@ describe("buildDelete", () => {
47
47
  });
48
48
  });
49
49
  test("throws error when model not found", () => {
50
- expect(() => buildDelete(db.config, "nonexistentModel", {
50
+ expect(() => buildDelete(db.config, [], "nonexistentModel", {
51
51
  where: { id: 1 },
52
52
  })).toThrow('Model "nonexistentModel" not found');
53
53
  });
54
54
  test("throws error when where clause is empty", () => {
55
- expect(() => buildDelete(db.config, "user", {
55
+ expect(() => buildDelete(db.config, [], "user", {
56
56
  where: {},
57
57
  })).toThrow("Delete queries must have a where clause");
58
58
  });
59
59
  test("throws error when where clause is undefined", () => {
60
- expect(() => buildDelete(db.config, "user", {
60
+ expect(() => buildDelete(db.config, [], "user", {
61
61
  // @ts-expect-error testing runtime behavior with invalid input
62
62
  where: undefined,
63
63
  })).toThrow("Delete queries must have a where clause");
64
64
  });
65
65
  test("handles complex where clauses", () => {
66
- const result = buildDelete(db.config, "user", {
66
+ const result = buildDelete(db.config, [], "user", {
67
67
  where: {
68
68
  id: 1,
69
69
  name: "test",
@@ -82,7 +82,7 @@ describe("buildDelete", () => {
82
82
  });
83
83
  });
84
84
  test("uses correct path for nested fields in returning clause", () => {
85
- const result = buildDelete(db.config, "user", {
85
+ const result = buildDelete(db.config, [], "user", {
86
86
  where: { id: 1 },
87
87
  returning: ["id", "name"],
88
88
  }, ["parent"]);
@@ -100,7 +100,7 @@ describe("buildDelete", () => {
100
100
  ]);
101
101
  });
102
102
  test("uses provided table index for alias generation", () => {
103
- const result = buildDelete(db.config, "user", {
103
+ const result = buildDelete(db.config, [], "user", {
104
104
  where: { id: 1 },
105
105
  }, [], 5);
106
106
  expect(result.table.alias).toBe("f");
@@ -1,8 +1,9 @@
1
1
  import { NormalizedConfig } from "@casekit/orm2-config";
2
2
  import { ModelDefinition, OperatorDefinitions } from "@casekit/orm2-schema";
3
3
  import { FindParams } from "#types/FindParams.js";
4
+ import { Middleware } from "../types/Middleware.js";
4
5
  import { FindBuilder } from "./types.js";
5
- export declare const buildFind: (config: NormalizedConfig, modelName: string, query: FindParams<Record<string, Required<ModelDefinition>>, OperatorDefinitions, string>, lateralBy?: {
6
+ export declare const buildFind: (config: NormalizedConfig, middleware: Middleware[], modelName: string, query: FindParams<Record<string, Required<ModelDefinition>>, OperatorDefinitions, string>, lateralBy?: {
6
7
  field: string;
7
8
  values: unknown[];
8
9
  }[], path?: string[], tableIndex?: number) => FindBuilder;
@@ -13,14 +13,13 @@
13
13
  import { pickBy, uniq } from "es-toolkit";
14
14
  import { max, min } from "es-toolkit/compat";
15
15
  import { getField, } from "@casekit/orm2-config";
16
- import { hasClauses } from "#util/hasClauses.js";
17
16
  import { makeTableAlias } from "#util/makeTableAlias.js";
18
17
  import { buildWhere } from "./buildWhere.js";
19
18
  /**
20
19
  * Builds a base FindBuilder, with only columns from the main table.
21
20
  * Joins will be added later.
22
21
  */
23
- const buildBaseFind = (config, m, query, lateralBy = [], path = [], tableIndex = 0) => {
22
+ const buildBaseFind = (config, middleware, m, query, lateralBy = [], path = [], tableIndex = 0) => {
24
23
  const model = config.models[m];
25
24
  if (!model)
26
25
  throw new Error(`Model "${m}" not found`);
@@ -47,9 +46,7 @@ const buildBaseFind = (config, m, query, lateralBy = [], path = [], tableIndex =
47
46
  path: [...path, f],
48
47
  };
49
48
  });
50
- const where = hasClauses(query.where)
51
- ? buildWhere(config, table, query.where)
52
- : null;
49
+ const where = buildWhere(config, middleware, table, query.where);
53
50
  return {
54
51
  table,
55
52
  columns,
@@ -122,18 +119,18 @@ const buildOrderBy = (config, model, query, builder) => {
122
119
  }
123
120
  }) ?? []);
124
121
  };
125
- export const buildFind = (config, modelName, query, lateralBy = [], path = [], tableIndex = 0) => {
122
+ export const buildFind = (config, middleware, modelName, query, lateralBy = [], path = [], tableIndex = 0) => {
126
123
  const model = config.models[modelName];
127
124
  if (!model)
128
125
  throw new Error(`Model "${modelName}" not found`);
129
- const builder = buildBaseFind(config, modelName, query, lateralBy, path, tableIndex);
126
+ const builder = buildBaseFind(config, middleware, modelName, query, lateralBy, path, tableIndex);
130
127
  const manyToOneQueries = collectManyToOneQueries(model, query);
131
128
  for (const [r, subquery] of Object.entries(manyToOneQueries)) {
132
129
  const relation = model.relations[r];
133
130
  if (relation.type !== "N:1") {
134
131
  throw new Error(`Unexpected relation type: ${relation.type}. Should be N:1`);
135
132
  }
136
- const joinBuilder = buildFind(config, relation.model, subquery, [], [...path, r], builder.tableIndex);
133
+ const joinBuilder = buildFind(config, middleware, relation.model, subquery, [], [...path, r], builder.tableIndex);
137
134
  builder.joins.push({
138
135
  relation: r,
139
136
  path: [...path, r],
@@ -4,7 +4,7 @@ import { buildFind } from "./buildFind.js";
4
4
  describe("buildFind", () => {
5
5
  const { db } = createTestDB();
6
6
  test("builds query with N:1 relation include", () => {
7
- const result = buildFind(db.config, "post", {
7
+ const result = buildFind(db.config, [], "post", {
8
8
  select: ["id", "title"],
9
9
  include: {
10
10
  author: {
@@ -81,7 +81,7 @@ describe("buildFind", () => {
81
81
  });
82
82
  test("builds query with optional N:1 relation include", () => {
83
83
  const { db } = createTestDB();
84
- const result = buildFind(db.config, "post", {
84
+ const result = buildFind(db.config, [], "post", {
85
85
  select: ["id"],
86
86
  include: {
87
87
  backgroundColor: {
@@ -152,7 +152,7 @@ describe("buildFind", () => {
152
152
  });
153
153
  test("automatically joins relations referenced in orderBy", () => {
154
154
  const { db } = createTestDB();
155
- const result = buildFind(db.config, "post", {
155
+ const result = buildFind(db.config, [], "post", {
156
156
  select: ["id", "title"],
157
157
  orderBy: ["author.name"],
158
158
  });
@@ -227,7 +227,7 @@ describe("buildFind", () => {
227
227
  });
228
228
  test("combines ordering, limits and offsets from nested queries", () => {
229
229
  const { db } = createTestDB();
230
- const result = buildFind(db.config, "post", {
230
+ const result = buildFind(db.config, [], "post", {
231
231
  select: ["id"],
232
232
  limit: 10,
233
233
  offset: 5,
@@ -1,5 +1,6 @@
1
1
  import { NormalizedConfig } from "@casekit/orm2-config";
2
2
  import { ModelDefinitions, ModelName, OperatorDefinitions } from "@casekit/orm2-schema";
3
3
  import { UpdateParams } from "#types/UpdateParams.js";
4
+ import { Middleware } from "../types/Middleware.js";
4
5
  import { UpdateBuilder } from "./types.js";
5
- export declare const buildUpdate: (config: NormalizedConfig, modelName: string, query: UpdateParams<ModelDefinitions, OperatorDefinitions, ModelName<ModelDefinitions>>, path?: string[], tableIndex?: number) => UpdateBuilder;
6
+ export declare const buildUpdate: (config: NormalizedConfig, middleware: Middleware[], modelName: string, query: UpdateParams<ModelDefinitions, OperatorDefinitions, ModelName<ModelDefinitions>>, path?: string[], tableIndex?: number) => UpdateBuilder;
@@ -1,8 +1,9 @@
1
1
  import { getField } from "@casekit/orm2-config";
2
2
  import { makeTableAlias } from "#util/makeTableAlias.js";
3
+ import { applySetMiddleware } from "../util/applySetMiddleware.js";
3
4
  import { hasClauses } from "../util/hasClauses.js";
4
5
  import { buildWhere } from "./buildWhere.js";
5
- export const buildUpdate = (config, modelName, query, path = [], tableIndex = 0) => {
6
+ export const buildUpdate = (config, middleware, modelName, query, path = [], tableIndex = 0) => {
6
7
  const model = config.models[modelName];
7
8
  if (!model)
8
9
  throw new Error(`Model "${modelName}" not found`);
@@ -12,14 +13,16 @@ export const buildUpdate = (config, modelName, query, path = [], tableIndex = 0)
12
13
  alias: makeTableAlias(tableIndex++),
13
14
  model: modelName,
14
15
  };
15
- const set = Object.entries(query.set).map(([field, value]) => [
16
+ // Apply set middleware before processing
17
+ const processedSet = applySetMiddleware(config, middleware, modelName, query.set);
18
+ const set = Object.entries(processedSet).map(([field, value]) => [
16
19
  getField(model, field).column,
17
20
  value,
18
21
  ]);
19
22
  if (!hasClauses(query.where)) {
20
23
  throw new Error("Update queries must have a where clause");
21
24
  }
22
- const where = buildWhere(config, table, query.where);
25
+ const where = buildWhere(config, middleware, table, query.where);
23
26
  const returning = query.returning?.map((f, index) => ({
24
27
  name: model.fields[f].column,
25
28
  alias: `${table.alias}_${index}`,
@@ -7,7 +7,7 @@ import { buildUpdate } from "./buildUpdate.js";
7
7
  describe("buildUpdate", () => {
8
8
  const { db } = createTestDB();
9
9
  test("builds basic update query", () => {
10
- const result = buildUpdate(db.config, "user", {
10
+ const result = buildUpdate(db.config, [], "user", {
11
11
  set: { name: "John", email: "john@example.com" },
12
12
  where: { id: 1 },
13
13
  });
@@ -27,7 +27,7 @@ describe("buildUpdate", () => {
27
27
  });
28
28
  });
29
29
  test("builds update query with returning clause", () => {
30
- const result = buildUpdate(db.config, "user", {
30
+ const result = buildUpdate(db.config, [], "user", {
31
31
  set: { name: "John" },
32
32
  where: { id: 1 },
33
33
  returning: ["id", "name"],
@@ -56,26 +56,26 @@ describe("buildUpdate", () => {
56
56
  });
57
57
  });
58
58
  test("throws error for nonexistent model", () => {
59
- expect(() => buildUpdate(db.config, "nonexistent", {
59
+ expect(() => buildUpdate(db.config, [], "nonexistent", {
60
60
  set: { name: "John" },
61
61
  where: { id: 1 },
62
62
  })).toThrow('Model "nonexistent" not found');
63
63
  });
64
64
  test("throws error when where clause is empty", () => {
65
- expect(() => buildUpdate(db.config, "user", {
65
+ expect(() => buildUpdate(db.config, [], "user", {
66
66
  set: { name: "John" },
67
67
  where: {},
68
68
  })).toThrow("Update queries must have a where clause");
69
69
  });
70
70
  test("throws error when where clause is undefined", () => {
71
- expect(() => buildUpdate(db.config, "user", {
71
+ expect(() => buildUpdate(db.config, [], "user", {
72
72
  set: { name: "John" },
73
73
  // @ts-expect-error testing runtime behavior
74
74
  where: undefined,
75
75
  })).toThrow("Update queries must have a where clause");
76
76
  });
77
77
  test("handles nested paths in returning clause", () => {
78
- const result = buildUpdate(db.config, "user", {
78
+ const result = buildUpdate(db.config, [], "user", {
79
79
  set: { name: "John" },
80
80
  where: { id: 1 },
81
81
  returning: ["id", "name"],
@@ -94,14 +94,14 @@ describe("buildUpdate", () => {
94
94
  ]);
95
95
  });
96
96
  test("uses provided table index for alias", () => {
97
- const result = buildUpdate(db.config, "user", {
97
+ const result = buildUpdate(db.config, [], "user", {
98
98
  set: { name: "John" },
99
99
  where: { id: 1 },
100
100
  }, [], 5);
101
101
  expect(result.table.alias).toBe("f");
102
102
  });
103
103
  test("handles complex where clauses", () => {
104
- const result = buildUpdate(db.config, "user", {
104
+ const result = buildUpdate(db.config, [], "user", {
105
105
  set: { name: "John" },
106
106
  where: {
107
107
  id: 1,
@@ -1,6 +1,7 @@
1
1
  import { NormalizedConfig } from "@casekit/orm2-config";
2
2
  import { ModelDefinitions, ModelName, OperatorDefinitions } from "@casekit/orm2-schema";
3
3
  import { SQLStatement } from "@casekit/sql";
4
+ import { Middleware } from "../types/Middleware.js";
4
5
  import { WhereClause } from "../types/WhereClause.js";
5
6
  import { Table } from "./types.js";
6
- export declare const buildWhere: (config: NormalizedConfig, table: Table, where: WhereClause<ModelDefinitions, OperatorDefinitions, ModelName<ModelDefinitions>>) => SQLStatement;
7
+ export declare const buildWhere: (config: NormalizedConfig, middleware: Middleware[], table: Table, where?: WhereClause<ModelDefinitions, OperatorDefinitions, ModelName<ModelDefinitions>>) => SQLStatement | null;
@@ -1,11 +1,18 @@
1
1
  import { sql } from "@casekit/sql";
2
2
  import { $and, $not, $or, defaultOperators } from "../operators.js";
3
- export const buildWhere = (config, table, where) => {
3
+ import { applyWhereMiddleware } from "../util/applyWhereMiddleware.js";
4
+ import { hasClauses } from "../util/hasClauses.js";
5
+ export const buildWhere = (config, middleware, table, where) => {
6
+ // Apply middleware to the where clause
7
+ const processedWhere = applyWhereMiddleware(config, middleware, table.model, where) ?? {};
8
+ if (!hasClauses(processedWhere)) {
9
+ return null;
10
+ }
4
11
  // NB. Object.entries does not iterate over the symbol keys
5
12
  // of an object, so this is a neat way for us to get just the
6
13
  // clauses that relate to specific fields, and not logic operators
7
14
  // like $and, $or, $not.
8
- const clauses = Object.entries(where).map(([field, value]) => {
15
+ const clauses = Object.entries(processedWhere).map(([field, value]) => {
9
16
  const tableAlias = table.alias;
10
17
  const column = config.models[table.model]?.fields[field]?.column;
11
18
  if (!column)
@@ -47,16 +54,31 @@ export const buildWhere = (config, table, where) => {
47
54
  return sql.join(subclauses, " AND ");
48
55
  });
49
56
  // logical operators
50
- if (where[$and]) {
51
- const subclauses = where[$and].map((clause) => buildWhere(config, table, clause));
57
+ if (processedWhere[$and]) {
58
+ const subclauses = processedWhere[$and].map((clause) => {
59
+ const subclause = buildWhere(config, middleware, table, clause);
60
+ if (!subclause) {
61
+ throw new Error("AND clause must not be empty");
62
+ }
63
+ return subclause;
64
+ });
52
65
  clauses.push(sql `(${sql.join(subclauses, " AND ")})`);
53
66
  }
54
- if (where[$or]) {
55
- const subclauses = where[$or].map((clause) => buildWhere(config, table, clause));
67
+ if (processedWhere[$or]) {
68
+ const subclauses = processedWhere[$or].map((clause) => {
69
+ const subclause = buildWhere(config, middleware, table, clause);
70
+ if (!subclause) {
71
+ throw new Error("OR clause must not be empty");
72
+ }
73
+ return subclause;
74
+ });
56
75
  clauses.push(sql `(${sql.join(subclauses, " OR ")})`);
57
76
  }
58
- if (where[$not]) {
59
- const subclause = buildWhere(config, table, where[$not]);
77
+ if (processedWhere[$not]) {
78
+ const subclause = buildWhere(config, middleware, table, processedWhere[$not]);
79
+ if (!subclause) {
80
+ throw new Error("NOT clause must not be empty");
81
+ }
60
82
  clauses.push(sql `NOT ${subclause}`);
61
83
  }
62
84
  return sql.join(clauses, " AND ");