json-rest-api 1.0.13 → 1.0.15

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 (118) hide show
  1. package/connectors_ideas/fastify.js +66 -68
  2. package/connectors_ideas/koa-plugin.js +50 -51
  3. package/eslint.config.js +13 -0
  4. package/examples/url-override-example.js +63 -63
  5. package/index.js +13 -13
  6. package/lib/enhanced-logger.js +51 -51
  7. package/lib/error-formatter.js +61 -61
  8. package/lib/rest-api-errors.js +28 -28
  9. package/package.json +9 -5
  10. package/plugins/core/bulk-operations-plugin.js +142 -145
  11. package/plugins/core/connectors/express-plugin.js +210 -217
  12. package/plugins/core/connectors/lib/busboy-detector.js +84 -84
  13. package/plugins/core/connectors/lib/formidable-detector.js +50 -50
  14. package/plugins/core/connectors/lib/request-helpers.js +74 -74
  15. package/plugins/core/file-handling-plugin.js +125 -125
  16. package/plugins/core/lib/anyapi/anyapi-registry.js +254 -254
  17. package/plugins/core/lib/anyapi/query/anyapi-query-adapter.js +287 -287
  18. package/plugins/core/lib/anyapi/schema-utils.js +101 -101
  19. package/plugins/core/lib/anyapi/utils/descriptor-helpers.js +41 -41
  20. package/plugins/core/lib/dbTablesOperations.js +134 -135
  21. package/plugins/core/lib/fractional-positioning.js +74 -76
  22. package/plugins/core/lib/querying/knex-cross-table-search.js +320 -320
  23. package/plugins/core/lib/querying/knex-json-api-transformers-querying.js +144 -144
  24. package/plugins/core/lib/querying/knex-pagination-helpers.js +164 -164
  25. package/plugins/core/lib/querying/knex-process-includes.js +50 -50
  26. package/plugins/core/lib/querying/knex-query-helpers-base.js +31 -31
  27. package/plugins/core/lib/querying/knex-query-helpers.js +451 -452
  28. package/plugins/core/lib/querying/knex-relationship-includes.js +1160 -1170
  29. package/plugins/core/lib/querying/knex-window-queries.js +89 -89
  30. package/plugins/core/lib/querying/url-helpers.js +35 -35
  31. package/plugins/core/lib/querying-writing/compile-schemas.js +97 -98
  32. package/plugins/core/lib/querying-writing/connectors-query-parser.js +31 -31
  33. package/plugins/core/lib/querying-writing/database-capabilities.js +94 -94
  34. package/plugins/core/lib/querying-writing/database-value-normalizers.js +76 -76
  35. package/plugins/core/lib/querying-writing/default-data-helpers.js +27 -27
  36. package/plugins/core/lib/querying-writing/field-utils.js +44 -42
  37. package/plugins/core/lib/querying-writing/knex-constants.js +15 -16
  38. package/plugins/core/lib/querying-writing/knex-field-helpers.js +117 -115
  39. package/plugins/core/lib/querying-writing/knex-json-api-transformers.js +41 -41
  40. package/plugins/core/lib/querying-writing/payload-validators.js +275 -275
  41. package/plugins/core/lib/querying-writing/schema-helpers.js +129 -129
  42. package/plugins/core/lib/querying-writing/scope-validations.js +143 -146
  43. package/plugins/core/lib/querying-writing/simplified-helpers.js +133 -129
  44. package/plugins/core/lib/storage/storage-adapter.js +68 -68
  45. package/plugins/core/lib/writing/knex-json-api-transformers-writing.js +31 -31
  46. package/plugins/core/lib/writing/many-to-many-manipulations.js +81 -81
  47. package/plugins/core/lib/writing/relationship-processor.js +58 -58
  48. package/plugins/core/rest-api-access.js +8 -8
  49. package/plugins/core/rest-api-anyapi-knex-plugin.js +920 -920
  50. package/plugins/core/rest-api-cors-plugin.js +170 -171
  51. package/plugins/core/rest-api-knex-plugin.js +482 -493
  52. package/plugins/core/rest-api-multihome-plugin.js +247 -251
  53. package/plugins/core/rest-api-plugin-hooks/compile-resource-schemas.js +5 -5
  54. package/plugins/core/rest-api-plugin-hooks/register-relationship-routes.js +20 -21
  55. package/plugins/core/rest-api-plugin-hooks/register-scope-routes.js +44 -44
  56. package/plugins/core/rest-api-plugin-hooks/turn-scope-init-into-vars.js +23 -23
  57. package/plugins/core/rest-api-plugin-hooks/validate-include-configurations.js +15 -15
  58. package/plugins/core/rest-api-plugin-methods/add-route.js +9 -9
  59. package/plugins/core/rest-api-plugin-methods/check-permissions.js +5 -6
  60. package/plugins/core/rest-api-plugin-methods/common.js +295 -297
  61. package/plugins/core/rest-api-plugin-methods/delete-relationship.js +35 -37
  62. package/plugins/core/rest-api-plugin-methods/delete.js +57 -59
  63. package/plugins/core/rest-api-plugin-methods/enrich-attributes.js +172 -171
  64. package/plugins/core/rest-api-plugin-methods/get-related.js +96 -96
  65. package/plugins/core/rest-api-plugin-methods/get-relationship.js +16 -18
  66. package/plugins/core/rest-api-plugin-methods/get.js +86 -86
  67. package/plugins/core/rest-api-plugin-methods/patch-relationship.js +16 -17
  68. package/plugins/core/rest-api-plugin-methods/patch.js +63 -64
  69. package/plugins/core/rest-api-plugin-methods/post-relationship.js +54 -55
  70. package/plugins/core/rest-api-plugin-methods/post.js +61 -62
  71. package/plugins/core/rest-api-plugin-methods/put.js +86 -87
  72. package/plugins/core/rest-api-plugin-methods/query.js +88 -88
  73. package/plugins/core/rest-api-plugin-methods/release.js +5 -6
  74. package/plugins/core/rest-api-plugin.js +76 -78
  75. package/plugins/core/rest-api-positioning-plugin.js +251 -252
  76. package/plugins/core/socketio-plugin.js +240 -236
  77. package/plugins/storage/local-storage.js +103 -103
  78. package/plugins/storage/s3-storage.js +41 -42
  79. package/quickTest.js +37 -37
  80. package/scripts/debug-anyapi-review-filter.mjs +18 -18
  81. package/scripts/inspect-anyapi-descriptor-country.mjs +10 -10
  82. package/scripts/inspect-anyapi-descriptor.mjs +10 -10
  83. package/scripts/run-docs.js +112 -113
  84. package/test-debug.js +28 -28
  85. package/tests/TEST_TEMPLATE.test.js +51 -51
  86. package/tests/anyapi-basic.test.js +42 -42
  87. package/tests/anyapi-cursor-pagination.test.js +48 -48
  88. package/tests/bulk-operations.test.js +208 -208
  89. package/tests/computed-fields.test.js +204 -204
  90. package/tests/cors.test.js +412 -413
  91. package/tests/curl.test.js +275 -276
  92. package/tests/custom-idproperty-relationships.test.js +266 -266
  93. package/tests/field-getters.test.js +132 -132
  94. package/tests/field-setters.test.js +110 -110
  95. package/tests/fixtures/api-configs.js +699 -699
  96. package/tests/helpers/storage-mode.js +32 -32
  97. package/tests/helpers/test-utils.js +76 -76
  98. package/tests/include-depth-validation.test.js +76 -76
  99. package/tests/includes.test.js +145 -145
  100. package/tests/multihome.test.js +175 -175
  101. package/tests/nested-includes.test.js +255 -255
  102. package/tests/pagination-cursor-multifield.test.js +164 -164
  103. package/tests/pagination-enhanced.test.js +225 -225
  104. package/tests/polymorphic-relationships.test.js +96 -96
  105. package/tests/polymorphic-simple.test.js +44 -44
  106. package/tests/positioning-advanced.test.js +226 -226
  107. package/tests/positioning-extended.test.js +399 -399
  108. package/tests/positioning-multifilter.test.js +204 -204
  109. package/tests/positioning.test.js +266 -266
  110. package/tests/queries.test.js +184 -184
  111. package/tests/query-limits.test.js +154 -155
  112. package/tests/relationship-endpoints.test.js +127 -127
  113. package/tests/relationships.test.js +178 -179
  114. package/tests/rest-api-access.test.js +86 -86
  115. package/tests/rest-api.test.js +103 -103
  116. package/tests/searchschema-merge.test.js +93 -93
  117. package/tests/socketio.test.js +326 -326
  118. package/tests/virtual-fields.test.js +164 -164
