json-rest-api 1.0.13 → 1.0.15

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 (118) hide show
  1. package/connectors_ideas/fastify.js +66 -68
  2. package/connectors_ideas/koa-plugin.js +50 -51
  3. package/eslint.config.js +13 -0
  4. package/examples/url-override-example.js +63 -63
  5. package/index.js +13 -13
  6. package/lib/enhanced-logger.js +51 -51
  7. package/lib/error-formatter.js +61 -61
  8. package/lib/rest-api-errors.js +28 -28
  9. package/package.json +9 -5
  10. package/plugins/core/bulk-operations-plugin.js +142 -145
  11. package/plugins/core/connectors/express-plugin.js +210 -217
  12. package/plugins/core/connectors/lib/busboy-detector.js +84 -84
  13. package/plugins/core/connectors/lib/formidable-detector.js +50 -50
  14. package/plugins/core/connectors/lib/request-helpers.js +74 -74
  15. package/plugins/core/file-handling-plugin.js +125 -125
  16. package/plugins/core/lib/anyapi/anyapi-registry.js +254 -254
  17. package/plugins/core/lib/anyapi/query/anyapi-query-adapter.js +287 -287
  18. package/plugins/core/lib/anyapi/schema-utils.js +101 -101
  19. package/plugins/core/lib/anyapi/utils/descriptor-helpers.js +41 -41
  20. package/plugins/core/lib/dbTablesOperations.js +134 -135
  21. package/plugins/core/lib/fractional-positioning.js +74 -76
  22. package/plugins/core/lib/querying/knex-cross-table-search.js +320 -320
  23. package/plugins/core/lib/querying/knex-json-api-transformers-querying.js +144 -144
  24. package/plugins/core/lib/querying/knex-pagination-helpers.js +164 -164
  25. package/plugins/core/lib/querying/knex-process-includes.js +50 -50
  26. package/plugins/core/lib/querying/knex-query-helpers-base.js +31 -31
  27. package/plugins/core/lib/querying/knex-query-helpers.js +451 -452
  28. package/plugins/core/lib/querying/knex-relationship-includes.js +1160 -1170
  29. package/plugins/core/lib/querying/knex-window-queries.js +89 -89
  30. package/plugins/core/lib/querying/url-helpers.js +35 -35
  31. package/plugins/core/lib/querying-writing/compile-schemas.js +97 -98
  32. package/plugins/core/lib/querying-writing/connectors-query-parser.js +31 -31
  33. package/plugins/core/lib/querying-writing/database-capabilities.js +94 -94
  34. package/plugins/core/lib/querying-writing/database-value-normalizers.js +76 -76
  35. package/plugins/core/lib/querying-writing/default-data-helpers.js +27 -27
  36. package/plugins/core/lib/querying-writing/field-utils.js +44 -42
  37. package/plugins/core/lib/querying-writing/knex-constants.js +15 -16
  38. package/plugins/core/lib/querying-writing/knex-field-helpers.js +117 -115
  39. package/plugins/core/lib/querying-writing/knex-json-api-transformers.js +41 -41
  40. package/plugins/core/lib/querying-writing/payload-validators.js +275 -275
  41. package/plugins/core/lib/querying-writing/schema-helpers.js +129 -129
  42. package/plugins/core/lib/querying-writing/scope-validations.js +143 -146
  43. package/plugins/core/lib/querying-writing/simplified-helpers.js +133 -129
  44. package/plugins/core/lib/storage/storage-adapter.js +68 -68
  45. package/plugins/core/lib/writing/knex-json-api-transformers-writing.js +31 -31
  46. package/plugins/core/lib/writing/many-to-many-manipulations.js +81 -81
  47. package/plugins/core/lib/writing/relationship-processor.js +58 -58
  48. package/plugins/core/rest-api-access.js +8 -8
  49. package/plugins/core/rest-api-anyapi-knex-plugin.js +920 -920
  50. package/plugins/core/rest-api-cors-plugin.js +170 -171
  51. package/plugins/core/rest-api-knex-plugin.js +482 -493
  52. package/plugins/core/rest-api-multihome-plugin.js +247 -251
  53. package/plugins/core/rest-api-plugin-hooks/compile-resource-schemas.js +5 -5
  54. package/plugins/core/rest-api-plugin-hooks/register-relationship-routes.js +20 -21
  55. package/plugins/core/rest-api-plugin-hooks/register-scope-routes.js +44 -44
  56. package/plugins/core/rest-api-plugin-hooks/turn-scope-init-into-vars.js +23 -23
  57. package/plugins/core/rest-api-plugin-hooks/validate-include-configurations.js +15 -15
  58. package/plugins/core/rest-api-plugin-methods/add-route.js +9 -9
  59. package/plugins/core/rest-api-plugin-methods/check-permissions.js +5 -6
  60. package/plugins/core/rest-api-plugin-methods/common.js +295 -297
  61. package/plugins/core/rest-api-plugin-methods/delete-relationship.js +35 -37
  62. package/plugins/core/rest-api-plugin-methods/delete.js +57 -59
  63. package/plugins/core/rest-api-plugin-methods/enrich-attributes.js +172 -171
  64. package/plugins/core/rest-api-plugin-methods/get-related.js +96 -96
  65. package/plugins/core/rest-api-plugin-methods/get-relationship.js +16 -18
  66. package/plugins/core/rest-api-plugin-methods/get.js +86 -86
  67. package/plugins/core/rest-api-plugin-methods/patch-relationship.js +16 -17
  68. package/plugins/core/rest-api-plugin-methods/patch.js +63 -64
  69. package/plugins/core/rest-api-plugin-methods/post-relationship.js +54 -55
  70. package/plugins/core/rest-api-plugin-methods/post.js +61 -62
  71. package/plugins/core/rest-api-plugin-methods/put.js +86 -87
  72. package/plugins/core/rest-api-plugin-methods/query.js +88 -88
  73. package/plugins/core/rest-api-plugin-methods/release.js +5 -6
  74. package/plugins/core/rest-api-plugin.js +76 -78
  75. package/plugins/core/rest-api-positioning-plugin.js +251 -252
  76. package/plugins/core/socketio-plugin.js +240 -236
  77. package/plugins/storage/local-storage.js +103 -103
  78. package/plugins/storage/s3-storage.js +41 -42
  79. package/quickTest.js +37 -37
  80. package/scripts/debug-anyapi-review-filter.mjs +18 -18
  81. package/scripts/inspect-anyapi-descriptor-country.mjs +10 -10
  82. package/scripts/inspect-anyapi-descriptor.mjs +10 -10
  83. package/scripts/run-docs.js +112 -113
  84. package/test-debug.js +28 -28
  85. package/tests/TEST_TEMPLATE.test.js +51 -51
  86. package/tests/anyapi-basic.test.js +42 -42
  87. package/tests/anyapi-cursor-pagination.test.js +48 -48
  88. package/tests/bulk-operations.test.js +208 -208
  89. package/tests/computed-fields.test.js +204 -204
  90. package/tests/cors.test.js +412 -413
  91. package/tests/curl.test.js +275 -276
  92. package/tests/custom-idproperty-relationships.test.js +266 -266
  93. package/tests/field-getters.test.js +132 -132
  94. package/tests/field-setters.test.js +110 -110
  95. package/tests/fixtures/api-configs.js +699 -699
  96. package/tests/helpers/storage-mode.js +32 -32
  97. package/tests/helpers/test-utils.js +76 -76
  98. package/tests/include-depth-validation.test.js +76 -76
  99. package/tests/includes.test.js +145 -145
  100. package/tests/multihome.test.js +175 -175
  101. package/tests/nested-includes.test.js +255 -255
  102. package/tests/pagination-cursor-multifield.test.js +164 -164
  103. package/tests/pagination-enhanced.test.js +225 -225
  104. package/tests/polymorphic-relationships.test.js +96 -96
  105. package/tests/polymorphic-simple.test.js +44 -44
  106. package/tests/positioning-advanced.test.js +226 -226
  107. package/tests/positioning-extended.test.js +399 -399
  108. package/tests/positioning-multifilter.test.js +204 -204
  109. package/tests/positioning.test.js +266 -266
  110. package/tests/queries.test.js +184 -184
  111. package/tests/query-limits.test.js +154 -155
  112. package/tests/relationship-endpoints.test.js +127 -127
  113. package/tests/relationships.test.js +178 -179
  114. package/tests/rest-api-access.test.js +86 -86
  115. package/tests/rest-api.test.js +103 -103
  116. package/tests/searchschema-merge.test.js +93 -93
  117. package/tests/socketio.test.js +326 -326
  118. package/tests/virtual-fields.test.js +164 -164
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Transformation functions for simplified API mode
3
- *
3
+ *
4
4
  * @description
