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,488 +0,0 @@
1
- 1 /**
2
- 2 * Schema processing utilities for search and field dependencies
3
- 3 *
4
- 4 * @description
5
- 5 * This module provides utilities for:
6
- 6 * - Marking search fields as indexed for database optimization
7
- 7 * - Generating search schemas from various configuration sources
8
- 8 * - Sorting fields by their dependencies (for getters/setters)
9
- 9 * - Detecting circular dependencies in field definitions
10
- 10 */
11
- 11
12
- 12 import { RestApiValidationError } from '../../../../lib/rest-api-errors.js'
13
- 13
14
- 14 /**
15
- 15 * Marks all fields in a searchSchema as indexed for database optimization
16
- 16 *
17
- 17 * @param {Object} searchSchema - Search schema object to process
18
- 18 * @returns {void} Modifies the searchSchema in-place
19
- 19 *
20
- 20 * @example
21
- 21 * // Input: Search fields without index flags
22
- 22 * const searchSchema = {
23
- 23 * title: { type: 'string', filterOperator: 'contains' },
24
- 24 * status: { type: 'string', filterOperator: '=' },
25
- 25 * created_at: { type: 'datetime', filterOperator: '>=' }
26
- 26 * };
27
- 27 * ensureSearchFieldsAreIndexed(searchSchema);
28
- 28 *
29
- 29 * // Output: All fields marked as indexed
30
- 30 * // {
31
- 31 * // title: { type: 'string', filterOperator: 'contains', indexed: true },
32
- 32 * // status: { type: 'string', filterOperator: '=', indexed: true },
33
- 33 * // created_at: { type: 'datetime', filterOperator: '>=', indexed: true }
34
- 34 * // }
35
- 35 *
36
- 36 * @example
37
- 37 * // Input: Virtual field for cross-table search
38
- 38 * const searchSchema = {
39
- 39 * name: { type: 'string', filterOperator: 'contains' },
40
- 40 * author_name: { // Virtual field
41
- 41 * type: 'string',
42
- 42 * filterOperator: 'contains',
43
- 43 * virtualField: {
44
- 44 * joinTo: 'users',
45
- 45 * joinOn: ['author_id', 'id'],
46
- 46 * searchField: 'name'
47
- 47 * }
48
- 48 * }
49
- 49 * };
50
- 50 * ensureSearchFieldsAreIndexed(searchSchema);
51
- 51 *
52
- 52 * // Output: Both regular and virtual fields indexed
53
- 53 * // {
54
- 54 * // name: { ..., indexed: true },
55
- 55 * // author_name: { ..., indexed: true } // Enables efficient JOIN
56
- 56 * // }
57
- 57 *
58
- 58 * @example
59
- 59 * // Input: Already indexed or explicitly false
60
- 60 * const searchSchema = {
61
- 61 * email: { type: 'string', indexed: false }, // Explicitly false
62
- 62 * username: { type: 'string', indexed: true } // Already true
63
- 63 * };
64
- 64 * ensureSearchFieldsAreIndexed(searchSchema);
65
- 65 *
66
- 66 * // Output: All forced to indexed: true
67
- 67 * // {
68
- 68 * // email: { type: 'string', indexed: true }, // Overridden
69
- 69 * // username: { type: 'string', indexed: true } // Unchanged
70
- 70 * // }
71
- 71 *
72
- 72 * @description
73
- 73 * Used by:
74
- 74 * - rest-api-plugin during scope initialization
75
- 75 * - Applied to all searchSchema fields before storage setup
76
- 76 *
77
- 77 * Purpose:
78
- 78 * - Signals storage plugins which fields need database indexes
79
- 79 * - Enables efficient filtering without full table scans
80
- 80 * - Supports cross-table searches via indexed JOINs
81
- 81 * - Improves query performance for filtered API requests
82
- 82 *
83
- 83 * Data flow:
84
- 84 * 1. Receives searchSchema object (or null)
85
- 85 * 2. Iterates through each field definition
86
- 86 * 3. Sets indexed: true on all fields
87
- 87 * 4. Storage plugins use this flag for index creation
88
- 88 */
89
- 89 export function ensureSearchFieldsAreIndexed (searchSchema) {
90
- 90 if (!searchSchema) return
91
- 91
92
- 92 Object.keys(searchSchema).forEach(fieldName => {
93
- 93 const fieldDef = searchSchema[fieldName]
94
- 94 if (fieldDef && typeof fieldDef === 'object') {
95
- 95 // Mark the field as indexed for cross-table search support
96
- 96 fieldDef.indexed = true
97
- 97 }
98
- 98 })
99
- 99 }
100
- 100
101
- 101 /**
102
- 102 * Generates complete searchSchema by merging schema search definitions with explicit searchSchema
103
- 103 *
104
- 104 * @param {Object} schema - Main resource schema with optional 'search' properties
105
- 105 * @param {Object} explicitSearchSchema - Optional explicit searchSchema to merge
106
- 106 * @returns {Object|null} Merged searchSchema or null if no search fields
107
- 107 * @throws {RestApiValidationError} If same field defined in multiple places
108
- 108 *
109
- 109 * @example
110
- 110 * // Input: Simple search fields in schema
111
- 111 * const schema = {
112
- 112 * title: { type: 'string', search: true }, // Simple
113
- 113 * content: { type: 'text' }, // Not searchable
114
- 114 * status: { type: 'string', search: { filterOperator: '=' } } // Custom
115
- 115 * };
116
- 116 * const result = generateSearchSchemaFromSchema(schema, null);
117
- 117 *
118
- 118 * // Output: Generated search schema
119
- 119 * // {
120
- 120 * // title: { type: 'string' }, // No operator set; resolver applies defaults
121
- 121 * // status: { type: 'string', filterOperator: '=' } // Specified operator
122
- 122 * // }
123
- 123 *
124
- 124 * @example
125
- 125 * // Input: Multiple filters from one field
126
- 126 * const schema = {
127
- 127 * published_at: {
128
- 128 * type: 'datetime',
129
- 129 * search: {
130
- 130 * published_after: { filterOperator: '>=', type: 'datetime' },
131
- 131 * published_before: { filterOperator: '<=', type: 'datetime' }
132
- 132 * }
133
- 133 * }
134
- 134 * };
135
- 135 * const result = generateSearchSchemaFromSchema(schema, null);
136
- 136 *
137
- 137 * // Output: Two search fields from one database field
138
- 138 * // {
139
- 139 * // published_after: {
140
- 140 * // type: 'datetime',
141
- 141 * // actualField: 'published_at', // Maps to real field
142
- 142 * // filterOperator: '>='
143
- 143 * // },
144
- 144 * // published_before: {
145
- 145 * // type: 'datetime',
146
- 146 * // actualField: 'published_at', // Same field, different operator
147
- 147 * // filterOperator: '<='
148
- 148 * // }
149
- 149 * // }
150
- 150 *
151
- 151 * @example
152
- 152 * // Input: Merging with explicit searchSchema
153
- 153 * const schema = {
154
- 154 * name: { type: 'string', search: true }
155
- 155 * };
156
- 156 * const explicitSearchSchema = {
157
- 157 * email: { type: 'string', filterOperator: '=' },
158
- 158 * age: { type: 'number', filterOperator: '>=' }
159
- 159 * };
160
- 160 * const result = generateSearchSchemaFromSchema(schema, explicitSearchSchema);
161
- 161 *
162
- 162 * // Output: Combined from both sources
163
- 163 * // {
164
- 164 * // email: { type: 'string', filterOperator: '=' }, // Explicit
165
- 165 * // age: { type: 'number', filterOperator: '>=' }, // Explicit
166
- 166 * // name: { type: 'string' } // From schema (no operator set)
167
- 167 * // }
168
- 168 *
169
- 169 * @example
170
- 170 * // Input: Explicit searchSchema takes precedence
171
- 171 * const schema = {
172
- 172 * email: { type: 'string', search: true }
173
- 173 * };
174
- 174 * const explicitSearchSchema = {
175
- 175 * email: { type: 'string', filterOperator: 'contains' }
176
- 176 * };
177
- 177 * const result = generateSearchSchemaFromSchema(schema, explicitSearchSchema);
178
- 178 * // Output: Explicit searchSchema wins
179
- 179 * // {
180
- 180 * // email: { type: 'string', filterOperator: 'contains' } // Uses explicit definition
181
- 181 * // }
182
- 182 *
183
- 183 * @example
184
- 184 * // Input: Virtual search fields
185
- 185 * const schema = {
186
- 186 * title: { type: 'string' },
187
- 187 * _virtual: {
188
- 188 * search: {
189
- 189 * category_name: { // Doesn't exist in DB
190
- 190 * type: 'string',
191
- 191 * filterOperator: 'contains',
192
- 192 * virtualField: {
193
- 193 * joinTo: 'categories',
194
- 194 * joinOn: ['category_id', 'id'], // JOIN condition
195
- 195 * searchField: 'name' // Field in related table
196
- 196 * }
197
- 197 * }
198
- 198 * }
199
- 199 * }
200
- 200 * };
201
- 201 *
202
- 202 * // Output: Virtual field for cross-table search
203
- 203 * // {
204
- 204 * // category_name: {
205
- 205 * // type: 'string',
206
- 206 * // filterOperator: 'contains',
207
- 207 * // virtualField: { ... join config ... }
208
- 208 * // }
209
- 209 * // }
210
- 210 *
211
- 211 * @description
212
- 212 * Used by:
213
- 213 * - rest-api-plugin during scope initialization
214
- 214 * - Generates searchSchema from various sources
215
- 215 *
216
- 216 * Purpose:
217
- 217 * - Provides flexible search configuration options
218
- 218 * - Enables virtual fields for cross-table searches
219
- 219 * - Supports range queries (before/after patterns)
220
- 220 * - Merges search:true fields with explicit searchSchema
221
- 221 * - Explicit searchSchema always takes precedence
222
- 222 * - Allows storage plugins to optimize queries
223
- 223 *
224
- 224 * Data flow:
225
- 225 * 1. Starts with explicit searchSchema (if any)
226
- 226 * 2. Processes schema fields with 'search' property
227
- 227 * 3. Skips fields already in explicit searchSchema (no conflicts)
228
- 228 * 4. Handles multiple filters from single field
229
- 229 * 5. Processes virtual search definitions
230
- 230 * 6. Returns merged searchSchema or null
231
- 231 */
232
- 232 export const generateSearchSchemaFromSchema = (schema, explicitSearchSchema) => {
233
- 233 // Start with explicit searchSchema or empty object
234
- 234 const searchSchema = explicitSearchSchema ? { ...explicitSearchSchema } : {}
235
- 235
236
- 236 if (!schema) {
237
- 237 return Object.keys(searchSchema).length > 0 ? searchSchema : null
238
- 238 }
239
- 239
240
- 240 // Process schema fields with 'search' property
241
- 241 Object.entries(schema).forEach(([fieldName, fieldDef]) => {
242
- 242 const effectiveSearch = fieldDef.search
243
- 243
244
- 244 if (effectiveSearch) {
245
- 245 if (effectiveSearch === true) {
246
- 246 // For belongsTo relationships, use the relationship name (as property) instead of the field name
247
- 247 const searchFieldName = (fieldDef.belongsTo && fieldDef.as) ? fieldDef.as : fieldName
248
- 248
249
- 249 // Check if field already exists in explicit searchSchema
250
- 250 if (searchSchema[searchFieldName]) {
251
- 251 // Skip - explicit searchSchema takes precedence
252
- 252 // This allows searchSchema to override fields marked with search:true
253
- 253 return
254
- 254 }
255
- 255
256
- 256 // Simple boolean - copy entire field definition (except search)
257
- 257 const { search, ...fieldDefWithoutSearch } = fieldDef
258
- 258 const searchEntry = {
259
- 259 ...fieldDefWithoutSearch,
260
- 260 // Do not set filterOperator here; centralized resolver will apply sensible defaults
261
- 261 }
262
- 262
263
- 263 // For belongsTo relationships, add metadata to map back to the actual field
264
- 264 if (fieldDef.belongsTo && fieldDef.as) {
265
- 265 searchEntry.actualField = fieldName // Maps to the DB column
266
- 266 searchEntry.isRelationship = true
267
- 267 searchEntry.targetResource = fieldDef.belongsTo
268
- 268 }
269
- 269
270
- 270 searchSchema[searchFieldName] = searchEntry
271
- 271 } else if (typeof effectiveSearch === 'object') {
272
- 272 // Check if search defines multiple filter fields
273
- 273 const hasNestedFilters = Object.values(effectiveSearch).some(
274
- 274 v => typeof v === 'object' && v.filterOperator
275
- 275 )
276
- 276
277
- 277 if (hasNestedFilters) {
278
- 278 // Multiple filters from one field (like published_after/before)
279
- 279 Object.entries(effectiveSearch).forEach(([filterName, filterDef]) => {
280
- 280 // Check if filter already exists in explicit searchSchema
281
- 281 if (searchSchema[filterName]) {
282
- 282 // Skip - explicit searchSchema takes precedence
283
- 283 return
284
- 284 }
285
- 285
286
- 286 searchSchema[filterName] = {
287
- 287 type: fieldDef.type,
288
- 288 actualField: fieldName,
289
- 289 ...filterDef
290
- 290 }
291
- 291 })
292
- 292 } else {
293
- 293 // For belongsTo relationships, use the relationship name (as property) instead of the field name
294
- 294 const searchFieldName = (fieldDef.belongsTo && fieldDef.as) ? fieldDef.as : fieldName
295
- 295
296
- 296 // Check if field already exists in explicit searchSchema
297
- 297 if (searchSchema[searchFieldName]) {
298
- 298 // Skip - explicit searchSchema takes precedence
299
- 299 return
300
- 300 }
301
- 301
302
- 302 // Single filter with config
303
- 303 const searchEntry = {
304
- 304 type: fieldDef.type,
305
- 305 ...effectiveSearch
306
- 306 }
307
- 307
308
- 308 // For belongsTo relationships, add metadata to map back to the actual field
309
- 309 if (fieldDef.belongsTo && fieldDef.as) {
310
- 310 searchEntry.actualField = fieldName // Maps to the DB column
311
- 311 searchEntry.isRelationship = true
312
- 312 searchEntry.targetResource = fieldDef.belongsTo
313
- 313 }
314
- 314
315
- 315 searchSchema[searchFieldName] = searchEntry
316
- 316 }
317
- 317 }
318
- 318 }
319
- 319 })
320
- 320
321
- 321 // Handle _virtual search definitions
322
- 322 if (schema._virtual?.search) {
323
- 323 Object.entries(schema._virtual.search).forEach(([filterName, filterDef]) => {
324
- 324 // Check if filter already exists in explicit searchSchema
325
- 325 if (searchSchema[filterName]) {
326
- 326 // Skip - explicit searchSchema takes precedence
327
- 327 return
328
- 328 }
329
- 329
330
- 330 searchSchema[filterName] = filterDef
331
- 331 })
332
- 332 }
333
- 333
334
- 334 return Object.keys(searchSchema).length > 0 ? searchSchema : null
335
- 335 }
336
- 336
337
- 337 /**
338
- 338 * Generic topological sort for handling dependencies
339
- 339 *
340
- 340 * @param {Array} items - Array of items to sort
341
- 341 * @param {Function} getDependencies - Function that returns dependencies for an item
342
- 342 * @returns {Array} Sorted array respecting dependencies
343
- 343 * @throws {Error} If circular dependencies or unknown dependencies detected
344
- 344 *
345
- 345 * @example
346
- 346 * // Input: Simple dependency chain
347
- 347 * const items = ['a', 'b', 'c'];
348
- 348 * const deps = { a: ['b'], b: ['c'], c: [] };
349
- 349 * const sorted = topologicalSort(items, item => deps[item]);
350
- 350 * // Output: ['c', 'b', 'a']
351
- 351 * // c first (no deps), then b (depends on c), then a (depends on b)
352
- 352 *
353
- 353 * @example
354
- 354 * // Input: Circular dependency
355
- 355 * const items = ['a', 'b'];
356
- 356 * const deps = { a: ['b'], b: ['a'] }; // Circular!
357
- 357 * topologicalSort(items, item => deps[item]);
358
- 358 * // Throws: Error "Circular dependency detected: b"
359
- 359 *
360
- 360 * @example
361
- 361 * // Input: Unknown dependency
362
- 362 * const items = ['a', 'b'];
363
- 363 * const deps = { a: ['c'], b: [] }; // 'c' not in items
364
- 364 * topologicalSort(items, item => deps[item]);
365
- 365 * // Throws: Error "Unknown dependency 'c' for 'a'"
366
- 366 *
367
- 367 * @description
368
- 368 * Used by:
369
- 369 * - sortFieldsByDependencies for ordering field operations
370
- 370 * - Any code needing dependency-based ordering
371
- 371 *
372
- 372 * Purpose:
373
- 373 * - Orders items so dependencies come before dependents
374
- 374 * - Detects circular dependencies early
375
- 375 * - Validates all dependencies exist
376
- 376 * - Uses depth-first search algorithm
377
- 377 */
378
- 378 export function topologicalSort (items, getDependencies) {
379
- 379 const sorted = []
380
- 380 const visited = new Set()
381
- 381 const visiting = new Set()
382
- 382
383
- 383 function visit (item) {
384
- 384 if (visited.has(item)) return
385
- 385
386
- 386 if (visiting.has(item)) {
387
- 387 throw new Error(`Circular dependency detected: ${item}`)
388
- 388 }
389
- 389
390
- 390 visiting.add(item)
391
- 391
392
- 392 const dependencies = getDependencies(item) || []
393
- 393 for (const dep of dependencies) {
394
- 394 if (!items.includes(dep)) {
395
- 395 throw new Error(`Unknown dependency '${dep}' for '${item}'`)
396
- 396 }
397
- 397 visit(dep)
398
- 398 }
399
- 399
400
- 400 visiting.delete(item)
401
- 401 visited.add(item)
402
- 402 sorted.push(item)
403
- 403 }
404
- 404
405
- 405 for (const item of items) {
406
- 406 visit(item)
407
- 407 }
408
- 408
409
- 409 return sorted
410
- 410 }
411
- 411
412
- 412 /**
413
- 413 * Sorts fields by their dependencies using topological sort
414
- 414 *
415
- 415 * @param {Object} fields - Object with field definitions
416
- 416 * @param {string} dependencyProperty - Property name containing dependencies
417
- 417 * @returns {Array} Field names sorted by dependencies
418
- 418 * @throws {Error} If circular dependencies or unknown fields detected
419
- 419 *
420
- 420 * @example
421
- 421 * // Input: Getter dependencies (fullName needs firstName and lastName)
422
- 422 * const fields = {
423
- 423 * firstName: { getter: v => v.trim() },
424
- 424 * lastName: { getter: v => v.trim() },
425
- 425 * fullName: {
426
- 426 * getter: (v, ctx) => `${ctx.attributes.firstName} ${ctx.attributes.lastName}`,
427
- 427 * runGetterAfter: ['firstName', 'lastName']
428
- 428 * }
429
- 429 * };
430
- 430 * const sorted = sortFieldsByDependencies(fields, 'runGetterAfter');
431
- 431 * // Output: ['firstName', 'lastName', 'fullName']
432
- 432 * // Ensures firstName/lastName getters run before fullName
433
- 433 *
434
- 434 * @example
435
- 435 * // Input: Complex dependency chain
436
- 436 * const fields = {
437
- 437 * a: { runGetterAfter: ['b', 'c'] }, // a needs b and c
438
- 438 * b: { runGetterAfter: ['d'] }, // b needs d
439
- 439 * c: { runGetterAfter: ['d'] }, // c needs d
440
- 440 * d: { runGetterAfter: [] } // d needs nothing
441
- 441 * };
442
- 442 * const sorted = sortFieldsByDependencies(fields, 'runGetterAfter');
443
- 443 * // Output: ['d', 'b', 'c', 'a']
444
- 444 * // d first, then b/c (both need d), then a (needs b/c)
445
- 445 *
446
- 446 * @example
447
- 447 * // Input: Circular dependency error
448
- 448 * const fields = {
449
- 449 * a: { runGetterAfter: ['b'] },
450
- 450 * b: { runGetterAfter: ['a'] } // Circular!
451
- 451 * };
452
- 452 * sortFieldsByDependencies(fields, 'runGetterAfter');
453
- 453 * // Throws: Error "Circular dependency detected: a in runGetterAfter"
454
- 454 *
455
- 455 * @description
456
- 456 * Used by:
457
- 457 * - Schema processing for getter/setter ordering
458
- 458 * - Ensures dependent fields process after dependencies
459
- 459 *
460
- 460 * Purpose:
461
- 461 * - Orders field operations by dependencies
462
- 462 * - Enables computed fields that depend on other fields
463
- 463 * - Validates dependency graph is acyclic
464
- 464 * - Provides clear error messages for debugging
465
- 465 *
466
- 466 * Data flow:
467
- 467 * 1. Extracts field names from object
468
- 468 * 2. Calls topologicalSort with dependency function
469
- 469 * 3. Returns ordered field names
470
- 470 * 4. Enhances error messages with property name
471
- 471 */
472
- 472 export function sortFieldsByDependencies (fields, dependencyProperty) {
473
- 473 const fieldNames = Object.keys(fields)
474
- 474
475
- 475 if (fieldNames.length === 0) return []
476
- 476
477
- 477 try {
478
- 478 return topologicalSort(fieldNames, (fieldName) => {
479
- 479 const field = fields[fieldName]
480
- 480 return field[dependencyProperty] || []
481
- 481 })
482
- 482 } catch (error) {
483
- 483 if (error.message.includes('Circular dependency')) {
484
- 484 throw new Error(`${error.message} in ${dependencyProperty}`)
485
- 485 }
486
- 486 throw error
487
- 487 }
488
- 488 }