json-rest-api 1.0.22 → 1.0.23

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 (260) hide show
  1. package/CLAUDE.md +1 -2
  2. package/JSKIT_BACKEND_MIGRATION_TRACKER.txt +180 -0
  3. package/SINGLE_VALIDATION_CONTRACT_TODO.txt +172 -0
  4. package/docs/API.md +51 -14
  5. package/docs/COMPARISON.md +2 -2
  6. package/docs/GUIDE/GUIDE_1_Initial_Setup.md +28 -1
  7. package/docs/GUIDE/GUIDE_2_2_Manipulating_And_Searching_Tables.md +5 -1
  8. package/docs/GUIDE/GUIDE_2_7_Pagination_And_Ordering.md +3 -1
  9. package/docs/GUIDE/GUIDE_2_8_Effects_of_PUT_and_PATCH.md +12 -60
  10. package/docs/GUIDE/GUIDE_2_Data_And_Relations.md +2 -2
  11. package/docs/GUIDE/GUIDE_3_Field_Transformations.md +5 -1
  12. package/docs/GUIDE/GUIDE_7_Hooks_Data_Management_And_Plugins.md +2 -0
  13. package/docs/GUIDE/GUIDE_X_Autofiltering.md +355 -0
  14. package/docs/GUIDE/GUIDE_X_Fastify.md +78 -0
  15. package/docs/GUIDE/GUIDE_X_Knex_Schema_And_Migrations.md +250 -0
  16. package/docs/GUIDE/GUIDE_X_Query_Projections.md +159 -0
  17. package/docs/GUIDE/GUIDE_X_Writing_Plugins.md +199 -0
  18. package/docs/GUIDE/index.md +18 -9
  19. package/docs/QUICKSTART.md +5 -1
  20. package/docs/index.md +1 -1
  21. package/index.js +3 -2
  22. package/package.json +2 -2
  23. package/plugins/core/connectors/express-plugin.js +97 -184
  24. package/plugins/core/connectors/fastify-plugin.js +206 -0
  25. package/plugins/core/connectors/lib/connector-core.js +195 -0
  26. package/plugins/core/connectors/lib/transport-http-helpers.js +122 -0
  27. package/plugins/core/connectors/lib/transport-route-schemas.js +92 -0
  28. package/plugins/core/lib/dbIntrospection.js +1078 -0
  29. package/plugins/core/lib/dbTablesOperations.js +1104 -160
  30. package/plugins/core/lib/querying/knex-json-api-transformers-querying.js +5 -2
  31. package/plugins/core/lib/querying/knex-process-includes.js +3 -4
  32. package/plugins/core/lib/querying/knex-query-helpers.js +17 -71
  33. package/plugins/core/lib/querying/knex-relationship-includes.js +466 -206
  34. package/plugins/core/lib/querying/query-builder-utils.js +19 -0
  35. package/plugins/core/lib/querying/query-field-sort-helpers.js +135 -0
  36. package/plugins/core/lib/querying/sort-helpers.js +33 -0
  37. package/plugins/core/lib/querying/storage-adapter-utils.js +51 -0
  38. package/plugins/core/lib/querying/url-helpers.js +8 -2
  39. package/plugins/core/lib/querying-writing/compile-schemas.js +12 -1
  40. package/plugins/core/lib/querying-writing/knex-field-helpers.js +83 -13
  41. package/plugins/core/lib/querying-writing/knex-json-api-transformers.js +13 -12
  42. package/plugins/core/lib/querying-writing/query-field-helpers.js +88 -0
  43. package/plugins/core/lib/querying-writing/request-contracts.js +590 -0
  44. package/plugins/core/lib/storage/canonical-storage-mapping.js +113 -0
  45. package/plugins/core/lib/storage/storage-adapter.js +79 -2
  46. package/plugins/core/lib/storage/storage-mapping.js +204 -0
  47. package/plugins/core/rest-api-anyapi-knex-plugin.js +355 -228
  48. package/plugins/core/rest-api-autofilter-plugin.js +414 -0
  49. package/plugins/core/rest-api-knex-plugin.js +249 -211
  50. package/plugins/core/rest-api-plugin-hooks/register-relationship-routes.js +10 -5
  51. package/plugins/core/rest-api-plugin-hooks/register-scope-routes.js +12 -6
  52. package/plugins/core/rest-api-plugin-methods/apply-query-filters.js +41 -0
  53. package/plugins/core/rest-api-plugin-methods/common.js +121 -83
  54. package/plugins/core/rest-api-plugin-methods/enrich-attributes.js +6 -1
  55. package/plugins/core/rest-api-plugin-methods/get.js +23 -14
  56. package/plugins/core/rest-api-plugin-methods/patch.js +28 -12
  57. package/plugins/core/rest-api-plugin-methods/post.js +13 -8
  58. package/plugins/core/rest-api-plugin-methods/put.js +45 -21
  59. package/plugins/core/rest-api-plugin-methods/query.js +15 -53
  60. package/plugins/core/rest-api-plugin.js +2 -0
  61. package/plugins/core/rest-api-positioning-plugin.js +1 -1
  62. package/plugins/core/rest-api-query-projections-plugin.js +130 -0
  63. package/plugins/core/socketio-plugin.js +1 -3
  64. package/tests/autofilter.test.js +463 -0
  65. package/tests/custom-id-simplified-post.test.js +75 -0
  66. package/tests/custom-idproperty-relationships.test.js +33 -0
  67. package/tests/db-introspection.test.js +518 -0
  68. package/tests/db-table-operations.test.js +781 -0
  69. package/tests/fastify-plugin.test.js +141 -0
  70. package/tests/field-getters.test.js +1 -2
  71. package/tests/field-setters.test.js +0 -4
  72. package/tests/fixtures/api-configs.js +188 -219
  73. package/tests/helpers/fake-fastify.js +99 -0
  74. package/tests/http-connectors-parity.test.js +235 -0
  75. package/tests/projected-fields.test.js +319 -0
  76. package/tests/relationships.test.js +3 -3
  77. package/tests/searchschema-knex-behavior.test.js +187 -0
  78. package/tests/searchschema-merge.test.js +21 -69
  79. package/tests/storage-mapping.test.js +210 -0
  80. package/audit/00-charter.md +0 -103
  81. package/audit/01-scope-map.md +0 -84
  82. package/audit/02-coverage-matrix.csv +0 -174
  83. package/audit/03-invariants.md +0 -123
  84. package/audit/04-standards-rubric.md +0 -56
  85. package/audit/05-competitor-rubric.md +0 -77
  86. package/audit/06-findings.md +0 -160
  87. package/audit/07-risk-register.csv +0 -10
  88. package/audit/08-remediation-roadmap.md +0 -177
  89. package/audit/09-signoff-check.md +0 -23
  90. package/audit/10-final-synthesis.md +0 -29
  91. package/audit/evidence/code/all-files-json-rest-api.txt +0 -168
  92. package/audit/evidence/code/all-files-json-rest-schema.txt +0 -12
  93. package/audit/evidence/code/boundary-leak-checks.txt +0 -72
  94. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__bulk-operations-plugin.js.txt +0 -480
  95. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__connectors__express-plugin.js.txt +0 -532
  96. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__connectors__lib__busboy-detector.js.txt +0 -175
  97. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__connectors__lib__formidable-detector.js.txt +0 -138
  98. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__connectors__lib__request-helpers.js.txt +0 -222
  99. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__file-handling-plugin.js.txt +0 -342
  100. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__dbTablesOperations.js.txt +0 -304
  101. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__fractional-positioning.js.txt +0 -188
  102. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying-writing__compile-schemas.js.txt +0 -336
  103. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying-writing__connectors-query-parser.js.txt +0 -165
  104. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying-writing__database-capabilities.js.txt +0 -265
  105. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying-writing__database-value-normalizers.js.txt +0 -369
  106. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying-writing__default-data-helpers.js.txt +0 -72
  107. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying-writing__field-utils.js.txt +0 -139
  108. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying-writing__knex-constants.js.txt +0 -45
  109. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying-writing__knex-field-helpers.js.txt +0 -396
  110. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying-writing__knex-json-api-transformers.js.txt +0 -162
  111. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying-writing__payload-validators.js.txt +0 -1314
  112. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying-writing__schema-helpers.js.txt +0 -488
  113. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying-writing__scope-validations.js.txt +0 -484
  114. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying-writing__simplified-helpers.js.txt +0 -857
  115. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying__knex-cross-table-search.js.txt +0 -565
  116. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying__knex-json-api-transformers-querying.js.txt +0 -485
  117. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying__knex-pagination-helpers.js.txt +0 -560
  118. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying__knex-process-includes.js.txt +0 -167
  119. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying__knex-query-helpers-base.js.txt +0 -109
  120. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying__knex-query-helpers.js.txt +0 -1064
  121. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying__knex-relationship-includes.js.txt +0 -1983
  122. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying__knex-window-queries.js.txt +0 -328
  123. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__querying__url-helpers.js.txt +0 -119
  124. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__storage__storage-adapter.js.txt +0 -160
  125. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__writing__many-to-many-manipulations.js.txt +0 -300
  126. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__lib__writing__relationship-processor.js.txt +0 -245
  127. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-access.js.txt +0 -459
  128. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-cors-plugin.js.txt +0 -237
  129. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-knex-plugin.js.txt +0 -1135
  130. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-multihome-plugin.js.txt +0 -314
  131. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-hooks__compile-resource-schemas.js.txt +0 -7
  132. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-hooks__register-relationship-routes.js.txt +0 -71
  133. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-hooks__register-scope-routes.js.txt +0 -102
  134. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-hooks__turn-scope-init-into-vars.js.txt +0 -49
  135. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-hooks__validate-include-configurations.js.txt +0 -39
  136. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-methods__add-route.js.txt +0 -23
  137. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-methods__check-permissions.js.txt +0 -13
  138. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-methods__common.js.txt +0 -639
  139. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-methods__delete-relationship.js.txt +0 -117
  140. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-methods__delete.js.txt +0 -104
  141. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-methods__enrich-attributes.js.txt +0 -199
  142. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-methods__get-related.js.txt +0 -353
  143. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-methods__get-relationship.js.txt +0 -61
  144. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-methods__get.js.txt +0 -184
  145. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-methods__patch-relationship.js.txt +0 -55
  146. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-methods__patch.js.txt +0 -191
  147. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-methods__post-relationship.js.txt +0 -110
  148. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-methods__post.js.txt +0 -168
  149. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-methods__put.js.txt +0 -253
  150. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-methods__query.js.txt +0 -207
  151. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin-methods__release.js.txt +0 -12
  152. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-plugin.js.txt +0 -165
  153. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__rest-api-positioning-plugin.js.txt +0 -668
  154. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__core__socketio-plugin.js.txt +0 -621
  155. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__storage__local-storage.js.txt +0 -218
  156. package/audit/evidence/code/critical-pass1/json-rest-api__plugins__storage__s3-storage.js.txt +0 -124
  157. package/audit/evidence/code/critical-pass1/json-rest-schema__src__CorePlugin.js.txt +0 -269
  158. package/audit/evidence/code/critical-pass1/json-rest-schema__src__Schema.js.txt +0 -242
  159. package/audit/evidence/code/critical-pass1/json-rest-schema__src__index.js.txt +0 -77
  160. package/audit/evidence/code/critical-pass1-risk-keywords.txt +0 -238
  161. package/audit/evidence/code/critical-pass1-summary.csv +0 -67
  162. package/audit/evidence/code/critical-pass2-summary.csv +0 -67
  163. package/audit/evidence/code/early-silent-zones-no-test-token.txt +0 -57
  164. package/audit/evidence/code/error-model-signals.txt +0 -98
  165. package/audit/evidence/code/git-files-json-rest-api.txt +0 -171
  166. package/audit/evidence/code/git-files-json-rest-schema.txt +0 -27
  167. package/audit/evidence/code/hook-flow-signals.txt +0 -282
  168. package/audit/evidence/code/idproperty-signals.txt +0 -157
  169. package/audit/evidence/code/import-cycles-non-anyapi.txt +0 -3
  170. package/audit/evidence/code/import-hotspots-non-anyapi.txt +0 -96
  171. package/audit/evidence/code/imports-json-rest-api.txt +0 -354
  172. package/audit/evidence/code/imports-json-rest-schema.txt +0 -15
  173. package/audit/evidence/code/in-scope-files-json-rest-api.txt +0 -160
  174. package/audit/evidence/code/in-scope-files-json-rest-schema.txt +0 -13
  175. package/audit/evidence/code/non-anyapi-core-files.txt +0 -101
  176. package/audit/evidence/code/permission-signals.txt +0 -193
  177. package/audit/evidence/code/query-include-signals.txt +0 -687
  178. package/audit/evidence/code/transaction-signals.txt +0 -28
  179. package/audit/evidence/code/validation-signals.txt +0 -650
  180. package/audit/evidence/code/write-path-signals.txt +0 -61
  181. package/audit/evidence/docs/docs-claims-signals.txt +0 -1075
  182. package/audit/evidence/docs/schema-docs-claims-signals.txt +0 -179
  183. package/audit/evidence/external/corpus-freeze.md +0 -21
  184. package/audit/evidence/external/npm-competitor-metadata.txt +0 -70
  185. package/audit/evidence/external/npm-download-signals.txt +0 -23
  186. package/audit/evidence/external/npm-jsonapi-server.txt +0 -8
  187. package/audit/evidence/external/npm-modern-jsonapi-metadata.txt +0 -38
  188. package/audit/evidence/external/readme-fortune-signals.txt +0 -26
  189. package/audit/evidence/external/readme-fortune.md +0 -165
  190. package/audit/evidence/external/readme-json-api-serializer-signals.txt +0 -80
  191. package/audit/evidence/external/readme-json-api-serializer.md +0 -690
  192. package/audit/evidence/external/readme-jsonapi-serde-server-signals.txt +0 -0
  193. package/audit/evidence/external/readme-jsonapi-serde-server.md +0 -22
  194. package/audit/evidence/external/readme-jsonapi-serializer-signals.txt +0 -63
  195. package/audit/evidence/external/readme-jsonapi-serializer.md +0 -315
  196. package/audit/evidence/external/readme-jsonapi-server-signals.txt +0 -19
  197. package/audit/evidence/external/readme-jsonapi-server.md +0 -104
  198. package/audit/evidence/external/readme-nestjs-json-api-signals.txt +0 -4
  199. package/audit/evidence/external/readme-nestjs-json-api.md +0 -94
  200. package/audit/evidence/external/readme-yaser-json-api-nestjs-signals.txt +0 -47
  201. package/audit/evidence/external/readme-yaser-json-api-nestjs.md +0 -447
  202. package/audit/evidence/external/standard-jsonapi-snippets.txt +0 -84
  203. package/audit/evidence/external/standard-rfc-snippets.txt +0 -168
  204. package/audit/evidence/external/standards-sources.txt +0 -6
  205. package/audit/evidence/runtime/baseline-commits.txt +0 -2
  206. package/audit/evidence/runtime/db-adapter-assumptions.txt +0 -3
  207. package/audit/evidence/runtime/environment.txt +0 -4
  208. package/audit/evidence/runtime/invariant-runtime-mapping.md +0 -38
  209. package/audit/evidence/runtime/probe-patch-relationship-error.exitcode +0 -1
  210. package/audit/evidence/runtime/probe-patch-relationship-error.log +0 -76
  211. package/audit/evidence/runtime/probe-patch-relationship-error.mjs +0 -67
  212. package/audit/evidence/runtime/probe-patch-relationship-referenceerror.exitcode +0 -1
  213. package/audit/evidence/runtime/probe-patch-relationship-referenceerror.log +0 -62
  214. package/audit/evidence/runtime/probe-patch-relationship-referenceerror.mjs +0 -47
  215. package/audit/evidence/runtime/test-exitcodes.txt +0 -4
  216. package/audit/evidence/runtime/test-summary.md +0 -107
  217. package/audit/evidence/runtime/trace-anchors.txt +0 -61
  218. package/audit/evidence/tests/access-multitenancy-test-signals.txt +0 -125
  219. package/audit/evidence/tests/failing-tests-summary.txt +0 -25
  220. package/audit/evidence/tests/npm-test-json-rest-api.exitcode +0 -1
  221. package/audit/evidence/tests/npm-test-json-rest-api.log +0 -5885
  222. package/audit/evidence/tests/npm-test-json-rest-schema.exitcode +0 -1
  223. package/audit/evidence/tests/npm-test-json-rest-schema.log +0 -356
  224. package/audit/evidence/tests/query-include-test-signals.txt +0 -948
  225. package/audit/evidence/tests/relationship-test-signals.txt +0 -223
  226. package/audit/evidence/tests/skipped-tests.txt +0 -10
  227. package/audit/evidence/tests/test-suite-list-non-anyapi.txt +0 -30
  228. package/audit/evidence/tests/test-suite-list.txt +0 -32
  229. package/audit/subsystems/docs-drift.md +0 -49
  230. package/audit/subsystems/pass2-cross-review.md +0 -131
  231. package/audit/subsystems/plugins-access.md +0 -51
  232. package/audit/subsystems/plugins-bulk.md +0 -50
  233. package/audit/subsystems/plugins-cors.md +0 -48
  234. package/audit/subsystems/plugins-file.md +0 -53
  235. package/audit/subsystems/plugins-multihome.md +0 -50
  236. package/audit/subsystems/plugins-positioning.md +0 -54
  237. package/audit/subsystems/plugins-socketio.md +0 -50
  238. package/audit/subsystems/query-engine.md +0 -61
  239. package/audit/subsystems/relationships.md +0 -55
  240. package/audit/subsystems/rest-orchestration.md +0 -66
  241. package/audit/subsystems/schema-core.md +0 -64
  242. package/audit/subsystems/storage-knex.md +0 -57
  243. package/audit/subsystems/tests.md +0 -48
  244. package/audit/subsystems/transport-express.md +0 -55
  245. package/audit/traces/trace-read-get.md +0 -22
  246. package/audit/traces/trace-read-query.md +0 -25
  247. package/audit/traces/trace-relationship-endpoints.md +0 -31
  248. package/audit/traces/trace-write-delete.md +0 -17
  249. package/audit/traces/trace-write-patch.md +0 -26
  250. package/audit/traces/trace-write-post.md +0 -22
  251. package/audit/traces/trace-write-put.md +0 -23
  252. package/docs/GUIDE/GUIDE_4_Authentication_And_Permissions.md +0 -707
  253. package/docs/GUIDE/GUIDE_X_Multihome.md +0 -666
  254. package/plugins/core/lib/querying-writing/payload-validators.js +0 -1314
  255. package/plugins/core/rest-api-access.js +0 -459
  256. package/plugins/core/rest-api-multihome-plugin.js +0 -314
  257. package/tests/multihome.test.js +0 -553
  258. package/tests/multihome.test.js.bak +0 -550
  259. package/tests/ownership-optout.test.js +0 -76
  260. package/tests/rest-api-access.test.js +0 -206
