@onchaindb/sdk 0.4.5 → 2.0.0

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 (117) hide show
  1. package/.claude/settings.local.json +10 -2
  2. package/README.md +422 -355
  3. package/dist/batch.d.ts +1 -10
  4. package/dist/batch.d.ts.map +1 -1
  5. package/dist/batch.js +4 -26
  6. package/dist/batch.js.map +1 -1
  7. package/dist/client.d.ts +31 -46
  8. package/dist/client.d.ts.map +1 -1
  9. package/dist/client.js +222 -357
  10. package/dist/client.js.map +1 -1
  11. package/dist/database.d.ts +14 -131
  12. package/dist/database.d.ts.map +1 -1
  13. package/dist/database.js +35 -131
  14. package/dist/database.js.map +1 -1
  15. package/dist/index.d.ts +10 -13
  16. package/dist/index.d.ts.map +1 -1
  17. package/dist/index.js +4 -18
  18. package/dist/index.js.map +1 -1
  19. package/dist/query-sdk/ConditionBuilder.d.ts +3 -11
  20. package/dist/query-sdk/ConditionBuilder.d.ts.map +1 -1
  21. package/dist/query-sdk/ConditionBuilder.js +10 -48
  22. package/dist/query-sdk/ConditionBuilder.js.map +1 -1
  23. package/dist/query-sdk/NestedBuilders.d.ts +33 -30
  24. package/dist/query-sdk/NestedBuilders.d.ts.map +1 -1
  25. package/dist/query-sdk/NestedBuilders.js +46 -43
  26. package/dist/query-sdk/NestedBuilders.js.map +1 -1
  27. package/{src/query-sdk/dist/OnChainDB.d.ts → dist/query-sdk/OnDB.d.ts} +10 -2
  28. package/dist/query-sdk/OnDB.d.ts.map +1 -0
  29. package/{src/query-sdk/dist/OnChainDB.js → dist/query-sdk/OnDB.js} +86 -18
  30. package/dist/query-sdk/OnDB.js.map +1 -0
  31. package/dist/query-sdk/QueryBuilder.d.ts +4 -2
  32. package/dist/query-sdk/QueryBuilder.d.ts.map +1 -1
  33. package/dist/query-sdk/QueryBuilder.js +47 -169
  34. package/dist/query-sdk/QueryBuilder.js.map +1 -1
  35. package/dist/query-sdk/QueryResult.d.ts +0 -38
  36. package/dist/query-sdk/QueryResult.d.ts.map +1 -1
  37. package/dist/query-sdk/QueryResult.js +1 -227
  38. package/dist/query-sdk/QueryResult.js.map +1 -1
  39. package/dist/query-sdk/index.d.ts +2 -2
  40. package/dist/query-sdk/index.d.ts.map +1 -1
  41. package/dist/query-sdk/index.js +3 -3
  42. package/dist/query-sdk/index.js.map +1 -1
  43. package/dist/query-sdk/operators.d.ts +32 -28
  44. package/dist/query-sdk/operators.d.ts.map +1 -1
  45. package/dist/query-sdk/operators.js +45 -155
  46. package/dist/query-sdk/operators.js.map +1 -1
  47. package/dist/types.d.ts +159 -36
  48. package/dist/types.d.ts.map +1 -1
  49. package/dist/types.js +8 -8
  50. package/dist/types.js.map +1 -1
  51. package/dist/x402/types.d.ts +1 -1
  52. package/dist/x402/types.d.ts.map +1 -1
  53. package/dist/x402/utils.js +2 -2
  54. package/dist/x402/utils.js.map +1 -1
  55. package/jest.config.js +4 -0
  56. package/package.json +1 -1
  57. package/skills.md +0 -1
  58. package/src/batch.d.ts +3 -3
  59. package/src/batch.js +1 -1
  60. package/src/client.ts +287 -823
  61. package/src/database.d.ts +1 -1
  62. package/src/database.js +4 -4
  63. package/src/database.ts +71 -494
  64. package/src/index.d.ts +18 -18
  65. package/src/index.js +16 -16
  66. package/src/index.ts +44 -198
  67. package/src/query-sdk/ConditionBuilder.ts +37 -89
  68. package/src/query-sdk/NestedBuilders.ts +90 -92
  69. package/src/query-sdk/{OnChainDB.ts → OnDB.ts} +1 -1
  70. package/src/query-sdk/QueryBuilder.ts +59 -218
  71. package/src/query-sdk/QueryResult.ts +4 -330
  72. package/src/query-sdk/README.md +218 -587
  73. package/src/query-sdk/index.ts +2 -2
  74. package/src/query-sdk/operators.ts +91 -200
  75. package/src/query-sdk/tests/FieldConditionBuilder.test.ts +70 -71
  76. package/src/query-sdk/tests/LogicalOperator.test.ts +43 -82
  77. package/src/query-sdk/tests/NestedBuilders.test.ts +229 -309
  78. package/src/query-sdk/tests/QueryBuilder.test.ts +5 -5
  79. package/src/query-sdk/tests/QueryResult.test.ts +41 -435
  80. package/src/query-sdk/tests/comprehensive.test.ts +4 -185
  81. package/src/tests/client-requests.test.ts +280 -0
  82. package/src/tests/client-validation.test.ts +80 -0
  83. package/src/types.d.ts +6 -6
  84. package/src/types.js +8 -8
  85. package/src/types.ts +239 -54
  86. package/src/x402/types.ts +3 -3
  87. package/src/x402/utils.ts +3 -3
  88. package/examples/blob-upload-example.ts +0 -140
  89. package/src/batch.ts +0 -257
  90. package/src/query-sdk/dist/ConditionBuilder.d.ts +0 -22
  91. package/src/query-sdk/dist/ConditionBuilder.js +0 -90
  92. package/src/query-sdk/dist/FieldConditionBuilder.d.ts +0 -1
  93. package/src/query-sdk/dist/FieldConditionBuilder.js +0 -6
  94. package/src/query-sdk/dist/NestedBuilders.d.ts +0 -43
  95. package/src/query-sdk/dist/NestedBuilders.js +0 -144
  96. package/src/query-sdk/dist/QueryBuilder.d.ts +0 -70
  97. package/src/query-sdk/dist/QueryBuilder.js +0 -295
  98. package/src/query-sdk/dist/QueryResult.d.ts +0 -52
  99. package/src/query-sdk/dist/QueryResult.js +0 -293
  100. package/src/query-sdk/dist/SelectionBuilder.d.ts +0 -20
  101. package/src/query-sdk/dist/SelectionBuilder.js +0 -80
  102. package/src/query-sdk/dist/adapters/HttpClientAdapter.d.ts +0 -27
  103. package/src/query-sdk/dist/adapters/HttpClientAdapter.js +0 -170
  104. package/src/query-sdk/dist/index.d.ts +0 -36
  105. package/src/query-sdk/dist/index.js +0 -27
  106. package/src/query-sdk/dist/operators.d.ts +0 -56
  107. package/src/query-sdk/dist/operators.js +0 -289
  108. package/src/query-sdk/dist/tests/setup.d.ts +0 -15
  109. package/src/query-sdk/dist/tests/setup.js +0 -46
  110. package/src/query-sdk/jest.config.js +0 -25
  111. package/src/query-sdk/package.json +0 -46
  112. package/src/query-sdk/tests/aggregations.test.ts +0 -653
  113. package/src/query-sdk/tests/integration.test.ts +0 -608
  114. package/src/query-sdk/tests/operators.test.ts +0 -327
  115. package/src/query-sdk/tests/unit.test.ts +0 -794
  116. package/src/query-sdk/tsconfig.json +0 -26
  117. package/src/query-sdk/yarn.lock +0 -3092
