json-rest-api 1.0.29 → 2.1.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 (294) 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 +744 -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 +733 -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 +103 -0
  36. package/docs/GUIDE/31-positioning.md +181 -0
  37. package/docs/GUIDE/{GUIDE_Y_Appendices.md → 32-date-and-time.md} +74 -30
  38. package/docs/GUIDE/33-migrating-to-v2.md +3838 -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 +37 -0
  44. package/index.js +3 -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 +70 -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 -177
  67. package/plugins/core/lib/anyapi/anyapi-registry.js +586 -418
  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 +120 -56
  71. package/plugins/core/lib/anyapi/stored-definition.js +109 -0
  72. package/plugins/core/lib/anyapi/utils/descriptor-helpers.js +0 -64
  73. package/plugins/core/lib/dbIntrospection.js +228 -21
  74. package/plugins/core/lib/dbTablesOperations.js +409 -92
  75. package/plugins/core/lib/fractional-positioning.js +12 -48
  76. package/plugins/core/lib/querying/identifier-query.js +19 -0
  77. package/plugins/core/lib/querying/include-query-helpers.js +364 -0
  78. package/plugins/core/lib/querying/include-to-many.js +428 -0
  79. package/plugins/core/lib/querying/include-to-one.js +579 -0
  80. package/plugins/core/lib/querying/knex-cross-table-search.js +31 -307
  81. package/plugins/core/lib/querying/knex-json-api-transformers-querying.js +99 -401
  82. package/plugins/core/lib/querying/knex-pagination-helpers.js +48 -317
  83. package/plugins/core/lib/querying/knex-process-includes.js +5 -110
  84. package/plugins/core/lib/querying/knex-query-helpers-base.js +21 -56
  85. package/plugins/core/lib/querying/knex-query-helpers.js +298 -448
  86. package/plugins/core/lib/querying/knex-relationship-includes.js +120 -2138
  87. package/plugins/core/lib/querying/knex-window-queries.js +62 -253
  88. package/plugins/core/lib/querying/query-builder-utils.js +44 -4
  89. package/plugins/core/lib/querying/query-constraint.d.ts +12 -0
  90. package/plugins/core/lib/querying/query-constraint.js +27 -0
  91. package/plugins/core/lib/querying/query-field-sort-helpers.js +134 -37
  92. package/plugins/core/lib/querying/relationship-identifiers.js +208 -0
  93. package/plugins/core/lib/querying/sort-helpers.js +13 -1
  94. package/plugins/core/lib/querying/storage-adapter-utils.js +13 -5
  95. package/plugins/core/lib/querying/url-helpers.js +11 -4
  96. package/plugins/core/lib/querying-writing/compile-schemas.js +148 -249
  97. package/plugins/core/lib/querying-writing/connectors-query-parser.js +33 -118
  98. package/plugins/core/lib/querying-writing/database-capabilities.js +149 -223
  99. package/plugins/core/lib/querying-writing/database-value-normalizers.js +179 -194
  100. package/plugins/core/lib/querying-writing/default-data-helpers.js +15 -45
  101. package/plugins/core/lib/querying-writing/field-selection-types.d.ts +43 -0
  102. package/plugins/core/lib/querying-writing/field-utils.js +113 -92
  103. package/plugins/core/lib/querying-writing/knex-constants.d.ts +18 -0
  104. package/plugins/core/lib/querying-writing/knex-constants.js +2 -26
  105. package/plugins/core/lib/querying-writing/knex-field-helpers.js +100 -290
  106. package/plugins/core/lib/querying-writing/knex-json-api-transformers.js +31 -120
  107. package/plugins/core/lib/querying-writing/query-field-helpers.js +108 -32
  108. package/plugins/core/lib/querying-writing/query-field-types.d.ts +60 -0
  109. package/plugins/core/lib/querying-writing/relationship-contracts.js +33 -13
  110. package/plugins/core/lib/querying-writing/request-contracts.js +93 -38
  111. package/plugins/core/lib/querying-writing/resource-id-normalization.js +23 -5
  112. package/plugins/core/lib/querying-writing/response-options.js +35 -0
  113. package/plugins/core/lib/querying-writing/schema-helpers.js +203 -324
  114. package/plugins/core/lib/querying-writing/scope-validations.js +92 -290
  115. package/plugins/core/lib/querying-writing/simplified-helpers.js +113 -712
  116. package/plugins/core/lib/storage/canonical-data-helpers.js +683 -0
  117. package/plugins/core/lib/storage/canonical-storage-mapping.js +51 -20
  118. package/plugins/core/lib/storage/ordinary-data-helpers.js +710 -0
  119. package/plugins/core/lib/storage/storage-adapter.js +128 -62
  120. package/plugins/core/lib/storage/storage-mapping.js +78 -28
  121. package/plugins/core/lib/storage/storage-types.d.ts +502 -0
  122. package/plugins/core/lib/writing/many-to-many-manipulations.js +114 -284
  123. package/plugins/core/lib/writing/relationship-processor.js +94 -158
  124. package/plugins/core/lib/writing/resource-version.js +172 -0
  125. package/plugins/core/lib/writing/reverse-relationship-manipulations.js +110 -0
  126. package/plugins/core/rest-api-anyapi-knex-plugin.js +290 -2475
  127. package/plugins/core/rest-api-autofilter-plugin.js +17 -19
  128. package/plugins/core/rest-api-cors-plugin.js +52 -205
  129. package/plugins/core/rest-api-knex-plugin.js +45 -1077
  130. package/plugins/core/rest-api-label-plugin.js +25 -38
  131. package/plugins/core/rest-api-plugin-hooks/register-relationship-routes.js +5 -5
  132. package/plugins/core/rest-api-plugin-hooks/register-scope-routes.js +9 -6
  133. package/plugins/core/rest-api-plugin-hooks/turn-scope-init-into-vars.js +7 -26
  134. package/plugins/core/rest-api-plugin-hooks/validate-include-configurations.js +20 -26
  135. package/plugins/core/rest-api-plugin-methods/add-route.js +4 -6
  136. package/plugins/core/rest-api-plugin-methods/apply-query-filters.js +42 -13
  137. package/plugins/core/rest-api-plugin-methods/check-permissions.js +11 -5
  138. package/plugins/core/rest-api-plugin-methods/common.js +291 -395
  139. package/plugins/core/rest-api-plugin-methods/delete-relationship.js +49 -47
  140. package/plugins/core/rest-api-plugin-methods/delete.js +31 -25
  141. package/plugins/core/rest-api-plugin-methods/enrich-attributes.js +108 -148
  142. package/plugins/core/rest-api-plugin-methods/get-related.js +113 -305
  143. package/plugins/core/rest-api-plugin-methods/get-relationship.js +25 -18
  144. package/plugins/core/rest-api-plugin-methods/get.js +36 -68
  145. package/plugins/core/rest-api-plugin-methods/lifecycle-types.d.ts +225 -0
  146. package/plugins/core/rest-api-plugin-methods/patch-relationship.js +28 -25
  147. package/plugins/core/rest-api-plugin-methods/patch.js +46 -99
  148. package/plugins/core/rest-api-plugin-methods/post-relationship.js +49 -53
  149. package/plugins/core/rest-api-plugin-methods/post.js +51 -51
  150. package/plugins/core/rest-api-plugin-methods/put.js +69 -148
  151. package/plugins/core/rest-api-plugin-methods/query.js +37 -81
  152. package/plugins/core/rest-api-plugin-methods/release.js +2 -1
  153. package/plugins/core/rest-api-plugin.js +32 -46
  154. package/plugins/core/rest-api-positioning-plugin.js +183 -628
  155. package/plugins/core/rest-api-query-projections-plugin.js +19 -102
  156. package/plugins/core/rest-api-row-policy-plugin.js +3 -3
  157. package/plugins/core/socketio-plugin.js +364 -344
  158. package/plugins/storage/local-storage.js +40 -36
  159. package/types/anyapi.d.ts +30 -0
  160. package/types/autofilter.d.ts +57 -0
  161. package/types/bulk-methods.d.ts +63 -0
  162. package/types/errors.d.ts +118 -0
  163. package/types/fastify-options.d.ts +19 -0
  164. package/types/file-detectors.d.ts +33 -0
  165. package/types/file-storage.d.ts +48 -0
  166. package/types/hook-context.d.ts +180 -0
  167. package/types/plugin-options.d.ts +65 -0
  168. package/types/relationship-methods.d.ts +74 -0
  169. package/types/representations.d.ts +50 -0
  170. package/types/resource-methods.d.ts +95 -0
  171. package/types/resource-schema.d.ts +157 -0
  172. package/types/row-policy.d.ts +31 -0
  173. package/types/runtime.d.ts +132 -0
  174. package/types/transactions.d.ts +21 -0
  175. package/.claude/agents/library-guide-expert.md +0 -30
  176. package/.claude/agents/rest-api-internals-expert.md +0 -54
  177. package/.claude/settings.local.json +0 -70
  178. package/AGENTS.md +0 -34
  179. package/CLAUDE.md +0 -160
  180. package/CRUSH.md +0 -55
  181. package/JSKIT_BACKEND_MIGRATION_TRACKER.txt +0 -180
  182. package/SINGLE_VALIDATION_CONTRACT_TODO.txt +0 -172
  183. package/TODO.todo +0 -13
  184. package/connectors_ideas/fastify.js +0 -217
  185. package/connectors_ideas/koa-plugin.js +0 -112
  186. package/docs/.bundle/config +0 -2
  187. package/docs/COMPARISON.md +0 -433
  188. package/docs/GUIDE/GUIDE_1_Initial_Setup.md +0 -1057
  189. package/docs/GUIDE/GUIDE_2_1_The_Starting_Point.md +0 -88
  190. package/docs/GUIDE/GUIDE_2_2_Manipulating_And_Searching_Tables.md +0 -808
  191. package/docs/GUIDE/GUIDE_2_3_BelongsTo_Relationships.md +0 -491
  192. package/docs/GUIDE/GUIDE_2_4_HasMany_Records.md +0 -398
  193. package/docs/GUIDE/GUIDE_2_5_HasMany_Polymorphic.md +0 -898
  194. package/docs/GUIDE/GUIDE_2_6_Many_To_Many.md +0 -712
  195. package/docs/GUIDE/GUIDE_2_7_Pagination_And_Ordering.md +0 -1247
  196. package/docs/GUIDE/GUIDE_2_8_Effects_of_PUT_and_PATCH.md +0 -748
  197. package/docs/GUIDE/GUIDE_2_9_Relationships_Urls.md +0 -517
  198. package/docs/GUIDE/GUIDE_2_Data_And_Relations.md +0 -66
  199. package/docs/GUIDE/GUIDE_3_Field_Transformations.md +0 -1252
  200. package/docs/GUIDE/GUIDE_5_Non-Db_Resources.md +0 -1107
  201. package/docs/GUIDE/GUIDE_6_Positioning.md +0 -692
  202. package/docs/GUIDE/GUIDE_7_Hooks_Data_Management_And_Plugins.md +0 -1491
  203. package/docs/GUIDE/GUIDE_X_Bulk_Operations.md +0 -732
  204. package/docs/GUIDE/GUIDE_X_Cors.md +0 -1957
  205. package/docs/GUIDE/GUIDE_X_Fastify.md +0 -78
  206. package/docs/GUIDE/GUIDE_X_File_Uploads.md +0 -729
  207. package/docs/GUIDE/GUIDE_X_Knex_Schema_And_Migrations.md +0 -370
  208. package/docs/GUIDE/GUIDE_X_Query_Projections.md +0 -159
  209. package/docs/GUIDE/GUIDE_X_SocketIO.md +0 -1040
  210. package/docs/GUIDE/GUIDE_X_Writing_Plugins.md +0 -201
  211. package/docs/Gemfile +0 -6
  212. package/docs/Gemfile.lock +0 -308
  213. package/docs/ONBOARDING.md +0 -315
  214. package/docs/_config.yml +0 -36
  215. package/docs/_layouts/default.html +0 -123
  216. package/docs/apple-touch-icon.svg +0 -4
  217. package/docs/assets/css/style.scss +0 -392
  218. package/docs/favicon.svg +0 -4
  219. package/docs/index.md +0 -164
  220. package/docs/mini-guides/file-uploads-complete.md +0 -161
  221. package/eslint.config.js +0 -13
  222. package/examples/url-override-example.js +0 -137
  223. package/fixing_plan.md +0 -112
  224. package/plugins/core/lib/anyapi/query/anyapi-query-adapter.js +0 -657
  225. package/plugins/core/lib/querying-writing/return-record-settings.js +0 -47
  226. package/plugins/core/lib/writing/knex-json-api-transformers-writing.js +0 -110
  227. package/plugins/storage/s3-storage.js +0 -93
  228. package/quickTest.js +0 -136
  229. package/scripts/debug-anyapi-review-filter.mjs +0 -63
  230. package/scripts/inspect-anyapi-descriptor-country.mjs +0 -19
  231. package/scripts/inspect-anyapi-descriptor.mjs +0 -19
  232. package/scripts/run-docs.js +0 -133
  233. package/test-debug.js +0 -59
  234. package/tests/README.md +0 -202
  235. package/tests/TEST_TEMPLATE.test.js +0 -178
  236. package/tests/anyapi-basic.test.js +0 -105
  237. package/tests/anyapi-cursor-pagination.test.js +0 -148
  238. package/tests/anyapi-custom-idproperty.test.js +0 -163
  239. package/tests/autofilter.test.js +0 -463
  240. package/tests/bulk-operations.test.js +0 -657
  241. package/tests/computed-fields.test.js +0 -586
  242. package/tests/cors.test.js +0 -834
  243. package/tests/curl.test.js +0 -467
  244. package/tests/custom-id-mysql-returning-fallback.test.js +0 -128
  245. package/tests/custom-id-simplified-post.test.js +0 -75
  246. package/tests/custom-idproperty-relationships.test.js +0 -932
  247. package/tests/db-introspection.test.js +0 -521
  248. package/tests/db-table-operations.test.js +0 -798
  249. package/tests/fastify-plugin.test.js +0 -184
  250. package/tests/field-getters.test.js +0 -370
  251. package/tests/field-setters.test.js +0 -306
  252. package/tests/file-handling.test.js +0 -155
  253. package/tests/fixtures/api-configs.js +0 -2566
  254. package/tests/full-jsonapi-belongsto-linkage.test.js +0 -150
  255. package/tests/helpers/fake-fastify.js +0 -99
  256. package/tests/helpers/storage-mode.js +0 -65
  257. package/tests/helpers/test-utils.js +0 -203
  258. package/tests/http-connectors-parity.test.js +0 -395
  259. package/tests/id-normalization.test.js +0 -444
  260. package/tests/include-depth-validation.test.js +0 -331
  261. package/tests/includes.test.js +0 -427
  262. package/tests/local-storage.test.js +0 -96
  263. package/tests/logical-id-write-validation.test.js +0 -151
  264. package/tests/nested-includes.test.js +0 -617
  265. package/tests/pagination-boundaries.test.js +0 -154
  266. package/tests/pagination-contract.test.js +0 -122
  267. package/tests/pagination-cursor-multifield.test.js +0 -641
  268. package/tests/pagination-enhanced.test.js +0 -563
  269. package/tests/polymorphic-relationships.test.js +0 -347
  270. package/tests/polymorphic-simple.test.js +0 -148
  271. package/tests/positioning-advanced.test.js +0 -692
  272. package/tests/positioning-extended.test.js +0 -915
  273. package/tests/positioning-multifilter.test.js +0 -699
  274. package/tests/positioning.test.js +0 -560
  275. package/tests/post-commit-hooks.test.js +0 -120
  276. package/tests/projected-fields.test.js +0 -319
  277. package/tests/public-exports.test.js +0 -15
  278. package/tests/put-create-parity.test.js +0 -216
  279. package/tests/queries.test.js +0 -521
  280. package/tests/query-limits.test.js +0 -584
  281. package/tests/relationship-endpoints.test.js +0 -620
  282. package/tests/relationship-include-storage-adapter.test.js +0 -56
  283. package/tests/relationships.test.js +0 -681
  284. package/tests/rest-api.test.js +0 -337
  285. package/tests/return-record-settings.test.js +0 -184
  286. package/tests/row-policy.test.js +0 -489
  287. package/tests/s3-storage.test.js +0 -38
  288. package/tests/searchschema-knex-behavior.test.js +0 -187
  289. package/tests/searchschema-merge.test.js +0 -151
  290. package/tests/socketio.test.js +0 -860
  291. package/tests/storage-mapping.test.js +0 -362
  292. package/tests/temporal-boundaries.test.js +0 -208
  293. package/tests/test-development-guide.md +0 -370
  294. package/tests/virtual-fields.test.js +0 -404
