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,16 +1,16 @@
1
- import { describe, it, before, beforeEach, after } from 'node:test';
2
- import assert from 'node:assert/strict';
3
- import knexLib from 'knex';
4
- import { Api } from 'hooked-api';
5
- import { RestApiPlugin } from '../plugins/core/rest-api-plugin.js';
6
- import { RestApiKnexPlugin } from '../plugins/core/rest-api-knex-plugin.js';
1
+ import { describe, it, before, beforeEach, after } from 'node:test'
2
+ import assert from 'node:assert/strict'
3
+ import knexLib from 'knex'
4
+ import { Api } from 'hooked-api'
5
+ import { RestApiPlugin } from '../plugins/core/rest-api-plugin.js'
6
+ import { RestApiKnexPlugin } from '../plugins/core/rest-api-knex-plugin.js'
7
7
  import {
8
8
  cleanTables,
9
9
  countRecords,
10
10
  createJsonApiDocument,
11
11
  createRelationship,
12
12
  validateJsonApiStructure
13
- } from './helpers/test-utils.js';
13
+ } from './helpers/test-utils.js'
14
14
 
15
15
  // Create Knex instance for tests
16
16
  const knex = knexLib({
@@ -19,16 +19,16 @@ const knex = knexLib({
19
19
  filename: ':memory:'
20
20
  },
21
21
  useNullAsDefault: true
22
- });
22
+ })
23
23
 
24
24
  describe('Query Limits and Include Limits', () => {
25
- let api;
25
+ let api
26
26
 
27
27
  before(async () => {
28
28
  // Create API with specific limits for testing
29
29
  api = new Api({
30
30
  name: 'test-api-limits'
31
- });
31
+ })
32
32
 
33
33
  await api.use(RestApiPlugin, {
34
34
  queryDefaultLimit: 10,
@@ -37,11 +37,11 @@ describe('Query Limits and Include Limits', () => {
37
37
  returnFullRecord: {
38
38
  post: true
39
39
  }
40
- });
40
+ })
41
41
 
42
42
  await api.use(RestApiKnexPlugin, {
43
- knex: knex
44
- });
43
+ knex
44
+ })
45
45
 
46
46
  // Countries
47
47
  await api.addResource('countries', {
@@ -50,8 +50,8 @@ describe('Query Limits and Include Limits', () => {
50
50
  name: { type: 'string', required: true },
51
51
  code: { type: 'string', required: true }
52
52
  }
53
- });
54
- await api.resources.countries.createKnexTable();
53
+ })
54
+ await api.resources.countries.createKnexTable()
55
55
 
56
56
  // Publishers
