@robosystems/client 0.1.10 → 0.1.11
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/OperationClient.js +297 -0
- package/extensions/OperationClient.ts +322 -0
- package/extensions/QueryClient.js +245 -0
- package/extensions/QueryClient.ts +283 -0
- package/extensions/SSEClient.js +166 -0
- package/extensions/SSEClient.ts +189 -0
- package/extensions/config.js +66 -0
- package/extensions/config.ts +91 -0
- package/extensions/hooks.js +435 -0
- package/extensions/hooks.ts +438 -0
- package/extensions/index.js +118 -0
- package/extensions/index.ts +123 -0
- package/package.json +2 -1
|
@@ -0,0 +1,189 @@
|
|
|
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
|
+
|
|
8
|
+
export interface SSEConfig {
|
|
9
|
+
baseUrl: string
|
|
10
|
+
credentials?: 'include' | 'same-origin' | 'omit'
|
|
11
|
+
headers?: Record<string, string>
|
|
12
|
+
maxRetries?: number
|
|
13
|
+
retryDelay?: number
|
|
14
|
+
heartbeatInterval?: number
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface SSEEvent {
|
|
18
|
+
event: string
|
|
19
|
+
data: any
|
|
20
|
+
id?: string
|
|
21
|
+
retry?: number
|
|
22
|
+
timestamp: Date
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export enum EventType {
|
|
26
|
+
OPERATION_STARTED = 'operation_started',
|
|
27
|
+
OPERATION_PROGRESS = 'operation_progress',
|
|
28
|
+
OPERATION_COMPLETED = 'operation_completed',
|
|
29
|
+
OPERATION_ERROR = 'operation_error',
|
|
30
|
+
OPERATION_CANCELLED = 'operation_cancelled',
|
|
31
|
+
DATA_CHUNK = 'data_chunk',
|
|
32
|
+
METADATA = 'metadata',
|
|
33
|
+
HEARTBEAT = 'heartbeat',
|
|
34
|
+
QUEUE_UPDATE = 'queue_update',
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export class SSEClient {
|
|
38
|
+
private config: SSEConfig
|
|
39
|
+
private eventSource?: EventSource
|
|
40
|
+
private reconnectAttempts: number = 0
|
|
41
|
+
private lastEventId?: string
|
|
42
|
+
private closed: boolean = false
|
|
43
|
+
private listeners: Map<string, Set<(data: any) => void>> = new Map()
|
|
44
|
+
|
|
45
|
+
constructor(config: SSEConfig) {
|
|
46
|
+
this.config = {
|
|
47
|
+
maxRetries: 5,
|
|
48
|
+
retryDelay: 1000,
|
|
49
|
+
heartbeatInterval: 30000,
|
|
50
|
+
...config,
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
async connect(operationId: string, fromSequence: number = 0): Promise<void> {
|
|
55
|
+
return new Promise((resolve, reject) => {
|
|
56
|
+
const url = `${this.config.baseUrl}/v1/operations/${operationId}/stream?from_sequence=${fromSequence}`
|
|
57
|
+
|
|
58
|
+
this.eventSource = new EventSource(url, {
|
|
59
|
+
withCredentials: this.config.credentials === 'include',
|
|
60
|
+
} as any)
|
|
61
|
+
|
|
62
|
+
const connectionTimeout = setTimeout(() => {
|
|
63
|
+
reject(new Error('Connection timeout'))
|
|
64
|
+
this.close()
|
|
65
|
+
}, 10000)
|
|
66
|
+
|
|
67
|
+
this.eventSource.onopen = () => {
|
|
68
|
+
clearTimeout(connectionTimeout)
|
|
69
|
+
this.reconnectAttempts = 0
|
|
70
|
+
this.emit('connected', null)
|
|
71
|
+
resolve()
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
this.eventSource.onerror = (error) => {
|
|
75
|
+
clearTimeout(connectionTimeout)
|
|
76
|
+
if (!this.closed) {
|
|
77
|
+
this.handleError(error, operationId, fromSequence)
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
this.eventSource.onmessage = (event) => {
|
|
82
|
+
this.handleMessage(event)
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Set up specific event listeners
|
|
86
|
+
Object.values(EventType).forEach((eventType) => {
|
|
87
|
+
this.eventSource!.addEventListener(eventType, (event: any) => {
|
|
88
|
+
this.handleTypedEvent(eventType, event)
|
|
89
|
+
})
|
|
90
|
+
})
|
|
91
|
+
})
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
private handleMessage(event: MessageEvent): void {
|
|
95
|
+
try {
|
|
96
|
+
const data = JSON.parse(event.data)
|
|
97
|
+
const sseEvent: SSEEvent = {
|
|
98
|
+
event: event.type || 'message',
|
|
99
|
+
data,
|
|
100
|
+
id: event.lastEventId,
|
|
101
|
+
timestamp: new Date(),
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
this.lastEventId = event.lastEventId
|
|
105
|
+
this.emit('event', sseEvent)
|
|
106
|
+
} catch (error) {
|
|
107
|
+
this.emit('parse_error', { error, rawData: event.data })
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
private handleTypedEvent(eventType: string, event: MessageEvent): void {
|
|
112
|
+
try {
|
|
113
|
+
const data = JSON.parse(event.data)
|
|
114
|
+
this.lastEventId = event.lastEventId
|
|
115
|
+
this.emit(eventType, data)
|
|
116
|
+
|
|
117
|
+
// Check for completion events
|
|
118
|
+
if (
|
|
119
|
+
eventType === EventType.OPERATION_COMPLETED ||
|
|
120
|
+
eventType === EventType.OPERATION_ERROR ||
|
|
121
|
+
eventType === EventType.OPERATION_CANCELLED
|
|
122
|
+
) {
|
|
123
|
+
this.close()
|
|
124
|
+
}
|
|
125
|
+
} catch (error) {
|
|
126
|
+
this.emit('parse_error', { error, rawData: event.data })
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
private async handleError(error: any, operationId: string, fromSequence: number): Promise<void> {
|
|
131
|
+
if (this.closed) return
|
|
132
|
+
|
|
133
|
+
if (this.reconnectAttempts < this.config.maxRetries!) {
|
|
134
|
+
this.reconnectAttempts++
|
|
135
|
+
const delay = this.config.retryDelay! * Math.pow(2, this.reconnectAttempts - 1)
|
|
136
|
+
|
|
137
|
+
this.emit('reconnecting', {
|
|
138
|
+
attempt: this.reconnectAttempts,
|
|
139
|
+
delay,
|
|
140
|
+
lastEventId: this.lastEventId,
|
|
141
|
+
})
|
|
142
|
+
|
|
143
|
+
setTimeout(() => {
|
|
144
|
+
const resumeFrom = this.lastEventId ? parseInt(this.lastEventId) + 1 : fromSequence
|
|
145
|
+
this.connect(operationId, resumeFrom).catch(() => {
|
|
146
|
+
// Error handled in connect
|
|
147
|
+
})
|
|
148
|
+
}, delay)
|
|
149
|
+
} else {
|
|
150
|
+
this.emit('max_retries_exceeded', error)
|
|
151
|
+
this.close()
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
on(event: string, listener: (data: any) => void): void {
|
|
156
|
+
if (!this.listeners.has(event)) {
|
|
157
|
+
this.listeners.set(event, new Set())
|
|
158
|
+
}
|
|
159
|
+
this.listeners.get(event)!.add(listener)
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
off(event: string, listener: (data: any) => void): void {
|
|
163
|
+
const listeners = this.listeners.get(event)
|
|
164
|
+
if (listeners) {
|
|
165
|
+
listeners.delete(listener)
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
private emit(event: string, data: any): void {
|
|
170
|
+
const listeners = this.listeners.get(event)
|
|
171
|
+
if (listeners) {
|
|
172
|
+
listeners.forEach((listener) => listener(data))
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
close(): void {
|
|
177
|
+
this.closed = true
|
|
178
|
+
if (this.eventSource) {
|
|
179
|
+
this.eventSource.close()
|
|
180
|
+
this.eventSource = undefined
|
|
181
|
+
}
|
|
182
|
+
this.emit('closed', null)
|
|
183
|
+
this.listeners.clear()
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
isConnected(): boolean {
|
|
187
|
+
return this.eventSource !== undefined && this.eventSource.readyState === EventSource.OPEN
|
|
188
|
+
}
|
|
189
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Configuration for SDK extensions
|
|
5
|
+
* Provides centralized configuration for CORS, credentials, and other settings
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
// Default configuration
|
|
11
|
+
const defaultConfig = {
|
|
12
|
+
baseUrl.env.NEXT_PUBLIC_API_URL || 'http://localhost:8000',
|
|
13
|
+
credentials,
|
|
14
|
+
timeout,
|
|
15
|
+
maxRetries,
|
|
16
|
+
retryDelay,
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// Global configuration singleton
|
|
20
|
+
let globalConfig = { ...defaultConfig }
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Set global configuration for SDK extensions
|
|
24
|
+
* @param config Partial configuration to merge with defaults
|
|
25
|
+
*/
|
|
26
|
+
export function setSDKExtensionsConfig(config) {
|
|
27
|
+
globalConfig = {
|
|
28
|
+
...globalConfig,
|
|
29
|
+
...config,
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Get current SDK extensions configuration
|
|
35
|
+
* @returns Current configuration
|
|
36
|
+
*/
|
|
37
|
+
export function getSDKExtensionsConfig() {
|
|
38
|
+
return { ...globalConfig }
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Reset configuration to defaults
|
|
43
|
+
*/
|
|
44
|
+
export function resetSDKExtensionsConfig() {
|
|
45
|
+
globalConfig = { ...defaultConfig }
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Get configuration for a specific environment
|
|
50
|
+
* @param env Environment name (production, staging, development)
|
|
51
|
+
* @returns Environment-specific configuration
|
|
52
|
+
*/
|
|
53
|
+
export function getEnvironmentConfig(
|
|
54
|
+
env | 'staging' | 'development' = 'development'
|
|
55
|
+
) {
|
|
56
|
+
const baseConfigs> = {
|
|
57
|
+
production,
|
|
58
|
+
staging,
|
|
59
|
+
development,
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return {
|
|
63
|
+
...defaultConfig,
|
|
64
|
+
...baseConfigs[env],
|
|
65
|
+
}
|
|
66
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Configuration for SDK extensions
|
|
5
|
+
* Provides centralized configuration for CORS, credentials, and other settings
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export interface SDKExtensionsConfig {
|
|
9
|
+
baseUrl?: string
|
|
10
|
+
credentials?: 'include' | 'same-origin' | 'omit'
|
|
11
|
+
headers?: Record<string, string>
|
|
12
|
+
timeout?: number
|
|
13
|
+
maxRetries?: number
|
|
14
|
+
retryDelay?: number
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// Default configuration
|
|
18
|
+
const defaultConfig: SDKExtensionsConfig = {
|
|
19
|
+
baseUrl: process.env.NEXT_PUBLIC_API_URL || 'http://localhost:8000',
|
|
20
|
+
credentials: 'include',
|
|
21
|
+
timeout: 30000,
|
|
22
|
+
maxRetries: 3,
|
|
23
|
+
retryDelay: 1000,
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Global configuration singleton
|
|
27
|
+
let globalConfig: SDKExtensionsConfig = { ...defaultConfig }
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Set global configuration for SDK extensions
|
|
31
|
+
* @param config Partial configuration to merge with defaults
|
|
32
|
+
*/
|
|
33
|
+
export function setSDKExtensionsConfig(config: Partial<SDKExtensionsConfig>) {
|
|
34
|
+
globalConfig = {
|
|
35
|
+
...globalConfig,
|
|
36
|
+
...config,
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Get current SDK extensions configuration
|
|
42
|
+
* @returns Current configuration
|
|
43
|
+
*/
|
|
44
|
+
export function getSDKExtensionsConfig(): SDKExtensionsConfig {
|
|
45
|
+
return { ...globalConfig }
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Reset configuration to defaults
|
|
50
|
+
*/
|
|
51
|
+
export function resetSDKExtensionsConfig() {
|
|
52
|
+
globalConfig = { ...defaultConfig }
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Get configuration for a specific environment
|
|
57
|
+
* @param env Environment name (production, staging, development)
|
|
58
|
+
* @returns Environment-specific configuration
|
|
59
|
+
*/
|
|
60
|
+
export function getEnvironmentConfig(
|
|
61
|
+
env: 'production' | 'staging' | 'development' = 'development'
|
|
62
|
+
): SDKExtensionsConfig {
|
|
63
|
+
const baseConfigs: Record<string, Partial<SDKExtensionsConfig>> = {
|
|
64
|
+
production: {
|
|
65
|
+
baseUrl: process.env.NEXT_PUBLIC_API_URL || 'https://api.robosystems.ai',
|
|
66
|
+
credentials: 'include',
|
|
67
|
+
timeout: 60000,
|
|
68
|
+
maxRetries: 5,
|
|
69
|
+
retryDelay: 2000,
|
|
70
|
+
},
|
|
71
|
+
staging: {
|
|
72
|
+
baseUrl: process.env.NEXT_PUBLIC_API_URL || 'https://staging-api.robosystems.ai',
|
|
73
|
+
credentials: 'include',
|
|
74
|
+
timeout: 45000,
|
|
75
|
+
maxRetries: 3,
|
|
76
|
+
retryDelay: 1500,
|
|
77
|
+
},
|
|
78
|
+
development: {
|
|
79
|
+
baseUrl: process.env.NEXT_PUBLIC_API_URL || 'http://localhost:8000',
|
|
80
|
+
credentials: 'include',
|
|
81
|
+
timeout: 30000,
|
|
82
|
+
maxRetries: 3,
|
|
83
|
+
retryDelay: 1000,
|
|
84
|
+
},
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return {
|
|
88
|
+
...defaultConfig,
|
|
89
|
+
...baseConfigs[env],
|
|
90
|
+
}
|
|
91
|
+
}
|