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,8 +1,9 @@
1
1
  import { RestApiResourceError } from '../../../lib/rest-api-errors.js'
2
- import { validateGetPayload } from '../lib/querying-writing/payload-validators.js'
3
2
  import { normalizeRecordAttributes } from '../lib/querying-writing/database-value-normalizers.js'
4
3
  import { getRequestedComputedFields } from '../lib/querying-writing/knex-field-helpers.js'
5
4
  import { transformJsonApiToSimplified } from '../lib/querying-writing/simplified-helpers.js'
5
+ import { getRequestContracts, validateRequestContractOrThrow } from '../lib/querying-writing/request-contracts.js'
6
+ import { requireExistingResourceId } from '../lib/querying-writing/resource-id-normalization.js'
6
7
  import { cascadeConfig } from './common.js'
7
8
 
8
9
  /**
@@ -41,11 +42,11 @@ export default async function getMethod ({
41
42
 
42
43
  // Assign common context properties
43
44
  context.schemaInfo = scopes[scopeName].vars.schemaInfo
44
- context.queryParams = params.queryParams || {}
45
-
46
- // These only make sense as parameter per query
47
- context.queryParams.fields = cascadeConfig('fields', [context.queryParams], {})
48
- context.queryParams.include = cascadeConfig('include', [context.queryParams], [])
45
+ const rawQueryParams = params.queryParams || {}
46
+ context.queryParams = {
47
+ fields: cascadeConfig('fields', [rawQueryParams], {}),
48
+ include: cascadeConfig('include', [rawQueryParams], [])
49
+ }
49
50
 
50
51
  context.transaction = params.transaction
51
52
  context.db = context.transaction || api.knex.instance
@@ -56,14 +57,28 @@ export default async function getMethod ({
56
57
  const schemaStructure = context.schemaInfo.schemaInstance.structure
57
58
  const schemaRelationships = context.schemaInfo.schemaRelationships
58
59
 
59
- context.id = params.id
60
-
61
- // Validate GET request to ensure required parameters are present and properly formatted.
62
- // This checks that 'id' parameter exists and is not empty (you can't GET without an ID),
63
- // validates 'include' contains valid relationship names (not arbitrary fields), and ensures
64
- // 'fields' for sparse fieldsets follow the format fields[type]=comma,separated,list.
65
- // Example: validates id: '123' exists, include: ['author', 'tags'] are real relationships.
66
- validateGetPayload({ id: context.id, queryParams: context.queryParams }, vars.includeDepthLimit)
60
+ const requestContracts = getRequestContracts({
61
+ scopeName,
62
+ schemaInfo: context.schemaInfo,
63
+ includeDepthLimit: vars.includeDepthLimit,
64
+ sortableFields: vars.sortableFields
65
+ })
66
+ const normalizedId = requireExistingResourceId(params.id, {
67
+ scopeOptions,
68
+ vars,
69
+ scopeName
70
+ })
71
+ const validatedRequest = validateRequestContractOrThrow(
72
+ requestContracts.get,
73
+ {
74
+ id: normalizedId,
75
+ queryParams: context.queryParams
76
+ },
77
+ 'GET request parameters are invalid'
78
+ )
79
+
80
+ context.id = validatedRequest.id
81
+ context.queryParams = validatedRequest.queryParams || {}
67
82
 
68
83
  // Fetch minimal record for authorization checks
69
84
  const minimalRecord = await helpers.dataGetMinimal({
@@ -1,3 +1,6 @@
1
+ import { handleWriteMethodError } from './common.js'
2
+ import { requireExistingResourceId } from '../lib/querying-writing/resource-id-normalization.js'
3
+
1
4
  /**
2
5
  * PATCH RELATIONSHIP
3
6
  * Completely replaces a relationship
@@ -8,9 +11,13 @@
8
11
  * @param {object|array} relationshipData - New relationship data
9
12
  * @returns {Promise<void>} 204 No Content
10
13
  */
