json-rest-api 1.0.22 → 1.0.24

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 (290) hide show
  1. package/CLAUDE.md +1 -2
  2. package/JSKIT_BACKEND_MIGRATION_TRACKER.txt +180 -0
  3. package/README.md +22 -1
  4. package/SINGLE_VALIDATION_CONTRACT_TODO.txt +172 -0
  5. package/docs/API.md +196 -38
  6. package/docs/COMPARISON.md +2 -2
  7. package/docs/GUIDE/GUIDE_1_Initial_Setup.md +111 -5
  8. package/docs/GUIDE/GUIDE_2_2_Manipulating_And_Searching_Tables.md +5 -1
  9. package/docs/GUIDE/GUIDE_2_7_Pagination_And_Ordering.md +3 -1
  10. package/docs/GUIDE/GUIDE_2_8_Effects_of_PUT_and_PATCH.md +12 -60
  11. package/docs/GUIDE/GUIDE_2_Data_And_Relations.md +2 -2
  12. package/docs/GUIDE/GUIDE_3_Field_Transformations.md +5 -1
  13. package/docs/GUIDE/GUIDE_7_Hooks_Data_Management_And_Plugins.md +2 -0
  14. package/docs/GUIDE/GUIDE_X_Autofiltering.md +355 -0
  15. package/docs/GUIDE/GUIDE_X_Fastify.md +78 -0
  16. package/docs/GUIDE/GUIDE_X_Knex_Schema_And_Migrations.md +370 -0
  17. package/docs/GUIDE/GUIDE_X_Query_Projections.md +159 -0
  18. package/docs/GUIDE/GUIDE_X_Writing_Plugins.md +199 -0
  19. package/docs/GUIDE/index.md +18 -9
  20. package/docs/QUICKSTART.md +5 -1
  21. package/docs/index.md +1 -1
  22. package/index.js +3 -2
  23. package/lib/enhanced-logger.js +1 -1
  24. package/package.json +6 -2
  25. package/plugins/core/bulk-operations-plugin.js +26 -34
  26. package/plugins/core/connectors/express-plugin.js +97 -190
  27. package/plugins/core/connectors/fastify-plugin.js +206 -0
  28. package/plugins/core/connectors/lib/busboy-detector.js +4 -4
  29. package/plugins/core/connectors/lib/connector-core.js +195 -0
  30. package/plugins/core/connectors/lib/transport-http-helpers.js +122 -0
  31. package/plugins/core/connectors/lib/transport-route-schemas.js +92 -0
  32. package/plugins/core/lib/anyapi/schema-utils.js +43 -4
  33. package/plugins/core/lib/anyapi/utils/descriptor-helpers.js +4 -1
  34. package/plugins/core/lib/dbIntrospection.js +1078 -0
  35. package/plugins/core/lib/dbTablesOperations.js +1111 -160
  36. package/plugins/core/lib/querying/knex-cross-table-search.js +0 -7
  37. package/plugins/core/lib/querying/knex-json-api-transformers-querying.js +5 -3
  38. package/plugins/core/lib/querying/knex-process-includes.js +3 -4
  39. package/plugins/core/lib/querying/knex-query-helpers.js +21 -76
  40. package/plugins/core/lib/querying/knex-relationship-includes.js +468 -215
  41. package/plugins/core/lib/querying/query-builder-utils.js +19 -0
  42. package/plugins/core/lib/querying/query-field-sort-helpers.js +135 -0
  43. package/plugins/core/lib/querying/sort-helpers.js +33 -0
  44. package/plugins/core/lib/querying/storage-adapter-utils.js +51 -0
  45. package/plugins/core/lib/querying/url-helpers.js +8 -2
  46. package/plugins/core/lib/querying-writing/compile-schemas.js +20 -5
  47. package/plugins/core/lib/querying-writing/knex-field-helpers.js +83 -14
  48. package/plugins/core/lib/querying-writing/knex-json-api-transformers.js +13 -12
  49. package/plugins/core/lib/querying-writing/query-field-helpers.js +88 -0
  50. package/plugins/core/lib/querying-writing/request-contracts.js +590 -0
  51. package/plugins/core/lib/querying-writing/resource-id-normalization.js +131 -0
  52. package/plugins/core/lib/querying-writing/schema-helpers.js +0 -2
  53. package/plugins/core/lib/querying-writing/simplified-helpers.js +4 -11
  54. package/plugins/core/lib/storage/canonical-storage-mapping.js +133 -0
  55. package/plugins/core/lib/storage/storage-adapter.js +82 -4
  56. package/plugins/core/lib/storage/storage-mapping.js +274 -0
  57. package/plugins/core/lib/writing/many-to-many-manipulations.js +4 -2
  58. package/plugins/core/rest-api-anyapi-knex-plugin.js +531 -312
  59. package/plugins/core/rest-api-autofilter-plugin.js +414 -0
  60. package/plugins/core/rest-api-knex-plugin.js +271 -226
  61. package/plugins/core/rest-api-plugin-hooks/register-relationship-routes.js +10 -5
  62. package/plugins/core/rest-api-plugin-hooks/register-scope-routes.js +12 -6
  63. package/plugins/core/rest-api-plugin-hooks/turn-scope-init-into-vars.js +1 -0
  64. package/plugins/core/rest-api-plugin-methods/apply-query-filters.js +41 -0
  65. package/plugins/core/rest-api-plugin-methods/common.js +148 -92
  66. package/plugins/core/rest-api-plugin-methods/delete-relationship.js +11 -2
  67. package/plugins/core/rest-api-plugin-methods/delete.js +6 -1
  68. package/plugins/core/rest-api-plugin-methods/enrich-attributes.js +6 -1
  69. package/plugins/core/rest-api-plugin-methods/get-related.js +7 -2
  70. package/plugins/core/rest-api-plugin-methods/get-relationship.js +7 -2
  71. package/plugins/core/rest-api-plugin-methods/get.js +29 -14
  72. package/plugins/core/rest-api-plugin-methods/patch-relationship.js +10 -3
  73. package/plugins/core/rest-api-plugin-methods/patch.js +56 -12
  74. package/plugins/core/rest-api-plugin-methods/post-relationship.js +11 -2
  75. package/plugins/core/rest-api-plugin-methods/post.js +21 -8
  76. package/plugins/core/rest-api-plugin-methods/put.js +73 -21
  77. package/plugins/core/rest-api-plugin-methods/query.js +15 -53
  78. package/plugins/core/rest-api-plugin-methods/release.js +0 -2
  79. package/plugins/core/rest-api-plugin.js +6 -0
  80. package/plugins/core/rest-api-positioning-plugin.js +2 -5
  81. package/plugins/core/rest-api-query-projections-plugin.js +130 -0
  82. package/plugins/core/socketio-plugin.js +1 -4
  83. package/plugins/storage/local-storage.js +1 -1
  84. package/plugins/storage/s3-storage.js +5 -2
  85. package/quickTest.js +1 -0
  86. package/scripts/run-docs.js +80 -84
  87. package/test-debug.js +1 -1
  88. package/tests/README.md +7 -0
  89. package/tests/anyapi-basic.test.js +3 -0
  90. package/tests/autofilter.test.js +463 -0
  91. package/tests/custom-id-mysql-returning-fallback.test.js +128 -0
  92. package/tests/custom-id-simplified-post.test.js +75 -0
  93. package/tests/custom-idproperty-relationships.test.js +33 -0
  94. package/tests/db-introspection.test.js +521 -0
  95. package/tests/db-table-operations.test.js +786 -0
  96. package/tests/fastify-plugin.test.js +156 -0
  97. package/tests/field-getters.test.js +1 -2
  98. package/tests/field-setters.test.js +0 -4
  99. package/tests/fixtures/api-configs.js +278 -176
  100. package/tests/helpers/fake-fastify.js +99 -0
  101. package/tests/http-connectors-parity.test.js +235 -0
  102. package/tests/id-normalization.test.js +444 -0
  103. package/tests/logical-id-write-validation.test.js +151 -0
  104. package/tests/pagination-cursor-multifield.test.js +11 -11
  105. package/tests/projected-fields.test.js +319 -0
  106. package/tests/relationships.test.js +3 -3
  107. package/tests/searchschema-knex-behavior.test.js +187 -0
  108. package/tests/searchschema-merge.test.js +21 -69
  109. package/tests/storage-mapping.test.js +259 -0
  110. package/audit/00-charter.md +0 -103
  111. package/audit/01-scope-map.md +0 -84
  112. package/audit/02-coverage-matrix.csv +0 -174
  113. package/audit/03-invariants.md +0 -123
  114. package/audit/04-standards-rubric.md +0 -56
  115. package/audit/05-competitor-rubric.md +0 -77
  116. package/audit/06-findings.md +0 -160
  117. package/audit/07-risk-register.csv +0 -10
  118. package/audit/08-remediation-roadmap.md +0 -177
  119. package/audit/09-signoff-check.md +0 -23
  120. package/audit/10-final-synthesis.md +0 -29
  121. package/audit/evidence/code/all-files-json-rest-api.txt +0 -168
  122. package/audit/evidence/code/all-files-json-rest-schema.txt +0 -12
  123. package/audit/evidence/code/boundary-leak-checks.txt +0 -72
  124. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__bulk-operations-plugin.js.txt +0 -480
  125. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__connectors__express-plugin.js.txt +0 -532
  126. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__connectors__lib__busboy-detector.js.txt +0 -175
  127. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__connectors__lib__formidable-detector.js.txt +0 -138
  128. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__connectors__lib__request-helpers.js.txt +0 -222
  129. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__file-handling-plugin.js.txt +0 -342
  130. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__dbTablesOperations.js.txt +0 -304
  131. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__fractional-positioning.js.txt +0 -188
  132. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying-writing__compile-schemas.js.txt +0 -336
  133. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying-writing__connectors-query-parser.js.txt +0 -165
  134. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying-writing__database-capabilities.js.txt +0 -265
  135. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying-writing__database-value-normalizers.js.txt +0 -369
  136. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying-writing__default-data-helpers.js.txt +0 -72
  137. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying-writing__field-utils.js.txt +0 -139
  138. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying-writing__knex-constants.js.txt +0 -45
  139. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying-writing__knex-field-helpers.js.txt +0 -396
  140. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying-writing__knex-json-api-transformers.js.txt +0 -162
  141. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying-writing__payload-validators.js.txt +0 -1314
  142. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying-writing__schema-helpers.js.txt +0 -488
  143. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying-writing__scope-validations.js.txt +0 -484
  144. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying-writing__simplified-helpers.js.txt +0 -857
  145. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying__knex-cross-table-search.js.txt +0 -565
  146. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying__knex-json-api-transformers-querying.js.txt +0 -485
  147. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying__knex-pagination-helpers.js.txt +0 -560
  148. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying__knex-process-includes.js.txt +0 -167
  149. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying__knex-query-helpers-base.js.txt +0 -109
  150. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying__knex-query-helpers.js.txt +0 -1064
  151. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying__knex-relationship-includes.js.txt +0 -1983
  152. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying__knex-window-queries.js.txt +0 -328
  153. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying__url-helpers.js.txt +0 -119
  154. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__storage__storage-adapter.js.txt +0 -160
  155. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__writing__many-to-many-manipulations.js.txt +0 -300
  156. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__writing__relationship-processor.js.txt +0 -245
  157. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-access.js.txt +0 -459
  158. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-cors-plugin.js.txt +0 -237
  159. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-knex-plugin.js.txt +0 -1135
  160. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-multihome-plugin.js.txt +0 -314
  161. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-hooks__compile-resource-schemas.js.txt +0 -7
  162. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-hooks__register-relationship-routes.js.txt +0 -71
  163. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-hooks__register-scope-routes.js.txt +0 -102
  164. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-hooks__turn-scope-init-into-vars.js.txt +0 -49
  165. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-hooks__validate-include-configurations.js.txt +0 -39
  166. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-methods__add-route.js.txt +0 -23
  167. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-methods__check-permissions.js.txt +0 -13
  168. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-methods__common.js.txt +0 -639
  169. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-methods__delete-relationship.js.txt +0 -117
  170. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-methods__delete.js.txt +0 -104
  171. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-methods__enrich-attributes.js.txt +0 -199
  172. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-methods__get-related.js.txt +0 -353
  173. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-methods__get-relationship.js.txt +0 -61
  174. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-methods__get.js.txt +0 -184
  175. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-methods__patch-relationship.js.txt +0 -55
  176. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-methods__patch.js.txt +0 -191
  177. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-methods__post-relationship.js.txt +0 -110
  178. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-methods__post.js.txt +0 -168
  179. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-methods__put.js.txt +0 -253
  180. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-methods__query.js.txt +0 -207
  181. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-methods__release.js.txt +0 -12
  182. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin.js.txt +0 -165
  183. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-positioning-plugin.js.txt +0 -668
  184. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__socketio-plugin.js.txt +0 -621
  185. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__storage__local-storage.js.txt +0 -218
  186. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__storage__s3-storage.js.txt +0 -124
  187. package/audit/evidence/code/critical-pass1/json-rest-schema__src__CorePlugin.js.txt +0 -269
  188. package/audit/evidence/code/critical-pass1/json-rest-schema__src__Schema.js.txt +0 -242
  189. package/audit/evidence/code/critical-pass1/json-rest-schema__src__index.js.txt +0 -77
  190. package/audit/evidence/code/critical-pass1-risk-keywords.txt +0 -238
  191. package/audit/evidence/code/critical-pass1-summary.csv +0 -67
  192. package/audit/evidence/code/critical-pass2-summary.csv +0 -67
  193. package/audit/evidence/code/early-silent-zones-no-test-token.txt +0 -57
  194. package/audit/evidence/code/error-model-signals.txt +0 -98
  195. package/audit/evidence/code/git-files-json-rest-api.txt +0 -171
  196. package/audit/evidence/code/git-files-json-rest-schema.txt +0 -27
  197. package/audit/evidence/code/hook-flow-signals.txt +0 -282
  198. package/audit/evidence/code/idproperty-signals.txt +0 -157
  199. package/audit/evidence/code/import-cycles-non-anyapi.txt +0 -3
  200. package/audit/evidence/code/import-hotspots-non-anyapi.txt +0 -96
  201. package/audit/evidence/code/imports-json-rest-api.txt +0 -354
  202. package/audit/evidence/code/imports-json-rest-schema.txt +0 -15
  203. package/audit/evidence/code/in-scope-files-json-rest-api.txt +0 -160
  204. package/audit/evidence/code/in-scope-files-json-rest-schema.txt +0 -13
  205. package/audit/evidence/code/non-anyapi-core-files.txt +0 -101
  206. package/audit/evidence/code/permission-signals.txt +0 -193
  207. package/audit/evidence/code/query-include-signals.txt +0 -687
  208. package/audit/evidence/code/transaction-signals.txt +0 -28
  209. package/audit/evidence/code/validation-signals.txt +0 -650
  210. package/audit/evidence/code/write-path-signals.txt +0 -61
  211. package/audit/evidence/docs/docs-claims-signals.txt +0 -1075
  212. package/audit/evidence/docs/schema-docs-claims-signals.txt +0 -179
  213. package/audit/evidence/external/corpus-freeze.md +0 -21
  214. package/audit/evidence/external/npm-competitor-metadata.txt +0 -70
  215. package/audit/evidence/external/npm-download-signals.txt +0 -23
  216. package/audit/evidence/external/npm-jsonapi-server.txt +0 -8
  217. package/audit/evidence/external/npm-modern-jsonapi-metadata.txt +0 -38
  218. package/audit/evidence/external/readme-fortune-signals.txt +0 -26
  219. package/audit/evidence/external/readme-fortune.md +0 -165
  220. package/audit/evidence/external/readme-json-api-serializer-signals.txt +0 -80
  221. package/audit/evidence/external/readme-json-api-serializer.md +0 -690
  222. package/audit/evidence/external/readme-jsonapi-serde-server-signals.txt +0 -0
  223. package/audit/evidence/external/readme-jsonapi-serde-server.md +0 -22
  224. package/audit/evidence/external/readme-jsonapi-serializer-signals.txt +0 -63
  225. package/audit/evidence/external/readme-jsonapi-serializer.md +0 -315
  226. package/audit/evidence/external/readme-jsonapi-server-signals.txt +0 -19
  227. package/audit/evidence/external/readme-jsonapi-server.md +0 -104
  228. package/audit/evidence/external/readme-nestjs-json-api-signals.txt +0 -4
  229. package/audit/evidence/external/readme-nestjs-json-api.md +0 -94
  230. package/audit/evidence/external/readme-yaser-json-api-nestjs-signals.txt +0 -47
  231. package/audit/evidence/external/readme-yaser-json-api-nestjs.md +0 -447
  232. package/audit/evidence/external/standard-jsonapi-snippets.txt +0 -84
  233. package/audit/evidence/external/standard-rfc-snippets.txt +0 -168
  234. package/audit/evidence/external/standards-sources.txt +0 -6
  235. package/audit/evidence/runtime/baseline-commits.txt +0 -2
  236. package/audit/evidence/runtime/db-adapter-assumptions.txt +0 -3
  237. package/audit/evidence/runtime/environment.txt +0 -4
  238. package/audit/evidence/runtime/invariant-runtime-mapping.md +0 -38
  239. package/audit/evidence/runtime/probe-patch-relationship-error.exitcode +0 -1
  240. package/audit/evidence/runtime/probe-patch-relationship-error.log +0 -76
  241. package/audit/evidence/runtime/probe-patch-relationship-error.mjs +0 -67
  242. package/audit/evidence/runtime/probe-patch-relationship-referenceerror.exitcode +0 -1
  243. package/audit/evidence/runtime/probe-patch-relationship-referenceerror.log +0 -62
  244. package/audit/evidence/runtime/probe-patch-relationship-referenceerror.mjs +0 -47
  245. package/audit/evidence/runtime/test-exitcodes.txt +0 -4
  246. package/audit/evidence/runtime/test-summary.md +0 -107
  247. package/audit/evidence/runtime/trace-anchors.txt +0 -61
  248. package/audit/evidence/tests/access-multitenancy-test-signals.txt +0 -125
  249. package/audit/evidence/tests/failing-tests-summary.txt +0 -25
  250. package/audit/evidence/tests/npm-test-json-rest-api.exitcode +0 -1
  251. package/audit/evidence/tests/npm-test-json-rest-api.log +0 -5885
  252. package/audit/evidence/tests/npm-test-json-rest-schema.exitcode +0 -1
  253. package/audit/evidence/tests/npm-test-json-rest-schema.log +0 -356
  254. package/audit/evidence/tests/query-include-test-signals.txt +0 -948
  255. package/audit/evidence/tests/relationship-test-signals.txt +0 -223
  256. package/audit/evidence/tests/skipped-tests.txt +0 -10
  257. package/audit/evidence/tests/test-suite-list-non-anyapi.txt +0 -30
  258. package/audit/evidence/tests/test-suite-list.txt +0 -32
  259. package/audit/subsystems/docs-drift.md +0 -49
  260. package/audit/subsystems/pass2-cross-review.md +0 -131
  261. package/audit/subsystems/plugins-access.md +0 -51
  262. package/audit/subsystems/plugins-bulk.md +0 -50
  263. package/audit/subsystems/plugins-cors.md +0 -48
  264. package/audit/subsystems/plugins-file.md +0 -53
  265. package/audit/subsystems/plugins-multihome.md +0 -50
  266. package/audit/subsystems/plugins-positioning.md +0 -54
  267. package/audit/subsystems/plugins-socketio.md +0 -50
  268. package/audit/subsystems/query-engine.md +0 -61
  269. package/audit/subsystems/relationships.md +0 -55
  270. package/audit/subsystems/rest-orchestration.md +0 -66
  271. package/audit/subsystems/schema-core.md +0 -64
  272. package/audit/subsystems/storage-knex.md +0 -57
  273. package/audit/subsystems/tests.md +0 -48
  274. package/audit/subsystems/transport-express.md +0 -55
  275. package/audit/traces/trace-read-get.md +0 -22
  276. package/audit/traces/trace-read-query.md +0 -25
  277. package/audit/traces/trace-relationship-endpoints.md +0 -31
  278. package/audit/traces/trace-write-delete.md +0 -17
  279. package/audit/traces/trace-write-patch.md +0 -26
  280. package/audit/traces/trace-write-post.md +0 -22
  281. package/audit/traces/trace-write-put.md +0 -23
  282. package/docs/GUIDE/GUIDE_4_Authentication_And_Permissions.md +0 -707
  283. package/docs/GUIDE/GUIDE_X_Multihome.md +0 -666
  284. package/plugins/core/lib/querying-writing/payload-validators.js +0 -1314
  285. package/plugins/core/rest-api-access.js +0 -459
  286. package/plugins/core/rest-api-multihome-plugin.js +0 -314
  287. package/tests/multihome.test.js +0 -553
  288. package/tests/multihome.test.js.bak +0 -550
  289. package/tests/ownership-optout.test.js +0 -76
  290. package/tests/rest-api-access.test.js +0 -206
