@objectql/server 3.0.1 → 4.0.1

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 (63) hide show
  1. package/CHANGELOG.md +18 -3
  2. package/dist/adapters/graphql.d.ts +7 -0
  3. package/dist/adapters/graphql.js +8 -1
  4. package/dist/adapters/graphql.js.map +1 -1
  5. package/dist/adapters/node.d.ts +7 -0
  6. package/dist/adapters/node.js +7 -0
  7. package/dist/adapters/node.js.map +1 -1
  8. package/dist/adapters/rest.d.ts +7 -0
  9. package/dist/adapters/rest.js +7 -0
  10. package/dist/adapters/rest.js.map +1 -1
  11. package/dist/file-handler.d.ts +7 -0
  12. package/dist/file-handler.js +7 -0
  13. package/dist/file-handler.js.map +1 -1
  14. package/dist/index.d.ts +7 -0
  15. package/dist/index.js +7 -0
  16. package/dist/index.js.map +1 -1
  17. package/dist/metadata.d.ts +7 -0
  18. package/dist/metadata.js +10 -3
  19. package/dist/metadata.js.map +1 -1
  20. package/dist/openapi.d.ts +7 -0
  21. package/dist/openapi.js +7 -0
  22. package/dist/openapi.js.map +1 -1
  23. package/dist/server.d.ts +7 -0
  24. package/dist/server.js +14 -2
  25. package/dist/server.js.map +1 -1
  26. package/dist/storage.d.ts +7 -0
  27. package/dist/storage.js +7 -0
  28. package/dist/storage.js.map +1 -1
  29. package/dist/templates.d.ts +7 -0
  30. package/dist/templates.js +7 -0
  31. package/dist/templates.js.map +1 -1
  32. package/dist/types.d.ts +7 -0
  33. package/dist/types.js +8 -1
  34. package/dist/types.js.map +1 -1
  35. package/dist/utils.d.ts +7 -0
  36. package/dist/utils.js +8 -1
  37. package/dist/utils.js.map +1 -1
  38. package/jest.config.js +8 -0
  39. package/package.json +3 -3
  40. package/src/adapters/graphql.ts +9 -1
  41. package/src/adapters/node.ts +8 -0
  42. package/src/adapters/rest.ts +8 -0
  43. package/src/file-handler.ts +8 -0
  44. package/src/index.ts +8 -0
  45. package/src/metadata.ts +11 -3
  46. package/src/openapi.ts +8 -0
  47. package/src/server.ts +13 -2
  48. package/src/storage.ts +8 -0
  49. package/src/templates.ts +8 -0
  50. package/src/types.ts +8 -0
  51. package/src/utils.ts +8 -0
  52. package/test/custom-routes.test.ts +8 -0
  53. package/test/file-upload-integration.example.ts +8 -0
  54. package/test/file-validation.test.ts +8 -0
  55. package/test/graphql.test.ts +10 -0
  56. package/test/integration-example.ts +8 -0
  57. package/test/metadata.test.ts +8 -0
  58. package/test/node.test.ts +10 -0
  59. package/test/openapi.test.ts +8 -0
  60. package/test/rest-advanced.test.ts +19 -4
  61. package/test/rest.test.ts +96 -20
  62. package/test/storage.test.ts +8 -0
  63. package/tsconfig.tsbuildinfo +1 -1
package/test/rest.test.ts CHANGED
@@ -1,3 +1,11 @@
1
+ /**
2
+ * ObjectQL
3
+ * Copyright (c) 2026-present ObjectStack Inc.
4
+ *
5
+ * This source code is licensed under the MIT license found in the
6
+ * LICENSE file in the root directory of this source tree.
7
+ */
8
+
1
9
  import request from 'supertest';
2
10
  import { createServer } from 'http';
3
11
  import { ObjectQL } from '@objectql/core';