11
- export default async function patchRelationshipMethod ({ params, context, vars, helpers, scope, scopes, runHooks, scopeName, api }) {
14
+ export default async function patchRelationshipMethod ({ params, context, vars, helpers, scope, scopes, runHooks, scopeOptions, scopeName, api, log }) {
12
15
  context.method = 'patchRelationship'
13
- context.id = params.id
16
+ context.id = requireExistingResourceId(params.id, {
17
+ scopeOptions,
18
+ vars,
19
+ scopeName
20
+ })
14
21
  context.relationshipName = params.relationshipName
15
22
 
16
23
  // Transaction handling
@@ -50,6 +57,6 @@ export default async function patchRelationshipMethod ({ params, context, vars,
50
57
 
51
58
  // 204 No Content
52
59
  } catch (error) {
53
- await handleWriteMethodError(error, context, 'PATCH_RELATIONSHIP', scopeName, log)
60
+ await handleWriteMethodError(error, context, 'PATCH_RELATIONSHIP', scopeName, log, runHooks)
54
61
  }
55
62
  }
@@ -1,8 +1,12 @@
1
- import { RestApiResourceError } from '../../../lib/rest-api-errors.js'
2
- import { validatePatchPayload } from '../lib/querying-writing/payload-validators.js'
1
+ import { RestApiResourceError, RestApiValidationError } from '../../../lib/rest-api-errors.js'
3
2
  import { processRelationships } from '../lib/writing/relationship-processor.js'
4
3
  import { updateManyToManyRelationship } from '../lib/writing/many-to-many-manipulations.js'
5
4
  import { ERROR_SUBTYPES } from '../lib/querying-writing/knex-constants.js'
5
+ import { getRequestContracts, validateRequestContractOrThrow } from '../lib/querying-writing/request-contracts.js'
6
+ import {
7
+ requireDocumentResourceId,
8
+ requireExistingResourceId
9
+ } from '../lib/querying-writing/resource-id-normalization.js'
6
10
  import {
7
11
  setupCommonRequest,
8
12
  validateResourceAttributesBeforeWrite,
@@ -49,23 +53,63 @@ export default async function patchMethod ({
49
53
  api,
50
54
  helpers
51
55
  })
52
- context.id = context.inputRecord.data.id
53
-
54
56
  // Run early hooks for pre-processing (e.g., file handling)
55
57
  await runHooks('beforeProcessing')
56
58
  await runHooks('beforeProcessingPatch')
57
59
 
58
- // Validate PATCH payload to ensure the partial update actually contains changes.
59
- // PATCH requests must include either attributes to update or relationships to modify -
60
- // an empty PATCH is invalid. This prevents accidental no-op requests and ensures clients
61
- // are explicit about what they want to change. Unlike PUT, PATCH preserves all fields
62
- // not mentioned in the request.
63
- // Example: data must have either attributes: {title: 'New'} or relationships: {author: {...}}
64
- validatePatchPayload(context.inputRecord, scopes)
60
+ const requestContracts = getRequestContracts({
61
+ scopeName,
62
+ schemaInfo: context.schemaInfo,
63
+ includeDepthLimit: vars.includeDepthLimit,
64
+ sortableFields: vars.sortableFields
65
+ })
66
+ const normalizedPathId = params.id === undefined
67
+ ? null
68
+ : requireExistingResourceId(params.id, {
69
+ scopeOptions,
70
+ vars,
71
+ scopeName
72
+ })
73
+
74
+ if (normalizedPathId && !context.inputRecord?.data?.id) {
75
+ context.inputRecord = {
76
+ ...context.inputRecord,
77
+ data: {
78
+ ...(context.inputRecord?.data || {}),
79
+ id: normalizedPathId
80
+ }
81
+ }
82
+ }
83
+
84
+ context.inputRecord = validateRequestContractOrThrow(
85
+ requestContracts.patch,
86
+ context.inputRecord,
87
+ 'PATCH request body is invalid'
88
+ )
89
+ const normalizedBodyId = requireDocumentResourceId(context.inputRecord.data.id, {
90
+ scopeOptions,
91
+ vars
92
+ })
93
+
94
+ if (normalizedPathId && normalizedPathId !== normalizedBodyId) {
95
+ throw new RestApiValidationError(
96
+ `ID mismatch. URL path ID '${normalizedPathId}' does not match request body ID '${normalizedBodyId}'`,
97
+ {
98
+ fields: ['data.id'],
99
+ violations: [{
100
+ field: 'data.id',
101
+ rule: 'id_consistency',
102
+ message: 'Request body ID must match URL path ID when both are provided'
103
+ }]
104
+ }
105
+ )
106
+ }
107
+ context.inputRecord.data.id = normalizedBodyId
108
+ context.id = normalizedPathId || normalizedBodyId
65
109
 
66
110
  // Validate that user has read access to all related resources
67
111
  // This ensures users can only create relationships to resources they can access
68
- await validateRelationshipAccess(context, context.inputRecord, helpers, runHooks, api)
112
+ await validateRelationshipAccess(context, context.inputRecord, helpers, api)
69
113
 
70
114
  // Extract foreign keys from JSON:API relationships and prepare many-to-many operations
71
115
  // Example: relationships.author -> author_id: '123' for storage
@@ -1,6 +1,10 @@
1
1
  import { RestApiResourceError, RestApiValidationError, RestApiPayloadError } from '../../../lib/rest-api-errors.js'
2
2
  import { findRelationshipDefinition, handleWriteMethodError } from './common.js'
3
3
  import { createPivotRecords } from '../lib/writing/many-to-many-manipulations.js'
4
+ import {
5
+ normalizeRelationshipIdentifiers,
6
+ requireExistingResourceId
7
+ } from '../lib/querying-writing/resource-id-normalization.js'
4
8
 
5
9
  /**
6
10
  * POST RELATIONSHIP
@@ -12,9 +16,13 @@ import { createPivotRecords } from '../lib/writing/many-to-many-manipulations.js
12
16
  * @param {array} relationshipData - Array of resource identifiers to add
13
17
  * @returns {Promise<void>} 204 No Content
14
18
  */
15
- export default async function postRelationshipMethod ({ params, context, vars, helpers, scope, scopes, runHooks, scopeName, api, log }) {
19
+ export default async function postRelationshipMethod ({ params, context, vars, helpers, scope, scopes, runHooks, scopeOptions, scopeName, api, log }) {
16
20
  context.method = 'postRelationship'
17
- context.id = params.id
21
+ context.id = requireExistingResourceId(params.id, {
22
+ scopeOptions,
23
+ vars,
24
+ scopeName
25
+ })
18
26
  context.relationshipName = params.relationshipName
19
27
  context.schemaInfo = scopes[scopeName].vars.schemaInfo
20
28
 
@@ -45,6 +53,7 @@ export default async function postRelationshipMethod ({ params, context, vars, h
45
53
  if (!Array.isArray(params.relationshipData)) {
46
54
  throw new RestApiPayloadError('POST to relationship requires array of resource identifiers')
47
55
  }
56
+ params.relationshipData = normalizeRelationshipIdentifiers(params.relationshipData, { api })
48
57
 
49
58
  // Check permissions
50
59
  await runHooks('checkPermissions')
@@ -1,6 +1,7 @@
1
- import { validatePostPayload } from '../lib/querying-writing/payload-validators.js'
2
1
  import { processRelationships } from '../lib/writing/relationship-processor.js'
2
+ import { getRequestContracts, validateRequestContractOrThrow } from '../lib/querying-writing/request-contracts.js'
3
3
  import { createPivotRecords } from '../lib/writing/many-to-many-manipulations.js'
4
+ import { requireDocumentResourceId } from '../lib/querying-writing/resource-id-normalization.js'
4
5
  import {
5
6
  setupCommonRequest,
6
7
  validateResourceAttributesBeforeWrite,
@@ -50,16 +51,28 @@ export default async function postMethod ({
50
51
  await runHooks('beforeProcessing')
51
52
  await runHooks('beforeProcessingPost')
52
53
 
53
- // Validate POST payload to ensure it follows JSON:API format and references valid resources.
54
- // This checks the payload has required 'data' object with 'type' and 'attributes', validates
55
- // that data.type matches a real resource type (preventing creation of non-existent resources),
56
- // and ensures any relationships reference valid resource types with proper ID format.
57
- // Example: data.type: 'articles' must be a registered scope, relationships.author must reference 'users'.
58
- validatePostPayload(context.inputRecord, scopes)
54
+ const requestContracts = getRequestContracts({
55
+ scopeName,
56
+ schemaInfo: context.schemaInfo,
57
+ includeDepthLimit: vars.includeDepthLimit,
58
+ sortableFields: vars.sortableFields
59
+ })
60
+ context.inputRecord = validateRequestContractOrThrow(
61
+ requestContracts.post,
62
+ context.inputRecord,
63
+ 'POST request body is invalid'
64
+ )
65
+
66
+ if (context.inputRecord?.data?.id !== undefined) {
67
+ context.inputRecord.data.id = requireDocumentResourceId(context.inputRecord.data.id, {
68
+ scopeOptions,
69
+ vars
70
+ })
71
+ }
59
72
 
60
73
  // Validate that user has read access to all related resources
61
74
  // This ensures users can only create relationships to resources they can access
62
- await validateRelationshipAccess(context, context.inputRecord, helpers, runHooks, api)
75
+ await validateRelationshipAccess(context, context.inputRecord, helpers, api)
63
76
 
64
77
  // Extract foreign keys from JSON:API relationships and prepare many-to-many operations
65
78
  // Example: relationships.author -> author_id: '123' for storage
@@ -1,10 +1,15 @@
1
- import { RestApiResourceError } from '../../../lib/rest-api-errors.js'
2
- import { validatePutPayload } from '../lib/querying-writing/payload-validators.js'
1
+ import { RestApiResourceError, RestApiValidationError } from '../../../lib/rest-api-errors.js'
3
2
  import { processRelationships } from '../lib/writing/relationship-processor.js'
4
3
  import { updateManyToManyRelationship, createPivotRecords } from '../lib/writing/many-to-many-manipulations.js'
5
4
  import { ERROR_SUBTYPES } from '../lib/querying-writing/knex-constants.js'
5
+ import { getRequestContracts, validateRequestContractOrThrow } from '../lib/querying-writing/request-contracts.js'
6
+ import {
7
+ requireDocumentResourceId,
8
+ requireExistingResourceId
9
+ } from '../lib/querying-writing/resource-id-normalization.js'
6
10
  import {
7
11
  setupCommonRequest,
12
+ validateCompleteReplacePayload,
8
13
  validateResourceAttributesBeforeWrite,
9
14
  validateRelationshipAccess,
10
15
  applyFieldSetters,
@@ -17,7 +22,9 @@ import {
17
22
  * PUT
18
23
  * Updates an existing top-level resource by completely replacing it.
19
24
  * This method supports updating both attributes and relationships (1:1 and n:n).
20
- * PUT is a complete replacement - any relationships not provided will be removed/nulled.
25
+ * Existing persisted values cannot be silently dropped: if a stored attribute or belongsTo
26
+ * relationship already has a value, PUT must include it explicitly. Relationship collections
27
+ * still follow replacement semantics when a relationships object is provided.
21
28
  * This method does NOT support creating new related resources via an `included` array.
22
29
  */
23
30
  export default async function putMethod ({
@@ -48,23 +55,63 @@ export default async function putMethod ({
48
55
  api,
49
56
  helpers
50
57
  })
51
- context.id = context.inputRecord.data.id
52
-
53
58
  // Run early hooks for pre-processing (e.g., file handling)
54
59
  await runHooks('beforeProcessing')
55
60
  await runHooks('beforeProcessingPut')
56
61
 
57
- // Validate PUT payload to ensure it's a complete resource replacement operation.
58
- // PUT requires the full resource representation including ID (unlike POST which generates ID).
59
- // It validates that data.id matches the URL parameter, prevents 'included' array (which is
60
- // read-only), and ensures the payload represents a complete replacement. Any fields not
61
- // provided will be removed or reset to defaults - this is the key difference from PATCH.
62
- // Example: PUT to /articles/123 must have data.id: '123' and all required fields.
63
- validatePutPayload(context.inputRecord, scopes)
62
+ const requestContracts = getRequestContracts({
63
+ scopeName,
64
+ schemaInfo: context.schemaInfo,
65
+ includeDepthLimit: vars.includeDepthLimit,
66
+ sortableFields: vars.sortableFields
67
+ })
68
+ const normalizedPathId = params.id === undefined
69
+ ? null
70
+ : requireExistingResourceId(params.id, {
71
+ scopeOptions,
72
+ vars,
73
+ scopeName
74
+ })
75
+
76
+ if (normalizedPathId && !context.inputRecord?.data?.id) {
77
+ context.inputRecord = {
78
+ ...context.inputRecord,
79
+ data: {
80
+ ...(context.inputRecord?.data || {}),
81
+ id: normalizedPathId
82
+ }
83
+ }
84
+ }
85
+
86
+ context.inputRecord = validateRequestContractOrThrow(
87
+ requestContracts.put,
88
+ context.inputRecord,
89
+ 'PUT request body is invalid'
90
+ )
91
+ const normalizedBodyId = requireDocumentResourceId(context.inputRecord.data.id, {
92
+ scopeOptions,
93
+ vars
94
+ })
95
+
96
+ if (normalizedPathId && normalizedPathId !== normalizedBodyId) {
97
+ throw new RestApiValidationError(
98
+ `ID mismatch. URL path ID '${normalizedPathId}' does not match request body ID '${normalizedBodyId}'`,
99
+ {
100
+ fields: ['data.id'],
101
+ violations: [{
102
+ field: 'data.id',
103
+ rule: 'id_consistency',
104
+ message: 'Request body ID must match URL path ID when both are provided'
105
+ }]
106
+ }
107
+ )
108
+ }
109
+ context.inputRecord.data.id = normalizedBodyId
110
+ context.id = normalizedPathId || normalizedBodyId
64
111
 
65
112
  // Validate that user has read access to all related resources
66
113
  // This ensures users can only create relationships to resources they can access
67
- await validateRelationshipAccess(context, context.inputRecord, helpers, runHooks, api)
114
+ await validateRelationshipAccess(context, context.inputRecord, helpers, api)
68
115
 
69
116
  // Extract foreign keys from JSON:API relationships and prepare many-to-many operations
70
117
  // Example: relationships.author -> author_id: '123' for storage
@@ -74,13 +121,6 @@ export default async function putMethod ({
74
121
  { context }
75
122
  )
76
123
 
77
- await validateResourceAttributesBeforeWrite({
78
- context,
79
- schema,
80
- belongsToUpdates,
81
- runHooks
82
- })
83
-
84
124
  // Check existence first
85
125
  context.exists = await helpers.dataExists({
86
126
  scopeName,
@@ -108,8 +148,20 @@ export default async function putMethod ({
108
148
  context.minimalRecord = minimalRecord
109
149
  }
110
150
 
151
+ validateCompleteReplacePayload({
152
+ context,
153
+ belongsToUpdates
154
+ })
155
+
156
+ await validateResourceAttributesBeforeWrite({
157
+ context,
158
+ schema,
159
+ belongsToUpdates,
160
+ runHooks
161
+ })
162
+
111
163
  // For PUT, we also need to handle relationships that are NOT provided
112
- // (they should be set to null/empty as PUT is a complete replacement)
164
+ // (they should be set to null/empty when a relationships object is provided)
113
165
  const allRelationships = {}
114
166
 
115
167
  // Collect all defined relationships for this resource
@@ -1,8 +1,8 @@
1
- import { RestApiValidationError } from '../../../lib/rest-api-errors.js'
2
- import { validateQueryPayload } from '../lib/querying-writing/payload-validators.js'
3
1
  import { normalizeRecordAttributes } from '../lib/querying-writing/database-value-normalizers.js'
4
2
  import { getRequestedComputedFields } from '../lib/querying-writing/knex-field-helpers.js'
3
+ import { getEffectiveSortableFields } from '../lib/querying/query-field-sort-helpers.js'
5
4
  import { transformJsonApiToSimplified } from '../lib/querying-writing/simplified-helpers.js'
5
+ import { getRequestContracts, validateRequestContractOrThrow } from '../lib/querying-writing/request-contracts.js'
6
6
  import { cascadeConfig } from './common.js'
7
7
 
8
8
  /**
@@ -61,66 +61,28 @@ export default async function queryMethod ({
61
61
  context.scopeName = scopeName
62
62
 
63
63
  // These are just shortcuts used in this function and will be returned
64
- const searchSchema = context.schemaInfo.searchSchemaInstance
65
64
  const schemaStructure = context.schemaInfo.schemaInstance.structure
66
65
  const schemaRelationships = context.schemaInfo.schemaRelationships
67
66
 
68
67
  // Sortable fields and sort (mab)
69
- context.sortableFields = vars.sortableFields
68
+ context.sortableFields = getEffectiveSortableFields(vars)
70
69
  // Apply default sort if no sort specified
71
70
  if (context.queryParams.sort.length === 0 && vars.defaultSort) {
72
71
  context.queryParams.sort = Array.isArray(vars.defaultSort) ? vars.defaultSort : [vars.defaultSort]
73
72
  }
74
73
 
75
- // Validate query parameters to ensure they follow JSON:API specification and security rules.
76
- // This checks that filters are valid field names, sort fields exist in sortableFields array
77
- // (preventing SQL injection), pagination uses valid page[size]/page[number] format, and include
78
- // paths reference real relationships. Example: sort: ['-createdAt', 'title'] is checked against
79
- // sortableFields to ensure users can't sort by sensitive fields like 'password_hash'.
80
- validateQueryPayload({ queryParams: context.queryParams }, context.sortableFields, vars.includeDepthLimit)
81
-
82
- // Validate search/filter parameters against searchSchema
83
- if (context.queryParams.filters && Object.keys(context.queryParams.filters).length > 0) {
84
- // Only allow filtering if searchSchema is defined
85
- if (!searchSchema) {
86
- throw new RestApiValidationError(
87
- `Filtering is not enabled for resource '${scopeName}'. To enable filtering, add 'search: true' to schema fields or define a searchSchema.`,
88
- {
89
- fields: Object.keys(context.queryParams.filters).map(field => `filters.${field}`),
90
- violations: [{
91
- field: 'filters',
92
- rule: 'filtering_not_enabled',
93
- message: 'Resource does not have searchable fields defined'
94
- }]
95
- }
96
- )
97
- }
98
-
99
- // Validate the filter parameters searchSchema
100
- const { validatedObject, errors } = await searchSchema.validate(context.queryParams.filters, {
101
- onlyObjectValues: true // Partial validation for filters
102
- })
103
-
104
- // If there are validation errors, throw an error
105
- if (Object.keys(errors).length > 0) {
106
- const violations = Object.entries(errors).map(([field, error]) => ({
107
- field: `filters.${field}`,
108
- rule: error.code || 'invalid_value',
109
- message: error.message
110
- }))
111
-
112
- throw new RestApiValidationError(
113
- 'Invalid filter parameters',
114
- {
115
- fields: Object.keys(errors).map(field => `filters.${field}`),
116
- violations
117
- }
118
- )
119
- }
120
-
121
- // Replace filter with validated/transformed values
122
- context.queryParams.filters = validatedObject
123
- }
74
+ const requestContracts = getRequestContracts({
75
+ scopeName,
76
+ schemaInfo: context.schemaInfo,
77
+ includeDepthLimit: vars.includeDepthLimit,
78
+ sortableFields: context.sortableFields
79
+ })
80
+ const validatedRequest = validateRequestContractOrThrow(
81
+ requestContracts.query,
82
+ { queryParams: context.queryParams },
83
+ 'Query parameters are invalid'
84
+ )
85
+ context.queryParams = validatedRequest.queryParams || {}
124
86
 
125
87
  // Centralised checkPermissions function
126
88
  await scope.checkPermissions({
@@ -1,5 +1,3 @@
1
- import { ValidationError } from 'hooked-api'
2
-
3
1
  /**
4
2
  * release
5
3
  * Runs the hook that tells plugins to release resources
@@ -16,6 +16,7 @@ import patchMethod from './rest-api-plugin-methods/patch.js'
16
16
  import deleteMethod from './rest-api-plugin-methods/delete.js'
17
17
  import enrichAttributesMethod from './rest-api-plugin-methods/enrich-attributes.js'
18
18
  import checkPermissionsMethod from './rest-api-plugin-methods/check-permissions.js'
19
+ import applyQueryFiltersMethod from './rest-api-plugin-methods/apply-query-filters.js'
19
20
  import addRouteMethod from './rest-api-plugin-methods/add-route.js'
20
21
  import releaseMethod from './rest-api-plugin-methods/release.js'
21
22
  import { defaultDataHelpers } from './lib/querying-writing/default-data-helpers.js'
@@ -26,6 +27,7 @@ import postRelationshipMethod from './rest-api-plugin-methods/post-relationship.
26
27
  import getRelationshipMethod from './rest-api-plugin-methods/get-relationship.js'
27
28
  import patchRelationshipMethod from './rest-api-plugin-methods/patch-relationship.js'
28
29
  import deleteRelationshipMethod from './rest-api-plugin-methods/delete-relationship.js'
30
+ import { defaultNormalizeResourceId } from './lib/querying-writing/resource-id-normalization.js'
29
31
 
30
32
  export const RestApiPlugin = {
31
33
  name: 'rest-api',
@@ -65,6 +67,9 @@ export const RestApiPlugin = {
65
67
  : true // Default true for better DX in programmatic API
66
68
 
67
69
  vars.idProperty = restApiOptions.idProperty || 'id'
70
+ vars.normalizeId = typeof restApiOptions.normalizeId === 'function'
71
+ ? restApiOptions.normalizeId
72
+ : defaultNormalizeResourceId
68
73
 
69
74
  // Return full record configuration for API and Transport
70
75
  // Support values: 'no', 'minimal', 'full'
@@ -148,6 +153,7 @@ export const RestApiPlugin = {
148
153
  // Non-URL methods
149
154
  addScopeMethod('enrichAttributes', enrichAttributesMethod)
150
155
  addScopeMethod('checkPermissions', checkPermissionsMethod)
156
+ addScopeMethod('applyQueryFilters', applyQueryFiltersMethod)
151
157
 
152
158
  // *********
153
159
  // Helpers
@@ -1,8 +1,6 @@
1
1
  import {
2
2
  calculatePosition,
3
3
  getInitialPosition,
4
- isValidPosition,
5
- assignInitialPositions,
6
4
  } from './lib/fractional-positioning.js'
7
5
  import { createStorageAdapter } from './lib/storage/storage-adapter.js'
8
6
 
@@ -27,7 +25,7 @@ export const PositioningPlugin = {
27
25
  // Get configuration - hooked-api namespaces options by plugin name
28
26
  const positioningOptions = pluginOptions || {}
29
27
 
30
- // Store configuration in vars (data only) - inspired by multihome pattern
28
+ // Store configuration in vars (data only) so hooks can use it without re-reading plugin options.
31
29
  vars.positioning = {
32
30
  field: positioningOptions.field || 'position',
33
31
  filters: positioningOptions.filters || [],
@@ -167,7 +165,7 @@ export const PositioningPlugin = {
167
165
  fieldExists = true
168
166
  } else {
169
167
  // Check if it's a relationship name (as property)
170
- for (const [fieldName, fieldDef] of Object.entries(schema)) {
168
+ for (const [, fieldDef] of Object.entries(schema)) {
171
169
  if (fieldDef.as === filterField) {
172
170
  fieldExists = true
173
171
  break
@@ -419,7 +417,6 @@ export const PositioningPlugin = {
419
417
  log.debug('*** Position filter conditions', { scopeName, filterConditions })
420
418
 
421
419
  // Query existing items in the same position group
422
- const knex = api.knex.instance
423
420
  const tableName = adapter.getTableName()
424
421
  const idProperty = schemaInfo.idProperty
425
422
  const idColumn = adapter.getIdColumn()