5
5
  * This module enables "simplified mode" - a developer-friendly alternative
6
6
  * to verbose JSON:API format. It allows APIs to:
@@ -10,11 +10,11 @@
10
10
  * - Maintain JSON:API compliance internally
11
11
  */
12
12
 
13
- import { RestApiValidationError } from '../../../../lib/rest-api-errors.js';
13
+ import { RestApiValidationError } from '../../../../lib/rest-api-errors.js'
14
14
 
15
15
  /**
16
16
  * Transforms simplified plain object into JSON:API format
17
- *
17
+ *
18
18
  * @param {Object} scope - Scope containing input data
19
19
  * @param {Object} scope.inputRecord - Plain object or JSON:API document
20
20
  * @param {Object} deps - Dependencies with context
@@ -22,7 +22,7 @@ import { RestApiValidationError } from '../../../../lib/rest-api-errors.js';
22
22
  * @param {Object} deps.context.schemaStructure - Schema with field definitions
23
23
  * @param {Object} deps.context.schemaRelationships - Relationship configurations
24
24
  * @returns {Object} JSON:API formatted document
25
- *
25
+ *
26
26
  * @example
27
27
  * // Input: Simple object with only attributes
28
28
  * const input = {
@@ -34,7 +34,7 @@ import { RestApiValidationError } from '../../../../lib/rest-api-errors.js';
34
34
  * { inputRecord: input },
35
35
  * { context: { scopeName: 'articles', schemaStructure: schema } }
36
36
  * );
37
- *
37
+ *
38
38
  * // Output: Proper JSON:API structure
39
39
  * // {
40
40
  * // data: {
@@ -46,15 +46,15 @@ import { RestApiValidationError } from '../../../../lib/rest-api-errors.js';
46
46
  * // }
47
47
  * // }
48
48
  * // }
49
- *
49
+ *
50
50
  * @example
51
51
  * // Input: Object with relationship name (preferred way)
52
52
  * const schema = {
53
53
  * title: { type: 'string' },
54
- * author_id: {
55
- * type: 'number',
56
- * belongsTo: 'users',
57
- * as: 'author'
54
+ * author_id: {
55
+ * type: 'number',
56
+ * belongsTo: 'users',
57
+ * as: 'author'
58
58
  * }
59
59
  * };
60
60
  * const input = {
@@ -62,7 +62,7 @@ import { RestApiValidationError } from '../../../../lib/rest-api-errors.js';
62
62
  * title: 'Another Article',
63
63
  * author: 789 // Using relationship name
64
64
  * };
65
- *
65
+ *
66
66
  * // Output: Relationship becomes JSON:API relationship
67
67
  * // {
68
68
  * // data: {
@@ -72,7 +72,7 @@ import { RestApiValidationError } from '../../../../lib/rest-api-errors.js';
72
72
  * // title: 'Another Article' // author removed from attributes
73
73
  * // },
74
74
  * // relationships: {
75
- * // author: {
75
+ * // author: {
76
76
  * // data: { type: 'users', id: '789' }
77
77
  * // }
78
78
  * // }
@@ -103,7 +103,7 @@ import { RestApiValidationError } from '../../../../lib/rest-api-errors.js';
103
103
  * title: 'Tagged Article',
104
104
  * tags: ['10', '20', '30'] // Array of IDs
105
105
  * };
106
- *
106
+ *
107
107
  * // Output: Array becomes relationship data array
108
108
  * // {
109
109
  * // data: {
@@ -122,7 +122,7 @@ import { RestApiValidationError } from '../../../../lib/rest-api-errors.js';
122
122
  * // }
123
123
  * // }
124
124
  * // }
125
- *
125
+ *
126
126
  * @example
127
127
  * // Input: Already JSON:API formatted
128
128
  * const input = {
@@ -149,7 +149,7 @@ import { RestApiValidationError } from '../../../../lib/rest-api-errors.js';
149
149
  * text: 'Great content!',
150
150
  * commentable: { id: '456', _type: 'posts' } // Minimal object format
151
151
  * };
152
- *
152
+ *
153
153
  * // Output: Proper JSON:API structure for polymorphic
154
154
  * // {
155
155
  * // data: {
@@ -171,7 +171,7 @@ import { RestApiValidationError } from '../../../../lib/rest-api-errors.js';
171
171
  * text: 'Updated comment',
172
172
  * commentable: null // Clear the relationship
173
173
  * };
174
- *
174
+ *
175
175
  * // Output: Relationship set to null
176
176
  * // {
177
177
  * // data: {
@@ -184,12 +184,12 @@ import { RestApiValidationError } from '../../../../lib/rest-api-errors.js';
184
184
  * // }
185
185
  * // }
186
186
  * // }
187
- *
187
+ *
188
188
  * @description
189
189
  * Used by:
190
190
  * - rest-api-plugin for POST/PUT/PATCH when simplified: true
191
191
  * - Processes input before validation and storage
192
- *
192
+ *
193
193
  * Purpose:
194
194
  * - Provides developer-friendly plain object API
195
195
  * - Automatically detects and converts relationships
@@ -197,7 +197,7 @@ import { RestApiValidationError } from '../../../../lib/rest-api-errors.js';
197
197
  * - Supports polymorphic relationships with minimal object format
198
198
  * - Maintains JSON:API compliance internally
199
199
  * - Allows gradual migration from simple to JSON:API
200
- *
200
+ *
201
201
  * Data flow:
202
202
  * 1. Checks if already JSON:API (has data.type)
203
203
  * 2. Extracts ID from input object
@@ -209,29 +209,29 @@ import { RestApiValidationError } from '../../../../lib/rest-api-errors.js';
209
209
  */