@@ -1,85 +1,85 @@
1
- import { RestApiValidationError, RestApiResourceError } from '../../lib/rest-api-errors.js';
1
+ import { RestApiValidationError, RestApiResourceError } from '../../lib/rest-api-errors.js'
2
2
 
3
3
  export const BulkOperationsPlugin = {
4
4
  name: 'bulk-operations',
5
5
  dependencies: ['rest-api'],
6
6
 
7
- async install({ api, log, addHook, addScopeMethod, helpers, pluginOptions }) {
8
- const bulkOptions = pluginOptions || {};
7
+ async install ({ api, log, addHook, addScopeMethod, helpers, pluginOptions }) {
8
+ const bulkOptions = pluginOptions || {}
9
9
  const {
10
10
  maxBulkOperations = 100,
11
11
  defaultAtomic = true,
12
12
  batchSize = 100,
13
13
  enableOptimizations = true
14
- } = bulkOptions;
14
+ } = bulkOptions
15
15
 
16
- log.info('Installing Bulk Operations plugin', { maxBulkOperations, defaultAtomic });
16
+ log.info('Installing Bulk Operations plugin', { maxBulkOperations, defaultAtomic })
17
17
 
18
18
  // Add bulk methods to each scope
19
19
  addScopeMethod('bulkPost', async ({ scope, scopeName, params, context, runHooks }) => {
20
- const { inputRecords, atomic = defaultAtomic } = params;
21
-
20
+ const { inputRecords, atomic = defaultAtomic } = params
21
+
22
22
  // Validate bulk size
23
23
  if (!Array.isArray(inputRecords) || inputRecords.length === 0) {
24
24
  throw new RestApiValidationError('Bulk operations require an array of records', {
25
25
  fields: ['data'],
26
26
  violations: [{ field: 'data', rule: 'required_array', message: 'Must be a non-empty array' }]
27
- });
27
+ })
28
28
  }
29
29
 
30
30
  if (inputRecords.length > maxBulkOperations) {
31
31
  throw new RestApiValidationError(`Bulk operations limited to ${maxBulkOperations} records`, {
32
32
  fields: ['data'],
33
- violations: [{
34
- field: 'data',
35
- rule: 'max_items',
36
- message: `Cannot process more than ${maxBulkOperations} records at once`
33
+ violations: [{
34
+ field: 'data',
35
+ rule: 'max_items',
36
+ message: `Cannot process more than ${maxBulkOperations} records at once`
37
37
  }]
38
- });
38
+ })
39
39
  }
40
40
 
41
- const results = [];
42
- const errors = [];
43
- let transaction = null;
41
+ const results = []
42
+ const errors = []
43
+ let transaction = null
44
44
 
45
45
  try {
46
46
  // Start transaction if atomic mode
47
47
  if (atomic && helpers.newTransaction) {
48
- transaction = await helpers.newTransaction();
48
+ transaction = await helpers.newTransaction()
49
49
  }
50
50
 
51
51
  // Process records in batches
52
52
  for (let i = 0; i < inputRecords.length; i += batchSize) {
53
- const batch = inputRecords.slice(i, i + batchSize);
54
-
53
+ const batch = inputRecords.slice(i, i + batchSize)
54
+
55
55
  for (let j = 0; j < batch.length; j++) {
56
- const recordIndex = i + j;
57
- const inputRecord = batch[j];
58
-
56
+ const recordIndex = i + j
57
+ const inputRecord = batch[j]
58
+
59
59
  try {
60
60
  // Create individual context for each record
61
- const recordContext = {
61
+ const recordContext = {
62
62
  ...context,
63
63
  bulkOperation: true,
64
64
  bulkIndex: recordIndex
65
- };
65
+ }
66
66
 
67
67
  // Use the existing post method with transaction
68
68
  const result = await scope.post({
69
69
  inputRecord: inputRecord.data ? inputRecord : { data: inputRecord },
70
70
  transaction
71
- }, recordContext);
71
+ }, recordContext)
72
72
 
73
73
  results.push({
74
74
  index: recordIndex,
75
75
  status: 'success',
76
76
  data: result.data
77
- });
77
+ })
78
78
  } catch (error) {
79
79
  if (atomic) {
80
80
  // In atomic mode, rollback and throw
81
- if (transaction) await transaction.rollback();
82
- throw error;
81
+ if (transaction) await transaction.rollback()
82
+ throw error
83
83
  } else {
84
84
  // In non-atomic mode, collect errors
85
85
  errors.push({
@@ -90,7 +90,7 @@ export const BulkOperationsPlugin = {
90
90
  message: error.message,
91
91
  details: error.details
92
92
  }
93
- });
93
+ })
94
94
  }
