json-rest-api 1.0.7 → 1.0.8

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 (146) hide show
  1. package/.claude/agents/library-guide-expert.md +29 -0
  2. package/.claude/agents/rest-api-internals-expert.md +53 -0
  3. package/CLAUDE.md +139 -1
  4. package/README.md +2 -5
  5. package/docTest.js +238 -0
  6. package/docs/GUIDE/GUIDE_1_Initial_Setup.md +955 -0
  7. package/docs/GUIDE/GUIDE_2_1_The_Starting_Point.md +54 -0
  8. package/docs/GUIDE/GUIDE_2_2_Manipulating_And_Searching_Tables.md +750 -0
  9. package/docs/GUIDE/GUIDE_2_3_BelongsTo_Relationships.md +494 -0
  10. package/docs/GUIDE/GUIDE_2_4_HasMany_Records.md +398 -0
  11. package/docs/GUIDE/GUIDE_2_5_HasMany_Polymorphic.md +882 -0
  12. package/docs/GUIDE/GUIDE_2_6_Many_To_Many.md +709 -0
  13. package/docs/GUIDE/GUIDE_2_7_Pagination_And_Ordering.md +1233 -0
  14. package/docs/GUIDE/GUIDE_2_8_Effects_of_PUT_and_PATCH.md +796 -0
  15. package/{README_X_Relationship_Plugin.md → docs/GUIDE/GUIDE_2_9_Relationships_Urls.md} +1 -4
  16. package/docs/GUIDE/GUIDE_2_Data_And_Relations.md +66 -0
  17. package/docs/GUIDE/GUIDE_3_Field_Transformations.md +1246 -0
  18. package/docs/GUIDE/GUIDE_5_Non-Db_Stores.md +0 -0
  19. package/docs/GUIDE/GUIDE_7_Hooks_Data_Management_And_Plugins.md +1490 -0
  20. package/{README_X_SocketIO.md → docs/GUIDE/GUIDE_X_SocketIO.md} +17 -17
  21. package/docs/GUIDE/index.md +58 -0
  22. package/docs/GUIDE.md +61 -2238
  23. package/docs/ONBOARDING/Computed_Properties_And_Sparse_Fields.md +398 -0
  24. package/docs/README.md +57 -2239
  25. package/{README_2_Computed_and_hidden_fields.md → docs/README_VARIOUS/GUIDE_2_Computed_and_hidden_fields.md} +22 -17
  26. package/docs/README_VARIOUS/GUIDE_IN_DOCS.md +2277 -0
  27. package/{README.ORIG.md → docs/README_VARIOUS/README.ORIG.md} +8 -8
  28. package/{README_8_Polymorphic_Relationships.md → docs/README_VARIOUS/README_8_Polymorphic_Relationships.md} +1 -1
  29. package/docs/README_VARIOUS/README_IN_DOCS.ms +2277 -0
  30. package/{RELATIONSHIPS_ADVANCED.md → docs/README_VARIOUS/RELATIONSHIPS_ADVANCED.md} +3 -3
  31. package/{correct-schema-format.md → docs/README_VARIOUS/correct-schema-format.md} +13 -13
  32. package/{pagination.md → docs/README_VARIOUS/pagination.md} +11 -8
  33. package/docs/assets/css/style.scss +78 -5
  34. package/docs/index.md +2 -12
  35. package/helper-reorganization-plan.md +85 -0
  36. package/helper-usage-analysis.md +94 -0
  37. package/package.json +5 -4
  38. package/plugins/core/bulk-operations-plugin.js +2 -2
  39. package/plugins/core/connectors/express-plugin.js +90 -32
  40. package/plugins/core/lib/querying/knex-cross-table-search.js +609 -0
  41. package/plugins/core/lib/querying/knex-json-api-transformers-querying.js +491 -0
  42. package/plugins/core/lib/querying/knex-pagination-helpers.js +544 -0
  43. package/plugins/core/lib/{knex-process-includes.js → querying/knex-process-includes.js} +65 -50
  44. package/plugins/core/lib/querying/knex-query-helpers-base.js +74 -0
  45. package/plugins/core/lib/{knex-query-helpers.js → querying/knex-query-helpers.js} +416 -180
  46. package/plugins/core/lib/{knex-relationship-includes.js → querying/knex-relationship-includes.js} +430 -114
  47. package/plugins/core/lib/querying/knex-window-queries.js +328 -0
  48. package/plugins/core/lib/querying-writing/compile-schemas.js +328 -0
  49. package/plugins/core/{utils → lib/querying-writing}/connectors-query-parser.js +62 -57
  50. package/plugins/core/lib/{database-capabilities.js → querying-writing/database-capabilities.js} +113 -7
  51. package/plugins/core/lib/querying-writing/database-value-normalizers.js +369 -0
  52. package/plugins/core/lib/querying-writing/default-data-helpers.js +72 -0
  53. package/plugins/core/{utils → lib/querying-writing}/field-utils.js +41 -31
  54. package/plugins/core/{utils → lib/querying-writing}/knex-constants.js +1 -0
  55. package/plugins/core/lib/{knex-field-helpers.js → querying-writing/knex-field-helpers.js} +190 -57
  56. package/plugins/core/lib/querying-writing/knex-json-api-transformers.js +162 -0
  57. package/plugins/core/lib/{payload-validators.js → querying-writing/payload-validators.js} +297 -217
  58. package/plugins/core/lib/querying-writing/schema-helpers.js +497 -0
  59. package/plugins/core/lib/querying-writing/scope-validations.js +431 -0
  60. package/plugins/core/lib/{simplified-helpers.js → querying-writing/simplified-helpers.js} +216 -176
  61. package/plugins/core/lib/writing/knex-json-api-transformers-writing.js +110 -0
  62. package/plugins/core/lib/writing/many-to-many-manipulations.js +300 -0
  63. package/plugins/core/lib/writing/relationship-processor.js +252 -0
  64. package/plugins/core/rest-api-knex-plugin.js +183 -62
  65. package/plugins/core/rest-api-plugin-hooks/compile-resource-schemas.js +7 -0
  66. package/plugins/core/rest-api-plugin-hooks/register-relationship-routes.js +72 -0
  67. package/plugins/core/rest-api-plugin-hooks/register-scope-routes.js +102 -0
  68. package/plugins/core/rest-api-plugin-hooks/turn-scope-init-into-vars.js +50 -0
  69. package/plugins/core/rest-api-plugin-hooks/validate-include-configurations.js +39 -0
  70. package/plugins/core/rest-api-plugin-methods/add-route.js +23 -0
  71. package/plugins/core/rest-api-plugin-methods/check-permissions.js +18 -0
  72. package/plugins/core/rest-api-plugin-methods/common.js +605 -0
  73. package/plugins/core/rest-api-plugin-methods/delete-relationship.js +115 -0
  74. package/plugins/core/rest-api-plugin-methods/delete.js +109 -0
  75. package/plugins/core/rest-api-plugin-methods/enrich-attributes.js +186 -0
  76. package/plugins/core/rest-api-plugin-methods/get-related.js +295 -0
  77. package/plugins/core/rest-api-plugin-methods/get-relationship.js +62 -0
  78. package/plugins/core/rest-api-plugin-methods/get.js +187 -0
  79. package/plugins/core/rest-api-plugin-methods/patch-relationship.js +56 -0
  80. package/plugins/core/rest-api-plugin-methods/patch.js +193 -0
  81. package/plugins/core/rest-api-plugin-methods/post-relationship.js +104 -0
  82. package/plugins/core/rest-api-plugin-methods/post.js +157 -0
  83. package/plugins/core/rest-api-plugin-methods/put.js +257 -0
  84. package/plugins/core/rest-api-plugin-methods/query.js +208 -0
  85. package/plugins/core/rest-api-plugin.js +108 -2498
  86. package/plugins/core/socketio-plugin.js +28 -5
  87. package/plugins/storage/local-storage.js +2 -2
  88. package/tests/computed-fields.test.js +483 -0
  89. package/tests/cors.test.js +4 -4
  90. package/tests/curl.test.js +5 -4
  91. package/tests/custom-idproperty-relationships.test.js +891 -0
  92. package/tests/field-getters.test.js +353 -0
  93. package/tests/field-setters.test.js +310 -0
  94. package/tests/fixtures/api-configs.js +800 -28
  95. package/tests/jwt-auth.test.js +0 -2
  96. package/tests/nested-includes.test.js +1 -1
  97. package/tests/pagination-cursor-multifield.test.js +514 -0
  98. package/tests/pagination-enhanced.test.js +12 -9
  99. package/tests/polymorphic-relationships.test.js +10 -8
  100. package/tests/polymorphic-simple.test.js +6 -4
  101. package/tests/positioning-advanced.test.js +12 -12
  102. package/tests/positioning-extended.test.js +25 -25
  103. package/tests/positioning-multifilter.test.js +24 -24
  104. package/tests/positioning.test.js +3 -3
  105. package/tests/query-limits.test.js +24 -8
  106. package/tests/relationship-endpoints.test.js +4 -5
  107. package/tests/relationships.test.js +0 -1
  108. package/tests/socketio.test.js +12 -1
  109. package/tests/virtual-fields.test.js +13 -13
  110. package/PLAN_FOR_WEBSOCKETS.md +0 -398
  111. package/README_1_Initial_Setup.md +0 -393
  112. package/README_X_Hooks.md +0 -647
  113. package/TODO_rest-api-plugin-extras.todo +0 -23
  114. package/connectors_ideas/express-plugin.js +0 -120
  115. package/connectors_ideas/http-plugin.js +0 -138
  116. package/docs/examples/express-plugin.js +0 -366
  117. package/example.js +0 -301
  118. package/plugins/core/HELPER_MODULES.md +0 -285
  119. package/plugins/core/lib/compile-schemas.js +0 -245
  120. package/plugins/core/lib/database-value-normalizers.js +0 -192
  121. package/plugins/core/lib/default-data-helpers.js +0 -100
  122. package/plugins/core/lib/knex-cross-table-search.js +0 -442
  123. package/plugins/core/lib/knex-json-api-helpers.js +0 -84
  124. package/plugins/core/lib/knex-json-api-transformers.js +0 -449
  125. package/plugins/core/lib/knex-pagination-helpers.js +0 -193
  126. package/plugins/core/lib/knex-window-queries.js +0 -142
  127. package/plugins/core/lib/many-to-many-manipulations.js +0 -343
  128. package/plugins/core/lib/relationship-processor.js +0 -278
  129. package/plugins/core/lib/schema-helpers.js +0 -343
  130. package/plugins/core/lib/scope-validations.js +0 -252
  131. package/plugins/core/rest-api-relationships-plugin.js +0 -700
  132. package/plugins/core/utils/knex-query-helpers-base.js +0 -56
  133. /package/{README_X_Permissions.md → docs/GUIDE/GUIDE_4_Authentication_And_Permissions.md} +0 -0
  134. /package/{README_X_Positioning.md → docs/GUIDE/GUIDE_6_Positioning.md} +0 -0
  135. /package/{README_X_Bulk_Operations.md → docs/GUIDE/GUIDE_X_Bulk_Operations.md} +0 -0
  136. /package/{README_X_Cors.md → docs/GUIDE/GUIDE_X_Cors.md} +0 -0
  137. /package/{README_X_Multihome.md → docs/GUIDE/GUIDE_X_Multihome.md} +0 -0
  138. /package/{README_X_Appendices.md → docs/GUIDE/GUIDE_Y_Appendices.md} +0 -0
  139. /package/{QUERY_DEPTH_PROTECTION.md → docs/README_VARIOUS/QUERY_DEPTH_PROTECTION.md} +0 -0
  140. /package/{RELATIONSHIPS.md → docs/README_VARIOUS/RELATIONSHIPS.md} +0 -0
  141. /package/{WEBSOCKET_IMPLEMENTATION_STATUS.md → docs/README_VARIOUS/WEBSOCKET_IMPLEMENTATION_STATUS.md} +0 -0
  142. /package/{helper-functions-chart.md → docs/README_VARIOUS/helper-functions-chart.md} +0 -0
  143. /package/docs/{implementation-hidden-computed-fields.md → README_VARIOUS/implementation-hidden-computed-fields.md} +0 -0
  144. /package/docs/{include-limits-migration-guide.md → README_VARIOUS/include-limits-migration-guide.md} +0 -0
  145. /package/{readmeSnippets → docs/README_VARIOUS}/test_put_behavior.js +0 -0
  146. /package/{readmeSnippets → docs/README_VARIOUS}/test_put_fields_without_defaults.js +0 -0