210
210
  export const transformSimplifiedToJsonApi = (scope, deps) => {
211
211
  // Extract needed values from scope and deps
212
- const input = scope.inputRecord;
213
- const scopeName = deps.context.scopeName;
214
- const schema = deps.context.schemaStructure;
215
- const relationships = deps.context.schemaRelationships;
212
+ const input = scope.inputRecord
213
+ const scopeName = deps.context.scopeName
214
+ const schema = deps.context.schemaStructure
215
+ const relationships = deps.context.schemaRelationships
216
216
 
217
217
  // If already JSON:API format, return as-is
218
218
  if (input?.data?.type) {
219
- return input;
219
+ return input
220
220
  }
221
221
 
222
- const attributes = {};
223
- const relationshipsData = {};
224
- const tempInput = { ...input }; // Create a mutable copy of the input
222
+ const attributes = {}
223
+ const relationshipsData = {}
224
+ const tempInput = { ...input } // Create a mutable copy of the input
225
225
 
226
- const id = tempInput.id;
227
- delete tempInput.id; // Extract ID from tempInput
226
+ const id = tempInput.id
227
+ delete tempInput.id // Extract ID from tempInput
228
228
 
229
229
  // 1. Process belongsTo relationships (from schema fields)
230
230
  // Iterate through the schema structure to find belongsTo fields
231
231
  for (const [fieldName, fieldDef] of Object.entries(schema)) {
232
232
  if (fieldDef.belongsTo) {
233
- const relAlias = fieldDef.as || fieldName; // Use 'as' alias or foreign key name as default alias
234
- let valueToProcess = undefined;
233
+ const relAlias = fieldDef.as || fieldName // Use 'as' alias or foreign key name as default alias
234
+ let valueToProcess
235
235
 
236
236
  // Check if the user provided the foreign key name (e.g., country_id) - NO LONGER ALLOWED
237
237
  if (tempInput[fieldName] !== undefined) {
@@ -243,23 +243,23 @@ export const transformSimplifiedToJsonApi = (scope, deps) => {
243
243
  violations: [{
244
244
  field: fieldName,
245
245
  rule: 'deprecated_foreign_key',
246
- message: `Use relationship name instead of foreign key field`
246
+ message: 'Use relationship name instead of foreign key field'
247
247
  }]
248
248
  }
249
- );
249
+ )
250
250
  }
251
-
251
+
252
252
  // Check if the user provided the relationship name (e.g., country)
253
- const relName = fieldDef.as || fieldName;
253
+ const relName = fieldDef.as || fieldName
254
254
  if (tempInput[relName] !== undefined) {
255
- valueToProcess = tempInput[relName];
256
- delete tempInput[relName]; // Remove from tempInput once processed
255
+ valueToProcess = tempInput[relName]
256
+ delete tempInput[relName] // Remove from tempInput once processed
257
257
  }
258
258
 
259
259
  if (valueToProcess !== undefined) {
260
260
  relationshipsData[relAlias] = {
261
261
  data: valueToProcess ? { type: fieldDef.belongsTo, id: String(valueToProcess) } : null
262
- };
262
+ }
263
263
  }
