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,192 +1,191 @@
1
- export const CorsPlugin = {
2
- name: 'rest-api-cors',
3
- dependencies: ['rest-api'],
4
-
5
- async install({ api, addHook, vars, helpers, log, pluginOptions, runHooks }) {
6
- // Get CORS configuration
7
- const corsOptions = pluginOptions || {};
8
-
9
- // Store configuration - using a plain object for runtime updates
10
- const corsConfig = {
11
- // Origin configuration
12
- origin: corsOptions.origin || '*', // Can be string, regex, array, or function
13
- credentials: corsOptions.credentials !== undefined ? corsOptions.credentials : true,
14
-
15
- // Allowed methods
16
- methods: corsOptions.methods || ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'],
17
-
18
- // Allowed headers
19
- allowedHeaders: corsOptions.allowedHeaders || [
20
- 'Content-Type',
21
- 'Authorization',
22
- 'X-Requested-With',
23
- 'X-HTTP-Method-Override',
24
- 'Accept',
25
- 'Origin'
26
- ],
27
-
28
- // Exposed headers (for client to read)
29
- exposedHeaders: corsOptions.exposedHeaders || [
30
- 'X-Total-Count',
31
- 'X-Page-Count',
32
- 'Link',
33
- 'Location'
34
- ],
35
-
36
- // Preflight cache
37
- maxAge: corsOptions.maxAge || 86400, // 24 hours
38
-
39
- // Options success status
40
- optionsSuccessStatus: corsOptions.optionsSuccessStatus || 204
41
- };
42
-
43
- // Store reference in vars for access in hooks
44
- vars.cors = corsConfig;
45
-
46
- // Helper to check if origin is allowed
47
- function isOriginAllowed(origin, allowedOrigin) {
48
- if (!origin) return !corsConfig.credentials; // No origin = same-origin request
49
-
50
- // String match
51
- if (typeof allowedOrigin === 'string') {
52
- return allowedOrigin === '*' || allowedOrigin === origin;
53
- }
1
+ export const CorsPlugin = {
2
+ name: 'rest-api-cors',
3
+ dependencies: ['rest-api'],
4
+
5
+ async install ({ api, addHook, vars, helpers, log, pluginOptions, runHooks }) {
6
+ // Get CORS configuration
7
+ const corsOptions = pluginOptions || {}
8
+
9
+ // Store configuration - using a plain object for runtime updates
10
+ const corsConfig = {
11
+ // Origin configuration
12
+ origin: corsOptions.origin || '*', // Can be string, regex, array, or function
13
+ credentials: corsOptions.credentials !== undefined ? corsOptions.credentials : true,
14
+
15
+ // Allowed methods
16
+ methods: corsOptions.methods || ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'],
17
+
18
+ // Allowed headers
19
+ allowedHeaders: corsOptions.allowedHeaders || [
20
+ 'Content-Type',
21
+ 'Authorization',
22
+ 'X-Requested-With',
23
+ 'X-HTTP-Method-Override',
24
+ 'Accept',
25
+ 'Origin'
26
+ ],
27
+
28
+ // Exposed headers (for client to read)
29
+ exposedHeaders: corsOptions.exposedHeaders || [
30
+ 'X-Total-Count',
31
+ 'X-Page-Count',
32
+ 'Link',
33
+ 'Location'
34
+ ],
35
+
36
+ // Preflight cache
37
+ maxAge: corsOptions.maxAge || 86400, // 24 hours
38
+
39
+ // Options success status
40
+ optionsSuccessStatus: corsOptions.optionsSuccessStatus || 204
41
+ }
54
42
 
55
- // Regex match
56
- if (allowedOrigin instanceof RegExp) {
57
- return allowedOrigin.test(origin);
58
- }
43
+ // Store reference in vars for access in hooks
44
+ vars.cors = corsConfig
59
45
 
60
- // Array of allowed origins
61
- if (Array.isArray(allowedOrigin)) {
62
- return allowedOrigin.some(allowed => isOriginAllowed(origin, allowed));
63
- }
46
+ // Helper to check if origin is allowed
47
+ function isOriginAllowed (origin, allowedOrigin) {
48
+ if (!origin) return !corsConfig.credentials // No origin = same-origin request
64
49
 
65
- // Function check
66
- if (typeof allowedOrigin === 'function') {
67
- return allowedOrigin(origin);
68
- }
50
+ // String match
51
+ if (typeof allowedOrigin === 'string') {
52
+ return allowedOrigin === '*' || allowedOrigin === origin
53
+ }
54
+
55
+ // Regex match
56
+ if (allowedOrigin instanceof RegExp) {
57
+ return allowedOrigin.test(origin)
58
+ }
69
59
 
70
- return false;
60
+ // Array of allowed origins
61
+ if (Array.isArray(allowedOrigin)) {
62
+ return allowedOrigin.some(allowed => isOriginAllowed(origin, allowed))
71
63
  }
72
64
 
73
- // Check if transport plugin is available
74
- if (!vars.transport) {
75
- log.error('CORS plugin requires a transport plugin (express, koa, etc.) to be installed');
76
- return;
65
+ // Function check
66
+ if (typeof allowedOrigin === 'function') {
67
+ return allowedOrigin(origin)
77
68
  }
78
69
 
70
+ return false
71
+ }
79
72
 
80
- // Register OPTIONS handler for all routes
81
- const optionsHandler = async ({ context, headers }) => {
82
- const origin = headers?.origin;
83
-
84
- log.debug('CORS OPTIONS request', { origin });
85
-
86
- // Check if origin is allowed
87
- if (isOriginAllowed(origin, corsConfig.origin)) {
88
- const responseHeaders = {
89
- 'Access-Control-Allow-Methods': corsConfig.methods.join(', '),
90
- 'Access-Control-Allow-Headers': corsConfig.allowedHeaders.join(', '),
91
- 'Access-Control-Max-Age': String(corsConfig.maxAge)
92
- };
93
-
94
- // Set origin header
95
- if (corsConfig.origin === '*' && !corsConfig.credentials) {
96
- responseHeaders['Access-Control-Allow-Origin'] = '*';
97
- } else if (origin) {
98
- responseHeaders['Access-Control-Allow-Origin'] = origin;
99
- responseHeaders['Vary'] = 'Origin';
100
- }
101
-
102
- // Set credentials if enabled
103
- if (corsConfig.credentials) {
104
- responseHeaders['Access-Control-Allow-Credentials'] = 'true';
105
- }
106
-
107
- return {
108
- statusCode: corsConfig.optionsSuccessStatus,
109
- headers: responseHeaders,
110
- body: null
111
- };
112
- } else {
113
- // Origin not allowed
114
- log.warn('CORS origin not allowed', { origin });
115
- return {
116
- statusCode: 403,
117
- body: { error: 'CORS origin not allowed' }
118
- };
73
+ // Check if transport plugin is available
74
+ if (!vars.transport) {
75
+ log.error('CORS plugin requires a transport plugin (express, koa, etc.) to be installed')
76
+ return
77
+ }
78
+
79
+ // Register OPTIONS handler for all routes
80
+ const optionsHandler = async ({ context, headers }) => {
81
+ const origin = headers?.origin
82
+
83
+ log.debug('CORS OPTIONS request', { origin })
84
+
85
+ // Check if origin is allowed
86
+ if (isOriginAllowed(origin, corsConfig.origin)) {
87
+ const responseHeaders = {
88
+ 'Access-Control-Allow-Methods': corsConfig.methods.join(', '),
89
+ 'Access-Control-Allow-Headers': corsConfig.allowedHeaders.join(', '),
90
+ 'Access-Control-Max-Age': String(corsConfig.maxAge)
119
91
  }
120
- };
121
-
122
- // Register OPTIONS route for all paths
123
- await api.addRoute({
124
- method: 'OPTIONS',
125
- path: vars.transport.matchAll,
126
- handler: optionsHandler
127
- });
128
-
129
- // Hook to handle CORS headers for all responses
130
- addHook('transport:response', 'cors-headers', { order: -1000 }, async ({ context }) => {
131
- // Transport data is now nested in context
132
- const { request, response } = context.transport || {};
133
-
134
- if (!request) {
135
- log.error('CORS: request is undefined in transport:response hook');
136
- return;
92
+
93
+ // Set origin header
94
+ if (corsConfig.origin === '*' && !corsConfig.credentials) {
95
+ responseHeaders['Access-Control-Allow-Origin'] = '*'
96
+ } else if (origin) {
97
+ responseHeaders['Access-Control-Allow-Origin'] = origin
98
+ responseHeaders['Vary'] = 'Origin'
137
99
  }
138
-
139
- const origin = request.headers?.origin;
140
- const method = request.method?.toUpperCase();
141
100
 
142
- log.debug('CORS processing response', {
143
- origin,
144
- method,
145
- path: request.path
146
- });
101
+ // Set credentials if enabled
102
+ if (corsConfig.credentials) {
103
+ responseHeaders['Access-Control-Allow-Credentials'] = 'true'
104
+ }
147
105
 
148
- // Skip if this was an OPTIONS request (already handled by route)
149
- if (method === 'OPTIONS') {
150
- return;
106
+ return {
107
+ statusCode: corsConfig.optionsSuccessStatus,
108
+ headers: responseHeaders,
109
+ body: null
151
110
  }
111
+ } else {
112
+ // Origin not allowed
113
+ log.warn('CORS origin not allowed', { origin })
114
+ return {
115
+ statusCode: 403,
116
+ body: { error: 'CORS origin not allowed' }
117
+ }
118
+ }
119
+ }
120
+
121
+ // Register OPTIONS route for all paths
122
+ await api.addRoute({
123
+ method: 'OPTIONS',
124
+ path: vars.transport.matchAll,
125
+ handler: optionsHandler
126
+ })
127
+
128
+ // Hook to handle CORS headers for all responses
129
+ addHook('transport:response', 'cors-headers', { order: -1000 }, async ({ context }) => {
130
+ // Transport data is now nested in context
131
+ const { request, response } = context.transport || {}
132
+
133
+ if (!request) {
134
+ log.error('CORS: request is undefined in transport:response hook')
135
+ return
136
+ }
137
+
138
+ const origin = request.headers?.origin
139
+ const method = request.method?.toUpperCase()
140
+
141
+ log.debug('CORS processing response', {
142
+ origin,
143
+ method,
144
+ path: request.path
145
+ })
146
+
147
+ // Skip if this was an OPTIONS request (already handled by route)
148
+ if (method === 'OPTIONS') {
149
+ return
150
+ }
152
151
 
153
- // Check if origin is allowed
154
- if (isOriginAllowed(origin, corsConfig.origin)) {
155
- // Set origin header
156
- if (corsConfig.origin === '*' && !corsConfig.credentials) {
157
- response.headers['Access-Control-Allow-Origin'] = '*';
158
- } else if (origin) {
159
- response.headers['Access-Control-Allow-Origin'] = origin;
160
- response.headers['Vary'] = 'Origin';
161
- }
162
-
163
- // Set credentials if enabled
164
- if (corsConfig.credentials) {
165
- response.headers['Access-Control-Allow-Credentials'] = 'true';
166
- }
167
-
168
- // Set exposed headers
169
- if (corsConfig.exposedHeaders.length > 0) {
170
- response.headers['Access-Control-Expose-Headers'] = corsConfig.exposedHeaders.join(', ');
171
- }
152
+ // Check if origin is allowed
153
+ if (isOriginAllowed(origin, corsConfig.origin)) {
154
+ // Set origin header
155
+ if (corsConfig.origin === '*' && !corsConfig.credentials) {
156
+ response.headers['Access-Control-Allow-Origin'] = '*'
172
157
  } else if (origin) {
173
- // Origin not allowed - don't set any CORS headers
174
- log.warn('CORS origin not allowed for response', {
175
- origin,
176
- allowedOrigins: corsConfig.origin
177
- });
158
+ response.headers['Access-Control-Allow-Origin'] = origin
159
+ response.headers['Vary'] = 'Origin'
178
160
  }
179
- });
180
161
 
181
- log.info('CORS plugin installed', {
182
- origin: corsConfig.origin,
183
- credentials: corsConfig.credentials,
184
- methods: corsConfig.methods
185
- });
186
- }
187
- };
162
+ // Set credentials if enabled
163
+ if (corsConfig.credentials) {
164
+ response.headers['Access-Control-Allow-Credentials'] = 'true'
165
+ }
166
+
167
+ // Set exposed headers
168
+ if (corsConfig.exposedHeaders.length > 0) {
169
+ response.headers['Access-Control-Expose-Headers'] = corsConfig.exposedHeaders.join(', ')
170
+ }
171
+ } else if (origin) {
172
+ // Origin not allowed - don't set any CORS headers
173
+ log.warn('CORS origin not allowed for response', {
174
+ origin,
175
+ allowedOrigins: corsConfig.origin
176
+ })
177
+ }
178
+ })
179
+
180
+ log.info('CORS plugin installed', {
181
+ origin: corsConfig.origin,
182
+ credentials: corsConfig.credentials,
183
+ methods: corsConfig.methods
184
+ })
185
+ }
186
+ }
188
187
 
189
- /*
188
+ /*
190
189
  Usage examples:
191
190
 
192
191
  // Basic usage - allow all origins
@@ -235,4 +234,4 @@
235
234
  }
236
235
  });
237
236
 
238
- */
237
+ */