@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.
- package/extensions/InvestorClient.d.ts +91 -0
- package/extensions/InvestorClient.js +223 -0
- package/extensions/InvestorClient.ts +501 -0
- package/extensions/LedgerClient.d.ts +213 -165
- package/extensions/LedgerClient.js +315 -393
- package/extensions/LedgerClient.ts +887 -564
- package/extensions/ReportClient.d.ts +77 -115
- package/extensions/ReportClient.js +124 -211
- package/extensions/ReportClient.ts +298 -358
- package/extensions/config.js +1 -1
- package/extensions/config.ts +1 -1
- package/extensions/graphql/client.d.ts +46 -0
- package/extensions/graphql/client.js +75 -0
- package/extensions/graphql/client.ts +82 -0
- package/extensions/graphql/generated/graphql.d.ts +1786 -0
- package/extensions/graphql/generated/graphql.js +2671 -0
- package/extensions/graphql/generated/graphql.ts +4491 -0
- package/extensions/graphql/queries/investor/holdings.d.ts +8 -0
- package/extensions/graphql/queries/investor/holdings.js +36 -0
- package/extensions/graphql/queries/investor/holdings.ts +34 -0
- package/extensions/graphql/queries/investor/portfolio.d.ts +4 -0
- package/extensions/graphql/queries/investor/portfolio.js +21 -0
- package/extensions/graphql/queries/investor/portfolio.ts +19 -0
- package/extensions/graphql/queries/investor/portfolios.d.ts +4 -0
- package/extensions/graphql/queries/investor/portfolios.js +29 -0
- package/extensions/graphql/queries/investor/portfolios.ts +27 -0
- package/extensions/graphql/queries/investor/position.d.ts +4 -0
- package/extensions/graphql/queries/investor/position.js +33 -0
- package/extensions/graphql/queries/investor/position.ts +31 -0
- package/extensions/graphql/queries/investor/positions.d.ts +8 -0
- package/extensions/graphql/queries/investor/positions.js +57 -0
- package/extensions/graphql/queries/investor/positions.ts +55 -0
- package/extensions/graphql/queries/investor/securities.d.ts +8 -0
- package/extensions/graphql/queries/investor/securities.js +50 -0
- package/extensions/graphql/queries/investor/securities.ts +48 -0
- package/extensions/graphql/queries/investor/security.d.ts +4 -0
- package/extensions/graphql/queries/investor/security.js +26 -0
- package/extensions/graphql/queries/investor/security.ts +24 -0
- package/extensions/graphql/queries/ledger/accountRollups.d.ts +7 -0
- package/extensions/graphql/queries/ledger/accountRollups.js +36 -0
- package/extensions/graphql/queries/ledger/accountRollups.ts +34 -0
- package/extensions/graphql/queries/ledger/accountTree.d.ts +11 -0
- package/extensions/graphql/queries/ledger/accountTree.js +61 -0
- package/extensions/graphql/queries/ledger/accountTree.ts +59 -0
- package/extensions/graphql/queries/ledger/accounts.d.ts +8 -0
- package/extensions/graphql/queries/ledger/accounts.js +45 -0
- package/extensions/graphql/queries/ledger/accounts.ts +43 -0
- package/extensions/graphql/queries/ledger/closingBookStructures.d.ts +6 -0
- package/extensions/graphql/queries/ledger/closingBookStructures.js +27 -0
- package/extensions/graphql/queries/ledger/closingBookStructures.ts +25 -0
- package/extensions/graphql/queries/ledger/elements.d.ts +6 -0
- package/extensions/graphql/queries/ledger/elements.js +56 -0
- package/extensions/graphql/queries/ledger/elements.ts +54 -0
- package/extensions/graphql/queries/ledger/entities.d.ts +7 -0
- package/extensions/graphql/queries/ledger/entities.js +31 -0
- package/extensions/graphql/queries/ledger/entities.ts +29 -0
- package/extensions/graphql/queries/ledger/entity.d.ts +14 -0
- package/extensions/graphql/queries/ledger/entity.js +55 -0
- package/extensions/graphql/queries/ledger/entity.ts +54 -0
- package/extensions/graphql/queries/ledger/fiscalCalendar.d.ts +8 -0
- package/extensions/graphql/queries/ledger/fiscalCalendar.js +35 -0
- package/extensions/graphql/queries/ledger/fiscalCalendar.ts +33 -0
- package/extensions/graphql/queries/ledger/mappedTrialBalance.d.ts +9 -0
- package/extensions/graphql/queries/ledger/mappedTrialBalance.js +29 -0
- package/extensions/graphql/queries/ledger/mappedTrialBalance.ts +27 -0
- package/extensions/graphql/queries/ledger/mapping.d.ts +5 -0
- package/extensions/graphql/queries/ledger/mapping.js +35 -0
- package/extensions/graphql/queries/ledger/mapping.ts +33 -0
- package/extensions/graphql/queries/ledger/mappingCoverage.d.ts +5 -0
- package/extensions/graphql/queries/ledger/mappingCoverage.js +22 -0
- package/extensions/graphql/queries/ledger/mappingCoverage.ts +20 -0
- package/extensions/graphql/queries/ledger/mappings.d.ts +6 -0
- package/extensions/graphql/queries/ledger/mappings.js +23 -0
- package/extensions/graphql/queries/ledger/mappings.ts +21 -0
- package/extensions/graphql/queries/ledger/periodCloseStatus.d.ts +6 -0
- package/extensions/graphql/queries/ledger/periodCloseStatus.js +29 -0
- package/extensions/graphql/queries/ledger/periodCloseStatus.ts +27 -0
- package/extensions/graphql/queries/ledger/periodDrafts.d.ts +8 -0
- package/extensions/graphql/queries/ledger/periodDrafts.js +45 -0
- package/extensions/graphql/queries/ledger/periodDrafts.ts +43 -0
- package/extensions/graphql/queries/ledger/publishList.d.ts +5 -0
- package/extensions/graphql/queries/ledger/publishList.js +29 -0
- package/extensions/graphql/queries/ledger/publishList.ts +27 -0
- package/extensions/graphql/queries/ledger/publishLists.d.ts +5 -0
- package/extensions/graphql/queries/ledger/publishLists.js +29 -0
- package/extensions/graphql/queries/ledger/publishLists.ts +27 -0
- package/extensions/graphql/queries/ledger/report.d.ts +5 -0
- package/extensions/graphql/queries/ledger/report.js +40 -0
- package/extensions/graphql/queries/ledger/report.ts +38 -0
- package/extensions/graphql/queries/ledger/reportingTaxonomy.d.ts +6 -0
- package/extensions/graphql/queries/ledger/reportingTaxonomy.js +27 -0
- package/extensions/graphql/queries/ledger/reportingTaxonomy.ts +25 -0
- package/extensions/graphql/queries/ledger/reports.d.ts +8 -0
- package/extensions/graphql/queries/ledger/reports.js +45 -0
- package/extensions/graphql/queries/ledger/reports.ts +43 -0
- package/extensions/graphql/queries/ledger/scheduleFacts.d.ts +7 -0
- package/extensions/graphql/queries/ledger/scheduleFacts.js +24 -0
- package/extensions/graphql/queries/ledger/scheduleFacts.ts +22 -0
- package/extensions/graphql/queries/ledger/schedules.d.ts +6 -0
- package/extensions/graphql/queries/ledger/schedules.js +24 -0
- package/extensions/graphql/queries/ledger/schedules.ts +22 -0
- package/extensions/graphql/queries/ledger/statement.d.ts +9 -0
- package/extensions/graphql/queries/ledger/statement.js +43 -0
- package/extensions/graphql/queries/ledger/statement.ts +41 -0
- package/extensions/graphql/queries/ledger/structures.d.ts +5 -0
- package/extensions/graphql/queries/ledger/structures.js +22 -0
- package/extensions/graphql/queries/ledger/structures.ts +20 -0
- package/extensions/graphql/queries/ledger/summary.d.ts +7 -0
- package/extensions/graphql/queries/ledger/summary.js +25 -0
- package/extensions/graphql/queries/ledger/summary.ts +23 -0
- package/extensions/graphql/queries/ledger/taxonomies.d.ts +5 -0
- package/extensions/graphql/queries/ledger/taxonomies.js +28 -0
- package/extensions/graphql/queries/ledger/taxonomies.ts +26 -0
- package/extensions/graphql/queries/ledger/transaction.d.ts +6 -0
- package/extensions/graphql/queries/ledger/transaction.js +49 -0
- package/extensions/graphql/queries/ledger/transaction.ts +47 -0
- package/extensions/graphql/queries/ledger/transactions.d.ts +7 -0
- package/extensions/graphql/queries/ledger/transactions.js +49 -0
- package/extensions/graphql/queries/ledger/transactions.ts +47 -0
- package/extensions/graphql/queries/ledger/trialBalance.d.ts +7 -0
- package/extensions/graphql/queries/ledger/trialBalance.js +28 -0
- package/extensions/graphql/queries/ledger/trialBalance.ts +26 -0
- package/extensions/graphql/queries/ledger/unmappedElements.d.ts +8 -0
- package/extensions/graphql/queries/ledger/unmappedElements.js +29 -0
- package/extensions/graphql/queries/ledger/unmappedElements.ts +27 -0
- package/extensions/hooks.d.ts +1 -1
- package/extensions/index.d.ts +5 -1
- package/extensions/index.js +13 -1
- package/extensions/index.ts +14 -0
- package/index.ts +2 -2
- package/package.json +14 -5
- package/sdk/index.d.ts +2 -2
- package/sdk/index.js +38 -65
- package/sdk/index.ts +2 -2
- package/sdk/sdk.gen.d.ts +101 -220
- package/sdk/sdk.gen.js +251 -445
- package/sdk/sdk.gen.ts +216 -409
- package/sdk/types.gen.d.ts +4586 -7045
- package/sdk/types.gen.ts +4702 -7334
- package/sdk.gen.d.ts +101 -220
- package/sdk.gen.js +251 -445
- package/sdk.gen.ts +216 -409
- package/types.gen.d.ts +4586 -7045
- package/types.gen.ts +4702 -7334
- package/extensions/AgentClient.test.ts +0 -403
- package/extensions/LedgerClient.test.ts +0 -1138
- package/extensions/OperationClient.test.ts +0 -365
- package/extensions/QueryClient.test.ts +0 -432
- package/extensions/ReportClient.test.ts +0 -828
- package/extensions/SSEClient.test.ts +0 -375
- package/extensions/config.test.ts +0 -199
- package/extensions/hooks.test.ts +0 -373
- package/extensions/index.test.ts +0 -420
- package/sdk-extensions/AgentClient.d.ts +0 -82
- package/sdk-extensions/AgentClient.js +0 -218
- package/sdk-extensions/AgentClient.test.ts +0 -403
- package/sdk-extensions/AgentClient.ts +0 -321
- package/sdk-extensions/LedgerClient.d.ts +0 -242
- package/sdk-extensions/LedgerClient.js +0 -496
- package/sdk-extensions/LedgerClient.test.ts +0 -1138
- package/sdk-extensions/LedgerClient.ts +0 -800
- package/sdk-extensions/OperationClient.d.ts +0 -65
- package/sdk-extensions/OperationClient.js +0 -251
- package/sdk-extensions/OperationClient.test.ts +0 -365
- package/sdk-extensions/OperationClient.ts +0 -324
- package/sdk-extensions/QueryClient.d.ts +0 -52
- package/sdk-extensions/QueryClient.js +0 -313
- package/sdk-extensions/QueryClient.test.ts +0 -432
- package/sdk-extensions/QueryClient.ts +0 -420
- package/sdk-extensions/README.md +0 -901
- package/sdk-extensions/ReportClient.d.ts +0 -151
- package/sdk-extensions/ReportClient.js +0 -278
- package/sdk-extensions/ReportClient.test.ts +0 -828
- package/sdk-extensions/ReportClient.ts +0 -486
- package/sdk-extensions/SSEClient.d.ts +0 -62
- package/sdk-extensions/SSEClient.js +0 -154
- package/sdk-extensions/SSEClient.test.ts +0 -375
- package/sdk-extensions/SSEClient.ts +0 -210
- package/sdk-extensions/config.d.ts +0 -57
- package/sdk-extensions/config.js +0 -152
- package/sdk-extensions/config.test.ts +0 -199
- package/sdk-extensions/config.ts +0 -175
- package/sdk-extensions/hooks.d.ts +0 -110
- package/sdk-extensions/hooks.js +0 -384
- package/sdk-extensions/hooks.test.ts +0 -373
- package/sdk-extensions/hooks.ts +0 -459
- package/sdk-extensions/index.d.ts +0 -63
- package/sdk-extensions/index.js +0 -164
- package/sdk-extensions/index.test.ts +0 -420
- package/sdk-extensions/index.ts +0 -203
|
@@ -1,375 +0,0 @@
|
|
|
1
|
-
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
|
2
|
-
import { EventType, SSEClient } from './SSEClient'
|
|
3
|
-
|
|
4
|
-
// Mock EventSource for testing
|
|
5
|
-
class MockEventSource {
|
|
6
|
-
url: string
|
|
7
|
-
withCredentials: boolean
|
|
8
|
-
readyState: number = 0 // 0 = CONNECTING, 1 = OPEN, 2 = CLOSED
|
|
9
|
-
onopen: ((event: any) => void) | null = null
|
|
10
|
-
onerror: ((event: any) => void) | null = null
|
|
11
|
-
onmessage: ((event: any) => void) | null = null
|
|
12
|
-
private eventListeners: Map<string, Set<(event: any) => void>> = new Map()
|
|
13
|
-
|
|
14
|
-
static CONNECTING = 0
|
|
15
|
-
static OPEN = 1
|
|
16
|
-
static CLOSED = 2
|
|
17
|
-
|
|
18
|
-
constructor(url: string, options?: { withCredentials?: boolean }) {
|
|
19
|
-
this.url = url
|
|
20
|
-
this.withCredentials = options?.withCredentials ?? false
|
|
21
|
-
|
|
22
|
-
// Simulate async connection
|
|
23
|
-
setTimeout(() => {
|
|
24
|
-
this.readyState = MockEventSource.OPEN
|
|
25
|
-
if (this.onopen) {
|
|
26
|
-
this.onopen({ type: 'open' })
|
|
27
|
-
}
|
|
28
|
-
}, 0)
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
addEventListener(event: string, listener: (event: any) => void) {
|
|
32
|
-
if (!this.eventListeners.has(event)) {
|
|
33
|
-
this.eventListeners.set(event, new Set())
|
|
34
|
-
}
|
|
35
|
-
this.eventListeners.get(event)!.add(listener)
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
removeEventListener(event: string, listener: (event: any) => void) {
|
|
39
|
-
const listeners = this.eventListeners.get(event)
|
|
40
|
-
if (listeners) {
|
|
41
|
-
listeners.delete(listener)
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
dispatchEvent(event: any) {
|
|
46
|
-
const listeners = this.eventListeners.get(event.type)
|
|
47
|
-
if (listeners) {
|
|
48
|
-
listeners.forEach((listener) => listener(event))
|
|
49
|
-
}
|
|
50
|
-
return true
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
close() {
|
|
54
|
-
this.readyState = MockEventSource.CLOSED
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
// Test helper to simulate receiving a message
|
|
58
|
-
simulateMessage(eventType: string, data: any, lastEventId?: string) {
|
|
59
|
-
const event = {
|
|
60
|
-
type: eventType,
|
|
61
|
-
data: JSON.stringify(data),
|
|
62
|
-
lastEventId: lastEventId || '',
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
if (eventType === 'message' && this.onmessage) {
|
|
66
|
-
this.onmessage(event)
|
|
67
|
-
} else {
|
|
68
|
-
const listeners = this.eventListeners.get(eventType)
|
|
69
|
-
if (listeners) {
|
|
70
|
-
listeners.forEach((listener) => listener(event))
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
// Test helper to simulate error
|
|
76
|
-
simulateError() {
|
|
77
|
-
if (this.onerror) {
|
|
78
|
-
this.onerror({ type: 'error' })
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
describe('SSEClient', () => {
|
|
84
|
-
let mockEventSource: MockEventSource
|
|
85
|
-
|
|
86
|
-
beforeEach(() => {
|
|
87
|
-
// Mock global EventSource
|
|
88
|
-
global.EventSource = MockEventSource as any
|
|
89
|
-
vi.clearAllMocks()
|
|
90
|
-
})
|
|
91
|
-
|
|
92
|
-
describe('connect', () => {
|
|
93
|
-
it('should connect to SSE endpoint successfully', async () => {
|
|
94
|
-
const client = new SSEClient({
|
|
95
|
-
baseUrl: 'http://localhost:8000',
|
|
96
|
-
})
|
|
97
|
-
|
|
98
|
-
await expect(client.connect('op_123')).resolves.toBeUndefined()
|
|
99
|
-
expect(client.isConnected()).toBe(true)
|
|
100
|
-
})
|
|
101
|
-
|
|
102
|
-
it('should include token in URL when provided', async () => {
|
|
103
|
-
const client = new SSEClient({
|
|
104
|
-
baseUrl: 'http://localhost:8000',
|
|
105
|
-
token: 'my-jwt-token',
|
|
106
|
-
})
|
|
107
|
-
|
|
108
|
-
await client.connect('op_123')
|
|
109
|
-
|
|
110
|
-
// We can't directly access the EventSource URL in this mock,
|
|
111
|
-
// but the client should have connected successfully
|
|
112
|
-
expect(client.isConnected()).toBe(true)
|
|
113
|
-
})
|
|
114
|
-
|
|
115
|
-
it('should include from_sequence parameter', async () => {
|
|
116
|
-
const client = new SSEClient({
|
|
117
|
-
baseUrl: 'http://localhost:8000',
|
|
118
|
-
})
|
|
119
|
-
|
|
120
|
-
await client.connect('op_123', 42)
|
|
121
|
-
expect(client.isConnected()).toBe(true)
|
|
122
|
-
})
|
|
123
|
-
|
|
124
|
-
it('should set withCredentials when credentials is include', async () => {
|
|
125
|
-
const client = new SSEClient({
|
|
126
|
-
baseUrl: 'http://localhost:8000',
|
|
127
|
-
credentials: 'include',
|
|
128
|
-
})
|
|
129
|
-
|
|
130
|
-
await client.connect('op_123')
|
|
131
|
-
expect(client.isConnected()).toBe(true)
|
|
132
|
-
})
|
|
133
|
-
})
|
|
134
|
-
|
|
135
|
-
describe('event handling', () => {
|
|
136
|
-
it('should emit connected event on connection', async () => {
|
|
137
|
-
const client = new SSEClient({
|
|
138
|
-
baseUrl: 'http://localhost:8000',
|
|
139
|
-
})
|
|
140
|
-
|
|
141
|
-
const connectedEvents: any[] = []
|
|
142
|
-
client.on('connected', (data) => connectedEvents.push(data))
|
|
143
|
-
|
|
144
|
-
await client.connect('op_123')
|
|
145
|
-
|
|
146
|
-
expect(connectedEvents).toHaveLength(1)
|
|
147
|
-
})
|
|
148
|
-
|
|
149
|
-
it('should handle typed events', async () => {
|
|
150
|
-
const client = new SSEClient({
|
|
151
|
-
baseUrl: 'http://localhost:8000',
|
|
152
|
-
})
|
|
153
|
-
|
|
154
|
-
const progressEvents: any[] = []
|
|
155
|
-
client.on(EventType.OPERATION_PROGRESS, (data) => progressEvents.push(data))
|
|
156
|
-
|
|
157
|
-
await client.connect('op_123')
|
|
158
|
-
|
|
159
|
-
// Simulate receiving a progress event
|
|
160
|
-
const eventSource = (client as any).eventSource as MockEventSource
|
|
161
|
-
eventSource.simulateMessage(EventType.OPERATION_PROGRESS, {
|
|
162
|
-
message: 'Processing...',
|
|
163
|
-
progress_percent: 50,
|
|
164
|
-
})
|
|
165
|
-
|
|
166
|
-
expect(progressEvents).toHaveLength(1)
|
|
167
|
-
expect(progressEvents[0].message).toBe('Processing...')
|
|
168
|
-
expect(progressEvents[0].progress_percent).toBe(50)
|
|
169
|
-
})
|
|
170
|
-
|
|
171
|
-
it('should auto-close on completion events', async () => {
|
|
172
|
-
const client = new SSEClient({
|
|
173
|
-
baseUrl: 'http://localhost:8000',
|
|
174
|
-
})
|
|
175
|
-
|
|
176
|
-
await client.connect('op_123')
|
|
177
|
-
|
|
178
|
-
const eventSource = (client as any).eventSource as MockEventSource
|
|
179
|
-
eventSource.simulateMessage(EventType.OPERATION_COMPLETED, {
|
|
180
|
-
result: { success: true },
|
|
181
|
-
})
|
|
182
|
-
|
|
183
|
-
// Client should close automatically
|
|
184
|
-
expect(client.isConnected()).toBe(false)
|
|
185
|
-
})
|
|
186
|
-
|
|
187
|
-
it('should auto-close on error events', async () => {
|
|
188
|
-
const client = new SSEClient({
|
|
189
|
-
baseUrl: 'http://localhost:8000',
|
|
190
|
-
})
|
|
191
|
-
|
|
192
|
-
await client.connect('op_123')
|
|
193
|
-
|
|
194
|
-
const eventSource = (client as any).eventSource as MockEventSource
|
|
195
|
-
eventSource.simulateMessage(EventType.OPERATION_ERROR, {
|
|
196
|
-
error: 'Something failed',
|
|
197
|
-
})
|
|
198
|
-
|
|
199
|
-
expect(client.isConnected()).toBe(false)
|
|
200
|
-
})
|
|
201
|
-
|
|
202
|
-
it('should auto-close on cancelled events', async () => {
|
|
203
|
-
const client = new SSEClient({
|
|
204
|
-
baseUrl: 'http://localhost:8000',
|
|
205
|
-
})
|
|
206
|
-
|
|
207
|
-
await client.connect('op_123')
|
|
208
|
-
|
|
209
|
-
const eventSource = (client as any).eventSource as MockEventSource
|
|
210
|
-
eventSource.simulateMessage(EventType.OPERATION_CANCELLED, {
|
|
211
|
-
message: 'Cancelled by user',
|
|
212
|
-
})
|
|
213
|
-
|
|
214
|
-
expect(client.isConnected()).toBe(false)
|
|
215
|
-
})
|
|
216
|
-
|
|
217
|
-
it('should handle heartbeat events', async () => {
|
|
218
|
-
const client = new SSEClient({
|
|
219
|
-
baseUrl: 'http://localhost:8000',
|
|
220
|
-
})
|
|
221
|
-
|
|
222
|
-
const heartbeats: any[] = []
|
|
223
|
-
client.on(EventType.HEARTBEAT, (data) => heartbeats.push(data))
|
|
224
|
-
|
|
225
|
-
await client.connect('op_123')
|
|
226
|
-
|
|
227
|
-
const eventSource = (client as any).eventSource as MockEventSource
|
|
228
|
-
eventSource.simulateMessage(EventType.HEARTBEAT, { timestamp: Date.now() })
|
|
229
|
-
|
|
230
|
-
expect(heartbeats).toHaveLength(1)
|
|
231
|
-
})
|
|
232
|
-
|
|
233
|
-
it('should handle data chunk events', async () => {
|
|
234
|
-
const client = new SSEClient({
|
|
235
|
-
baseUrl: 'http://localhost:8000',
|
|
236
|
-
})
|
|
237
|
-
|
|
238
|
-
const chunks: any[] = []
|
|
239
|
-
client.on(EventType.DATA_CHUNK, (data) => chunks.push(data))
|
|
240
|
-
|
|
241
|
-
await client.connect('op_123')
|
|
242
|
-
|
|
243
|
-
const eventSource = (client as any).eventSource as MockEventSource
|
|
244
|
-
eventSource.simulateMessage(EventType.DATA_CHUNK, {
|
|
245
|
-
rows: [
|
|
246
|
-
{ id: 1, name: 'Alice' },
|
|
247
|
-
{ id: 2, name: 'Bob' },
|
|
248
|
-
],
|
|
249
|
-
})
|
|
250
|
-
|
|
251
|
-
expect(chunks).toHaveLength(1)
|
|
252
|
-
expect(chunks[0].rows).toHaveLength(2)
|
|
253
|
-
})
|
|
254
|
-
})
|
|
255
|
-
|
|
256
|
-
describe('listener management', () => {
|
|
257
|
-
it('should add and remove listeners', async () => {
|
|
258
|
-
const client = new SSEClient({
|
|
259
|
-
baseUrl: 'http://localhost:8000',
|
|
260
|
-
})
|
|
261
|
-
|
|
262
|
-
const events: any[] = []
|
|
263
|
-
const listener = (data: any) => events.push(data)
|
|
264
|
-
|
|
265
|
-
client.on('test-event', listener)
|
|
266
|
-
await client.connect('op_123')
|
|
267
|
-
|
|
268
|
-
// Manually emit an event
|
|
269
|
-
;(client as any).emit('test-event', { value: 1 })
|
|
270
|
-
expect(events).toHaveLength(1)
|
|
271
|
-
|
|
272
|
-
// Remove listener
|
|
273
|
-
client.off('test-event', listener)
|
|
274
|
-
;(client as any).emit('test-event', { value: 2 })
|
|
275
|
-
expect(events).toHaveLength(1) // Should not have increased
|
|
276
|
-
})
|
|
277
|
-
|
|
278
|
-
it('should support multiple listeners for same event', async () => {
|
|
279
|
-
const client = new SSEClient({
|
|
280
|
-
baseUrl: 'http://localhost:8000',
|
|
281
|
-
})
|
|
282
|
-
|
|
283
|
-
const events1: any[] = []
|
|
284
|
-
const events2: any[] = []
|
|
285
|
-
|
|
286
|
-
client.on('test-event', (data) => events1.push(data))
|
|
287
|
-
client.on('test-event', (data) => events2.push(data))
|
|
288
|
-
|
|
289
|
-
await client.connect('op_123')
|
|
290
|
-
;(client as any).emit('test-event', { value: 1 })
|
|
291
|
-
|
|
292
|
-
expect(events1).toHaveLength(1)
|
|
293
|
-
expect(events2).toHaveLength(1)
|
|
294
|
-
})
|
|
295
|
-
})
|
|
296
|
-
|
|
297
|
-
describe('close', () => {
|
|
298
|
-
it('should close the connection', async () => {
|
|
299
|
-
const client = new SSEClient({
|
|
300
|
-
baseUrl: 'http://localhost:8000',
|
|
301
|
-
})
|
|
302
|
-
|
|
303
|
-
await client.connect('op_123')
|
|
304
|
-
expect(client.isConnected()).toBe(true)
|
|
305
|
-
|
|
306
|
-
client.close()
|
|
307
|
-
expect(client.isConnected()).toBe(false)
|
|
308
|
-
})
|
|
309
|
-
|
|
310
|
-
it('should emit closed event', async () => {
|
|
311
|
-
const client = new SSEClient({
|
|
312
|
-
baseUrl: 'http://localhost:8000',
|
|
313
|
-
})
|
|
314
|
-
|
|
315
|
-
const closedEvents: any[] = []
|
|
316
|
-
client.on('closed', (data) => closedEvents.push(data))
|
|
317
|
-
|
|
318
|
-
await client.connect('op_123')
|
|
319
|
-
client.close()
|
|
320
|
-
|
|
321
|
-
expect(closedEvents).toHaveLength(1)
|
|
322
|
-
})
|
|
323
|
-
|
|
324
|
-
it('should clear all listeners on close', async () => {
|
|
325
|
-
const client = new SSEClient({
|
|
326
|
-
baseUrl: 'http://localhost:8000',
|
|
327
|
-
})
|
|
328
|
-
|
|
329
|
-
const events: any[] = []
|
|
330
|
-
client.on('test-event', (data) => events.push(data))
|
|
331
|
-
|
|
332
|
-
await client.connect('op_123')
|
|
333
|
-
client.close()
|
|
334
|
-
|
|
335
|
-
// Try to emit after close - should not receive events
|
|
336
|
-
;(client as any).emit('test-event', { value: 1 })
|
|
337
|
-
expect(events).toHaveLength(0)
|
|
338
|
-
})
|
|
339
|
-
|
|
340
|
-
it('should be safe to call close multiple times', async () => {
|
|
341
|
-
const client = new SSEClient({
|
|
342
|
-
baseUrl: 'http://localhost:8000',
|
|
343
|
-
})
|
|
344
|
-
|
|
345
|
-
await client.connect('op_123')
|
|
346
|
-
|
|
347
|
-
client.close()
|
|
348
|
-
expect(() => client.close()).not.toThrow()
|
|
349
|
-
})
|
|
350
|
-
})
|
|
351
|
-
|
|
352
|
-
describe('configuration', () => {
|
|
353
|
-
it('should accept all config options', () => {
|
|
354
|
-
const client = new SSEClient({
|
|
355
|
-
baseUrl: 'http://localhost:8000',
|
|
356
|
-
credentials: 'include',
|
|
357
|
-
headers: { 'X-Custom': 'value' },
|
|
358
|
-
token: 'jwt-token',
|
|
359
|
-
maxRetries: 5,
|
|
360
|
-
retryDelay: 2000,
|
|
361
|
-
heartbeatInterval: 60000,
|
|
362
|
-
})
|
|
363
|
-
|
|
364
|
-
expect(client).toBeInstanceOf(SSEClient)
|
|
365
|
-
})
|
|
366
|
-
|
|
367
|
-
it('should use default config values', () => {
|
|
368
|
-
const client = new SSEClient({
|
|
369
|
-
baseUrl: 'http://localhost:8000',
|
|
370
|
-
})
|
|
371
|
-
|
|
372
|
-
expect(client).toBeInstanceOf(SSEClient)
|
|
373
|
-
})
|
|
374
|
-
})
|
|
375
|
-
})
|
|
@@ -1,210 +0,0 @@
|
|
|
1
|
-
'use client'
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Core SSE (Server-Sent Events) client for RoboSystems API
|
|
5
|
-
* Provides automatic reconnection, event replay, and type-safe event handling
|
|
6
|
-
*
|
|
7
|
-
* SECURITY NOTE: When using JWT authentication, tokens are passed as query parameters
|
|
8
|
-
* due to EventSource API limitations. This means tokens may appear in:
|
|
9
|
-
* - Server access logs
|
|
10
|
-
* - Proxy logs
|
|
11
|
-
* - Browser history
|
|
12
|
-
* - Referer headers
|
|
13
|
-
*
|
|
14
|
-
* For production environments with sensitive data, consider:
|
|
15
|
-
* - Using cookie-based authentication instead
|
|
16
|
-
* - Implementing a WebSocket-based alternative
|
|
17
|
-
* - Using short-lived tokens that expire quickly
|
|
18
|
-
* - Ensuring all connections use HTTPS
|
|
19
|
-
*/
|
|
20
|
-
|
|
21
|
-
export interface SSEConfig {
|
|
22
|
-
baseUrl: string
|
|
23
|
-
credentials?: 'include' | 'same-origin' | 'omit'
|
|
24
|
-
headers?: Record<string, string>
|
|
25
|
-
token?: string // JWT token for authentication
|
|
26
|
-
maxRetries?: number
|
|
27
|
-
retryDelay?: number
|
|
28
|
-
heartbeatInterval?: number
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
export interface SSEEvent {
|
|
32
|
-
event: string
|
|
33
|
-
data: any
|
|
34
|
-
id?: string
|
|
35
|
-
retry?: number
|
|
36
|
-
timestamp: Date
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
export enum EventType {
|
|
40
|
-
OPERATION_STARTED = 'operation_started',
|
|
41
|
-
OPERATION_PROGRESS = 'operation_progress',
|
|
42
|
-
OPERATION_COMPLETED = 'operation_completed',
|
|
43
|
-
OPERATION_ERROR = 'operation_error',
|
|
44
|
-
OPERATION_CANCELLED = 'operation_cancelled',
|
|
45
|
-
DATA_CHUNK = 'data_chunk',
|
|
46
|
-
METADATA = 'metadata',
|
|
47
|
-
HEARTBEAT = 'heartbeat',
|
|
48
|
-
QUEUE_UPDATE = 'queue_update',
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
export class SSEClient {
|
|
52
|
-
private config: SSEConfig
|
|
53
|
-
private eventSource?: EventSource
|
|
54
|
-
private reconnectAttempts: number = 0
|
|
55
|
-
private lastEventId?: string
|
|
56
|
-
private closed: boolean = false
|
|
57
|
-
private listeners: Map<string, Set<(data: any) => void>> = new Map()
|
|
58
|
-
|
|
59
|
-
constructor(config: SSEConfig) {
|
|
60
|
-
this.config = {
|
|
61
|
-
maxRetries: 5,
|
|
62
|
-
retryDelay: 1000,
|
|
63
|
-
heartbeatInterval: 30000,
|
|
64
|
-
...config,
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
async connect(operationId: string, fromSequence: number = 0): Promise<void> {
|
|
69
|
-
return new Promise((resolve, reject) => {
|
|
70
|
-
let url = `${this.config.baseUrl}/v1/operations/${operationId}/stream?from_sequence=${fromSequence}`
|
|
71
|
-
|
|
72
|
-
// Add JWT token as query parameter if provided
|
|
73
|
-
// WARNING: EventSource API doesn't support custom headers, so tokens are passed via query param
|
|
74
|
-
// This has security implications - see class documentation
|
|
75
|
-
if (this.config.token) {
|
|
76
|
-
url += `&token=${encodeURIComponent(this.config.token)}`
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
this.eventSource = new EventSource(url, {
|
|
80
|
-
withCredentials: this.config.credentials === 'include',
|
|
81
|
-
} as any)
|
|
82
|
-
|
|
83
|
-
const connectionTimeout = setTimeout(() => {
|
|
84
|
-
reject(new Error('Connection timeout'))
|
|
85
|
-
this.close()
|
|
86
|
-
}, 10000)
|
|
87
|
-
|
|
88
|
-
this.eventSource.onopen = () => {
|
|
89
|
-
clearTimeout(connectionTimeout)
|
|
90
|
-
this.reconnectAttempts = 0
|
|
91
|
-
this.emit('connected', null)
|
|
92
|
-
resolve()
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
this.eventSource.onerror = (error) => {
|
|
96
|
-
clearTimeout(connectionTimeout)
|
|
97
|
-
if (!this.closed) {
|
|
98
|
-
this.handleError(error, operationId, fromSequence)
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
this.eventSource.onmessage = (event) => {
|
|
103
|
-
this.handleMessage(event)
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
// Set up specific event listeners
|
|
107
|
-
Object.values(EventType).forEach((eventType) => {
|
|
108
|
-
this.eventSource!.addEventListener(eventType, (event: any) => {
|
|
109
|
-
this.handleTypedEvent(eventType, event)
|
|
110
|
-
})
|
|
111
|
-
})
|
|
112
|
-
})
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
private handleMessage(event: MessageEvent): void {
|
|
116
|
-
try {
|
|
117
|
-
const data = JSON.parse(event.data)
|
|
118
|
-
const sseEvent: SSEEvent = {
|
|
119
|
-
event: event.type || 'message',
|
|
120
|
-
data,
|
|
121
|
-
id: event.lastEventId,
|
|
122
|
-
timestamp: new Date(),
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
this.lastEventId = event.lastEventId
|
|
126
|
-
this.emit('event', sseEvent)
|
|
127
|
-
} catch (error) {
|
|
128
|
-
this.emit('parse_error', { error, rawData: event.data })
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
private handleTypedEvent(eventType: string, event: MessageEvent): void {
|
|
133
|
-
try {
|
|
134
|
-
const data = JSON.parse(event.data)
|
|
135
|
-
this.lastEventId = event.lastEventId
|
|
136
|
-
this.emit(eventType, data)
|
|
137
|
-
|
|
138
|
-
// Check for completion events
|
|
139
|
-
if (
|
|
140
|
-
eventType === EventType.OPERATION_COMPLETED ||
|
|
141
|
-
eventType === EventType.OPERATION_ERROR ||
|
|
142
|
-
eventType === EventType.OPERATION_CANCELLED
|
|
143
|
-
) {
|
|
144
|
-
this.close()
|
|
145
|
-
}
|
|
146
|
-
} catch (error) {
|
|
147
|
-
this.emit('parse_error', { error, rawData: event.data })
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
private async handleError(error: any, operationId: string, fromSequence: number): Promise<void> {
|
|
152
|
-
if (this.closed) return
|
|
153
|
-
|
|
154
|
-
if (this.reconnectAttempts < this.config.maxRetries!) {
|
|
155
|
-
this.reconnectAttempts++
|
|
156
|
-
const delay = this.config.retryDelay! * Math.pow(2, this.reconnectAttempts - 1)
|
|
157
|
-
|
|
158
|
-
this.emit('reconnecting', {
|
|
159
|
-
attempt: this.reconnectAttempts,
|
|
160
|
-
delay,
|
|
161
|
-
lastEventId: this.lastEventId,
|
|
162
|
-
})
|
|
163
|
-
|
|
164
|
-
setTimeout(() => {
|
|
165
|
-
const resumeFrom = this.lastEventId ? parseInt(this.lastEventId) + 1 : fromSequence
|
|
166
|
-
this.connect(operationId, resumeFrom).catch(() => {
|
|
167
|
-
// Error handled in connect
|
|
168
|
-
})
|
|
169
|
-
}, delay)
|
|
170
|
-
} else {
|
|
171
|
-
this.emit('max_retries_exceeded', error)
|
|
172
|
-
this.close()
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
on(event: string, listener: (data: any) => void): void {
|
|
177
|
-
if (!this.listeners.has(event)) {
|
|
178
|
-
this.listeners.set(event, new Set())
|
|
179
|
-
}
|
|
180
|
-
this.listeners.get(event)!.add(listener)
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
off(event: string, listener: (data: any) => void): void {
|
|
184
|
-
const listeners = this.listeners.get(event)
|
|
185
|
-
if (listeners) {
|
|
186
|
-
listeners.delete(listener)
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
private emit(event: string, data: any): void {
|
|
191
|
-
const listeners = this.listeners.get(event)
|
|
192
|
-
if (listeners) {
|
|
193
|
-
listeners.forEach((listener) => listener(data))
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
close(): void {
|
|
198
|
-
this.closed = true
|
|
199
|
-
if (this.eventSource) {
|
|
200
|
-
this.eventSource.close()
|
|
201
|
-
this.eventSource = undefined
|
|
202
|
-
}
|
|
203
|
-
this.emit('closed', null)
|
|
204
|
-
this.listeners.clear()
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
isConnected(): boolean {
|
|
208
|
-
return this.eventSource !== undefined && this.eventSource.readyState === EventSource.OPEN
|
|
209
|
-
}
|
|
210
|
-
}
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Configuration for SDK extensions
|
|
3
|
-
* Provides centralized configuration for CORS, credentials, and other settings
|
|
4
|
-
*/
|
|
5
|
-
export interface SDKExtensionsConfig {
|
|
6
|
-
baseUrl?: string;
|
|
7
|
-
credentials?: 'include' | 'same-origin' | 'omit';
|
|
8
|
-
headers?: Record<string, string>;
|
|
9
|
-
token?: string;
|
|
10
|
-
timeout?: number;
|
|
11
|
-
maxRetries?: number;
|
|
12
|
-
retryDelay?: number;
|
|
13
|
-
}
|
|
14
|
-
/**
|
|
15
|
-
* Set global configuration for SDK extensions
|
|
16
|
-
* @param config Partial configuration to merge with defaults
|
|
17
|
-
*/
|
|
18
|
-
export declare function setSDKExtensionsConfig(config: Partial<SDKExtensionsConfig>): void;
|
|
19
|
-
/**
|
|
20
|
-
* Get current SDK extensions configuration
|
|
21
|
-
* @returns Current configuration
|
|
22
|
-
*/
|
|
23
|
-
export declare function getSDKExtensionsConfig(): SDKExtensionsConfig;
|
|
24
|
-
/**
|
|
25
|
-
* Reset configuration to defaults
|
|
26
|
-
*/
|
|
27
|
-
export declare function resetSDKExtensionsConfig(): void;
|
|
28
|
-
/**
|
|
29
|
-
* Extract JWT token from Authorization header
|
|
30
|
-
* @param authHeader Authorization header value
|
|
31
|
-
* @returns JWT token or undefined
|
|
32
|
-
*/
|
|
33
|
-
export declare function extractJWTFromHeader(authHeader?: string | string[]): string | undefined;
|
|
34
|
-
/**
|
|
35
|
-
* Validate JWT token format (basic validation)
|
|
36
|
-
* @param token JWT token to validate
|
|
37
|
-
* @returns true if token appears valid
|
|
38
|
-
*/
|
|
39
|
-
export declare function isValidJWT(token: string | undefined): boolean;
|
|
40
|
-
/**
|
|
41
|
-
* Extract JWT token from SDK client configuration
|
|
42
|
-
* Centralizes the logic for extracting tokens from the client
|
|
43
|
-
* @returns JWT token or undefined
|
|
44
|
-
*/
|
|
45
|
-
export declare function extractTokenFromSDKClient(): string | undefined;
|
|
46
|
-
/**
|
|
47
|
-
* Configure SDK extensions for JWT authentication
|
|
48
|
-
* @param token JWT token
|
|
49
|
-
* @param config Additional configuration options
|
|
50
|
-
*/
|
|
51
|
-
export declare function configureWithJWT(token: string, config?: Partial<SDKExtensionsConfig>): void;
|
|
52
|
-
/**
|
|
53
|
-
* Get configuration for a specific environment
|
|
54
|
-
* @param env Environment name (production, staging, development)
|
|
55
|
-
* @returns Environment-specific configuration
|
|
56
|
-
*/
|
|
57
|
-
export declare function getEnvironmentConfig(env?: 'production' | 'staging' | 'development'): SDKExtensionsConfig;
|