264
264
  }
265
265
  }
@@ -269,39 +269,39 @@ export const transformSimplifiedToJsonApi = (scope, deps) => {
269
269
  for (const [relName, relConfig] of Object.entries(relationships || {})) {
270
270
  // Check if the user provided data for this relationship name
271
271
  if (tempInput[relName] !== undefined) {
272
- const value = tempInput[relName];
273
- delete tempInput[relName]; // Remove from tempInput once processed
272
+ const value = tempInput[relName]
273
+ delete tempInput[relName] // Remove from tempInput once processed
274
274
 
275
275
  if ((relConfig.type === 'hasMany' || relConfig.type === 'manyToMany') && Array.isArray(value)) {
276
276
  // Determine the target type based on relationship type:
277
277
  // - For manyToMany: the targetType is the relationship name itself
278
278
  // - For hasMany/hasOne: the targetType is specified in the target property
279
- const targetType = relConfig.type === 'manyToMany' ? relName : relConfig.target;
279
+ const targetType = relConfig.type === 'manyToMany' ? relName : relConfig.target
280
280
  relationshipsData[relName] = {
281
281
  data: value.map(relId => ({ type: targetType, id: String(relId) }))
282
- };
282
+ }
283
283
  }
284
284
  // Handle hasOne relationships
285
285
  else if (relConfig.type === 'hasOne' && value !== undefined) {
286
286
  relationshipsData[relName] = {
287
287
  data: value ? { type: relConfig.target, id: String(value) } : null
288
- };
288
+ }
289
289
  }
290
290
  // Handle single belongsTo relationships if they were also defined in 'relationships'
291
291
  // (though typically they are defined in schema.belongsTo)
292
292
  else if (relConfig.belongsTo && value !== undefined) {
293
- relationshipsData[relName] = {
294
- data: value ? { type: relConfig.belongsTo, id: String(value) } : null
295
- };
293
+ relationshipsData[relName] = {
294
+ data: value ? { type: relConfig.belongsTo, id: String(value) } : null
295
+ }
296
296
  }
297
297
  // Handle polymorphic belongsTo relationships with minimal object format
298
298
  else if (relConfig.belongsToPolymorphic && value !== undefined) {
299
299
  if (value === null) {
300
- relationshipsData[relName] = { data: null };
300
+ relationshipsData[relName] = { data: null }
301
301
  } else if (typeof value === 'object' && value !== null && value.id && value._type) {
302
302
  relationshipsData[relName] = {
303
303
  data: { type: value._type, id: String(value.id) }
304
- };
304
+ }
305
305
  } else {
306
306
  throw new RestApiValidationError(
307
307
  `Invalid format for polymorphic relationship '${relName}'. Expected { id: 'value', _type: 'type' } or null`,
@@ -313,14 +313,14 @@ export const transformSimplifiedToJsonApi = (scope, deps) => {
313
313
  message: 'Must be an object with id and _type properties, or null'
314
314
  }]
315
315
  }
316
- );
316
+ )
317
317
  }
