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,175 +0,0 @@
1
- 1 /**
2
- 2 * Busboy File Detector for HTTP Multipart Uploads
3
- 3 *
4
- 4 * This detector handles multipart/form-data uploads using the busboy library.
5
- 5 * It's suitable for both HTTP and Express connectors.
6
- 6 *
7
- 7 * Features:
8
- 8 * - Streaming parser (memory efficient)
9
- 9 * - Configurable file size limits
10
- 10 * - Automatic field parsing
11
- 11 * - No temporary files by default (keeps in memory)
12
- 12 *
13
- 13 * Usage:
14
- 14 * ```javascript
15
- 15 * import { createBusboyDetector } from 'jsonrestapi/plugins/core/lib/busboy-detector.js';
16
- 16 *
17
- 17 * api.use(HttpPlugin, {
18
- 18 * fileParser: 'busboy',
19
- 19 * fileParserOptions: {
20
- 20 * limits: { fileSize: 10 * 1024 * 1024 } // 10MB
21
- 21 * }
22
- 22 * });
23
- 23 * ```
24
- 24 */
25
- 25
26
- 26 import { requirePackage } from 'hooked-api'
27
- 27 import { Readable } from 'stream'
28
- 28
29
- 29 let Busboy
30
- 30 try {
31
- 31 Busboy = (await import('busboy')).default
32
- 32 } catch (e) {
33
- 33 requirePackage('busboy', 'express/http-connector',
34
- 34 'Busboy is required for multipart/form-data file uploads. This is a peer dependency.')
35
- 35 }
36
- 36
37
- 37 /**
38
- 38 * Creates a busboy-based file detector
39
- 39 *
40
- 40 * @param {Object} options - Busboy configuration options
41
- 41 * @param {Object} options.limits - Size limits
42
- 42 * @param {number} options.limits.fileSize - Max file size in bytes
43
- 43 * @param {number} options.limits.files - Max number of files
44
- 44 * @param {number} options.limits.fields - Max number of fields
45
- 45 * @returns {Object} Detector object with detect() and parse() methods
46
- 46 */
47
- 47 export function createBusboyDetector (options = {}) {
48
- 48 return {
49
- 49 name: 'busboy-multipart',
50
- 50
51
- 51 /**
52
- 52 * Check if this detector can handle the request
53
- 53 * @param {Object} params - Request parameters
54
- 54 * @returns {boolean} True if this is a multipart request
55
- 55 */
56
- 56 detect: (params) => {
57
- 57 const req = params._httpReq || params._expressReq
58
- 58 if (!req || !req.headers) return false
59
- 59
60
- 60 const contentType = req.headers['content-type'] || ''
61
- 61 return contentType.includes('multipart/form-data')
62
- 62 },
63
- 63
64
- 64 /**
65
- 65 * Parse multipart data from the request
66
- 66 * @param {Object} params - Request parameters
67
- 67 * @returns {Promise<{fields: Object, files: Object}>} Parsed data
68
- 68 */
69
- 69 parse: async (params) => {
70
- 70 const req = params._httpReq || params._expressReq
71
- 71
72
- 72 return new Promise((resolve, reject) => {
73
- 73 const busboy = new Busboy({
74
- 74 headers: req.headers,
75
- 75 ...options
76
- 76 })
77
- 77
78
- 78 const fields = {}
79
- 79 const files = {}
80
- 80 const filePromises = []
81
- 81
82
- 82 // Handle fields
83
- 83 busboy.on('field', (fieldname, val) => {
84
- 84 // Handle array notation (field[] or field[0])
85
- 85 const arrayMatch = fieldname.match(/^(.+)\[\d*\]$/)
86
- 86 if (arrayMatch) {
87
- 87 const baseName = arrayMatch[1]
88
- 88 if (!fields[baseName]) {
89
- 89 fields[baseName] = []
90
- 90 }
91
- 91 if (Array.isArray(fields[baseName])) {
92
- 92 fields[baseName].push(val)
93
- 93 }
94
- 94 } else {
95
- 95 fields[fieldname] = val
96
- 96 }
97
- 97 })
98
- 98
99
- 99 // Handle files
100
- 100 busboy.on('file', (fieldname, file, filename, encoding, mimetype) => {
101
- 101 const chunks = []
102
- 102 let size = 0
103
- 103
104
- 104 const filePromise = new Promise((fileResolve, fileReject) => {
105
- 105 file.on('data', (chunk) => {
106
- 106 chunks.push(chunk)
107
- 107 size += chunk.length
108
- 108 })
109
- 109
110
- 110 file.on('limit', () => {
111
- 111 fileReject(new Error(`File size limit exceeded for field '${fieldname}'`))
112
- 112 })
113
- 113
114
- 114 file.on('end', () => {
115
- 115 files[fieldname] = {
116
- 116 filename,
117
- 117 mimetype,
118
- 118 encoding,
119
- 119 size,
120
- 120 data: Buffer.concat(chunks)
121
- 121 }
122
- 122 fileResolve()
123
- 123 })
124
- 124
125
- 125 file.on('error', fileReject)
126
- 126 })
127
- 127
128
- 128 filePromises.push(filePromise)
129
- 129 })
130
- 130
131
- 131 // Handle completion
132
- 132 busboy.on('finish', async () => {
133
- 133 try {
134
- 134 // Wait for all files to be fully read
135
- 135 await Promise.all(filePromises)
136
- 136 resolve({ fields, files })
137
- 137 } catch (error) {
138
- 138 reject(error)
139
- 139 }
140
- 140 })
141
- 141
142
- 142 // Handle errors
143
- 143 busboy.on('error', (error) => {
144
- 144 reject(error)
145
- 145 })
146
- 146
147
- 147 // Handle limit errors
148
- 148 busboy.on('partsLimit', () => {
149
- 149 reject(new Error('Parts limit exceeded'))
150
- 150 })
151
- 151
152
- 152 busboy.on('filesLimit', () => {
153
- 153 reject(new Error('Files limit exceeded'))
154
- 154 })
155
- 155
156
- 156 busboy.on('fieldsLimit', () => {
157
- 157 reject(new Error('Fields limit exceeded'))
158
- 158 })
159
- 159
160
- 160 // Pipe request to busboy
161
- 161 if (req.pipe) {
162
- 162 req.pipe(busboy)
163
- 163 } else if (req.on) {
164
- 164 // For already buffered requests
165
- 165 const stream = new Readable()
166
- 166 stream.push(req.body || req)
167
- 167 stream.push(null)
168
- 168 stream.pipe(busboy)
169
- 169 } else {
170
- 170 reject(new Error('Request object is not a stream'))
171
- 171 }
172
- 172 })
173
- 173 }
174
- 174 }
175
- 175 }
@@ -1,138 +0,0 @@
1
- 1 /**
2
- 2 * Formidable File Detector for HTTP Multipart Uploads
3
- 3 *
4
- 4 * This detector handles multipart/form-data uploads using the formidable library.
5
- 5 * Unlike busboy, formidable saves files to disk temporarily, which is better
6
- 6 * for large files.
7
- 7 *
8
- 8 * Features:
9
- 9 * - Disk-based storage (better for large files)
10
- 10 * - Automatic temp file cleanup
11
- 11 * - Progress tracking support
12
- 12 * - Built-in file type detection
13
- 13 *
14
- 14 * Usage:
15
- 15 * ```javascript
16
- 16 * import { createFormidableDetector } from 'jsonrestapi/plugins/core/lib/formidable-detector.js';
17
- 17 *
18
- 18 * api.use(ExpressPlugin, {
19
- 19 * fileParser: 'formidable',
20
- 20 * fileParserOptions: {
21
- 21 * uploadDir: './uploads/temp',
22
- 22 * keepExtensions: true,
23
- 23 * maxFileSize: 200 * 1024 * 1024 // 200MB
24
- 24 * }
25
- 25 * });
26
- 26 * ```
27
- 27 */
28
- 28
29
- 29 import { requirePackage } from 'hooked-api'
30
- 30 import { promises as fs } from 'fs'
31
- 31 import path from 'path'
32
- 32
33
- 33 let formidable
34
- 34 try {
35
- 35 formidable = (await import('formidable')).default
36
- 36 } catch (e) {
37
- 37 requirePackage('formidable', 'express/http-connector',
38
- 38 'Formidable is required for multipart/form-data file uploads with disk storage. This is a peer dependency.')
39
- 39 }
40
- 40
41
- 41 /**
42
- 42 * Creates a formidable-based file detector
43
- 43 *
44
- 44 * @param {Object} options - Formidable configuration options
45
- 45 * @param {string} options.uploadDir - Directory for temporary files
46
- 46 * @param {boolean} options.keepExtensions - Keep file extensions
47
- 47 * @param {number} options.maxFileSize - Max file size in bytes
48
- 48 * @param {boolean} options.multiples - Allow multiple files per field
49
- 49 * @returns {Object} Detector object with detect() and parse() methods
50
- 50 */
51
- 51 export function createFormidableDetector (options = {}) {
52
- 52 // Set default upload directory
53
- 53 const uploadDir = options.uploadDir || path.join(process.cwd(), 'uploads', 'temp')
54
- 54
55
- 55 return {
56
- 56 name: 'formidable-multipart',
57
- 57
58
- 58 /**
59
- 59 * Check if this detector can handle the request
60
- 60 * @param {Object} params - Request parameters
61
- 61 * @returns {boolean} True if this is a multipart request
62
- 62 */
63
- 63 detect: (params) => {
64
- 64 const req = params._httpReq || params._expressReq
65
- 65 if (!req || !req.headers) return false
66
- 66
67
- 67 const contentType = req.headers['content-type'] || ''
68
- 68 return contentType.includes('multipart/form-data')
69
- 69 },
70
- 70
71
- 71 /**
72
- 72 * Parse multipart data from the request
73
- 73 * @param {Object} params - Request parameters
74
- 74 * @returns {Promise<{fields: Object, files: Object}>} Parsed data
75
- 75 */
76
- 76 parse: async (params) => {
77
- 77 const req = params._httpReq || params._expressReq
78
- 78
79
- 79 // Ensure upload directory exists
80
- 80 await fs.mkdir(uploadDir, { recursive: true })
81
- 81
82
- 82 // Create form parser
83
- 83 const form = formidable({
84
- 84 uploadDir,
85
- 85 keepExtensions: true,
86
- 86 ...options
87
- 87 })
88
- 88
89
- 89 // Parse the request
90
- 90 const [fields, fileUploads] = await form.parse(req)
91
- 91
92
- 92 // Normalize formidable's file format to our standard format
93
- 93 const files = {}
94
- 94
95
- 95 for (const [fieldname, uploadedFiles] of Object.entries(fileUploads)) {
96
- 96 // Formidable returns arrays for multiple files
97
- 97 const fileArray = Array.isArray(uploadedFiles) ? uploadedFiles : [uploadedFiles]
98
- 98
99
- 99 // For now, just take the first file (TODO: handle multiple files per field)
100
- 100 const file = fileArray[0]
101
- 101
102
- 102 if (file) {
103
- 103 // Read file data into memory (for small files)
104
- 104 // For large files, you might want to keep them on disk
105
- 105 const data = await fs.readFile(file.filepath)
106
- 106
107
- 107 files[fieldname] = {
108
- 108 filename: file.originalFilename || 'unknown',
109
- 109 mimetype: file.mimetype || 'application/octet-stream',
110
- 110 size: file.size,
111
- 111 data,
112
- 112 filepath: file.filepath, // Keep for reference
113
- 113
114
- 114 // Cleanup function to remove temp file
115
- 115 cleanup: async () => {
116
- 116 try {
117
- 117 await fs.unlink(file.filepath)
118
- 118 } catch (error) {
119
- 119 // File might already be deleted, ignore
120
- 120 if (error.code !== 'ENOENT') {
121
- 121 throw error
122
- 122 }
123
- 123 }
124
- 124 }
125
- 125 }
126
- 126 }
127
- 127 }
128
- 128
129
- 129 // Normalize fields (formidable returns arrays for repeated fields)
130
- 130 const normalizedFields = {}
131
- 131 for (const [key, value] of Object.entries(fields)) {
132
- 132 normalizedFields[key] = Array.isArray(value) && value.length === 1 ? value[0] : value
133
- 133 }
134
- 134
135
- 135 return { fields: normalizedFields, files }
136
- 136 }
137
- 137 }
138
- 138 }
@@ -1,222 +0,0 @@
1
- 1 /**
2
- 2 * Helper functions for HTTP request context creation
3
- 3 * Used by both HTTP and Express plugins
4
- 4 */
5
- 5
6
- 6 /**
7
- 7 * Extract the real client IP address, handling proxies
8
- 8 * @param {Object} req - The request object (Node.js http.IncomingMessage or Express Request)
9
- 9 * @returns {string} The client IP address
10
- 10 */
11
- 11 export function getClientIP (req) {
12
- 12 // Check X-Forwarded-For header (comma-separated list, first is original client)
13
- 13 const xForwardedFor = req.headers['x-forwarded-for']
14
- 14 if (xForwardedFor) {
15
- 15 return xForwardedFor.split(',')[0].trim()
16
- 16 }
17
- 17
18
- 18 // Check X-Real-IP header (single IP)
19
- 19 const xRealIP = req.headers['x-real-ip']
20
- 20 if (xRealIP) {
21
- 21 return xRealIP
22
- 22 }
23
- 23
24
- 24 // Check Cloudflare's CF-Connecting-IP
25
- 25 const cfConnectingIP = req.headers['cf-connecting-ip']
26
- 26 if (cfConnectingIP) {
27
- 27 return cfConnectingIP
28
- 28 }
29
- 29
30
- 30 // Fall back to direct connection
31
- 31 return req.connection?.remoteAddress ||
32
- 32 req.socket?.remoteAddress ||
33
- 33 req.ip || // Express specific
34
- 34 'unknown'
35
- 35 }
36
- 36
37
- 37 /**
38
- 38 * Check if the request is using HTTPS
39
- 39 * @param {Object} req - The request object
40
- 40 * @returns {boolean} True if the request is secure
41
- 41 */
42
- 42 export function isSecure (req) {
43
- 43 // Express-specific property
44
- 44 if (req.secure !== undefined) {
45
- 45 return req.secure
46
- 46 }
47
- 47
48
- 48 // Check X-Forwarded-Proto header (used by proxies)
49
- 49 const xForwardedProto = req.headers['x-forwarded-proto']
50
- 50 if (xForwardedProto) {
51
- 51 return xForwardedProto === 'https'
52
- 52 }
53
- 53
54
- 54 // Check if connection is encrypted (Node.js)
55
- 55 if (req.connection?.encrypted) {
56
- 56 return true
57
- 57 }
58
- 58
59
- 59 // Check protocol directly
60
- 60 if (req.protocol === 'https') {
61
- 61 return true
62
- 62 }
63
- 63
64
- 64 // Default to false
65
- 65 return false
66
- 66 }
67
- 67
68
- 68 /**
69
- 69 * Get the hostname from the request
70
- 70 * @param {Object} req - The request object
71
- 71 * @returns {string} The hostname
72
- 72 */
73
- 73 export function getHostname (req) {
74
- 74 // Express-specific property
75
- 75 if (req.hostname) {
76
- 76 return req.hostname
77
- 77 }
78
- 78
79
- 79 // Check Host header
80
- 80 const hostHeader = req.headers.host
81
- 81 if (hostHeader) {
82
- 82 // Remove port if present
83
- 83 const colonIndex = hostHeader.indexOf(':')
84
- 84 return colonIndex === -1 ? hostHeader : hostHeader.substring(0, colonIndex)
85
- 85 }
86
- 86
87
- 87 // Fall back
88
- 88 return 'localhost'
89
- 89 }
90
- 90
91
- 91 /**
92
- 92 * Get the port from the request
93
- 93 * @param {Object} req - The request object
94
- 94 * @returns {number|null} The port number or null
95
- 95 */
96
- 96 export function getPort (req) {
97
- 97 // Check Host header for port
98
- 98 const hostHeader = req.headers.host
99
- 99 if (hostHeader) {
100
- 100 const colonIndex = hostHeader.indexOf(':')
101
- 101 if (colonIndex !== -1) {
102
- 102 const port = parseInt(hostHeader.substring(colonIndex + 1), 10)
103
- 103 if (!isNaN(port)) {
104
- 104 return port
105
- 105 }
106
- 106 }
107
- 107 }
108
- 108
109
- 109 // Check connection local port
110
- 110 if (req.connection?.localPort) {
111
- 111 return req.connection.localPort
112
- 112 }
113
- 113
114
- 114 if (req.socket?.localPort) {
115
- 115 return req.socket.localPort
116
- 116 }
117
- 117
118
- 118 // Default ports based on protocol
119
- 119 return isSecure(req) ? 443 : 80
120
- 120 }
121
- 121
122
- 122 /**
123
- 123 * Parse cookies from cookie header string
124
- 124 * @param {string} cookieString - The cookie header value
125
- 125 * @returns {Object} Parsed cookies as key-value pairs
126
- 126 */
127
- 127 export function parseCookies (cookieString) {
128
- 128 const cookies = {}
129
- 129
130
- 130 if (!cookieString || typeof cookieString !== 'string') {
131
- 131 return cookies
132
- 132 }
133
- 133
134
- 134 // Split by semicolons and parse each cookie
135
- 135 cookieString.split(';').forEach(cookie => {
136
- 136 const trimmedCookie = cookie.trim()
137
- 137 if (trimmedCookie) {
138
- 138 const eqIndex = trimmedCookie.indexOf('=')
139
- 139 if (eqIndex !== -1) {
140
- 140 const name = trimmedCookie.substring(0, eqIndex).trim()
141
- 141 const value = trimmedCookie.substring(eqIndex + 1).trim()
142
- 142 if (name) {
143
- 143 // Remove quotes if present
144
- 144 cookies[name] = value.replace(/^"(.*)"$/, '$1')
145
- 145 }
146
- 146 }
147
- 147 }
148
- 148 })
149
- 149
150
- 150 return cookies
151
- 151 }
152
- 152
153
- 153 /**
154
- 154 * Extract token from request based on common patterns
155
- 155 * @param {Object} req - The request object
156
- 156 * @returns {string|null} The extracted token or null
157
- 157 */
158
- 158 export function extractToken (req) {
159
- 159 // Check Authorization header for Bearer token
160
- 160 const authHeader = req.headers.authorization || req.headers.Authorization
161
- 161 if (authHeader && typeof authHeader === 'string') {
162
- 162 const bearerMatch = authHeader.match(/^Bearer\s+(.+)$/i)
163
- 163 if (bearerMatch) {
164
- 164 return bearerMatch[1]
165
- 165 }
166
- 166 }
167
- 167
168
- 168 // Check cookies for session token (Auth.js pattern)
169
- 169 const cookies = parseCookies(req.headers.cookie || '')
170
- 170 if (cookies['__Secure-authjs.session-token']) {
171
- 171 return cookies['__Secure-authjs.session-token']
172
- 172 }
173
- 173 if (cookies['authjs.session-token']) {
174
- 174 return cookies['authjs.session-token']
175
- 175 }
176
- 176
177
- 177 return null
178
- 178 }
179
- 179
180
- 180 /**
181
- 181 * Create a context object for the request
182
- 182 * @param {Object} req - The request object
183
- 183 * @param {Object} res - The response object
184
- 184 * @param {string} source - The source plugin ('http' or 'express')
185
- 185 * @returns {Object} The context object
186
- 186 */
187
- 187 export function createContext (req, res, source) {
188
- 188 const context = {
189
- 189 source,
190
- 190 auth: {
191
- 191 userId: null,
192
- 192 claims: null
193
- 193 },
194
- 194 request: {
195
- 195 ip: getClientIP(req),
196
- 196 userAgent: req.headers['user-agent'] || null,
197
- 197 protocol: isSecure(req) ? 'https' : 'http',
198
- 198 hostname: getHostname(req),
199
- 199 port: getPort(req),
200
- 200 method: req.method,
201
- 201 path: req.url || req.path,
202
- 202 headers: req.headers,
203
- 203 cookies: parseCookies(req.headers.cookie || ''),
204
- 204 token: extractToken(req)
205
- 205 },
206
- 206 raw: { req, res },
207
- 207 handled: false,
208
- 208 rejection: null
209
- 209 }
210
- 210
211
- 211 // Add reject function
212
- 212 context.reject = (status, message, details = {}) => {
213
- 213 context.handled = true
214
- 214 context.rejection = {
215
- 215 status,
216
- 216 message,
217
- 217 ...details
218
- 218 }
219
- 219 }
220
- 220
221
- 221 return context
222
- 222 }