@@ -2,11 +2,9 @@ import { createQueryResult, QueryResult } from '../QueryResult';
2
2
 
3
3
  describe('QueryResult', () => {
4
4
  const testData = [
5
- { id: 1, name: 'Alice', age: 30, department: 'Engineering', salary: 75000, active: true },
6
- { id: 2, name: 'Bob', age: 25, department: 'Design', salary: 65000, active: true },
7
- { id: 3, name: 'Carol', age: 35, department: 'Engineering', salary: 85000, active: false },
8
- { id: 4, name: 'David', age: 28, department: 'Marketing', salary: 55000, active: true },
9
- { id: 5, name: 'Eve', age: 32, department: 'Engineering', salary: 95000, active: true }
5
+ { id: 1, name: 'Alice', age: 30 },
6
+ { id: 2, name: 'Bob', age: 25 },
7
+ { id: 3, name: 'Carol', age: 35 },
10
8
  ];
11
9
 
12
10
  let result: QueryResult<any>;
@@ -15,450 +13,58 @@ describe('QueryResult', () => {
15
13
  result = createQueryResult(testData);
16
14
  });
17
15
 
18
- describe('Basic operations', () => {
19
- test('should return correct length', () => {
20
- expect(result.len()).toBe(5);
21
- expect(result.length()).toBe(5);
22
- });
23
-
24
- test('should identify empty results', () => {
25
- const emptyResult = createQueryResult([]);
26
- expect(emptyResult.isEmpty()).toBe(true);
27
- expect(result.isEmpty()).toBe(false);
28
- });
29
-
30
- test('should return first and last items', () => {
31
- expect(result.first()?.name).toBe('Alice');
32
- expect(result.last()?.name).toBe('Eve');
33
- });
34
-
35
- test('should get item by index', () => {
36
- expect(result.get(0)?.name).toBe('Alice');
37
- expect(result.get(2)?.name).toBe('Carol');
38
- expect(result.get(10)).toBeUndefined();
39
- });
40
-
41
- test('should handle single item input', () => {
42
- const singleResult = createQueryResult({ id: 1, name: 'Test' } as any);
43
- expect(singleResult.len()).toBe(1);
44
- expect(singleResult.first()?.name).toBe('Test');
45
- });
46
- });
47
-
48
- describe('Iteration methods', () => {
49
- test('any should work correctly', () => {
50
- expect(result.any(item => item.age > 30)).toBe(true);
51
- expect(result.any(item => item.age > 100)).toBe(false);
52
- });
53
-
54
- test('all should work correctly', () => {
55
- expect(result.all(item => item.id > 0)).toBe(true);
56
- expect(result.all(item => item.active)).toBe(false);
57
- });
58
-
59
- test('find should return correct item', () => {
60
- const found = result.find(item => item.name === 'Carol');
61
- expect(found?.id).toBe(3);
62
- expect(result.find(item => item.name === 'Unknown')).toBeUndefined();
63
- });
64
-
65
- test('filter should return correct items', () => {
66
- const activeUsers = result.filter(item => item.active);
67
- expect(activeUsers).toHaveLength(4);
68
-
69
- const engineers = result.filter(item => item.department === 'Engineering');
70
- expect(engineers).toHaveLength(3);
71
- });
72
-
73
- test('map should transform items correctly', () => {
74
- const names = result.map(item => item.name);
75
- expect(names).toEqual(['Alice', 'Bob', 'Carol', 'David', 'Eve']);
76
-
77
- const ages = result.map(item => item.age);
78
- expect(ages).toEqual([30, 25, 35, 28, 32]);
79
- });
80
-
81
- test('forEach should iterate over all items', () => {
82
- const mockCallback = jest.fn();
83
- result.forEach(mockCallback);
84
- expect(mockCallback).toHaveBeenCalledTimes(5);
85
- });
86
-
87
- test('reduce should accumulate correctly', () => {
88
- const totalAge = result.reduce((sum, item) => sum + item.age, 0);
89
- expect(totalAge).toBe(150);
90
-
91
- const nameList = result.reduce((names, item) => names + item.name + ',', '');
92
- expect(nameList).toBe('Alice,Bob,Carol,David,Eve,');
93
- });
94
- });
95
-
96
- describe('Data extraction', () => {
97
- test('pluck should extract field values', () => {
98
- const names = result.pluck('name');
99
- expect(names).toEqual(['Alice', 'Bob', 'Carol', 'David', 'Eve']);
100
-
101
- const ages = result.pluck('age');
102
- expect(ages).toEqual([30, 25, 35, 28, 32]);
103
- });
104
-
105
- test('pluckStrings should extract and convert to strings', () => {
106
- const ageStrings = result.pluckStrings('age');
107
- expect(ageStrings).toEqual(['30', '25', '35', '28', '32']);
108
-
109
- const names = result.pluckStrings('name');
110
- expect(names).toEqual(['Alice', 'Bob', 'Carol', 'David', 'Eve']);
111
- });
112
-
113
- test('pluckNumbers should extract and convert to numbers', () => {
114
- const ages = result.pluckNumbers('age');
115
- expect(ages).toEqual([30, 25, 35, 28, 32]);
116
-
117
- const salaries = result.pluckNumbers('salary');
118
- expect(salaries).toEqual([75000, 65000, 85000, 55000, 95000]);
119
- });
120
-
121
- test('pluckNumbers should handle non-numeric values', () => {
122
- const testDataWithNulls = [
123
- { id: 1, score: 85 },
124
- { id: 2, score: null },
125
- { id: 3, score: '90' },
126
- { id: 4, score: 'invalid' }
127
- ];
128
-
129
- const resultWithNulls = createQueryResult(testDataWithNulls);
130
- const scores = resultWithNulls.pluckNumbers('score');
131
- expect(scores).toEqual([85, null, 90, null]);
132
- });
133
- });
134
-
135
- describe('Numeric summary', () => {
136
- test('should calculate numeric summary correctly', () => {
137
- const ageSummary = result.summarizeNumeric('age');
138
-
139
- expect(ageSummary).not.toBeNull();
140
- expect(ageSummary!.count).toBe(5);
141
- expect(ageSummary!.sum).toBe(150);
142
- expect(ageSummary!.mean).toBe(30);
143
- expect(ageSummary!.min).toBe(25);
144
- expect(ageSummary!.max).toBe(35);
145
- });
146
-
147
- test('should calculate median correctly', () => {
148
- const ageSummary = result.summarizeNumeric('age');
149
- expect(ageSummary!.median).toBe(30); // [25, 28, 30, 32, 35]
150
- });
151
-
152
- test('should calculate standard deviation correctly', () => {
153
- const ageSummary = result.summarizeNumeric('age');
154
- expect(ageSummary!.standardDeviation).toBeCloseTo(3.74, 2);
155
- });
156
-
157
- test('should return null for non-numeric fields', () => {
158
- const nameSummary = result.summarizeNumeric('name');
159
- expect(nameSummary).toBeNull();
160
- });
161
-
162
- test('should handle empty data', () => {
163
- const emptyResult = createQueryResult([]);
164
- const summary = emptyResult.summarizeNumeric('age');
165
- expect(summary).toBeNull();
166
- });
16
+ test('len() and length() return correct count', () => {
17
+ expect(result.len()).toBe(3);
18
+ expect(result.length()).toBe(3);
167
19
  });
168
20
 
169
- describe('Grouping and aggregation', () => {
170
- test('groupBy should group items correctly', () => {
171
- const byDepartment = result.groupBy('department');
172
-
173
- expect(byDepartment['Engineering']).toHaveLength(3);
174
- expect(byDepartment['Design']).toHaveLength(1);
175
- expect(byDepartment['Marketing']).toHaveLength(1);
176
- });
177
-
178
- test('countBy should count values correctly', () => {
179
- const departmentCounts = result.countBy('department');
180
-
181
- expect(departmentCounts['Engineering']).toBe(3);
182
- expect(departmentCounts['Design']).toBe(1);
183
- expect(departmentCounts['Marketing']).toBe(1);
184
- });
185
-
186
- test('count with predicate should work correctly', () => {
187
- const activeCount = result.count(item => item.active);
188
- expect(activeCount).toBe(4);
189
-
190
- const engineerCount = result.count(item => item.department === 'Engineering');
191
- expect(engineerCount).toBe(3);
192
- });
193
-
194
- test('count without predicate should return total count', () => {
195
- expect(result.count()).toBe(5);
196
- });
21
+ test('isEmpty() works correctly', () => {
22
+ expect(createQueryResult([]).isEmpty()).toBe(true);
23
+ expect(result.isEmpty()).toBe(false);
197
24
  });
198
25
 
199
- describe('Sorting', () => {
200
- test('sortBy should sort correctly', () => {
201
- const sortedByAge = result.sortBy('age');
202
- const ages = sortedByAge.map(item => item.age);
203
- expect(ages).toEqual([25, 28, 30, 32, 35]);
204
-
205
- const sortedByName = result.sortBy('name');
206
- const names = sortedByName.map(item => item.name);
207
- expect(names).toEqual(['Alice', 'Bob', 'Carol', 'David', 'Eve']);
208
- });
209
-
210
- test('sortBy should handle descending order', () => {
211
- const sortedByAgeDesc = result.sortBy('age', false);
212
- const ages = sortedByAgeDesc.map(item => item.age);
213
- expect(ages).toEqual([35, 32, 30, 28, 25]);
214
- });
215
-
216
- test('sortByMultiple should handle multiple sort fields', () => {
217
- const sorted = result.sortByMultiple([
218
- { field: 'department', ascending: true },
219
- { field: 'salary', ascending: false }
220
- ]);
221
-
222
- // Should sort by department first, then by salary descending
223
- expect(sorted[0].department).toBe('Design');
224
- expect(sorted[1].department).toBe('Engineering');
225
- expect(sorted[1].salary).toBe(95000); // Highest paid engineer first
226
- });
227
-
228
- test('sortBy should handle null values', () => {
229
- const dataWithNulls = [
230
- { id: 1, score: 10 },
231
- { id: 2, score: null },
232
- { id: 3, score: 5 }
233
- ];
234
-
235
- const resultWithNulls = createQueryResult(dataWithNulls);
236
- const sorted = resultWithNulls.sortBy('score');
237
-
238
- // Null values should sort first in ascending order
239
- expect(sorted[0].score).toBeNull();
240
- expect(sorted[1].score).toBe(5);
241
- expect(sorted[2].score).toBe(10);
242
- });
26
+ test('first() and last() return correct items', () => {
27
+ expect(result.first()?.name).toBe('Alice');
28
+ expect(result.last()?.name).toBe('Carol');
243
29
  });
244
30
 
245
- describe('Pagination and chunking', () => {
246
- test('paginate should return correct page', () => {
247
- const page1 = result.paginate(1, 2);
248
- expect(page1).toHaveLength(2);
249
- expect(page1[0].name).toBe('Alice');
250
- expect(page1[1].name).toBe('Bob');
251
-
252
- const page2 = result.paginate(2, 2);
253
- expect(page2).toHaveLength(2);
254
- expect(page2[0].name).toBe('Carol');
255
- expect(page2[1].name).toBe('David');
256
-
257
- const page3 = result.paginate(3, 2);
258
- expect(page3).toHaveLength(1);
259
- expect(page3[0].name).toBe('Eve');
260
- });
261
-
262
- test('chunk should split data into chunks', () => {
263
- const chunks = result.chunk(2);
264
- expect(chunks).toHaveLength(3);
265
- expect(chunks[0]).toHaveLength(2);
266
- expect(chunks[1]).toHaveLength(2);
267
- expect(chunks[2]).toHaveLength(1);
268
-
269
- expect(chunks[0][0].name).toBe('Alice');
270
- expect(chunks[2][0].name).toBe('Eve');
271
- });
31
+ test('first() and last() return undefined on empty result', () => {
32
+ const empty = createQueryResult([]);
33
+ expect(empty.first()).toBeUndefined();
34
+ expect(empty.last()).toBeUndefined();
272
35
  });
273
36
 
274
- describe('Distinct and unique', () => {
275
- test('distinctBy should return unique values', () => {
276
- const uniqueDepartments = result.distinctBy('department');
277
- const departments = uniqueDepartments.map(item => item.department);
278
- expect(departments).toEqual(['Engineering', 'Design', 'Marketing']);
279
- });
280
-
281
- test('unique should remove duplicates', () => {
282
- const duplicateData = [1, 2, 2, 3, 3, 3, 4];
283
- const duplicateResult = createQueryResult(duplicateData);
284
- const unique = duplicateResult.unique();
285
- expect(unique).toEqual([1, 2, 3, 4]);
286
- });
37
+ test('get(index) returns correct item or undefined', () => {
38
+ expect(result.get(0)?.name).toBe('Alice');
39
+ expect(result.get(2)?.name).toBe('Carol');
40
+ expect(result.get(99)).toBeUndefined();
287
41
  });
288
42
 
289
- describe('Joining', () => {
290
- const departments = createQueryResult([
291
- { id: 'Engineering', budget: 100000, manager: 'John' },
292
- { id: 'Design', budget: 75000, manager: 'Jane' },
293
- { id: 'Marketing', budget: 50000, manager: 'Mike' }
294
- ]);
295
-
296
- test('innerJoin should join matching records', () => {
297
- const joined = result.innerJoin(departments, 'department', 'id');
298
-
299
- expect(joined).toHaveLength(5);
300
- expect(joined[0].budget).toBe(100000); // Alice from Engineering
301
- expect(joined[1].budget).toBe(75000); // Bob from Design
302
- });
303
-
304
- test('leftJoin should include all left records', () => {
305
- const leftJoined = result.leftJoin(departments, 'department', 'id');
306
-
307
- expect(leftJoined).toHaveLength(5);
308
- // All original records should be present
309
- expect(leftJoined.map(r => r.name)).toEqual(['Alice', 'Bob', 'Carol', 'David', 'Eve']);
310
- });
43
+ test('toArray() returns a copy of the data', () => {
44
+ const arr = result.toArray();
45
+ expect(Array.isArray(arr)).toBe(true);
46
+ expect(arr).toHaveLength(3);
47
+ expect(arr[0]).toEqual(testData[0]);
48
+ // Ensure it's a copy, not the same reference
49
+ arr.push({ id: 99, name: 'Extra', age: 0 });
50
+ expect(result.len()).toBe(3);
311
51
  });
312
52
 
313
- describe('Export functions', () => {
314
- test('toArray should return array copy', () => {
315
- const array = result.toArray();
316
- expect(Array.isArray(array)).toBe(true);
317
- expect(array).toHaveLength(5);
318
- expect(array[0]).toEqual(testData[0]);
319
- });
320
-
321
- test('toJSON should return JSON string', () => {
322
- const json = result.toJSON();
323
- const parsed = JSON.parse(json);
324
- expect(parsed).toEqual(testData);
325
- });
326
-
327
- test('toCsv should generate CSV format', () => {
328
- const csv = result.toCsv();
329
- const lines = csv.split('\n');
330
-
331
- expect(lines.length).toBe(6); // Header + 5 data rows
332
- expect(lines[0]).toContain('id,name,age,department,salary,active');
333
- expect(lines[1]).toContain('1,Alice,30,Engineering,75000,true');
334
- });
335
-
336
- test('toCsv should handle custom delimiter', () => {
337
- const tsv = result.toCsv('\t');
338
- expect(tsv).toContain('\t');
339
- expect(tsv.split('\n')[0]).toContain('id\tname\tage');
340
- });
341
-
342
- test('toCsv should escape values with delimiters', () => {
343
- const dataWithCommas = [
344
- { name: 'Smith, John', description: 'A person, who works' }
345
- ];
346
- const resultWithCommas = createQueryResult(dataWithCommas);
347
- const csv = resultWithCommas.toCsv();
348
-
349
- expect(csv).toContain('"Smith, John"');
350
- expect(csv).toContain('"A person, who works"');
351
- });
53
+ test('toJSON() returns valid JSON string of the data', () => {
54
+ const json = result.toJSON();
55
+ expect(JSON.parse(json)).toEqual(testData);
352
56
  });
353
57
 
354
- describe('Nested field access', () => {
355
- const nestedData = [
356
- {
357
- id: 1,
358
- user: {
359
- profile: {
360
- name: 'Alice',
361
- age: 30
362
- },
363
- settings: {
364
- theme: 'dark'
365
- }
366
- }
367
- },
368
- {
369
- id: 2,
370
- user: {
371
- profile: {
372
- name: 'Bob',
373
- age: 25
374
- },
375
- settings: {
376
- theme: 'light'
377
- }
378
- }
379
- }
380
- ];
381
-
382
- test('should handle nested field plucking', () => {
383
- const nestedResult = createQueryResult(nestedData);
384
-
385
- const names = nestedResult.pluck('user.profile.name');
386
- expect(names).toEqual(['Alice', 'Bob']);
387
-
388
- const ages = nestedResult.pluck('user.profile.age');
389
- expect(ages).toEqual([30, 25]);
390
- });
391
-
392
- test('should handle nested field sorting', () => {
393
- const nestedResult = createQueryResult(nestedData);
394
- const sorted = nestedResult.sortBy('user.profile.age');
395
-
396
- expect(sorted[0].user.profile.name).toBe('Bob');
397
- expect(sorted[1].user.profile.name).toBe('Alice');
398
- });
399
-
400
- test('should handle nested field grouping', () => {
401
- const nestedResult = createQueryResult(nestedData);
402
- const grouped = nestedResult.groupBy('user.settings.theme');
403
-
404
- expect(grouped['dark']).toHaveLength(1);
405
- expect(grouped['light']).toHaveLength(1);
406
- });
407
-
408
- test('should handle missing nested fields gracefully', () => {
409
- const dataWithMissing = [
410
- { id: 1, user: { profile: { name: 'Alice' } } },
411
- { id: 2, user: { name: 'Bob' } }, // Missing profile
412
- { id: 3 } // Missing user entirely
413
- ];
414
-
415
- const resultWithMissing = createQueryResult(dataWithMissing);
416
- const names = resultWithMissing.pluck('user.profile.name');
417
-
418
- expect(names).toEqual(['Alice', undefined, undefined]);
419
- });
58
+ test('cast() returns a new QueryResult with the same data', () => {
59
+ const casted = result.cast<{ id: number; name: string }>();
60
+ expect(casted).toBeInstanceOf(QueryResult);
61
+ expect(casted.len()).toBe(3);
62
+ expect(casted.first()?.name).toBe('Alice');
420
63
  });
421
64
 
422
- describe('Type casting and transformation', () => {
423
- test('cast should change type', () => {
424
- const casted = result.cast<{ id: number; name: string }>();
425
- expect(casted).toBeInstanceOf(QueryResult);
426
- expect(casted.len()).toBe(5);
427
- });
428
- });
429
-
430
- describe('Debugging and inspection', () => {
431
- test('inspect should not throw error', () => {
432
- // Mock console.log to prevent output during tests
433
- const consoleSpy = jest.spyOn(console, 'log').mockImplementation();
434
-
435
- result.inspect();
436
- expect(consoleSpy).toHaveBeenCalled();
437
-
438
- result.inspect(2); // Limited output
439
- expect(consoleSpy).toHaveBeenCalledTimes(2);
440
-
441
- consoleSpy.mockRestore();
442
- });
443
- });
444
-
445
- describe('Edge cases', () => {
446
- test('should handle empty arrays gracefully', () => {
447
- const emptyResult = createQueryResult([]);
448
-
449
- expect(emptyResult.len()).toBe(0);
450
- expect(emptyResult.isEmpty()).toBe(true);
451
- expect(emptyResult.first()).toBeUndefined();
452
- expect(emptyResult.summarizeNumeric('age')).toBeNull();
453
- expect(emptyResult.groupBy('department')).toEqual({});
454
- });
455
-
456
- test('should handle invalid field names', () => {
457
- const invalidSummary = result.summarizeNumeric('nonexistent');
458
- expect(invalidSummary).toBeNull();
459
-
460
- const invalidPluck = result.pluck('nonexistent');
461
- expect(invalidPluck.every(val => val === undefined)).toBe(true);
462
- });
65
+ test('constructor wraps a single non-array item in an array', () => {
66
+ const single = createQueryResult({ id: 1, name: 'Test' } as any);
67
+ expect(single.len()).toBe(1);
68
+ expect((single.first() as any)?.name).toBe('Test');
463
69
  });
464
- });
70
+ });