@@ -19,37 +27,97 @@ class MockDriver implements Driver {
19
27
  async find(objectName: string, query: any) {
20
28
  let items = this.data[objectName] || [];
21
29
 
22
- // Apply filters if provided
30
+ // Apply filters if provided (supports FilterNode array format)
23
31
  if (query && query.filters) {
24
- const filters = query.filters;
25
- if (typeof filters === 'object') {
26
- const filterKeys = Object.keys(filters);
27
- if (filterKeys.length > 0) {
28
- items = items.filter(item => {
29
- for (const [key, value] of Object.entries(filters)) {
30
- if (item[key] !== value) {
31
- return false;
32
- }
33
- }
34
- return true;
35
- });
36
- }
37
- }
32
+ items = items.filter(item => this.matchesFilter(item, query.filters));
38
33
  }
39
34
 
40
- // Apply skip and limit if provided
35
+ // Apply skip and top/limit if provided (QueryAST uses 'top' for limit)
41
36
  if (query) {
42
37
  if (query.skip) {
43
38
  items = items.slice(query.skip);
44
39
  }
45
- if (query.limit) {
46
- items = items.slice(0, query.limit);
40
+ if (query.top || query.limit) {
41
+ items = items.slice(0, query.top || query.limit);
47
42
  }
48
43
  }
49
44
 
50
45
  return items;
51
46
  }
52
-
47
+
48
+ /**
49
+ * Matches an item against a filter condition
50
+ * @param item - The data item to test
51
+ * @param filter - The filter in FilterNode array format or simple object format
52
+ * FilterNode format: [field, op, value] for single condition
53
+ * Complex filters: [[field, op, value], 'and', [field2, op2, value2]]
54
+ * Simple format: { field: value }
55
+ * @returns true if item matches the filter
56
+ */
57
+ private matchesFilter(item: any, filter: any): boolean {
58
+ if (!filter) return true;
59
+
60
+ // Handle FilterNode array format: [[field, op, value]] or [[field, op, value], 'and', [field2, op2, value2]]
61
+ if (Array.isArray(filter)) {
62
+ // Single condition: [field, op, value]
63
+ if (filter.length === 3 && typeof filter[0] === 'string') {
64
+ const [field, op, value] = filter;
65
+ return this.evaluateCondition(item, field, op, value);
66
+ }
67
+
68
+ // Multiple conditions with logical operators
69
+ let result = true;
70
+ let currentOp = 'and';
71
+ for (let i = 0; i < filter.length; i++) {
72
+ const element = filter[i];
73
+ if (typeof element === 'string') {
74
+ currentOp = element; // 'and' or 'or'
75
+ } else if (Array.isArray(element)) {
76
+ const conditionResult = this.matchesFilter(item, element);
77
+ if (currentOp === 'and') {
78
+ result = result && conditionResult;
79
+ } else if (currentOp === 'or') {
80
+ result = result || conditionResult;
81
+ }
82
+ }
83
+ }
84
+ return result;
85
+ }
86
+
87
+ // Handle simple object format: { field: value }
88
+ if (typeof filter === 'object') {
89
+ for (const [key, value] of Object.entries(filter)) {
90
+ if (item[key] !== value) {
91
+ return false;
92
+ }
93
+ }
94
+ return true;
95
+ }
96
+
97
+ return true;
98
+ }
99
+
100
+ /**
101
+ * Evaluates a single filter condition
102
+ * @param item - The data item to test
103
+ * @param field - The field name
104
+ * @param op - The operator: '=', '!=', '>', '>=', '<', '<=', 'in'
105
+ * @param value - The value to compare against
106
+ * @returns true if the condition is satisfied
107
+ */
108
+ private evaluateCondition(item: any, field: string, op: string, value: any): boolean {
109
+ const fieldValue = item[field];
110
+ switch (op) {
111
+ case '=': return fieldValue === value;
112
+ case '!=': return fieldValue !== value;
113
+ case '>': return fieldValue > value;
114
+ case '>=': return fieldValue >= value;
115
+ case '<': return fieldValue < value;
116
+ case '<=': return fieldValue <= value;
117
+ case 'in': return Array.isArray(value) ? value.includes(fieldValue) : false;
118
+ default: return true;
119
+ }
120
+ }
53
121
  async findOne(objectName: string, id: string | number, query?: any, options?: any) {
54
122
  const items = this.data[objectName] || [];
55
123
  if (id !== undefined && id !== null) {
@@ -89,7 +157,13 @@ class MockDriver implements Driver {
89
157
  }
90
158
 
91
159
  async count(objectName: string, query: any) {
92
- return (this.data[objectName] || []).length;
160
+ // Count should apply filters but not skip/limit
161
+ const countQuery = { ...query };
162
+ delete countQuery.skip;
163
+ delete countQuery.top;
164
+ delete countQuery.limit;
165
+ const items = await this.find(objectName, countQuery);
166
+ return items.length;
93
167
  }
94
168
 
95
169
  async createMany(objectName: string, data: any[]) {
@@ -193,6 +267,8 @@ describe('REST API Adapter', () => {
193
267
  }
194
268
  }
195
269
  });
270
+
271
+ await app.init();
196
272
 
197
273
  // Create handler and server once for all tests
198
274
  handler = createRESTHandler(app);
@@ -1,3 +1,11 @@
1
+ /**
2
+ * ObjectQL
3
+ * Copyright (c) 2026-present ObjectStack Inc.
4
+ *
5
+ * This source code is licensed under the MIT license found in the
6
+ * LICENSE file in the root directory of this source tree.
7
+ */
8
+
1
9
  import { MemoryFileStorage } from '../src/storage';
2
10
  import { AttachmentData } from '../src/types';
3
11