95
95
  }
96
96
  }
@@ -98,7 +98,7 @@ export const BulkOperationsPlugin = {
98
98
 
99
99
  // Commit transaction if atomic
100
100
  if (transaction) {
101
- await transaction.commit();
101
+ await transaction.commit()
102
102
  }
103
103
 
104
104
  // Build response
@@ -111,51 +111,50 @@ export const BulkOperationsPlugin = {
111
111
  failed: errors.length,
112
112
  atomic
113
113
  }
114
- };
115
-
114
+ }
116
115
  } catch (error) {
117
116
  // Ensure rollback on error
118
117
  if (transaction && !transaction.isCompleted()) {
119
- await transaction.rollback();
118
+ await transaction.rollback()
120
119
  }
121
- throw error;
120
+ throw error
122
121
  }
123
- });
122
+ })
124
123
 
125
124
  addScopeMethod('bulkPatch', async ({ scope, scopeName, params, context, runHooks }) => {
126
- const { operations, atomic = defaultAtomic } = params;
127
-
125
+ const { operations, atomic = defaultAtomic } = params
126
+
128
127
  // Validate operations
129
128
  if (!Array.isArray(operations) || operations.length === 0) {
130
129
  throw new RestApiValidationError('Bulk patch requires an array of operations', {
131
130
  fields: ['operations'],
132
131
  violations: [{ field: 'operations', rule: 'required_array', message: 'Must be a non-empty array' }]
133
- });
132
+ })
134
133
  }