57
57
  await api.addResource('publishers', {
@@ -61,9 +61,9 @@ describe('Query Limits and Include Limits', () => {
61
61
  country_id: { type: 'number', belongsTo: 'countries', as: 'country' }
62
62
  },
63
63
  relationships: {
64
- books: {
64
+ books: {
65
65
  type: 'hasMany',
66
- target: 'books',
66
+ target: 'books',
67
67
  foreignKey: 'publisher_id',
68
68
  // Relationships are always includable via ?include=
69
69
  include: {
@@ -71,8 +71,8 @@ describe('Query Limits and Include Limits', () => {
71
71
  }
72
72
  }
73
73
  }
74
- });
75
- await api.resources.publishers.createKnexTable();
74
+ })
75
+ await api.resources.publishers.createKnexTable()
76
76
 
77
77
  // Authors
78
78
  await api.addResource('authors', {
@@ -82,10 +82,10 @@ describe('Query Limits and Include Limits', () => {
82
82
  country_id: { type: 'number', belongsTo: 'countries', as: 'country' }
83
83
  },
84
84
  relationships: {
85
- books: {
85
+ books: {
86
86
  type: 'manyToMany',
87
- through: 'book_authors',
88
- foreignKey: 'author_id',
87
+ through: 'book_authors',
88
+ foreignKey: 'author_id',
89
89
  otherKey: 'book_id',
90
90
  // Relationships are always includable via ?include=
91
91
  include: {
@@ -94,8 +94,8 @@ describe('Query Limits and Include Limits', () => {
94
94
  }
95
95
  }
96
96
  }
97
- });
98
- await api.resources.authors.createKnexTable();
97
+ })
98
+ await api.resources.authors.createKnexTable()
99
99
 
100
100
  // Books
101
101
  await api.addResource('books', {
@@ -105,17 +105,17 @@ describe('Query Limits and Include Limits', () => {
105
105
  publisher_id: { type: 'number', belongsTo: 'publishers', as: 'publisher' }
106
106
  },
107
107
  relationships: {
108
- authors: {
108
+ authors: {
109
109
  type: 'manyToMany',
110
- through: 'book_authors',
111
- foreignKey: 'book_id',
110
+ through: 'book_authors',
111
+ foreignKey: 'book_id',
112
112
  otherKey: 'author_id'
113
113
  // Relationships are always includable via ?include=
114
114
  },
115
115
  reviews: { type: 'hasMany', target: 'reviews', via: 'reviewable' }
116
116
  }
117
- });
118
- await api.resources.books.createKnexTable();
117
+ })
118
+ await api.resources.books.createKnexTable()
119
119
 
120
120
  // Book Authors (pivot table)
121
121
  await api.addResource('book_authors', {
@@ -124,8 +124,8 @@ describe('Query Limits and Include Limits', () => {
124
124
  book_id: { type: 'number', belongsTo: 'books', as: 'book' },
125
125
  author_id: { type: 'number', belongsTo: 'authors', as: 'author' }
126
126
  }
127
- });
128
- await api.resources.book_authors.createKnexTable();
127
+ })
128
+ await api.resources.book_authors.createKnexTable()
129
129
 
130
130
  // Reviews (polymorphic)
131
131
  await api.addResource('reviews', {
@@ -146,19 +146,19 @@ describe('Query Limits and Include Limits', () => {
146
146
  }
147
147
  }
148
148
  }
149
- });
150
- await api.resources.reviews.createKnexTable();
151
- });
149
+ })
150
+ await api.resources.reviews.createKnexTable()
151
+ })
152
152
 
153
153
  after(async () => {
154
- await knex.destroy();
155
- });
154
+ await knex.destroy()
155
+ })
156
156
 
157
157
  beforeEach(async () => {
158
158
  await cleanTables(knex, [
159
159
  'countries', 'publishers', 'authors', 'books', 'book_authors', 'reviews'
160
- ]);
161
- });
160
+ ])
161
+ })
162
162
 
163
163
  describe('Query Default Limits', () => {
164
164
  it('should apply queryDefaultLimit when no page size specified', async () => {
@@ -169,18 +169,18 @@ describe('Query Limits and Include Limits', () => {
169
169
  name: `Country ${i}`,
170
170
  code: `C${i}`
171
171
  })
172
- });
172
+ })
173
173
  }
174
174
 
175
175
  // Query without page size
176
176
  const result = await api.resources.countries.query({
177
177
  queryParams: {},
178
178
  simplified: false
179
- });
179
+ })
180
180
 
181
181
  // Should use queryDefaultLimit of 10
182
- assert.equal(result.data.length, 10, 'Should apply default limit of 10');
183
- });
182
+ assert.equal(result.data.length, 10, 'Should apply default limit of 10')
183
+ })
184
184
 
185
185
  it('should respect queryMaxLimit when page size exceeds it', async () => {
186
186
  // Create 100 countries
@@ -190,7 +190,7 @@ describe('Query Limits and Include Limits', () => {
190
190
  name: `Country ${i}`,
191
191
  code: `C${i}`
192
192
  })
193
- });
193
+ })
194
194
  }
195
195
 
196
196
  // Query with page size exceeding max (use offset-based pagination)
@@ -199,12 +199,12 @@ describe('Query Limits and Include Limits', () => {
199
199
  page: { size: 100, number: 1 } // Exceeds queryMaxLimit of 50
200
200
  },
201
201
  simplified: false
202
- });
202
+ })
203
203
 
204
204
  // Should be capped at queryMaxLimit of 50
205
- assert.equal(result.data.length, 50, 'Should cap at queryMaxLimit of 50');
206
- });
207
- });
205
+ assert.equal(result.data.length, 50, 'Should cap at queryMaxLimit of 50')
206
+ })
207
+ })
208
208
 
