json-rest-api 1.0.29 → 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 (292) 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 +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} +74 -30
  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 -177
  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 +48 -317
  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 +134 -37
  91. package/plugins/core/lib/querying/relationship-identifiers.js +208 -0
  92. package/plugins/core/lib/querying/sort-helpers.js +13 -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 +179 -194
  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 +100 -290
  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 +93 -38
  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 +128 -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 -2485
  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 -1077
  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 -395
  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 -148
  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 +36 -68
  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 +37 -81
  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 -1247
  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 -148
  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 -155
  251. package/tests/fixtures/api-configs.js +0 -2566
  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-boundaries.test.js +0 -154
  264. package/tests/pagination-contract.test.js +0 -122
  265. package/tests/pagination-cursor-multifield.test.js +0 -641
  266. package/tests/pagination-enhanced.test.js +0 -563
  267. package/tests/polymorphic-relationships.test.js +0 -347
  268. package/tests/polymorphic-simple.test.js +0 -148
  269. package/tests/positioning-advanced.test.js +0 -692
  270. package/tests/positioning-extended.test.js +0 -915
  271. package/tests/positioning-multifilter.test.js +0 -699
  272. package/tests/positioning.test.js +0 -560
  273. package/tests/post-commit-hooks.test.js +0 -120
  274. package/tests/projected-fields.test.js +0 -319
  275. package/tests/public-exports.test.js +0 -15
  276. package/tests/put-create-parity.test.js +0 -216
  277. package/tests/queries.test.js +0 -521
  278. package/tests/query-limits.test.js +0 -584
  279. package/tests/relationship-endpoints.test.js +0 -620
  280. package/tests/relationship-include-storage-adapter.test.js +0 -56
  281. package/tests/relationships.test.js +0 -681
  282. package/tests/rest-api.test.js +0 -337
  283. package/tests/return-record-settings.test.js +0 -184
  284. package/tests/row-policy.test.js +0 -489
  285. package/tests/s3-storage.test.js +0 -38
  286. package/tests/searchschema-knex-behavior.test.js +0 -187
  287. package/tests/searchschema-merge.test.js +0 -151
  288. package/tests/socketio.test.js +0 -860
  289. package/tests/storage-mapping.test.js +0 -362
  290. package/tests/temporal-boundaries.test.js +0 -208
  291. package/tests/test-development-guide.md +0 -370
  292. package/tests/virtual-fields.test.js +0 -404
