json-rest-api 1.0.14 → 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 +11 -2
  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 +1 -1
  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,12 +1,12 @@
1
- import { describe, it, before, beforeEach, after } from 'node:test';
2
- import assert from 'node:assert/strict';
3
- import knexLib from 'knex';
4
- import {
5
- cleanTables,
6
- createJsonApiDocument,
7
- createRelationship
8
- } from './helpers/test-utils.js';
9
- import { createSearchSchemaMergeApi } from './fixtures/api-configs.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 {
5
+ cleanTables,
6
+ createJsonApiDocument,
7
+ createRelationship
8
+ } from './helpers/test-utils.js'
9
+ import { createSearchSchemaMergeApi } from './fixtures/api-configs.js'
10
10
 
11
11
  // Create Knex instance for tests - always use SQLite in-memory
12
12
  const knex = knexLib({
@@ -15,105 +15,105 @@ const knex = knexLib({
15
15
  filename: ':memory:'
16
16
  },
17
17
  useNullAsDefault: true
18
- });
18
+ })
19
19
 
20
20
  describe('SearchSchema Merge Behavior', () => {
21
- let api;
21
+ let api
22
22
 
23
23
  before(async () => {
24
24
  // Create API instance once - NEVER in beforeEach
25
- api = await createSearchSchemaMergeApi(knex);
26
- });
25
+ api = await createSearchSchemaMergeApi(knex)
26
+ })
27
27
 
28
28
  after(async () => {
29
29
  // Clean up - destroy connection
30
- await knex.destroy();
31
- });
30
+ await knex.destroy()
31
+ })
32
32
 
33
33
  beforeEach(async () => {
34
34
  // Clean tables between tests
35
- await cleanTables(knex, ['searchmerge_products', 'searchmerge_users', 'searchmerge_orders']);
36
- });
35
+ await cleanTables(knex, ['searchmerge_products', 'searchmerge_users', 'searchmerge_orders'])
36
+ })
37
37
 
38
38
  it('should merge search:true fields with explicit searchSchema', async () => {
39
39
  // First do a query to trigger schema compilation
40
40
  await api.resources.products.query({
41
41
  queryParams: {}
42
- });
43
-
42
+ })
43
+
44
44
  // Get the compiled searchSchema for products (has both search:true and searchSchema)
45
- const scope = api.resources.products;
46
- const searchSchema = scope.vars.schemaInfo.searchSchemaInstance;
47
-
45
+ const scope = api.resources.products
46
+ const searchSchema = scope.vars.schemaInfo.searchSchemaInstance
47
+
48
48
  // Verify searchSchema exists
49
- assert.ok(searchSchema, 'searchSchema should exist');
50
- assert.ok(searchSchema.structure, 'searchSchema should have structure');
51
-
49
+ assert.ok(searchSchema, 'searchSchema should exist')
50
+ assert.ok(searchSchema.structure, 'searchSchema should have structure')
51
+
52
52
  // Check that search:true fields were added
53
- assert.ok(searchSchema.structure.name, 'name field should be searchable');
54
- assert.equal(searchSchema.structure.name.filterOperator || '=', '=', 'name should use default = operator');
55
-
56
- assert.ok(searchSchema.structure.description, 'description field should be searchable');
57
- assert.equal(searchSchema.structure.description.filterOperator || '=', '=', 'description should use default = operator');
58
-
59
- assert.ok(searchSchema.structure.sku, 'sku field should be searchable');
60
- assert.equal(searchSchema.structure.sku.filterOperator || '=', '=', 'sku should use default = operator');
61
-
53
+ assert.ok(searchSchema.structure.name, 'name field should be searchable')
54
+ assert.equal(searchSchema.structure.name.filterOperator || '=', '=', 'name should use default = operator')
55
+
56
+ assert.ok(searchSchema.structure.description, 'description field should be searchable')
57
+ assert.equal(searchSchema.structure.description.filterOperator || '=', '=', 'description should use default = operator')
58
+
59
+ assert.ok(searchSchema.structure.sku, 'sku field should be searchable')
60
+ assert.equal(searchSchema.structure.sku.filterOperator || '=', '=', 'sku should use default = operator')
61
+
62
62
  // Check that explicit searchSchema fields override search:true