318
318
  }
319
319
  }
320
320
  }
321
321
 
322
322
  // 3. Any remaining fields in tempInput are attributes
323
- Object.assign(attributes, tempInput);
323
+ Object.assign(attributes, tempInput)
324
324
 
325
325
  return {
326
326
  data: {
@@ -329,12 +329,12 @@ export const transformSimplifiedToJsonApi = (scope, deps) => {
329
329
  ...(Object.keys(attributes).length > 0 && { attributes }),
330
330
  ...(Object.keys(relationshipsData).length > 0 && { relationships: relationshipsData })
331
331
  }
332
- };
333
- };
332
+ }
333
+ }
334
334
 
335
335
  /**
336
336
  * Transforms JSON:API response into simplified plain object
337
- *
337
+ *
338
338
  * @param {Object} scope - Scope containing JSON:API response
339
339
  * @param {Object} scope.record - JSON:API document to transform
340
340
  * @param {Object} deps - Dependencies with context
@@ -342,7 +342,7 @@ export const transformSimplifiedToJsonApi = (scope, deps) => {
342
342
  * @param {Object} deps.context.schemaRelationships - Relationship configurations
343
343
  * @param {Object} deps.context.scopes - All scopes for nested transformations
344
344
  * @returns {Object|Object[]} Simplified object or array of objects
345
- *
345
+ *
346
346
  * @example
347
347
  * // Input: Single resource JSON:API
348
348
  * const jsonApi = {
@@ -359,14 +359,14 @@ export const transformSimplifiedToJsonApi = (scope, deps) => {
359
359
  * { record: jsonApi },
360
360
  * { context: { schemaStructure: schema } }
361
361
  * );
362
- *
362
+ *
363
363
  * // Output: Flattened object
364
364
  * // {
365
365
  * // id: '123',
366
366
  * // title: 'My Article', // Attributes flattened
367
367
  * // content: 'Content here'
368
368
  * // }
369
- *
369
+ *
370
370
  * @example
371
371
  * // Input: Resource with belongsTo relationship
372
372
  * const schema = {
@@ -385,14 +385,14 @@ export const transformSimplifiedToJsonApi = (scope, deps) => {
385
385
  * }
386
386
  * }
387
387
  * };
388
- *
388
+ *
389
389
  * // Output: Relationship becomes foreign key
390
390
  * // {
391
391
  * // id: '456',
392
392
  * // title: 'Article with Author',
393
393
  * // author_id: '789' // Restored from relationship
394
394
  * // }
395
- *
395
+ *
396
396
  * @example
397
397
  * // Input: Collection response
398
398
  * const jsonApi = {
@@ -411,7 +411,7 @@ export const transformSimplifiedToJsonApi = (scope, deps) => {
411
411
  * meta: { total: 2 },
412
412
  * links: { next: '/articles?page=2' }
413
413
  * };
414
- *
414
+ *
415
415
  * // Output: Preserves meta and links
416
416
  * // {
417
417
  * // data: [
@@ -459,7 +459,7 @@ export const transformSimplifiedToJsonApi = (scope, deps) => {
459
459
  * }
460
460
  * ]
461
461
  * };
462
- *
462
+ *
463
463
  * // Output: Expands included resources
464
464
  * // {
465
465
  * // id: '400',
@@ -480,14 +480,14 @@ export const transformSimplifiedToJsonApi = (scope, deps) => {
480
480
  * Used by:
481
481
  * - rest-api-plugin for GET/QUERY when simplified: true
482
482
  * - Transforms responses before sending to client
483
- *
483
+ *
484
484
  * Purpose:
485
485
  * - Provides cleaner response format for clients
486
486
  * - Creates minimal relationship objects from relationships
487
487
  * - Expands included resources for convenience
488
488
  * - Maintains compatibility with collection metadata
489
489
  * - Handles both single and array responses
490
- *
490
+ *
491
491
  * Data flow:
492
492
  * 1. Detects array vs single resource
493
493
  * 2. For arrays, preserves meta/links at root
@@ -496,12 +496,12 @@ export const transformSimplifiedToJsonApi = (scope, deps) => {
496
496
  */