@@ -0,0 +1,75 @@
1
+ ---
2
+ title: "Architecture"
3
+ ---
4
+
5
+ # Architecture
6
+
7
+ The library exposes resource methods directly and uses connectors to translate
8
+ HTTP requests into those same calls. Storage and transaction rules are shared
9
+ between direct calls and transports.
10
+
11
+ ## Runtime
12
+
13
+ `lib/runtime/json-rest-api.js` contains the API object and its sequential hook list.
14
+ It registers ordinary resource methods, installs plugins and constructs the argument
15
+ object passed to existing operation handlers. There are no proxy-based resource
16
+ objects, generic scope aliases or automatic dispatcher logs.
17
+
18
+ A resource inherits shared methods; its vars and helpers inherit API defaults.
19
+ Local properties override those defaults. The internal operation argument names
20
+ `scope`, `scopes` and `scopeName` refer to resources. The public collection is
21
+ `api.resources`.
22
+
23
+ ## Resource operations and schemas
24
+
25
+ `plugins/core/rest-api-plugin.js` installs CRUD, relationship and transaction methods.
26
+ The operation handlers live in `plugins/core/rest-api-plugin-methods/`. They validate
27
+ requests, run permission/lifecycle hooks and call storage helpers.
28
+
29
+ Writes select plain input with `data` or JSON:API input with `document`.
30
+ `request-contracts.js` validates that selection; plain conversion produces the
31
+ normalized `context.inputRecord` used by the existing write lifecycle. `format`
32
+ controls output only. Both input forms run through the same resource operation,
33
+ including response preparation before transaction completion. Connectors submit
34
+ documents through this same boundary.
35
+
36
+ Hooks deliberately share mutable operation context for caches, added information
37
+ and phase-specific field changes. The caller observes those mutations. See the
38
+ [hook contract](GUIDE/13-hooks-and-lifecycle.md) for wrapper hooks, nested calls,
39
+ writable values and library-owned bookkeeping. Independent calls use separate
40
+ contexts; this is not an immutable-context API.
41
+
42
+ `plugins/core/lib/querying-writing/compile-schemas.js` compiles resource declarations.
43
+ Its neighboring modules own response options, field selection, ID normalization and
44
+ representation conversion. The JSON schema package remains a separate dependency;
45
+ it is unrelated to the removed hooked-api runtime.
46
+
47
+ ## Storage
48
+
49
+ `rest-api-knex-plugin.js` maps resources to ordinary tables.
50
+ `rest-api-anyapi-knex-plugin.js` maps them to canonical records and relationship links.
51
+ Both use native Knex queries and the common storage interfaces under
52
+ `plugins/core/lib/storage/`.
53
+
54
+ Canonical links and relationship reads have separate modules under `lib/anyapi/`
55
+ within the core plugin. Include traversal and to-one/to-many loaders live under
56
+ `plugins/core/lib/querying/`. Keep query ownership and visibility checks in those
57
+ modules rather than duplicating them in connectors.
58
+
59
+ ## Transactions and errors
60
+
61
+ `lib/error-context.js` owns managed transactions, participating operations,
62
+ completion hooks and write outcomes. `lib/knex-transaction.js` handles connection
63
+ completion. Resource methods delegate to these modules.
64
+
65
+ `lib/rest-api-errors.js` defines the local error hierarchy. The runtime preserves
66
+ thrown values; operation-level code attaches transaction outcomes and secondary
67
+ cleanup diagnostics. See the [transaction contract](GUIDE/19-managed-transactions.md)
68
+ and [error outcomes](GUIDE/20-transaction-outcomes.md).
69
+
70
+ ## Transports and files
71
+
72
+ Express and Fastify connectors share parsing, route and response helpers under
73
+ `plugins/core/connectors/lib/`. Socket.IO remains an optional integration.
74
+ File handling owns upload lifetime; storage plugins under `plugins/storage/` own
75
+ file bytes. Their effects participate in the documented completion-hook lifecycle.
@@ -0,0 +1,178 @@
1
+ ---
2
+ title: "Contributing"
3
+ ---
4
+
5
+ # Contributing
6
+
7
+ Use **Node 24** for development and verification. The library requires Node 24 or
8
+ newer; `.nvmrc` pins the version used by this repository and CI. Do not add a
9
+ second Node-version matrix without a concrete compatibility requirement.
10
+
11
+ ## Setup
12
+
13
+ ```sh
14
+ nvm install
15
+ nvm use
16
+ npm ci
17
+ ```
18
+
19
+ SQLite fixtures need no external database or credentials. If the native SQLite
20
+ driver compiles from source, install Python 3, make and a C/C++ compiler.
21
+ Documentation uses Ruby 3.2 and Bundler 2.6.9. Install Bundler and run `npm run docs`;
22
+ the script installs missing gems into `docs/vendor/bundle` before building.
23
+ `npm run docs:dev -- --no-open` serves the site without opening a browser.
24
+
25
+ ## Repository layout
26
+
27
+ | Path | Purpose |
28
+ | --- | --- |
29
+ | `index.js`, `index.d.ts` | Public exports |
30
+ | `lib/` | Runtime, errors, diagnostics and transaction ownership |
31
+ | `plugins/` | Resource operations, storage and optional integrations |
32
+ | `types/` | Public TypeScript contracts |
33
+ | `tests/fixtures/`, `tests/helpers/` | Resource factories and test infrastructure |
34
+ | `tests/*.test.js` | Library regression suites |
35
+ | `examples/` | Maintained runnable examples and data migration utilities |
36
+ | `scripts/` | Documentation, package and verification tools |
37
+ | `docs/GUIDE/` | User guides and the application migration guide |
38
+ | `old/` | Inactive history; excluded from packaging and automated discovery |
39
+
40
+ [Architecture](architecture.md) explains the main execution boundaries.
41
+ `tests/README.md` in a source checkout contains fixture conventions and the command matrix.
42
+
43
+ ## Verification
44
+
45
+ Run focused suites for changed behavior. Reserve the comprehensive gate for
46
+ changes that need it and release preparation:
47
+
48
+ ```sh
49
+ npm run verify
50
+ ```
51
+
52
+ This runs checked JavaScript/types, packed public declarations and imports,
53
+ query budgets, both complete SQLite storage suites, Express 4 checks, lint and
54
+ the documentation build. A storage-specific test may be skipped in one mode
55
+ and executed in the other; inspect both results before calling it untested.
56
+
57
+ `npm run test:clean-package` installs the actual tarball in a fresh temporary
58
+ consumer and checks optional dependencies plus ordinary/canonical CRUD.
59
+ The `test:*guide`, `test:quickstart`, `test:api-reference` and `test:server-examples`
60
+ commands execute maintained documentation examples. Run those when their code changes.
61
+ `npm run test:migration-guide:package` executes the migration guide's selected
62
+ examples against an extracted tarball and reports its hash. It reuses local test
63
+ dependencies; the separate clean-package check verifies fresh installation.
64
+
65
+ Tests and package tools use temporary directories and dispose of their databases,
66
+ servers and sockets. They do not operate on application databases. Consumer checks
67
+ require an explicit path and command; library verification does not establish an
68
+ application migration.
69
+
70
+ ## Resource stress checks
71
+
72
+ For smaller repeatable measurements, `node scripts/measure-write-lifecycles.js`
73
+ records SQL counts and checks child-hook order for bulk and reverse-relationship
74
+ writes. It supports both storage modes and the database runner.
75
+ `node scripts/measure-diagnostics.js` reports bounded logger formatting cost;
76
+ its local timings are observations, not pass/fail performance thresholds.
77
+
78
+ `npm run test:stress` runs a bounded workload in both SQLite storage modes,
79
+ separately from the full suite. It repeats authorized, paginated `tasks` and
80
+ `shared_tasks` relationship reads with polymorphic `subject` includes. The
81
+ fixture contains equal visible, policy-hidden and foreign-workspace groups;
82
+ every response must contain exactly the expected visible records and includes.
83
+
84
+ Four sequential write scenarios check owned and managed commit/rollback,
85
+ persisted values and exact completion-hook counts. SQL is attributed to each
86
+ operation with `AsyncLocalStorage`; fixed per-operation query ceilings and zero
87
+ runtime metadata lookups are enforced. SQL attribution failures fail the run.
88
+
89
+ To save one JSON report directly:
90
+
91
+ ```sh
92
+ node scripts/stress-resources.js > /tmp/json-rest-api-stress-knex.json
93
+ JSON_REST_API_STORAGE=anyapi node scripts/stress-resources.js > /tmp/json-rest-api-stress-anyapi.json
94
+ ```
95
+
96
+ Store reports outside the tracked source tree. The report includes configuration,
97
+ environment, per-scenario query counts, latency p50/p95/p99, throughput and memory
98
+ samples. Timing includes correctness checks; writes include a persisted-state
99
+ read. There are no machine-dependent latency or memory pass thresholds.
100
+
101
+ | Environment variable | Default | Allowed values |
102
+ | --- | ---: | --- |
103
+ | `JSON_REST_API_STRESS_ROWS` | 100 | 1–1000 per visibility group |
104
+ | `JSON_REST_API_STRESS_READS` | 200 | 2–100000 per batch |
105
+ | `JSON_REST_API_STRESS_WRITES` | 32 | 4–20000 per batch, alternating four scenarios |
106
+ | `JSON_REST_API_STRESS_CONCURRENCY` | 4 | 1–32 overlapping read operations |
107
+ | `JSON_REST_API_STRESS_BATCHES` | 1 | 1–100 repetitions against the same fixture |
108
+ | `JSON_REST_API_STRESS_PAGE_SIZE` | 20 | 1–100 |
109
+
110
+ Totals across batches are capped at 100000 reads and 20000 writes. Defaults seed
111
+ 300 linked tasks plus a project and a task used as a polymorphic subject.
112
+ Each independent operation receives fresh context. SQLite uses one in-memory
113
+ connection, so overlapping reads queue their SQL; this does not test concurrent
114
+ SQLite writers. Writes are sequential on every backend.
115
+
116
+ For a longer bounded run with explicit retained-heap sampling:
117
+
118
+ ```sh
119
+ JSON_REST_API_STRESS_BATCHES=10 node --expose-gc scripts/stress-resources.js > /tmp/json-rest-api-stress-soak.json
120
+ ```
121
+
122
+ Without `--expose-gc`, memory samples describe ordinary heap/RSS fluctuations.
123
+ With it, samples additionally record heap retained after requested garbage
124
+ collection. Both are observations; a bounded run cannot establish leak freedom.
125
+ The driver-aware fixture also supports native checks through the existing runner:
126
+
127
+ ```sh
128
+ node scripts/test-databases.js pg scripts/stress-resources.js
129
+ node scripts/test-databases.js mysql2 scripts/stress-resources.js
130
+ ```
131
+
132
+ ## Real databases and Redis
133
+
134
+ The database runner starts disposable servers and creates isolated test databases.
135
+ Supply PostgreSQL's `initdb`/`postgres` and MySQL's `mysqld`, then run:
136
+
137
+ ```sh
138
+ npm run test:databases:pg
139
+ npm run test:databases:mysql
140
+ npm run test:redis
141
+ ```
142
+
143
+ `JSON_REST_API_POSTGRES_BIN` selects the PostgreSQL executable directory; otherwise
144
+ the runner uses `pg_config --bindir`. `JSON_REST_API_MYSQL_BIN` selects the MySQL
145
+ executable; otherwise it uses `mysqld` on PATH. MariaDB does not replace this MySQL
146
+ check. `JSON_REST_API_REDIS_BIN` selects Redis; otherwise the runner uses PATH.
147
+
148
+ For an affected subset, pass test paths directly:
149
+
150
+ ```sh
151
+ node scripts/test-databases.js pg tests/conformance-managed-transactions.test.js
152
+ ```
153
+
154
+ The runner checks both storage modes by default. Select one explicitly when
155
+ rechecking a backend-specific failure:
156
+
157
+ ```sh
158
+ JSON_REST_API_RUNNER_STORAGE=anyapi npm run test:databases:pg
159
+ ```
160
+
161
+ A complete backend/storage selection has a 30-minute limit; explicitly selected
162
+ test files retain a 15-minute limit. Both limits terminate the owned process group
163
+ and dispose of the database. They do not retry tests or relax assertions.
164
+
165
+ See [backend capabilities](GUIDE/30-backend-capabilities.md) for supported semantics.
166
+ The CI workflow documents its disposable binary setup and database matrix.
167
+
168
+ ## Releases
169
+
170
+ 1. Review the public API and update the [migration guide](GUIDE/33-migrating-to-v2.md).
171
+ 2. Use a new major version for breaking changes. Keep the manifest and lock aligned.
172
+ 3. Run the agreed verification scope and inspect failures, cancellations and skips.
173
+ 4. Inspect the tarball contents and confirm that `old/`, tests, local configuration
174
+ and development dependencies are absent from the installed runtime package.
175
+ 5. Commit the release changes. Publishing and tagging are explicit maintainer actions.
176
+
177
+ Keep secrets in ignored local configuration. Record release evidence in the release
178
+ or pull request; do not add another execution ledger or TODO document to the active tree.
package/index.d.ts ADDED
@@ -0,0 +1,37 @@
1
+ export * from './types/errors.js'
2
+ export * from './types/file-storage.js'
3
+ export type * from './types/representations.js'
4
+ export type * from './types/resource-methods.js'
5
+ export type * from './types/relationship-methods.js'
6
+ export type * from './types/bulk-methods.js'
7
+ export type { ManagedTransaction, ManagedTransactionParam, TransactionMethods } from './types/transactions.js'
8
+ export type * from './types/plugin-options.js'
9
+ export * from './types/anyapi.js'
10
+ export type * from './types/file-detectors.js'
11
+ export type * from './types/fastify-options.js'
12
+ export type * from './types/row-policy.js'
13
+ export type * from './types/autofilter.js'
14
+
15
+ export { JsonRestApi } from './types/runtime.js'
16
+ export type * from './types/runtime.js'
17
+ export type * from './types/resource-schema.js'
18
+ export type * from './types/hook-context.js'
19
+ import type { LibraryPlugin } from './types/runtime.js'
20
+
21
+ export const RestApiPlugin: LibraryPlugin<'rest-api'>
22
+ export const AutoFilterPlugin: LibraryPlugin<'autofilter'>
23
+ export const RowPolicyPlugin: LibraryPlugin<'row-policy'>
24
+ export const QueryProjectionsPlugin: LibraryPlugin<'query-projections'>
25
+ export const FileHandlingPlugin: LibraryPlugin<'file-handling'>
26
+ export const CorsPlugin: LibraryPlugin<'rest-api-cors'>
27
+ export const LabelPlugin: LibraryPlugin<'rest-api-label'>
28
+ export const SocketIOPlugin: LibraryPlugin<'socketio'>
29
+ export const RestApiKnexPlugin: LibraryPlugin<'rest-api-knex'>
30
+ export const RestApiAnyapiKnexPlugin: LibraryPlugin<'rest-api-anyapi-knex'>
31
+ export const ExpressPlugin: LibraryPlugin<'express'>
32
+ export const FastifyPlugin: LibraryPlugin<'fastify'>
33
+
34
+ export function getUrlPrefix(
35
+ context?: { urlPrefixOverride?: string; urlPrefix?: string } | null,
36
+ scope?: { vars?: { transport?: { publicBaseUrl?: string; mountPath?: string } } } | null
37
+ ): string
package/index.js CHANGED
@@ -1,12 +1,4 @@
1
- /**
2
- * JSON REST API - REST API plugin for hooked-api with JSON:API compliance
3
- *
4
- * This package provides:
5
- * - REST API plugin that adds query, get, post, put, patch, delete methods to scopes
6
- * - Connector plugins for various HTTP frameworks (Express, etc.)
7
- * - JSON:API compliant request/response handling
8
- * - Comprehensive error handling and validation
9
- */
1
+ export { JsonRestApi } from './lib/runtime/json-rest-api.js'
10
2
 