package/example.js DELETED
@@ -1,301 +0,0 @@
1
- import { Api } from 'hooked-api';
2
- import { RestApiPlugin, RestApiKnexPlugin } from './index.js';
3
- import knexLib from 'knex';
4
- import util from 'util';
5
-
6
-
7
- // import { setupDatabase } from './setup-database.js';
8
-
9
- // Create a Knex instance connected to SQLite in-memory database
10
- const knex = knexLib({
11
- client: 'better-sqlite3',
12
- connection: {
13
- filename: ':memory:'
14
- },
15
- useNullAsDefault: true
16
- });
17
-
18
- // Create API instance
19
- const api = new Api({
20
- name: 'book-catalog-api',
21
- version: '1.0.0'
22
- });
23
-
24
- // Install plugins
25
- await api.use(RestApiPlugin);
26
- await api.use(RestApiKnexPlugin, { knex });
27
-
28
- // Define schemas for our book catalog system
29
-
30
- // Countries table
31
- await api.addResource('countries', {
32
- schema: {
33
- name: { type: 'string', required: true, max: 100 },
34
- code: { type: 'string', max: 2, unique: true }, // ISO country code
35
- },
36
- relationships: {
37
- publishers: { hasMany: 'publishers', foreignKey: 'country_id' },
38
- books: { hasMany: 'books', foreignKey: 'country_id' }
39
- },
40
- returnFullRecord: {
41
- post: false,
42
- patch: false,
43
- put: false
44
- },
45
- // tableName: 'stocazzo',
46
- // idProperty: 'anotherId'
47
- });
48
- await api.resources.countries.createKnexTable()
49
-
50
- // Publishers table
51
- await api.addResource('publishers', {
52
- schema: {
53
- name: { type: 'string', required: true, max: 200 },
54
- country_id: { type: 'number', belongsTo: 'countries', as: 'country' },
55
- },
56
- relationships: {
57
- books: { hasMany: 'books', foreignKey: 'publisher_id' }
58
- }
59
- });
60
- await api.resources.publishers.createKnexTable()
61
-
62
- // Authors table
63
- await api.addResource('authors', {
64
- schema: {
65
- name: { type: 'string', required: true, max: 200 },
66
- },
67
- relationships: {
68
- books: { hasMany: 'books', through: 'book_authors', foreignKey: 'author_id', otherKey: 'book_id' }
69
- }
70
- });
71
- await api.resources.authors.createKnexTable()
72
-
73
- // Books table
74
- await api.addResource('books', {
75
- schema: {
76
- title: { type: 'string', required: true, max: 300 },
77
- country_id: { type: 'number', required: true, belongsTo: 'countries', as: 'country' },
78
- publisher_id: { type: 'number', belongsTo: 'publishers', as: 'publisher' },
79
- },
80
- relationships: {
81
- authors: { hasMany: 'authors', through: 'book_authors', foreignKey: 'book_id', otherKey: 'author_id' },
82
- }
83
- });
84
- await api.resources.books.createKnexTable()
85
-
86
- // Book-Authors pivot table (many-to-many relationship)
87
- await api.addResource('book_authors', {
88
- schema: {
89
- book_id: { type: 'number', required: true, belongsTo: 'books', as: 'book' },
90
- author_id: { type: 'number', required: true, belongsTo: 'authors', as: 'author' },
91
- }
92
- });
93
- await api.resources.book_authors.createKnexTable()
94
-
95
-
96
-
97
- /////////////////////////////////////////////////////////////////////////////////////////////
98
-
99
-
100
- // Now you can use the API directly in your code
101
- try {
102
-
103
- // Create a country
104
- const countryResult1 = await api.resources.countries.post({
105
- name: 'United States',
106
- code: 'US'
107
- });
108
- console.log('Created country:', countryResult1);
109
-
110
- // Create another country
111
- const countryResult2 = await api.resources.countries.post({
112
- name: 'United Kingdom',
113
- });
114
- console.log('Created country:', countryResult2);
115
-
116
- // Create publisher using the OLD way (foreign key field name)
117
- const publisher1Result = await api.resources.publishers.post({
118
- name: 'Penguin Random House',
119
- country_id: countryResult1.id
120
- });
121
- console.log('Created publisher1 (using country_id):', publisher1Result);
122
-
123
- // Create publisher using the NEW way (relationship alias)
124
- const publisher1bResult = await api.resources.publishers.post({
125
- name: 'HarperCollins',
126
- country: countryResult2.id // Using 'country' instead of 'country_id'
127
- });
128
- console.log('Created publisher1b (using country alias):', publisher1bResult);
129
-
130
- debugger
131
- // Test error case: providing both country_id and country
132
- try {
133
- await api.resources.publishers.post({
134
- name: 'Invalid Publisher',
135
- country_id: countryResult1.id,
136
- country: countryResult2.id // ERROR: both specified!
137
- });
138
- } catch (error) {
139
- console.log('Expected error when both country_id and country are provided:', error.message);
140
- }
141
-
142
- // Create another publisher. NOT the simplified version
143
- const publisher2Result = await api.resources.publishers.post({
144
- inputRecord: {
145
- data: {
146
- type: 'publishers',
147
- attributes: {
148
- name: 'Apress'
149
- },
150
- relationships: {
151
- country: {
152
- data: { type: 'countries', id: countryResult1.id }
153
- }
154
- }
155
- }
156
- },
157
- simplified: false
158
- });
159
- console.log('Created publisher:', publisher2Result);
160
-
161
-
162
- const fetchedPublisher = await api.resources.publishers.get({
163
- id: publisher2Result.data.id,
164
- queryParams: { include: ['country']},
165
- simplified: false
166
- })
167
- console.log('Refetched publisher:', util.inspect(fetchedPublisher, { depth: 5 }));
168
-
169
- // Create a publisher
170
- const publisherResult = await api.resources.publishers.post({
171
- inputRecord: {
172
- data: {
173
- type: 'publishers',
174
- attributes: {
175
- name: 'Penguin Random House'
176
- },
177
- relationships: {
178
- country: {
179
- data: { type: 'countries', id: countryResult1.id }
180
- }
181
- }
182
- }
183
- }
184
- });
185
-
186
-
187
- // Create authors
188
- const author1Result = await api.resources.authors.post({
189
- inputRecord: {
190
- data: {
191
- type: 'authors',
192
- attributes: {
193
- name: 'George Orwell'
194
- }
195
- }
196
- }
197
- });
198
- console.log('Created author:');
199
-
200
- const author2Result = await api.resources.authors.post({
201
- inputRecord: {
202
- data: {
203
- type: 'authors',
204
- attributes: {
205
- name: 'Aldous Huxley'
206
- }
207
- }
208
- }
209
- });
210
- console.log('Created another author:');
211
-
212
- // Create a book with relationships
213
- const bookResult = await api.resources.books.post({
214
- simplified: false,
215
- queryParams: { include: ['authors'] },
216
- inputRecord: {
217
- data: {
218
- type: 'books',
219
- attributes: {
220
- title: '1984'
221
- },
222
- relationships: {
223
- country: {
224
- data: { type: 'countries', id: countryResult1.id }
225
- },
226
- publisher: {
227
- data: { type: 'publishers', id: publisherResult.id }
228
- },
229
- authors: {
230
- data: [
231
- { type: 'authors', id: author1Result.id },
232
- { type: 'authors', id: author2Result.id }
233
-
234
- ]
235
- }
236
- }
237
- }
238
- }
239
- });
240
- console.log('Created book:', util.inspect(bookResult, { depth: 5 }))
241
-
242
- const pivotRecords = await knex('book_authors').where('book_id', bookResult.data.id);
243
- console.log('Pivot records:', pivotRecords);
244
-
245
-
246
-
247
- // Clean up database connection
248
- await knex.destroy();
249
- console.log('\nExample completed successfully!');
250
-
251
-
252
-
253
- /*
254
-
255
- // Create a book with relationships
256
- const bookResult = await api.resources.books.post({
257
- inputRecord: {
258
- data: {
259
- type: 'books',
260
- attributes: {
261
- title: '1984'
262
- },
263
- relationships: {
264
- country: {
265
- data: { type: 'countries', id: ukResult.data.id }
266
- },
267
- publisher: {
268
- data: { type: 'publishers', id: publisherResult.data.id }
269
- },
270
- authors: {
271
- data: [
272
- { type: 'authors', id: author1Result.data.id }
273
- ]
274
- }
275
- }
276
- }
277
- }
278
- });
279
- console.log('Created book:', bookResult.data);
280
-
281
- // Query books with included relationships
282
- const booksWithRelationships = await api.resources.books.query({
283
- include: 'authors,publisher,country'
284
- });
285
- console.log('\nBooks with relationships:');
286
- console.log(JSON.stringify(booksWithRelationships, null, 2));
287
-
288
- // Clean up
289
- await knex.destroy();
290
- console.log('\nExample completed successfully!');
291
-
292
- */
293
-
294
- } catch (error) {
295
- console.error('Error:', error.message, error.details);
296
- console.error('Stack trace:', error.stack);
297
- await knex.destroy();
298
- process.exit(1);
299
- }
300
-
301
-
@@ -1,285 +0,0 @@
1
-
2
- # Diagram of helpers and their dependencies
3
-
4
- ## Refactoring Pattern
5
-
6
- All helper functions have been refactored to use a consistent `(scope, deps)` signature:
7
-
8
- **Before:**
9
- ```javascript
10
- function helperFunction(param1, param2, param3, param4, param5) {
11
- // Many individual parameters
12
- }
13
- ```
14
-
15
- **After:**
16
- ```javascript
17
- function helperFunction(scope, deps) {
18
- // Extract from scope (resource-specific data)
19
- const { vars: { schemaInfo } } = scope;
20
-
21
- // Extract from deps (dependencies and context)
22
- const { api, context, transaction } = deps;
23
- const { scopeName, queryParams } = context;
24
- }
25
- ```
26
-
27
- This pattern reduces parameters by 50-70% and provides clear separation between:
28
- - **scope**: Resource configuration and schema information
29
- - **deps**: Dependencies (api, knex) and request context
30
-
31
- ## Pure Utility Functions (utils/ directory)
32
-
33
- ### utils/field-utils.js
34
- ```
35
- ┌─────────────────────────────┐
36
- │ getForeignKeyFields() │ - Extracts foreign key fields from schema
37
- │ (schema) │ - Identifies belongsTo relationships
38
- │ │ - Returns: Set(['author_id', ...])
39
- └─────────────────────────────┘
40
-
41
- ┌─────────────────────────────┐
42
- │ filterAttributeFields() │ - Filters out foreign keys & internals
43
- │ (attributes, │ - Removes polymorphic fields
44
- │ foreignKeyFields, │ - Returns: cleaned attributes object
45
- │ polymorphicFields) │
46
- └─────────────────────────────┘
47
- ```
48
-
49
- ### utils/knex-query-helpers-base.js
50
- ```
51
- ┌─────────────────────────────┐
52
- │ parseIncludeTree() │ - Parses include parameter string
53
- │ (includeParam) │ - Converts "author,comments.user"
54
- │ │ - Returns: nested object tree
55
- └─────────────────────────────┘
56
- ```
57
-
58
- ## Functions with (scope, deps) Pattern (lib/ directory)
59
-
60
- ### lib/knex-json-api-helpers.js
61
- ```
62
- ┌─────────────────────────────┐
63
- │ toJsonApi() │ - Converts DB record to JSON:API format
64
- │ (scope, record, deps) │ - Filters out foreign keys
65
- │ │ - Returns: {type, id, attributes}
66
- │ deps.context: { │
67
- │ scopeName, │
68
- │ schemaInfo, │
69
- │ polymorphicFields │
70
- │ } │
71
- └─────────────────────────────┘
72
- ```
73
-
74
- ### lib/knex-relationship-includes.js
75
- ```
76
- ┌─────────────────────────────┐
77
- │ groupByPolymorphicType() │ - Groups records by polymorphic type
78
- │ (records, typeField, │ - Used for batch loading
79
- │ idField) │ - Returns: {type: [ids...]}
80
- └─────────────────────────────┘
81
- ```
82
-
83
- ```
84
- ┌─────────────────────────────────────────────────────────────────┐
85
- │ buildIncludedResources() │
86
- │ (scope, records, deps) │
87
- │ │
88
- │ Main entry point - orchestrates the include loading process │
89
- │ │
90
- │ deps: { │
91
- │ includeParam, fields, included, processedPaths │
92
- │ } │
93
- │ │
94
- │ 1. Parses includes ──────────────┐ │
95
- │ ▼ │
96
- │ 2. Calls processIncludes ─────► processIncludes() │
97
- │ │
98
- │ 3. Returns: {included: [...], recordsWithRelationships: [...]} │
99
- └─────────────────────────────────────────────────────────────────┘
100
- │
101
- ▼
102
- ┌────────────────────────────────────────────────────────────────┐
103
- │ processIncludes() │
104
- │ (scope, records, deps) │
105
- │ │
106
- │ Recursive function that examines schema and calls loaders │
107
- │ │
108
- │ deps: { │
109
- │ includeTree, included, processedPaths, currentPath, fields │
110
- │ } │
111
- │ │
112
- │ Dispatches to: ─────────────────┬───────────────┬──────────────┤
113
- └─────────────────────────────────┴───────────────┴──────────────┘
114
- │ │ │
115
- ▼ ▼ ▼
116
- ┌───────────────────────────┐ ┌─────────────────────┐ ┌──────────────────────────┐
117
- │ loadBelongsTo() │ │ loadHasMany() │ │ loadPolymorphicBelongsTo │
118
- │ (scope, records, deps) │ │ (scope, records, │ │ (scope, records, deps) │
119
- │ │ │ deps) │ │ │
120
- │ deps: { │ │ │ │ deps: { │
121
- │ fieldName, fieldDef, │ │ deps: { │ │ relName, relDef, │
122
- │ includeName, │ │ includeName, │ │ subIncludes, included, │
123
- │ subIncludes, included, │ │ relDef, │ │ processedPaths, │
124
- │ processedPaths, │ │ subIncludes, │ │ currentPath, fields │
125
- │ currentPath, fields │ │ included, │ │ } │
126
- │ } │ │ processedPaths, │ │ │
127
- │ │ │ currentPath, │ │ Loads polymorphic │
128
- │ Loads many-to-one │ │ fields │ │ e.g. comment→(article │
129
- │ e.g. comment→article │ │ } │ │ or video) │
130
- │ │ │ │ │ │
131
- │ ┌─────────────────┐ │ │ Loads one-to-many │ │ ┌──────────────────┐ │
132
- │ │ Recursively │ │ │ or many-to-many │ │ │ Recursively │ │
133
- │ │ calls │◄──────┼─┤ e.g. article→ │ │ │ calls │◄────┤
134
- │ │ processIncludes │ │ │ comments │ │ │ processIncludes │ │
135
- │ └─────────────────┘ │ │ │ │ └──────────────────┘ │
136
- └───────────────────────────┘ │ ┌─────────────────┐ │ └──────────────────────────┘
137
- │ │ Recursively │ │
138
- │ │ calls │◄┤
139
- │ │ processIncludes │ │ Also dispatches to:
140
- │ └─────────────────┘ │ │
141
- └─────────────────────┘ ▼
142
- ┌──────────────────────────┐
143
- │ loadReversePolymorphic() │
144
- │ (scope, records, deps) │
145
- │ │
146
- │ deps: { │
147
- │ includeName, relDef, │
148
- │ subIncludes, included, │
149
- │ processedPaths, │
150
- │ currentPath, fields │
151
- │ } │
152
- │ │
153
- │ Loads via relationships │
154
- │ e.g. article→comments │
155
- │ (via commentable) │
156
- │ │
157
- │ ┌──────────────────┐ │
158
- │ │ Recursively │ │
159
- │ │ calls │◄────┤
160
- │ │ processIncludes │ │
161
- │ └──────────────────┘ │
162
- └──────────────────────────┘
163
- ```
164
-
165
- ### lib/simplified-helpers.js
166
- ```
167
- ┌─────────────────────────────────────────────────────────────────┐
168
- │ transformSimplifiedToJsonApi() │
169
- │ (scope, deps) │
170
- │ │
171
- │ Converts plain objects to JSON:API format │
172
- │ - Handles both foreign key names and 'as' aliases │
173
- │ - Converts relationships to proper JSON:API structure │
174
- │ │
175
- │ scope: { inputRecord } │
176
- │ deps.context: { scopeName, schemaStructure, schemaRelationships }│
177
- └─────────────────────────────────────────────────────────────────┘
178
-
179
- ┌─────────────────────────────────────────────────────────────────┐
180
- │ transformJsonApiToSimplified() │
181
- │ (scope, deps) │
182
- │ │
183
- │ Converts JSON:API responses to plain objects │
184
- │ - Restores foreign keys from relationships │
185
- │ - Handles both single resources and collections │
186
- │ │
187
- │ scope: { record } │
188
- │ deps.context: { schemaStructure, schemaRelationships } │
189
- └─────────────────────────────────────────────────────────────────┘
190
- ```
191
-
192
- ### lib/many-to-many-manipulations.js
193
- ```
194
- ┌─────────────────────────────────────────────────────────────────┐
195
- │ updateManyToManyRelationship() │
196
- │ (scope, deps) │
197
- │ │
198
- │ Intelligently syncs many-to-many relationships │
199
- │ - Preserves existing pivot data │
200
- │ - Only creates/deletes changed relationships │
201
- │ │
202
- │ deps.context: { │
203
- │ resourceId, relDef, relData, transaction │
204
- │ } │
205
- └─────────────────────────────────────────────────────────────────┘
206
- ```
207
-
208
- ### lib/relationship-processor.js
209
- ```
210
- ┌─────────────────────────────────────────────────────────────────┐
211
- │ processRelationships() │
212
- │ (scope, deps) │
213
- │ │
214
- │ Extracts relationship data from JSON:API payloads │
215
- │ - Converts belongsTo to foreign keys │
216
- │ - Handles polymorphic relationships │
217
- │ - Prepares many-to-many operations │
218
- │ │
219
- │ deps.context: { inputRecord } │
220
- │ Returns: { belongsToUpdates, manyToManyRelationships } │
221
- └─────────────────────────────────────────────────────────────────┘
222
- ```
223
-
224
- ## Module: lib/knex-process-includes.js
225
-
226
- ```
227
- ┌─────────────────────────────────────────────────────────────────┐
228
- │ processIncludes() │
229
- │ (records, scopeName, queryParams, transaction, dependencies) │
230
- │ │
231
- │ High-level orchestrator that: │
232
- │ 1. Extracts scopes, log, knex from dependencies │
233
- │ 2. Uses transaction if provided, otherwise uses knex │
234
- │ 3. Calls buildIncludedResources with all parameters │
235
- │ 4. Returns just the included array │
236
- │ │
237
- │ Called by: REST API plugin dataGet/dataQuery methods │
238
- └─────────────────────────────────────────────────────────────────┘
239
- ```
240
-
241
- ## Data Flow:
242
- ```
243
- REST API Plugin
244
- │
245
- ├─► processIncludes (lib/knex-process-includes.js)
246
- │ │
247
- │ └─► buildIncludedResources (lib/knex-relationship-includes.js)
248
- │ │
249
- │ ├─► Uses pure utilities:
250
- │ │ - parseIncludeTree (utils/knex-query-helpers-base.js)
251
- │ │ - getForeignKeyFields (utils/field-utils.js)
252
- │ │
253
- │ └─► processIncludes (recursive orchestrator)
254
- │ │
255
- │ ├─► loadBelongsTo
256
- │ ├─► loadHasMany
257
- │ ├─► loadPolymorphicBelongsTo
258
- │ └─► loadReversePolymorphic
259
- │ │
260
- │ └─► All loaders use:
261
- │ - toJsonApi (lib/knex-json-api-helpers.js)
262
- │ - groupByPolymorphicType (lib/knex-relationship-includes.js)
263
- │
264
- └─► Returns: Array of included resources in JSON:API format
265
- ```
266
-
267
- ## File Organization:
268
-
269
- ### Pure Utilities (utils/ directory)
270
- - `field-utils.js` - Schema field inspection utilities
271
- - `knex-query-helpers-base.js` - Query parsing utilities
272
- - `knex-constants.js` - Shared constants
273
- - `connectors-query-parser.js` - Connector query parsing
274
-
275
- ### Scoped Functions (lib/ directory)
276
- - `knex-json-api-helpers.js` - JSON:API transformation with (scope, deps)
277
- - `knex-relationship-includes.js` - Relationship loading with (scope, deps)
278
- - `knex-process-includes.js` - Top-level orchestrator
279
- - `knex-field-helpers.js` - Field selection and filtering with (scope, deps)
280
- - `knex-window-queries.js` - Window query builders
281
- - `simplified-helpers.js` - Simplified/JSON:API transformations with (scope, deps)
282
- - `many-to-many-manipulations.js` - Many-to-many relationship updates with (scope, deps)
283
- - `relationship-processor.js` - Relationship processing with (scope, deps)
284
- - `compile-schemas.js` - Schema compilation with (scope, deps)
285
- - `default-data-helpers.js` - Default data helper creation