@baasix/baasix 0.1.61 → 0.1.63

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 (126) hide show
  1. package/dist/app/404/index.html +1 -1
  2. package/dist/app/404.html +1 -1
  3. package/dist/app/_next/static/chunks/2667-7993936698bf75a9.js +1 -0
  4. package/dist/app/_next/static/chunks/3233-ae028c73c510ebea.js +1 -0
  5. package/dist/app/_next/static/chunks/4033-ea89dd457b87cead.js +1 -0
  6. package/dist/app/_next/static/chunks/799-d4372ad380b0dec6.js +1 -0
  7. package/dist/app/_next/static/chunks/app/(authenticated)/activity-log/all-activity/{page-a7e9f0b39624b489.js → page-8e1f084e807ff5ee.js} +1 -1
  8. package/dist/app/_next/static/chunks/app/(authenticated)/activity-log/email-log/{page-40f2b9ecf73e74b5.js → page-4e08df2865fe9d0f.js} +1 -1
  9. package/dist/app/_next/static/chunks/app/(authenticated)/activity-log/notifications/{page-21fcdd2f0b3549db.js → page-3a5070a5088835ae.js} +1 -1
  10. package/dist/app/_next/static/chunks/app/(authenticated)/activity-log/sessions/{page-82662735f1ec476a.js → page-edfa379cab0d3ccb.js} +1 -1
  11. package/dist/app/_next/static/chunks/app/(authenticated)/activity-log/workflow-executions/{page-4a781c95e5fb25f0.js → page-d007890f17b60862.js} +1 -1
  12. package/dist/app/_next/static/chunks/app/(authenticated)/activity-log/workflow-logs/{page-70bf8d31074dcba0.js → page-976e9829706ac8f2.js} +1 -1
  13. package/dist/app/_next/static/chunks/app/(authenticated)/dashboard/page-6bff6e8911fc5036.js +1 -0
  14. package/dist/app/_next/static/chunks/app/(authenticated)/data-browser/{page-d5715b9743d8dba4.js → page-fbb7a4d4f2a4353d.js} +1 -1
  15. package/dist/app/_next/static/chunks/app/(authenticated)/file-manager/{page-c008de2bcf6811b9.js → page-cea6a2c1670d4ef5.js} +1 -1
  16. package/dist/app/_next/static/chunks/app/(authenticated)/layout-b7f18737e4c308a4.js +1 -0
  17. package/dist/app/_next/static/chunks/app/(authenticated)/settings/migrations/page-505c3389890685b9.js +1 -0
  18. package/dist/app/_next/static/chunks/app/(authenticated)/settings/permissions/{page-2d3043500adcdb12.js → page-ed80f4bd5d81df95.js} +1 -1
  19. package/dist/app/_next/static/chunks/app/(authenticated)/settings/project/{page-6a58f15efb013e17.js → page-310b8ce72d0516ea.js} +1 -1
  20. package/dist/app/_next/static/chunks/app/(authenticated)/settings/roles/{page-00cf3e4483986407.js → page-fd62dc19ac88dd64.js} +1 -1
  21. package/dist/app/_next/static/chunks/app/(authenticated)/settings/schema/page-3d91124704c2e853.js +1 -0
  22. package/dist/app/_next/static/chunks/app/(authenticated)/settings/schema-diff/page-cae8772a7ae93b9a.js +1 -0
  23. package/dist/app/_next/static/chunks/app/(authenticated)/settings/tasks/{page-fa3c401992a2fb8f.js → page-1479604bbfc53a83.js} +1 -1
  24. package/dist/app/_next/static/chunks/app/(authenticated)/settings/templates/edit/{page-4ce715d527c717a8.js → page-a611195661ce22bd.js} +1 -1
  25. package/dist/app/_next/static/chunks/app/(authenticated)/settings/templates/{page-aafbfd4f12875738.js → page-6787b62a4960304f.js} +1 -1
  26. package/dist/app/_next/static/chunks/app/(authenticated)/settings/tenants/{page-f34d97ce6f68183b.js → page-856d4af5fbd76c8e.js} +1 -1
  27. package/dist/app/_next/static/chunks/app/(authenticated)/users/invites/{page-20dd05a8745b6283.js → page-ed5493d55f6c1db8.js} +1 -1
  28. package/dist/app/_next/static/chunks/app/(authenticated)/users/list/{page-479a164304f4de3f.js → page-02e6ef533bdc3bbc.js} +1 -1
  29. package/dist/app/_next/static/chunks/app/(authenticated)/users/{page-909d7be1b8d9800c.js → page-faf321b61a6fdeef.js} +1 -1
  30. package/dist/app/_next/static/chunks/app/(authenticated)/users/preferences/{page-e3fbcc425e1710e5.js → page-2116015e962a2ecc.js} +1 -1
  31. package/dist/app/_next/static/chunks/app/(authenticated)/users/user-roles/{page-3f654c69d79269f7.js → page-7d1bcbbfc8f0694b.js} +1 -1
  32. package/dist/app/_next/static/chunks/app/(authenticated)/workflows/edit/{page-753cf274fd329860.js → page-45c465caaa5679bd.js} +1 -1
  33. package/dist/app/_next/static/chunks/app/(authenticated)/workflows/{page-33c1c69678122839.js → page-7f9cb24aaa798f42.js} +1 -1
  34. package/dist/app/_next/static/chunks/{main-324e91f5a430cddf.js → main-8ffa7219a08e42cf.js} +1 -1
  35. package/dist/app/_next/static/chunks/{webpack-b29732cfe967a5f5.js → webpack-7a100cbf9402e7ea.js} +1 -1
  36. package/dist/app/_next/static/css/45276c37be155f45.css +3 -0
  37. package/dist/app/activity-log/all-activity/index.html +1 -1
  38. package/dist/app/activity-log/all-activity/index.txt +3 -3
  39. package/dist/app/activity-log/email-log/index.html +1 -1
  40. package/dist/app/activity-log/email-log/index.txt +3 -3
  41. package/dist/app/activity-log/index.html +1 -1
  42. package/dist/app/activity-log/index.txt +2 -2
  43. package/dist/app/activity-log/notifications/index.html +1 -1
  44. package/dist/app/activity-log/notifications/index.txt +3 -3
  45. package/dist/app/activity-log/sessions/index.html +1 -1
  46. package/dist/app/activity-log/sessions/index.txt +3 -3
  47. package/dist/app/activity-log/workflow-executions/index.html +1 -1
  48. package/dist/app/activity-log/workflow-executions/index.txt +3 -3
  49. package/dist/app/activity-log/workflow-logs/index.html +1 -1
  50. package/dist/app/activity-log/workflow-logs/index.txt +3 -3
  51. package/dist/app/change-password/index.html +1 -1
  52. package/dist/app/change-password/index.txt +2 -2
  53. package/dist/app/dashboard/index.html +1 -1
  54. package/dist/app/dashboard/index.txt +3 -3
  55. package/dist/app/data-browser/index.html +1 -1
  56. package/dist/app/data-browser/index.txt +3 -3
  57. package/dist/app/file-manager/index.html +1 -1
  58. package/dist/app/file-manager/index.txt +3 -3
  59. package/dist/app/forgot-password/index.html +1 -1
  60. package/dist/app/forgot-password/index.txt +1 -1
  61. package/dist/app/index.html +1 -1
  62. package/dist/app/index.txt +1 -1
  63. package/dist/app/login/index.html +1 -1
  64. package/dist/app/login/index.txt +1 -1
  65. package/dist/app/register/index.html +1 -1
  66. package/dist/app/register/index.txt +1 -1
  67. package/dist/app/settings/migrations/index.html +1 -1
  68. package/dist/app/settings/migrations/index.txt +3 -3
  69. package/dist/app/settings/permissions/index.html +1 -1
  70. package/dist/app/settings/permissions/index.txt +3 -3
  71. package/dist/app/settings/project/index.html +1 -1
  72. package/dist/app/settings/project/index.txt +3 -3
  73. package/dist/app/settings/roles/index.html +1 -1
  74. package/dist/app/settings/roles/index.txt +3 -3
  75. package/dist/app/settings/schema/index.html +1 -1
  76. package/dist/app/settings/schema/index.txt +3 -3
  77. package/dist/app/settings/schema-diff/index.html +1 -0
  78. package/dist/app/settings/schema-diff/index.txt +14 -0
  79. package/dist/app/settings/tasks/index.html +1 -1
  80. package/dist/app/settings/tasks/index.txt +3 -3
  81. package/dist/app/settings/templates/edit/index.html +1 -1
  82. package/dist/app/settings/templates/edit/index.txt +3 -3
  83. package/dist/app/settings/templates/index.html +1 -1
  84. package/dist/app/settings/templates/index.txt +3 -3
  85. package/dist/app/settings/tenants/index.html +1 -1
  86. package/dist/app/settings/tenants/index.txt +3 -3
  87. package/dist/app/users/index.html +1 -1
  88. package/dist/app/users/index.txt +3 -3
  89. package/dist/app/users/invites/index.html +1 -1
  90. package/dist/app/users/invites/index.txt +3 -3
  91. package/dist/app/users/list/index.html +1 -1
  92. package/dist/app/users/list/index.txt +3 -3
  93. package/dist/app/users/preferences/index.html +1 -1
  94. package/dist/app/users/preferences/index.txt +3 -3
  95. package/dist/app/users/user-roles/index.html +1 -1
  96. package/dist/app/users/user-roles/index.txt +3 -3
  97. package/dist/app/workflows/detail/index.html +1 -1
  98. package/dist/app/workflows/detail/index.txt +2 -2
  99. package/dist/app/workflows/edit/index.html +1 -1
  100. package/dist/app/workflows/edit/index.txt +3 -3
  101. package/dist/app/workflows/execution/index.html +1 -1
  102. package/dist/app/workflows/execution/index.txt +2 -2
  103. package/dist/app/workflows/index.html +1 -1
  104. package/dist/app/workflows/index.txt +3 -3
  105. package/dist/routes/mcp.route.d.ts.map +1 -1
  106. package/dist/routes/mcp.route.js +283 -2
  107. package/dist/routes/mcp.route.js.map +1 -1
  108. package/dist/routes/schema-diff.route.d.ts +7 -0
  109. package/dist/routes/schema-diff.route.d.ts.map +1 -0
  110. package/dist/routes/schema-diff.route.js +320 -0
  111. package/dist/routes/schema-diff.route.js.map +1 -0
  112. package/dist/services/ItemsService.d.ts +5 -4
  113. package/dist/services/ItemsService.d.ts.map +1 -1
  114. package/dist/services/ItemsService.js +30 -26
  115. package/dist/services/ItemsService.js.map +1 -1
  116. package/package.json +1 -1
  117. package/dist/app/_next/static/chunks/2033-8355304f13887db5.js +0 -1
  118. package/dist/app/_next/static/chunks/4952-1b97320cf61f3f21.js +0 -1
  119. package/dist/app/_next/static/chunks/6547-1aaab011e6089042.js +0 -1
  120. package/dist/app/_next/static/chunks/app/(authenticated)/dashboard/page-1ceeac9e72997a8a.js +0 -1
  121. package/dist/app/_next/static/chunks/app/(authenticated)/layout-2a4b221af5cbbade.js +0 -1
  122. package/dist/app/_next/static/chunks/app/(authenticated)/settings/migrations/page-6681c14dd478cc9a.js +0 -1
  123. package/dist/app/_next/static/chunks/app/(authenticated)/settings/schema/page-e7f836d3a7f7c5d0.js +0 -1
  124. package/dist/app/_next/static/css/17a2e01c4184b106.css +0 -3
  125. /package/dist/app/_next/static/{ORfvBD2VaKIB_8_7O9AgQ → O6PA41CeUjDJQ4sma7vMH}/_buildManifest.js +0 -0
  126. /package/dist/app/_next/static/{ORfvBD2VaKIB_8_7O9AgQ → O6PA41CeUjDJQ4sma7vMH}/_ssgManifest.js +0 -0
