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,314 +0,0 @@
1
- 1 export const MultiHomePlugin = {
2
- 2 name: 'multihome',
3
- 3 dependencies: ['rest-api'],
4
- 4
5
- 5 install ({ api, addHook, vars, helpers, log, scopes, pluginOptions }) {
6
- 6 if (!api.knex?.instance) {
7
- 7 throw new Error('Multihome plugin requires a storage plugin with knex support (rest-api-knex or rest-api-anyapi-knex)')
8
- 8 }
9
- 9
10
- 10 // Get configuration - hooked-api namespaces options by plugin name
11
- 11 const multihomeOptions = pluginOptions || {}
12
- 12
13
- 13 // Store configuration in vars (data only)
14
- 14 vars.multihome = {
15
- 15 field: multihomeOptions.field || 'multihome_id',
16
- 16 excludeResources: multihomeOptions.excludeResources || ['system_migrations', 'system_logs'],
17
- 17 requireAuth: multihomeOptions.requireAuth !== undefined ? multihomeOptions.requireAuth : true,
18
- 18 allowMissing: multihomeOptions.allowMissing || false
19
- 19 }
20
- 20
21
- 21 const resolveStorageAdapter = (scopeName, hookContext = {}) => {
22
- 22 if (!scopeName) return null
23
- 23 if (hookContext.storageAdapter) return hookContext.storageAdapter
24
- 24 if (hookContext.knexQuery?.storageAdapter) return hookContext.knexQuery.storageAdapter
25
- 25 if (helpers.getStorageAdapter) {
26
- 26 return helpers.getStorageAdapter(scopeName)
27
- 27 }
28
- 28 return null
29
- 29 }
30
- 30
31
- 31 const translateColumnReference = ({ field, tableName, scopeName, hookContext }) => {
32
- 32 const storageAdapter = resolveStorageAdapter(scopeName, hookContext)
33
- 33 const translated = storageAdapter?.translateColumn?.(field) ?? field
34
- 34 if (tableName) {
35
- 35 return `${tableName}.${translated}`
36
- 36 }
37
- 37 return translated
38
- 38 }
39
- 39
40
- 40 const translateFilterValue = ({ field, value, scopeName, hookContext }) => {
41
- 41 const storageAdapter = resolveStorageAdapter(scopeName, hookContext)
42
- 42 if (!storageAdapter?.translateFilterValue) return value
43
- 43 return storageAdapter.translateFilterValue(field, value)
44
- 44 }
45
- 45
46
- 46 // Default extractor function
47
- 47 function defaultSubdomainExtractor (request) {
48
- 48 // Extract from subdomain (e.g., 'mobily' from 'mobily.app.com')
49
- 49 const host = request.headers?.host || request.hostname || ''
50
- 50 const subdomain = host.split('.')[0]
51
- 51
52
- 52 // Don't use 'www' or 'api' as tenant IDs
53
- 53 if (subdomain && !['www', 'api', 'app'].includes(subdomain)) {
54
- 54 return subdomain
55
- 55 }
56
- 56
57
- 57 // Fallback to header
58
- 58 return request.headers?.['x-multihome-id'] || null
59
- 59 }
60
- 60
61
- 61 // Store extractor function in helpers
62
- 62 helpers.extractMultihomeId = multihomeOptions.extractor || defaultSubdomainExtractor
63
- 63
64
- 64 // Hook into transport layer to extract multihome_id
65
- 65 addHook('transport:request', 'extract-multihome-id', {}, async ({ context, request, helpers }) => {
66
- 66 // Extract multihome_id using configured extractor
67
- 67 const multihomeId = helpers.extractMultihomeId(request)
68
- 68
69
- 69 if (multihomeId) {
70
- 70 // Store in auth context (even if no user auth)
71
- 71 context.auth = context.auth || {}
72
- 72 context.auth.multihome_id = multihomeId
73
- 73
74
- 74 log.debug('Multihome ID extracted', {
75
- 75 multihome_id: multihomeId,
76
- 76 source: request.headers?.host
77
- 77 })
78
- 78 } else if (vars.multihome.requireAuth) {
79
- 79 log.warn('No multihome ID found in request', {
80
- 80 host: request.headers?.host,
81
- 81 headers: Object.keys(request.headers || {})
82
- 82 })
83
- 83 }
84
- 84 })
85
- 85
86
- 86 // Validate that resources have multihome field when added
87
- 87 addHook('scope:added', 'validate-multihome-field', {}, ({ context, vars }) => {
88
- 88 const { scopeName, scopeOptions } = context
89
- 89
90
- 90 // Skip excluded resources
91
- 91 if (vars.multihome.excludeResources.includes(scopeName)) {
92
- 92 log.debug(`Resource ${scopeName} excluded from multihome validation`)
93
- 93 return
94
- 94 }
95
- 95
96
- 96 // Check if schema has multihome field
97
- 97 const schema = scopeOptions.schema
98
- 98 if (schema && !schema[vars.multihome.field]) {
99
- 99 if (vars.multihome.allowMissing) {
100
- 100 log.warn(`Resource ${scopeName} missing ${vars.multihome.field} field - multihome filtering disabled for this
101
- 101 resource`)
102
- 102 } else {
103
- 103 throw new Error(
104
- 104 `Resource '${scopeName}' must have '${vars.multihome.field}' field in schema for multi-tenancy`
105
- 105 )
106
- 106 }
107
- 107 }
108
- 108 })
109
- 109
110
- 110 // Main filtering hook - adds WHERE clause to all queries
111
- 111 log.debug('Registering multihome filter hook')
112
- 112 addHook('knexQueryFiltering', 'multihome-filter', {}, async ({ context, vars }) => {
113
- 113 // Get query info from context
114
- 114 const { query, tableName, scopeName } = context.knexQuery
115
- 115
116
- 116 // Skip excluded resources first
117
- 117 if (vars.multihome.excludeResources.includes(scopeName)) {
118
- 118 log.trace(`Skipping multihome filter for excluded resource: ${scopeName}`)
119
- 119 return
120
- 120 }
121
- 121
122
- 122 // Skip if no multihome context
123
- 123 if (!context.auth?.multihome_id) {
124
- 124 if (vars.multihome.requireAuth) {
125
- 125 throw new Error('No multihome context available - cannot execute query')
126
- 126 }
127
- 127 return
128
- 128 }
129
- 129
130
- 130 log.debug('Applying multihome tenant filter', {
131
- 131 scopeName,
132
- 132 tableName,
133
- 133 tenant: context.auth.multihome_id,
134
- 134 })
135
- 135
136
- 136 // Check if this resource has multihome field
137
- 137 const scope = scopes[scopeName]
138
- 138 const hasMultihomeField = scope?.vars?.schemaInfo?.schemaStructure?.[vars.multihome.field]
139
- 139
140
- 140 if (!hasMultihomeField) {
141
- 141 if (vars.multihome.allowMissing) {
142
- 142 log.trace(`Resource ${scopeName} has no ${vars.multihome.field} field - skipping filter`)
143
- 143 return
144
- 144 } else {
145
- 145 throw new Error(`Resource ${scopeName} missing required ${vars.multihome.field} field`)
146
- 146 }
147
- 147 }
148
- 148
149
- 149 const storageAdapter = resolveStorageAdapter(scopeName, context)
150
- 150 if (storageAdapter && !context.storageAdapter) {
151
- 151 context.storageAdapter = storageAdapter
152
- 152 }
153
- 153
154
- 154 const columnRef = translateColumnReference({
155
- 155 field: vars.multihome.field,
156
- 156 tableName,
157
- 157 scopeName,
158
- 158 hookContext: context,
159
- 159 })
160
- 160
161
- 161 const tenantValue = translateFilterValue({
162
- 162 field: vars.multihome.field,
163
- 163 value: context.auth.multihome_id,
164
- 164 scopeName,
165
- 165 hookContext: context,
166
- 166 })
167
- 167
168
- 168 if (tenantValue === null) {
169
- 169 query.whereNull(columnRef)
170
- 170 } else {
171
- 171 query.where(columnRef, tenantValue)
172
- 172 }
173
- 173
174
- 174 log.trace('Added multihome filter', {
175
- 175 scopeName,
176
- 176 tableName,
177
- 177 multihome_id: context.auth.multihome_id
178
- 178 })
179
- 179 })
180
- 180
181
- 181 // Set multihome_id on new records
182
- 182 addHook('beforeSchemaValidate', 'set-multihome-id', {}, async ({ context, scopeName, vars }) => {
183
- 183 // Skip excluded resources first
184
- 184 if (vars.multihome.excludeResources.includes(scopeName)) {
185
- 185 return
186
- 186 }
187
- 187
188
- 188 // Skip if no multihome context
189
- 189 if (!context.auth?.multihome_id) {
190
- 190 if (vars.multihome.requireAuth) {
191
- 191 throw new Error('Cannot create record without multihome context')
192
- 192 }
193
- 193 return
194
- 194 }
195
- 195
196
- 196 // Check if this resource has multihome field
197
- 197 const scope = scopes[scopeName]
198
- 198 const hasMultihomeField = scope?.vars?.schemaInfo?.schemaStructure?.[vars.multihome.field]
199
- 199
200
- 200 if (!hasMultihomeField) {
201
- 201 if (!vars.multihome.allowMissing) {
202
- 202 throw new Error(`Resource ${scopeName} missing required ${vars.multihome.field} field`)
203
- 203 }
204
- 204 return
205
- 205 }
206
- 206
207
- 207 // For POST, always set multihome_id
208
- 208 if (context.method === 'post') {
209
- 209 context.inputRecord.data.attributes = context.inputRecord.data.attributes || {}
210
- 210 context.inputRecord.data.attributes[vars.multihome.field] = context.auth.multihome_id
211
- 211
212
- 212 log.debug('Set multihome_id on new record', {
213
- 213 scopeName,
214
- 214 multihome_id: context.auth.multihome_id
215
- 215 })
216
- 216 }
217
- 217
218
- 218 // For PUT/PATCH, validate multihome_id if provided
219
- 219 if ((context.method === 'put' || context.method === 'patch') &&
220
- 220 context.inputRecord.data.attributes?.[vars.multihome.field]) {
221
- 221 const providedId = context.inputRecord.data.attributes[vars.multihome.field]
222
- 222 if (providedId !== context.auth.multihome_id) {
223
- 223 throw new Error(
224
- 224 `Cannot set ${vars.multihome.field} to '${providedId}' - must match current context '${context.auth.multihome_id}'`
225
- 225 )
226
- 226 }
227
- 227 }
228
- 228 })
229
- 229
230
- 230 // Add checkPermissions hook to enforce tenant isolation
231
- 231 // This is the single source of truth for single-record access control
232
- 232 addHook('checkPermissions', 'multihome-check-permissions', {}, async ({ context, scopeName }) => {
233
- 233 // Extract the needed values from originalContext
234
- 234 const auth = context.originalContext?.auth
235
- 235 const minimalRecord = context.originalContext?.minimalRecord
236
- 236 const id = context.originalContext?.id
237
- 237
238
- 238 // Skip excluded resources
239
- 239 if (vars.multihome.excludeResources.includes(scopeName)) {
240
- 240 return
241
- 241 }
242
- 242
243
- 243 // Skip if no multihome context
244
- 244 if (!auth?.multihome_id) {
245
- 245 if (vars.multihome.requireAuth) {
246
- 246 throw new Error('No multihome context available')
247
- 247 }
248
- 248 return
249
- 249 }
250
- 250
251
- 251 // Get the scope to check if it has multihome field
252
- 252 const scope = scopes[scopeName]
253
- 253 const hasMultihomeField = scope?.vars?.schemaInfo?.schemaStructure?.[vars.multihome.field]
254
- 254
255
- 255 if (!hasMultihomeField) {
256
- 256 return // Resource doesn't support multihome
257
- 257 }
258
- 258
259
- 259 // For operations on existing records, verify tenant ownership
260
- 260 if (minimalRecord) {
261
- 261 // minimalRecord is in JSON:API format, so tenant_id is in attributes
262
- 262 const recordTenant = minimalRecord.attributes?.[vars.multihome.field]
263
- 263 const userTenant = auth.multihome_id
264
- 264
265
- 265 if (recordTenant !== userTenant) {
266
- 266 log.error('Multihome permission violation', {
267
- 267 scopeName,
268
- 268 recordId: id,
269
- 269 recordTenant,
270
- 270 userTenant,
271
- 271 method: context.method
272
- 272 })
273
- 273
274
- 274 // Return 404 for GET to prevent information leakage
275
- 275 // Return 403 for other operations
276
- 276 if (context.method === 'get') {
277
- 277 const error = new Error('Resource not found')
278
- 278 error.code = 'REST_API_RESOURCE'
279
- 279 throw error
280
- 280 } else {
281
- 281 const error = new Error('Access denied - insufficient permissions')
282
- 282 error.code = 'REST_API_FORBIDDEN'
283
- 283 throw error
284
- 284 }
285
- 285 }
286
- 286 }
287
- 287 })
288
- 288
289
- 289 // Note: PUT, PATCH, and DELETE operations will fail naturally if the record
290
- 290 // doesn't exist due to tenant filtering in the GET operation that precedes them
291
- 291
292
- 292 // Add API method to get current multihome context
293
- 293 api.multihome = {
294
- 294 getCurrentTenant: () => {
295
- 295 // This would need to be called within a request context
296
- 296 // Real implementation would need access to current request context
297
- 297 return null
298
- 298 },
299
- 299
300
- 300 // Runtime configuration helper (for debugging/testing)
301
- 301 getConfig: () => ({
302
- 302 ...vars.multihome,
303
- 303 hasCustomExtractor: helpers.extractMultihomeId !== defaultSubdomainExtractor
304
- 304 })
305
- 305 }
306
- 306
307
- 307 log.info('MultiHome plugin installed', {
308
- 308 field: vars.multihome.field,
309
- 309 excludedResources: vars.multihome.excludeResources,
310
- 310 requireAuth: vars.multihome.requireAuth,
311
- 311 hasCustomExtractor: helpers.extractMultihomeId !== defaultSubdomainExtractor
312
- 312 })
313
- 313 }
314
- 314 }
@@ -1,7 +0,0 @@
1
- 1 import { compileSchemas } from '../lib/querying-writing/compile-schemas.js'
2
- 2
3
- 3 export default async function compileResourceSchemas ({ context, scopes, runHooks }) {
4
- 4 const scope = scopes[context.scopeName]
5
- 5 // Pass scopeOptions and vars from context since scope object structure is different
6
- 6 return compileSchemas({ ...scope, scopeOptions: context.scopeOptions, vars: context.vars }, { context: { scopeName: context.scopeName }, runHooks })
7
- 7 }
@@ -1,71 +0,0 @@
1
- 1 import { parseJsonApiQuery } from '../lib/querying-writing/connectors-query-parser.js'
2
- 2
3
- 3 export default async function registerRelationshipRoutes ({ context, api, log }) {
4
- 4 const { scopeName } = context
5
- 5 const basePath = api.scopes[scopeName].vars.transport?.mountPath || ''
6
- 6 const scopePath = `${basePath}/${scopeName}`
7
- 7
8
- 8 // Helper to create route handlers
9
- 9 const createRouteHandler = (methodName) => {
10
- 10 return async ({ params, body, queryString }) => {
11
- 11 const scope = api.scopes[scopeName]
12
- 12
13
- 13 const methodParams = {
14
- 14 id: params.id,
15
- 15 relationshipName: params.relationshipName,
16
- 16 isTransport: true
17
- 17 }
18
- 18
19
- 19 // Add query params for getRelated
20
- 20 if (methodName === 'getRelated' && queryString) {
21
- 21 methodParams.queryParams = parseJsonApiQuery(queryString)
22
- 22 }
23
- 23
24
- 24 // Add body data for write operations
25
- 25 if (body && body.data !== undefined) {
26
- 26 methodParams.relationshipData = body.data
27
- 27 }
28
- 28
29
- 29 return await scope[methodName](methodParams)
30
- 30 }
31
- 31 }
32
- 32
33
- 33 // Register relationship routes
34
- 34
35
- 35 // GET /api/{scope}/{id}/relationships/{relationshipName}
36
- 36 await api.addRoute({
37
- 37 method: 'GET',
38
- 38 path: `${scopePath}/:id/relationships/:relationshipName`,
39
- 39 handler: createRouteHandler('getRelationship')
40
- 40 })
41
- 41
42
- 42 // GET /api/{scope}/{id}/{relationshipName}
43
- 43 await api.addRoute({
44
- 44 method: 'GET',
45
- 45 path: `${scopePath}/:id/:relationshipName`,
46
- 46 handler: createRouteHandler('getRelated')
47
- 47 })
48
- 48
49
- 49 // POST /api/{scope}/{id}/relationships/{relationshipName}
50
- 50 await api.addRoute({
51
- 51 method: 'POST',
52
- 52 path: `${scopePath}/:id/relationships/:relationshipName`,
53
- 53 handler: createRouteHandler('postRelationship')
54
- 54 })
55
- 55
56
- 56 // PATCH /api/{scope}/{id}/relationships/{relationshipName}
57
- 57 await api.addRoute({
58
- 58 method: 'PATCH',
59
- 59 path: `${scopePath}/:id/relationships/:relationshipName`,
60
- 60 handler: createRouteHandler('patchRelationship')
61
- 61 })
62
- 62
63
- 63 // DELETE /api/{scope}/{id}/relationships/{relationshipName}
64
- 64 await api.addRoute({
65
- 65 method: 'DELETE',
66
- 66 path: `${scopePath}/:id/relationships/:relationshipName`,
67
- 67 handler: createRouteHandler('deleteRelationship')
68
- 68 })
69
- 69
70
- 70 log.trace(`Registered relationship routes for scope: ${scopeName}`)
71
- 71 }
@@ -1,102 +0,0 @@
1
- 1 import { RestApiResourceError } from '../../../lib/rest-api-errors.js'
2
- 2 import { parseJsonApiQuery } from '../lib/querying-writing/connectors-query-parser.js'
3
- 3
4
- 4 export default async function registerScopeRoutes ({ context, api, vars, log }) {
5
- 5 const { scopeName } = context
6
- 6 const basePath = vars.transport?.mountPath || ''
7
- 7
8
- 8 // Helper to create route handlers
9
- 9 const createRouteHandler = (scopeName, methodName) => {
10
- 10 return async ({ queryString, headers, params, body, context }) => {
11
- 11 const scope = api.scopes[scopeName]
12
- 12 if (!scope) {
13
- 13 throw new RestApiResourceError(
14
- 14 `Scope '${scopeName}' not found`,
15
- 15 {
16
- 16 subtype: 'not_found',
17
- 17 resourceType: 'scope',
18
- 18 resourceId: scopeName
19
- 19 }
20
- 20 )
21
- 21 }
22
- 22
23
- 23 // Build parameters for the scope method
24
- 24 const methodParams = {}
25
- 25
26
- 26 // Add ID for single-resource operations
27
- 27 if (['get', 'put', 'patch', 'delete'].includes(methodName)) {
28
- 28 methodParams.id = params.id
29
- 29 }
30
- 30
31
- 31 // Parse query parameters for read operations
32
- 32 if (['query', 'get'].includes(methodName)) {
33
- 33 methodParams.queryParams = parseJsonApiQuery(queryString)
34
- 34 methodParams.isTransport = true
35
- 35 }
36
- 36
37
- 37 // Add body for write operations
38
- 38 if (['post', 'put', 'patch'].includes(methodName)) {
39
- 39 methodParams.inputRecord = body
40
- 40 methodParams.isTransport = true
41
- 41
42
- 42 // Add query params for includes/fields on write operations
43
- 43 if (queryString) {
44
- 44 methodParams.queryParams = parseJsonApiQuery(queryString)
45
- 45 }
46
- 46 }
47
- 47
48
- 48 // Call the scope method
49
- 49 const result = await scope[methodName](methodParams, context)
50
- 50
51
- 51 // Return the result (transport plugin handles response formatting)
52
- 52 return result
53
- 53 }
54
- 54 }
55
- 55
56
- 56 const scopePath = `${basePath}/${scopeName}`
57
- 57
58
- 58 // Register routes for each HTTP method
59
- 59 // GET /api/{scope} - Query collection
60
- 60 await api.addRoute({
61
- 61 method: 'GET',
62
- 62 path: scopePath,
63
- 63 handler: createRouteHandler(scopeName, 'query')
64
- 64 })
65
- 65
66
- 66 // GET /api/{scope}/{id} - Get single resource
67
- 67 await api.addRoute({
68
- 68 method: 'GET',
69
- 69 path: `${scopePath}/:id`,
70
- 70 handler: createRouteHandler(scopeName, 'get')
71
- 71 })
72
- 72
73
- 73 // POST /api/{scope} - Create resource
74
- 74 await api.addRoute({
75
- 75 method: 'POST',
76
- 76 path: scopePath,
77
- 77 handler: createRouteHandler(scopeName, 'post')
78
- 78 })
79
- 79
80
- 80 // PUT /api/{scope}/{id} - Replace resource
81
- 81 await api.addRoute({
82
- 82 method: 'PUT',
83
- 83 path: `${scopePath}/:id`,
84
- 84 handler: createRouteHandler(scopeName, 'put')
85
- 85 })
86
- 86
87
- 87 // PATCH /api/{scope}/{id} - Update resource
88
- 88 await api.addRoute({
89
- 89 method: 'PATCH',
90
- 90 path: `${scopePath}/:id`,
91
- 91 handler: createRouteHandler(scopeName, 'patch')
92
- 92 })
93
- 93
94
- 94 // DELETE /api/{scope}/{id} - Delete resource
95
- 95 await api.addRoute({
96
- 96 method: 'DELETE',
97
- 97 path: `${scopePath}/:id`,
98
- 98 handler: createRouteHandler(scopeName, 'delete')
99
- 99 })
100
- 100
101
- 101 log.info(`Routes registered for scope '${scopeName}'`)
102
- 102 }
@@ -1,49 +0,0 @@
1
- 1 export default async function turnScopeInitIntoVars ({ context, scopes, vars: apiVars }) {
2
- 2 // Refer to the scope's vars
3
- 3 const scope = scopes[context.scopeName]
4
- 4 const scopeOptions = scope?.scopeOptions || context.scopeOptions || {}
5
- 5 const vars = scope?.vars || apiVars
6
- 6
7
- 7 // The scope-specific ones
8
- 8 vars.sortableFields = scopeOptions.sortableFields || []
9
- 9 vars.defaultSort = scopeOptions.defaultSort || null
10
- 10
11
- 11 // The general ones that are also set at api level, but overrideable
12
- 12 if (typeof scopeOptions.queryDefaultLimit !== 'undefined') vars.queryDefaultLimit = scopeOptions.queryDefaultLimit
13
- 13 if (typeof scopeOptions.queryMaxLimit !== 'undefined') vars.queryMaxLimit = scopeOptions.queryMaxLimit
14
- 14 if (typeof scopeOptions.includeDepthLimit !== 'undefined') vars.includeDepthLimit = scopeOptions.includeDepthLimit
15
- 15 if (typeof scopeOptions.enablePaginationCounts !== 'undefined') vars.enablePaginationCounts = scopeOptions.enablePaginationCounts
16
- 16
17
- 17 // Set simplified settings as scope vars
18
- 18 if (typeof scopeOptions.simplifiedApi !== 'undefined') vars.simplifiedApi = scopeOptions.simplifiedApi
19
- 19 if (typeof scopeOptions.simplifiedTransport !== 'undefined') vars.simplifiedTransport = scopeOptions.simplifiedTransport
20
- 20
21
- 21 // Set returnRecord settings as scope vars
22
- 22 if (typeof scopeOptions.returnRecordApi !== 'undefined') vars.returnRecordApi = scopeOptions.returnRecordApi
23
- 23 if (typeof scopeOptions.returnRecordTransport !== 'undefined') vars.returnRecordTransport = scopeOptions.returnRecordTransport
24
- 24
25
- 25 // Set idProperty as scope var
26
- 26 if (typeof scopeOptions.idProperty !== 'undefined') vars.idProperty = scopeOptions.idProperty
27
- 27
28
- 28 // Add validation for query limits
29
- 29 if (vars.queryDefaultLimit && vars.queryMaxLimit) {
30
- 30 if (vars.queryDefaultLimit > vars.queryMaxLimit) {
31
- 31 throw new Error(
32
- 32 `Invalid scope '${context.scopeName}' configuration: ` +
33
- 33 `queryDefaultLimit (${vars.queryDefaultLimit}) cannot exceed queryMaxLimit (${vars.queryMaxLimit})`
34
- 34 )
35
- 35 }
36
- 36 }
37
- 37
38
- 38 // Validate relationship include limits at scope creation time
39
- 39 Object.entries(scopeOptions.relationships || {}).forEach(([relName, relDef]) => {
40
- 40 if (relDef.include?.limit && vars.queryMaxLimit) {
41
- 41 if (relDef.include.limit > vars.queryMaxLimit) {
42
- 42 throw new Error(
43
- 43 `Invalid relationship '${context.scopeName}.${relName}' configuration: ` +
44
- 44 `include.limit (${relDef.include.limit}) cannot exceed queryMaxLimit (${vars.queryMaxLimit})`
45
- 45 )
46
- 46 }
47
- 47 }
48
- 48 })
49
- 49 }
@@ -1,39 +0,0 @@
1
- 1 export default async function validateIncludeConfigurations ({ context, scopes, log }) {
2
- 2 const { scopeName } = context
3
- 3 const scope = scopes[scopeName]
4
- 4 const relationships = scope.vars.schemaInfo?.schemaRelationships
5
- 5
6
- 6 if (!relationships) return
7
- 7
8
- 8 // Check each relationship for include configuration
9
- 9 for (const [relName, relDef] of Object.entries(relationships)) {
10
- 10 if (relDef.include?.strategy === 'window') {
11
- 11 // This relationship requires window functions
12
- 12 // We'll validate this at query time since the database might not be connected yet
13
- 13 log.debug(`Relationship ${scopeName}.${relName} configured for window function includes`)
14
- 14 }
15
- 15
16
- 16 // Validate include configuration
17
- 17 if (relDef.include?.limit) {
18
- 18 if (typeof relDef.include.limit !== 'number') {
19
- 19 throw new Error(
20
- 20 `Invalid include limit for ${scopeName}.${relName}: limit must be a number`
21
- 21 )
22
- 22 }
23
- 23 // Check against queryMaxLimit if available
24
- 24 const maxLimit = scope.vars?.queryMaxLimit
25
- 25 if (maxLimit && relDef.include.limit > maxLimit) {
26
- 26 throw new Error(
27
- 27 `Invalid include limit for ${scopeName}.${relName}: ` +
28
- 28 `limit (${relDef.include.limit}) exceeds queryMaxLimit (${maxLimit})`
29
- 29 )
30
- 30 }
31
- 31 }
32
- 32
33
- 33 if (relDef.include?.orderBy && !Array.isArray(relDef.include.orderBy)) {
34
- 34 throw new Error(
35
- 35 `Invalid include orderBy for ${scopeName}.${relName}: orderBy must be an array`
36
- 36 )
37
- 37 }
38
- 38 }
39
- 39 }
@@ -1,23 +0,0 @@
1
- 1 import { ValidationError } from 'hooked-api'
2
- 2
3
- 3 /**
4
- 4 * addRoute
5
- 5 * Registers a new route with the transport layer
6
- 6 */
7
- 7 export default async ({ params, context, runHooks }) => {
8
- 8 const { method, path, handler } = params
9
- 9
10
- 10 // Validate route configuration
11
- 11 if (!method || !path || !handler) {
12
- 12 throw new ValidationError('Route requires method, path, and handler')
13
- 13 }
14
- 14
15
- 15 // debugger
16
- 16 // Create context for enrichAttributes hooks
17
- 17 Object.assign(context, params)
18
- 18
19
- 19 // Run the addRoute hook to notify transport plugins
20
- 20 await runHooks('addRoute')
21
- 21
22
- 22 return { registered: true, method, path }
23
- 23 }
@@ -1,13 +0,0 @@
1
- 1 /**
2
- 2 * checkPermissions
3
- 3 * Check if ther are permissions to access a resource.
4
- 4 *
5
- 5 */
6
- 6 export default async function checkPermissionsMethod ({ context, params, runHooks, scopeName, scopes, helpers }) {
7
- 7 Object.assign(context, {
8
- 8 method: params.method,
9
- 9 originalContext: params.originalContext,
10
- 10 })
11
- 11
12
- 12 await runHooks('checkPermissions')
13
- 13 }