135
134
 
136
135
  if (operations.length > maxBulkOperations) {
137
136
  throw new RestApiValidationError(`Bulk operations limited to ${maxBulkOperations} operations`, {
138
137
  fields: ['operations'],
139
- violations: [{
140
- field: 'operations',
141
- rule: 'max_items',
142
- message: `Cannot process more than ${maxBulkOperations} operations at once`
138
+ violations: [{
139
+ field: 'operations',
140
+ rule: 'max_items',
141
+ message: `Cannot process more than ${maxBulkOperations} operations at once`
143
142
  }]
144
- });
143
+ })
145
144
  }
146
145
 
147
- const results = [];
148
- const errors = [];
149
- let transaction = null;
146
+ const results = []
147
+ const errors = []
148
+ let transaction = null
150
149
 
151
150
  try {
152
151
  if (atomic && helpers.newTransaction) {
153
- transaction = await helpers.newTransaction();
152
+ transaction = await helpers.newTransaction()
154
153
  }
155
154
 
156
155
  for (let i = 0; i < operations.length; i++) {
157
- const operation = operations[i];
158
-
156
+ const operation = operations[i]
157
+
159
158
  // Validate operation structure
160
159
  if (!operation.id || !operation.data) {
161
160
  errors.push({
@@ -166,42 +165,42 @@ export const BulkOperationsPlugin = {
166
165
  message: 'Operation must include id and data',
167
166
  details: { operation }
168
167
  }
169
- });
168
+ })
170
169
  if (atomic) {
171
- if (transaction) await transaction.rollback();
170
+ if (transaction) await transaction.rollback()
172
171
  throw new RestApiValidationError('Invalid operation structure', {
173
172
  fields: [`operations[${i}]`],
174
- violations: [{
175
- field: `operations[${i}]`,
176
- rule: 'required_fields',
177
- message: 'Operation must include id and data'
173
+ violations: [{
174
+ field: `operations[${i}]`,
175
+ rule: 'required_fields',
176
+ message: 'Operation must include id and data'
178
177
  }]
179
- });
178
+ })
180
179
  }
181
- continue;
180
+ continue
182
181
  }