497
497
  export const transformJsonApiToSimplified = (scope, deps) => {
498
498
  // Extract needed values from scope and deps
499
- const jsonApi = scope.record;
500
- const schema = deps.context.schemaStructure;
501
- const relationships = deps.context.schemaRelationships;
502
- const scopes = deps.context.scopes;
499
+ const jsonApi = scope.record
500
+ const schema = deps.context.schemaStructure
501
+ const relationships = deps.context.schemaRelationships
502
+ const scopes = deps.context.scopes
503
503
 
504
- if (!jsonApi?.data) return jsonApi;
504
+ if (!jsonApi?.data) return jsonApi
505
505
 
506
506
  // Handle array response (QUERY)
507
507
  if (Array.isArray(jsonApi.data)) {
@@ -510,34 +510,34 @@ export const transformJsonApiToSimplified = (scope, deps) => {
510
510
  { data: item, included: jsonApi.included },
511
511
  { context: { schemaStructure: schema, schemaRelationships: relationships, scopes } }
512
512
  )
513
- );
514
-
513
+ )
514
+
515
515
  // For query results, return object with data, meta, and links
516
- const result = { data: simplifiedData };
517
-
516
+ const result = { data: simplifiedData }
517
+
518
518
  // Add meta if present
519
519
  if (jsonApi.meta) {
520
- result.meta = jsonApi.meta;
520
+ result.meta = jsonApi.meta
521
521
  }
522
-
522
+
523
523
  // Add links if present
524
524
  if (jsonApi.links) {
525
- result.links = jsonApi.links;
525
+ result.links = jsonApi.links
526
526
  }
527
-
528
- return result;
527
+
528
+ return result
529
529
  }
530
530
 
531
531
  // Handle single response (no change for single resources)
532
532
  return transformSingleJsonApiToSimplified(
533
533
  { data: jsonApi.data, included: jsonApi.included },
534
534
  { context: { schemaStructure: schema, schemaRelationships: relationships, scopes } }
535
- );
536
- };
535
+ )
536
+ }
537
537
 
