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
@@ -1,110 +0,0 @@
1
- import { getSchemaStructure } from '../querying-writing/knex-constants.js'
2
-
3
- /**
4
- * Processes belongsTo relationships from JSON:API input to foreign key updates
5
- *
6
- * @param {Object} scope - The scope object containing schema info
7
- * @param {Object} deps - Dependencies object containing context with inputRecord
8
- * @returns {Object} Object mapping foreign key fields to their values
9
- *
10
- * @example
11
- * // Input: JSON:API document with relationships
12
- * const inputRecord = {
13
- * data: {
14
- * type: 'articles',
15
- * attributes: { title: 'My Article' },
16
- * relationships: {
17
- * author: {
18
- * data: { type: 'users', id: '123' }
19
- * },
20
- * category: {
21
- * data: { type: 'categories', id: '456' }
22
- * }
23
- * }
24
- * }
25
- * };
26
- *
27
- * // Schema has: author_id field with belongsTo: 'users', as: 'author'
28
- * // Schema has: category_id field with belongsTo: 'categories', as: 'category'
29
- *
30
- * const updates = processBelongsToRelationships(scope, { context: { inputRecord } });
31
- *
32
- * // Output: Foreign key values extracted
33
- * // {
34
- * // author_id: '123',
35
- * // category_id: '456'
36
- * // }
37
- *
38
- * @example
39
- * // Input: Removing a relationship (setting to null)
40
- * const inputRecord = {
41
- * data: {
42
- * type: 'articles',
43
- * id: '1',
44
- * relationships: {
45
- * author: {
46
- * data: null // Remove author
47
- * }
48
- * }
49
- * }
50
- * };
51
- *
52
- * const updates = processBelongsToRelationships(scope, { context: { inputRecord } });
53
- *
54
- * // Output: Foreign key set to null
55
- * // {
56
- * // author_id: null
57
- * // }
58
- *
59
- * @description
60
- * Used by:
61
- * - rest-api-knex-plugin's dataPut and dataPatch methods
62
- * - Called before saving to extract foreign keys from JSON:API relationships
63
- * - Works in conjunction with relationship-processor for complete handling
64
- *
65
- * Purpose:
66
- * - Converts JSON:API relationship format to database foreign keys
67
- * - Maps from relationship names (e.g., 'author') to field names (e.g., 'author_id')
68
- * - Handles both setting relationships (with id) and removing them (null)
69
- * - Only processes belongsTo relationships, not hasMany
70
- *
71
- * Data flow:
72
- * 1. Receives JSON:API document with relationships section
73
- * 2. Looks up schema to find foreign key fields
74
- * 3. For each belongsTo field, checks if relationship exists in input
75
- * 4. Extracts id from relationship data or sets null
76
- * 5. Returns object ready to merge with attributes for database update
77
- */
78
- export const processBelongsToRelationships = (scope, deps) => {
79
- const foreignKeyUpdates = {}
80
-
81
- // Extract values from deps
82
- const { context } = deps
83
- const inputRecord = context.inputRecord
84
-
85
- if (!inputRecord.data.relationships) {
86
- return foreignKeyUpdates
87
- }
88
-
89
- // Extract schema from scope
90
- const {
91
- vars: {
92
- schemaInfo: { schema }
93
- }
94
- } = scope
95
-
96
- // Schema might be a Schema object or plain object
97
- const schemaStructure = getSchemaStructure(schema)
98
-
99
- for (const [fieldName, fieldDef] of Object.entries(schemaStructure)) {
100
- if (fieldDef.belongsTo && fieldDef.as) {
101
- const relName = fieldDef.as
102
- if (inputRecord.data.relationships[relName] !== undefined) {
103
- const relData = inputRecord.data.relationships[relName]
104
- foreignKeyUpdates[fieldName] = relData.data?.id || null
105
- }
106
- }
107
- }
108
-
109
- return foreignKeyUpdates
110
- }
@@ -1,93 +0,0 @@
1
- /**
2
- * S3 Storage Adapter (mock/demo)
3
- *
4
- * Generates S3-style URLs without uploading files to Amazon S3.
5
- *
6
- * Usage:
7
- * ```javascript
8
- * import { S3Storage } from 'json-rest-api';
9
- *
10
- * const storage = new S3Storage({
11
- * bucket: 'my-uploads',
12
- * region: 'us-east-1',
13
- * prefix: 'uploads/',
14
- * mockMode: true
15
- * });
16
- *
17
- * const schema = {
18
- * image: { type: 'file', storage }
19
- * };
20
- * ```
21
- */
22
-
23
- import crypto from 'crypto'
24
- import path from 'path'
25
-
26
- const REAL_S3_NOT_IMPLEMENTED_MESSAGE = 'S3Storage real S3 mode is not implemented. The included adapter only supports mockMode: true; provide a production storage adapter or add an AWS SDK-backed implementation.'
27
-
28
- function createRealS3NotImplementedError () {
29
- return new Error(REAL_S3_NOT_IMPLEMENTED_MESSAGE)
30
- }
31
-
32
- export class S3Storage {
33
- constructor (options = {}) {
34
- this.bucket = options.bucket
35
- this.region = options.region || 'us-east-1'
36
- this.prefix = options.prefix || ''
37
- this.acl = options.acl || 'public-read'
38
-
39
- if (!this.bucket) {
40
- throw new Error('S3Storage requires a bucket name')
41
- }
42
-
43
- this.mockMode = options.mockMode !== false // Default to mock mode
44
-
45
- if (!this.mockMode) {
46
- throw createRealS3NotImplementedError()
47
- }
48
- }
49
-
50
- /**
51
- * Upload a file to mock S3 storage.
52
- * @param {Object} file - File object from detector
53
- * @returns {Promise<string>} URL of the uploaded file
54
- */
55
- async upload (file) {
56
- // Generate S3 key
57
- const ext = path.extname(file.filename)
58
- const hash = crypto.randomBytes(16).toString('hex')
59
- const key = `${this.prefix}${hash}${ext}`
60
-
61
- if (this.mockMode) {
62
- // Mock mode - just return a fake URL
63
- const url = `https://${this.bucket}.s3.${this.region}.amazonaws.com/${key}`
64
-
65
- // Simulate upload delay
66
- await new Promise(resolve => setTimeout(resolve, 100))
67
-
68
- return url
69
- }
70
-
71
- throw createRealS3NotImplementedError()
72
- }
73
-
74
- /**
75
- * Delete a file from mock S3 storage.
76
- * @param {string} url - URL returned by upload()
77
- * @returns {Promise<void>}
78
- */
79
- async delete (url) {
80
- if (!this.mockMode) {
81
- throw createRealS3NotImplementedError()
82
- }
83
-
84
- const urlObj = new URL(url)
85
-
86
- if (urlObj.pathname.length === 0) {
87
- throw new Error('Invalid S3 object URL')
88
- }
89
-
90
- // Mock mode - just simulate deletion
91
- await new Promise(resolve => setTimeout(resolve, 50))
92
- }
93
- }
package/quickTest.js DELETED
@@ -1,136 +0,0 @@
1
- //
2
- // index.js
3
- //
4
- import { RestApiPlugin, RestApiKnexPlugin, ExpressPlugin } from './index.js' // Added: ExpressPlugin
5
- import { Api } from 'hooked-api'
6
- import knexLib from 'knex'
7
- import util from 'util'
8
- import express from 'express' // Added: Express
9
-
10
- // Utility used throughout this guide
11
- const inspect = (obj) => util.inspect(obj, { depth: 8 })
12
-
13
- // Create a Knex instance connected to SQLite in-memory database
14
- const knex = knexLib({
15
- client: 'better-sqlite3',
16
- connection: {
17
- filename: ':memory:'
18
- },
19
- useNullAsDefault: true
20
- })
21
-
22
- // Create API instance
23
- const api = new Api({ name: 'book-catalog-api', logLevel: 'trace' })
24
-
25
- // Install plugins
26
- await api.use(RestApiPlugin, { returnBasePath: '/api' })
27
- await api.use(RestApiKnexPlugin, { knex })
28
- await api.use(ExpressPlugin, { mountPath: '/api' }) // Added: Express Plugin
29
-
30
- // Define publishers resource
31
- await api.addResource('publishers', {
32
- schema: {
33
- name: { type: 'string', required: true, max: 255, search: true, indexed: true },
34
- },
35
- relationships: {
36
- // A publisher has many authors
37
- authors: { hasMany: 'authors', foreignKey: 'publisher_id' },
38
- },
39
- searchSchema: { // Adding search schema for publishers
40
- name: { type: 'string', filterOperator: 'like' }
41
- }
42
- })
43
- await api.resources.publishers.createKnexTable()
44
-
45
- // Define authors resource, which belongs to a publisher
46
- await api.addResource('authors', {
47
- schema: {
48
- name: { type: 'string', required: true, max: 100, search: true },
49
- surname: { type: 'string', required: true, max: 100, search: true },
50
- publisher_id: { type: 'id', belongsTo: 'publishers', as: 'publisher', nullable: true }
51
- },
52
- searchSchema: { // Adding search schema for authors
53
- name: { type: 'string', filterOperator: 'like' },
54
- surname: { type: 'string', filterOperator: 'like' },
55
- publisher: { type: 'id', actualField: 'publisher_id', nullable: true },
56
- publisherName: { type: 'string', actualField: 'publishers.name', filterOperator: 'like' } // Cross-table search
57
- }
58
- })
59
- await api.resources.authors.createKnexTable()
60
-
61
- // Method 1: Simplified mode without inputRecord (most concise)
62
- const penguinResult = await api.resources.publishers.post({
63
- name: 'Penguin Random House'
64
- })
65
- console.log('Created publisher:', inspect(penguinResult))
66
-
67
- // Method 2: Simplified mode with inputRecord (explicit)
68
- const harperResult = await api.resources.publishers.post({
69
- inputRecord: {
70
- name: 'HarperCollins'
71
- }
72
- })
73
- console.log('Created second publisher:', inspect(harperResult))
74
-
75
- // Method 3: Full JSON:API mode (standards compliant)
76
- const oxfordResult = await api.resources.publishers.post({
77
- inputRecord: {
78
- data: {
79
- type: 'publishers',
80
- attributes: {
81
- name: 'Oxford University Press'
82
- }
83
- }
84
- },
85
- simplified: false
86
- })
87
- console.log('JSON:API response:', inspect(oxfordResult))
88
-
89
- // Create an author linked to the first publisher (simplified)
90
- const authorResult = await api.resources.authors.post({
91
- name: 'George',
92
- surname: 'Orwell',
93
- publisher_id: penguinResult.id
94
- })
95
- console.log('Created author:', inspect(authorResult))
96
-
97
- // Get all publishers
98
- const allPublishers = await api.resources.publishers.query({})
99
- console.log('All publishers:', inspect(allPublishers))
100
-
101
- // Get publisher with included authors
102
- const publisherWithAuthors = await api.resources.publishers.get({
103
- id: penguinResult.id,
104
- include: ['authors']
105
- })
106
- console.log('Publisher with authors:', inspect(publisherWithAuthors))
107
-
108
- // Search authors by name
109
- const searchResult = await api.resources.authors.query({
110
- filter: { name: 'George' }
111
- })
112
- console.log('Search results:', inspect(searchResult))
113
-
114
- // Update an author
115
- const updateResult = await api.resources.authors.patch({
116
- id: authorResult.id,
117
- surname: 'Orwell (Eric Blair)'
118
- })
119
- console.log('Updated author:', inspect(updateResult))
120
-
121
- /// *** ...programmatic calls here... ***
122
-
123
- // Create the express server and add the API's routes
124
- const app = express()
125
- app.use(api.http.express.router)
126
- app.use(api.http.express.notFoundRouter)
127
-
128
- app.listen(3000, () => {
129
- console.log('Express server started on port 3000. API available at http://localhost:3000/api')
130
- }).on('error', (err) => {
131
- console.error('Failed to start server:', err)
132
- process.exit(1)
133
- }).on('error', (err) => {
134
- console.error('Failed to start server:', err)
135
- process.exit(1)
136
- })
@@ -1,63 +0,0 @@
1
- import knexLib from 'knex'
2
- import { createCustomIdPropertyApi } from '../tests/fixtures/api-configs.js'
3
- import { createJsonApiDocument } from '../tests/helpers/test-utils.js'
4
-
5
- async function run () {
6
- const knex = knexLib({ client: 'better-sqlite3', connection: { filename: ':memory:' }, useNullAsDefault: true })
7
- const api = await createCustomIdPropertyApi(knex)
8
-
9
- const country = await api.resources.countries.post({
10
- inputRecord: createJsonApiDocument('countries', { name: 'USA', code: 'US' })
11
- })
12
-
13
- const book = await api.resources.books.post({
14
- inputRecord: createJsonApiDocument('books',
15
- { title: 'Test Book' },
16
- { country: { data: { type: 'countries', id: country.data.id } } }
17
- )
18
- })
19
-
20
- const author = await api.resources.authors.post({
21
- inputRecord: createJsonApiDocument('authors', { name: 'Author' })
22
- })
23
-
24
- await api.resources.reviews.post({
25
- inputRecord: createJsonApiDocument('reviews', {
26
- rating: 5,
27
- title: 'Book review',
28
- content: 'Great book',
29
- reviewer_name: 'Reader',
30
- reviewable_type: 'books',
31
- reviewable_id: book.data.id
32
- })
33
- })
34
-
35
- await api.resources.reviews.post({
36
- inputRecord: createJsonApiDocument('reviews', {
37
- rating: 4,
38
- title: 'Author review',
39
- content: 'Great author',
40
- reviewer_name: 'Fan',
41
- reviewable_type: 'authors',
42
- reviewable_id: author.data.id
43
- })
44
- })
45
-
46
- try {
47
- const res = await api.resources.reviews.query({
48
- queryParams: { filters: { reviewable_type: 'books' } }
49
- })
50
- console.log('Query succeeded with rows:', res.data.length)
51
- } catch (error) {
52
- console.error('Query failed:', error)
53
- if (error?.sql) {
54
- console.error('SQL:', error.sql)
55
- }
56
- }
57
-
58
- await knex.destroy()
59
- }
60
-
61
- run().catch((err) => {
62
- console.error('Unexpected failure:', err)
63
- })
@@ -1,19 +0,0 @@
1
- import knexLib from 'knex'
2
- import { createCustomIdPropertyApi } from '../tests/fixtures/api-configs.js'
3
-
4
- async function run () {
5
- const knex = knexLib({ client: 'better-sqlite3', connection: { filename: ':memory:' }, useNullAsDefault: true })
6
- const api = await createCustomIdPropertyApi(knex)
7
- const descriptor = await api.anyapi.registry.getDescriptor('default', 'countries')
8
- console.log(JSON.stringify({
9
- idProperty: descriptor.idProperty,
10
- fields: descriptor.fields,
11
- canonicalFieldMap: descriptor.canonicalFieldMap,
12
- reverseAttributes: descriptor.reverseAttributes,
13
- }, null, 2))
14
- await knex.destroy()
15
- }
16
-
17
- run().catch((err) => {
18
- console.error('Failed to inspect descriptor', err)
19
- })
@@ -1,19 +0,0 @@
1
- import knexLib from 'knex'
2
- import { createCustomIdPropertyApi } from '../tests/fixtures/api-configs.js'
3
-
4
- async function run () {
5
- const knex = knexLib({ client: 'better-sqlite3', connection: { filename: ':memory:' }, useNullAsDefault: true })
6
- const api = await createCustomIdPropertyApi(knex)
7
- const descriptor = await api.anyapi.registry.getDescriptor('default', 'reviews')
8
- console.log(JSON.stringify({
9
- idProperty: descriptor.idProperty,
10
- fields: descriptor.fields,
11
- canonicalFieldMap: descriptor.canonicalFieldMap,
12
- reverseAttributes: descriptor.reverseAttributes,
13
- }, null, 2))
14
- await knex.destroy()
15
- }
16
-
17
- run().catch((err) => {
18
- console.error('Failed to inspect descriptor', err)
19
- })
@@ -1,133 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- import { spawn } from 'child_process'
4
- import path from 'path'
5
- import { fileURLToPath } from 'url'
6
-
7
- const __filename = fileURLToPath(import.meta.url)
8
- const __dirname = path.dirname(__filename)
9
- const projectRoot = path.join(__dirname, '..')
10
- const docsRoot = path.join(projectRoot, 'docs')
11
- const LOCAL_URL = 'http://localhost:4000/json-rest-api/'
12
- const BROWSER_DELAY_MS = 3000
13
-
14
- function parseMode (argv = []) {
15
- if (argv.includes('--serve') || argv.includes('--dev')) {
16
- return 'serve'
17
- }
18
-
19
- return 'build'
20
- }
21
-
22
- async function runCommand (command, args, options = {}) {
23
- return new Promise((resolve, reject) => {
24
- const proc = spawn(command, args, {
25
- stdio: 'inherit',
26
- shell: true,
27
- ...options
28
- })
29
-
30
- proc.on('error', reject)
31
- proc.on('exit', (code) => {
32
- if (code === 0) {
33
- resolve()
34
- return
35
- }
36
-
37
- reject(new Error(`Command failed with code ${code}`))
38
- })
39
- })
40
- }
41
-
42
- async function ensureBundlerDependencies () {
43
- try {
44
- await runCommand('bundle', ['check'], { cwd: docsRoot })
45
- } catch {
46
- console.log('Installing Jekyll dependencies...')
47
- await runCommand('bundle', ['install'], { cwd: docsRoot })
48
- }
49
- }
50
-
51
- async function openBrowser (url) {
52
- const platform = process.platform
53
- const command = platform === 'darwin'
54
- ? 'open'
55
- : platform === 'win32'
56
- ? 'start'
57
- : 'xdg-open'
58
-
59
- try {
60
- await runCommand(command, [url])
61
- console.log(`Opened browser at ${url}`)
62
- } catch {
63
- console.log(`Visit ${url} in your browser`)
64
- }
65
- }
66
-
67
- async function buildDocs () {
68
- console.log('Building documentation site...')
69
- await runCommand('bundle', ['exec', 'jekyll', 'build'], { cwd: docsRoot })
70
- }
71
-
72
- async function serveDocs () {
73
- console.log('Starting documentation server...')
74
-
75
- const jekyll = spawn('bundle', ['exec', 'jekyll', 'serve', '--watch'], {
76
- cwd: docsRoot,
77
- stdio: 'pipe',
78
- shell: true
79
- })
80
-
81
- jekyll.stdout.on('data', (data) => {
82
- const output = data.toString()
83
- process.stdout.write(output)
84
-
85
- if (output.includes('Server running') || output.includes('Server address:')) {
86
- setTimeout(() => openBrowser(LOCAL_URL), 1000)
87
- }
88
- })
89
-
90
- jekyll.stderr.on('data', (data) => {
91
- process.stderr.write(data)
92
- })
93
-
94
- process.on('SIGINT', () => {
95
- console.log('\nShutting down Jekyll server...')
96
- jekyll.kill()
97
- process.exit(0)
98
- })
99
-
100
- jekyll.on('error', (err) => {
101
- console.error('Jekyll server error:', err)
102
- process.exit(1)
103
- })
104
-
105
- jekyll.on('exit', (code) => {
106
- if (code !== 0 && code !== null) {
107
- console.error(`Jekyll server exited with code ${code}`)
108
- process.exit(code)
109
- }
110
- })
111
-
112
- setTimeout(() => openBrowser(LOCAL_URL), BROWSER_DELAY_MS)
113
- }
114
-
115
- async function main () {
116
- const mode = parseMode(process.argv.slice(2))
117
-
118
- try {
119
- await ensureBundlerDependencies()
120
-
121
- if (mode === 'serve') {
122
- await serveDocs()
123
- return
124
- }
125
-
126
- await buildDocs()
127
- } catch (error) {
128
- console.error('Error:', error.message)
129
- process.exit(1)
130
- }
131
- }
132
-
133
- main()
package/test-debug.js DELETED
@@ -1,59 +0,0 @@
1
- import { createBasicApi } from './tests/fixtures/api-configs.js'
2
- import { cleanTables, createJsonApiDocument } from './tests/helpers/test-utils.js'
3
- import knex from 'knex'
4
-
5
- const dbConfig = {
6
- client: 'better-sqlite3',
7
- connection: { filename: ':memory:' },
8
- useNullAsDefault: true
9
- }
10
-
11
- const db = knex(dbConfig)
12
-
13
- async function test () {
14
- const api = await createBasicApi(db)
15
-
16
- // Create test data
17
- await cleanTables(db, ['basic_countries', 'basic_publishers', 'basic_authors', 'basic_books', 'basic_book_authors'])
18
-
19
- // Create a country first
20
- const countryDoc = createJsonApiDocument('countries', { name: 'Test Country', code: 'TC' })
21
- const country = await api.resources.countries.post({ inputRecord: countryDoc })
22
-
23
- // Create an author
24
- const authorDoc = createJsonApiDocument('authors', { name: 'Test Author' })
25
- const author = await api.resources.authors.post({ inputRecord: authorDoc })
26
-
27
- // Create a book with proper relationships
28
- const bookDoc = {
29
- data: {
30
- type: 'books',
31
- attributes: { title: 'Test Book' },
32
- relationships: {
33
- country: { data: { type: 'countries', id: String(country.data.id) } }
34
- }
35
- }
36
- }
37
- const book = await api.resources.books.post({ inputRecord: bookDoc })
38
-
39
- // Create the many-to-many relationship
40
- await db('basic_book_authors').insert({
41
- author_id: author.data.id,
42
- book_id: book.data.id
43
- })
44
-
45
- // Now try to query authors with books included
46
- console.log('Querying authors with books included...')
47
- try {
48
- const result = await api.resources.authors.query({
49
- queryParams: { include: ['books'] }
50
- })
51
- console.log('Success!', JSON.stringify(result, null, 2))
52
- } catch (error) {
53
- console.error('Error:', error.message)
54
- }
55
-
56
- await db.destroy()
57
- }
58
-
59
- test().catch(console.error)