11
3
  // Core plugins
12
4
  export { RestApiPlugin } from './plugins/core/rest-api-plugin.js'
@@ -21,27 +13,16 @@ export { SocketIOPlugin } from './plugins/core/socketio-plugin.js'
21
13
  // Database plugins
22
14
  export { RestApiKnexPlugin } from './plugins/core/rest-api-knex-plugin.js'
23
15
  export { RestApiAnyapiKnexPlugin } from './plugins/core/rest-api-anyapi-knex-plugin.js'
16
+ export { AnyapiRegistry } from './plugins/core/lib/anyapi/anyapi-registry.js'
17
+ export { ensureAnyApiSchema } from './plugins/core/lib/anyapi/schema-utils.js'
24
18
 
25
19
  // Connector plugins
26
20
  export { ExpressPlugin } from './plugins/core/connectors/express-plugin.js'
27
21
  export { FastifyPlugin } from './plugins/core/connectors/fastify-plugin.js'
28
- // Future: export { KoaPlugin } from './plugins/core/connectors/koa-plugin.js';
29
22
 
30
23
  // Storage plugins for file handling
31
24
  export { LocalStorage } from './plugins/storage/local-storage.js'
32
- export { S3Storage } from './plugins/storage/s3-storage.js'
33
25
 
34
- // Error classes for consumers who need them
35
- export {
36
- RestApiFieldsetError,
37
- RestApiTemporalDataError,
38
- RestApiValidationError,
39
- RestApiResourceError,
40
- RestApiPayloadError
41
- } from './lib/rest-api-errors.js'
42
-
43
- // Re-export everything for backward compatibility
44
26
  export * from './lib/rest-api-errors.js'
