@robosystems/client 0.2.48 → 0.3.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 (190) hide show
  1. package/extensions/InvestorClient.d.ts +91 -0
  2. package/extensions/InvestorClient.js +223 -0
  3. package/extensions/InvestorClient.ts +501 -0
  4. package/extensions/LedgerClient.d.ts +213 -165
  5. package/extensions/LedgerClient.js +315 -393
  6. package/extensions/LedgerClient.ts +887 -564
  7. package/extensions/ReportClient.d.ts +77 -115
  8. package/extensions/ReportClient.js +124 -211
  9. package/extensions/ReportClient.ts +298 -358
  10. package/extensions/config.js +1 -1
  11. package/extensions/config.ts +1 -1
  12. package/extensions/graphql/client.d.ts +46 -0
  13. package/extensions/graphql/client.js +75 -0
  14. package/extensions/graphql/client.ts +82 -0
  15. package/extensions/graphql/generated/graphql.d.ts +1786 -0
  16. package/extensions/graphql/generated/graphql.js +2671 -0
  17. package/extensions/graphql/generated/graphql.ts +4491 -0
  18. package/extensions/graphql/queries/investor/holdings.d.ts +8 -0
  19. package/extensions/graphql/queries/investor/holdings.js +36 -0
  20. package/extensions/graphql/queries/investor/holdings.ts +34 -0
  21. package/extensions/graphql/queries/investor/portfolio.d.ts +4 -0
  22. package/extensions/graphql/queries/investor/portfolio.js +21 -0
  23. package/extensions/graphql/queries/investor/portfolio.ts +19 -0
  24. package/extensions/graphql/queries/investor/portfolios.d.ts +4 -0
  25. package/extensions/graphql/queries/investor/portfolios.js +29 -0
  26. package/extensions/graphql/queries/investor/portfolios.ts +27 -0
  27. package/extensions/graphql/queries/investor/position.d.ts +4 -0
  28. package/extensions/graphql/queries/investor/position.js +33 -0
  29. package/extensions/graphql/queries/investor/position.ts +31 -0
  30. package/extensions/graphql/queries/investor/positions.d.ts +8 -0
  31. package/extensions/graphql/queries/investor/positions.js +57 -0
  32. package/extensions/graphql/queries/investor/positions.ts +55 -0
  33. package/extensions/graphql/queries/investor/securities.d.ts +8 -0
  34. package/extensions/graphql/queries/investor/securities.js +50 -0
  35. package/extensions/graphql/queries/investor/securities.ts +48 -0
  36. package/extensions/graphql/queries/investor/security.d.ts +4 -0
  37. package/extensions/graphql/queries/investor/security.js +26 -0
  38. package/extensions/graphql/queries/investor/security.ts +24 -0
  39. package/extensions/graphql/queries/ledger/accountRollups.d.ts +7 -0
  40. package/extensions/graphql/queries/ledger/accountRollups.js +36 -0
  41. package/extensions/graphql/queries/ledger/accountRollups.ts +34 -0
  42. package/extensions/graphql/queries/ledger/accountTree.d.ts +11 -0
  43. package/extensions/graphql/queries/ledger/accountTree.js +61 -0
  44. package/extensions/graphql/queries/ledger/accountTree.ts +59 -0
  45. package/extensions/graphql/queries/ledger/accounts.d.ts +8 -0
  46. package/extensions/graphql/queries/ledger/accounts.js +45 -0
  47. package/extensions/graphql/queries/ledger/accounts.ts +43 -0
  48. package/extensions/graphql/queries/ledger/closingBookStructures.d.ts +6 -0
  49. package/extensions/graphql/queries/ledger/closingBookStructures.js +27 -0
  50. package/extensions/graphql/queries/ledger/closingBookStructures.ts +25 -0
  51. package/extensions/graphql/queries/ledger/elements.d.ts +6 -0
  52. package/extensions/graphql/queries/ledger/elements.js +56 -0
  53. package/extensions/graphql/queries/ledger/elements.ts +54 -0
  54. package/extensions/graphql/queries/ledger/entities.d.ts +7 -0
  55. package/extensions/graphql/queries/ledger/entities.js +31 -0
  56. package/extensions/graphql/queries/ledger/entities.ts +29 -0
  57. package/extensions/graphql/queries/ledger/entity.d.ts +14 -0
  58. package/extensions/graphql/queries/ledger/entity.js +55 -0
  59. package/extensions/graphql/queries/ledger/entity.ts +54 -0
  60. package/extensions/graphql/queries/ledger/fiscalCalendar.d.ts +8 -0
  61. package/extensions/graphql/queries/ledger/fiscalCalendar.js +35 -0
  62. package/extensions/graphql/queries/ledger/fiscalCalendar.ts +33 -0
  63. package/extensions/graphql/queries/ledger/mappedTrialBalance.d.ts +9 -0
  64. package/extensions/graphql/queries/ledger/mappedTrialBalance.js +29 -0
  65. package/extensions/graphql/queries/ledger/mappedTrialBalance.ts +27 -0
  66. package/extensions/graphql/queries/ledger/mapping.d.ts +5 -0
  67. package/extensions/graphql/queries/ledger/mapping.js +35 -0
  68. package/extensions/graphql/queries/ledger/mapping.ts +33 -0
  69. package/extensions/graphql/queries/ledger/mappingCoverage.d.ts +5 -0
  70. package/extensions/graphql/queries/ledger/mappingCoverage.js +22 -0
  71. package/extensions/graphql/queries/ledger/mappingCoverage.ts +20 -0
  72. package/extensions/graphql/queries/ledger/mappings.d.ts +6 -0
  73. package/extensions/graphql/queries/ledger/mappings.js +23 -0
  74. package/extensions/graphql/queries/ledger/mappings.ts +21 -0
  75. package/extensions/graphql/queries/ledger/periodCloseStatus.d.ts +6 -0
  76. package/extensions/graphql/queries/ledger/periodCloseStatus.js +29 -0
  77. package/extensions/graphql/queries/ledger/periodCloseStatus.ts +27 -0
  78. package/extensions/graphql/queries/ledger/periodDrafts.d.ts +8 -0
  79. package/extensions/graphql/queries/ledger/periodDrafts.js +45 -0
  80. package/extensions/graphql/queries/ledger/periodDrafts.ts +43 -0
  81. package/extensions/graphql/queries/ledger/publishList.d.ts +5 -0
  82. package/extensions/graphql/queries/ledger/publishList.js +29 -0
  83. package/extensions/graphql/queries/ledger/publishList.ts +27 -0
  84. package/extensions/graphql/queries/ledger/publishLists.d.ts +5 -0
  85. package/extensions/graphql/queries/ledger/publishLists.js +29 -0
  86. package/extensions/graphql/queries/ledger/publishLists.ts +27 -0
  87. package/extensions/graphql/queries/ledger/report.d.ts +5 -0
  88. package/extensions/graphql/queries/ledger/report.js +40 -0
  89. package/extensions/graphql/queries/ledger/report.ts +38 -0
  90. package/extensions/graphql/queries/ledger/reportingTaxonomy.d.ts +6 -0
  91. package/extensions/graphql/queries/ledger/reportingTaxonomy.js +27 -0
  92. package/extensions/graphql/queries/ledger/reportingTaxonomy.ts +25 -0
  93. package/extensions/graphql/queries/ledger/reports.d.ts +8 -0
  94. package/extensions/graphql/queries/ledger/reports.js +45 -0
  95. package/extensions/graphql/queries/ledger/reports.ts +43 -0
  96. package/extensions/graphql/queries/ledger/scheduleFacts.d.ts +7 -0
  97. package/extensions/graphql/queries/ledger/scheduleFacts.js +24 -0
  98. package/extensions/graphql/queries/ledger/scheduleFacts.ts +22 -0
  99. package/extensions/graphql/queries/ledger/schedules.d.ts +6 -0
  100. package/extensions/graphql/queries/ledger/schedules.js +24 -0
  101. package/extensions/graphql/queries/ledger/schedules.ts +22 -0
  102. package/extensions/graphql/queries/ledger/statement.d.ts +9 -0
  103. package/extensions/graphql/queries/ledger/statement.js +43 -0
  104. package/extensions/graphql/queries/ledger/statement.ts +41 -0
  105. package/extensions/graphql/queries/ledger/structures.d.ts +5 -0
  106. package/extensions/graphql/queries/ledger/structures.js +22 -0
  107. package/extensions/graphql/queries/ledger/structures.ts +20 -0
  108. package/extensions/graphql/queries/ledger/summary.d.ts +7 -0
  109. package/extensions/graphql/queries/ledger/summary.js +25 -0
  110. package/extensions/graphql/queries/ledger/summary.ts +23 -0
  111. package/extensions/graphql/queries/ledger/taxonomies.d.ts +5 -0
  112. package/extensions/graphql/queries/ledger/taxonomies.js +28 -0
  113. package/extensions/graphql/queries/ledger/taxonomies.ts +26 -0
  114. package/extensions/graphql/queries/ledger/transaction.d.ts +6 -0
  115. package/extensions/graphql/queries/ledger/transaction.js +49 -0
  116. package/extensions/graphql/queries/ledger/transaction.ts +47 -0
  117. package/extensions/graphql/queries/ledger/transactions.d.ts +7 -0
  118. package/extensions/graphql/queries/ledger/transactions.js +49 -0
  119. package/extensions/graphql/queries/ledger/transactions.ts +47 -0
  120. package/extensions/graphql/queries/ledger/trialBalance.d.ts +7 -0
  121. package/extensions/graphql/queries/ledger/trialBalance.js +28 -0
  122. package/extensions/graphql/queries/ledger/trialBalance.ts +26 -0
  123. package/extensions/graphql/queries/ledger/unmappedElements.d.ts +8 -0
  124. package/extensions/graphql/queries/ledger/unmappedElements.js +29 -0
  125. package/extensions/graphql/queries/ledger/unmappedElements.ts +27 -0
  126. package/extensions/hooks.d.ts +1 -1
  127. package/extensions/index.d.ts +5 -1
  128. package/extensions/index.js +13 -1
  129. package/extensions/index.ts +14 -0
  130. package/index.ts +2 -2
  131. package/package.json +14 -5
  132. package/sdk/index.d.ts +2 -2
  133. package/sdk/index.js +38 -65
  134. package/sdk/index.ts +2 -2
  135. package/sdk/sdk.gen.d.ts +101 -220
  136. package/sdk/sdk.gen.js +251 -445
  137. package/sdk/sdk.gen.ts +216 -409
  138. package/sdk/types.gen.d.ts +4586 -7045
  139. package/sdk/types.gen.ts +4702 -7334
  140. package/sdk.gen.d.ts +101 -220
  141. package/sdk.gen.js +251 -445
  142. package/sdk.gen.ts +216 -409
  143. package/types.gen.d.ts +4586 -7045
  144. package/types.gen.ts +4702 -7334
  145. package/extensions/AgentClient.test.ts +0 -403
  146. package/extensions/LedgerClient.test.ts +0 -1138
  147. package/extensions/OperationClient.test.ts +0 -365
  148. package/extensions/QueryClient.test.ts +0 -432
  149. package/extensions/ReportClient.test.ts +0 -828
  150. package/extensions/SSEClient.test.ts +0 -375
  151. package/extensions/config.test.ts +0 -199
  152. package/extensions/hooks.test.ts +0 -373
  153. package/extensions/index.test.ts +0 -420
  154. package/sdk-extensions/AgentClient.d.ts +0 -82
  155. package/sdk-extensions/AgentClient.js +0 -218
  156. package/sdk-extensions/AgentClient.test.ts +0 -403
  157. package/sdk-extensions/AgentClient.ts +0 -321
  158. package/sdk-extensions/LedgerClient.d.ts +0 -242
  159. package/sdk-extensions/LedgerClient.js +0 -496
  160. package/sdk-extensions/LedgerClient.test.ts +0 -1138
  161. package/sdk-extensions/LedgerClient.ts +0 -800
  162. package/sdk-extensions/OperationClient.d.ts +0 -65
  163. package/sdk-extensions/OperationClient.js +0 -251
  164. package/sdk-extensions/OperationClient.test.ts +0 -365
  165. package/sdk-extensions/OperationClient.ts +0 -324
  166. package/sdk-extensions/QueryClient.d.ts +0 -52
  167. package/sdk-extensions/QueryClient.js +0 -313
  168. package/sdk-extensions/QueryClient.test.ts +0 -432
  169. package/sdk-extensions/QueryClient.ts +0 -420
  170. package/sdk-extensions/README.md +0 -901
  171. package/sdk-extensions/ReportClient.d.ts +0 -151
  172. package/sdk-extensions/ReportClient.js +0 -278
  173. package/sdk-extensions/ReportClient.test.ts +0 -828
  174. package/sdk-extensions/ReportClient.ts +0 -486
  175. package/sdk-extensions/SSEClient.d.ts +0 -62
  176. package/sdk-extensions/SSEClient.js +0 -154
  177. package/sdk-extensions/SSEClient.test.ts +0 -375
  178. package/sdk-extensions/SSEClient.ts +0 -210
  179. package/sdk-extensions/config.d.ts +0 -57
  180. package/sdk-extensions/config.js +0 -152
  181. package/sdk-extensions/config.test.ts +0 -199
  182. package/sdk-extensions/config.ts +0 -175
  183. package/sdk-extensions/hooks.d.ts +0 -110
  184. package/sdk-extensions/hooks.js +0 -384
  185. package/sdk-extensions/hooks.test.ts +0 -373
  186. package/sdk-extensions/hooks.ts +0 -459
  187. package/sdk-extensions/index.d.ts +0 -63
  188. package/sdk-extensions/index.js +0 -164
  189. package/sdk-extensions/index.test.ts +0 -420
  190. package/sdk-extensions/index.ts +0 -203
