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
@@ -195,7 +195,7 @@ describe('Advanced Positioning Plugin Tests', { timeout: 30000 }, () => {
195
195
  });
196
196
 
197
197
  // Query tasks
198
- const tasks = await api.resources.tasks.query({
198
+ const { data: tasks } = await api.resources.tasks.query({
199
199
  queryParams: {
200
200
  filters: { category_id: category.id },
201
201
  sort: ['position']
@@ -245,7 +245,7 @@ describe('Advanced Positioning Plugin Tests', { timeout: 30000 }, () => {
245
245
  beforeId: task1.id
246
246
  });
247
247
 
248
- const tasks = await api.resources.tasks.query({
248
+ const { data: tasks } = await api.resources.tasks.query({
249
249
  queryParams: {
250
250
  filters: { category_id: category.id },
251
251
  sort: ['position']
@@ -295,7 +295,7 @@ describe('Advanced Positioning Plugin Tests', { timeout: 30000 }, () => {
295
295
  assert(item1.sort_order < item2.sort_order);
296
296
 
297
297
  // Query should work with custom field
298
- const items = await api.resources.items.query({
298
+ const { data: items } = await api.resources.items.query({
299
299
  queryParams: {
300
300
  filters: { project_id: project.id },
301
301
  sort: ['sort_order']
@@ -339,7 +339,7 @@ describe('Advanced Positioning Plugin Tests', { timeout: 30000 }, () => {
339
339
  insertBefore: item2.id
340
340
  });
341
341
 
342
- const items = await api.resources.items.query({
342
+ const { data: items } = await api.resources.items.query({
343
343
  queryParams: {
344
344
  filters: { project_id: project.id },
345
345
  sort: ['position']
@@ -387,7 +387,7 @@ describe('Advanced Positioning Plugin Tests', { timeout: 30000 }, () => {
387
387
  assert.equal(results.length, 5);
388
388
 
389
389
  // Query all tasks
390
- const tasks = await api.resources.tasks.query({
390
+ const { data: tasks } = await api.resources.tasks.query({
391
391
  queryParams: {
392
392
  filters: { category_id: category.id },
393
393
  sort: ['position']
@@ -453,7 +453,7 @@ describe('Advanced Positioning Plugin Tests', { timeout: 30000 }, () => {
453
453
  await Promise.all(updates);
454
454
 
455
455
  // Verify final state
456
- const finalTasks = await api.resources.tasks.query({
456
+ const { data: finalTasks } = await api.resources.tasks.query({
457
457
  queryParams: {
458
458
  filters: { category_id: category.id },
459
459
  sort: ['position']
@@ -499,7 +499,7 @@ describe('Advanced Positioning Plugin Tests', { timeout: 30000 }, () => {
499
499
  beforeId: 'FIRST'
500
500
  });
501
501
 
502
- const allTasks = await api.resources.tasks.query({
502
+ const { data: allTasks } = await api.resources.tasks.query({
503
503
  queryParams: {
504
504
  filters: { category_id: category.id },
505
505
  sort: ['position']
@@ -515,7 +515,7 @@ describe('Advanced Positioning Plugin Tests', { timeout: 30000 }, () => {
515
515
  beforeId: 'FIRST'
516
516
  });
517
517
 
518
- const updatedTasks = await api.resources.tasks.query({
518
+ const { data: updatedTasks } = await api.resources.tasks.query({
519
519
  queryParams: {
520
520
  filters: { category_id: category.id },
521
521
  sort: ['position']
@@ -548,7 +548,7 @@ describe('Advanced Positioning Plugin Tests', { timeout: 30000 }, () => {
548
548
  beforeId: null
549
549
  });
550
550
 
551
- const tasks = await api.resources.tasks.query({
551
+ const { data: tasks } = await api.resources.tasks.query({
552
552
  queryParams: {
553
553
  filters: { category_id: category.id },
554
554
  sort: ['position']
@@ -600,7 +600,7 @@ describe('Advanced Positioning Plugin Tests', { timeout: 30000 }, () => {
600
600
  });
601
601
 
602
602
  // Query non-deleted tasks
603
- const activeTasks = await softDeleteApi.resources.tasks.query({
603
+ const { data: activeTasks } = await softDeleteApi.resources.tasks.query({
604
604
  queryParams: {
605
605
  filters: {
606
606
  category_id: category.id,
@@ -660,7 +660,7 @@ describe('Advanced Positioning Plugin Tests', { timeout: 30000 }, () => {
660
660
  });
661
661
 
662
662
  // Query v1 tasks
663
- const v1Items = await versionApi.resources.tasks.query({
663
+ const { data: v1Items } = await versionApi.resources.tasks.query({
664
664
  queryParams: {
665
665
  filters: { category_id: category.id, version: 1 },
666
666
  sort: ['position']
@@ -673,7 +673,7 @@ describe('Advanced Positioning Plugin Tests', { timeout: 30000 }, () => {
673
673
  assert.equal(v1Items[2].title, 'Task 3 v1');
674
674
 
675
675
  // Query v2 tasks
676
- const v2Items = await versionApi.resources.tasks.query({
676
+ const { data: v2Items } = await versionApi.resources.tasks.query({
677
677
  queryParams: {
678
678
  filters: { category_id: category.id, version: 2 },
679
679
  sort: ['position']
@@ -74,7 +74,7 @@ describe('Extended Positioning Plugin Tests', { timeout: 30000 }, () => {
74
74
  });
75
75
 
76
76
  // Query and verify order
77
- const tasks = await api.resources.tasks.query({
77
+ const { data: tasks } = await api.resources.tasks.query({
78
78
  queryParams: {
79
79
  filters: { category_id: category.id },
80
80
  sort: ['position']
@@ -120,7 +120,7 @@ describe('Extended Positioning Plugin Tests', { timeout: 30000 }, () => {
120
120
  }
121
121
 
122
122
  // Query and verify all are properly ordered
123
- const tasks = await api.resources.tasks.query({
123
+ const { data: tasks } = await api.resources.tasks.query({
124
124
  queryParams: {
125
125
  filters: { category_id: category.id },
126
126
  sort: ['position']
@@ -152,7 +152,7 @@ describe('Extended Positioning Plugin Tests', { timeout: 30000 }, () => {
152
152
  });
153
153
 
154
154
  // Should position at end since beforeId doesn't exist
155
- const tasks = await api.resources.tasks.query({
155
+ const { data: tasks } = await api.resources.tasks.query({
156
156
  queryParams: {
157
157
  filters: { category_id: category.id },
158
158
  sort: ['position']
@@ -180,7 +180,7 @@ describe('Extended Positioning Plugin Tests', { timeout: 30000 }, () => {
180
180
  });
181
181
 
182
182
  // Task 2 should be positioned normally in its category (ignoring beforeId from other category)
183
- const cat2Tasks = await api.resources.tasks.query({
183
+ const { data: cat2Tasks } = await api.resources.tasks.query({
184
184
  queryParams: {
185
185
  filters: { category_id: category2.id },
186
186
  sort: ['position']
@@ -226,7 +226,7 @@ describe('Extended Positioning Plugin Tests', { timeout: 30000 }, () => {
226
226
  });
227
227
 
228
228
  // Query final order
229
- const finalTasks = await api.resources.tasks.query({
229
+ const { data: finalTasks } = await api.resources.tasks.query({
230
230
  queryParams: {
231
231
  filters: { category_id: category.id },
232
232
  sort: ['position']
@@ -270,7 +270,7 @@ describe('Extended Positioning Plugin Tests', { timeout: 30000 }, () => {
270
270
  });
271
271
 
272
272
  // Verify category 1 only has task 2
273
- const cat1Tasks = await api.resources.tasks.query({
273
+ const { data: cat1Tasks } = await api.resources.tasks.query({
274
274
  queryParams: {
275
275
  filters: { category_id: category1.id },
276
276
  sort: ['position']
@@ -281,7 +281,7 @@ describe('Extended Positioning Plugin Tests', { timeout: 30000 }, () => {
281
281
  assert.equal(cat1Tasks[0].title, 'Task 2');
282
282
 
283
283
  // Verify category 2 has tasks in correct order
284
- const cat2Tasks = await api.resources.tasks.query({
284
+ const { data: cat2Tasks } = await api.resources.tasks.query({
285
285
  queryParams: {
286
286
  filters: { category_id: category2.id },
287
287
  sort: ['position']
@@ -320,7 +320,7 @@ describe('Extended Positioning Plugin Tests', { timeout: 30000 }, () => {
320
320
  assert.equal(updatedTask.position, originalPosition, 'Position should not change');
321
321
 
322
322
  // Verify order is maintained
323
- const allTasks = await api.resources.tasks.query({
323
+ const { data: allTasks } = await api.resources.tasks.query({
324
324
  queryParams: {
325
325
  filters: { category_id: category.id },
326
326
  sort: ['position']
@@ -355,7 +355,7 @@ describe('Extended Positioning Plugin Tests', { timeout: 30000 }, () => {
355
355
  await Promise.all(promises);
356
356
 
357
357
  // Query all tasks
358
- const tasks = await api.resources.tasks.query({
358
+ const { data: tasks } = await api.resources.tasks.query({
359
359
  queryParams: {
360
360
  filters: { category_id: category.id },
361
361
  sort: ['position']
@@ -402,7 +402,7 @@ describe('Extended Positioning Plugin Tests', { timeout: 30000 }, () => {
402
402
  await Promise.all(promises);
403
403
 
404
404
  // Query all tasks
405
- const tasks = await api.resources.tasks.query({
405
+ const { data: tasks } = await api.resources.tasks.query({
406
406
  queryParams: {
407
407
  filters: { category_id: category.id },
408
408
  sort: ['position']
@@ -459,7 +459,7 @@ describe('Extended Positioning Plugin Tests', { timeout: 30000 }, () => {
459
459
  });
460
460
 
461
461
  // Query with ascending sort
462
- const ascTasks = await api.resources.tasks.query({
462
+ const { data: ascTasks } = await api.resources.tasks.query({
463
463
  queryParams: {
464
464
  filters: { category_id: category.id },
465
465
  sort: ['position']
@@ -470,7 +470,7 @@ describe('Extended Positioning Plugin Tests', { timeout: 30000 }, () => {
470
470
  assert.equal(ascTasks.map(t => t.title).join(','), 'Task A,Task B,Task C,Task D,Task E');
471
471
 
472
472
  // Query with descending sort
473
- const descTasks = await api.resources.tasks.query({
473
+ const { data: descTasks } = await api.resources.tasks.query({
474
474
  queryParams: {
475
475
  filters: { category_id: category.id },
476
476
  sort: ['-position']
@@ -493,7 +493,7 @@ describe('Extended Positioning Plugin Tests', { timeout: 30000 }, () => {
493
493
  }
494
494
 
495
495
  // Query first page
496
- const page1 = await api.resources.tasks.query({
496
+ const { data: page1 } = await api.resources.tasks.query({
497
497
  queryParams: {
498
498
  filters: { category_id: category.id },
499
499
  sort: ['position'],
@@ -506,7 +506,7 @@ describe('Extended Positioning Plugin Tests', { timeout: 30000 }, () => {
506
506
  assert.equal(page1[4].title, 'Task 05');
507
507
 
508
508
  // Query second page
509
- const page2 = await api.resources.tasks.query({
509
+ const { data: page2 } = await api.resources.tasks.query({
510
510
  queryParams: {
511
511
  filters: { category_id: category.id },
512
512
  sort: ['position'],
@@ -544,7 +544,7 @@ describe('Extended Positioning Plugin Tests', { timeout: 30000 }, () => {
544
544
  }
545
545
 
546
546
  // Sort by title then position
547
- const tasks = await api.resources.tasks.query({
547
+ const { data: tasks } = await api.resources.tasks.query({
548
548
  queryParams: {
549
549
  filters: { category_id: category.id },
550
550
  sort: ['title', 'position']
@@ -587,7 +587,7 @@ describe('Extended Positioning Plugin Tests', { timeout: 30000 }, () => {
587
587
  }
588
588
 
589
589
  // Query all and verify ordering
590
- const allTasks = await api.resources.tasks.query({
590
+ const { data: allTasks } = await api.resources.tasks.query({
591
591
  queryParams: {
592
592
  filters: { category_id: category.id },
593
593
  sort: ['position'],
@@ -652,7 +652,7 @@ describe('Extended Positioning Plugin Tests', { timeout: 30000 }, () => {
652
652
  });
653
653
 
654
654
  // Verify final state
655
- const finalTasks = await api.resources.tasks.query({
655
+ const { data: finalTasks } = await api.resources.tasks.query({
656
656
  queryParams: {
657
657
  filters: { category_id: category.id },
658
658
  sort: ['position']
@@ -702,7 +702,7 @@ describe('Extended Positioning Plugin Tests', { timeout: 30000 }, () => {
702
702
  });
703
703
 
704
704
  // Query tasks with category
705
- const tasksWithCat = await api.resources.tasks.query({
705
+ const { data: tasksWithCat } = await api.resources.tasks.query({
706
706
  queryParams: {
707
707
  filters: { category_id: category.id },
708
708
  sort: ['position']
@@ -713,7 +713,7 @@ describe('Extended Positioning Plugin Tests', { timeout: 30000 }, () => {
713
713
  assert(tasksWithCat[0].position < tasksWithCat[1].position);
714
714
 
715
715
  // Query tasks without category
716
- const tasksNoCat = await api.resources.tasks.query({
716
+ const { data: tasksNoCat } = await api.resources.tasks.query({
717
717
  queryParams: {
718
718
  filters: { category_id: null },
719
719
  sort: ['position']
@@ -721,7 +721,7 @@ describe('Extended Positioning Plugin Tests', { timeout: 30000 }, () => {
721
721
  });
722
722
 
723
723
  // Debug: Check what we actually have
724
- const allTasks = await api.resources.tasks.query({
724
+ const { data: allTasks } = await api.resources.tasks.query({
725
725
  queryParams: {
726
726
  sort: ['position']
727
727
  }
@@ -730,7 +730,7 @@ describe('Extended Positioning Plugin Tests', { timeout: 30000 }, () => {
730
730
  console.log('Tasks with null category:', tasksNoCat.length);
731
731
 
732
732
  // Try querying without filter
733
- const tasksNoFilter = await api.resources.tasks.query({
733
+ const { data: tasksNoFilter } = await api.resources.tasks.query({
734
734
  queryParams: {
735
735
  sort: ['position']
736
736
  }
@@ -801,7 +801,7 @@ describe('Extended Positioning Plugin Tests', { timeout: 30000 }, () => {
801
801
  beforeId: task1.id // Should be used
802
802
  });
803
803
 
804
- const tasks = await api.resources.tasks.query({
804
+ const { data: tasks } = await api.resources.tasks.query({
805
805
  queryParams: {
806
806
  filters: { category_id: category.id },
807
807
  sort: ['position']
@@ -829,7 +829,7 @@ describe('Extended Positioning Plugin Tests', { timeout: 30000 }, () => {
829
829
  }
830
830
 
831
831
  // Query with position sort
832
- const tasks = await api.resources.tasks.query({
832
+ const { data: tasks } = await api.resources.tasks.query({
833
833
  queryParams: {
834
834
  filters: { category_id: category.id },
835
835
  sort: ['position'],
@@ -852,7 +852,7 @@ describe('Extended Positioning Plugin Tests', { timeout: 30000 }, () => {
852
852
  });
853
853
 
854
854
  // Verify it's positioned correctly
855
- const updatedTasks = await api.resources.tasks.query({
855
+ const { data: updatedTasks } = await api.resources.tasks.query({
856
856
  queryParams: {
857
857
  filters: { category_id: category.id },
858
858
  sort: ['position'],
@@ -891,7 +891,7 @@ describe('Extended Positioning Plugin Tests', { timeout: 30000 }, () => {
891
891
  await Promise.all(updates);
892
892
 
893
893
  // Verify final state
894
- const finalTasks = await api.resources.tasks.query({
894
+ const { data: finalTasks } = await api.resources.tasks.query({
895
895
  queryParams: {
896
896
  filters: { category_id: category.id },
897
897
  sort: ['position']
@@ -98,7 +98,7 @@ describe('Multi-Filter Positioning Tests', { timeout: 30000 }, () => {
98
98
  });
99
99
 
100
100
  // Query each combination
101
- const p1ActiveItems = await api.resources.items.query({
101
+ const { data: p1ActiveItems } = await api.resources.items.query({
102
102
  queryParams: {
103
103
  filters: { project_id: project1.id, status: 'active' },
104
104
  sort: ['position']
@@ -109,7 +109,7 @@ describe('Multi-Filter Positioning Tests', { timeout: 30000 }, () => {
109
109
  assert.equal(p1ActiveItems[0].name, 'P1 Active 1');
110
110
  assert.equal(p1ActiveItems[1].name, 'P1 Active 2');
111
111
 
112
- const p1ArchivedItems = await api.resources.items.query({
112
+ const { data: p1ArchivedItems } = await api.resources.items.query({
113
113
  queryParams: {
114
114
  filters: { project_id: project1.id, status: 'archived' },
115
115
  sort: ['position']
@@ -120,7 +120,7 @@ describe('Multi-Filter Positioning Tests', { timeout: 30000 }, () => {
120
120
  assert.equal(p1ArchivedItems[0].name, 'P1 Archived 1');
121
121
  assert.equal(p1ArchivedItems[1].name, 'P1 Archived 2');
122
122
 
123
- const p2ActiveItems = await api.resources.items.query({
123
+ const { data: p2ActiveItems } = await api.resources.items.query({
124
124
  queryParams: {
125
125
  filters: { project_id: project2.id, status: 'active' },
126
126
  sort: ['position']
@@ -170,7 +170,7 @@ describe('Multi-Filter Positioning Tests', { timeout: 30000 }, () => {
170
170
  });
171
171
 
172
172
  // Query different combinations
173
- const projectActiveItems = await api.resources.items.query({
173
+ const { data: projectActiveItems } = await api.resources.items.query({
174
174
  queryParams: {
175
175
  filters: { project_id: project.id, status: 'active' },
176
176
  sort: ['position']
@@ -179,7 +179,7 @@ describe('Multi-Filter Positioning Tests', { timeout: 30000 }, () => {
179
179
  assert.equal(projectActiveItems.length, 1);
180
180
  assert.equal(projectActiveItems[0].name, 'Item 1');
181
181
 
182
- const projectNullStatusItems = await api.resources.items.query({
182
+ const { data: projectNullStatusItems } = await api.resources.items.query({
183
183
  queryParams: {
184
184
  filters: { project_id: project.id, status: null },
185
185
  sort: ['position']
@@ -188,7 +188,7 @@ describe('Multi-Filter Positioning Tests', { timeout: 30000 }, () => {
188
188
  assert.equal(projectNullStatusItems.length, 1);
189
189
  assert.equal(projectNullStatusItems[0].name, 'Item 2');
190
190
 
191
- const nullProjectActiveItems = await api.resources.items.query({
191
+ const { data: nullProjectActiveItems } = await api.resources.items.query({
192
192
  queryParams: {
193
193
  filters: { project_id: null, status: 'active' },
194
194
  sort: ['position']
@@ -197,7 +197,7 @@ describe('Multi-Filter Positioning Tests', { timeout: 30000 }, () => {
197
197
  assert.equal(nullProjectActiveItems.length, 1);
198
198
  assert.equal(nullProjectActiveItems[0].name, 'Item 3');
199
199
 
200
- const bothNullItems = await api.resources.items.query({
200
+ const { data: bothNullItems } = await api.resources.items.query({
201
201
  queryParams: {
202
202
  filters: { project_id: null, status: null },
203
203
  sort: ['position']
@@ -240,7 +240,7 @@ describe('Multi-Filter Positioning Tests', { timeout: 30000 }, () => {
240
240
  });
241
241
 
242
242
  // Item 4 should be first in its filter group
243
- const archivedItems = await api.resources.items.query({
243
+ const { data: archivedItems } = await api.resources.items.query({
244
244
  queryParams: {
245
245
  filters: { project_id: project.id, status: 'archived' },
246
246
  sort: ['position']
@@ -259,7 +259,7 @@ describe('Multi-Filter Positioning Tests', { timeout: 30000 }, () => {
259
259
  beforeId: item2.id
260
260
  });
261
261
 
262
- const activeItems = await api.resources.items.query({
262
+ const { data: activeItems } = await api.resources.items.query({
263
263
  queryParams: {
264
264
  filters: { project_id: project.id, status: 'active' },
265
265
  sort: ['position']
@@ -312,7 +312,7 @@ describe('Multi-Filter Positioning Tests', { timeout: 30000 }, () => {
312
312
  });
313
313
 
314
314
  // Verify project 1 now has 2 items
315
- const p1Items = await api.resources.items.query({
315
+ const { data: p1Items } = await api.resources.items.query({
316
316
  queryParams: {
317
317
  filters: { project_id: project1.id, status: 'active' },
318
318
  sort: ['position']
@@ -324,7 +324,7 @@ describe('Multi-Filter Positioning Tests', { timeout: 30000 }, () => {
324
324
  assert.equal(p1Items[1].name, 'Item 3');
325
325
 
326
326
  // Verify project 2 has items in correct order
327
- const p2Items = await api.resources.items.query({
327
+ const { data: p2Items } = await api.resources.items.query({
328
328
  queryParams: {
329
329
  filters: { project_id: project2.id, status: 'active' },
330
330
  sort: ['position']
@@ -342,7 +342,7 @@ describe('Multi-Filter Positioning Tests', { timeout: 30000 }, () => {
342
342
  });
343
343
 
344
344
  // Verify it's now in a different filter group
345
- const p1ActiveItems = await api.resources.items.query({
345
+ const { data: p1ActiveItems } = await api.resources.items.query({
346
346
  queryParams: {
347
347
  filters: { project_id: project1.id, status: 'active' },
348
348
  sort: ['position']
@@ -352,7 +352,7 @@ describe('Multi-Filter Positioning Tests', { timeout: 30000 }, () => {
352
352
  assert.equal(p1ActiveItems.length, 1);
353
353
  assert.equal(p1ActiveItems[0].name, 'Item 1');
354
354
 
355
- const p1ArchivedItems = await api.resources.items.query({
355
+ const { data: p1ArchivedItems } = await api.resources.items.query({
356
356
  queryParams: {
357
357
  filters: { project_id: project1.id, status: 'archived' },
358
358
  sort: ['position']
@@ -405,7 +405,7 @@ describe('Multi-Filter Positioning Tests', { timeout: 30000 }, () => {
405
405
  // Each combination should have its own sequence
406
406
  for (const status of statuses) {
407
407
  for (const priority of priorities) {
408
- const filtered = await multiApi.resources.items.query({
408
+ const { data: filtered } = await multiApi.resources.items.query({
409
409
  queryParams: {
410
410
  filters: {
411
411
  project_id: project.id,
@@ -435,7 +435,7 @@ describe('Multi-Filter Positioning Tests', { timeout: 30000 }, () => {
435
435
  }
436
436
 
437
437
  // Verify the active-high combination now has 5 items
438
- const activeHighItems = await multiApi.resources.items.query({
438
+ const { data: activeHighItems } = await multiApi.resources.items.query({
439
439
  queryParams: {
440
440
  filters: {
441
441
  project_id: project.id,
@@ -486,7 +486,7 @@ describe('Multi-Filter Positioning Tests', { timeout: 30000 }, () => {
486
486
  });
487
487
 
488
488
  // Query with only one filter (should return items from multiple position groups)
489
- const activeItems = await api.resources.items.query({
489
+ const { data: activeItems } = await api.resources.items.query({
490
490
  queryParams: {
491
491
  filters: { status: 'active' },
492
492
  sort: ['position']
@@ -545,7 +545,7 @@ describe('Multi-Filter Positioning Tests', { timeout: 30000 }, () => {
545
545
  await Promise.all(updates);
546
546
 
547
547
  // Verify distributions
548
- const p1Active = await api.resources.items.query({
548
+ const { data: p1Active } = await api.resources.items.query({
549
549
  queryParams: {
550
550
  filters: { project_id: project1.id, status: 'active' },
551
551
  sort: ['position']
@@ -553,7 +553,7 @@ describe('Multi-Filter Positioning Tests', { timeout: 30000 }, () => {
553
553
  });
554
554
  assert.equal(p1Active.length, 4); // 10 - 3 archived - 3 moved to p2
555
555
 
556
- const p1Archived = await api.resources.items.query({
556
+ const { data: p1Archived } = await api.resources.items.query({
557
557
  queryParams: {
558
558
  filters: { project_id: project1.id, status: 'archived' },
559
559
  sort: ['position']
@@ -561,7 +561,7 @@ describe('Multi-Filter Positioning Tests', { timeout: 30000 }, () => {
561
561
  });
562
562
  assert.equal(p1Archived.length, 3);
563
563
 
564
- const p2Active = await api.resources.items.query({
564
+ const { data: p2Active } = await api.resources.items.query({
565
565
  queryParams: {
566
566
  filters: { project_id: project2.id, status: 'active' },
567
567
  sort: ['position']
@@ -569,7 +569,7 @@ describe('Multi-Filter Positioning Tests', { timeout: 30000 }, () => {
569
569
  });
570
570
  assert.equal(p2Active.length, 2);
571
571
 
572
- const p2Archived = await api.resources.items.query({
572
+ const { data: p2Archived } = await api.resources.items.query({
573
573
  queryParams: {
574
574
  filters: { project_id: project2.id, status: 'archived' },
575
575
  sort: ['position']
@@ -619,7 +619,7 @@ describe('Multi-Filter Positioning Tests', { timeout: 30000 }, () => {
619
619
  });
620
620
 
621
621
  // Query with empty string filter
622
- const emptyStatusItems = await api.resources.items.query({
622
+ const { data: emptyStatusItems } = await api.resources.items.query({
623
623
  queryParams: {
624
624
  filters: { project_id: project.id, status: '' },
625
625
  sort: ['position']
@@ -630,7 +630,7 @@ describe('Multi-Filter Positioning Tests', { timeout: 30000 }, () => {
630
630
  assert.equal(emptyStatusItems[0].name, 'Item 1');
631
631
 
632
632
  // Query with null filter
633
- const nullStatusItems = await api.resources.items.query({
633
+ const { data: nullStatusItems } = await api.resources.items.query({
634
634
  queryParams: {
635
635
  filters: { project_id: project.id, status: null },
636
636
  sort: ['position']
@@ -671,7 +671,7 @@ describe('Multi-Filter Positioning Tests', { timeout: 30000 }, () => {
671
671
  });
672
672
 
673
673
  // Query with string '1'
674
- const status1Items = await api.resources.items.query({
674
+ const { data: status1Items } = await api.resources.items.query({
675
675
  queryParams: {
676
676
  filters: { project_id: project.id, status: '1' },
677
677
  sort: ['position']
@@ -684,7 +684,7 @@ describe('Multi-Filter Positioning Tests', { timeout: 30000 }, () => {
684
684
  assert(status1Items[0].position < status1Items[1].position);
685
685
 
686
686
  // Query with string '01'
687
- const status01Items = await api.resources.items.query({
687
+ const { data: status01Items } = await api.resources.items.query({
688
688
  queryParams: {
689
689
  filters: { project_id: project.id, status: '01' },
690
690
  sort: ['position']
@@ -223,7 +223,7 @@ describe('Positioning Plugin Tests', { timeout: 30000 }, () => {
223
223
  });
224
224
 
225
225
  // Get fresh data
226
- const allTasks = await api.resources.tasks.query({
226
+ const { data: allTasks } = await api.resources.tasks.query({
227
227
  queryParams: {
228
228
  filters: { category_id: category.id },
229
229
  sort: ['position']
@@ -529,7 +529,7 @@ describe('Positioning Plugin Tests', { timeout: 30000 }, () => {
529
529
  });
530
530
 
531
531
  // Query with position sort
532
- const sorted = await api.resources.tasks.query({
532
+ const { data: sorted } = await api.resources.tasks.query({
533
533
  queryParams: {
534
534
  filters: { category_id: category.id },
535
535
  sort: ['position']
@@ -544,7 +544,7 @@ describe('Positioning Plugin Tests', { timeout: 30000 }, () => {
544
544
  assert.equal(sorted[2].title, 'C Task');
545
545
 
546
546
  // Query with reverse position sort
547
- const reverseSorted = await api.resources.tasks.query({
547
+ const { data: reverseSorted } = await api.resources.tasks.query({
548
548
  queryParams: {
549
549
  filters: { category_id: category.id },
550
550
  sort: ['-position']
@@ -131,15 +131,17 @@ describe('Query Limits and Include Limits', () => {
131
131
  id: { type: 'id' },
132
132
  content: { type: 'string', required: true },
133
133
  rating: { type: 'number', required: true },
134
+ reviewer_name: { type: 'string', required: true },
134
135
  reviewable_type: { type: 'string', required: true },
135
- reviewable_id: { type: 'number', required: true },
136
+ reviewable_id: { type: 'number', required: true }
137
+ },
138
+ relationships: {
136
139
  reviewable: {
137
140
  belongsToPolymorphic: {
138
141
  types: ['books', 'authors', 'publishers'],
139
142
  typeField: 'reviewable_type',
140
143
  idField: 'reviewable_id'
141
- },
142
- as: 'reviewable'
144
+ }
143
145
  }
144
146
  }
145
147
  });
@@ -318,10 +320,19 @@ describe('Query Limits and Include Limits', () => {
318
320
 
319
321
  describe('Include Limits - Polymorphic Relationships', () => {
320
322
  beforeEach(async () => {
321
- // Create a publisher first
323
+ // Create a country first
324
+ const country = await api.resources.countries.post({
325
+ inputRecord: createJsonApiDocument('countries', {
326
+ name: 'Test Country',
327
+ code: 'TC'
328
+ })
329
+ });
330
+
331
+ // Create a publisher with country
322
332
  const publisher = await api.resources.publishers.post({
323
333
  inputRecord: createJsonApiDocument('publishers', {
324
- name: 'Test Publisher'
334
+ name: 'Test Publisher',
335
+ country_id: parseInt(country.id)
325
336
  })
326
337
  });
327
338
 
@@ -349,13 +360,17 @@ describe('Query Limits and Include Limits', () => {
349
360
  type: 'reviews',
350
361
  attributes: {
351
362
  content: `Review ${i} for Book ${bookNum}`,
352
- rating: Math.floor(Math.random() * 5) + 1
363
+ rating: Math.floor(Math.random() * 5) + 1,
364
+ reviewer_name: `Reviewer ${i}`
353
365
  },
354
366
  relationships: {
355
- reviewable: { data: { type: 'books', id: book.id } }
367
+ reviewable: {
368
+ data: { type: 'books', id: book.id }
369
+ }
356
370
  }
357
371
  }
358
- }
372
+ },
373
+ simplified: false
359
374
  });
360
375
  }
361
376
  }
@@ -405,6 +420,7 @@ describe('Query Limits and Include Limits', () => {
405
420
  relationships: {
406
421
  subitems: {
407
422
  hasMany: 'subitems',
423
+ foreignKey: 'test_item_id',
408
424
  include: {
409
425
  limit: 25 // Exceeds queryMaxLimit of 20
410
426
  }
@@ -11,7 +11,6 @@ import {
11
11
  countRecords
12
12
  } from './helpers/test-utils.js';
13
13
  import { createBasicApi } from './fixtures/api-configs.js';
14
- import { RestApiRelationshipsPlugin } from '../plugins/core/rest-api-relationships-plugin.js';
15
14
 
16
15
  // Create Knex instance for tests
17
16
  const knex = knexLib({
@@ -29,11 +28,11 @@ describe('Relationship Endpoints Plugin', () => {
29
28
  before(async () => {
30
29
  // Initialize API with the relationships plugin
31
30
  api = await createBasicApi(knex, {
32
- resourceUrlPrefix: '/api'
31
+ express: {
32
+ mountPath: '' // No mount path for this test
33
+ },
34
+ includeExpress: true
33
35
  });
34
-
35
- // Add the relationships plugin
36
- await api.use(RestApiRelationshipsPlugin);
37
36
  });
38
37
 
39
38
  after(async () => {
@@ -331,7 +331,6 @@ describe('Relationship Operations', () => {
331
331
  }
332
332
  };
333
333
  console.log('THE NEXT LINE WILL BLOCK THE TEST')
334
- debugger
335
334
  await basicApi.resources.books.put({
336
335
  id: bookResult.data.id,
337
336
  inputRecord: putDoc,