183
182
 
184
183
  try {
185
- const recordContext = {
184
+ const recordContext = {
186
185
  ...context,
187
186
  bulkOperation: true,
188
187
  bulkIndex: i
189
- };
188
+ }
190
189
 
191
190
  const result = await scope.patch({
192
191
  id: operation.id,
193
192
  inputRecord: { data: operation.data },
194
193
  transaction
195
- }, recordContext);
194
+ }, recordContext)
196
195
 
197
196
  // If patch doesn't return full record, fetch it
198
- let resultData = result.data;
197
+ let resultData = result.data
199
198
  if (!resultData && result.id) {
200
199
  const fetchedRecord = await scope.get({
201
200
  id: result.id,
202
201
  transaction
203
- }, recordContext);
204
- resultData = fetchedRecord.data;
202
+ }, recordContext)
203
+ resultData = fetchedRecord.data
205
204
  }
206
205
 
207
206
  results.push({
@@ -209,11 +208,11 @@ export const BulkOperationsPlugin = {
209
208
  id: operation.id,
210
209
  status: 'success',
211
210
  data: resultData
212
- });
211
+ })
213
212
  } catch (error) {
214
213
  if (atomic) {
215
- if (transaction) await transaction.rollback();
216
- throw error;
214
+ if (transaction) await transaction.rollback()
215
+ throw error
217
216
  } else {
218
217
  errors.push({
219
218
  index: i,
@@ -224,13 +223,13 @@ export const BulkOperationsPlugin = {
224
223
  message: error.message,
225
224
  details: error.details
226
225
  }
227
- });
226
+ })
228
227
  }
229
228
  }
230
229
  }
231
230
 
232
231
  if (transaction) {
233
- await transaction.commit();
232
+ await transaction.commit()
234
233
  }
235
234
 
236
235
  return {
@@ -242,72 +241,71 @@ export const BulkOperationsPlugin = {
242
241
  failed: errors.length,
243
242
  atomic
244
243
  }
245
- };
246
-
244
+ }
247
245
  } catch (error) {
248
246
  if (transaction && !transaction.isCompleted()) {
249
- await transaction.rollback();
247
+ await transaction.rollback()
250
248
  }
251
- throw error;
249
+ throw error
252
250
  }
253
- });
251
+ })
254
252
 
255
253
  addScopeMethod('bulkDelete', async ({ scope, scopeName, params, context, runHooks }) => {
256
- const { ids, atomic = defaultAtomic } = params;
257
-
254
+ const { ids, atomic = defaultAtomic } = params
255
+
258
256
  // Validate IDs
259
257
  if (!Array.isArray(ids) || ids.length === 0) {
260
258
  throw new RestApiValidationError('Bulk delete requires an array of IDs', {
261
259
  fields: ['ids'],
262
260
  violations: [{ field: 'ids', rule: 'required_array', message: 'Must be a non-empty array' }]
263
- });
261
+ })
264
262
  }
265
263
 
266
264
  if (ids.length > maxBulkOperations) {
267
265
  throw new RestApiValidationError(`Bulk operations limited to ${maxBulkOperations} IDs`, {
268
266
  fields: ['ids'],
269
- violations: [{
270
- field: 'ids',
271
- rule: 'max_items',
272
- message: `Cannot process more than ${maxBulkOperations} IDs at once`
267
+ violations: [{
268
+ field: 'ids',
269
+ rule: 'max_items',
270
+ message: `Cannot process more than ${maxBulkOperations} IDs at once`
273
271
  }]
274
- });
272
+ })
275
273
  }
276
274
 