209
209
  describe('Include Limits - HasMany Relationships', () => {
210
210
  beforeEach(async () => {
@@ -214,27 +214,27 @@ describe('Query Limits and Include Limits', () => {
214
214
  name: 'USA',
215
215
  code: 'US'
216
216
  })
217
- });
217
+ })
218
218
 
219
219
  const publisher = await api.resources.publishers.post({
220
220
  inputRecord: createJsonApiDocument('publishers', {
221
221
  name: 'Test Publisher'
222
222
  }, {
223
- country: createRelationship({ type: 'countries', id: String(country.id) })
224
- })
225
- });
223
+ country: createRelationship({ type: 'countries', id: String(country.id) })
224
+ })
225
+ })
226
226
 
227
227
  // Create 25 books for the publisher
228
228
  for (let i = 1; i <= 25; i++) {
229
229
  await api.resources.books.post({
230
230
  inputRecord: createJsonApiDocument('books', {
231
- title: `Book ${i}`
232
- }, {
233
- publisher: createRelationship({ type: 'publishers', id: String(publisher.id) })
234
- })
235
- });
231
+ title: `Book ${i}`
232
+ }, {
233
+ publisher: createRelationship({ type: 'publishers', id: String(publisher.id) })
234
+ })
235
+ })
236
236
  }
237
- });
237
+ })
238
238
 
239
239
  it('should apply default limit to hasMany includes when not specified', async () => {
240
240
  // Query publishers with books (no explicit limit in include config)
@@ -243,17 +243,17 @@ describe('Query Limits and Include Limits', () => {
243
243
  include: ['books']
244
244
  },
245
245
  simplified: false
246
- });
246
+ })
247
+
248
+ assert(result.data, 'Result should have data array')
249
+ assert(result.data.length > 0, 'Should have at least one publisher')
247
250
 
248
- assert(result.data, 'Result should have data array');
249
- assert(result.data.length > 0, 'Should have at least one publisher');
250
-
251
- const publisher = result.data[0];
252
- const publisherBooks = (result.included || []).filter(r => r.type === 'books');
251
+ const publisher = result.data[0]
252
+ const publisherBooks = (result.included || []).filter(r => r.type === 'books')
253
253
 
254
254
  // Should apply queryDefaultLimit of 10
255
- assert.equal(publisherBooks.length, 10, 'Should include only 10 books by default');
256
- });
255
+ assert.equal(publisherBooks.length, 10, 'Should include only 10 books by default')
256
+ })
257
257
 
258
258
  it('should respect explicit limit in relationship config', async () => {
259
259
  // Create a country first
@@ -262,8 +262,8 @@ describe('Query Limits and Include Limits', () => {
262
262
  name: 'Test Country',
263
263
  code: 'TC'
264
264
  })
265
- });
266
-
265
+ })
266
+
267
267
  // Create a publisher
268
268
  const publisher = await api.resources.publishers.post({
269
269
  inputRecord: createJsonApiDocument('publishers', {
@@ -271,33 +271,33 @@ describe('Query Limits and Include Limits', () => {
271
271
  }, {
272
272
  country: createRelationship({ type: 'countries', id: String(country.id) })
273
273
  })
274
- });
274
+ })
275
275
 
276
276
  // Create authors with many books
277
277
  const author = await api.resources.authors.post({
278
278
  inputRecord: createJsonApiDocument('authors', {
279
279
  name: 'Test Author'
280
280
  }, {
281
- country: createRelationship({ type: 'countries', id: String(country.id) })
282
- })
283
- });
281
+ country: createRelationship({ type: 'countries', id: String(country.id) })
282
+ })
283
+ })
284
284
 
285
285
  // Create 10 books and associate with author
286
286
  for (let i = 1; i <= 10; i++) {
287
287
  const book = await api.resources.books.post({
288
288
  inputRecord: createJsonApiDocument('books', {
289
- title: `Book ${i}`
290
- }, {
291
- publisher: createRelationship({ type: 'publishers', id: String(publisher.id) })
292
- })
293
- });
289
+ title: `Book ${i}`
290
+ }, {
291
+ publisher: createRelationship({ type: 'publishers', id: String(publisher.id) })
292
+ })
293
+ })
294
294
 
