@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.
@@ -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
+ }