@@ -1,432 +0,0 @@
1
- import { beforeEach, describe, expect, it, vi } from 'vitest'
2
- import { QueryClient, QueuedQueryError } from './QueryClient'
3
-
4
- // Helper to create proper mock Response objects
5
- function createMockResponse(data: any, options: { ok?: boolean; status?: number } = {}) {
6
- return {
7
- ok: options.ok ?? true,
8
- status: options.status ?? 200,
9
- statusText: options.status === 200 ? 'OK' : 'Error',
10
- headers: new Headers({ 'content-type': 'application/json' }),
11
- json: async () => data,
12
- text: async () => JSON.stringify(data),
13
- blob: async () => new Blob([JSON.stringify(data)]),
14
- arrayBuffer: async () => new TextEncoder().encode(JSON.stringify(data)).buffer,
15
- }
16
- }
17
-
18
- describe('QueryClient', () => {
19
- let queryClient: QueryClient
20
- let mockFetch: any
21
-
22
- beforeEach(() => {
23
- queryClient = new QueryClient({
24
- baseUrl: 'http://localhost:8000',
25
- headers: { 'X-API-Key': 'test-key' },
26
- })
27
-
28
- // Mock global fetch (also set globalThis for SDK client compatibility)
29
- mockFetch = vi.fn()
30
- global.fetch = mockFetch
31
- globalThis.fetch = mockFetch
32
-
33
- // Reset all mocks
34
- vi.clearAllMocks()
35
- })
36
-
37
- describe('executeQuery - immediate response', () => {
38
- it('should execute a sync query successfully', async () => {
39
- mockFetch.mockResolvedValueOnce(
40
- createMockResponse({
41
- data: [
42
- { name: 'Alice', age: 30 },
43
- { name: 'Bob', age: 25 },
44
- ],
45
- columns: ['name', 'age'],
46
- row_count: 2,
47
- execution_time_ms: 45,
48
- timestamp: '2025-01-01T00:00:00Z',
49
- })
50
- )
51
-
52
- const result = await queryClient.executeQuery('graph_123', {
53
- query: 'MATCH (n:Person) RETURN n.name, n.age',
54
- })
55
-
56
- // Should be a QueryResult, not an iterator
57
- expect(result).toHaveProperty('data')
58
- expect(result).toHaveProperty('columns')
59
-
60
- const queryResult = result as any
61
- expect(queryResult.data).toHaveLength(2)
62
- expect(queryResult.columns).toEqual(['name', 'age'])
63
- expect(queryResult.row_count).toBe(2)
64
- expect(queryResult.execution_time_ms).toBe(45)
65
- expect(queryResult.graph_id).toBe('graph_123')
66
- })
67
-
68
- it('should handle query with parameters', async () => {
69
- mockFetch.mockResolvedValueOnce(
70
- createMockResponse({
71
- data: [{ name: 'Alice' }],
72
- columns: ['name'],
73
- row_count: 1,
74
- execution_time_ms: 20,
75
- })
76
- )
77
-
78
- const result = await queryClient.executeQuery(
79
- 'graph_123',
80
- {
81
- query: 'MATCH (n:Person {age: $age}) RETURN n.name',
82
- parameters: { age: 30 },
83
- },
84
- { mode: 'sync' }
85
- )
86
-
87
- const queryResult = result as any
88
- expect(queryResult.data).toHaveLength(1)
89
- expect(queryResult.data[0].name).toBe('Alice')
90
- })
91
-
92
- it('should default row_count if not provided', async () => {
93
- mockFetch.mockResolvedValueOnce(
94
- createMockResponse({
95
- data: [{ a: 1 }, { b: 2 }, { c: 3 }],
96
- columns: ['value'],
97
- })
98
- )
99
-
100
- const result = await queryClient.executeQuery('graph_123', {
101
- query: 'RETURN 1',
102
- })
103
-
104
- const queryResult = result as any
105
- expect(queryResult.row_count).toBe(3) // Should default to data.length
106
- })
107
-
108
- it('should default execution_time_ms if not provided', async () => {
109
- mockFetch.mockResolvedValueOnce(
110
- createMockResponse({
111
- data: [],
112
- columns: [],
113
- })
114
- )
115
-
116
- const result = await queryClient.executeQuery('graph_123', {
117
- query: 'RETURN 1',
118
- })
119
-
120
- const queryResult = result as any
121
- expect(queryResult.execution_time_ms).toBe(0)
122
- })
123
- })
124
-
125
- describe('executeQuery - queued response', () => {
126
- it('should throw QueuedQueryError when maxWait is 0', async () => {
127
- mockFetch.mockResolvedValueOnce(
128
- createMockResponse({
129
- status: 'queued',
130
- operation_id: 'op_123',
131
- queue_position: 5,
132
- estimated_wait_seconds: 30,
133
- message: 'Query queued',
134
- })
135
- )
136
-
137
- await expect(
138
- queryClient.executeQuery(
139
- 'graph_123',
140
- { query: 'RETURN 1' },
141
- { maxWait: 0 } // Don't wait for queued queries
142
- )
143
- ).rejects.toThrow(QueuedQueryError)
144
- })
145
-
146
- it('should call onQueueUpdate when query is queued', async () => {
147
- mockFetch.mockResolvedValueOnce(
148
- createMockResponse({
149
- status: 'queued',
150
- operation_id: 'op_123',
151
- queue_position: 3,
152
- estimated_wait_seconds: 15,
153
- message: 'Query queued',
154
- })
155
- )
156
-
157
- const queueUpdates: Array<{ position: number; wait: number }> = []
158
-
159
- try {
160
- await queryClient.executeQuery(
161
- 'graph_123',
162
- { query: 'RETURN 1' },
163
- {
164
- maxWait: 0,
165
- onQueueUpdate: (position, estimatedWait) => {
166
- queueUpdates.push({ position, wait: estimatedWait })
167
- },
168
- }
169
- )
170
- } catch (error) {
171
- // Expected to throw QueuedQueryError
172
- expect(error).toBeInstanceOf(QueuedQueryError)
173
- expect((error as QueuedQueryError).queueInfo.queue_position).toBe(3)
174
- }
175
-
176
- expect(queueUpdates).toHaveLength(1)
177
- expect(queueUpdates[0].position).toBe(3)
178
- expect(queueUpdates[0].wait).toBe(15)
179
- })
180
- })
181
-
182
- describe('executeQuery - error handling', () => {
183
- it('should throw error for unexpected response format', async () => {
184
- mockFetch.mockResolvedValueOnce(
185
- createMockResponse({
186
- unexpected: 'format',
187
- })
188
- )
189
-
190
- await expect(queryClient.executeQuery('graph_123', { query: 'RETURN 1' })).rejects.toThrow(
191
- 'Unexpected response format'
192
- )
193
- })
194
-
195
- it('should handle fetch errors', async () => {
196
- mockFetch.mockRejectedValueOnce(new Error('Network error'))
197
-
198
- await expect(queryClient.executeQuery('graph_123', { query: 'RETURN 1' })).rejects.toThrow(
199
- 'Network error'
200
- )
201
- })
202
- })
203
-
204
- describe('query (convenience method)', () => {
205
- it('should execute a simple query', async () => {
206
- mockFetch.mockResolvedValueOnce(
207
- createMockResponse({
208
- data: [{ result: 42 }],
209
- columns: ['result'],
210
- row_count: 1,
211
- execution_time_ms: 10,
212
- })
213
- )
214
-
215
- const result = await queryClient.query('graph_123', 'RETURN 42 as result')
216
-
217
- expect(result.data).toHaveLength(1)
218
- expect(result.data[0].result).toBe(42)
219
- })
220
-
221
- it('should execute query with parameters', async () => {
222
- mockFetch.mockResolvedValueOnce(
223
- createMockResponse({
224
- data: [{ sum: 10 }],
225
- columns: ['sum'],
226
- row_count: 1,
227
- execution_time_ms: 5,
228
- })
229
- )
230
-
231
- const result = await queryClient.query('graph_123', 'RETURN $a + $b as sum', {
232
- a: 3,
233
- b: 7,
234
- })
235
-
236
- expect(result.data[0].sum).toBe(10)
237
- })
238
- })
239
-
240
- describe('executeQuery - stream mode with NDJSON', () => {
241
- it('should parse NDJSON streaming response', async () => {
242
- const ndjsonBody = [
243
- '{"columns":["name","age"],"rows":[["Alice",30],["Bob",25]]}',
244
- '{"rows":[["Charlie",35]]}',
245
- '',
246
- ].join('\n')
247
-
248
- // Create a ReadableStream from the NDJSON string
249
- const stream = new ReadableStream({
250
- start(controller) {
251
- controller.enqueue(new TextEncoder().encode(ndjsonBody))
252
- controller.close()
253
- },
254
- })
255
-
256
- const mockStreamResponse = {
257
- data: undefined,
258
- error: undefined,
259
- response: {
260
- ok: true,
261
- status: 200,
262
- headers: new Headers({ 'content-type': 'application/x-ndjson' }),
263
- body: stream,
264
- json: async () => ({}),
265
- },
266
- }
267
-
268
- // Mock the SDK function via fetch
269
- mockFetch.mockResolvedValueOnce({
270
- ok: true,
271
- status: 200,
272
- headers: new Headers({ 'content-type': 'application/x-ndjson' }),
273
- body: stream,
274
- json: async () => mockStreamResponse,
275
- text: async () => JSON.stringify(mockStreamResponse),
276
- })
277
-
278
- // We can't easily test the full stream mode through the SDK mock
279
- // because the SDK parses the response. Test the NDJSON parser directly instead.
280
- const parser = (queryClient as any).parseNDJSONResponse.bind(queryClient)
281
-
282
- const ndjsonStream = new ReadableStream({
283
- start(controller) {
284
- controller.enqueue(
285
- new TextEncoder().encode(
286
- '{"columns":["name"],"rows":[["Alice"],["Bob"]]}\n{"rows":[["Charlie"]]}\n'
287
- )
288
- )
289
- controller.close()
290
- },
291
- })
292
-
293
- const mockResp = {
294
- body: ndjsonStream,
295
- headers: new Headers({ 'content-type': 'application/x-ndjson' }),
296
- }
297
-
298
- const result = await parser(mockResp, 'graph_1')
299
-
300
- expect(result.columns).toEqual(['name'])
301
- expect(result.data).toEqual([['Alice'], ['Bob'], ['Charlie']])
302
- expect(result.row_count).toBe(3)
303
- expect(result.graph_id).toBe('graph_1')
304
- })
305
-
306
- it('should handle NDJSON with data key instead of rows', async () => {
307
- const parser = (queryClient as any).parseNDJSONResponse.bind(queryClient)
308
-
309
- const stream = new ReadableStream({
310
- start(controller) {
311
- controller.enqueue(
312
- new TextEncoder().encode(
313
- '{"columns":["id"],"data":[{"id":1},{"id":2}],"execution_time_ms":50}\n'
314
- )
315
- )
316
- controller.close()
317
- },
318
- })
319
-
320
- const result = await parser({ body: stream }, 'graph_1')
321
-
322
- expect(result.data).toEqual([{ id: 1 }, { id: 2 }])
323
- expect(result.row_count).toBe(2)
324
- expect(result.execution_time_ms).toBe(50)
325
- })
326
-
327
- it('should throw on NDJSON parse errors', async () => {
328
- const parser = (queryClient as any).parseNDJSONResponse.bind(queryClient)
329
-
330
- const stream = new ReadableStream({
331
- start(controller) {
332
- controller.enqueue(new TextEncoder().encode('not valid json\n'))
333
- controller.close()
334
- },
335
- })
336
-
337
- await expect(parser({ body: stream }, 'graph_1')).rejects.toThrow('NDJSON parsing failed')
338
- })
339
-
340
- it('should throw when body is not readable', async () => {
341
- const parser = (queryClient as any).parseNDJSONResponse.bind(queryClient)
342
-
343
- await expect(parser({ body: null }, 'graph_1')).rejects.toThrow(
344
- 'Response body is not readable'
345
- )
346
- })
347
-
348
- it('should track max execution_time_ms across chunks', async () => {
349
- const parser = (queryClient as any).parseNDJSONResponse.bind(queryClient)
350
-
351
- const stream = new ReadableStream({
352
- start(controller) {
353
- controller.enqueue(
354
- new TextEncoder().encode(
355
- '{"columns":["x"],"rows":[[1]],"execution_time_ms":10}\n' +
356
- '{"rows":[[2]],"execution_time_ms":50}\n' +
357
- '{"rows":[[3]],"execution_time_ms":25}\n'
358
- )
359
- )
360
- controller.close()
361
- },
362
- })
363
-
364
- const result = await parser({ body: stream }, 'graph_1')
365
-
366
- expect(result.execution_time_ms).toBe(50) // max across chunks
367
- expect(result.row_count).toBe(3)
368
- })
369
- })
370
-
371
- describe('streamQuery', () => {
372
- it('should yield all data items when result is non-streaming', async () => {
373
- mockFetch.mockResolvedValueOnce(
374
- createMockResponse({
375
- data: [{ id: 1 }, { id: 2 }, { id: 3 }],
376
- columns: ['id'],
377
- row_count: 3,
378
- execution_time_ms: 10,
379
- })
380
- )
381
-
382
- const items: any[] = []
383
- for await (const item of queryClient.streamQuery('graph_1', 'MATCH (n) RETURN n')) {
384
- items.push(item)
385
- }
386
-
387
- expect(items).toEqual([{ id: 1 }, { id: 2 }, { id: 3 }])
388
- })
389
- })
390
-
391
- describe('close', () => {
392
- it('should close without error when no SSE client exists', () => {
393
- expect(() => queryClient.close()).not.toThrow()
394
- })
395
-
396
- it('should be safe to call multiple times', () => {
397
- queryClient.close()
398
- expect(() => queryClient.close()).not.toThrow()
399
- })
400
- })
401
-
402
- describe('QueuedQueryError', () => {
403
- it('should create error with queue info', () => {
404
- const queueInfo = {
405
- status: 'queued' as const,
406
- operation_id: 'op_123',
407
- queue_position: 5,
408
- estimated_wait_seconds: 30,
409
- message: 'Query queued',
410
- }
411
-
412
- const error = new QueuedQueryError(queueInfo)
413
-
414
- expect(error.message).toBe('Query was queued')
415
- expect(error.name).toBe('QueuedQueryError')
416
- expect(error.queueInfo).toEqual(queueInfo)
417
- })
418
-
419
- it('should be instanceof Error', () => {
420
- const error = new QueuedQueryError({
421
- status: 'queued',
422
- operation_id: 'op_1',
423
- queue_position: 1,
424
- estimated_wait_seconds: 5,
425
- message: 'Queued',
426
- })
427
-
428
- expect(error).toBeInstanceOf(Error)
429
- expect(error).toBeInstanceOf(QueuedQueryError)
430
- })
431
- })
432
- })