json-rest-api 1.0.28 → 2.0.0

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 (289) hide show
  1. package/LICENSE +10 -670
  2. package/LICENSE-GPL-3.0 +674 -0
  3. package/LICENSE-MIT +21 -0
  4. package/README.md +39 -30
  5. package/docs/API.md +746 -1273
  6. package/docs/GUIDE/01-getting-started.md +338 -0
  7. package/docs/GUIDE/02-resources-and-relationships.md +33 -0
  8. package/docs/GUIDE/03-running-example.md +87 -0
  9. package/docs/GUIDE/04-creating-and-querying.md +181 -0
  10. package/docs/GUIDE/05-belongs-to.md +177 -0
  11. package/docs/GUIDE/06-has-many.md +184 -0
  12. package/docs/GUIDE/07-polymorphic-relationships.md +191 -0
  13. package/docs/GUIDE/08-many-to-many.md +184 -0
  14. package/docs/GUIDE/09-pagination-and-sorting.md +149 -0
  15. package/docs/GUIDE/10-put-and-patch.md +174 -0
  16. package/docs/GUIDE/11-relationship-endpoints.md +174 -0
  17. package/docs/GUIDE/12-field-transformations.md +287 -0
  18. package/docs/GUIDE/13-hooks-and-lifecycle.md +479 -0
  19. package/docs/GUIDE/14-custom-resource-methods.md +125 -0
  20. package/docs/GUIDE/15-query-projections.md +266 -0
  21. package/docs/GUIDE/{GUIDE_X_Autofiltering.md → 16-autofiltering.md} +77 -15
  22. package/docs/GUIDE/{GUIDE_X_Row_Policies.md → 17-row-policies.md} +137 -44
  23. package/docs/GUIDE/18-labels.md +86 -0
  24. package/docs/GUIDE/19-managed-transactions.md +179 -0
  25. package/docs/GUIDE/20-transaction-outcomes.md +207 -0
  26. package/docs/GUIDE/21-schema-and-migrations.md +634 -0
  27. package/docs/GUIDE/22-version-field-migrations.md +208 -0
  28. package/docs/GUIDE/23-fastify.md +179 -0
  29. package/docs/GUIDE/24-cors.md +172 -0
  30. package/docs/GUIDE/{GUIDE_X_URL_Management.md → 25-url-management.md} +7 -1
  31. package/docs/GUIDE/26-file-uploads.md +278 -0
  32. package/docs/GUIDE/27-bulk-operations.md +277 -0
  33. package/docs/GUIDE/28-socketio.md +325 -0
  34. package/docs/GUIDE/29-writing-plugins.md +278 -0
  35. package/docs/GUIDE/30-backend-capabilities.md +100 -0
  36. package/docs/GUIDE/31-positioning.md +181 -0
  37. package/docs/GUIDE/{GUIDE_Y_Appendices.md → 32-date-and-time.md} +90 -31
  38. package/docs/GUIDE/33-migrating-to-v2.md +3827 -0
  39. package/docs/GUIDE/index.md +51 -59
  40. package/docs/QUICKSTART.md +120 -287
  41. package/docs/architecture.md +75 -0
  42. package/docs/contributing.md +178 -0
  43. package/index.d.ts +36 -0
  44. package/index.js +1 -22
  45. package/lib/enhanced-logger.js +91 -85
  46. package/lib/error-context.js +445 -5
  47. package/lib/error-formatter.js +219 -59
  48. package/lib/knex-transaction.js +64 -0
  49. package/lib/logger-types.d.ts +22 -0
  50. package/lib/missing-package.js +4 -0
  51. package/lib/rest-api-errors.js +103 -8
  52. package/lib/runtime/json-rest-api.js +172 -0
  53. package/lib/transaction-types.d.ts +59 -0
  54. package/package.json +97 -27
  55. package/plugins/core/bulk-operations-plugin.js +191 -184
  56. package/plugins/core/connectors/express-plugin.js +148 -219
  57. package/plugins/core/connectors/fastify-plugin.js +125 -82
  58. package/plugins/core/connectors/lib/busboy-detector.js +74 -156
  59. package/plugins/core/connectors/lib/connector-core.js +144 -20
  60. package/plugins/core/connectors/lib/formidable-detector.js +63 -120
  61. package/plugins/core/connectors/lib/http-validators.js +77 -0
  62. package/plugins/core/connectors/lib/multipart-helpers.js +18 -0
  63. package/plugins/core/connectors/lib/request-helpers.js +10 -1
  64. package/plugins/core/connectors/lib/transport-http-helpers.js +89 -8
  65. package/plugins/core/connectors/lib/transport-route-schemas.js +7 -4
  66. package/plugins/core/file-handling-plugin.js +87 -174
  67. package/plugins/core/lib/anyapi/anyapi-registry.js +213 -164
  68. package/plugins/core/lib/anyapi/canonical-link-store.js +507 -0
  69. package/plugins/core/lib/anyapi/canonical-relationship-reader.js +598 -0
  70. package/plugins/core/lib/anyapi/schema-utils.js +25 -16
  71. package/plugins/core/lib/anyapi/utils/descriptor-helpers.js +0 -64
  72. package/plugins/core/lib/dbIntrospection.js +228 -21
  73. package/plugins/core/lib/dbTablesOperations.js +409 -92
  74. package/plugins/core/lib/fractional-positioning.js +12 -48
  75. package/plugins/core/lib/querying/identifier-query.js +19 -0
  76. package/plugins/core/lib/querying/include-query-helpers.js +364 -0
  77. package/plugins/core/lib/querying/include-to-many.js +428 -0
  78. package/plugins/core/lib/querying/include-to-one.js +579 -0
  79. package/plugins/core/lib/querying/knex-cross-table-search.js +31 -307
  80. package/plugins/core/lib/querying/knex-json-api-transformers-querying.js +99 -401
  81. package/plugins/core/lib/querying/knex-pagination-helpers.js +67 -330
  82. package/plugins/core/lib/querying/knex-process-includes.js +5 -110
  83. package/plugins/core/lib/querying/knex-query-helpers-base.js +21 -56
  84. package/plugins/core/lib/querying/knex-query-helpers.js +298 -448
  85. package/plugins/core/lib/querying/knex-relationship-includes.js +120 -2138
  86. package/plugins/core/lib/querying/knex-window-queries.js +62 -253
  87. package/plugins/core/lib/querying/query-builder-utils.js +44 -4
  88. package/plugins/core/lib/querying/query-constraint.d.ts +12 -0
  89. package/plugins/core/lib/querying/query-constraint.js +27 -0
  90. package/plugins/core/lib/querying/query-field-sort-helpers.js +205 -30
  91. package/plugins/core/lib/querying/relationship-identifiers.js +208 -0
  92. package/plugins/core/lib/querying/sort-helpers.js +20 -1
  93. package/plugins/core/lib/querying/storage-adapter-utils.js +13 -5
  94. package/plugins/core/lib/querying/url-helpers.js +11 -4
  95. package/plugins/core/lib/querying-writing/compile-schemas.js +148 -249
  96. package/plugins/core/lib/querying-writing/connectors-query-parser.js +33 -118
  97. package/plugins/core/lib/querying-writing/database-capabilities.js +149 -223
  98. package/plugins/core/lib/querying-writing/database-value-normalizers.js +238 -187
  99. package/plugins/core/lib/querying-writing/default-data-helpers.js +15 -45
  100. package/plugins/core/lib/querying-writing/field-selection-types.d.ts +43 -0
  101. package/plugins/core/lib/querying-writing/field-utils.js +113 -92
  102. package/plugins/core/lib/querying-writing/knex-constants.d.ts +18 -0
  103. package/plugins/core/lib/querying-writing/knex-constants.js +2 -26
  104. package/plugins/core/lib/querying-writing/knex-field-helpers.js +111 -287
  105. package/plugins/core/lib/querying-writing/knex-json-api-transformers.js +31 -120
  106. package/plugins/core/lib/querying-writing/query-field-helpers.js +108 -32
  107. package/plugins/core/lib/querying-writing/query-field-types.d.ts +60 -0
  108. package/plugins/core/lib/querying-writing/relationship-contracts.js +33 -13
  109. package/plugins/core/lib/querying-writing/request-contracts.js +109 -44
  110. package/plugins/core/lib/querying-writing/resource-id-normalization.js +23 -5
  111. package/plugins/core/lib/querying-writing/response-options.js +35 -0
  112. package/plugins/core/lib/querying-writing/schema-helpers.js +203 -324
  113. package/plugins/core/lib/querying-writing/scope-validations.js +92 -290
  114. package/plugins/core/lib/querying-writing/simplified-helpers.js +113 -712
  115. package/plugins/core/lib/storage/canonical-data-helpers.js +683 -0
  116. package/plugins/core/lib/storage/canonical-storage-mapping.js +51 -20
  117. package/plugins/core/lib/storage/ordinary-data-helpers.js +710 -0
  118. package/plugins/core/lib/storage/storage-adapter.js +142 -62
  119. package/plugins/core/lib/storage/storage-mapping.js +78 -28
  120. package/plugins/core/lib/storage/storage-types.d.ts +502 -0
  121. package/plugins/core/lib/writing/many-to-many-manipulations.js +114 -284
  122. package/plugins/core/lib/writing/relationship-processor.js +94 -158
  123. package/plugins/core/lib/writing/resource-version.js +172 -0
  124. package/plugins/core/lib/writing/reverse-relationship-manipulations.js +110 -0
  125. package/plugins/core/rest-api-anyapi-knex-plugin.js +116 -2509
  126. package/plugins/core/rest-api-autofilter-plugin.js +17 -19
  127. package/plugins/core/rest-api-cors-plugin.js +52 -205
  128. package/plugins/core/rest-api-knex-plugin.js +45 -1069
  129. package/plugins/core/rest-api-label-plugin.js +25 -38
  130. package/plugins/core/rest-api-plugin-hooks/register-relationship-routes.js +5 -5
  131. package/plugins/core/rest-api-plugin-hooks/register-scope-routes.js +9 -6
  132. package/plugins/core/rest-api-plugin-hooks/turn-scope-init-into-vars.js +7 -26
  133. package/plugins/core/rest-api-plugin-hooks/validate-include-configurations.js +20 -26
  134. package/plugins/core/rest-api-plugin-methods/add-route.js +4 -6
  135. package/plugins/core/rest-api-plugin-methods/apply-query-filters.js +42 -13
  136. package/plugins/core/rest-api-plugin-methods/check-permissions.js +11 -5
  137. package/plugins/core/rest-api-plugin-methods/common.js +291 -385
  138. package/plugins/core/rest-api-plugin-methods/delete-relationship.js +49 -47
  139. package/plugins/core/rest-api-plugin-methods/delete.js +31 -25
  140. package/plugins/core/rest-api-plugin-methods/enrich-attributes.js +108 -145
  141. package/plugins/core/rest-api-plugin-methods/get-related.js +113 -305
  142. package/plugins/core/rest-api-plugin-methods/get-relationship.js +25 -18
  143. package/plugins/core/rest-api-plugin-methods/get.js +38 -69
  144. package/plugins/core/rest-api-plugin-methods/lifecycle-types.d.ts +225 -0
  145. package/plugins/core/rest-api-plugin-methods/patch-relationship.js +28 -25
  146. package/plugins/core/rest-api-plugin-methods/patch.js +46 -99
  147. package/plugins/core/rest-api-plugin-methods/post-relationship.js +49 -53
  148. package/plugins/core/rest-api-plugin-methods/post.js +51 -51
  149. package/plugins/core/rest-api-plugin-methods/put.js +69 -148
  150. package/plugins/core/rest-api-plugin-methods/query.js +39 -82
  151. package/plugins/core/rest-api-plugin-methods/release.js +2 -1
  152. package/plugins/core/rest-api-plugin.js +32 -46
  153. package/plugins/core/rest-api-positioning-plugin.js +183 -628
  154. package/plugins/core/rest-api-query-projections-plugin.js +19 -102
  155. package/plugins/core/rest-api-row-policy-plugin.js +3 -3
  156. package/plugins/core/socketio-plugin.js +364 -344
  157. package/plugins/storage/local-storage.js +40 -36
  158. package/types/autofilter.d.ts +57 -0
  159. package/types/bulk-methods.d.ts +63 -0
  160. package/types/errors.d.ts +118 -0
  161. package/types/fastify-options.d.ts +19 -0
  162. package/types/file-detectors.d.ts +33 -0
  163. package/types/file-storage.d.ts +48 -0
  164. package/types/hook-context.d.ts +180 -0
  165. package/types/plugin-options.d.ts +53 -0
  166. package/types/relationship-methods.d.ts +74 -0
  167. package/types/representations.d.ts +50 -0
  168. package/types/resource-methods.d.ts +95 -0
  169. package/types/resource-schema.d.ts +157 -0
  170. package/types/row-policy.d.ts +31 -0
  171. package/types/runtime.d.ts +132 -0
  172. package/types/transactions.d.ts +21 -0
  173. package/.claude/agents/library-guide-expert.md +0 -30
  174. package/.claude/agents/rest-api-internals-expert.md +0 -54
  175. package/.claude/settings.local.json +0 -70
  176. package/AGENTS.md +0 -34
  177. package/CLAUDE.md +0 -160
  178. package/CRUSH.md +0 -55
  179. package/JSKIT_BACKEND_MIGRATION_TRACKER.txt +0 -180
  180. package/SINGLE_VALIDATION_CONTRACT_TODO.txt +0 -172
  181. package/TODO.todo +0 -13
  182. package/connectors_ideas/fastify.js +0 -217
  183. package/connectors_ideas/koa-plugin.js +0 -112
  184. package/docs/.bundle/config +0 -2
  185. package/docs/COMPARISON.md +0 -433
  186. package/docs/GUIDE/GUIDE_1_Initial_Setup.md +0 -1057
  187. package/docs/GUIDE/GUIDE_2_1_The_Starting_Point.md +0 -88
  188. package/docs/GUIDE/GUIDE_2_2_Manipulating_And_Searching_Tables.md +0 -808
  189. package/docs/GUIDE/GUIDE_2_3_BelongsTo_Relationships.md +0 -491
  190. package/docs/GUIDE/GUIDE_2_4_HasMany_Records.md +0 -398
  191. package/docs/GUIDE/GUIDE_2_5_HasMany_Polymorphic.md +0 -898
  192. package/docs/GUIDE/GUIDE_2_6_Many_To_Many.md +0 -712
  193. package/docs/GUIDE/GUIDE_2_7_Pagination_And_Ordering.md +0 -1238
  194. package/docs/GUIDE/GUIDE_2_8_Effects_of_PUT_and_PATCH.md +0 -748
  195. package/docs/GUIDE/GUIDE_2_9_Relationships_Urls.md +0 -517
  196. package/docs/GUIDE/GUIDE_2_Data_And_Relations.md +0 -66
  197. package/docs/GUIDE/GUIDE_3_Field_Transformations.md +0 -1252
  198. package/docs/GUIDE/GUIDE_5_Non-Db_Resources.md +0 -1107
  199. package/docs/GUIDE/GUIDE_6_Positioning.md +0 -692
  200. package/docs/GUIDE/GUIDE_7_Hooks_Data_Management_And_Plugins.md +0 -1491
  201. package/docs/GUIDE/GUIDE_X_Bulk_Operations.md +0 -732
  202. package/docs/GUIDE/GUIDE_X_Cors.md +0 -1957
  203. package/docs/GUIDE/GUIDE_X_Fastify.md +0 -78
  204. package/docs/GUIDE/GUIDE_X_File_Uploads.md +0 -729
  205. package/docs/GUIDE/GUIDE_X_Knex_Schema_And_Migrations.md +0 -370
  206. package/docs/GUIDE/GUIDE_X_Query_Projections.md +0 -159
  207. package/docs/GUIDE/GUIDE_X_SocketIO.md +0 -1040
  208. package/docs/GUIDE/GUIDE_X_Writing_Plugins.md +0 -201
  209. package/docs/Gemfile +0 -6
  210. package/docs/Gemfile.lock +0 -308
  211. package/docs/ONBOARDING.md +0 -315
  212. package/docs/_config.yml +0 -36
  213. package/docs/_layouts/default.html +0 -123
  214. package/docs/apple-touch-icon.svg +0 -4
  215. package/docs/assets/css/style.scss +0 -392
  216. package/docs/favicon.svg +0 -4
  217. package/docs/index.md +0 -164
  218. package/docs/mini-guides/file-uploads-complete.md +0 -161
  219. package/eslint.config.js +0 -13
  220. package/examples/url-override-example.js +0 -137
  221. package/fixing_plan.md +0 -112
  222. package/plugins/core/lib/anyapi/query/anyapi-query-adapter.js +0 -657
  223. package/plugins/core/lib/querying-writing/return-record-settings.js +0 -47
  224. package/plugins/core/lib/writing/knex-json-api-transformers-writing.js +0 -110
  225. package/plugins/storage/s3-storage.js +0 -93
  226. package/quickTest.js +0 -136
  227. package/scripts/debug-anyapi-review-filter.mjs +0 -63
  228. package/scripts/inspect-anyapi-descriptor-country.mjs +0 -19
  229. package/scripts/inspect-anyapi-descriptor.mjs +0 -19
  230. package/scripts/run-docs.js +0 -133
  231. package/test-debug.js +0 -59
  232. package/tests/README.md +0 -202
  233. package/tests/TEST_TEMPLATE.test.js +0 -178
  234. package/tests/anyapi-basic.test.js +0 -105
  235. package/tests/anyapi-cursor-pagination.test.js +0 -150
  236. package/tests/anyapi-custom-idproperty.test.js +0 -163
  237. package/tests/autofilter.test.js +0 -463
  238. package/tests/bulk-operations.test.js +0 -657
  239. package/tests/computed-fields.test.js +0 -586
  240. package/tests/cors.test.js +0 -834
  241. package/tests/curl.test.js +0 -467
  242. package/tests/custom-id-mysql-returning-fallback.test.js +0 -128
  243. package/tests/custom-id-simplified-post.test.js +0 -75
  244. package/tests/custom-idproperty-relationships.test.js +0 -932
  245. package/tests/db-introspection.test.js +0 -521
  246. package/tests/db-table-operations.test.js +0 -798
  247. package/tests/fastify-plugin.test.js +0 -184
  248. package/tests/field-getters.test.js +0 -370
  249. package/tests/field-setters.test.js +0 -306
  250. package/tests/file-handling.test.js +0 -118
  251. package/tests/fixtures/api-configs.js +0 -2509
  252. package/tests/full-jsonapi-belongsto-linkage.test.js +0 -150
  253. package/tests/helpers/fake-fastify.js +0 -99
  254. package/tests/helpers/storage-mode.js +0 -65
  255. package/tests/helpers/test-utils.js +0 -203
  256. package/tests/http-connectors-parity.test.js +0 -395
  257. package/tests/id-normalization.test.js +0 -444
  258. package/tests/include-depth-validation.test.js +0 -331
  259. package/tests/includes.test.js +0 -427
  260. package/tests/local-storage.test.js +0 -96
  261. package/tests/logical-id-write-validation.test.js +0 -151
  262. package/tests/nested-includes.test.js +0 -617
  263. package/tests/pagination-cursor-multifield.test.js +0 -643
  264. package/tests/pagination-enhanced.test.js +0 -563
  265. package/tests/polymorphic-relationships.test.js +0 -347
  266. package/tests/polymorphic-simple.test.js +0 -148
  267. package/tests/positioning-advanced.test.js +0 -692
  268. package/tests/positioning-extended.test.js +0 -915
  269. package/tests/positioning-multifilter.test.js +0 -699
  270. package/tests/positioning.test.js +0 -560
  271. package/tests/post-commit-hooks.test.js +0 -120
  272. package/tests/projected-fields.test.js +0 -319
  273. package/tests/public-exports.test.js +0 -15
  274. package/tests/put-create-parity.test.js +0 -216
  275. package/tests/queries.test.js +0 -521
  276. package/tests/query-limits.test.js +0 -584
  277. package/tests/relationship-endpoints.test.js +0 -620
  278. package/tests/relationship-include-storage-adapter.test.js +0 -56
  279. package/tests/relationships.test.js +0 -681
  280. package/tests/rest-api.test.js +0 -337
  281. package/tests/return-record-settings.test.js +0 -184
  282. package/tests/row-policy.test.js +0 -489
  283. package/tests/s3-storage.test.js +0 -38
  284. package/tests/searchschema-knex-behavior.test.js +0 -187
  285. package/tests/searchschema-merge.test.js +0 -151
  286. package/tests/socketio.test.js +0 -860
  287. package/tests/storage-mapping.test.js +0 -362
  288. package/tests/test-development-guide.md +0 -370
  289. package/tests/virtual-fields.test.js +0 -404