295
295
  await api.resources.book_authors.post({
296
296
  inputRecord: createJsonApiDocument('book_authors', {}, {
297
- book: createRelationship({ type: 'books', id: String(book.id) }),
298
- author: createRelationship({ type: 'authors', id: String(author.id) })
299
- })
300
- });
297
+ book: createRelationship({ type: 'books', id: String(book.id) }),
298
+ author: createRelationship({ type: 'authors', id: String(author.id) })
299
+ })
300
+ })
301
301
  }
302
302
 
303
303
  // Query authors with books (has explicit limit of 3)
@@ -306,12 +306,12 @@ describe('Query Limits and Include Limits', () => {
306
306
  include: ['books']
307
307
  },
308
308
  simplified: false
309
- });
309
+ })
310
310
 
311
- const authorBooks = result.included.filter(r => r.type === 'books');
312
- assert.equal(authorBooks.length, 3, 'Should respect explicit limit of 3');
313
- });
314
- });
311
+ const authorBooks = result.included.filter(r => r.type === 'books')
312
+ assert.equal(authorBooks.length, 3, 'Should respect explicit limit of 3')
313
+ })
314
+ })
315
315
 
316
316
  describe('Include Limits - Polymorphic Relationships', () => {
317
317
  beforeEach(async () => {
@@ -321,16 +321,16 @@ describe('Query Limits and Include Limits', () => {
321
321
  name: 'Test Country',
322
322
  code: 'TC'
323
323
  })
324
- });
324
+ })
325
325
 
326
326
  // Create a publisher with country
327
327
  const publisher = await api.resources.publishers.post({
328
328
  inputRecord: createJsonApiDocument('publishers', {
329
329
  name: 'Test Publisher'
330
330
  }, {
331
- country: createRelationship({ type: 'countries', id: String(country.id) })
332
- })
333
- });
331
+ country: createRelationship({ type: 'countries', id: String(country.id) })
332
+ })
333
+ })
334
334
 
335
335
  // Create books with reviews
336
336
  for (let bookNum = 1; bookNum <= 3; bookNum++) {
@@ -346,7 +346,7 @@ describe('Query Limits and Include Limits', () => {
346
346
  }
347
347
  }
348
348
  }
349
- });
349
+ })
350
350
 
351
351
  // Create 15 reviews for each book
352
352
  for (let i = 1; i <= 15; i++) {
@@ -367,10 +367,10 @@ describe('Query Limits and Include Limits', () => {
367
367
  }
368
368
  },
369
369
  simplified: false
370
- });
370
+ })
371
371
  }
372
372
  }
373
- });
373
+ })
374
374
 
375
375
  it('should apply default limit to polymorphic hasMany includes', async () => {
376
376
  // Query books with reviews
@@ -379,21 +379,21 @@ describe('Query Limits and Include Limits', () => {
379
379
  include: ['reviews']
380
380
  },
381
381
  simplified: false
382
- });
382
+ })
383
383
 
384
384
  // Without window strategy, the default strategy will limit total reviews
385
- const totalReviews = result.included.filter(r => r.type === 'reviews');
386
-
385
+ const totalReviews = result.included.filter(r => r.type === 'reviews')
386
+
387
387
  // Should apply queryDefaultLimit of 10 to total reviews
388
- assert.equal(totalReviews.length, 10, 'Should limit total reviews to default of 10');
389
- });
390
- });
388
+ assert.equal(totalReviews.length, 10, 'Should limit total reviews to default of 10')
389
+ })
390
+ })
391
391
 