45
27
 
46
- // URL helper function for generating API URL prefix
47
28
  export { getUrlPrefix } from './plugins/core/lib/querying/url-helpers.js'
@@ -1,124 +1,130 @@
1
- /**
2
- * Enhanced logger wrapper that automatically serializes errors with full details
3
- * Wraps any logger to provide comprehensive error logging
4
- */
1
+ // @ts-check
2
+ /** @import { DiagnosticLogger, DiagnosticOptions, EnhancedDiagnosticLogger, DiagnosticWriter } from './logger-types.js' */
5
3
 
6
- import { formatError, formatErrorSummary } from './error-formatter.js'
4
+ import { isErrorObject } from './error-context.js'
5
+ import { formatDiagnosticValue, formatError, formatErrorSummary, readDiagnosticProperty } from './error-formatter.js'
7
6
 
8
7
  /**
9
- * Creates an enhanced logger that automatically formats errors
10
- * @param {Object} baseLogger - The base logger to wrap (must have debug, info, warn, error methods)
11
- * @param {Object} options - Configuration options
12
- * @param {boolean} options.logFullErrors - Whether to log full error details (default: true)
13
- * @param {boolean} options.includeStack - Whether to include stack traces (default: true)
14
- * @returns {Object} Enhanced logger with automatic error formatting
8
+ * @template {DiagnosticLogger} Logger
9
+ * @overload
10
+ * @param {Logger & { error: DiagnosticWriter }} baseLogger
11
+ * @param {DiagnosticOptions} [options]
12
+ * @returns {EnhancedDiagnosticLogger & { error: DiagnosticWriter }}
13
+ */
14
+ /**
15
+ * @template {DiagnosticLogger} Logger
16
+ * @overload
17
+ * @param {Logger} baseLogger
18
+ * @param {DiagnosticOptions} [options]
19
+ * @returns {EnhancedDiagnosticLogger}
20
+ */
21
+ /**
22
+ * Bound diagnostic previews and apply compiled field visibility to supplied levels.
23
+ * @param {DiagnosticLogger} baseLogger
24
+ * @param {DiagnosticOptions} [options]
25
+ * @returns {EnhancedDiagnosticLogger}
15
26
  */