@@ -1,172 +0,0 @@
1
- Single Validation Contract TODO
2
-
3
- Goal
4
- - Make `json-rest-schema` the single authored validation contract in `json-rest-api`.
5
- - Keep plain JSON Schema only as a derived transport artifact for connectors.
6
- - Remove the split between:
7
- - resource/search validation via `json-rest-schema`
8
- - hand-written JSON:API/query validators in `plugins/core/lib/querying-writing/payload-validators.js`
9
-
10
- Current audit status
11
- - [done] Audit the live validation paths and identify the actual indirection.
12
-
13
- Root cause
14
- - Validation is split across three places:
15
- 1. `plugins/core/lib/querying-writing/compile-schemas.js`
16
- - compiles resource and search schemas with `createSchema(...)`
17
- 2. `plugins/core/lib/querying-writing/payload-validators.js`
18
- - manually validates JSON:API request envelopes and query parameter objects
19
- 3. `plugins/core/connectors/lib/transport-route-schemas.js`
20
- - separately builds connector-facing JSON Schemas from `schemaInfo`
21
- - This duplicates contract meaning across runtime and transport.
22
- - Route methods still call imperative payload validators before calling
23
- `schemaInstance.create()`, `schemaInstance.replace()`, or `schemaInstance.patch()`.
24
- - `setupCommonRequest()` in `plugins/core/rest-api-plugin-methods/common.js`
25
- also repeats some checks that are already transport-contract concerns.
26
-
27
- What the neat end state looks like
28
- - Authored contract:
29
- - `json-rest-schema` only
30
- - Derived transport schema:
31
- - plain JSON Schema, generated from the same compiled contract
32
- - Runtime write validation:
33
- - uses the same compiled contract as Fastify pre-validation
34
- - Remaining imperative checks:
35
- - only stateful/business checks that are not schema concerns
36
- - examples: permission checks, record existence, relationship access, complete-replace semantics
37
-
38
- What should stay imperative
39
- - `validateRelationshipAccess()` in `plugins/core/rest-api-plugin-methods/common.js`
40
- - `validateCompleteReplacePayload()` in `plugins/core/rest-api-plugin-methods/common.js`
41
- - path/body cross-source checks such as URL id vs body id consistency
42
- - permission and data existence checks
43
-
44
- What should stop existing
45
- - `validateGetPayload()`
46
- - `validateQueryPayload()`
47
- - `validatePostPayload()`
48
- - `validatePutPayload()`
49
- - `validatePatchPayload()`
50
- - duplicate route/body/query syntax checks spread across route methods and `setupCommonRequest()`
51
-
52
- Work list
53
-
54
- 1. [done] Define one compiled request-contract surface for resource `get/query/post/put/patch`.
55
- Target files:
56
- - `plugins/core/lib/querying-writing/compile-schemas.js`
57
- - `plugins/core/connectors/lib/transport-route-schemas.js`
58
- Deliverables:
59
- - compile request contracts once per scope
60
- - store them on `scope.vars.schemaInfo` or an adjacent compiled-contract object
61
- - separate:
62
- - write document contracts (`post`, `put`, `patch`)
63
- - query/get contracts (`get`, `query`)
64
- - relationship route contracts
65
- Status:
66
- - resource route contracts now compile once in `request-contracts.js`
67
- - relationship route contracts are still separate and remain pending work
68
-
69
- 2. [done] Replace manual write-document validation with compiled schema contracts.
70
- Target files:
71
- - `plugins/core/lib/querying-writing/payload-validators.js`
72
- - `plugins/core/rest-api-plugin-methods/post.js`
73
- - `plugins/core/rest-api-plugin-methods/put.js`
74
- - `plugins/core/rest-api-plugin-methods/patch.js`
75
- Deliverables:
76
- - one compiled JSON:API document contract per write mode
77
- - `data.type` constrained to the scope name inside the contract
78
- - `data.id` required/optional according to mode
79
- - `attributes` contract derived from `schemaInstance.toJsonSchema({ mode })`
80
- - relationship identifier contracts derived from `schemaRelationships` and `belongsTo` metadata
81
- - `included` rules expressed once in the contract rather than repeated manually
82
-
83
- 3. [done] Replace manual GET/query parameter validation with compiled schema contracts.
84
- Target files:
85
- - `plugins/core/lib/querying-writing/payload-validators.js`
86
- - `plugins/core/rest-api-plugin-methods/get.js`
87
- - `plugins/core/rest-api-plugin-methods/query.js`
88
- Deliverables:
89
- - a compiled `get` contract for:
90
- - `id`
91
- - `queryParams.include`
92
- - `queryParams.fields`
93
- - a compiled `query` contract for:
94
- - `include`
95
- - `fields`
96
- - `filters`
97
- - `sort`
98
- - `page`
99
- - include-depth validation and sortable-field restrictions represented in the compiled contract, not duplicated in ad hoc code
100
- Notes:
101
- - `searchSchemaInstance` should remain the value contract for `filters`
102
- - the query contract should validate the envelope/shape, not duplicate filter semantics
103
-
104
- 4. [done] Collapse Fastify pre-validation and in-process validation onto the same source for resource write routes.
105
- Target files:
106
- - `plugins/core/connectors/lib/transport-route-schemas.js`
107
- - `plugins/core/connectors/fastify-plugin.js`
108
- - route methods in `plugins/core/rest-api-plugin-methods/`
109
- Deliverables:
110
- - Fastify consumes transport JSON Schema exported from the compiled request contract
111
- - route methods consume the same compiled contract directly for runtime validation
112
- - no second hand-written envelope contract path
113
-
114
- 5. [done] Shrink `setupCommonRequest()` to non-schema responsibilities.
115
- Target file:
116
- - `plugins/core/rest-api-plugin-methods/common.js`
117
- Deliverables:
118
- - keep simplified-mode conversion, transaction setup, context wiring
119
- - keep stateful/business checks only
120
- - remove syntax/envelope checks that belong in the compiled contract
121
- - keep only cross-input checks that a single schema cannot express cleanly
122
-
123
- 6. [done] Delete `payload-validators.js`.
124
- Target file:
125
- - `plugins/core/lib/querying-writing/payload-validators.js`
126
- Deliverables:
127
- - no route method imports it anymore
128
- - helper logic worth keeping is either:
129
- - absorbed into compiled contracts
130
- - or moved into narrowly named stateful helpers
131
-
132
- 7. [done] Tighten tests around the single-contract flow.
133
- Target areas:
134
- - `tests/fastify-plugin.test.js`
135
- - route method coverage exercised by the existing REST suites
136
- - add one focused contract test suite if needed
137
- Deliverables:
138
- - prove Fastify pre-validation and runtime validation reject the same bad inputs
139
- - cover:
140
- - wrong `data.type`
141
- - missing/invalid `data.id`
142
- - invalid relationships
143
- - invalid query/include/page/sort shapes
144
- - verify there is no drift between transport schema and runtime schema behavior
145
-
146
- 8. [done] Proof cleanup.
147
- Deliverables:
148
- - `rg` proof that route methods no longer import `payload-validators.js`
149
- - `rg` proof that `validateGetPayload`, `validateQueryPayload`, `validatePostPayload`,
150
- `validatePutPayload`, and `validatePatchPayload` are gone
151
- - `npm test`
152
-
153
- Current state
154
- - Resource `get/query/post/put/patch` now use one compiled contract source.
155
- - Fastify transport schemas for those resource routes are derived from the same compiled contract.
156
- - `payload-validators.js` is deleted.
157
- - `npm test` passes.
158
- - The main remaining simplification is the separate relationship-route contract path.
159
-
160
- File map for the next implementation pass
161
- - Remaining relationship-route cleanup:
162
- - `plugins/core/rest-api-plugin-methods/post-relationship.js`
163
- - `plugins/core/rest-api-plugin-methods/patch-relationship.js`
164
- - `plugins/core/rest-api-plugin-methods/delete-relationship.js`
165
- - Transport schema generator to reuse, not duplicate:
166
- - `plugins/core/connectors/lib/transport-route-schemas.js`
167
-
168
- Explicit non-goals
169
- - Do not move permission checks into schemas.
170
- - Do not move DB existence checks into schemas.
171
- - Do not turn `searchSchema` into a generic filter DSL.
172
- - Do not add a compatibility layer that preserves both manual validators and compiled contracts.
package/TODO.todo DELETED
@@ -1,13 +0,0 @@
1
- Potential Enhancements
2
-
3
- 1. Provider Priority: Consider adding a "primary_provider" field to handle users who authenticate through multiple providers.
4
- 2. Field Cleanup: Maybe add a helper to remove provider fields when a user unlinks that provider.
5
- 3. Migration Safety: The automatic column creation via RestApiKnexPlugin is convenient, but you might want provider plugins to check if their
6
- fields already exist before extending.
7
-
8
-
9
- ☐ Delete "Creating Custom Storage Plugins for JSON REST API", make an op plugin instead.
10
- ☐ Improve the look of the CSS, especially black and long code fragments, title fonts
11
- ☐ Check manually that the API is OK
12
- ☐ Check that the guide has full coverage of features, map the missing parts
13
-
@@ -1,217 +0,0 @@
1
- /**
2
- * http-plugin-fastify.js
3
- * This plugin for Hooked API provides a high-performance HTTP layer using the Fastify framework.
4
- * It automatically generates JSON:API compliant RESTful routes and schemas for each scope.
5
- *
6
- * You'll need to install dependencies: npm install fastify
7
- */
8
- import { HookedApiError } from './hooked-api.js'
9
- import { handleFastifyApiError } from './error-handler-fastify.js'
10
-
11
- /**
12
- * **UPGRADED:** Automatically generates a comprehensive Fastify schema object that conforms
13
- * to the full JSON:API specification, including relationships, links, and meta.
14
- * @param {string} scopeName - The name of the resource type (e.g., 'articles').
15
- * @param {object} scopeOptions - The full options object for the scope.
16
- * @returns {object} A complete Fastify schema for all CRUD routes.
17
- */
18
- function _generateFastifySchema (scopeName, scopeOptions = {}) {
19
- const appSchema = scopeOptions.schema || {}
20
- const relationshipsSchema = scopeOptions.relationships || {}
21
-
22
- // --- Reusable Schema Components ---
23
- const metaObject = { type: 'object', additionalProperties: true }
24
- const linksObject = {
25
- type: 'object',
26
- properties: {
27
- self: { type: 'string', format: 'uri-reference' },
28
- related: { type: 'string', format: 'uri-reference' },
29
- }
30
- }
31
- const resourceIdentifierObject = {
32
- type: 'object',
33
- required: ['type', 'id'],
34
- properties: {
35
- type: { type: 'string' },
36
- id: { type: 'string' }
37
- }
38
- }
39
-
40
- // --- Build Attribute Properties from simple schema ---
41
- const attributeProperties = {}
42
- const requiredAttributes = []
43
- for (const [key, value] of Object.entries(appSchema)) {
44
- let jsonSchemaType = 'string'
45
- if (value.type === 'number') jsonSchemaType = 'number'
46
- if (value.type === 'boolean') jsonSchemaType = 'boolean'
47
-
48
- attributeProperties[key] = { type: jsonSchemaType }
49
- if (value.format) attributeProperties[key].format = value.format
50
- if (value.required) requiredAttributes.push(key)
51
- }
52
-
53
- // --- Build Relationship Properties ---
54
- const relationshipProperties = {}
55
- for (const [key, value] of Object.entries(relationshipsSchema)) {
56
- const isToMany = value.type === 'hasMany'
57
- relationshipProperties[key] = {
58
- type: 'object',
59
- properties: {
60
- links: linksObject,
61
- data: isToMany
62
- ? { type: 'array', items: resourceIdentifierObject }
63
- : resourceIdentifierObject
64
- }
65
- }
66
- }
67
-
68
- // --- Define the core Resource Object schema ---
69
- const resourceObject = {
70
- type: 'object',
71
- required: ['type', 'id'],
72
- properties: {
73
- id: { type: 'string' },
74
- type: { type: 'string', const: scopeName },
75
- attributes: {
76
- type: 'object',
77
- properties: attributeProperties
78
- },
79
- relationships: {
80
- type: 'object',
81
- properties: relationshipProperties
82
- },
83
- links: linksObject
84
- }
85
- }
86
-
87
- // --- Define the full top-level JSON:API Document ---
88
- const singleDocument = {
89
- type: 'object',
90
- properties: {
91
- data: resourceObject,
92
- included: { type: 'array', items: { type: 'object' } }, // Generic included for simplicity
93
- links: linksObject,
94
- meta: metaObject
95
- }
96
- }
97
- const collectionDocument = {
98
- type: 'object',
99
- properties: {
100
- data: { type: 'array', items: resourceObject },
101
- included: { type: 'array', items: { type: 'object' } },
102
- links: { ...linksObject, properties: { ...linksObject.properties, next: { type: 'string' }, prev: { type: 'string' } } },
103
- meta: metaObject
104
- }
105
- }
106
-
107
- // --- Build the final schema for each route ---
108
- return {
109
- post: {
110
- body: {
111
- type: 'object',
112
- required: ['data'],
113
- properties: {
114
- data: {
115
- type: 'object',
116
- required: ['type', 'attributes'],
117
- properties: {
118
- type: { type: 'string', const: scopeName },
119
- attributes: {
120
- type: 'object',
121
- required: requiredAttributes,
122
- properties: attributeProperties
123
- },
124
- relationships: {
125
- type: 'object',
126
- properties: relationshipProperties
127
- }
128
- }
129
- }
130
- }
131
- },
132
- response: { 201: singleDocument }
133
- },
134
- get: {
135
- response: { 200: singleDocument }
136
- },
137
- query: {
138
- response: { 200: collectionDocument }
139
- },
140
- patch: {
141
- body: { /* A more complex partial schema would be needed for full validation */ },
142
- response: { 200: singleDocument }
143
- },
144
- put: {
145
- body: { /* Similar to POST body */ },
146
- response: { 200: singleDocument }
147
- }
148
- }
149
- }
150
-
151
- export const HttpFastifyPlugin = {
152
- name: 'http-fastify',
153
-
154
- install (context, api) {
155
- const { pluginOptions, log } = context
156
- const httpOptions = pluginOptions.http || {}
157
- const { app, prefix = '/api' } = httpOptions
158
-
159
- if (!app) {
160
- throw new HookedApiError(
161
- "The 'http-fastify' plugin requires a Fastify 'app' instance to be passed in the options. Example: api.use(HttpFastifyPlugin, { http: { app: myFastifyApp } })",
162
- 'CONFIGURATION_ERROR'
163
- )
164
- }
165
-
166
- log.info(`Fastify HTTP plugin installed. Routes will be prefixed with '${prefix}'.`)
167
-
168
- const _createFastifyHandler = (methodName) => {
169
- return async (request, reply) => {
170
- const { scopeName, id } = request.params
171
- const apiParams = {
172
- id,
173
- inputRecord: request.body,
174
- queryParams: request.query,
175
- }
176
- log.debug(`Fastify request for ${scopeName}.${methodName}`, { id, query: apiParams.queryParams })
177
- const result = await api.scopes[scopeName][methodName](apiParams)
178
- switch (methodName) {
179
- case 'post':
180
- reply.code(201).send(result)
181
- break
182
- case 'delete':
183
- reply.code(204).send()
184
- break
185
- default:
186
- reply.code(200).send(result)
187
- }
188
- }
189
- }
190
-
191
- const _createScopePlugin = (scopeName, scopeOptions) => {
192
- const fastifySchema = scopeOptions.fastifySchema || _generateFastifySchema(scopeName, scopeOptions)
193
-
194
- return async (fastifyInstance) => {
195
- log.info(`Creating Fastify HTTP routes for scope: '${scopeName}'`)
196
- fastifyInstance.get(`/${scopeName}`, { schema: fastifySchema.query }, _createFastifyHandler('query'))
197
- fastifyInstance.get(`/${scopeName}/:id`, { schema: fastifySchema.get }, _createFastifyHandler('get'))
198
- fastifyInstance.post(`/${scopeName}`, { schema: fastifySchema.post }, _createFastifyHandler('post'))
199
- fastifyInstance.put(`/${scopeName}/:id`, { schema: fastifySchema.put }, _createFastifyHandler('put'))
200
- fastifyInstance.patch(`/${scopeName}/:id`, { schema: fastifySchema.patch }, _createFastifyHandler('patch'))
201
- fastifyInstance.delete(`/${scopeName}/:id`, { schema: fastifySchema.delete }, _createFastifyHandler('delete'))
202
- }
203
- }
204
-
205
- const originalAddScope = api.addScope
206
-
207
- api.addScope = (name, options, extras) => {
208
- const result = originalAddScope.call(api, name, options, extras)
209
- app.register(_createScopePlugin(name, options), { prefix })
210
- return result
211
- }
212
-
213
- app.setErrorHandler((error, request, reply) => {
214
- handleFastifyApiError(error, request, reply, log)
215
- })
216
- }
217
- }
@@ -1,112 +0,0 @@
1
- /**
2
- * http-plugin-koa.js
3
- * This plugin for Hooked API provides a lightweight HTTP layer using the Koa.js framework.
4
- * It automatically generates JSON:API compliant RESTful routes for each scope using `koa-router`.
5
- *
6
- * It assumes you have `koa-bodyparser` installed and used before this plugin.
7
- * You'll need to install dependencies: npm install koa koa-router koa-bodyparser qs
8
- */
9
- import KoaRouter from 'koa-router'
10
- import qs from 'qs' // The same query string parser Express uses
11
- import { HookedApiError } from './hooked-api.js'
12
- import { handleKoaApiError } from './error-handler-koa.js'
13
-
14
- export const HttpKoaPlugin = {
15
- name: 'http-koa',
16
-
17
- install (context, api) {
18
- const { pluginOptions, log } = context
19
- const httpOptions = pluginOptions.http || {}
20
- const { app, prefix = '/api' } = httpOptions
21
-
22
- if (!app) {
23
- throw new HookedApiError(
24
- "The 'http-koa' plugin requires a Koa 'app' instance to be passed in the options. Example: api.use(HttpKoaPlugin, { http: { app: myKoaApp } })",
25
- 'CONFIGURATION_ERROR'
26
- )
27
- }
28
-
29
- // Create a router instance for our API routes
30
- const router = new KoaRouter({ prefix })
31
-
32
- /**
33
- * Creates a generic Koa middleware for a specific API method.
34
- * @param {string} methodName - The name of the scope method to call (e.g., 'get', 'query').
35
- * @returns {Function} An async Koa middleware function.
36
- */
37
- const _createKoaHandler = (methodName) => {
38
- return async (ctx) => {
39
- const { scopeName, id } = ctx.params
40
-
41
- try {
42
- // 1. Translate Koa ctx into API method params
43
- const apiParams = {}
44
-
45
- if (['get', 'put', 'patch', 'delete'].includes(methodName)) {
46
- apiParams.id = id
47
- }
48
- if (['post', 'put', 'patch'].includes(methodName)) {
49
- // Assumes koa-bodyparser is used
50
- apiParams.inputRecord = ctx.request.body
51
- }
52
- // Use qs to properly parse nested query objects
53
- apiParams.queryParams = ctx.querystring ? qs.parse(ctx.querystring) : {}
54
-
55
- log.debug(`Koa request for ${scopeName}.${methodName}`, { id, query: apiParams.queryParams })
56
-
57
- // 2. Execute the corresponding API method
58
- const result = await api.scopes[scopeName][methodName](apiParams)
59
-
60
- // 3. Set the successful Koa response
61
- switch (methodName) {
62
- case 'post':
63
- ctx.status = 201
64
- ctx.body = result
65
- break
66
- case 'delete':
67
- ctx.status = 204
68
- // No body for 204
69
- break
70
- default:
71
- ctx.status = 200
72
- ctx.body = result
73
- }
74
- } catch (error) {
75
- // 4. Handle any errors thrown by the API
76
- handleKoaApiError(error, ctx, log)
77
- }
78
- }
79
- }
80
-
81
- /**
82
- * Creates all the standard RESTful routes for a given scope on the router.
83
- * @param {string} scopeName - The name of the scope to create routes for.
84
- */
85
- const _createRoutesForScope = (scopeName) => {
86
- const collectionPath = `/${scopeName}`
87
- const resourcePath = `${collectionPath}/:id`
88
-
89
- log.info(`Creating Koa HTTP routes for scope: '${scopeName}'`)
90
-
91
- router.get(collectionPath, _createKoaHandler('query'))
92
- router.get(resourcePath, _createKoaHandler('get'))
93
- router.post(collectionPath, _createKoaHandler('post'))
94
- router.put(resourcePath, _createKoaHandler('put'))
95
- router.patch(resourcePath, _createKoaHandler('patch'))
96
- router.del(resourcePath, _createKoaHandler('delete')) // .del is koa-router's alias for delete
97
- }
98
-
99
- // --- Main Logic: Wrap `addScope` to automate route creation ---
100
- const originalAddScope = api.addScope
101
-
102
- api.addScope = (name, options, extras) => {
103
- const result = originalAddScope.call(api, name, options, extras)
104
- _createRoutesForScope(name)
105
- return result
106
- }
107
-
108
- // Apply the router middleware to the Koa app
109
- app.use(router.routes()).use(router.allowedMethods())
110
- log.info(`Koa router installed with prefix '${prefix}'.`)
111
- }
112
- }
@@ -1,2 +0,0 @@
1
- ---
2
- BUNDLE_PATH: "vendor/bundle"