538
538
  /**
539
539
  * Transforms single JSON:API resource into simplified format
540
- *
540
+ *
541
541
  * @param {Object} scope - Scope with data and included resources
542
542
  * @param {Object} scope.data - Single JSON:API resource object
543
543
  * @param {Array} scope.included - Array of included resources
@@ -546,7 +546,7 @@ export const transformJsonApiToSimplified = (scope, deps) => {
546
546
  * @param {Object} deps.context.schemaRelationships - Relationship configs
547
547
  * @param {Object} deps.context.scopes - All scopes for recursion
548
548
  * @returns {Object} Simplified plain object
549
- *
549
+ *
550
550
  * @example
551
551
  * // Input: Basic resource with relationships
552
552
  * const data = {
@@ -575,7 +575,7 @@ export const transformJsonApiToSimplified = (scope, deps) => {
575
575
  * { data: data, included: null },
576
576
  * { context: { schemaStructure: schema } }
577
577
  * );
578
- *
578
+ *
579
579
  * // Output: Minimal relationship objects
580
580
  * // {
581
581
  * // id: '100',
@@ -584,7 +584,7 @@ export const transformJsonApiToSimplified = (scope, deps) => {
584
584
  * // author: { id: '50' }, // Minimal object from relationships.author
585
585
  * // article: { id: '200' } // Minimal object from relationships.article
586
586
  * // }
587
- *
587
+ *
588
588
  * @example
589
589
  * // Input: Many-to-many relationships
590
590
  * const data = {
@@ -604,14 +604,14 @@ export const transformJsonApiToSimplified = (scope, deps) => {
604
604
  * const relationships = {
605
605
  * tags: { manyToMany: { via: 'article_tags' } }
606
606
  * };
607
- *
607
+ *
608
608
  * // Output: Minimal relationship objects
609
609
  * // {
610
610
  * // id: '300',
611
611
  * // title: 'My Article',
612
612
  * // tags: [{ id: '1' }, { id: '2' }, { id: '3' }] // Minimal objects
613
613
  * // }
614
- *
614
+ *
615
615
  * @example
616
616
  * // Input: Polymorphic relationship
617
617
  * const data = {
@@ -633,7 +633,7 @@ export const transformJsonApiToSimplified = (scope, deps) => {
633
633
  * }
634
634
  * }
635
635
  * };
636
- *
636
+ *
637
637
  * // Output: Minimal polymorphic object with type
638
638
  * // {
639
639
  * // id: '500',
@@ -641,7 +641,7 @@ export const transformJsonApiToSimplified = (scope, deps) => {
641
641
  * // comment: 'Great!',
642
642
  * // reviewable: { id: '75', type: 'authors' } // Minimal object with type
643
643
  * // }
644
- *
644
+ *
645
645
  * @example
646
646
  * // Input: Polymorphic relationship with included resource
647
647
  * const dataWithInclude = {
@@ -661,7 +661,7 @@ export const transformJsonApiToSimplified = (scope, deps) => {
661
661
  * attributes: { name: 'Jane Smith', bio: 'Fantastic author' }
662
662
  * }
663
663
  * ];
664
- *
664
+ *
665
665
  * // Output: Full polymorphic object (type field removed as redundant)
666
666
  * // {
667
667
  * // id: '500',
@@ -673,7 +673,7 @@ export const transformJsonApiToSimplified = (scope, deps) => {
673
673
  * // bio: 'Fantastic author'
674
674
  * // }
675
675
  * // }
676
- *
676
+ *
677
677
  * @example
678
678
  * // Input: With included resources (recursive expansion)
679
679
  * const data = {
@@ -714,7 +714,7 @@ export const transformJsonApiToSimplified = (scope, deps) => {
714
714
  * attributes: { number: 2, title: 'Basics' }
715
715
  * }
716
716
  * ];
717
- *
717
+ *
718
718
  * // Output: Recursively expanded
719
719
  * // {
720
720
  * // id: '400',
@@ -729,19 +729,19 @@ export const transformJsonApiToSimplified = (scope, deps) => {
729
729
  * // { id: '1002', number: 2, title: 'Basics' }
730
730
  * // ]
731
731
  * // }
732
- *
732
+ *
733
733
  * @description
734
734
  * Used by:
735
735
  * - transformJsonApiToSimplified for each resource
736
736
  * - Calls itself recursively for included resources
737
- *
737
+ *
738
738
  * Purpose:
739
739
  * - Core transformation logic for single resources
740
740
  * - Creates minimal relationship objects from relationships
741
741
  * - Creates arrays of minimal relationship objects for to-many relationships
742
742
  * - Recursively expands and transforms included data
743
743
  * - Handles polymorphic relationships
744
- *
744
+ *
745
745
  * Data flow:
746
746
  * 1. Copies ID and attributes to result
747
747
  * 2. Processes each relationship:
@@ -752,102 +752,106 @@ export const transformJsonApiToSimplified = (scope, deps) => {
752
752
  * - Finds matching included resources
753
753
  * - Recursively transforms them (preserving their relationships!)
754
754
  * - Adds transformed objects/arrays to result
755
- *
755
+ *
756
756
  * @private
757
757
  */
