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,657 +0,0 @@
1
- import { resolveFieldInfo } from '../utils/descriptor-helpers.js'
2
-
3
- const JOIN_METHODS = new Set([
4
- 'join',
5
- 'innerJoin',
6
- 'leftJoin',
7
- 'leftOuterJoin',
8
- 'rightJoin',
9
- 'rightOuterJoin',
10
- 'outerJoin',
11
- 'crossJoin',
12
- ])
13
-
14
- const JOIN_ON_METHODS = new Set([
15
- 'on',
16
- 'andOn',
17
- 'orOn',
18
- 'onVal',
19
- 'andOnVal',
20
- 'orOnVal',
21
- ])
22
-
23
- const JOIN_ON_IN_METHODS = new Set([
24
- 'onIn',
25
- 'orOnIn',
26
- 'onNotIn',
27
- 'orOnNotIn',
28
- ])
29
-
30
- const JOIN_ON_NULL_METHODS = new Set([
31
- 'onNull',
32
- 'orOnNull',
33
- 'onNotNull',
34
- 'orOnNotNull',
35
- ])
36
-
37
- const JOIN_ON_BETWEEN_METHODS = new Set([
38
- 'onBetween',
39
- 'orOnBetween',
40
- 'onNotBetween',
41
- 'orOnNotBetween',
42
- ])
43
-
44
- const WHERE_METHODS = new Set([
45
- 'where',
46
- 'andWhere',
47
- 'orWhere',
48
- 'whereNot',
49
- 'andWhereNot',
50
- 'orWhereNot',
51
- 'having',
52
- 'orHaving',
53
- ])
54
-
55
- const WHERE_IN_METHODS = new Set([
56
- 'whereIn',
57
- 'orWhereIn',
58
- 'whereNotIn',
59
- 'orWhereNotIn',
60
- ])
61
-
62
- const BETWEEN_METHODS = new Set(['whereBetween', 'orWhereBetween', 'whereNotBetween', 'orWhereNotBetween'])
63
-
64
- const NULL_METHODS = new Set(['whereNull', 'orWhereNull', 'whereNotNull', 'orWhereNotNull'])
65
-
66
- const ORDER_METHODS = new Set(['orderBy', 'orderByRaw'])
67
-
68
- const GROUP_METHODS = new Set(['groupBy', 'groupByRaw'])
69
-
70
- const SELECT_METHODS = new Set(['select', 'columns', 'distinct'])
71
-
72
- const AGGREGATE_METHODS = new Set(['count', 'min', 'max', 'sum', 'avg'])
73
-
74
- const isFunction = (value) => typeof value === 'function'
75
- const isObject = (value) => value && typeof value === 'object' && !Array.isArray(value)
76
-
77
- const SQL_OPERATORS = new Set([
78
- '=',
79
- '!=',
80
- '<>',
81
- '<',
82
- '<=',
83
- '>',
84
- '>=',
85
- 'like',
86
- 'not like',
87
- 'ilike',
88
- 'not ilike',
89
- 'is',
90
- 'is not',
91
- 'is null',
92
- 'is not null',
93
- ])
94
-
95
- const parseTableExpression = (expression) => {
96
- if (expression && typeof expression === 'object' && !Array.isArray(expression)) {
97
- const [alias, tableName] = Object.entries(expression)[0] || []
98
- if (!alias) {
99
- return { original: expression }
100
- }
101
- return { alias, tableName, original: expression }
102
- }
103
-
104
- if (typeof expression !== 'string') {
105
- return { original: expression }
106
- }
107
-
108
- const raw = expression.trim()
109
- if (raw === '') {
110
- return { original: expression }
111
- }
112
-
113
- const asMatch = raw.split(/\s+as\s+/i)
114
- if (asMatch.length === 2) {
115
- return { tableName: asMatch[0].trim(), alias: asMatch[1].trim(), original: expression }
116
- }
117
-
118
- const parts = raw.split(/\s+/)
119
- if (parts.length === 2) {
120
- return { tableName: parts[0].trim(), alias: parts[1].trim(), original: expression }
121
- }
122
-
123
- return { tableName: raw, alias: raw, original: expression }
124
- }
125
-
126
- const translateObjectKeys = (adapter, obj) => {
127
- const result = {}
128
- for (const [key, value] of Object.entries(obj)) {
129
- result[adapter.translateColumn(key)] = value
130
- }
131
- return result
132
- }
133
-
134
- const translateJoinObject = (adapter, obj) => {
135
- const result = {}
136
- for (const [lhs, rhs] of Object.entries(obj)) {
137
- const translatedKey = typeof lhs === 'string' ? adapter.translateColumn(lhs) : lhs
138
- const translatedValue = typeof rhs === 'string' ? adapter.translateColumn(rhs) : rhs
139
- result[translatedKey] = translatedValue
140
- }
141
- return result
142
- }
143
-
144
- const isRaw = (value) => Boolean(value && typeof value === 'object' && typeof value.toSQL === 'function' && value.toSQL().method)
145
-
146
- const ensureArray = (value) => (Array.isArray(value) ? value : [value])
147
-
148
- export async function preloadRelatedDescriptors ({ registry, descriptor }) {
149
- const visited = new Map()
150
- const queue = [descriptor.resource]
151
- visited.set(descriptor.resource, descriptor)
152
-
153
- while (queue.length > 0) {
154
- const resource = queue.pop()
155
- const current = visited.get(resource)
156
- if (!current) continue
157
-
158
- const relatedTargets = new Set()
159
-
160
- Object.values(current.belongsTo || {}).forEach((info) => {
161
- if (info?.target) relatedTargets.add(info.target)
162
- })
163
-
164
- Object.values(current.relationships || {}).forEach((info) => {
165
- if (info?.target) relatedTargets.add(info.target)
166
- })
167
-
168
- Object.values(current.manyToMany || {}).forEach((info) => {
169
- if (info?.target) relatedTargets.add(info.target)
170
- if (info?.through) relatedTargets.add(info.through)
171
- })
172
-
173
- Object.values(current.polymorphicBelongsTo || {}).forEach((info) => {
174
- ensureArray(info?.types || []).forEach((type) => relatedTargets.add(type))
175
- })
176
-
177
- for (const target of relatedTargets) {
178
- if (!target || visited.has(target)) continue
179
- const nextDescriptor = await registry.getDescriptor(current.tenant, target)
180
- if (!nextDescriptor) continue
181
- visited.set(target, nextDescriptor)
182
- queue.push(target)
183
- }
184
- }
185
-
186
- return visited
187
- }
188
-
189
- export class AnyapiQueryAdapter {
190
- constructor ({
191
- descriptor,
192
- db,
193
- registry,
194
- descriptorsMap,
195
- resourceToTableName,
196
- tableNameToResource,
197
- log,
198
- }) {
199
- this.descriptor = descriptor
200
- this.db = db
201
- this.registry = registry
202
- this.descriptorsMap = descriptorsMap
203
- this.resourceToTableName = resourceToTableName || new Map()
204
- this.tableNameToResource = tableNameToResource || new Map()
205
- this.log = log || console
206
-
207
- this.primaryAlias = this.resourceToTableName.get(descriptor.resource) || descriptor.resource
208
- this.aliasDescriptors = new Map()
209
- this.aliasDescriptors.set(this.primaryAlias, descriptor)
210
-
211
- this.aliasDescriptors.set(descriptor.resource, descriptor)
212
- this.aliasActualNames = new Map()
213
- this.aliasActualNames.set(this.primaryAlias, this.primaryAlias)
214
- this.aliasActualNames.set(descriptor.resource, this.primaryAlias)
215
-
216
- const physicalTableName = this.resourceToTableName.get(descriptor.resource)
217
- if (physicalTableName) {
218
- this.aliasDescriptors.set(physicalTableName, descriptor)
219
- this.aliasActualNames.set(physicalTableName, this.primaryAlias)
220
- }
221
-
222
- this.builders = new WeakMap()
223
-
224
- this.builder = db({ [this.primaryAlias]: descriptor.canonical.tableName })
225
- .where(`${this.primaryAlias}.${descriptor.canonical.tenantColumn}`, descriptor.tenant)
226
- .where(`${this.primaryAlias}.${descriptor.canonical.resourceColumn}`, descriptor.resource)
227
-
228
- this.proxy = this.#createProxy(this.builder)
229
- }
230
-
231
- get tableAlias () {
232
- return this.primaryAlias
233
- }
234
-
235
- get query () {
236
- return this.proxy
237
- }
238
-
239
- translateColumn (reference) {
240
- if (typeof reference !== 'string') {
241
- return reference
242
- }
243
-
244
- const trimmed = reference.trim()
245
- if (trimmed === '*' || trimmed === '') {
246
- return reference
247
- }
248
-
249
- const parts = trimmed.split('.')
250
- let requestedAlias
251
- let field
252
-
253
- if (parts.length === 1) {
254
- requestedAlias = this.primaryAlias
255
- field = parts[0]
256
- } else {
257
- requestedAlias = parts.shift()
258
- field = parts.join('.')
259
- }
260
-
261
- if (!field) {
262
- return reference
263
- }
264
-
265
- let descriptor = this.aliasDescriptors.get(requestedAlias)
266
- if (!descriptor) {
267
- const resource = this.tableNameToResource.get(requestedAlias) || requestedAlias
268
- descriptor = this.descriptorsMap.get(resource)
269
- if (descriptor) {
270
- this.aliasDescriptors.set(requestedAlias, descriptor)
271
- const actualAlias = this.resourceToTableName.get(resource) || resource
272
- this.aliasActualNames.set(requestedAlias, actualAlias)
273
- }
274
- }
275
-
276
- if (!descriptor) {
277
- return reference
278
- }
279
-
280
- const info = resolveFieldInfo(descriptor, field)
281
- if (!info?.column) {
282
- return reference
283
- }
284
-
285
- const actualAlias = this.aliasActualNames.get(requestedAlias) ||
286
- this.resourceToTableName.get(descriptor.resource) ||
287
- descriptor.resource
288
-
289
- return `${actualAlias}.${info.column}`
290
- }
291
-
292
- translateColumns (columns) {
293
- if (Array.isArray(columns)) {
294
- return columns.map((column) => this.translateColumn(column))
295
- }
296
- return this.translateColumn(columns)
297
- }
298
-
299
- registerAlias (alias, descriptor) {
300
- if (!alias || !descriptor) return
301
- this.aliasDescriptors.set(alias, descriptor)
302
- this.aliasActualNames.set(alias, alias)
303
- }
304
-
305
- #wrapQueryCallback (callback) {
306
- if (!isFunction(callback)) return callback
307
- const adapter = this
308
-
309
- return function wrappedCallback (...args) {
310
- const callbackTarget = this
311
- const callbackProxy = adapter.#createProxy(callbackTarget)
312
- const callbackArgs = args.map((arg) => arg === callbackTarget ? callbackProxy : arg)
313
- return callback.apply(callbackProxy, callbackArgs)
314
- }
315
- }
316
-
317
- #wrapJoinCallback (callback) {
318
- if (!isFunction(callback)) return callback
319
- const adapter = this
320
-
321
- return function wrappedCallback (...args) {
322
- const callbackTarget = this
323
- const callbackProxy = adapter.#createJoinProxy(callbackTarget)
324
- const callbackArgs = args.map((arg) => arg === callbackTarget ? callbackProxy : arg)
325
- return callback.apply(callbackProxy, callbackArgs)
326
- }
327
- }
328
-
329
- #translateWhereArgs (args, { joinClause = false } = {}) {
330
- if (args.length === 0) return args
331
- const [first, ...rest] = args
332
-
333
- if (isFunction(first)) {
334
- const wrappedGroup = joinClause
335
- ? this.#wrapJoinCallback(first)
336
- : this.#wrapQueryCallback(first)
337
- return [wrappedGroup, ...rest]
338
- }
339
-
340
- if (isObject(first) && !isRaw(first)) {
341
- return [translateObjectKeys(this, first), ...rest]
342
- }
343
-
344
- if (Array.isArray(first)) {
345
- return [first.map((item) => (typeof item === 'string' ? this.translateColumn(item) : item)), ...rest]
346
- }
347
-
348
- const translatedFirst = this.translateColumn(first)
349
-
350
- if (rest.length > 0 && isFunction(rest[rest.length - 1])) {
351
- const newRest = [...rest]
352
- newRest[newRest.length - 1] = this.#wrapQueryCallback(rest[rest.length - 1])
353
- return [translatedFirst, ...newRest]
354
- }
355
-
356
- return [translatedFirst, ...rest]
357
- }
358
-
359
- #translateWhereInArgs (args) {
360
- if (args.length === 0) return args
361
- const [column, values, ...rest] = args
362
- const translatedColumn = this.translateColumn(column)
363
-
364
- if (isFunction(values)) {
365
- return [translatedColumn, this.#wrapQueryCallback(values), ...rest]
366
- }
367
-
368
- return [translatedColumn, values, ...rest]
369
- }
370
-
371
- #translateBetweenArgs (args) {
372
- if (args.length === 0) return args
373
- const [column, range, ...rest] = args
374
- return [this.translateColumn(column), range, ...rest]
375
- }
376
-
377
- #translateNullArgs (args) {
378
- if (args.length === 0) return args
379
- const [column, ...rest] = args
380
- return [this.translateColumn(column), ...rest]
381
- }
382
-
383
- #translateOrderArgs (method, args) {
384
- if (args.length === 0) return args
385
-
386
- if (method === 'orderByRaw') {
387
- return args
388
- }
389
-
390
- const [column, direction, ...rest] = args
391
-
392
- if (Array.isArray(column)) {
393
- return [column.map((entry) => {
394
- if (typeof entry === 'string') {
395
- return this.translateColumn(entry)
396
- }
397
- if (isObject(entry)) {
398
- const [col, dir] = Object.entries(entry)[0] || []
399
- if (!col) return entry
400
- return { [this.translateColumn(col)]: dir }
401
- }
402
- return entry
403
- }), direction, ...rest]
404
- }
405
-
406
- if (isObject(column) && column.column) {
407
- return [{ ...column, column: this.translateColumn(column.column) }, direction, ...rest]
408
- }
409
-
410
- return [this.translateColumn(column), direction, ...rest]
411
- }
412
-
413
- #translateGroupArgs (args) {
414
- if (args.length === 0) return args
415
- const [first, ...rest] = args
416
- if (Array.isArray(first)) {
417
- return [first.map((entry) => (typeof entry === 'string' ? this.translateColumn(entry) : entry)), ...rest]
418
- }
419
- return [this.translateColumn(first), ...rest]
420
- }
421
-
422
- #translateSelectArgs (args) {
423
- if (args.length === 0) return args
424
- const [first, ...rest] = args
425
-
426
- if (Array.isArray(first)) {
427
- return [first.map((entry) => (typeof entry === 'string' ? this.translateColumn(entry) : entry)), ...rest]
428
- }
429
-
430
- if (typeof first === 'string') {
431
- return [this.translateColumn(first), ...rest]
432
- }
433
-
434
- return [first, ...rest]
435
- }
436
-
437
- #translateAggregateArgs (args) {
438
- if (args.length === 0) return args
439
- const [column, options] = args
440
- if (Array.isArray(column)) {
441
- return [column.map((entry) => (typeof entry === 'string' ? this.translateColumn(entry) : entry)), options]
442
- }
443
- if (typeof column === 'string') {
444
- return [this.translateColumn(column), options]
445
- }
446
- return args
447
- }
448
-
449
- #translateOnArgs (args) {
450
- if (args.length === 0) return args
451
- const [first, ...rest] = args
452
-
453
- if (isFunction(first)) {
454
- return [this.#wrapJoinCallback(first), ...rest]
455
- }
456
-
457
- if (isObject(first) && !isRaw(first)) {
458
- return [translateJoinObject(this, first), ...rest]
459
- }
460
-
461
- const translatedFirst = typeof first === 'string' ? this.translateColumn(first) : first
462
-
463
- if (rest.length === 0) {
464
- return [translatedFirst]
465
- }
466
-
467
- const translatedRest = rest.map((value, index) => {
468
- if (isFunction(value)) {
469
- return this.#wrapJoinCallback(value)
470
- }
471
- if (typeof value !== 'string') {
472
- return value
473
- }
474
- const lower = value.toLowerCase()
475
- if (SQL_OPERATORS.has(lower)) {
476
- return value
477
- }
478
- return this.translateColumn(value)
479
- })
480
-
481
- return [translatedFirst, ...translatedRest]
482
- }
483
-
484
- #translateOnInArgs (args) {
485
- if (args.length === 0) return args
486
- const [column, values, ...rest] = args
487
- const translatedColumn = typeof column === 'string' ? this.translateColumn(column) : column
488
-
489
- if (isFunction(values)) {
490
- return [translatedColumn, this.#wrapQueryCallback(values), ...rest]
491
- }
492
-
493
- return [translatedColumn, values, ...rest]
494
- }
495
-
496
- #translateOnBetweenArgs (args) {
497
- if (args.length === 0) return args
498
- const [column, range, ...rest] = args
499
- return [typeof column === 'string' ? this.translateColumn(column) : column, range, ...rest]
500
- }
501
-
502
- #translateOnNullArgs (args) {
503
- if (args.length === 0) return args
504
- const [column, ...rest] = args
505
- return [typeof column === 'string' ? this.translateColumn(column) : column, ...rest]
506
- }
507
-
508
- #addResourceConstraints (joinClause, alias, descriptor) {
509
- joinClause.on(`${alias}.${descriptor.canonical.tenantColumn}`, this.db.raw('?', [descriptor.tenant]))
510
- joinClause.andOn(`${alias}.${descriptor.canonical.resourceColumn}`, this.db.raw('?', [descriptor.resource]))
511
- }
512
-
513
- #createJoinProxy (joinClause) {
514
- const adapter = this
515
- const handler = {
516
- get (target, prop, receiver) {
517
- const original = target[prop]
518
- if (typeof original !== 'function') {
519
- return Reflect.get(target, prop, receiver)
520
- }
521
- return (...args) => {
522
- let translatedArgs = args
523
-
524
- if (WHERE_METHODS.has(prop)) {
525
- translatedArgs = adapter.#translateWhereArgs(args, { joinClause: true })
526
- } else if (WHERE_IN_METHODS.has(prop)) {
527
- translatedArgs = adapter.#translateWhereInArgs(args)
528
- } else if (BETWEEN_METHODS.has(prop)) {
529
- translatedArgs = adapter.#translateBetweenArgs(args)
530
- } else if (NULL_METHODS.has(prop)) {
531
- translatedArgs = adapter.#translateNullArgs(args)
532
- } else if (JOIN_ON_METHODS.has(prop)) {
533
- translatedArgs = adapter.#translateOnArgs(args)
534
- } else if (JOIN_ON_IN_METHODS.has(prop)) {
535
- translatedArgs = adapter.#translateOnInArgs(args)
536
- } else if (JOIN_ON_BETWEEN_METHODS.has(prop)) {
537
- translatedArgs = adapter.#translateOnBetweenArgs(args)
538
- } else if (JOIN_ON_NULL_METHODS.has(prop)) {
539
- translatedArgs = adapter.#translateOnNullArgs(args)
540
- }
541
-
542
- const result = original.apply(target, translatedArgs)
543
- return result === target ? receiver : result
544
- }
545
- },
546
- }
547
-
548
- return new Proxy(joinClause, handler)
549
- }
550
-
551
- #handleJoin (method, args) {
552
- if (args.length === 0) return args
553
- const [tableExpression, ...rest] = args
554
- const parsed = parseTableExpression(tableExpression)
555
-
556
- if (!parsed.tableName || !parsed.alias) {
557
- return args
558
- }
559
-
560
- const resource = this.tableNameToResource.get(parsed.tableName) || parsed.tableName
561
- const descriptor = this.descriptorsMap.get(resource)
562
-
563
- if (!descriptor) {
564
- return args
565
- }
566
-
567
- this.registerAlias(parsed.alias, descriptor)
568
-
569
- const joinTarget = { [parsed.alias]: descriptor.canonical.tableName }
570
-
571
- if (rest.length === 0) {
572
- return [joinTarget]
573
- }
574
-
575
- const [first, ...remaining] = rest
576
-
577
- if (isFunction(first)) {
578
- const joinCallback = first
579
- const wrappedCallback = (joinClause) => {
580
- this.#addResourceConstraints(joinClause, parsed.alias, descriptor)
581
- const proxyClause = this.#createJoinProxy(joinClause)
582
- return joinCallback.call(proxyClause, proxyClause)
583
- }
584
- return [joinTarget, wrappedCallback, ...remaining]
585
- }
586
-
587
- const translatedArgs = rest.map((value, index) => {
588
- if (index === 0 || index === 1) {
589
- return this.translateColumn(value)
590
- }
591
- return value
592
- })
593
-
594
- return [joinTarget, ...translatedArgs]
595
- }
596
-
597
- #createProxy (builder) {
598
- const adapter = this
599
- const handler = {
600
- get (target, prop, receiver) {
601
- if (prop === '__adapter__') {
602
- return adapter
603
- }
604
-
605
- const value = target[prop]
606
- if (typeof value !== 'function') {
607
- return Reflect.get(target, prop, receiver)
608
- }
609
-
610
- if (JOIN_METHODS.has(prop)) {
611
- return (...args) => {
612
- const translatedArgs = adapter.#handleJoin(prop, args)
613
- const result = value.apply(target, translatedArgs)
614
- return result === target ? receiver : result
615
- }
616
- }
617
-
618
- return (...args) => {
619
- let translatedArgs = args
620
-
621
- if (WHERE_METHODS.has(prop)) {
622
- translatedArgs = adapter.#translateWhereArgs(args)
623
- } else if (WHERE_IN_METHODS.has(prop)) {
624
- translatedArgs = adapter.#translateWhereInArgs(args)
625
- } else if (BETWEEN_METHODS.has(prop)) {
626
- translatedArgs = adapter.#translateBetweenArgs(args)
627
- } else if (NULL_METHODS.has(prop)) {
628
- translatedArgs = adapter.#translateNullArgs(args)
629
- } else if (JOIN_ON_METHODS.has(prop)) {
630
- translatedArgs = adapter.#translateOnArgs(args)
631
- } else if (JOIN_ON_IN_METHODS.has(prop)) {
632
- translatedArgs = adapter.#translateOnInArgs(args)
633
- } else if (JOIN_ON_BETWEEN_METHODS.has(prop)) {
634
- translatedArgs = adapter.#translateOnBetweenArgs(args)
635
- } else if (JOIN_ON_NULL_METHODS.has(prop)) {
636
- translatedArgs = adapter.#translateOnNullArgs(args)
637
- } else if (ORDER_METHODS.has(prop)) {
638
- translatedArgs = adapter.#translateOrderArgs(prop, args)
639
- } else if (GROUP_METHODS.has(prop)) {
640
- translatedArgs = adapter.#translateGroupArgs(args)
641
- } else if (SELECT_METHODS.has(prop)) {
642
- translatedArgs = adapter.#translateSelectArgs(args)
643
- } else if (AGGREGATE_METHODS.has(prop)) {
644
- translatedArgs = adapter.#translateAggregateArgs(args)
645
- }
646
-
647
- const result = value.apply(target, translatedArgs)
648
- return result === target ? receiver : result
649
- }
650
- },
651
- }
652
-
653
- const proxy = new Proxy(builder, handler)
654
- this.builders.set(proxy, builder)
655
- return proxy
656
- }
657
- }
@@ -1,47 +0,0 @@
1
- const RETURN_RECORD_METHODS = ['post', 'put', 'patch']
2
- const RETURN_RECORD_MODES = ['no', 'minimal', 'full']
3
-
4
- export function normalizeReturnRecordMode (value, defaultValue = 'no') {
5
- if (value === true) return 'full'
6
- if (value === false) return 'no'
7
- if (RETURN_RECORD_MODES.includes(value)) return value
8
-
9
- if (defaultValue === true) return 'full'
10
- if (defaultValue === false) return 'no'
11
- if (RETURN_RECORD_MODES.includes(defaultValue)) return defaultValue
12
-
13
- return 'no'
14
- }
15
-
16
- function defaultModeForMethod (defaultValue, method) {
17
- if (defaultValue && typeof defaultValue === 'object' && !Array.isArray(defaultValue)) {
18
- return normalizeReturnRecordMode(defaultValue[method], 'no')
19
- }
20
-
21
- return normalizeReturnRecordMode(defaultValue, 'no')
22
- }
23
-
24
- export function normalizeReturnRecordSetting (value, defaultValue = 'no') {
25
- if (value === undefined) {
26
- return Object.fromEntries(
27
- RETURN_RECORD_METHODS.map(method => [
28
- method,
29
- defaultModeForMethod(defaultValue, method)
30
- ])
31
- )
32
- }
33
-
34
- if (value && typeof value === 'object' && !Array.isArray(value)) {
35
- return Object.fromEntries(
36
- RETURN_RECORD_METHODS.map(method => [
37
- method,
38
- normalizeReturnRecordMode(value[method], defaultModeForMethod(defaultValue, method))
39
- ])
40
- )
41
- }
42
-
43
- const normalized = normalizeReturnRecordMode(value, defaultModeForMethod(defaultValue, 'post'))
44
- return Object.fromEntries(
45
- RETURN_RECORD_METHODS.map(method => [method, normalized])
46
- )
47
- }