@@ -0,0 +1,320 @@
1
+ import { getSqlClient, getDatabase } from "../utils/db.js";
2
+ import { schemaManager } from "../utils/schemaManager.js";
3
+ import { adminOnly } from "../utils/auth.js";
4
+ import { APIError } from "../utils/errorHandler.js";
5
+ /**
6
+ * Determines which schema fields should produce actual database columns.
7
+ * Relation-only fields (no explicit type) do NOT produce columns,
8
+ * but BelongsTo relations produce a foreignKey column.
9
+ */
10
+ function getExpectedColumns(schema) {
11
+ const columns = new Map();
12
+ for (const [fieldName, fieldSchema] of Object.entries(schema.fields)) {
13
+ const fs = fieldSchema;
14
+ // BelongsTo relations create a foreign key column
15
+ if (fs.relType === "BelongsTo") {
16
+ const foreignKey = fs.foreignKey || `${fieldName}_Id`;
17
+ if (foreignKey === fieldName && fs.type) {
18
+ // Field itself is the FK column with explicit type
19
+ columns.set(fieldName, { type: fs.type, fromField: fieldName });
20
+ }
21
+ else if (foreignKey !== fieldName) {
22
+ // Separate FK column
23
+ columns.set(foreignKey, { type: fs.type || "UUID", fromField: fieldName });
24
+ }
25
+ // If foreignKey === fieldName but no explicit type, handled by FK logic
26
+ if (foreignKey === fieldName && !fs.type) {
27
+ columns.set(foreignKey, { type: "UUID", fromField: fieldName });
28
+ }
29
+ continue;
30
+ }
31
+ // Skip relation-only fields (no explicit type)
32
+ if (fs.relType && !fs.type)
33
+ continue;
34
+ columns.set(fieldName, { type: fs.type || "String", fromField: fieldName });
35
+ }
36
+ // Implicit columns from schema flags
37
+ if (schema.timestamps !== false) {
38
+ if (!columns.has("createdAt"))
39
+ columns.set("createdAt", { type: "DateTime", fromField: "[timestamps]" });
40
+ if (!columns.has("updatedAt"))
41
+ columns.set("updatedAt", { type: "DateTime", fromField: "[timestamps]" });
42
+ }
43
+ if (schema.paranoid) {
44
+ if (!columns.has("deletedAt"))
45
+ columns.set("deletedAt", { type: "DateTime", fromField: "[paranoid]" });
46
+ }
47
+ if (schema.sortEnabled) {
48
+ if (!columns.has("sort"))
49
+ columns.set("sort", { type: "Integer", fromField: "[sortEnabled]" });
50
+ }
51
+ if (schema.usertrack) {
52
+ if (!columns.has("userCreated"))
53
+ columns.set("userCreated", { type: "UUID", fromField: "[usertrack]" });
54
+ if (!columns.has("userUpdated"))
55
+ columns.set("userUpdated", { type: "UUID", fromField: "[usertrack]" });
56
+ }
57
+ return columns;
58
+ }
59
+ const registerEndpoint = (app, context) => {
60
+ /**
61
+ * GET /utils/schema-diff
62
+ * Compare schema definitions with actual database tables/columns.
63
+ * Admin-only endpoint.
64
+ */
65
+ app.get("/utils/schema-diff", adminOnly, async (req, res, next) => {
66
+ try {
67
+ const sql = getSqlClient();
68
+ // 1. Get all tables in the public schema from the actual database
69
+ // Tables to exclude from comparison (e.g. PostGIS internal tables)
70
+ const excludedTables = ['spatial_ref_sys'];
71
+ const dbTables = await sql `
72
+ SELECT table_name
73
+ FROM information_schema.tables
74
+ WHERE table_schema = 'public'
75
+ AND table_type = 'BASE TABLE'
76
+ AND table_name != ALL(${excludedTables})
77
+ ORDER BY table_name
78
+ `;
79
+ const dbTableNames = new Set(dbTables.map((t) => t.table_name));
80
+ // 2. Get all columns per table from the database
81
+ const dbColumns = await sql `
82
+ SELECT table_name, column_name, data_type, is_nullable, column_default,
83
+ character_maximum_length, numeric_precision, numeric_scale
84
+ FROM information_schema.columns
85
+ WHERE table_schema = 'public'
86
+ AND table_name != ALL(${excludedTables})
87
+ ORDER BY table_name, ordinal_position
88
+ `;
89
+ const dbColumnsByTable = new Map();
90
+ for (const col of dbColumns) {
91
+ if (!dbColumnsByTable.has(col.table_name)) {
92
+ dbColumnsByTable.set(col.table_name, []);
93
+ }
94
+ dbColumnsByTable.get(col.table_name).push(col);
95
+ }
96
+ // 3. Get all schema definitions from baasix
97
+ const allSchemaDefinitions = new Map();
98
+ // Access internal schemaDefinitions via the public API
99
+ const schemasMap = schemaManager.getAllSchemas();
100
+ for (const [collectionName] of schemasMap) {
101
+ const schemaDef = await schemaManager.getSchemaDefinition(collectionName);
102
+ if (schemaDef) {
103
+ allSchemaDefinitions.set(collectionName, schemaDef);
104
+ }
105
+ }
106
+ const schemaCollectionNames = new Set(allSchemaDefinitions.keys());
107
+ // 4. Compute diffs
108
+ // Tables in schema but not in database
109
+ const tablesOnlyInSchema = [];
110
+ for (const name of schemaCollectionNames) {
111
+ if (!dbTableNames.has(name)) {
112
+ tablesOnlyInSchema.push(name);
113
+ }
114
+ }
115
+ // Tables in database but not in schema
116
+ const tablesOnlyInDb = [];
117
+ for (const name of dbTableNames) {
118
+ if (!schemaCollectionNames.has(name)) {
119
+ tablesOnlyInDb.push(name);
120
+ }
121
+ }
122
+ // Per-table field diffs (only for tables that exist in both)
123
+ const fieldDiffs = [];
124
+ for (const [collectionName, schemaDef] of allSchemaDefinitions) {
125
+ if (!dbTableNames.has(collectionName))
126
+ continue; // table doesn't exist in DB
127
+ const expectedColumns = getExpectedColumns(schemaDef);
128
+ const actualColumns = dbColumnsByTable.get(collectionName) || [];
129
+ const actualColumnNames = new Set(actualColumns.map((c) => c.column_name));
130
+ const fieldsOnlyInSchema = [];
131
+ const fieldsOnlyInDb = [];
132
+ // Fields in schema but not in database
133
+ for (const [colName, meta] of expectedColumns) {
134
+ if (!actualColumnNames.has(colName)) {
135
+ fieldsOnlyInSchema.push({ column: colName, type: meta.type, fromField: meta.fromField });
136
+ }
137
+ }
138
+ // Fields in database but not in schema
139
+ for (const col of actualColumns) {
140
+ if (!expectedColumns.has(col.column_name)) {
141
+ fieldsOnlyInDb.push({
142
+ column: col.column_name,
143
+ dataType: col.data_type,
144
+ isNullable: col.is_nullable,
145
+ columnDefault: col.column_default,
146
+ });
147
+ }
148
+ }
149
+ if (fieldsOnlyInSchema.length > 0 || fieldsOnlyInDb.length > 0) {
150
+ fieldDiffs.push({
151
+ collection: collectionName,
152
+ fieldsOnlyInSchema,
153
+ fieldsOnlyInDb,
154
+ });
155
+ }
156
+ }
157
+ // 5. Summary
158
+ const summary = {
159
+ totalSchemaCollections: schemaCollectionNames.size,
160
+ totalDbTables: dbTableNames.size,
161
+ tablesOnlyInSchemaCount: tablesOnlyInSchema.length,
162
+ tablesOnlyInDbCount: tablesOnlyInDb.length,
163
+ collectionsWithFieldDiffs: fieldDiffs.length,
164
+ };
165
+ return res.status(200).json({
166
+ data: {
167
+ summary,
168
+ tablesOnlyInSchema,
169
+ tablesOnlyInDb,
170
+ fieldDiffs,
171
+ },
172
+ });
173
+ }
174
+ catch (error) {
175
+ next(error);
176
+ }
177
+ });
178
+ /**
179
+ * DELETE /utils/schema-diff/table/schema/:collection
180
+ * Remove a collection from schema definitions (keeps database table).
181
+ */
182
+ app.delete("/utils/schema-diff/table/schema/:collection", adminOnly, async (req, res, next) => {
183
+ try {
184
+ const { collection } = req.params;
185
+ // Verify collection exists in schema
186
+ const schemaDef = await schemaManager.getSchemaDefinition(collection);
187
+ if (!schemaDef) {
188
+ throw new APIError(`Collection '${collection}' not found in schema definitions`, 404);
189
+ }
190
+ // Prevent deleting system schemas
191
+ if (collection.startsWith("baasix_")) {
192
+ throw new APIError(`Cannot remove system schema '${collection}'`, 400);
193
+ }
194
+ await schemaManager.removeSchemaDefinition(collection);
195
+ return res.status(200).json({
196
+ data: { message: `Schema definition '${collection}' removed successfully` },
197
+ });
198
+ }
199
+ catch (error) {
200
+ next(error);
201
+ }
202
+ });
203
+ /**
204
+ * DELETE /utils/schema-diff/table/db/:table
205
+ * Drop a table from the database (keeps schema definition).
206
+ */
207
+ app.delete("/utils/schema-diff/table/db/:table", adminOnly, async (req, res, next) => {
208
+ try {
209
+ const { table } = req.params;
210
+ const sql = getSqlClient();
211
+ // Validate table name: only allow alphanumeric and underscores
212
+ if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(table)) {
213
+ throw new APIError("Invalid table name", 400);
214
+ }
215
+ // Verify table exists
216
+ const exists = await sql `
217
+ SELECT EXISTS (
218
+ SELECT FROM information_schema.tables
219
+ WHERE table_schema = 'public' AND table_name = ${table}
220
+ )
221
+ `;
222
+ if (!exists[0].exists) {
223
+ throw new APIError(`Table '${table}' not found in database`, 404);
224
+ }
225
+ // Prevent dropping system tables
226
+ if (table.startsWith("baasix_")) {
227
+ throw new APIError(`Cannot drop system table '${table}'`, 400);
228
+ }
229
+ await sql.unsafe(`DROP TABLE IF EXISTS "${table}" CASCADE`);
230
+ return res.status(200).json({
231
+ data: { message: `Table '${table}' dropped from database successfully` },
232
+ });
233
+ }
234
+ catch (error) {
235
+ next(error);
236
+ }
237
+ });
238
+ /**
239
+ * DELETE /utils/schema-diff/field/schema/:collection/:field
240
+ * Remove a field from a schema definition (keeps database column).
241
+ */
242
+ app.delete("/utils/schema-diff/field/schema/:collection/:field", adminOnly, async (req, res, next) => {
243
+ try {
244
+ const { collection, field } = req.params;
245
+ const schemaDef = await schemaManager.getSchemaDefinition(collection);
246
+ if (!schemaDef) {
247
+ throw new APIError(`Collection '${collection}' not found in schema definitions`, 404);
248
+ }
249
+ if (!schemaDef.fields || !schemaDef.fields[field]) {
250
+ throw new APIError(`Field '${field}' not found in schema for '${collection}'`, 404);
251
+ }
252
+ // Remove the field from schema
253
+ const updatedSchema = { ...schemaDef };
254
+ const updatedFields = { ...updatedSchema.fields };
255
+ delete updatedFields[field];
256
+ updatedSchema.fields = updatedFields;
257
+ // Persist updated schema
258
+ const db = getDatabase();
259
+ const { pgTable, text, jsonb, timestamp } = await import("drizzle-orm/pg-core");
260
+ const { eq } = await import("drizzle-orm");
261
+ const baasixSchemaDefinition = pgTable("baasix_SchemaDefinition", {
262
+ collectionName: text("collectionName").primaryKey().notNull(),
263
+ schema: jsonb("schema").notNull(),
264
+ createdAt: timestamp("createdAt", { withTimezone: true }).defaultNow(),
265
+ updatedAt: timestamp("updatedAt", { withTimezone: true }).defaultNow(),
266
+ });
267
+ await db
268
+ .update(baasixSchemaDefinition)
269
+ .set({ schema: updatedSchema, updatedAt: new Date() })
270
+ .where(eq(baasixSchemaDefinition.collectionName, collection));
271
+ // Update in-memory schema definition
272
+ // Re-initialize the model so in-memory state matches
273
+ await schemaManager.updateModel(collection, updatedSchema);
274
+ return res.status(200).json({
275
+ data: { message: `Field '${field}' removed from schema definition of '${collection}'` },
276
+ });
277
+ }
278
+ catch (error) {
279
+ next(error);
280
+ }
281
+ });
282
+ /**
283
+ * DELETE /utils/schema-diff/field/db/:collection/:column
284
+ * Drop a column from a database table (keeps schema field).
285
+ */
286
+ app.delete("/utils/schema-diff/field/db/:collection/:column", adminOnly, async (req, res, next) => {
287
+ try {
288
+ const { collection, column } = req.params;
289
+ const sql = getSqlClient();
290
+ // Validate names
291
+ if (!/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(collection) || !/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(column)) {
292
+ throw new APIError("Invalid table or column name", 400);
293
+ }
294
+ // Verify column exists
295
+ const colExists = await sql `
296
+ SELECT EXISTS (
297
+ SELECT FROM information_schema.columns
298
+ WHERE table_schema = 'public'
299
+ AND table_name = ${collection}
300
+ AND column_name = ${column}
301
+ )
302
+ `;
303
+ if (!colExists[0].exists) {
304
+ throw new APIError(`Column '${column}' not found in table '${collection}'`, 404);
305
+ }
306
+ await sql.unsafe(`ALTER TABLE "${collection}" DROP COLUMN IF EXISTS "${column}" CASCADE`);
307
+ return res.status(200).json({
308
+ data: { message: `Column '${column}' dropped from table '${collection}'` },
309
+ });
310
+ }
311
+ catch (error) {
312
+ next(error);
313
+ }
314
+ });
315
+ };
316
+ export default {
317
+ id: "schema-diff",
318
+ handler: registerEndpoint,
319
+ };
320
+ //# sourceMappingURL=schema-diff.route.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema-diff.route.js","sourceRoot":"","sources":["../../baasix/routes/schema-diff.route.ts"],"names":[],"mappings":"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"}
@@ -1,3 +1,4 @@
1
+ import { Transaction } from '../utils/db.js';
1
2
  import type { QueryOptions as BaseQueryOptions, ServiceParams as BaseServiceParams, OperationOptions, ReadResult } from '../types/index.js';