277
- const results = [];
278
- const errors = [];
279
- let transaction = null;
275
+ const results = []
276
+ const errors = []
277
+ let transaction = null
280
278
 
281
279
  try {
282
280
  if (atomic && helpers.newTransaction) {
283
- transaction = await helpers.newTransaction();
281
+ transaction = await helpers.newTransaction()
284
282
  }
285
283
 
286
284
  // Process deletes
287
285
  for (let i = 0; i < ids.length; i++) {
288
- const id = ids[i];
289
-
286
+ const id = ids[i]
287
+
290
288
  try {
291
- const recordContext = {
289
+ const recordContext = {
292
290
  ...context,
293
291
  bulkOperation: true,
294
292
  bulkIndex: i
295
- };
293
+ }
296
294
 
297
295
  await scope.delete({
298
296
  id,
299
297
  transaction
300
- }, recordContext);
298
+ }, recordContext)
301
299
 
302
300
  results.push({
303
301
  index: i,
304
302
  id,
305
303
  status: 'success'
306
- });
304
+ })
307
305
  } catch (error) {
308
306
  if (atomic) {
309
- if (transaction) await transaction.rollback();
310
- throw error;
307
+ if (transaction) await transaction.rollback()
308
+ throw error
311
309
  } else {
312
310
  errors.push({
313
311
  index: i,
@@ -318,13 +316,13 @@ export const BulkOperationsPlugin = {
318
316
  message: error.message,
319
317
  details: error.details
320
318
  }
321
- });
319
+ })
322
320
  }
323
321
  }
324
322
  }
325
323
 
326
324
  if (transaction) {
327
- await transaction.commit();
325
+ await transaction.commit()
328
326
  }
329
327
 
330
328
  return {
@@ -336,85 +334,84 @@ export const BulkOperationsPlugin = {
336
334
  atomic
337
335
  },
338
336
  errors: errors.length > 0 ? errors : undefined
339
- };
340
-
337
+ }
341
338
  } catch (error) {
342
339
  if (transaction && !transaction.isCompleted()) {
343
- await transaction.rollback();
340
+ await transaction.rollback()
344
341
  }
345
- throw error;
342
+ throw error
346
343
  }
347
- });
344
+ })
348
345
 
349
346
  // Hook into scope creation to add bulk routes
350
347
  addHook('afterAddScope', 'bulkOperationsRoutes', {}, async ({ scopeName }) => {
351
348
  // Note: context is not available here during route registration
352
349
  // The urlPrefix will be calculated per-request in the handler
353
- const urlPrefix = api.vars.transport?.mountPath || '';
354
- const scopePath = `${urlPrefix}/${scopeName}`;
350
+ const urlPrefix = api.vars.transport?.mountPath || ''
351
+ const scopePath = `${urlPrefix}/${scopeName}`
355
352
 
356
353
  // Create route handlers
357
354
  const createBulkRouteHandler = (method) => {
358
355
  return async ({ context, body, query }) => {
359
356
  try {
360
357
  // Parse query params for atomic mode override
361
- const atomic = query?.atomic !== undefined
362
- ? query.atomic === 'true'
363
- : defaultAtomic;
358
+ const atomic = query?.atomic !== undefined
359
+ ? query.atomic === 'true'
360
+ : defaultAtomic
364
361
 
365
- let params;
362
+ let params
366
363
  if (method === 'bulkPost') {
367
364
  // For bulk create, expect array in data field (JSON:API style)
368
365
  params = {
369
366
  inputRecords: body?.data || body,
370
367
  atomic
371
- };
368
+ }
372
369
  } else if (method === 'bulkPatch') {
373
370
  // For bulk update, expect operations array
374
371
  params = {
375
372
  operations: body?.operations || body,
376
373
  atomic
377
- };
374
+ }
378
375
  } else if (method === 'bulkDelete') {
379
376
  // For bulk delete, expect IDs array
380
377
  params = {
381
378
  ids: body?.data || body?.ids || body,
382
379
  atomic
383
- };
380
+ }
384
381
  }
385
382
 
386
383
  // Call the scope method
387
- const result = await api.scopes[scopeName][method](params);
388
-
389
- return result;
384
+ const result = await api.scopes[scopeName][method](params)
385
+
386
+ return result
390
387
  } catch (error) {
391
388
  // Let transport plugin handle error mapping
392
- throw error;
389
+ throw error
393
390
  }
394
- };
395
- };
391
+ }
392
+ }
396
393
 
