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,553 +0,0 @@
1
- import { describe, it, before, after, beforeEach } from 'node:test'
2
- import assert from 'node:assert/strict'
3
- import knexLib from 'knex'
4
- import {
5
- validateJsonApiStructure,
6
- cleanTables,
7
- createJsonApiDocument,
8
- assertResourceAttributes,
9
- createRelationship,
10
- resourceIdentifier
11
- } from './helpers/test-utils.js'
12
- import { createMultiHomeApi } from './fixtures/api-configs.js'
13
-
14
- // Create Knex instance for tests - always use SQLite in-memory
15
- const knex = knexLib({
16
- client: 'better-sqlite3',
17
- connection: {
18
- filename: ':memory:'
19
- },
20
- useNullAsDefault: true
21
- })
22
-
23
- // API instance that persists across ALL tests
24
- let api
25
-
26
- describe('MultiHome Plugin Tests', () => {
27
- // IMPORTANT: before() runs ONCE for the entire test suite
28
- before(async () => {
29
- // Create API instance ONCE - this is reused for all tests
30
- api = await createMultiHomeApi(knex)
31
- })
32
-
33
- // IMPORTANT: after() cleans up resources
34
- after(async () => {
35
- // Always destroy knex connection to allow tests to exit
36
- await knex.destroy()
37
- })
38
-
39
- // IMPORTANT: beforeEach() cleans data but does NOT recreate API
40
- beforeEach(async () => {
41
- // Clean all tables - list ALL tables your tests use
42
- await cleanTables(knex, [
43
- 'multihome_projects',
44
- 'multihome_tasks',
45
- 'multihome_users',
46
- 'multihome_system_settings'
47
- ])
48
- })
49
-
50
- describe('Tenant Isolation', () => {
51
- it('should automatically set tenant_id on new records', async () => {
52
- // Create a project for tenant A
53
- const doc = createJsonApiDocument('projects', {
54
- name: 'Tenant A Project',
55
- description: 'A project for tenant A'
56
- })
57
-
58
- // Create with tenant context
59
- const createResult = await api.resources.projects.post({
60
- inputRecord: doc,
61
- simplified: false
62
- }, {
63
- auth: { multihome_id: 'tenant-a' }
64
- })
65
-
66
- // Verify response
67
- validateJsonApiStructure(createResult)
68
- assert.equal(createResult.data.type, 'projects')
69
- assertResourceAttributes(createResult.data, {
70
- name: 'Tenant A Project',
71
- description: 'A project for tenant A',
72
- tenant_id: 'tenant-a' // Should be automatically set
73
- })
74
- })
75
-
76
- it('should filter queries by tenant_id', async () => {
77
- // Create projects for different tenants
78
- const projectA1 = await api.resources.projects.post({
79
- inputRecord: createJsonApiDocument('projects', {
80
- name: 'Project A1'
81
- }),
82
- simplified: false
83
- }, {
84
- auth: { multihome_id: 'tenant-a' }
85
- })
86
-
87
- const projectA2 = await api.resources.projects.post({
88
- inputRecord: createJsonApiDocument('projects', {
89
- name: 'Project A2'
90
- }),
91
- simplified: false
92
- }, {
93
- auth: { multihome_id: 'tenant-a' }
94
- })
95
-
96
- const projectB1 = await api.resources.projects.post({
97
- inputRecord: createJsonApiDocument('projects', {
98
- name: 'Project B1'
99
- }),
100
- simplified: false
101
- }, {
102
- auth: { multihome_id: 'tenant-b' }
103
- })
104
-
105
- // Query as tenant A - should only see tenant A's projects
106
- const resultA = await api.resources.projects.query({
107
- simplified: false
108
- }, {
109
- auth: { multihome_id: 'tenant-a' }
110
- })
111
-
112
- validateJsonApiStructure(resultA, true) // true for collection
113
- assert.equal(resultA.data.length, 2, 'Tenant A should see 2 projects')
114
- assert(resultA.data.every(p => p.attributes.name.startsWith('Project A')))
115
-
116
- // Query as tenant B - should only see tenant B's projects
117
- const resultB = await api.resources.projects.query({
118
- simplified: false
119
- }, {
120
- auth: { multihome_id: 'tenant-b' }
121
- })
122
-
123
- validateJsonApiStructure(resultB, true) // true for collection
124
- assert.equal(resultB.data.length, 1, 'Tenant B should see 1 project')
125
- assert.equal(resultB.data[0].attributes.name, 'Project B1')
126
- })
127
-
128
- it('should prevent access to other tenant records', async () => {
129
- // Create a project for tenant A
130
- const projectA = await api.resources.projects.post({
131
- inputRecord: createJsonApiDocument('projects', {
132
- name: 'Tenant A Secret Project'
133
- }),
134
- simplified: false
135
- }, {
136
- auth: { multihome_id: 'tenant-a' }
137
- })
138
-
139
- // Try to access as tenant B - should fail
140
- await assert.rejects(
141
- async () => {
142
- await api.resources.projects.get({
143
- id: projectA.data.id,
144
- simplified: false
145
- }, {
146
- auth: { multihome_id: 'tenant-b' }
147
- })
148
- },
149
- (err) => {
150
- return err.code === 'REST_API_RESOURCE'
151
- },
152
- 'Should not be able to access other tenant data'
153
- )
154
- })
155
-
156
- it('should prevent updating tenant_id to different value', async () => {
157
- // Create a project for tenant A
158
- const project = await api.resources.projects.post({
159
- inputRecord: createJsonApiDocument('projects', {
160
- name: 'Original Project'
161
- }),
162
- simplified: false
163
- }, {
164
- auth: { multihome_id: 'tenant-a' }
165
- })
166
-
167
- // Try to update with different tenant_id - should fail
168
- await assert.rejects(
169
- async () => {
170
- await api.resources.projects.patch({
171
- id: project.data.id,
172
- inputRecord: {
173
- data: {
174
- type: 'projects',
175
- id: project.data.id,
176
- attributes: {
177
- name: 'Updated Project',
178
- tenant_id: 'tenant-b' // Trying to change tenant
179
- }
180
- }
181
- },
182
- simplified: false
183
- }, {
184
- auth: { multihome_id: 'tenant-a' }
185
- })
186
- },
187
- (err) => {
188
- console.log('PATCH error:', err.message)
189
- console.log('PATCH error stack:', err.stack)
190
- return err.message.includes('must match current context')
191
- },
192
- 'Should not be able to change tenant_id'
193
- )
194
- })
195
- })
196
-
197
- describe('Relationships Across Tenants', () => {
198
- it('should maintain tenant isolation for related records', async () => {
199
- // Create projects for different tenants
200
- const projectA = await api.resources.projects.post({
201
- inputRecord: createJsonApiDocument('projects', {
202
- name: 'Tenant A Project'
203
- }),
204
- simplified: false
205
- }, {
206
- auth: { multihome_id: 'tenant-a' }
207
- })
208
-
209
- const projectB = await api.resources.projects.post({
210
- inputRecord: createJsonApiDocument('projects', {
211
- name: 'Tenant B Project'
212
- }),
213
- simplified: false
214
- }, {
215
- auth: { multihome_id: 'tenant-b' }
216
- })
217
-
218
- // Create task for tenant A project
219
- const taskA = await api.resources.tasks.post({
220
- inputRecord: createJsonApiDocument('tasks',
221
- {
222
- title: 'Task for Tenant A'
223
- },
224
- {
225
- project: createRelationship(resourceIdentifier('projects', projectA.data.id))
226
- }
227
- ),
228
- simplified: false
229
- }, {
230
- auth: { multihome_id: 'tenant-a' }
231
- })
232
-
233
- // Try to create task for tenant B project as tenant A - should fail
234
- await assert.rejects(
235
- async () => {
236
- await api.resources.tasks.post({
237
- inputRecord: createJsonApiDocument('tasks',
238
- {
239
- title: 'Invalid Task'
240
- },
241
- {
242
- project: createRelationship(resourceIdentifier('projects', projectB.data.id))
243
- }
244
- ),
245
- simplified: false
246
- }, {
247
- auth: { multihome_id: 'tenant-a' }
248
- })
249
- },
250
- (err) => {
251
- // Should fail because project belongs to different tenant
252
- return err.code === 'REST_API_VALIDATION' || err.code === 'REST_API_RESOURCE'
253
- },
254
- 'Should not be able to link to other tenant resources'
255
- )
256
-
257
- // Query with includes - should only see own tenant data
258
- const projectWithTasks = await api.resources.projects.get({
259
- id: projectA.data.id,
260
- queryParams: {
261
- include: ['tasks']
262
- },
263
- simplified: false
264
- }, {
265
- auth: { multihome_id: 'tenant-a' }
266
- })
267
-
268
- validateJsonApiStructure(projectWithTasks)
269
- assert(projectWithTasks.included, 'Should have included tasks')
270
- assert.equal(projectWithTasks.included.length, 1, 'Should include one task')
271
- assert.equal(projectWithTasks.included[0].attributes.tenant_id, 'tenant-a')
272
- })
273
- })
274
-
275
- describe('Excluded Resources', () => {
276
- it('should allow access to excluded resources without tenant context', async () => {
277
- // Create system setting without tenant context
278
- const setting = await api.resources.system_settings.post({
279
- inputRecord: createJsonApiDocument('system_settings', {
280
- key: 'app.version',
281
- value: '1.0.0'
282
- }),
283
- simplified: false
284
- })
285
-
286
- validateJsonApiStructure(setting)
287
- assert.equal(setting.data.type, 'system_settings')
288
- assert(!setting.data.attributes.tenant_id, 'Should not have tenant_id')
289
-
290
- // Query without tenant context should work
291
- const allSettings = await api.resources.system_settings.query({
292
- simplified: false
293
- })
294
-
295
- assert.equal(allSettings.data.length, 1)
296
-
297
- // Should be accessible by any tenant
298
- const settingByTenantA = await api.resources.system_settings.get({
299
- id: setting.data.id,
300
- simplified: false
301
- }, {
302
- auth: { multihome_id: 'tenant-a' }
303
- })
304
-
305
- assert.equal(settingByTenantA.data.id, setting.data.id)
306
- })
307
- })
308
-
309
- describe('Configuration Options', () => {
310
- it('should require tenant context when requireAuth is true', async () => {
311
- // Try to create without tenant context - should fail
312
- await assert.rejects(
313
- async () => {
314
- await api.resources.projects.post({
315
- inputRecord: createJsonApiDocument('projects', {
316
- name: 'No Tenant Project'
317
- }),
318
- simplified: false
319
- })
320
- },
321
- (err) => {
322
- return err.message.includes('multihome context')
323
- },
324
- 'Should require tenant context when requireAuth is true'
325
- )
326
- })
327
-
328
- it('should validate resources have tenant field when allowMissing is false', async () => {
329
- // This test validates that the plugin threw an error during setup
330
- // if a resource was missing the tenant field. Since our test resources
331
- // all have tenant_id (except excluded ones), we can verify the
332
- // configuration is working by checking vars
333
- assert.equal(api.vars.multihome.field, 'tenant_id')
334
- assert.equal(api.vars.multihome.allowMissing, false)
335
- assert(api.vars.multihome.excludeResources.includes('system_settings'))
336
- })
337
- })
338
-
339
- describe('Complex Queries', () => {
340
- it('should apply tenant filter to filtered queries', async () => {
341
- // Create users for different tenants
342
- await api.resources.users.post({
343
- inputRecord: createJsonApiDocument('users', {
344
- email: 'admin@tenant-a.com',
345
- name: 'Admin A',
346
- role: 'admin'
347
- }),
348
- simplified: false
349
- }, {
350
- auth: { multihome_id: 'tenant-a' }
351
- })
352
-
353
- await api.resources.users.post({
354
- inputRecord: createJsonApiDocument('users', {
355
- email: 'user@tenant-a.com',
356
- name: 'User A',
357
- role: 'member'
358
- }),
359
- simplified: false
360
- }, {
361
- auth: { multihome_id: 'tenant-a' }
362
- })
363
-
364
- await api.resources.users.post({
365
- inputRecord: createJsonApiDocument('users', {
366
- email: 'admin@tenant-b.com',
367
- name: 'Admin B',
368
- role: 'admin'
369
- }),
370
- simplified: false
371
- }, {
372
- auth: { multihome_id: 'tenant-b' }
373
- })
374
-
375
- // Query for admins as tenant A - should only see tenant A admin
376
- const adminsA = await api.resources.users.query({
377
- queryParams: {
378
- filters: { role: 'admin' }
379
- },
380
- simplified: false
381
- }, {
382
- auth: { multihome_id: 'tenant-a' }
383
- })
384
-
385
- assert.equal(adminsA.data.length, 1, 'Should only see tenant A admin')
386
- assert.equal(adminsA.data[0].attributes.email, 'admin@tenant-a.com')
387
-
388
- // Query for all users as tenant B - should only see tenant B users
389
- const usersB = await api.resources.users.query({
390
- simplified: false
391
- }, {
392
- auth: { multihome_id: 'tenant-b' }
393
- })
394
-
395
- assert.equal(usersB.data.length, 1, 'Should only see tenant B users')
396
- assert.equal(usersB.data[0].attributes.email, 'admin@tenant-b.com')
397
- })
398
-
399
- it('should apply tenant filter with sorting and pagination', async () => {
400
- // Create multiple projects for tenant A
401
- for (let i = 1; i <= 5; i++) {
402
- await api.resources.projects.post({
403
- inputRecord: createJsonApiDocument('projects', {
404
- name: `Project A${i}`,
405
- status: i % 2 === 0 ? 'active' : 'inactive'
406
- }),
407
- simplified: false
408
- }, {
409
- auth: { multihome_id: 'tenant-a' }
410
- })
411
- }
412
-
413
- // Create projects for tenant B
414
- for (let i = 1; i <= 3; i++) {
415
- await api.resources.projects.post({
416
- inputRecord: createJsonApiDocument('projects', {
417
- name: `Project B${i}`
418
- }),
419
- simplified: false
420
- }, {
421
- auth: { multihome_id: 'tenant-b' }
422
- })
423
- }
424
-
425
- // Query with pagination as tenant A
426
- const page1 = await api.resources.projects.query({
427
- queryParams: {
428
- page: { size: 2, number: 1 },
429
- sort: ['-name'] // Sort by name descending
430
- },
431
- simplified: false
432
- }, {
433
- auth: { multihome_id: 'tenant-a' }
434
- })
435
-
436
- assert.equal(page1.data.length, 2, 'Should return page size')
437
- assert.equal(page1.data[0].attributes.name, 'Project A5', 'Should be sorted descending')
438
- assert.equal(page1.data[1].attributes.name, 'Project A4')
439
-
440
- // Get page 2
441
- const page2 = await api.resources.projects.query({
442
- queryParams: {
443
- page: { size: 2, number: 2 },
444
- sort: ['-name']
445
- },
446
- simplified: false
447
- }, {
448
- auth: { multihome_id: 'tenant-a' }
449
- })
450
-
451
- assert.equal(page2.data.length, 2, 'Should return page size')
452
- assert(page2.data.every(p => p.attributes.tenant_id === 'tenant-a'))
453
- })
454
- })
455
-
456
- describe('Error Handling', () => {
457
- it('should provide clear error when no tenant context available', async () => {
458
- await assert.rejects(
459
- async () => {
460
- await api.resources.projects.query({
461
- simplified: false
462
- })
463
- },
464
- (err) => {
465
- return err.message.includes('No multihome context available')
466
- },
467
- 'Should provide clear error message'
468
- )
469
- })
470
-
471
- it('should handle missing tenant field gracefully for excluded resources', async () => {
472
- // system_settings is excluded and doesn't have tenant_id field
473
- const result = await api.resources.system_settings.post({
474
- inputRecord: createJsonApiDocument('system_settings', {
475
- key: 'test.setting',
476
- value: 'test'
477
- }),
478
- simplified: false
479
- }, {
480
- auth: { multihome_id: 'tenant-a' } // Should be ignored for excluded resources
481
- })
482
-
483
- validateJsonApiStructure(result)
484
- assert(!result.data.attributes.tenant_id, 'Excluded resource should not have tenant_id')
485
- })
486
- })
487
-
488
- describe('Security', () => {
489
- it('should prevent bypassing tenant filter with malicious IDs', async () => {
490
- // Create a project for tenant A
491
- const project = await api.resources.projects.post({
492
- inputRecord: createJsonApiDocument('projects', {
493
- name: 'Secure Project'
494
- }),
495
- simplified: false
496
- }, {
497
- auth: { multihome_id: 'tenant-a' }
498
- })
499
-
500
- // Try various bypass attempts as tenant B
501
- const maliciousIds = [
502
- project.data.id,
503
- `${project.data.id} OR 1=1`,
504
- `${project.data.id}'; DROP TABLE projects; --`
505
- ]
506
-
507
- for (const id of maliciousIds) {
508
- await assert.rejects(
509
- async () => {
510
- await api.resources.projects.get({
511
- id,
512
- simplified: false
513
- }, {
514
- auth: { multihome_id: 'tenant-b' }
515
- })
516
- },
517
- undefined, // Any error is fine
518
- `Should not bypass security with ID: ${id}`
519
- )
520
- }
521
- })
522
-
523
- it('should log security violations', async () => {
524
- // This test verifies that the plugin logs security violations
525
- // In a real implementation, you might capture logs and verify
526
- // For now, we just ensure the security check happens
527
-
528
- // Create data for tenant A
529
- const project = await api.resources.projects.post({
530
- inputRecord: createJsonApiDocument('projects', {
531
- name: 'Tenant A Data'
532
- }),
533
- simplified: false
534
- }, {
535
- auth: { multihome_id: 'tenant-a' }
536
- })
537
-
538
- // Attempt access as tenant B
539
- try {
540
- await api.resources.projects.get({
541
- id: project.data.id,
542
- simplified: false
543
- }, {
544
- auth: { multihome_id: 'tenant-b' }
545
- })
546
- assert.fail('Should have thrown error')
547
- } catch (err) {
548
- // Error is expected - security violation should be logged
549
- assert(err.code === 'REST_API_RESOURCE' || err.message.includes('Access denied'))
550
- }
551
- })
552
- })
553
- })