@@ -1,161 +0,0 @@
1
- # Complete File Upload Example with Multiple Files
2
-
3
- This guide shows a complete example of implementing file uploads with jsonrestapi, including:
4
- - Multiple file fields (images and PDFs)
5
- - Different storage configurations for different file types
6
- - HTML form for testing uploads
7
- - Express static file serving
8
-
9
- ## Complete Example
10
-
11
- ```javascript
12
- import { Api } from 'hooked-api';
13
- import { RestApiPlugin, FileHandlingPlugin, ExpressPlugin } from 'json-rest-api';
14
- import { LocalStorage } from 'json-rest-api/plugins/storage/local-storage.js';
15
- import express from 'express';
16
-
17
- // Create API
18
- const api = new Api({
19
- name: 'my-library-api',
20
- });
21
-
22
- // Create storage for different file types
23
- const coverStorage = new LocalStorage({
24
- directory: './uploads/covers',
25
- baseUrl: 'http://localhost:3000/uploads/covers',
26
- nameStrategy: 'hash',
27
- allowedExtensions: ['.jpg', '.jpeg', '.png', '.gif']
28
- });
29
-
30
- const pdfStorage = new LocalStorage({
31
- directory: './uploads/pdfs',
32
- baseUrl: 'http://localhost:3000/uploads/pdfs',
33
- nameStrategy: 'timestamp'
34
- });
35
-
36
- // Use plugins
37
- api.use(RestApiPlugin);
38
- api.use(FileHandlingPlugin);
39
- api.use(ExpressPlugin); // File parser configuration shown in connector plugins section
40
-
41
- // Define schema with multiple file fields
42
- api.addResource('books', {
43
- schema: {
44
- title: { type: 'string', required: true },
45
- author: { type: 'string', required: true },
46
- year: { type: 'number' },
47
- cover: {
48
- type: 'file',
49
- storage: coverStorage,
50
- accepts: ['image/*'],
51
- maxSize: '5mb'
52
- },
53
- sample: {
54
- type: 'file',
55
- storage: pdfStorage,
56
- accepts: ['application/pdf'],
57
- maxSize: '10mb',
58
- required: false
59
- }
60
- }
61
- });
62
-
63
- // Simple data helpers
64
- api.vars.helpers.dataPost = async ({ scopeName, inputRecord }) => {
65
- const newBook = {
66
- id: String(Date.now()),
67
- ...inputRecord.data.attributes
68
- };
69
-
70
- console.log('Created book:', newBook);
71
-
72
- return {
73
- data: {
74
- type: 'books',
75
- id: newBook.id,
76
- attributes: newBook
77
- }
78
- };
79
- };
80
-
81
- api.vars.helpers.dataQuery = async () => {
82
- return { data: [] };
83
- };
84
-
85
- // Express setup
86
- const app = express();
87
- app.use('/uploads', express.static('./uploads'));
88
-
89
- // Test form
90
- app.get('/', (req, res) => {
91
- res.send(`
92
- <form action="/api/books" method="POST" enctype="multipart/form-data">
93
- <h2>Add a Book</h2>
94
- <p>Title: <input name="title" required></p>
95
- <p>Author: <input name="author" required></p>
96
- <p>Year: <input name="year" type="number"></p>
97
- <p>Cover: <input name="cover" type="file" accept="image/*"></p>
98
- <p>Sample PDF: <input name="sample" type="file" accept=".pdf"></p>
99
- <button type="submit">Add Book</button>
100
- </form>
101
- `);
102
- });
103
-
104
- api.express.mount(app);
105
-
106
- app.listen(3000, () => {
107
- console.log('Library API running at http://localhost:3000');
108
- console.log('Test form at http://localhost:3000');
109
- console.log('API endpoints at http://localhost:3000/api/books');
110
- }).on('error', (err) => {
111
- console.error('Failed to start server:', err);
112
- process.exit(1)
113
- });
114
- ```
115
-
116
- Remember to install the required peer dependency:
117
-
118
- ```bash
119
- npm install busboy
120
- ```
121
-
122
- ## Key Features Demonstrated
123
-
124
- ### Multiple Storage Configurations
125
-
126
- The example shows how to create different storage configurations for different file types:
127
- - **Cover images**: Use hash naming strategy and restrict to image files
128
- - **PDF samples**: Use timestamp naming strategy for PDFs
129
-
130
- ### HTML Test Form
131
-
132
- The example includes a simple HTML form for testing file uploads without needing external tools.
133
-
134
- ### Static File Serving
135
-
136
- The Express app serves uploaded files directly:
137
- ```javascript
138
- app.use('/uploads', express.static('./uploads'));
139
- ```
140
-
141
- This allows uploaded files to be accessed via URLs like:
142
- - `http://localhost:3000/uploads/covers/abc123.jpg`
143
- - `http://localhost:3000/uploads/pdfs/1234567890.pdf`
144
-
145
- ## Testing the Example
146
-
147
- 1. Run the server
148
- 2. Open http://localhost:3000 in your browser
149
- 3. Fill out the form and select files
150
- 4. Submit to see the file upload in action
151
- 5. Check the console for the created book data
152
- 6. Access uploaded files via their URLs
153
-
154
- ## Next Steps
155
-
156
- - Add validation for file types
157
- - Implement file deletion when records are deleted
158
- - Add image resizing for covers
159
- - Generate thumbnails
160
- - Add virus scanning for uploaded files
161
- - Implement S3 storage for production
package/eslint.config.js DELETED
@@ -1,13 +0,0 @@
1
- import neostandard from 'neostandard'
2
-
3
- export default [
4
- ...neostandard({
5
- ignores: ['node_modules/**', 'docs/**', 'tests/**', 'examples/**']
6
- }),
7
- {
8
- files: ['index.js'],
9
- rules: {
10
- 'import-x/export': 'off'
11
- }
12
- }
13
- ]
@@ -1,137 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- /**
4
- * Example: Custom URL Override Using Hooks
5
- *
6
- * This example demonstrates how to override URL generation using the
7
- * hooked-api hooks system. This is the recommended approach for custom
8
- * URL scenarios like API gateways, CDNs, or multi-tenant applications.
9
- */
10
-
11
- import { Api } from 'hooked-api'
12
- import { RestApiPlugin, RestApiKnexPlugin, ExpressPlugin } from 'json-rest-api'
13
- import knexLib from 'knex'
14
- import express from 'express'
15
-
16
- // Create in-memory database
17
- const knex = knexLib({
18
- client: 'better-sqlite3',
19
- connection: { filename: ':memory:' },
20
- useNullAsDefault: true
21
- })
22
-
23
- // Create API with URL override hook
24
- const api = new Api({
25
- name: 'url-override-demo',
26
-
27
- // Define hooks at API creation time
28
- hooks: {
29
- // This hook runs at the start of each request
30
- 'transport:request': [
31
- async (payload) => {
32
- const { context, req } = payload
33
-
34
- // Example 1: Override based on custom header
35
- // Useful for CDN or API gateway scenarios
36
- if (req?.headers?.['x-public-url']) {
37
- context.urlPrefixOverride = req.headers['x-public-url']
38
- console.log(`✓ URL overridden via X-Public-URL header: ${context.urlPrefixOverride}`)
39
- }
40
-
41
- // Example 2: API versioning via header
42
- // Route different API versions while maintaining same backend
43
- else if (req?.headers?.['x-api-version'] === 'v2') {
44
- context.urlPrefixOverride = 'https://api.example.com/v2'
45
- console.log(`✓ URL overridden for API v2: ${context.urlPrefixOverride}`)
46
- }
47
-
48
- // Example 3: Multi-tenant based on host
49
- // Each tenant gets their own domain in responses
50
- else if (req?.hostname?.includes('tenant-a')) {
51
- context.urlPrefixOverride = `https://tenant-a.api.com${req.baseUrl || ''}`
52
- console.log(`✓ URL overridden for tenant-a: ${context.urlPrefixOverride}`)
53
- }
54
-
55
- // Example 4: Environment-based override
56
- // Force production URLs in staging environment
57
- else if (process.env.FORCE_PRODUCTION_URLS === 'true') {
58
- context.urlPrefixOverride = 'https://api.production.com/api'
59
- console.log(`✓ URL overridden via environment: ${context.urlPrefixOverride}`)
60
- }
61
-
62
- return payload
63
- }
64
- ]
65
- }
66
- })
67
-
68
- // Install plugins
69
- await api.use(RestApiPlugin, {
70
- returnRecordTransport: { post: 'full' } // Return full JSON:API response
71
- })
72
- await api.use(RestApiKnexPlugin, { knex })
73
- await api.use(ExpressPlugin, { mountPath: '/api' })
74
-
75
- // Define a simple resource
76
- await api.addResource('items', {
77
- schema: {
78
- name: { type: 'string', required: true, max: 100 },
79
- description: { type: 'string', max: 500 }
80
- }
81
- })
82
- await api.resources.items.createKnexTable()
83
-
84
- // Create some test data
85
- console.log('\n📝 Creating test data...')
86
- await api.resources.items.post({
87
- inputRecord: {
88
- data: {
89
- type: 'items',
90
- attributes: {
91
- name: 'Test Item 1',
92
- description: 'This is a test item'
93
- }
94
- }
95
- },
96
- simplified: false
97
- })
98
-
99
- // Create Express app
100
- const app = express()
101
- app.use(api.http.express.router)
102
-
103
- // Start server
104
- const PORT = process.env.PORT || 3333
105
- const server = app.listen(PORT, () => {
106
- console.log(`\n🚀 Server running on http://localhost:${PORT}`)
107
- console.log('\n📚 Try these examples:\n')
108
-
109
- console.log('1. Default URL (auto-detected):')
110
- console.log(` curl http://localhost:${PORT}/api/items`)
111
- console.log(' → URLs will be: http://localhost:${PORT}/api/items/1\n')
112
-
113
- console.log('2. Custom CDN URL:')
114
- console.log(` curl -H "X-Public-URL: https://cdn.example.com/api" http://localhost:${PORT}/api/items`)
115
- console.log(' → URLs will be: https://cdn.example.com/api/items/1\n')
116
-
117
- console.log('3. API Version Header:')
118
- console.log(` curl -H "X-API-Version: v2" http://localhost:${PORT}/api/items`)
119
- console.log(' → URLs will be: https://api.example.com/v2/items/1\n')
120
-
121
- console.log('4. Create with custom URL:')
122
- console.log(' curl -X POST -H "Content-Type: application/vnd.api+json" \\')
123
- console.log(' -H "X-Public-URL: https://public.api.com/api" \\')
124
- console.log(' -d \'{"data":{"type":"items","attributes":{"name":"New Item"}}}\' \\')
125
- console.log(` http://localhost:${PORT}/api/items`)
126
- console.log(' → Response URLs will use: https://public.api.com/api/items/2\n')
127
-
128
- console.log('Press Ctrl+C to stop the server\n')
129
- })
130
-
131
- // Graceful shutdown
132
- process.on('SIGINT', async () => {
133
- console.log('\n\n👋 Shutting down gracefully...')
134
- server.close()
135
- await knex.destroy()
136
- process.exit(0)
137
- })
package/fixing_plan.md DELETED
@@ -1,112 +0,0 @@
1
- # Fixing Plan
2
-
3
- Checklist-style plan for addressing the audit findings. Items marked as requiring approval change public behavior, public API, or dependencies.
4
-
5
- ## High Priority
6
-
7
- - [x] Fix AnyAPI custom `idProperty` canonical mapping.
8
- - [x] Treat `schemaInfo.idProperty` as an alias of logical `id` in the canonical storage adapter.
9
- - [x] Skip custom id fields such as `item_id` when building DB attribute selections.
10
- - [x] Ensure cursor, sort, projection, sparse fieldset, and include paths translate both `id` and `idProperty` to `logical_id`.
11
- - [x] Add AnyAPI tests for custom-id `post`, `get`, `query`, sparse fields, and cursor pagination.
12
-
13
- - [x] Fix AnyAPI PUT create parity.
14
- - [x] Branch `helpers.dataPut` on `context.isCreate`.
15
- - [x] In create mode, insert into `any_records` with tenant, resource, logical id, and translated attributes.
16
- - [x] Keep the current update path for update mode.
17
- - [x] Add legacy Knex and AnyAPI parity tests for `PUT /resource/:id` create, including custom IDs.
18
-
19
- - [x] Fix pagination link query-string round trips.
20
- - [x] Replace manual query-string concatenation with one serializer that is the inverse of `parseJsonApiQuery`.
21
- - [x] Use `URLSearchParams` for keys and values.
22
- - [x] Map internal `queryParams.filters` back to public JSON:API `filter[...]`.
23
- - [x] Add tests that parse generated `links.next` and prove filters, fields, sort, include, and page params survive round trip.
24
-
25
- - [x] Fix relationship PATCH cardinality validation.
26
- - [x] Generate relationship-aware request contracts from relationship metadata.
27
- - [x] Allow to-one relationship PATCH data to be `null` or one resource identifier only.
28
- - [x] Allow to-many relationship data to be arrays only.
29
- - [x] Make `processRelationships()` throw when a belongsTo relationship receives an array.
30
- - [x] Add API and transport tests for invalid to-one arrays and invalid to-many scalars.
31
-
32
- - [x] Run `afterCommit` for relationship routes.
33
- - [x] After `postRelationship`, `patchRelationship`, and `deleteRelationship` commit their own transaction, run `afterCommit`.
34
- - [x] Preserve rollback behavior through `afterRollback`.
35
- - [x] Verify Socket.IO deferred relationship broadcasts work, or define a separate relationship notification policy.
36
- - [x] Add tests with an `afterCommit` hook counter and Socket.IO relationship-write subscription behavior.
37
-
38
- - [x] Decide and fix post-commit hook failure semantics.
39
- - [x] Split transaction work from post-commit side effects.
40
- - [x] Once `commit()` succeeds, do not call rollback handling.
41
- - [x] Surface post-commit hook errors while preserving already-committed data.
42
- - [x] Add tests proving the row remains committed and the response/error semantics are intentional.
43
-
44
- ## Medium Priority
45
-
46
- - [x] Fix AnyAPI pagination fixture cleanup.
47
- - [x] Register `cursor_products` and `cursor_items` with `storageMode.registerTable()` in `createCursorPaginationApi`.
48
- - [x] Add a focused cleanup test proving `cleanTables(knex, ['cursor_products'])` deletes matching `any_records`.
49
- - [x] Keep metadata-derived cleanup mappings as a later refactor if a broader helper change is approved.
50
-
51
- - [x] Fix file temp and orphan cleanup.
52
- - [x] Wrap file processing in `try/finally` so detector temp files are cleaned after validation failures.
53
- - [x] Track successfully uploaded URLs on `context`.
54
- - [x] On rollback or later write error, call storage `delete(url)` where available.
55
- - [x] Document `delete(url)` as the rollback-capable storage contract.
56
- - [x] Add tests for invalid MIME cleanup and post-upload DB/validation rollback cleanup.
57
-
58
- - [x] Harden `LocalStorage` path containment.
59
- - [x] Sanitize or reject custom basenames containing `/`, `\`, drive prefixes, or empty names.
60
- - [x] Replace `startsWith(resolvedDir)` containment with `path.relative(resolvedDir, resolvedPath)`.
61
- - [x] Add tests for sibling-prefix traversal such as `../uploads_evil/pwn`.
62
-
63
- - [x] Fix untrusted proxy URL generation. Requires maintainer approval.
64
- - [x] Stop trusting `Host` and `X-Forwarded-*` by default for absolute links.
65
- - [x] Prefer relative links unless `urlPrefixOverride` or an explicit public base URL is configured.
66
- - [x] If forwarded headers remain supported, gate them behind `trustProxy` and allowed host/proto validation. Resolved by not supporting forwarded headers for URL generation.
67
- - [x] Add Express and Fastify tests with hostile `Host` and `X-Forwarded-Host` headers.
68
-
69
- - [x] Fix `S3Storage` non-mock behavior. Dependency/public behavior choice requires maintainer approval.
70
- - [x] Smallest safe fix: throw a clear "real S3 storage is not implemented" error when `mockMode: false`.
71
- - [x] Reject full AWS SDK support for this pass because it adds dependency, credential, and public behavior scope.
72
- - [x] Add tests for mock mode and the chosen non-mock behavior.
73
-
74
- ## Low Priority
75
-
76
- - [x] Make field setter errors fail closed.
77
- - [x] Change setter error handling to throw by default.
78
- - [x] Do not add an opt-in non-fatal mode without a real use case.
79
- - [x] Add a rollback test where a setter throws and no row is persisted.
80
-
81
- - [x] Normalize return-record boolean compatibility.
82
- - [x] Create one shared return-record normalizer.
83
- - [x] Normalize `true -> 'full'`, `false -> 'no'`, and keep valid strings unchanged.
84
- - [x] Use the shared normalizer in plugin install and per-call setup.
85
- - [x] Update tests and docs to prefer `'no'`, `'minimal'`, and `'full'` while preserving boolean compatibility.
86
-
87
- ## Contract Drift
88
-
89
- - [x] Fix Socket.IO public exports and docs.
90
- - [x] Export `SocketIOPlugin` from `index.js`.
91
- - [x] Fix docs to import `Api` from `hooked-api`, not `json-rest-api`.
92
- - [x] Remove invalid deep imports such as `json-rest-api/plugins/socketio`, or add formal package subpath exports. Subpath exports require maintainer approval.
93
- - [x] Add a public import smoke test.
94
-
95
- - [x] Align S3 documentation with implementation.
96
- - [x] Since real S3 is not implemented, make README and guide text state that the included adapter is mock/demo only.
97
- - [x] Leave production S3 as a future approved dependency/API design rather than documenting unsupported behavior.
98
-
99
- - [x] Finish relationship validation contract consolidation.
100
- - [x] Move relationship-route schemas into the same request-contract surface as resource routes.
101
- - [x] Do this after the cardinality fix so the centralized contract is correct rather than merely centralized.
102
-
103
- ## Verification Checklist
104
-
105
- - [x] `npm test`
106
- - [x] `npm run lint`
107
- - [x] `npm run test:anyapi`
108
- - [x] `npm run verify`
109
- - [x] Narrow repro for AnyAPI custom `idProperty`
110
- - [x] Narrow repro for relationship route `afterCommit`
111
- - [x] Narrow repro for pagination link round trip
112
- - [x] Import smoke test for public exports