397
394
  // Register bulk routes
398
395
  await api.addRoute({
399
396
  method: 'POST',
400
397
  path: `${scopePath}/bulk`,
401
398
  handler: createBulkRouteHandler('bulkPost')
402
- });
399
+ })
403
400
 
404
401
  await api.addRoute({
405
402
  method: 'PATCH',
406
403
  path: `${scopePath}/bulk`,
407
404
  handler: createBulkRouteHandler('bulkPatch')
408
- });
405
+ })
409
406
 
410
407
  await api.addRoute({
411
408
  method: 'DELETE',
412
409
  path: `${scopePath}/bulk`,
413
410
  handler: createBulkRouteHandler('bulkDelete')
414
- });
411
+ })
415
412
 
416
- log.debug(`Added bulk operation routes for scope: ${scopeName}`);
417
- });
413
+ log.debug(`Added bulk operation routes for scope: ${scopeName}`)
414
+ })
418
415
 
419
416
  // Add optimized bulk insert for Knex if available
420
417
  if (enableOptimizations) {
@@ -422,40 +419,40 @@ export const BulkOperationsPlugin = {
422
419
  // Check if we can use optimized path
423
420
  if (params.atomic && api.knex?.instance && context.bulkOperation === undefined) {
424
421
  // This is the main bulk operation, not individual records
425
- const { inputRecords } = params;
426
-
422
+ const { inputRecords } = params
423
+
427
424
  // Transform records for direct insertion
428
- const recordsToInsert = [];
425
+ const recordsToInsert = []
429
426
  for (const inputRecord of inputRecords) {
430
427
  // Run validation
431
- const validated = await scope.validateInput({
432
- inputRecord,
433
- operation: 'create'
434
- });
435
-
428
+ const validated = await scope.validateInput({
429
+ inputRecord,
430
+ operation: 'create'
431
+ })
432
+
436
433
  // Transform to database format
437
- const dbRecord = await scope.transformForDatabase({
434
+ const dbRecord = await scope.transformForDatabase({
438
435
  record: validated,
439
436
  operation: 'create'
440
- });
441
-
442
- recordsToInsert.push(dbRecord);
437
+ })
438
+
439
+ recordsToInsert.push(dbRecord)
443
440
  }
444
441
 
445
442
  // Perform bulk insert
446
- const knex = api.knex.instance;
447
- const tableName = scope.vars.schemaInfo.tableName;
448
-
443
+ const knex = api.knex.instance
444
+ const tableName = scope.vars.schemaInfo.tableName
445
+
449
446
  try {
450
447
  const inserted = await knex(tableName)
451
448
  .insert(recordsToInsert)
452
- .returning('*');
453
-
449
+ .returning('*')
450
+
454
451
  // Transform back to API format
455
- const results = [];
452
+ const results = []
456
453
  for (const record of inserted) {
457
- const apiRecord = await scope.transformFromDatabase({ record });
458
- results.push(apiRecord);
454
+ const apiRecord = await scope.transformFromDatabase({ record })
455
+ results.push(apiRecord)
459
456
  }
460
457
 
461
458
  // Return optimized result
@@ -469,15 +466,15 @@ export const BulkOperationsPlugin = {
469
466
  optimized: true
470
467
  },
471
468
  skipDefault: true // Tell the default handler to skip
472
- };
469
+ }
473
470
  } catch (error) {
474
471
  // Fall back to default handling
475
- log.warn('Optimized bulk insert failed, falling back to default', { error: error.message });
472
+ log.warn('Optimized bulk insert failed, falling back to default', { error: error.message })
476
473
  }
477
474
  }
478
- });
475
+ })
479
476
  }
480
477
 
481
- log.info('Bulk Operations plugin installed successfully');
478
+ log.info('Bulk Operations plugin installed successfully')
482
479
  }
483
- };
480
+ }