63
- assert.ok(searchSchema.structure.price, 'price field should be searchable');
64
- assert.equal(searchSchema.structure.price.filterOperator, 'between', 'price should use between operator from searchSchema');
65
-
63
+ assert.ok(searchSchema.structure.price, 'price field should be searchable')
64
+ assert.equal(searchSchema.structure.price.filterOperator, 'between', 'price should use between operator from searchSchema')
65
+
66
66
  // Check that virtual fields from searchSchema are present
67
- assert.ok(searchSchema.structure.category_name, 'category_name virtual field should be searchable');
68
- assert.equal(searchSchema.structure.category_name.filterOperator, 'like', 'category_name should use like operator');
69
- assert.equal(searchSchema.structure.category_name.actualField, 'category.name', 'category_name should map to category.name');
70
-
67
+ assert.ok(searchSchema.structure.category_name, 'category_name virtual field should be searchable')
68
+ assert.equal(searchSchema.structure.category_name.filterOperator, 'like', 'category_name should use like operator')
69
+ assert.equal(searchSchema.structure.category_name.actualField, 'category.name', 'category_name should map to category.name')
70
+
71
71
  // Check explicit searchSchema field not marked with search:true
72
- assert.ok(searchSchema.structure.status, 'status field should be searchable from searchSchema');
73
- assert.equal(searchSchema.structure.status.filterOperator, 'in', 'status should use in operator');
74
-
72
+ assert.ok(searchSchema.structure.status, 'status field should be searchable from searchSchema')
73
+ assert.equal(searchSchema.structure.status.filterOperator, 'in', 'status should use in operator')
74
+
75
75
  // Check that non-searchable fields are not included
76
- assert.ok(!searchSchema.structure.category_id, 'category_id should not be searchable');
77
- });
76
+ assert.ok(!searchSchema.structure.category_id, 'category_id should not be searchable')
77
+ })
78
78
 
79
79
  it('should work with only search:true fields (no explicit searchSchema)', async () => {
80
80
  // First do a query to trigger schema compilation
81
81
  await api.resources.users.query({
82
82
  queryParams: {}
83
- });
84
-
83
+ })
84
+
85
85
  // Get the compiled searchSchema for users (only search:true fields)
86
- const scope = api.resources.users;
87
- const searchSchema = scope.vars.schemaInfo.searchSchemaInstance;
88
-
89
- assert.ok(searchSchema, 'searchSchema should exist');
90
- assert.ok(searchSchema.structure.username, 'username should be searchable');
91
- assert.ok(searchSchema.structure.email, 'email should be searchable');
92
- assert.ok(searchSchema.structure.bio, 'bio should be searchable');
93
- assert.equal(searchSchema.structure.bio.filterOperator, 'like', 'bio should use like operator');
94
- assert.ok(!searchSchema.structure.age, 'age should not be searchable');
95
- });
86
+ const scope = api.resources.users
87
+ const searchSchema = scope.vars.schemaInfo.searchSchemaInstance
88
+
89
+ assert.ok(searchSchema, 'searchSchema should exist')
90
+ assert.ok(searchSchema.structure.username, 'username should be searchable')
91
+ assert.ok(searchSchema.structure.email, 'email should be searchable')
92
+ assert.ok(searchSchema.structure.bio, 'bio should be searchable')
93
+ assert.equal(searchSchema.structure.bio.filterOperator, 'like', 'bio should use like operator')
94
+ assert.ok(!searchSchema.structure.age, 'age should not be searchable')
95
+ })
96
96
 
97
97
  it('should work with only explicit searchSchema (no search:true)', async () => {
98
98
  // First do a query to trigger schema compilation
99
99
  await api.resources.orders.query({
100
100
  queryParams: {}
101
- });
102
-
101
+ })
102
+
103
103
  // Get the compiled searchSchema for orders (only explicit searchSchema)