392
392
  describe('Include Limit Validation', () => {
393
393
  it('should throw error when include limit exceeds queryMaxLimit at resource definition', async () => {
394
394
  // Create a new API instance with stricter limits
395
- const strictApi = new Api({ name: 'strict-api' });
396
-
395
+ const strictApi = new Api({ name: 'strict-api' })
396
+
397
397
  await strictApi.use(RestApiPlugin, {
398
398
  queryDefaultLimit: 5,
399
399
  queryMaxLimit: 20,
@@ -401,9 +401,9 @@ describe('Query Limits and Include Limits', () => {
401
401
  returnFullRecord: {
402
402
  post: true
403
403
  }
404
- });
405
-
406
- await strictApi.use(RestApiKnexPlugin, { knex });
404
+ })
405
+
406
+ await strictApi.use(RestApiKnexPlugin, { knex })
407
407
 
408
408
  // Try to create a resource with include limit exceeding max
409
409
  await assert.rejects(
@@ -423,15 +423,15 @@ describe('Query Limits and Include Limits', () => {
423
423
  }
424
424
  }
425
425
  }
426
- });
426
+ })
427
427
  },
428
428
  (err) => {
429
- return err.message && err.message.includes('limit') && err.message.includes('exceeds queryMaxLimit');
429
+ return err.message && err.message.includes('limit') && err.message.includes('exceeds queryMaxLimit')
430
430
  },
431
431
  'Should throw error for excessive include limit'
432
- );
433
- });
434
- });
432
+ )
433
+ })
434
+ })
435
435
 
436
436
  describe('Window Strategy with Defaults', () => {
437
437
  beforeEach(async () => {
@@ -441,7 +441,7 @@ describe('Query Limits and Include Limits', () => {
441
441
  name: 'USA',
442
442
  code: 'US'
443
443
  })
444
- });
444
+ })
445
445
 
446
446
  // Create multiple publishers with different numbers of books
447
447
  for (let pubNum = 1; pubNum <= 3; pubNum++) {
@@ -450,13 +450,13 @@ describe('Query Limits and Include Limits', () => {
450
450
  data: createJsonApiDocument('publishers', {
451
451
  name: `Publisher ${pubNum}`
452
452
  }, {
453
- country: createRelationship({ type: 'countries', id: String(country.id) })
454
- }).data
453
+ country: createRelationship({ type: 'countries', id: String(country.id) })
454
+ }).data
455
455
  }
456
456
  })
457
457
 
458
458
  // Create different number of books for each publisher
459
- const bookCount = pubNum * 8; // 8, 16, 24 books
459
+ const bookCount = pubNum * 8 // 8, 16, 24 books
460
460
  for (let i = 1; i <= bookCount; i++) {
461
461
  await api.resources.books.post({
462
462
  inputRecord: {
@@ -470,10 +470,10 @@ describe('Query Limits and Include Limits', () => {
470
470
  }
471
471
  }
472
472
  }
473
- });
473
+ })
474
474
  }
475
475
  }
476
- });
476
+ })
477
477
 
478
478
  it('should apply per-parent limits with window strategy', async () => {
479
479
  // Query all publishers with books (window strategy configured)
@@ -482,27 +482,27 @@ describe('Query Limits and Include Limits', () => {
482
482
  include: ['books']
483
483
  },
484
484
  simplified: false
485
- });
485
+ })
486
486
 
487
487
  // Each publisher should have its own limit applied
488
488
  for (const publisher of result.data) {
489
- const publisherBooks = result.included.filter(r =>
490
- r.type === 'books' &&
489
+ const publisherBooks = result.included.filter(r =>
490
+ r.type === 'books' &&
491
491
  r.relationships.publisher.data.id === publisher.id
492
- );
493
-
492
+ )
493
+
494
494
  // Each publisher gets up to queryDefaultLimit (10) books
495
- assert(publisherBooks.length <= 10,
496
- `Publisher ${publisher.id} should have at most 10 books with window strategy`);
495
+ assert(publisherBooks.length <= 10,
496
+ `Publisher ${publisher.id} should have at most 10 books with window strategy`)
497
497
  }
498
498
 
499
499
  // Total books can exceed queryDefaultLimit since each parent gets its own limit
500
- const totalBooks = result.included.filter(r => r.type === 'books').length;
501
- assert(totalBooks > 10, 'Total books should exceed 10 with window strategy');
500
+ const totalBooks = result.included.filter(r => r.type === 'books').length
501
+ assert(totalBooks > 10, 'Total books should exceed 10 with window strategy')
502
502
  // Publisher 1 has 8 books, Publishers 2 and 3 are limited to 10 each