@@ -1,72 +0,0 @@
1
- # Boundary leak checks (non-anyapi)
2
-
3
- ## transport touching db/knex signals
4
-
5
- ## plugins with direct knex access (review for contract bypass)
6
- plugins/core/rest-api-positioning-plugin.js:23: if (!api.knex?.instance) {
7
- plugins/core/rest-api-positioning-plugin.js:52: if (scopeVars.schemaInfo && api.knex?.instance) {
8
- plugins/core/rest-api-positioning-plugin.js:53: const adapter = createStorageAdapter({ knex: api.knex.instance, schemaInfo: scopeVars.schemaInfo })
9
- plugins/core/rest-api-positioning-plugin.js:197: if (!schemaInfo || !api.knex?.instance) {
10
- plugins/core/rest-api-positioning-plugin.js:208: const knex = api.knex.instance
11
- plugins/core/rest-api-positioning-plugin.js:422: const knex = api.knex.instance
12
- plugins/core/rest-api-plugin.js:158: // Example: helpers.dataGet, helpers.dataPost, etc. will throw "No storage implementation" errors
13
- plugins/core/rest-api-multihome-plugin.js:6: if (!api.knex?.instance) {
14
- plugins/core/bulk-operations-plugin.js:420: if (params.atomic && api.knex?.instance && context.bulkOperation === undefined) {
15
- plugins/core/bulk-operations-plugin.js:443: const knex = api.knex.instance
16
- plugins/core/bulk-operations-plugin.js:447: const inserted = await knex(tableName)
17
- plugins/core/rest-api-knex-plugin.js:73: api.knex = {
18
- plugins/core/rest-api-knex-plugin.js:99: api.knex.helpers.getStorageAdapter = getScopeStorageAdapter
19
- plugins/core/rest-api-knex-plugin.js:125: api.knex.capabilities = {
20
- plugins/core/rest-api-knex-plugin.js:168: async ({ api }) => api.knex.instance.destroy()
21
- plugins/core/rest-api-knex-plugin.js:187: await createKnexTable(api.knex.instance, vars.schemaInfo, tableSchemaInstance, scopeOptions)
22
- plugins/core/rest-api-knex-plugin.js:198: api.knex.instance,
23
- plugins/core/rest-api-knex-plugin.js:210: await addKnexFields(api.knex.instance, vars.schemaInfo.tableName, partialTableSchema)
24
- plugins/core/rest-api-knex-plugin.js:232: * @param {Object} params.context.db - Database connection (knex instance or transaction)
25
- plugins/core/rest-api-knex-plugin.js:235: helpers.dataExists = async ({ scopeName, context }) => {
26
- plugins/core/rest-api-knex-plugin.js:245: const db = context.db || api.knex.instance
27
- plugins/core/rest-api-knex-plugin.js:270: * @param {Object} params.context.db - Database connection (knex instance or transaction)
28
- plugins/core/rest-api-knex-plugin.js:278: helpers.dataGet = async ({ scopeName, context, runHooks }) => {
29
- plugins/core/rest-api-knex-plugin.js:299: const db = context.db || api.knex.instance
30
- plugins/core/rest-api-knex-plugin.js:369: * @param {Object} params.context.db - Database connection (knex instance or transaction)
31
- plugins/core/rest-api-knex-plugin.js:372: helpers.dataGetMinimal = async ({ scopeName, context }) => {
32
- plugins/core/rest-api-knex-plugin.js:381: const db = context.db || api.knex.instance
33
- plugins/core/rest-api-knex-plugin.js:426: * @param {Object} params.context.db - Database connection (knex instance or transaction)
34
- plugins/core/rest-api-knex-plugin.js:433: helpers.dataQuery = async ({ scopeName, context, runHooks }) => {
35
- plugins/core/rest-api-knex-plugin.js:442: const db = context.db || api.knex.instance
36
- plugins/core/rest-api-knex-plugin.js:893: * @param {Object} params.context.db - Database connection (knex instance or transaction)
37
- plugins/core/rest-api-knex-plugin.js:899: helpers.dataPost = async ({ scopeName, context }) => {
38
- plugins/core/rest-api-knex-plugin.js:907: const db = context.db || api.knex.instance
39
- plugins/core/rest-api-knex-plugin.js:937: * @param {Object} params.context.db - Database connection (knex instance or transaction)
40
- plugins/core/rest-api-knex-plugin.js:946: helpers.dataPut = async ({ scopeName, context }) => {
41
- plugins/core/rest-api-knex-plugin.js:955: const db = context.db || api.knex.instance
42
- plugins/core/rest-api-knex-plugin.js:1022: * @param {Object} params.context.db - Database connection (knex instance or transaction)
43
- plugins/core/rest-api-knex-plugin.js:1030: helpers.dataPatch = async ({ scopeName, context }) => {
44
- plugins/core/rest-api-knex-plugin.js:1039: const db = context.db || api.knex.instance
45
- plugins/core/rest-api-knex-plugin.js:1091: * @param {Object} params.context.db - Database connection (knex instance or transaction)
46
- plugins/core/rest-api-knex-plugin.js:1095: helpers.dataDelete = async ({ scopeName, context }) => {
47
- plugins/core/rest-api-knex-plugin.js:1105: const db = context.db || api.knex.instance
48
-
49
- ## method-layer to storage-contract call sites
50
- plugins/core/rest-api-plugin-methods/query.js:126: await scope.checkPermissions({
51
- plugins/core/rest-api-plugin-methods/query.js:133: context.record = await helpers.dataQuery({
52
- plugins/core/rest-api-plugin-methods/put.js:95: const minimalRecord = await helpers.dataGetMinimal({
53
- plugins/core/rest-api-plugin-methods/put.js:171: await scope.checkPermissions({
54
- plugins/core/rest-api-plugin-methods/put.js:191: await helpers.dataPut({
55
- plugins/core/rest-api-plugin-methods/post.js:98: await scope.checkPermissions({
56
- plugins/core/rest-api-plugin-methods/post.js:118: context.id = await helpers.dataPost({
57
- plugins/core/rest-api-plugin-methods/delete.js:39: (helpers.newTransaction && !params.transaction ? await helpers.newTransaction() : null)
58
- plugins/core/rest-api-plugin-methods/delete.js:47: const minimalRecord = await helpers.dataGetMinimal({
59
- plugins/core/rest-api-plugin-methods/delete.js:68: await scope.checkPermissions({
60
- plugins/core/rest-api-plugin-methods/delete.js:81: await helpers.dataDelete({
61
- plugins/core/rest-api-plugin-methods/delete-relationship.js:22: (helpers.newTransaction && !params.transaction ? await helpers.newTransaction() : null)
62
- plugins/core/rest-api-plugin-methods/post-relationship.js:23: (helpers.newTransaction && !params.transaction ? await helpers.newTransaction() : null)
63
- plugins/core/rest-api-plugin-methods/common.js:128: (helpers.newTransaction && !params.transaction ? await helpers.newTransaction() : null)
64
- plugins/core/rest-api-plugin-methods/common.js:450: const record = await helpers.dataGetMinimal({
65
- plugins/core/rest-api-plugin-methods/common.js:557: const currentRecord = await helpers.dataGetMinimal({
66
- plugins/core/rest-api-plugin-methods/patch-relationship.js:18: (helpers.newTransaction && !params.transaction ? await helpers.newTransaction() : null)
67
- plugins/core/rest-api-plugin-methods/get.js:69: const minimalRecord = await helpers.dataGetMinimal({
68
- plugins/core/rest-api-plugin-methods/get.js:89: await scope.checkPermissions({
69
- plugins/core/rest-api-plugin-methods/get.js:97: context.record = await helpers.dataGet({
70
- plugins/core/rest-api-plugin-methods/patch.js:87: const minimalRecord = await helpers.dataGetMinimal({
71
- plugins/core/rest-api-plugin-methods/patch.js:103: await scope.checkPermissions({
72
- plugins/core/rest-api-plugin-methods/patch.js:131: await helpers.dataPatch({
@@ -1,480 +0,0 @@
1
- 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
- 7 async install ({ api, log, addHook, addScopeMethod, helpers, pluginOptions }) {
8
- 8 const bulkOptions = pluginOptions || {}
9
- 9 const {
10
- 10 maxBulkOperations = 100,
11
- 11 defaultAtomic = true,
12
- 12 batchSize = 100,
13
- 13 enableOptimizations = true
14
- 14 } = bulkOptions
15
- 15
16
- 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
- 20 const { inputRecords, atomic = defaultAtomic } = params
21
- 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
- 33 violations: [{
34
- 34 field: 'data',
35
- 35 rule: 'max_items',
36
- 36 message: `Cannot process more than ${maxBulkOperations} records at once`
37
- 37 }]
38
- 38 })
39
- 39 }
40
- 40
41
- 41 const results = []
42
- 42 const errors = []
43
- 43 let transaction = null
44
- 44
45
- 45 try {
46
- 46 // Start transaction if atomic mode
47
- 47 if (atomic && helpers.newTransaction) {
48
- 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
- 53 const batch = inputRecords.slice(i, i + batchSize)
54
- 54
55
- 55 for (let j = 0; j < batch.length; j++) {
56
- 56 const recordIndex = i + j
57
- 57 const inputRecord = batch[j]
58
- 58
59
- 59 try {
60
- 60 // Create individual context for each record
61
- 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
- 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
- 81 if (transaction) await transaction.rollback()
82
- 82 throw error
83
- 83 } else {
84
- 84 // In non-atomic mode, collect errors
85
- 85 errors.push({
86
- 86 index: recordIndex,
87
- 87 status: 'error',
88
- 88 error: {
89
- 89 code: error.code || 'UNKNOWN_ERROR',
90
- 90 message: error.message,
91
- 91 details: error.details
92
- 92 }
93
- 93 })
94
- 94 }
95
- 95 }
96
- 96 }
97
- 97 }
98
- 98
99
- 99 // Commit transaction if atomic
100
- 100 if (transaction) {
101
- 101 await transaction.commit()
102
- 102 }
103
- 103
104
- 104 // Build response
105
- 105 return {
106
- 106 data: results.filter(r => r.status === 'success').map(r => r.data),
107
- 107 errors: errors.length > 0 ? errors : undefined,
108
- 108 meta: {
109
- 109 total: inputRecords.length,
110
- 110 succeeded: results.length,
111
- 111 failed: errors.length,
112
- 112 atomic
113
- 113 }
114
- 114 }
115
- 115 } catch (error) {
116
- 116 // Ensure rollback on error
117
- 117 if (transaction && !transaction.isCompleted()) {
118
- 118 await transaction.rollback()
119
- 119 }
120
- 120 throw error
121
- 121 }
122
- 122 })
123
- 123
124
- 124 addScopeMethod('bulkPatch', async ({ scope, scopeName, params, context, runHooks }) => {
125
- 125 const { operations, atomic = defaultAtomic } = params
126
- 126
127
- 127 // Validate operations
128
- 128 if (!Array.isArray(operations) || operations.length === 0) {
129
- 129 throw new RestApiValidationError('Bulk patch requires an array of operations', {
130
- 130 fields: ['operations'],
131
- 131 violations: [{ field: 'operations', rule: 'required_array', message: 'Must be a non-empty array' }]
132
- 132 })
133
- 133 }
134
- 134
135
- 135 if (operations.length > maxBulkOperations) {
136
- 136 throw new RestApiValidationError(`Bulk operations limited to ${maxBulkOperations} operations`, {
137
- 137 fields: ['operations'],
138
- 138 violations: [{
139
- 139 field: 'operations',
140
- 140 rule: 'max_items',
141
- 141 message: `Cannot process more than ${maxBulkOperations} operations at once`
142
- 142 }]
143
- 143 })
144
- 144 }
145
- 145
146
- 146 const results = []
147
- 147 const errors = []
148
- 148 let transaction = null
149
- 149
150
- 150 try {
151
- 151 if (atomic && helpers.newTransaction) {
152
- 152 transaction = await helpers.newTransaction()
153
- 153 }
154
- 154
155
- 155 for (let i = 0; i < operations.length; i++) {
156
- 156 const operation = operations[i]
157
- 157
158
- 158 // Validate operation structure
159
- 159 if (!operation.id || !operation.data) {
160
- 160 errors.push({
161
- 161 index: i,
162
- 162 status: 'error',
163
- 163 error: {
164
- 164 code: 'INVALID_OPERATION',
165
- 165 message: 'Operation must include id and data',
166
- 166 details: { operation }
167
- 167 }
168
- 168 })
169
- 169 if (atomic) {
170
- 170 if (transaction) await transaction.rollback()
171
- 171 throw new RestApiValidationError('Invalid operation structure', {
172
- 172 fields: [`operations[${i}]`],
173
- 173 violations: [{
174
- 174 field: `operations[${i}]`,
175
- 175 rule: 'required_fields',
176
- 176 message: 'Operation must include id and data'
177
- 177 }]
178
- 178 })
179
- 179 }
180
- 180 continue
181
- 181 }
182
- 182
183
- 183 try {
184
- 184 const recordContext = {
185
- 185 ...context,
186
- 186 bulkOperation: true,
187
- 187 bulkIndex: i
188
- 188 }
189
- 189
190
- 190 const result = await scope.patch({
191
- 191 id: operation.id,
192
- 192 inputRecord: { data: operation.data },
193
- 193 transaction
194
- 194 }, recordContext)
195
- 195
196
- 196 // If patch doesn't return full record, fetch it
197
- 197 let resultData = result.data
198
- 198 if (!resultData && result.id) {
199
- 199 const fetchedRecord = await scope.get({
200
- 200 id: result.id,
201
- 201 transaction
202
- 202 }, recordContext)
203
- 203 resultData = fetchedRecord.data
204
- 204 }
205
- 205
206
- 206 results.push({
207
- 207 index: i,
208
- 208 id: operation.id,
209
- 209 status: 'success',
210
- 210 data: resultData
211
- 211 })
212
- 212 } catch (error) {
213
- 213 if (atomic) {
214
- 214 if (transaction) await transaction.rollback()
215
- 215 throw error
216
- 216 } else {
217
- 217 errors.push({
218
- 218 index: i,
219
- 219 id: operation.id,
220
- 220 status: 'error',
221
- 221 error: {
222
- 222 code: error.code || 'UNKNOWN_ERROR',
223
- 223 message: error.message,
224
- 224 details: error.details
225
- 225 }
226
- 226 })
227
- 227 }
228
- 228 }
229
- 229 }
230
- 230
231
- 231 if (transaction) {
232
- 232 await transaction.commit()
233
- 233 }
234
- 234
235
- 235 return {
236
- 236 data: results.filter(r => r.status === 'success').map(r => r.data),
237
- 237 errors: errors.length > 0 ? errors : undefined,
238
- 238 meta: {
239
- 239 total: operations.length,
240
- 240 succeeded: results.length,
241
- 241 failed: errors.length,
242
- 242 atomic
243
- 243 }
244
- 244 }
245
- 245 } catch (error) {
246
- 246 if (transaction && !transaction.isCompleted()) {
247
- 247 await transaction.rollback()
248
- 248 }
249
- 249 throw error
250
- 250 }
251
- 251 })
252
- 252
253
- 253 addScopeMethod('bulkDelete', async ({ scope, scopeName, params, context, runHooks }) => {
254
- 254 const { ids, atomic = defaultAtomic } = params
255
- 255
256
- 256 // Validate IDs
257
- 257 if (!Array.isArray(ids) || ids.length === 0) {
258
- 258 throw new RestApiValidationError('Bulk delete requires an array of IDs', {
259
- 259 fields: ['ids'],
260
- 260 violations: [{ field: 'ids', rule: 'required_array', message: 'Must be a non-empty array' }]
261
- 261 })
262
- 262 }
263
- 263
264
- 264 if (ids.length > maxBulkOperations) {
265
- 265 throw new RestApiValidationError(`Bulk operations limited to ${maxBulkOperations} IDs`, {
266
- 266 fields: ['ids'],
267
- 267 violations: [{
268
- 268 field: 'ids',
269
- 269 rule: 'max_items',
270
- 270 message: `Cannot process more than ${maxBulkOperations} IDs at once`
271
- 271 }]
272
- 272 })
273
- 273 }
274
- 274
275
- 275 const results = []
276
- 276 const errors = []
277
- 277 let transaction = null
278
- 278
279
- 279 try {
280
- 280 if (atomic && helpers.newTransaction) {
281
- 281 transaction = await helpers.newTransaction()
282
- 282 }
283
- 283
284
- 284 // Process deletes
285
- 285 for (let i = 0; i < ids.length; i++) {
286
- 286 const id = ids[i]
287
- 287
288
- 288 try {
289
- 289 const recordContext = {
290
- 290 ...context,
291
- 291 bulkOperation: true,
292
- 292 bulkIndex: i
293
- 293 }
294
- 294
295
- 295 await scope.delete({
296
- 296 id,
297
- 297 transaction
298
- 298 }, recordContext)
299
- 299
300
- 300 results.push({
301
- 301 index: i,
302
- 302 id,
303
- 303 status: 'success'
304
- 304 })
305
- 305 } catch (error) {
306
- 306 if (atomic) {
307
- 307 if (transaction) await transaction.rollback()
308
- 308 throw error
309
- 309 } else {
310
- 310 errors.push({
311
- 311 index: i,
312
- 312 id,
313
- 313 status: 'error',
314
- 314 error: {
315
- 315 code: error.code || 'UNKNOWN_ERROR',
316
- 316 message: error.message,
317
- 317 details: error.details
318
- 318 }
319
- 319 })
320
- 320 }
321
- 321 }
322
- 322 }
323
- 323
324
- 324 if (transaction) {
325
- 325 await transaction.commit()
326
- 326 }
327
- 327
328
- 328 return {
329
- 329 meta: {
330
- 330 total: ids.length,
331
- 331 succeeded: results.length,
332
- 332 failed: errors.length,
333
- 333 deleted: results.filter(r => r.status === 'success').map(r => r.id),
334
- 334 atomic
335
- 335 },
336
- 336 errors: errors.length > 0 ? errors : undefined
337
- 337 }
338
- 338 } catch (error) {
339
- 339 if (transaction && !transaction.isCompleted()) {
340
- 340 await transaction.rollback()
341
- 341 }
342
- 342 throw error
343
- 343 }
344
- 344 })
345
- 345
346
- 346 // Hook into scope creation to add bulk routes
347
- 347 addHook('afterAddScope', 'bulkOperationsRoutes', {}, async ({ scopeName }) => {
348
- 348 // Note: context is not available here during route registration
349
- 349 // The urlPrefix will be calculated per-request in the handler
350
- 350 const urlPrefix = api.vars.transport?.mountPath || ''
351
- 351 const scopePath = `${urlPrefix}/${scopeName}`
352
- 352
353
- 353 // Create route handlers
354
- 354 const createBulkRouteHandler = (method) => {
355
- 355 return async ({ context, body, query }) => {
356
- 356 try {
357
- 357 // Parse query params for atomic mode override
358
- 358 const atomic = query?.atomic !== undefined
359
- 359 ? query.atomic === 'true'
360
- 360 : defaultAtomic
361
- 361
362
- 362 let params
363
- 363 if (method === 'bulkPost') {
364
- 364 // For bulk create, expect array in data field (JSON:API style)
365
- 365 params = {
366
- 366 inputRecords: body?.data || body,
367
- 367 atomic
368
- 368 }
369
- 369 } else if (method === 'bulkPatch') {
370
- 370 // For bulk update, expect operations array
371
- 371 params = {
372
- 372 operations: body?.operations || body,
373
- 373 atomic
374
- 374 }
375
- 375 } else if (method === 'bulkDelete') {
376
- 376 // For bulk delete, expect IDs array
377
- 377 params = {
378
- 378 ids: body?.data || body?.ids || body,
379
- 379 atomic
380
- 380 }
381
- 381 }
382
- 382
383
- 383 // Call the scope method
384
- 384 const result = await api.scopes[scopeName][method](params)
385
- 385
386
- 386 return result
387
- 387 } catch (error) {
388
- 388 // Let transport plugin handle error mapping
389
- 389 throw error
390
- 390 }
391
- 391 }
392
- 392 }
393
- 393
394
- 394 // Register bulk routes
395
- 395 await api.addRoute({
396
- 396 method: 'POST',
397
- 397 path: `${scopePath}/bulk`,
398
- 398 handler: createBulkRouteHandler('bulkPost')
399
- 399 })
400
- 400
401
- 401 await api.addRoute({
402
- 402 method: 'PATCH',
403
- 403 path: `${scopePath}/bulk`,
404
- 404 handler: createBulkRouteHandler('bulkPatch')
405
- 405 })
406
- 406
407
- 407 await api.addRoute({
408
- 408 method: 'DELETE',
409
- 409 path: `${scopePath}/bulk`,
410
- 410 handler: createBulkRouteHandler('bulkDelete')
411
- 411 })
412
- 412
413
- 413 log.debug(`Added bulk operation routes for scope: ${scopeName}`)
414
- 414 })
415
- 415
416
- 416 // Add optimized bulk insert for Knex if available
417
- 417 if (enableOptimizations) {
418
- 418 addHook('beforeBulkPost', 'optimizedBulkInsert', {}, async ({ scope, params, context }) => {
419
- 419 // Check if we can use optimized path
420
- 420 if (params.atomic && api.knex?.instance && context.bulkOperation === undefined) {
421
- 421 // This is the main bulk operation, not individual records
422
- 422 const { inputRecords } = params
423
- 423
424
- 424 // Transform records for direct insertion
425
- 425 const recordsToInsert = []
426
- 426 for (const inputRecord of inputRecords) {
427
- 427 // Run validation
428
- 428 const validated = await scope.validateInput({
429
- 429 inputRecord,
430
- 430 operation: 'create'
431
- 431 })
432
- 432
433
- 433 // Transform to database format
434
- 434 const dbRecord = await scope.transformForDatabase({
435
- 435 record: validated,
436
- 436 operation: 'create'
437
- 437 })
438
- 438
439
- 439 recordsToInsert.push(dbRecord)
440
- 440 }
441
- 441
442
- 442 // Perform bulk insert
443
- 443 const knex = api.knex.instance
444
- 444 const tableName = scope.vars.schemaInfo.tableName
445
- 445
446
- 446 try {
447
- 447 const inserted = await knex(tableName)
448
- 448 .insert(recordsToInsert)
449
- 449 .returning('*')
450
- 450
451
- 451 // Transform back to API format
452
- 452 const results = []
453
- 453 for (const record of inserted) {
454
- 454 const apiRecord = await scope.transformFromDatabase({ record })
455
- 455 results.push(apiRecord)
456
- 456 }
457
- 457
458
- 458 // Return optimized result
459
- 459 return {
460
- 460 data: results,
461
- 461 meta: {
462
- 462 total: inputRecords.length,
463
- 463 succeeded: results.length,
464
- 464 failed: 0,
465
- 465 atomic: true,
466
- 466 optimized: true
467
- 467 },
468
- 468 skipDefault: true // Tell the default handler to skip
469
- 469 }
470
- 470 } catch (error) {
471
- 471 // Fall back to default handling
472
- 472 log.warn('Optimized bulk insert failed, falling back to default', { error: error.message })
473
- 473 }
474
- 474 }
475
- 475 })
476
- 476 }
477
- 477
478
- 478 log.info('Bulk Operations plugin installed successfully')
479
- 479 }
480
- 480 }