@@ -1,484 +0,0 @@
1
- 1 /**
2
- 2 * Scope configuration validators for relationship definitions
3
- 3 *
4
- 4 * @description
5
- 5 * These validators run at scope registration time to ensure:
6
- 6 * - Polymorphic relationships are properly configured
7
- 7 * - HasMany relationships specify required foreign keys
8
- 8 * - ManyToMany relationships have pivot table settings
9
- 9 * - Referenced scopes actually exist in the system
10
- 10 */
11
- 11
12
- 12 /**
13
- 13 * Validates all relationships in a scope configuration
14
- 14 *
15
- 15 * @param {Object} params - Validation parameters
16
- 16 * @param {Object} params.context - Event context with scopeName and scopeOptions
17
- 17 * @param {Object} params.scopes - All registered scopes for validation
18
- 18 * @throws {Error} If any relationship is misconfigured
19
- 19 *
20
- 20 * @example
21
- 21 * // Input: Valid polymorphic relationship
22
- 22 * validateRelationships({
23
- 23 * context: {
24
- 24 * scopeName: 'comments',
25
- 25 * scopeOptions: {
26
- 26 * schema: {
27
- 27 * commentable_type: { type: 'string' },
28
- 28 * commentable_id: { type: 'integer' }
29
- 29 * },
30
- 30 * relationships: {
31
- 31 * commentable: {
32
- 32 * belongsToPolymorphic: {
33
- 33 * types: ['posts', 'videos'], // Must exist
34
- 34 * typeField: 'commentable_type',
35
- 35 * idField: 'commentable_id'
36
- 36 * }
37
- 37 * }
38
- 38 * }
39
- 39 * }
40
- 40 * },
41
- 41 * scopes: { posts: {}, videos: {} } // Referenced types exist
42
- 42 * });
43
- 43 * // Output: No error (valid configuration)
44
- 44 *
45
- 45 * @example
46
- 46 * // Input: Missing required field
47
- 47 * validateRelaationships({
48
- 48 * context: {
49
- 49 * scopeName: 'attachments',
50
- 50 * scopeOptions: {
51
- 51 * relationships: {
52
- 52 * attachable: {
53
- 53 * belongsToPolymorphic: {
54
- 54 * types: ['documents'],
55
- 55 * typeField: 'attachable_type'
56
- 56 * // Missing: idField
57
- 57 * }
58
- 58 * }
59
- 59 * }
60
- 60 * }
61
- 61 * },
62
- 62 * scopes: { documents: {} }
63
- 63 * });
64
- 64 * // Throws: Error
65
- 65 * // "Invalid polymorphic relationship 'attachable' in scope 'attachments':
66
- 66 * // belongsToPolymorphic.idField must be specified"
67
- 67 *
68
- 68 * @example
69
- 69 * // Input: HasMany without foreignKey
70
- 70 * validateRelationships({
71
- 71 * context: {
72
- 72 * scopeName: 'users',
73
- 73 * scopeOptions: {
74
- 74 * relationships: {
75
- 75 * posts: {
76
- 76 * type: 'hasMany',
77
- 77 * target: 'posts'
78
- 78 * // Missing: foreignKey
79
- 79 * }
80
- 80 * }
81
- 81 * }
82
- 82 * },
83
- 83 * scopes: { posts: {} }
84
- 84 * });
85
- 85 * // Throws: Error
86
- 86 * // "Invalid hasMany relationship 'posts' in scope 'users':
87
- 87 * // hasMany relationship requires foreignKey to be specified"
88
- 88 *
89
- 89 * @example
90
- 90 * // Input: Complex activity feed with multiple polymorphic relationships
91
- 91 * validateRelationships({
92
- 92 * context: {
93
- 93 * scopeName: 'activities',
94
- 94 * scopeOptions: {
95
- 95 * schema: {
96
- 96 * trackable_type: { type: 'string' },
97
- 97 * trackable_id: { type: 'integer' },
98
- 98 * actor_type: { type: 'string' },
99
- 99 * actor_id: { type: 'integer' }
100
- 100 * },
101
- 101 * relationships: {
102
- 102 * trackable: { // What was changed
103
- 103 * belongsToPolymorphic: {
104
- 104 * types: ['posts', 'comments', 'users'],
105
- 105 * typeField: 'trackable_type',
106
- 106 * idField: 'trackable_id'
107
- 107 * }
108
- 108 * },
109
- 109 * actor: { // Who made the change
110
- 110 * belongsToPolymorphic: {
111
- 111 * types: ['users', 'api_clients'],
112
- 112 * typeField: 'actor_type',
113
- 113 * idField: 'actor_id'
114
- 114 * }
115
- 115 * }
116
- 116 * }
117
- 117 * }
118
- 118 * },
119
- 119 * scopes: { posts: {}, comments: {}, users: {}, api_clients: {} }
120
- 120 * });
121
- 121 * // Output: No error (both polymorphic relationships valid)
122
- 122 *
123
- 123 * @description
124
- 124 * Used by:
125
- 125 * - rest-api-plugin on 'scope:added' event
126
- 126 * - Runs automatically when scopes are registered
127
- 127 *
128
- 128 * Purpose:
129
- 129 * - Catches configuration errors at startup, not runtime
130
- 130 * - Validates polymorphic relationship structure
131
- 131 * - Ensures referenced scope types exist
132
- 132 * - Validates hasMany/manyToMany foreign keys
133
- 133 * - Provides clear error messages for debugging
134
- 134 *
135
- 135 * Data flow:
136
- 136 * 1. Extracts relationships from scope options
137
- 137 * 2. For each relationship, checks its type
138
- 138 * 3. Validates polymorphic: types, typeField, idField
139
- 139 * 4. Validates hasMany: foreignKey (unless via)
140
- 140 * 5. Validates manyToMany: through, foreignKey, otherKey
141
- 141 * 6. Throws descriptive error on first validation failure
142
- 142 */
143
- 143 export function validateRelationships ({ context, scopes }) {
144
- 144 const { scopeName, scopeOptions } = context
145
- 145 const relationships = scopeOptions.relationships || {}
146
- 146
147
- 147 for (const [relName, relDef] of Object.entries(relationships)) {
148
- 148 if (relDef.belongsToPolymorphic) {
149
- 149 const validation = validatePolymorphicRelationship(relDef, scopeName, scopes)
150
- 150 if (!validation.valid) {
151
- 151 throw new Error(
152
- 152 `Invalid polymorphic relationship '${relName}' in scope '${scopeName}': ${validation.error}`
153
- 153 )
154
- 154 }
155
- 155 }
156
- 156
157
- 157 // Validate based on relationship type
158
- 158 if (relDef.type === 'hasMany') {
159
- 159 const validation = validateHasManyRelationship(relDef, relName, scopeName)
160
- 160 if (!validation.valid) {
161
- 161 throw new Error(
162
- 162 `Invalid hasMany relationship '${relName}' in scope '${scopeName}': ${validation.error}`
163
- 163 )
164
- 164 }
165
- 165 }
166
- 166
167
- 167 if (relDef.type === 'hasOne') {
168
- 168 const validation = validateHasOneRelationship(relDef, relName, scopeName)
169
- 169 if (!validation.valid) {
170
- 170 throw new Error(
171
- 171 `Invalid hasOne relationship '${relName}' in scope '${scopeName}': ${validation.error}`
172
- 172 )
173
- 173 }
174
- 174 }
175
- 175
176
- 176 if (relDef.type === 'manyToMany') {
177
- 177 const validation = validateManyToManyRelationship(relDef, relName, scopeName)
178
- 178 if (!validation.valid) {
179
- 179 throw new Error(
180
- 180 `Invalid manyToMany relationship '${relName}' in scope '${scopeName}': ${validation.error}`
181
- 181 )
182
- 182 }
183
- 183 }
184
- 184 }
185
- 185 }
186
- 186
187
- 187 /**
188
- 188 * Validates a polymorphic relationship definition
189
- 189 *
190
- 190 * @param {Object} relDef - Relationship definition with belongsToPolymorphic
191
- 191 * @param {string} scopeName - Scope being registered (for error messages)
192
- 192 * @param {Object} scopes - All registered scopes for validation
193
- 193 * @returns {Object} Validation result {valid: boolean, error?: string}
194
- 194 *
195
- 195 * @example
196
- 196 * // Input: Valid polymorphic configuration
197
- 197 * const relDef = {
198
- 198 * belongsToPolymorphic: {
199
- 199 * types: ['articles', 'videos', 'products'],
200
- 200 * typeField: 'commentable_type',
201
- 201 * idField: 'commentable_id'
202
- 202 * }
203
- 203 * };
204
- 204 * validatePolymorphicRelationship(relDef, 'comments', scopes);
205
- 205 * // Output: { valid: true }
206
- 206 *
207
- 207 * @example
208
- 208 * // Input: Missing required types array
209
- 209 * const relDef = {
210
- 210 * belongsToPolymorphic: {
211
- 211 * typeField: 'commentable_type',
212
- 212 * idField: 'commentable_id'
213
- 213 * // Missing: types
214
- 214 * }
215
- 215 * };
216
- 216 * validatePolymorphicRelationship(relDef, 'comments', scopes);
217
- 217 * // Output: {
218
- 218 * // valid: false,
219
- 219 * // error: 'belongsToPolymorphic.types must be a non-empty array'
220
- 220 * // }
221
- 221 *
222
- 222 * @example
223
- 223 * // Input: References non-existent scope
224
- 224 * const relDef = {
225
- 225 * belongsToPolymorphic: {
226
- 226 * types: ['articles', 'unicorns'], // 'unicorns' doesn't exist
227
- 227 * typeField: 'attachable_type',
228
- 228 * idField: 'attachable_id'
229
- 229 * }
230
- 230 * };
231
- 231 * validatePolymorphicRelationship(relDef, 'attachments', scopes);
232
- 232 * // Output: {
233
- 233 * // valid: false,
234
- 234 * // error: "Polymorphic type 'unicorns' is not a registered scope"
235
- 235 * // }
236
- 236 *
237
- 237 * @private
238
- 238 */
239
- 239 const validatePolymorphicRelationship = (relDef, scopeName, scopes) => {
240
- 240 // Validation logic:
241
- 241 // 1. Check relDef.belongsToPolymorphic exists
242
- 242 // 2. Validate required properties: types, typeField, idField
243
- 243 // 3. Ensure types is non-empty array
244
- 244 // 4. Verify all types are registered scopes
245
- 245 // 5. Check that typeField and idField exist in the schema
246
- 246
247
- 247 const { belongsToPolymorphic } = relDef
248
- 248
249
- 249 if (!belongsToPolymorphic) {
250
- 250 return { valid: false, error: 'Missing belongsToPolymorphic definition' }
251
- 251 }
252
- 252
253
- 253 const { types, typeField, idField } = belongsToPolymorphic
254
- 254
255
- 255 if (!types || !Array.isArray(types) || types.length === 0) {
256
- 256 return {
257
- 257 valid: false,
258
- 258 error: 'belongsToPolymorphic.types must be a non-empty array'
259
- 259 }
260
- 260 }
261
- 261
262
- 262 if (!typeField || typeof typeField !== 'string') {
263
- 263 return {
264
- 264 valid: false,
265
- 265 error: 'belongsToPolymorphic.typeField must be specified'
266
- 266 }
267
- 267 }
268
- 268
269
- 269 if (!idField || typeof idField !== 'string') {
270
- 270 return {
271
- 271 valid: false,
272
- 272 error: 'belongsToPolymorphic.idField must be specified'
273
- 273 }
274
- 274 }
275
- 275
276
- 276 // Check that all types are valid scopes
277
- 277 for (const type of types) {
278
- 278 if (!scopes[type]) {
279
- 279 return {
280
- 280 valid: false,
281
- 281 error: `Polymorphic type '${type}' is not a registered scope`
282
- 282 }
283
- 283 }
284
- 284 }
285
- 285
286
- 286 return { valid: true }
287
- 287 }
288
- 288
289
- 289 /**
290
- 290 * Validates a hasMany relationship definition
291
- 291 *
292
- 292 * @param {Object} relDef - Relationship definition with hasMany
293
- 293 * @param {string} relName - Relationship name
294
- 294 * @param {string} scopeName - Scope being registered
295
- 295 * @returns {Object} Validation result {valid: boolean, error?: string}
296
- 296 *
297
- 297 * @example
298
- 298 * // Input: Valid hasMany configuration
299
- 299 * const relDef = {
300
- 300 * type: 'hasMany',
301
- 301 * target: 'posts',
302
- 302 * foreignKey: 'author_id' // Required!
303
- 303 * };
304
- 304 * validateHasManyRelationship(relDef, 'posts', 'users');
305
- 305 * // Output: { valid: true }
306
- 306 *
307
- 307 * @example
308
- 308 * // Input: Missing required foreignKey
309
- 309 * const relDef = {
310
- 310 * type: 'hasMany',
311
- 311 * target: 'posts'
312
- 312 * // Missing: foreignKey
313
- 313 * };
314
- 314 * validateHasManyRelationship(relDef, 'posts', 'users');
315
- 315 * // Output: {
316
- 316 * // valid: false,
317
- 317 * // error: 'hasMany relationship requires foreignKey to be specified...'
318
- 318 * // }
319
- 319 *
320
- 320 * @example
321
- 321 * // Input: Polymorphic hasMany with 'via' (doesn't need foreignKey)
322
- 322 * const relDef = {
323
- 323 * type: 'hasMany',
324
- 324 * target: 'comments',
325
- 325 * via: 'commentable' // Uses polymorphic relationship
326
- 326 * };
327
- 327 * validateHasManyRelationship(relDef, 'comments', 'posts');
328
- 328 * // Output: { valid: true }
329
- 329 *
330
- 330 * @private
331
- 331 */
332
- 332 const validateHasManyRelationship = (relDef, relName, scopeName) => {
333
- 333 // Validate target is specified
334
- 334 if (!relDef.target) {
335
- 335 return {
336
- 336 valid: false,
337
- 337 error: 'hasMany relationship requires \'target\' to be specified.'
338
- 338 }
339
- 339 }
340
- 340
341
- 341 // Polymorphic hasMany relationships using 'via' don't need foreignKey
342
- 342 // They use the polymorphic fields (typeField, idField) from the belongsToPolymorphic relationship
343
- 343 if (relDef.via) {
344
- 344 return { valid: true }
345
- 345 }
346
- 346
347
- 347 if (!relDef.foreignKey) {
348
- 348 return {
349
- 349 valid: false,
350
- 350 error: 'hasMany relationship requires foreignKey to be specified. Add foreignKey: \'<field_name>\' to the relationship definition.'
351
- 351 }
352
- 352 }
353
- 353
354
- 354 if (typeof relDef.foreignKey !== 'string') {
355
- 355 return {
356
- 356 valid: false,
357
- 357 error: `hasMany relationship foreignKey must be a string, got ${typeof relDef.foreignKey}`
358
- 358 }
359
- 359 }
360
- 360
361
- 361 return { valid: true }
362
- 362 }
363
- 363
364
- 364 /**
365
- 365 * Validates a hasOne relationship definition
366
- 366 *
367
- 367 * @param {Object} relDef - Relationship definition with hasOne
368
- 368 * @param {string} relName - Relationship name
369
- 369 * @param {string} scopeName - Scope being registered
370
- 370 * @returns {Object} Validation result {valid: boolean, error?: string}
371
- 371 *
372
- 372 * @example
373
- 373 * // Input: Valid hasOne configuration
374
- 374 * const relDef = {
375
- 375 * type: 'hasOne',
376
- 376 * target: 'profile',
377
- 377 * foreignKey: 'user_id' // Required!
378
- 378 * };
379
- 379 * validateHasOneRelationship(relDef, 'profile', 'users');
380
- 380 * // Output: { valid: true }
381
- 381 *
382
- 382 * @private
383
- 383 */
384
- 384 const validateHasOneRelationship = (relDef, relName, scopeName) => {
385
- 385 // Validate target is specified
386
- 386 if (!relDef.target) {
387
- 387 return {
388
- 388 valid: false,
389
- 389 error: 'hasOne relationship requires \'target\' to be specified.'
390
- 390 }
391
- 391 }
392
- 392
393
- 393 if (!relDef.foreignKey) {
394
- 394 return {
395
- 395 valid: false,
396
- 396 error: 'hasOne relationship requires foreignKey to be specified. Add foreignKey: \'<field_name>\' to the relationship definition.'
397
- 397 }
398
- 398 }
399
- 399
400
- 400 if (typeof relDef.foreignKey !== 'string') {
401
- 401 return {
402
- 402 valid: false,
403
- 403 error: `hasOne relationship foreignKey must be a string, got ${typeof relDef.foreignKey}`
404
- 404 }
405
- 405 }
406
- 406
407
- 407 return { valid: true }
408
- 408 }
409
- 409
410
- 410 /**
411
- 411 * Validates a manyToMany relationship definition
412
- 412 *
413
- 413 * @param {Object} relDef - Relationship definition with manyToMany
414
- 414 * @param {string} relName - Relationship name
415
- 415 * @param {string} scopeName - Scope being registered
416
- 416 * @returns {Object} Validation result {valid: boolean, error?: string}
417
- 417 *
418
- 418 * @example
419
- 419 * // Input: Valid manyToMany configuration
420
- 420 * const relDef = {
421
- 421 * type: 'manyToMany',
422
- 422 * through: 'article_tags', // Pivot table
423
- 423 * foreignKey: 'article_id', // This scope's FK
424
- 424 * otherKey: 'tag_id' // Other scope's FK
425
- 425 * };
426
- 426 * validateManyToManyRelationship(relDef, 'tags', 'articles');
427
- 427 * // Output: { valid: true }
428
- 428 *
429
- 429 * @example
430
- 430 * // Input: Missing required fields
431
- 431 * const relDef = {
432
- 432 * type: 'manyToMany',
433
- 433 * through: 'article_tags',
434
- 434 * foreignKey: 'article_id'
435
- 435 * // Missing: otherKey
436
- 436 * };
437
- 437 * validateManyToManyRelationship(relDef, 'tags', 'articles');
438
- 438 * // Output: {
439
- 439 * // valid: false,
440
- 440 * // error: 'manyToMany relationship requires otherKey to be specified...'
441
- 441 * // }
442
- 442 *
443
- 443 * @private
444
- 444 */
445
- 445 const validateManyToManyRelationship = (relDef, relName, scopeName) => {
446
- 446 const { through, foreignKey, otherKey } = relDef
447
- 447
448
- 448 if (!through) {
449
- 449 return {
450
- 450 valid: false,
451
- 451 error: 'manyToMany relationship requires \'through\' table to be specified'
452
- 452 }
453
- 453 }
454
- 454
455
- 455 if (!foreignKey) {
456
- 456 return {
457
- 457 valid: false,
458
- 458 error: 'manyToMany relationship requires foreignKey to be specified. Add foreignKey: \'<field_name>\' to the manyToMany configuration.'
459
- 459 }
460
- 460 }
461
- 461
462
- 462 if (!otherKey) {
463
- 463 return {
464
- 464 valid: false,
465
- 465 error: 'manyToMany relationship requires otherKey to be specified. Add otherKey: \'<field_name>\' to the manyToMany configuration.'
466
- 466 }
467
- 467 }
468
- 468
469
- 469 if (typeof foreignKey !== 'string') {
470
- 470 return {
471
- 471 valid: false,
472
- 472 error: `manyToMany relationship foreignKey must be a string, got ${typeof foreignKey}`
473
- 473 }
474
- 474 }
475
- 475
476
- 476 if (typeof otherKey !== 'string') {
477
- 477 return {
478
- 478 valid: false,
479
- 479 error: `manyToMany relationship otherKey must be a string, got ${typeof otherKey}`
480
- 480 }
481
- 481 }
482
- 482
483
- 483 return { valid: true }
484
- 484 }