104
- const scope = api.resources.orders;
105
- const searchSchema = scope.vars.schemaInfo.searchSchemaInstance;
106
-
107
- assert.ok(searchSchema, 'searchSchema should exist');
108
- assert.ok(searchSchema.structure.order_number, 'order_number should be searchable');
109
- assert.ok(searchSchema.structure.status, 'status should be searchable');
110
- assert.equal(searchSchema.structure.status.filterOperator, 'in', 'status should use in operator');
111
- assert.ok(!searchSchema.structure.total, 'total should not be searchable');
112
- });
113
-
104
+ const scope = api.resources.orders
105
+ const searchSchema = scope.vars.schemaInfo.searchSchemaInstance
106
+
107
+ assert.ok(searchSchema, 'searchSchema should exist')
108
+ assert.ok(searchSchema.structure.order_number, 'order_number should be searchable')
109
+ assert.ok(searchSchema.structure.status, 'status should be searchable')
110
+ assert.equal(searchSchema.structure.status.filterOperator, 'in', 'status should use in operator')
111
+ assert.ok(!searchSchema.structure.total, 'total should not be searchable')
112
+ })
113
+
114
114
  it('should allow filtering with merged searchSchema fields', async () => {
115
- const context = {};
116
-
115
+ const context = {}
116
+
117
117
  // Create some test data using API methods
118
118
  const product1 = await api.resources.products.post({
119
119
  inputRecord: createJsonApiDocument('products', {
@@ -123,8 +123,8 @@ describe('SearchSchema Merge Behavior', () => {
123
123
  sku: 'WGT-001',
124
124
  status: 'active'
125
125
  })
126
- }, context);
127
-
126
+ }, context)
127
+
128
128
  const product2 = await api.resources.products.post({
129
129
  inputRecord: createJsonApiDocument('products', {
130
130
  name: 'Widget B',
@@ -133,8 +133,8 @@ describe('SearchSchema Merge Behavior', () => {
133
133
  sku: 'WGT-002',
134
134
  status: 'inactive'
135
135
  })
136
- }, context);
137
-
136
+ }, context)
137
+
138
138
  // Test filtering with field from search:true
139
139
  const nameResults = await api.resources.products.query({
140
140
  queryParams: {
@@ -142,10 +142,10 @@ describe('SearchSchema Merge Behavior', () => {
142
142
  name: 'Widget A'
143
143
  }
144
144
  }
145
- }, context);
146
- assert.equal(nameResults.data.length, 1, 'Should find one product by name');
147
- assert.equal(nameResults.data[0].attributes.name, 'Widget A');
148
-
145
+ }, context)
146
+ assert.equal(nameResults.data.length, 1, 'Should find one product by name')
147
+ assert.equal(nameResults.data[0].attributes.name, 'Widget A')
148
+
149
149
  // Test filtering with field from explicit searchSchema (between operator)
150
150
  const priceResults = await api.resources.products.query({
151
151
  queryParams: {
@@ -153,10 +153,10 @@ describe('SearchSchema Merge Behavior', () => {
153
153
  price: [40, 60] // Between 40 and 60
154
154
  }
155
155
  }
156
- }, context);
157
- assert.equal(priceResults.data.length, 1, 'Should find one product in price range');
158
- assert.equal(priceResults.data[0].attributes.price, 50);
159
-
156
+ }, context)
157
+ assert.equal(priceResults.data.length, 1, 'Should find one product in price range')
158
+ assert.equal(priceResults.data[0].attributes.price, 50)
159
+
160
160
  // Test filtering with explicit searchSchema field (in operator)
161
161
  const statusResults = await api.resources.products.query({
162
162
  queryParams: {
@@ -164,10 +164,10 @@ describe('SearchSchema Merge Behavior', () => {
164
164
  status: ['active']
165
165
  }
166
166
  }
167
- }, context);
168
- assert.equal(statusResults.data.length, 1, 'Should find one active product');
169
- assert.equal(statusResults.data[0].attributes.status, 'active');
170
-
167
+ }, context)
168
+ assert.equal(statusResults.data.length, 1, 'Should find one active product')
169
+ assert.equal(statusResults.data[0].attributes.status, 'active')
170
+
171
171
  // Test filtering with search:true field that wasn't overridden
172
172
  const skuResults = await api.resources.products.query({
173
173
  queryParams: {
@@ -175,8 +175,8 @@ describe('SearchSchema Merge Behavior', () => {
175
175
  sku: 'WGT-002'
176
176
  }
177
177
  }
178
- }, context);
179
- assert.equal(skuResults.data.length, 1, 'Should find one product by SKU');
180
- assert.equal(skuResults.data[0].attributes.sku, 'WGT-002');
181
- });
182
- });
178
+ }, context)
179
+ assert.equal(skuResults.data.length, 1, 'Should find one product by SKU')
180
+ assert.equal(skuResults.data[0].attributes.sku, 'WGT-002')
181
+ })
182
+ })