2
3
  export type QueryOptions = BaseQueryOptions;
3
4
  export type ServiceParams = BaseServiceParams;
@@ -131,7 +132,7 @@ export declare class ItemsService {
131
132
  /**
132
133
  * Read records by query
133
134
  */
134
- readByQuery(query?: QueryOptions, bypassPermissions?: boolean): Promise<ReadResult>;
135
+ readByQuery(query?: QueryOptions, bypassPermissions?: boolean, transaction?: Transaction): Promise<ReadResult>;
135
136
  /**
136
137
  * Execute aggregate query
137
138
  */
@@ -139,7 +140,7 @@ export declare class ItemsService {
139
140
  /**
140
141
  * Read a single record by ID
141
142
  */
142
- readOne(id: string | number, query?: QueryOptions, bypassPermissions?: boolean): Promise<any>;
143
+ readOne(id: string | number, query?: QueryOptions, bypassPermissions?: boolean, transaction?: Transaction): Promise<any>;
143
144
  /**
144
145
  * Create a new record
145
146
  * Uses createOneCore for the transactional logic and executes after hooks after commit
@@ -242,11 +243,11 @@ export declare class ItemsService {
242
243
  /**
243
244
  * Alias for readByQuery
244
245
  */
245
- list(query?: QueryOptions, bypassPermissions?: boolean): Promise<ReadResult>;
246
+ list(query?: QueryOptions, bypassPermissions?: boolean, transaction?: Transaction): Promise<ReadResult>;
246
247
  /**
247
248
  * Alias for readOne
248
249
  */
249
- read(id: string | number, query?: QueryOptions, bypassPermissions?: boolean): Promise<any>;
250
+ read(id: string | number, query?: QueryOptions, bypassPermissions?: boolean, transaction?: Transaction): Promise<any>;
250
251
  /**
251
252
  * Alias for createOne
252
253
  */
@@ -1 +1 @@
1
- {"version":3,"file":"ItemsService.d.ts","sourceRoot":"","sources":["../../baasix/services/ItemsService.ts"],"names":[],"mappings":"AAuCA,OAAO,KAAK,EAIV,YAAY,IAAI,gBAAgB,EAChC,aAAa,IAAI,iBAAiB,EAClC,gBAAgB,EAChB,UAAU,EACX,MAAM,mBAAmB,CAAC;AAS3B,MAAM,MAAM,YAAY,GAAG,gBAAgB,CAAC;AAC5C,MAAM,MAAM,aAAa,GAAG,iBAAiB,CAAC;AAC9C,YAAY,EAAE,UAAU,EAAE,gBAAgB,EAAE,CAAC;AAE7C;;;;;;;;;;GAUG;AACH,qBAAa,YAAY;IACvB,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,cAAc,CAAC,CAAkC;IACzD,OAAO,CAAC,MAAM,CAAC,CAAkB;IACjC,OAAO,CAAC,KAAK,CAAU;IACvB,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,aAAa,CAAU;gBAEnB,UAAU,EAAE,MAAM,EAAE,MAAM,GAAE,aAAkB;IAgB1D;;OAEG;IACH,OAAO,CAAC,mBAAmB;IAI3B;;;OAGG;IACH,OAAO,CAAC,OAAO;IAWf;;;OAGG;IACH,OAAO,CAAC,gBAAgB;IA2CxB;;;OAGG;IACH,OAAO,CAAC,iBAAiB;IAyCzB;;OAEG;IACH,OAAO,CAAC,gBAAgB;IA2BxB;;;OAGG;YACW,gBAAgB;IAsC9B;;;OAGG;YACW,eAAe;IAwB7B;;;OAGG;IACH,OAAO,CAAC,SAAS;IAiBjB;;OAEG;YACW,eAAe;IA4C7B;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAaxB;;OAEG;YACW,0BAA0B;IAiBxC;;OAEG;YACW,+BAA+B;IAsC7C;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAexB;;;;;;;;;;;;;OAaG;IACH,OAAO,CAAC,6BAA6B;IAqGrC;;OAEG;YACW,UAAU;IAyTxB;;;;;;;OAOG;IACH,OAAO,CAAC,uBAAuB;IA8D/B;;OAEG;YACW,qBAAqB;IAkDnC;;;OAGG;IACH,OAAO,CAAC,8BAA8B;IA+BtC;;OAEG;YACW,gBAAgB;IAY9B;;OAEG;YACW,uBAAuB;IA4QrC;;OAEG;IACH,OAAO,CAAC,oBAAoB;IAwC5B;;OAEG;IACG,WAAW,CACf,KAAK,GAAE,YAAiB,EACxB,iBAAiB,GAAE,OAAe,GACjC,OAAO,CAAC,UAAU,CAAC;IAkQtB;;OAEG;YACW,qBAAqB;IA4TnC;;OAEG;IACG,OAAO,CACX,EAAE,EAAE,MAAM,GAAG,MAAM,EACnB,KAAK,GAAE,YAAiB,EACxB,iBAAiB,GAAE,OAAe,GACjC,OAAO,CAAC,GAAG,CAAC;IAwGf;;;OAGG;IACG,SAAS,CACb,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EACzB,OAAO,GAAE,gBAAqB,GAC7B,OAAO,CAAC,MAAM,GAAG,MAAM,CAAC;IAsD3B;;;;;;;;;OASG;YACW,aAAa;IAmM3B;;;;;;;;;;;;;;;;OAgBG;IACG,UAAU,CACd,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,EAC5B,OAAO,GAAE,gBAAqB,GAC7B,OAAO,CAAC,CAAC,MAAM,GAAG,MAAM,CAAC,EAAE,CAAC;IAuE/B;;;;;;;;;;OAUG;YACW,aAAa;IAwP3B;;;;;;;;;;;;OAYG;IACG,UAAU,CACd,OAAO,EAAE;QAAE,EAAE,EAAE,MAAM,GAAG,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;QAAC,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAA;KAAE,EAAE,EAClF,OAAO,GAAE,gBAAqB,GAC7B,OAAO,CAAC,CAAC,MAAM,GAAG,MAAM,CAAC,EAAE,CAAC;IAgF/B;;;OAGG;IACG,SAAS,CACb,EAAE,EAAE,MAAM,GAAG,MAAM,EACnB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EACzB,OAAO,GAAE,gBAAqB,GAC7B,OAAO,CAAC,MAAM,GAAG,MAAM,CAAC;IAwD3B;;;;;;;;;OASG;YACW,aAAa;IAsI3B;;;;;;;;;;;;OAYG;IACG,UAAU,CACd,GAAG,EAAE,CAAC,MAAM,GAAG,MAAM,CAAC,EAAE,EACxB,OAAO,GAAE,gBAAqB,GAC7B,OAAO,CAAC,CAAC,MAAM,GAAG,MAAM,CAAC,EAAE,CAAC;IAiE/B;;;OAGG;IACG,SAAS,CACb,EAAE,EAAE,MAAM,GAAG,MAAM,EACnB,OAAO,GAAE,gBAAqB,GAC7B,OAAO,CAAC,MAAM,GAAG,MAAM,CAAC;IAkD3B;;OAEG;IACG,IAAI,CAAC,KAAK,GAAE,YAAiB,EAAE,iBAAiB,GAAE,OAAe,GAAG,OAAO,CAAC,UAAU,CAAC;IAI7F;;OAEG;IACG,IAAI,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,EAAE,KAAK,GAAE,YAAiB,EAAE,iBAAiB,GAAE,OAAe,GAAG,OAAO,CAAC,GAAG,CAAC;IAI3G;;OAEG;IACG,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,OAAO,GAAE,gBAAqB,GAAG,OAAO,CAAC,MAAM,GAAG,MAAM,CAAC;IAIjG;;OAEG;IACG,MAAM,CACV,EAAE,EAAE,MAAM,GAAG,MAAM,EACnB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EACzB,OAAO,GAAE,gBAAqB,GAC7B,OAAO,CAAC,MAAM,GAAG,MAAM,CAAC;IAI3B;;OAEG;IACG,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,EAAE,OAAO,GAAE,gBAAqB,GAAG,OAAO,CAAC,MAAM,GAAG,MAAM,CAAC;IAI3F;;;OAGG;IACG,OAAO,CACX,EAAE,EAAE,MAAM,GAAG,MAAM,EACnB,OAAO,GAAE,gBAAqB,GAC7B,OAAO,CAAC,MAAM,GAAG,MAAM,CAAC;IAuE3B;;;;;;OAMG;YACW,cAAc;IAqE5B;;;;OAIG;YACW,iBAAiB;CAoDhC;AAED,eAAe,YAAY,CAAC"}
1
+ {"version":3,"file":"ItemsService.d.ts","sourceRoot":"","sources":["../../baasix/services/ItemsService.ts"],"names":[],"mappings":"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"}
@@ -833,7 +833,8 @@ export class ItemsService {
833
833
  /**
834
834
  * Read with two-query approach for HasMany sorting
835
835
  */
836
- async readWithHasManyHandling(query, whereClause, orderByClause, processedIncludes, limit, offset, isAdmin, bypassPermissions, filterJoins = []) {
836
+ async readWithHasManyHandling(query, whereClause, orderByClause, processedIncludes, limit, offset, isAdmin, bypassPermissions, filterJoins = [], transaction) {
837
+ const dbClient = transaction || db;
837
838
  console.log('[ItemsService] Using Drizzle query builder for HasMany sorting/filtering');
838
839
  // STEP 1: Build ID query using PostgreSQL DISTINCT ON for proper deduplication
839
840
  // DISTINCT ON ensures we get unique IDs BEFORE applying LIMIT, not after
@@ -889,7 +890,7 @@ export class ItemsService {
889
890
  ? `DISTINCT ON (${distinctOnFields.join(', ')}) ${pkField}`
890
891
  : pkField;
891
892
  console.log(`[ItemsService] Using DISTINCT ON with fields: ${distinctOnFields.join(', ')}`);
892
- let idQuery = db
893
+ let idQuery = dbClient
893
894
  .select({ [this.primaryKey]: sql.raw(distinctOnClause) })
894
895
  .from(this.table)
895
896
  .$dynamic();
@@ -978,7 +979,7 @@ export class ItemsService {
978
979
  }
979
980
  const { selectColumns } = buildSelectWithJoins(this.table, directFields, processedIncludes // Use original includes with separate: true for HasMany
980
981
  );
981
- let fullQuery = db.select(selectColumns).from(this.table);
982
+ let fullQuery = dbClient.select(selectColumns).from(this.table);
982
983
  // Apply joins (only BelongsTo/HasOne, not HasMany)
983
984
  for (const include of processedIncludes) {
984
985
  if (include.separate)
@@ -998,7 +999,7 @@ export class ItemsService {
998
999
  let finalRecords = records;
999
1000
  if (hasSeparateQueries(processedIncludes)) {
1000
1001
  // This handles both nesting joined relations and loading separate ones
1001
- finalRecords = await loadSeparateRelations(db, records, processedIncludes, this.collection);
1002
+ finalRecords = await loadSeparateRelations(dbClient, records, processedIncludes, this.collection);
1002
1003
  }
1003
1004
  else {
1004
1005
  // No separate queries, but we still need to nest joined BelongsTo/HasOne relations
@@ -1008,7 +1009,7 @@ export class ItemsService {
1008
1009
  const recordMap = new Map(finalRecords.map(r => [r[this.primaryKey], r]));
1009
1010
  const orderedRecords = ids.map(id => recordMap.get(id)).filter(r => r != null);
1010
1011
  // Get total count using Drizzle query builder (same joins as ID query)
1011
- let countQuery = db
1012
+ let countQuery = dbClient
1012
1013
  .select({ count: sql `COUNT(DISTINCT ${this.getPrimaryKeyColumn()})`.mapWith(Number) })
1013
1014
  .from(this.table)
1014
1015
  .$dynamic();
@@ -1085,15 +1086,16 @@ export class ItemsService {
1085
1086
  /**
1086
1087
  * Read records by query
1087
1088
  */
1088
- async readByQuery(query = {}, bypassPermissions = false) {
1089
+ async readByQuery(query = {}, bypassPermissions = false, transaction) {
1090
+ const dbClient = transaction || db;
1089
1091
  // Execute before-read hooks
1090
- let hookData = await hooksManager.executeHooks(this.collection, 'items.read', this.accountability, { query });
1092
+ let hookData = await hooksManager.executeHooks(this.collection, 'items.read', this.accountability, { query, transaction });
1091
1093
  const modifiedQuery = hookData.query;
1092
1094
  try {
1093
1095
  const isAdmin = await this.isAdministrator();
1094
1096
  // Check if this is an aggregate query
1095
1097
  if (modifiedQuery.aggregate) {
1096
- return await this.executeAggregateQuery(modifiedQuery, isAdmin, bypassPermissions);
1098
+ return await this.executeAggregateQuery(modifiedQuery, isAdmin, bypassPermissions, transaction);
1097
1099
  }
1098
1100
  // Build query components
1099
1101
  const { whereClause, orderByClause, selectColumns, joins, processedIncludes, limit, offset, filterJoins = [], userRequestedFields = [], userRequestedIncludes = [] } = await this.buildQuery(modifiedQuery, {
@@ -1130,7 +1132,7 @@ export class ItemsService {
1130
1132
  // This matches Sequelize's approach of using a two-query pattern
1131
1133
  if (needsHasManyHandling || hasFilterJoins) {
1132
1134
  console.log(`[ItemsService] Using readWithHasManyHandling for ${needsHasManyHandling ? 'HasMany sorting' : 'HasMany filtering'}`);
1133
- return await this.readWithHasManyHandling(modifiedQuery, whereClause, orderByClause, processedIncludes, limit, offset, isAdmin, bypassPermissions, filterJoins);
1135
+ return await this.readWithHasManyHandling(modifiedQuery, whereClause, orderByClause, processedIncludes, limit, offset, isAdmin, bypassPermissions, filterJoins, transaction);
1134
1136
  }
1135
1137
  // Build base query
1136
1138
  console.log(`[ItemsService.readByQuery] Building base query for ${this.collection}, selectColumns has ${Object.keys(selectColumns).length} keys`);
@@ -1138,7 +1140,7 @@ export class ItemsService {
1138
1140
  console.error(`[ItemsService.readByQuery] ERROR: selectColumns is EMPTY for ${this.collection}! This will cause SQL syntax error.`);
1139
1141
  console.error(`[ItemsService.readByQuery] query:`, query);
1140
1142
  }
1141
- let baseQuery = db.select(selectColumns).from(this.table);
1143
+ let baseQuery = dbClient.select(selectColumns).from(this.table);
1142
1144
  // Apply filterJoins using Drizzle's query builder
1143
1145
  // FilterJoins are created when filtering by relation paths (e.g., "userRoles.role.name")
1144
1146
  if (hasFilterJoins) {
@@ -1206,7 +1208,7 @@ export class ItemsService {
1206
1208
  // Load separate relations (HasMany, BelongsToMany) and nest joined relations
1207
1209
  let processedRecords = records;
1208
1210
  if (hasSeparateQueries(processedIncludes)) {
1209
- processedRecords = await loadSeparateRelations(db, records, processedIncludes, this.collection);
1211
+ processedRecords = await loadSeparateRelations(dbClient, records, processedIncludes, this.collection);
1210
1212
  }
1211
1213
  else {
1212
1214
  // No separate queries, but we still need to nest joined BelongsTo/HasOne relations
@@ -1219,7 +1221,7 @@ export class ItemsService {
1219
1221
  console.error(`[ItemsService.readByQuery] Primary key name:`, this.primaryKey);
1220
1222
  console.error(`[ItemsService.readByQuery] Available columns:`, Object.keys(this.table).filter(k => !k.startsWith('_')));
1221
1223
  }
1222
- let countQuery = db.select({ count: sql `COUNT(DISTINCT ${primaryKeyColumn})` }).from(this.table);
1224
+ let countQuery = dbClient.select({ count: sql `COUNT(DISTINCT ${primaryKeyColumn})` }).from(this.table);
1223
1225
  // Apply same joins and where for count
1224
1226
  if (hasFilterJoins) {
1225
1227
  // Use filterJoins for count query as well
@@ -1266,7 +1268,7 @@ export class ItemsService {
1266
1268
  // Sanitize auto-added fields (remove fields user didn't request)
1267
1269
  const sanitizedRecords = this.sanitizeAutoAddedFields(strippedRecords, userRequestedFields, userRequestedIncludes);
1268
1270
  // Execute after-read hooks
1269
- hookData = await hooksManager.executeHooks(this.collection, 'items.read.after', this.accountability, { query: modifiedQuery, result: { data: sanitizedRecords, totalCount } });
1271
+ hookData = await hooksManager.executeHooks(this.collection, 'items.read.after', this.accountability, { query: modifiedQuery, result: { data: sanitizedRecords, totalCount }, transaction });
1270
1272
  return hookData.result;
1271
1273
  }
1272
1274
  catch (error) {
@@ -1277,7 +1279,8 @@ export class ItemsService {
1277
1279
  /**
1278
1280
  * Execute aggregate query
1279
1281
  */
1280
- async executeAggregateQuery(query, isAdmin, bypassPermissions) {
1282
+ async executeAggregateQuery(query, isAdmin, bypassPermissions, transaction) {
1283
+ const dbClient = transaction || db;
1281
1284
  const { aggregate, groupBy = [], filter = {}, sort, limit: queryLimit, page: queryPage } = query;
1282
1285
  if (!aggregate) {
1283
1286
  throw new APIError('Aggregate query requires aggregate parameter', 400);
@@ -1406,7 +1409,7 @@ export class ItemsService {
1406
1409
  let results;
1407
1410
  if (allJoins.length > 0) {
1408
1411
  // Build aggregate query using Drizzle query builder
1409
- let aggregateQuery = db.select(selectObj).from(this.table).$dynamic();
1412
+ let aggregateQuery = dbClient.select(selectObj).from(this.table).$dynamic();
1410
1413
  // Apply joins (same as filterJoins)
1411
1414
  for (const join of allJoins) {
1412
1415
  // Create aliased table with the exact alias
@@ -1461,7 +1464,7 @@ export class ItemsService {
1461
1464
  }
1462
1465
  else {
1463
1466
  // No relation joins - use standard Drizzle API
1464
- let aggregateQuery = db.select(selectObj).from(this.table).$dynamic();
1467
+ let aggregateQuery = dbClient.select(selectObj).from(this.table).$dynamic();
1465
1468
  if (whereClause) {
1466
1469
  aggregateQuery = aggregateQuery.where(whereClause);
1467
1470
  }
@@ -1513,7 +1516,7 @@ export class ItemsService {
1513
1516
  const groupExpr = buildGroupByExpressions([groupField], undefined, pathToAliasMap, this.collection)[0];
1514
1517
  countSelect[groupField] = groupExpr;
1515
1518
  }
1516
- let countQuery = db.select(countSelect).from(this.table).$dynamic();
1519
+ let countQuery = dbClient.select(countSelect).from(this.table).$dynamic();
1517
1520
  // Apply same joins
1518
1521
  for (const join of allJoins) {
1519
1522
  const aliasedTable = alias(join.table, join.alias);
@@ -1542,13 +1545,14 @@ export class ItemsService {
1542
1545
  /**
1543
1546
  * Read a single record by ID
1544
1547
  */
1545
- async readOne(id, query = {}, bypassPermissions = false) {
1548
+ async readOne(id, query = {}, bypassPermissions = false, transaction) {
1549
+ const dbClient = transaction || db;
1546
1550
  const parsedId = this.parseId(id);
1547
1551
  if (!parsedId) {
1548
1552
  throw new APIError('Invalid ID', 400);
1549
1553
  }
1550
1554
  // Execute before-read-one hooks
1551
- let hookData = await hooksManager.executeHooks(this.collection, 'items.read.one', this.accountability, { id: parsedId, query });
1555
+ let hookData = await hooksManager.executeHooks(this.collection, 'items.read.one', this.accountability, { id: parsedId, query, transaction });
1552
1556
  try {
1553
1557
  const isAdmin = await this.isAdministrator();
1554
1558
  // Build query with ID filter
@@ -1559,7 +1563,7 @@ export class ItemsService {
1559
1563
  idFilter: parsedId
1560
1564
  });
1561
1565
  // Build base query
1562
- let baseQuery = db.select(selectColumns).from(this.table);
1566
+ let baseQuery = dbClient.select(selectColumns).from(this.table);
1563
1567
  // Apply filterJoins first (these come from relation path filters in WHERE clause)
1564
1568
  // use Drizzle query builder methods instead of raw SQL
1565
1569
  if (filterJoins && filterJoins.length > 0) {
@@ -1592,7 +1596,7 @@ export class ItemsService {
1592
1596
  // Load separate relations and nest joined relations
1593
1597
  let finalRecords = records;
1594
1598
  if (hasSeparateQueries(processedIncludes)) {
1595
- finalRecords = await loadSeparateRelations(db, records, processedIncludes, this.collection);
1599
+ finalRecords = await loadSeparateRelations(dbClient, records, processedIncludes, this.collection);
1596
1600
  }
1597
1601
  else {
1598
1602
  // No separate queries, but we still need to nest joined BelongsTo/HasOne relations
@@ -1602,7 +1606,7 @@ export class ItemsService {
1602
1606
  // Strip hidden fields from the document
1603
1607
  const strippedDocument = fieldUtils.stripHiddenFields(this.collection, document);
1604
1608
  // Execute after-read-one hooks
1605
- hookData = await hooksManager.executeHooks(this.collection, 'items.read.one.after', this.accountability, { id: parsedId, query, document: strippedDocument });
1609
+ hookData = await hooksManager.executeHooks(this.collection, 'items.read.one.after', this.accountability, { id: parsedId, query, document: strippedDocument, transaction });
1606
1610
  return hookData.document;
1607
1611
  }
1608
1612
  catch (error) {
@@ -2409,14 +2413,14 @@ export class ItemsService {
2409
2413
  /**
2410
2414
  * Alias for readByQuery
2411
2415
  */
2412
- async list(query = {}, bypassPermissions = false) {
2413
- return this.readByQuery(query, bypassPermissions);
2416
+ async list(query = {}, bypassPermissions = false, transaction) {
2417
+ return this.readByQuery(query, bypassPermissions, transaction);
2414
2418
  }
2415
2419
  /**
2416
2420
  * Alias for readOne
2417
2421
  */
2418
- async read(id, query = {}, bypassPermissions = false) {
2419
- return this.readOne(id, query, bypassPermissions);
2422
+ async read(id, query = {}, bypassPermissions = false, transaction) {
2423
+ return this.readOne(id, query, bypassPermissions, transaction);
2420
2424
  }
2421
2425
  /**
2422
2426
  * Alias for createOne