758
758
  export const transformSingleJsonApiToSimplified = (scope, deps) => {
759
759
  // Extract needed values from scope and deps
760
- const data = scope.data;
761
- const included = scope.included;
762
- const schema = deps.context.schemaStructure;
763
- const relationships = deps.context.schemaRelationships;
764
- const scopes = deps.context.scopes;
760
+ const data = scope.data
761
+ const included = scope.included
762
+ const schema = deps.context.schemaStructure
763
+ const relationships = deps.context.schemaRelationships
764
+ const scopes = deps.context.scopes
765
765
 
766
- const simplified = {};
766
+ const simplified = {}
767
767
 
768
768
  // Add ID
769
769
  if (data.id) {
770
- simplified.id = data.id;
770
+ simplified.id = data.id
771
771
  }
772
772
 
773
773
  // Add attributes
774
- Object.assign(simplified, data.attributes || {});
774
+ Object.assign(simplified, data.attributes || {})
775
775
 
776
776
  // Process relationships to create minimal objects
777
777
  if (data.relationships) {
778
778
  for (const [relName, relData] of Object.entries(data.relationships)) {
779
779
  // Find schema field for this belongsTo relationship
780
- const schemaEntry = Object.entries(schema).find(([_, def]) => def.as === relName);
780
+ const schemaEntry = Object.entries(schema).find(([_, def]) => def.as === relName)
781
781
 
782
782
  if (schemaEntry) {
783
- const [fieldName, fieldDef] = schemaEntry;
783
+ const [fieldName, fieldDef] = schemaEntry
784
784
  if (fieldDef.belongsTo && relData.data) {
785
785
  // Create minimal relationship object (no more foreign key field)
786
- simplified[relName] = { id: relData.data.id };
786
+ simplified[relName] = { id: relData.data.id }
787
787
  }
788
788
  }
789
789
 
790
790
  // Check if this is a polymorphic relationship from the relationships config
791
- const rel = relationships?.[relName];
791
+ const rel = relationships?.[relName]
792
792
  if (rel?.belongsToPolymorphic && relData.data) {
793
793
  // Create minimal relationship object for polymorphic (no more type/id fields)
794
- simplified[relName] = { id: relData.data.id, _type: relData.data.type };
794
+ simplified[relName] = { id: relData.data.id, _type: relData.data.type }
795
795
  }
796
796
 
797
797
  // Handle to-many relationships (create minimal objects with just IDs)
798
798
  if (rel?.type === 'hasMany' || rel?.type === 'manyToMany') {
799
799
  if (relData.data && Array.isArray(relData.data)) {
800
- simplified[relName] = relData.data.map(item => ({ id: item.id }));
800
+ simplified[relName] = relData.data.map(item => ({ id: item.id }))
801
801
  }
802
802
  }
803
803
 
804
804
  // Handle includes (nested objects)
805
805
  if (included && relData.data) {
806
806
  const findIncluded = (ref) =>
807
- included.find(inc => inc.type === ref.type && inc.id === ref.id);
807
+ included.find(inc => inc.type === ref.type && inc.id === ref.id)
808
808
 
809
809
  if (Array.isArray(relData.data)) {
810
- const nestedData = relData.data.map(findIncluded).filter(Boolean);
810
+ const nestedData = relData.data.map(findIncluded).filter(Boolean)
811
811
  if (nestedData.length > 0) {
812
812
  simplified[relName] = nestedData.map(item => {
813
813
  // For each included record, transform it recursively to restore its relationship fields
814
814
  const itemSimplified = transformSingleJsonApiToSimplified(
815
815
  { data: item, included: null },
816
- { context: {
817
- schemaStructure: scopes?.[item.type]?.vars?.schemaInfo?.schema || {},
818
- schemaRelationships: scopes?.[item.type]?.vars?.schemaInfo?.schemaRelationships || {},
819
- scopes
820
- } }
821
- );
816
+ {
817
+ context: {
818
+ schemaStructure: scopes?.[item.type]?.vars?.schemaInfo?.schema || {},
819
+ schemaRelationships: scopes?.[item.type]?.vars?.schemaInfo?.schemaRelationships || {},
820
+ scopes
821
+ }
822
+ }
823
+ )
822
824
  // For polymorphic relationships, add _type field to identify the resource type
823
825
  if (rel?.belongsToPolymorphic) {
824
- itemSimplified._type = item.type;
826
+ itemSimplified._type = item.type
825
827
  }
826
- return itemSimplified;
827
- });
828
+ return itemSimplified
829
+ })
828
830
  }
829
831
  } else {
830
- const nestedData = findIncluded(relData.data);
832
+ const nestedData = findIncluded(relData.data)
831
833
  if (nestedData) {
832
834
  // Transform the single included record to restore its relationship fields
833
835
  const itemSimplified = transformSingleJsonApiToSimplified(
834
836
  { data: nestedData, included: null },
835
- { context: {
836
- schemaStructure: scopes?.[nestedData.type]?.vars?.schemaInfo?.schema || {},
837
- schemaRelationships: scopes?.[nestedData.type]?.vars?.schemaInfo?.schemaRelationships || {},
838
- scopes
839
- } }
840
- );
837
+ {
838
+ context: {
839
+ schemaStructure: scopes?.[nestedData.type]?.vars?.schemaInfo?.schema || {},
840
+ schemaRelationships: scopes?.[nestedData.type]?.vars?.schemaInfo?.schemaRelationships || {},
841
+ scopes
842
+ }
843
+ }
844
+ )
841
845
  // For polymorphic relationships, add _type field to identify the resource type
842
846
  if (rel?.belongsToPolymorphic) {
843
- itemSimplified._type = nestedData.type;
847
+ itemSimplified._type = nestedData.type
844
848
  }
845
- simplified[relName] = itemSimplified;
849
+ simplified[relName] = itemSimplified
846
850
  }
847
851
  }
848
852
  }
849
853
  }
850
854
  }
851
855
 
852
- return simplified;
853
- };
856
+ return simplified
857
+ }