16
27
  export function createEnhancedLogger (baseLogger, options = {}) {
17
28
  const {
18
29
  logFullErrors = true,
19
- includeStack = true
30
+ includeStack = true,
31
+ redactFields = [],
32
+ schemaInfo
20
33
  } = options
21
-
22
- // Helper to process arguments and format errors
23
- function processArgs (args) {
24
- return args.map(arg => {
25
- // If it's an error object, format it
26
- if (arg instanceof Error) {
27
- if (logFullErrors) {
28
- return formatError(arg, { includeStack })
29
- } else {
30
- return formatErrorSummary(arg)
31
- }
32
- }
33
-
34
- // If it's an object with an error property, format the error
35
- if (arg && typeof arg === 'object' && arg.error instanceof Error) {
36
- return {
37
- ...arg,
38
- error: logFullErrors
39
- ? formatError(arg.error, { includeStack })
40
- : formatErrorSummary(arg.error)
41
- }
34
+ const hiddenFields = Object.entries(schemaInfo?.outputFields || {})
35
+ .filter(([, definition]) => definition.hidden === true || definition.normallyHidden === true)
36
+ .map(([field]) => field)
37
+ const diagnosticOptions = { includeStack, redactFields: [...redactFields, ...hiddenFields] }
38
+ const formatLoggedError = logFullErrors ? formatError : formatErrorSummary
39
+
40
+ /** @param {unknown} arg */
41
+ function formatArgument (arg) {
42
+ try {
43
+ if (isErrorObject(arg)) return formatLoggedError(arg, diagnosticOptions)
44
+ if (!arg || typeof arg !== 'object' || diagnosticOptions.redactFields.includes('error')) return arg
45
+
46
+ /** @type {unknown} */
47
+ const nestedError = Object.getOwnPropertyDescriptor(arg, 'error')?.value
48
+ if (!isErrorObject(nestedError)) return arg
49
+ /** @type {object} */
50
+ const metadata = Object(formatDiagnosticValue(arg, diagnosticOptions))
51
+ if (!Object.hasOwn(metadata, 'error')) return metadata
52
+ return {
53
+ ...metadata,
54
+ error: formatLoggedError(nestedError, diagnosticOptions)
42
55
  }
43
-
56
+ } catch {
57
+ // The final shared formatter contains values that cannot even be classified.
44
58
  return arg
45
- })
59
+ }
46
60
  }
47
61
 
48
62
  // Create wrapper methods for each log level
63
+ /** @type {DiagnosticLogger & Record<string, unknown>} */
49
64
  const enhancedLogger = {}
50
65
 
51
- // Common log levels
52
- const logLevels = ['debug', 'info', 'warn', 'error', 'trace', 'fatal', 'log']
66
+ const logLevels = /** @type {const} */ (['debug', 'info', 'warn', 'error', 'trace', 'fatal', 'log'])
53
67
 
54
68
  for (const level of logLevels) {
55
- if (typeof baseLogger[level] === 'function') {
56
- enhancedLogger[level] = function (...args) {
57
- const processedArgs = processArgs(args)
58
-
59
- // Special handling for error level - always log full details
60
- if (level === 'error' && args.some(arg => arg instanceof Error || (arg?.error instanceof Error))) {
61
- // Log a formatted version as well for better readability
62
- const errors = args.filter(arg => arg instanceof Error || arg?.error instanceof Error)
63
- for (const errorArg of errors) {
64
- const error = errorArg instanceof Error ? errorArg : errorArg.error
65
- if (error.details?.violations && Array.isArray(error.details.violations) && error.details.violations.length > 0) {
66
- // Log violations in a readable format
67
- baseLogger[level]('Validation error details:', {
68
- message: error.message,
69
- violations: error.details.violations.map(v => ({
70
- field: v.field,
71
- rule: v.rule,
72
- message: v.message
73
- }))
74
- })
75
- }
76
- }
77
- }
78
-
79
- return baseLogger[level](...processedArgs)
80
- }
69
+ if (typeof baseLogger[level] !== 'function') continue
70
+ // Capture before enhanceLogger replaces methods on the same object.
71
+ const write = baseLogger[level].bind(baseLogger)
72
+ enhancedLogger[level] = function (...args) {
73
+ const processedArgs = args.slice(0, 50).map(formatArgument)
74
+ if (args.length > 50) processedArgs.push('[Truncated]')
75
+ const formatted = formatDiagnosticValue(processedArgs, diagnosticOptions)
76
+ return write(...(Array.isArray(formatted) ? formatted : [formatted]))
81
77
  }
82
78
  }
83
79
 
84
80
  // Preserve any other properties/methods from the base logger
85
81
  for (const key in baseLogger) {
86
82
  if (!(key in enhancedLogger)) {
87
- enhancedLogger[key] = baseLogger[key]
83
+ enhancedLogger[key] = Reflect.get(baseLogger, key)
88
84
  }
89
85
  }
90
86
 
91
- // Add utility methods
92
- enhancedLogger.logError = function (message, error, additionalData = {}) {
93
- const errorData = formatError(error, { includeStack })
94
- this.error(message, {
95
- ...additionalData,
87
+ /** @type {EnhancedDiagnosticLogger['logError']} */
88
+ const logError = function (message, error, additionalData = {}) {
89
+ const errorData = formatError(error, diagnosticOptions)
90
+ /** @type {object} */
91
+ const metadata = Object(formatDiagnosticValue(additionalData, diagnosticOptions))
92
+ return this.error(message, {
93
+ ...metadata,
96
94
  error: errorData,
97
- errorSummary: formatErrorSummary(error)
95
+ errorSummary: formatErrorSummary(error, diagnosticOptions)
98
96
  })
99
97
  }
100
98
 
101
- enhancedLogger.logValidationError = function (message, error, additionalData = {}) {
102
- if (error.details?.violations && Array.isArray(error.details.violations)) {
103
- this.error(message, {
104
- ...additionalData,
105
- message: error.message,
106
- code: error.code,
107
- violations: error.details.violations,
108
- fields: error.details?.fields || [],
109
- fullError: formatError(error, { includeStack: false })
110
- })
111
- } else {
112
- this.logError(message, error, additionalData)
113
- }
99
+ /** @type {EnhancedDiagnosticLogger['logValidationError']} */
100
+ const logValidationError = function (message, error, additionalData = {}) {
101
+ const details = diagnosticOptions.redactFields.includes('details') ? undefined : readDiagnosticProperty(error, 'details')
102
+ const violations = diagnosticOptions.redactFields.includes('violations') ? undefined : readDiagnosticProperty(details, 'violations')
103
+ if (!Array.isArray(violations)) return this.logError(message, error, additionalData)
104
+ const formatted = formatError(error, { ...diagnosticOptions, includeStack: false })
105
+ const formattedDetails = readDiagnosticProperty(formatted, 'details')
106
+ /** @type {object} */
107
+ const metadata = Object(formatDiagnosticValue(additionalData, diagnosticOptions))
108
+ return this.error(message, {
109
+ ...metadata,
110
+ message: readDiagnosticProperty(formatted, 'message'),
111
+ code: readDiagnosticProperty(formatted, 'code'),
112
+ violations: readDiagnosticProperty(formattedDetails, 'violations'),
113
+ fields: readDiagnosticProperty(formattedDetails, 'fields') || [],
114
+ fullError: formatted
115
+ })
114
116
  }
115
117
 
116
- return enhancedLogger
118
+ return Object.assign(enhancedLogger, { logError, logValidationError })
117
119
  }
118
120
 
119
121
  /**
120
122
  * Middleware to enhance an existing logger instance
121
123
  * Can be used to patch global loggers
124
+ * @template {DiagnosticLogger} Logger
125
+ * @param {Logger} logger
126
+ * @param {DiagnosticOptions} [options]
127
+ * @returns {Logger}
122
128
  */
123
129
  export function enhanceLogger (logger, options = {}) {
124
130
  const enhanced = createEnhancedLogger(logger, options)
@@ -126,7 +132,7 @@ export function enhanceLogger (logger, options = {}) {
126
132
  // Replace methods on the original logger
127
133
  for (const key in enhanced) {
128
134
  if (typeof enhanced[key] === 'function') {
129
- logger[key] = enhanced[key]
135
+ Object.assign(logger, { [key]: enhanced[key] })
130
136
  }
131
137
  }
132
138