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,300 +0,0 @@
1
- 1 import { RestApiResourceError } from '../../../../lib/rest-api-errors.js'
2
- 2 import { transformSimplifiedToJsonApi } from '../querying-writing/simplified-helpers.js'
3
- 3
4
- 4 /**
5
- 5 * Updates many-to-many relationships intelligently by synchronizing pivot table records
6
- 6 *
7
- 7 * @param {Object} scope - The scope object (not used directly, for consistency)
8
- 8 * @param {Object} deps - Dependencies object
9
- 9 * @returns {Promise<void>}
10
- 10 *
11
- 11 * @example
12
- 12 * // Input: Article currently has tags [1, 2], want to change to [1, 3]
13
- 13 * const deps = {
14
- 14 * api,
15
- 15 * context: {
16
- 16 * resourceId: '100',
17
- 17 * relDef: {
18
- 18 * through: 'article_tags', // Pivot table
19
- 19 * foreignKey: 'article_id', // Points to article
20
- 20 * otherKey: 'tag_id' // Points to tag
21
- 21 * },
22
- 22 * relData: [
23
- 23 * { type: 'tags', id: '1' }, // Keep this
24
- 24 * { type: 'tags', id: '3' } // Add this
25
- 25 * ],
26
- 26 * transaction: trx
27
- 27 * }
28
- 28 * };
29
- 29 *
30
- 30 * // Before: article_tags table
31
- 31 * // article_id | tag_id | created_at
32
- 32 * // 100 | 1 | 2024-01-01
33
- 33 * // 100 | 2 | 2024-01-02
34
- 34 *
35
- 35 * await updateManyToManyRelationship(null, deps);
36
- 36 *
37
- 37 * // After: article_tags table
38
- 38 * // article_id | tag_id | created_at
39
- 39 * // 100 | 1 | 2024-01-01 (preserved!)
40
- 40 * // 100 | 3 | 2024-12-01 (new)
41
- 41 * // Tag 2 was deleted, Tag 1 kept its metadata
42
- 42 *
43
- 43 * @example
44
- 44 * // Input: Pivot table has extra fields to preserve
45
- 45 * // article_tags has: article_id, tag_id, display_order, featured
46
- 46 *
47
- 47 * // Current data:
48
- 48 * // article_id | tag_id | display_order | featured
49
- 49 * // 100 | 1 | 1 | true
50
- 50 * // 100 | 2 | 2 | false
51
- 51 *
52
- 52 * const deps = {
53
- 53 * context: {
54
- 54 * resourceId: '100',
55
- 55 * relData: [
56
- 56 * { type: 'tags', id: '1' }, // Keep tag 1
57
- 57 * { type: 'tags', id: '5' } // Add tag 5
58
- 58 * ]
59
- 59 * }
60
- 60 * };
61
- 61 *
62
- 62 * await updateManyToManyRelationship(null, deps);
63
- 63 *
64
- 64 * // Result:
65
- 65 * // article_id | tag_id | display_order | featured
66
- 66 * // 100 | 1 | 1 | true (preserved!)
67
- 67 * // 100 | 5 | NULL | NULL (new with defaults)
68
- 68 *
69
- 69 * @example
70
- 70 * // Input: Clear all relationships
71
- 71 * const deps = {
72
- 72 * context: {
73
- 73 * resourceId: '100',
74
- 74 * relData: [] // Empty array means remove all
75
- 75 * }
76
- 76 * };
77
- 77 *
78
- 78 * await updateManyToManyRelationship(null, deps);
79
- 79 * // All article_tags records for article 100 are deleted
80
- 80 *
81
- 81 * @description
82
- 82 * Used by:
83
- 83 * - relationship-processor.js calls this for many-to-many updates
84
- 84 * - Used in PATCH operations to sync relationships
85
- 85 * - Also used in PUT operations (replaces deleteExistingPivotRecords pattern)
86
- 86 *
87
- 87 * Purpose:
88
- 88 * - Intelligently syncs pivot table to match desired state
89
- 89 * - Preserves existing pivot records that should remain (with their metadata)
90
- 90 * - Only deletes records that should be removed
91
- 91 * - Only creates records that are new
92
- 92 * - Much better than delete-all-then-recreate pattern
93
- 93 *
94
- 94 * Data flow:
95
- 95 * 1. Queries existing pivot records for the resource
96
- 96 * 2. Compares existing IDs with desired IDs
97
- 97 * 3. Calculates which to delete and which to add
98
- 98 * 4. Validates new related resources exist (optional)
99
- 99 * 5. Performs bulk delete for removed relationships
100
- 100 * 6. Performs bulk insert for new relationships
101
- 101 * 7. Records that exist in both are untouched (metadata preserved)
102
- 102 */
103
- 103 export const updateManyToManyRelationship = async (scope, deps) => {
104
- 104 // Extract values from deps
105
- 105 const { api, context } = deps
106
- 106 const { resourceId, relDef, relData, transaction: trx } = context
107
- 107
108
- 108 // Get the knex instance from the pivot scope
109
- 109 const pivotScope = api.resources[relDef.through]
110
- 110 if (!pivotScope) {
111
- 111 throw new Error(`Pivot table resource '${relDef.through}' not found`)
112
- 112 }
113
- 113
114
- 114 // Get the actual database table name (might be different from scope name)
115
- 115 const tableName = pivotScope.vars.schemaInfo.tableName || relDef.through
116
- 116
117
- 117 // Get existing pivot records directly from database
118
- 118 const existingRecords = await trx(tableName)
119
- 119 .where(relDef.foreignKey, resourceId)
120
- 120 .select(relDef.otherKey)
121
- 121
122
- 122 // Create sets for efficient comparison
123
- 123 const existingIds = new Set(existingRecords.map(r => String(r[relDef.otherKey])))
124
- 124 const newIds = new Set(relData.map(r => String(r.id)))
125
- 125
126
- 126 // Determine what to delete and add
127
- 127 const toDelete = [...existingIds].filter(id => !newIds.has(id))
128
- 128 const toAdd = [...newIds].filter(id => !existingIds.has(id))
129
- 129
130
- 130 // Validate related resources exist if needed (do this before any changes)
131
- 131 if (relDef.validateExists !== false && toAdd.length > 0) {
132
- 132 for (const relatedId of toAdd) {
133
- 133 const related = relData.find(r => String(r.id) === relatedId)
134
- 134 try {
135
- 135 await api.resources[related.type].get({
136
- 136 id: related.id,
137
- 137 transaction: trx
138
- 138 })
139
- 139 } catch (error) {
140
- 140 throw new RestApiResourceError(
141
- 141 `Related ${related.type} with id ${related.id} not found`,
142
- 142 {
143
- 143 subtype: 'not_found',
144
- 144 resourceType: related.type,
145
- 145 resourceId: related.id
146
- 146 }
147
- 147 )
148
- 148 }
149
- 149 }
150
- 150 }
151
- 151
152
- 152 // Bulk delete records that should be removed
153
- 153 if (toDelete.length > 0) {
154
- 154 await trx(tableName)
155
- 155 .where(relDef.foreignKey, resourceId)
156
- 156 .whereIn(relDef.otherKey, toDelete)
157
- 157 .delete()
158
- 158 }
159
- 159
160
- 160 // Bulk insert new records
161
- 161 if (toAdd.length > 0) {
162
- 162 const recordsToInsert = toAdd.map(relatedId => ({
163
- 163 [relDef.foreignKey]: resourceId,
164
- 164 [relDef.otherKey]: relatedId
165
- 165 }))
166
- 166
167
- 167 await trx(tableName).insert(recordsToInsert)
168
- 168 }
169
- 169
170
- 170 // Records that exist in both are automatically preserved with their pivot data
171
- 171 }
172
- 172
173
- 173 // Note: deleteExistingPivotRecords has been removed in favor of using
174
- 174 // updateManyToManyRelationship for all sync operations (including PUT).
175
- 175 // This aligns with industry standards where ORMs use intelligent sync
176
- 176 // rather than delete-all-then-recreate patterns.
177
- 177
178
- 178 /**
179
- 179 * Creates new pivot table records for many-to-many relationships
180
- 180 *
181
- 181 * @param {Object} api - The API instance with access to resources
182
- 182 * @param {string|number} resourceId - The ID of the primary resource
183
- 183 * @param {Object} relDef - The relationship definition
184
- 184 * @param {Array} relData - Array of related resources to link
185
- 185 * @param {Object} trx - Database transaction object
186
- 186 * @returns {Promise<void>}
187
- 187 *
188
- 188 * @example
189
- 189 * // Input: Create article-tag relationships
190
- 190 * const relDef = {
191
- 191 * through: 'article_tags',
192
- 192 * foreignKey: 'article_id',
193
- 193 * otherKey: 'tag_id'
194
- 194 * };
195
- 195 * const relData = [
196
- 196 * { type: 'tags', id: '10' },
197
- 197 * { type: 'tags', id: '20' },
198
- 198 * { type: 'tags', id: '30' }
199
- 199 * ];
200
- 200 *
201
- 201 * await createPivotRecords(api, '100', relDef, relData, trx);
202
- 202 *
203
- 203 * // Result: 3 new records in article_tags table
204
- 204 * // article_id | tag_id
205
- 205 * // 100 | 10
206
- 206 * // 100 | 20
207
- 207 * // 100 | 30
208
- 208 *
209
- 209 * @example
210
- 210 * // Input: Validation ensures related resources exist
211
- 211 * const relData = [
212
- 212 * { type: 'tags', id: '999' } // Non-existent tag
213
- 213 * ];
214
- 214 *
215
- 215 * try {
216
- 216 * await createPivotRecords(api, '100', relDef, relData, trx);
217
- 217 * } catch (error) {
218
- 218 * console.log(error.message);
219
- 219 * // "Related tags with id 999 not found"
220
- 220 * // Transaction rolled back, no records created
221
- 221 * }
222
- 222 *
223
- 223 * @example
224
- 224 * // Input: Skip validation for performance
225
- 225 * const relDef = {
226
- 226 * through: 'user_permissions',
227
- 227 * foreignKey: 'user_id',
228
- 228 * otherKey: 'permission_id',
229
- 229 * validateExists: false // Skip GET requests
230
- 230 * };
231
- 231 *
232
- 232 * // With 100 permissions, saves 100 GET requests
233
- 233 * await createPivotRecords(api, userId, relDef, permissions, trx);
234
- 234 *
235
- 235 * // Risk: Could create orphaned relationships if permissions don't exist
236
- 236 * // Benefit: Much faster for bulk operations when you trust the data
237
- 237 *
238
- 238 * @description
239
- 239 * Used by:
240
- 240 * - relationship-processor.js for POST operations
241
- 241 * - updateManyToManyRelationship internally for new relationships
242
- 242 * - Any code that needs to create pivot records
243
- 243 *
244
- 244 * Purpose:
245
- 245 * - Creates pivot table records to link resources
246
- 246 * - Validates related resources exist by default (referential integrity)
247
- 247 * - Supports bulk insert for efficiency
248
- 248 * - Works within transactions for atomicity
249
- 249 * - Allows skipping validation when performance matters
250
- 250 *
251
- 251 * Data flow:
252
- 252 * 1. Validates pivot table resource exists
253
- 253 * 2. Gets actual database table name
254
- 254 * 3. Optionally validates each related resource exists (GET requests)
255
- 255 * 4. Prepares bulk insert data with foreign keys
256
- 256 * 5. Performs single INSERT with all records
257
- 257 * 6. Returns (no data returned, throws on error)
258
- 258 */
259
- 259 export const createPivotRecords = async (api, resourceId, relDef, relData, trx) => {
260
- 260 if (relData.length === 0) return // Early exit if nothing to create
261
- 261
262
- 262 // Get pivot table info
263
- 263 const pivotScope = api.resources[relDef.through]
264
- 264 if (!pivotScope) {
265
- 265 throw new Error(`Pivot table resource '${relDef.through}' not found`)
266
- 266 }
267
- 267
268
- 268 // Get the actual database table name (might be different from scope name)
269
- 269 const tableName = pivotScope.vars.schemaInfo.tableName || relDef.through
270
- 270
271
- 271 // Validate all related resources exist if needed (do this before any inserts)
272
- 272 if (relDef.validateExists !== false) {
273
- 273 for (const related of relData) {
274
- 274 try {
275
- 275 await api.resources[related.type].get({
276
- 276 id: related.id,
277
- 277 transaction: trx
278
- 278 })
279
- 279 } catch (error) {
280
- 280 throw new RestApiResourceError(
281
- 281 `Related ${related.type} with id ${related.id} not found`,
282
- 282 {
283
- 283 subtype: 'not_found',
284
- 284 resourceType: related.type,
285
- 285 resourceId: related.id
286
- 286 }
287
- 287 )
288
- 288 }
289
- 289 }
290
- 290 }
291
- 291
292
- 292 // Prepare records for bulk insert
293
- 293 const recordsToInsert = relData.map(related => ({
294
- 294 [relDef.foreignKey]: resourceId,
295
- 295 [relDef.otherKey]: related.id
296
- 296 }))
297
- 297
298
- 298 // Bulk insert all pivot records in a single query
299
- 299 await trx(tableName).insert(recordsToInsert)
300
- 300 }
@@ -1,245 +0,0 @@
1
- 1 import { RestApiValidationError } from '../../../../lib/rest-api-errors.js'
2
- 2
3
- 3 /**
4
- 4 * Processes JSON:API relationship data and converts to database operations
5
- 5 *
6
- 6 * @param {Object} scope - The scope object containing schema info
7
- 7 * @param {Object} deps - Dependencies object
8
- 8 * @returns {Object} Object with belongsToUpdates and manyToManyRelationships arrays
9
- 9 *
10
- 10 * @example
11
- 11 * // Input: Simple belongsTo relationship
12
- 12 * const inputRecord = {
13
- 13 * data: {
14
- 14 * type: 'articles',
15
- 15 * attributes: { title: 'My Article' },
16
- 16 * relationships: {
17
- 17 * author: {
18
- 18 * data: { type: 'users', id: '123' }
19
- 19 * },
20
- 20 * category: {
21
- 21 * data: { type: 'categories', id: '5' }
22
- 22 * }
23
- 23 * }
24
- 24 * }
25
- 25 * };
26
- 26 *
27
- 27 * // Schema has:
28
- 28 * // author_id: { belongsTo: 'users', as: 'author' }
29
- 29 * // category_id: { belongsTo: 'categories', as: 'category' }
30
- 30 *
31
- 31 * const result = processRelationships(scope, { context: { inputRecord } });
32
- 32 *
33
- 33 * // Output: Foreign keys extracted
34
- 34 * // {
35
- 35 * // belongsToUpdates: {
36
- 36 * // author_id: '123',
37
- 37 * // category_id: '5'
38
- 38 * // },
39
- 39 * // manyToManyRelationships: []
40
- 40 * // }
41
- 41 *
42
- 42 * @example
43
- 43 * // Input: Polymorphic relationship
44
- 44 * const inputRecord = {
45
- 45 * data: {
46
- 46 * type: 'comments',
47
- 47 * relationships: {
48
- 48 * commentable: {
49
- 49 * data: { type: 'posts', id: '456' }
50
- 50 * }
51
- 51 * }
52
- 52 * }
53
- 53 * };
54
- 54 *
55
- 55 * // Schema relationships has:
56
- 56 * // commentable: {
57
- 57 * // belongsToPolymorphic: {
58
- 58 * // types: ['posts', 'videos'],
59
- 59 * // typeField: 'commentable_type',
60
- 60 * // idField: 'commentable_id'
61
- 61 * // }
62
- 62 * // }
63
- 63 *
64
- 64 * const result = processRelationships(scope, { context: { inputRecord } });
65
- 65 *
66
- 66 * // Output: Both type and id fields set
67
- 67 * // {
68
- 68 * // belongsToUpdates: {
69
- 69 * // commentable_type: 'posts',
70
- 70 * // commentable_id: '456'
71
- 71 * // },
72
- 72 * // manyToManyRelationships: []
73
- 73 * // }
74
- 74 *
75
- 75 * @example
76
- 76 * // Input: Many-to-many relationship
77
- 77 * const inputRecord = {
78
- 78 * data: {
79
- 79 * type: 'articles',
80
- 80 * relationships: {
81
- 81 * tags: {
82
- 82 * data: [
83
- 83 * { type: 'tags', id: '10' },
84
- 84 * { type: 'tags', id: '20' }
85
- 85 * ]
86
- 86 * }
87
- 87 * }
88
- 88 * }
89
- 89 * };
90
- 90 *
91
- 91 * // Schema relationships has:
92
- 92 * // tags: {
93
- 93 * // type: 'manyToMany',
94
- 94 * // through: 'article_tags',
95
- 95 * // foreignKey: 'article_id',
96
- 96 * // otherKey: 'tag_id'
97
- 97 * // }
98
- 98 *
99
- 99 * const result = processRelationships(scope, { context: { inputRecord } });
100
- 100 *
101
- 101 * // Output: Many-to-many data collected
102
- 102 * // {
103
- 103 * // belongsToUpdates: {},
104
- 104 * // manyToManyRelationships: [{
105
- 105 * // relName: 'tags',
106
- 106 * // relDef: {
107
- 107 * // through: 'article_tags',
108
- 108 * // foreignKey: 'article_id',
109
- 109 * // otherKey: 'tag_id'
110
- 110 * // },
111
- 111 * // relData: [
112
- 112 * // { type: 'tags', id: '10' },
113
- 113 * // { type: 'tags', id: '20' }
114
- 114 * // ]
115
- 115 * // }]
116
- 116 * // }
117
- 117 *
118
- 118 * @example
119
- 119 * // Input: Clearing relationships
120
- 120 * const inputRecord = {
121
- 121 * data: {
122
- 122 * relationships: {
123
- 123 * author: { data: null }, // Clear belongsTo
124
- 124 * tags: { data: [] } // Clear many-to-many
125
- 125 * }
126
- 126 * }
127
- 127 * };
128
- 128 *
129
- 129 * const result = processRelationships(scope, { context: { inputRecord } });
130
- 130 *
131
- 131 * // Output: Null for belongsTo, empty array for many-to-many
132
- 132 * // {
133
- 133 * // belongsToUpdates: { author_id: null },
134
- 134 * // manyToManyRelationships: [{
135
- 135 * // relName: 'tags',
136
- 136 * // relDef: { ... },
137
- 137 * // relData: [] // Will delete all pivot records
138
- 138 * // }]
139
- 139 * // }
140
- 140 *
141
- 141 * @description
142
- 142 * Used by:
143
- 143 * - rest-api-knex-plugin's dataPut and dataPatch methods
144
- 144 * - Called before database writes to prepare relationship updates
145
- 145 *
146
- 146 * Purpose:
147
- 147 * - Converts JSON:API relationship format to database operations
148
- 148 * - Handles all relationship types: belongsTo, polymorphic, many-to-many
149
- 149 * - Validates polymorphic types against allowed values
150
- 150 * - Separates concerns: foreign keys vs pivot table operations
151
- 151 *
152
- 152 * Data flow:
153
- 153 * 1. Receives JSON:API document with relationships section
154
- 154 * 2. Analyzes schema to determine relationship types
155
- 155 * 3. For belongsTo: extracts foreign key values
156
- 156 * 4. For polymorphic: extracts both type and id fields
157
- 157 * 5. For many-to-many: collects data for pivot operations
158
- 158 * 6. Returns structured data for database updates
159
- 159 */
160
- 160 export const processRelationships = (scope, deps) => {
161
- 161 // Extract values from scope
162
- 162 const {
163
- 163 vars: {
164
- 164 schemaInfo: { schemaStructure: schemaFields, schemaRelationships: relationships }
165
- 165 }
166
- 166 } = scope
167
- 167
168
- 168 // Extract values from deps
169
- 169 const { context } = deps
170
- 170 const { inputRecord } = context
171
- 171 const belongsToUpdates = {}
172
- 172 const manyToManyRelationships = []
173
- 173
174
- 174 if (!inputRecord.data.relationships) {
175
- 175 return { belongsToUpdates, manyToManyRelationships }
176
- 176 }
177
- 177
178
- 178 for (const [relName, relData] of Object.entries(inputRecord.data.relationships)) {
179
- 179 const relDef = relationships?.[relName]
180
- 180
181
- 181 // Find the schema field that defines this relationship
182
- 182 const schemaField = Object.entries(schemaFields).find(([fieldName, fieldDef]) =>
183
- 183 fieldDef.as === relName
184
- 184 )
185
- 185
186
- 186 if (schemaField) {
187
- 187 const [fieldName, fieldDef] = schemaField
188
- 188
189
- 189 // Handle regular belongsTo (1:1)
190
- 190 if (fieldDef.belongsTo) {
191
- 191 if (relData.data === null) {
192
- 192 belongsToUpdates[fieldName] = null
193
- 193 } else if (relData.data?.id) {
194
- 194 belongsToUpdates[fieldName] = relData.data.id
195
- 195 }
196
- 196 }
197
- 197 }
198
- 198
199
- 199 // Check for polymorphic belongsTo defined in relationships object (not as schema field)
200
- 200 if (!schemaField && relDef?.belongsToPolymorphic && relData.data !== undefined) {
201
- 201 if (relData.data === null) {
202
- 202 const { typeField, idField } = relDef.belongsToPolymorphic
203
- 203 belongsToUpdates[typeField] = null
204
- 204 belongsToUpdates[idField] = null
205
- 205 } else if (relData.data) {
206
- 206 const { type, id } = relData.data
207
- 207 const { types, typeField, idField } = relDef.belongsToPolymorphic
208
- 208
209
- 209 // Validate type is allowed
210
- 210 if (!types.includes(type)) {
211
- 211 throw new RestApiValidationError(
212
- 212 `Invalid type '${type}' for polymorphic relationship '${relName}'. Allowed types: ${types.join(', ')}`,
213
- 213 {
214
- 214 fields: [`data.relationships.${relName}.data.type`],
215
- 215 violations: [{
216
- 216 field: `data.relationships.${relName}.data.type`,
217
- 217 rule: 'polymorphic_type',
218
- 218 message: `Type must be one of: ${types.join(', ')}`
219
- 219 }]
220
- 220 }
221
- 221 )
222
- 222 }
223
- 223
224
- 224 belongsToUpdates[typeField] = type
225
- 225 belongsToUpdates[idField] = id
226
- 226 }
227
- 227 }
228
- 228
229
- 229 // Check relationship type and process accordingly
230
- 230 if (relDef?.type === 'manyToMany' && relData.data !== undefined) {
231
- 231 manyToManyRelationships.push({
232
- 232 relName,
233
- 233 relDef: {
234
- 234 through: relDef.through,
235
- 235 foreignKey: relDef.foreignKey,
236
- 236 otherKey: relDef.otherKey
237
- 237 },
238
- 238 relData: relData.data || [] // null means empty array for many-to-many
239
- 239 })
240
- 240 }
241
- 241 // Note: hasOne and hasMany don't need processing here as they don't update the current record
242
- 242 }
243
- 243
244
- 244 return { belongsToUpdates, manyToManyRelationships }
245
- 245 }