503
- assert.equal(totalBooks, 28, 'Should have 28 total books (8 + 10 + 10)');
504
- });
505
- });
503
+ assert.equal(totalBooks, 28, 'Should have 28 total books (8 + 10 + 10)')
504
+ })
505
+ })
506
506
 
507
507
  describe('Many-to-Many with Window Strategy', () => {
508
508
  it('should apply window limits to many-to-many relationships', async () => {
@@ -512,38 +512,38 @@ describe('Query Limits and Include Limits', () => {
512
512
  name: 'USA',
513
513
  code: 'US'
514
514
  })
515
- });
515
+ })
516
516
 
517
517
  const publisher = await api.resources.publishers.post({
518
518
  inputRecord: createJsonApiDocument('publishers', {
519
519
  name: 'Test Publisher'
520
520
  }, {
521
- country: createRelationship({ type: 'countries', id: String(country.id) })
522
- })
523
- });
521
+ country: createRelationship({ type: 'countries', id: String(country.id) })
522
+ })
523
+ })
524
524
 
525
525
  // Create 2 authors
526
- const authors = [];
526
+ const authors = []
527
527
  for (let i = 1; i <= 2; i++) {
528
528
  const author = await api.resources.authors.post({
529
529
  inputRecord: createJsonApiDocument('authors', {
530
530
  name: `Author ${i}`
531
531
  }, {
532
- country: createRelationship({ type: 'countries', id: String(country.id) })
533
- })
534
- });
535
- authors.push(author);
532
+ country: createRelationship({ type: 'countries', id: String(country.id) })
533
+ })
534
+ })
535
+ authors.push(author)
536
536
  }
537
537
 
538
538
  // Create 10 books and associate ALL with BOTH authors
539
539
  for (let i = 1; i <= 10; i++) {
540
540
  const book = await api.resources.books.post({
541
541
  inputRecord: createJsonApiDocument('books', {
542
- title: `Book ${i}`
543
- }, {
544
- publisher: createRelationship({ type: 'publishers', id: String(publisher.id) })
545
- })
546
- });
542
+ title: `Book ${i}`
543
+ }, {
544
+ publisher: createRelationship({ type: 'publishers', id: String(publisher.id) })
545
+ })
546
+ })
547
547
 
548
548
  // Associate with both authors
549
549
  for (const author of authors) {
@@ -552,7 +552,7 @@ describe('Query Limits and Include Limits', () => {
552
552
  book: createRelationship({ type: 'books', id: String(book.id) }),
553
553
  author: createRelationship({ type: 'authors', id: String(author.id) })
554
554
  })
555
- });
555
+ })
556
556
  }
557
557
  }
558
558
 
@@ -562,24 +562,23 @@ describe('Query Limits and Include Limits', () => {
562
562
  include: ['books']
563
563
  },
564
564
  simplified: false
565
- });
565
+ })
566
566
 
567
-
568
567
  // Each author should get exactly 3 books (configured limit)
569
568
  for (const author of result.data) {
570
569
  // Check the author's relationships to see which books they have
571
- const authorBookIds = author.relationships?.books?.data?.map(b => b.id) || [];
572
-
570
+ const authorBookIds = author.relationships?.books?.data?.map(b => b.id) || []
571
+
573
572
  // Verify the books exist in the included array
574
- const authorBooks = result.included.filter(r =>
573
+ const authorBooks = result.included.filter(r =>
575
574
  r.type === 'books' && authorBookIds.includes(r.id)
576
- );
577
-
578
- assert.equal(authorBookIds.length, 3,
579
- `Author ${author.id} should have exactly 3 book relationships (configured limit)`);
580
- assert.equal(authorBooks.length, 3,
581
- `Author ${author.id} should have exactly 3 books in included array`);
575
+ )
576
+
577
+ assert.equal(authorBookIds.length, 3,
578
+ `Author ${author.id} should have exactly 3 book relationships (configured limit)`)
579
+ assert.equal(authorBooks.length, 3,
580
+ `Author ${author.id} should have exactly 3 books in included array`)
582
581
  }
583
- });
584
- });
585
- });
582
+ })
583
+ })
584
+ })