@laplace.live/event-bridge-sdk 0.0.1 → 0.0.2

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 (2) hide show
  1. package/index.ts +227 -0
  2. package/package.json +4 -3
package/index.ts ADDED
@@ -0,0 +1,227 @@
1
+ import type { LaplaceEvent, LaplaceEventTypes } from '@laplace.live/event-types'
2
+
3
+ // Create a conditional type that maps from event type string to event object type
4
+ export type EventTypeMap = {
5
+ [K in LaplaceEventTypes]: Extract<LaplaceEvent, { type: K }>
6
+ }
7
+
8
+ export type EventHandler<T extends LaplaceEvent> = (event: T) => void
9
+ export type EventTypeHandler = (event: LaplaceEvent) => void
10
+ export type AnyEventHandler = (event: LaplaceEvent) => void
11
+
12
+ export interface ConnectionOptions {
13
+ url?: string
14
+ token?: string
15
+ reconnect?: boolean
16
+ reconnectInterval?: number
17
+ maxReconnectAttempts?: number
18
+ }
19
+
20
+ export class LaplaceEventClient {
21
+ private ws: WebSocket | null = null
22
+ private eventHandlers = new Map<string, EventHandler<any>[]>()
23
+ private anyEventHandlers: AnyEventHandler[] = []
24
+ private reconnectTimer: number | null = null
25
+ private reconnectAttempts = 0
26
+ private clientId: string | null = null
27
+ private isConnected = false
28
+
29
+ private options: Required<ConnectionOptions> = {
30
+ url: 'ws://localhost:9696',
31
+ token: '',
32
+ reconnect: true,
33
+ reconnectInterval: 3000,
34
+ maxReconnectAttempts: 10,
35
+ }
36
+
37
+ constructor(options: ConnectionOptions = {}) {
38
+ this.options = { ...this.options, ...options }
39
+ }
40
+
41
+ /**
42
+ * Connect to the LAPLACE Event Bridge
43
+ * @returns A promise that resolves when the connection is established
44
+ */
45
+ public connect(): Promise<void> {
46
+ return new Promise((resolve, reject) => {
47
+ try {
48
+ if (this.ws) {
49
+ this.ws.close()
50
+ }
51
+
52
+ const protocols: string[] = []
53
+ if (this.options.token) {
54
+ // Add the token as the second protocol parameter
55
+ protocols.push('laplace-event-bridge-role-client', this.options.token)
56
+ }
57
+
58
+ this.ws = new WebSocket(this.options.url, protocols)
59
+
60
+ this.ws.onopen = () => {
61
+ this.isConnected = true
62
+ this.reconnectAttempts = 0
63
+ console.log('Connected to LAPLACE Event Bridge')
64
+ resolve()
65
+ }
66
+
67
+ this.ws.onmessage = event => {
68
+ try {
69
+ const data = JSON.parse(event.data)
70
+
71
+ // Store client ID from the established message
72
+ if (data.type === 'established' && data.clientId) {
73
+ this.clientId = data.clientId
74
+ console.log(`Connection established with client ID: ${this.clientId}`)
75
+ }
76
+
77
+ // Process the event
78
+ this.processEvent(data)
79
+ } catch (err) {
80
+ console.error('Failed to parse event data:', err)
81
+ }
82
+ }
83
+
84
+ this.ws.onerror = error => {
85
+ console.error('WebSocket error:', error)
86
+ reject(error)
87
+ }
88
+
89
+ this.ws.onclose = () => {
90
+ this.isConnected = false
91
+ console.log('Disconnected from LAPLACE Event Bridge')
92
+
93
+ if (this.options.reconnect && this.reconnectAttempts < this.options.maxReconnectAttempts) {
94
+ this.reconnectAttempts++
95
+ console.log(`Attempting to reconnect (${this.reconnectAttempts}/${this.options.maxReconnectAttempts})...`)
96
+ this.reconnectTimer = setTimeout(() => {
97
+ this.connect().catch(err => {
98
+ console.error('Reconnection failed:', err)
99
+ })
100
+ }, this.options.reconnectInterval) as unknown as number
101
+ }
102
+ }
103
+ } catch (err) {
104
+ reject(err)
105
+ }
106
+ })
107
+ }
108
+
109
+ /**
110
+ * Disconnect from the LAPLACE Event Bridge
111
+ */
112
+ public disconnect(): void {
113
+ if (this.reconnectTimer) {
114
+ clearTimeout(this.reconnectTimer)
115
+ this.reconnectTimer = null
116
+ }
117
+
118
+ if (this.ws) {
119
+ this.ws.close()
120
+ this.ws = null
121
+ }
122
+
123
+ this.isConnected = false
124
+ this.clientId = null
125
+ }
126
+
127
+ /**
128
+ * Register an event handler for a specific event type
129
+ * @param eventType The event type to listen for
130
+ * @param handler The handler function to call when the event is received
131
+ */
132
+ public on<T extends LaplaceEventTypes>(eventType: T, handler: (event: EventTypeMap[T]) => void): void {
133
+ if (!this.eventHandlers.has(eventType)) {
134
+ this.eventHandlers.set(eventType, [])
135
+ }
136
+ this.eventHandlers.get(eventType)!.push(handler)
137
+ }
138
+
139
+ /**
140
+ * Register a handler for all events
141
+ * @param handler The handler function to call for any event
142
+ */
143
+ public onAny(handler: AnyEventHandler): void {
144
+ this.anyEventHandlers.push(handler)
145
+ }
146
+
147
+ /**
148
+ * Remove an event handler for a specific event type
149
+ * @param eventType The event type to remove the handler for
150
+ * @param handler The handler function to remove
151
+ */
152
+ public off<T extends LaplaceEventTypes>(eventType: T, handler: (event: EventTypeMap[T]) => void): void {
153
+ if (!this.eventHandlers.has(eventType)) {
154
+ return
155
+ }
156
+
157
+ const handlers = this.eventHandlers.get(eventType)!
158
+ const index = handlers.indexOf(handler)
159
+
160
+ if (index !== -1) {
161
+ handlers.splice(index, 1)
162
+ }
163
+
164
+ if (handlers.length === 0) {
165
+ this.eventHandlers.delete(eventType)
166
+ }
167
+ }
168
+
169
+ /**
170
+ * Remove a handler for all events
171
+ * @param handler The handler function to remove
172
+ */
173
+ public offAny(handler: AnyEventHandler): void {
174
+ const index = this.anyEventHandlers.indexOf(handler)
175
+ if (index !== -1) {
176
+ this.anyEventHandlers.splice(index, 1)
177
+ }
178
+ }
179
+
180
+ /**
181
+ * Check if the client is connected to the bridge
182
+ */
183
+ public isConnectedToBridge(): boolean {
184
+ return this.isConnected
185
+ }
186
+
187
+ /**
188
+ * Get the client ID assigned by the bridge
189
+ */
190
+ public getClientId(): string | null {
191
+ return this.clientId
192
+ }
193
+
194
+ /**
195
+ * Send an event to the bridge
196
+ * @param event The event to send
197
+ */
198
+ public send(event: LaplaceEvent): void {
199
+ if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
200
+ throw new Error('Not connected to LAPLACE Event Bridge')
201
+ }
202
+
203
+ this.ws.send(JSON.stringify(event))
204
+ }
205
+
206
+ private processEvent(event: LaplaceEvent): void {
207
+ // Call specific event handlers
208
+ if (this.eventHandlers.has(event.type)) {
209
+ for (const handler of this.eventHandlers.get(event.type)!) {
210
+ try {
211
+ handler(event)
212
+ } catch (err) {
213
+ console.error(`Error in event handler for type ${event.type}:`, err)
214
+ }
215
+ }
216
+ }
217
+
218
+ // Call any event handlers
219
+ for (const handler of this.anyEventHandlers) {
220
+ try {
221
+ handler(event)
222
+ } catch (err) {
223
+ console.error('Error in any event handler:', err)
224
+ }
225
+ }
226
+ }
227
+ }
package/package.json CHANGED
@@ -1,11 +1,12 @@
1
1
  {
2
2
  "name": "@laplace.live/event-bridge-sdk",
3
3
  "description": "LAPLACE Event Bridge SDK",
4
- "version": "0.0.1",
4
+ "version": "0.0.2",
5
5
  "module": "index.ts",
6
6
  "type": "module",
7
7
  "scripts": {
8
- "build": "bun build index.ts --outdir dist --target browser --minify"
8
+ "build": "bun build index.ts --outdir dist --target browser --minify",
9
+ "publish": "bun run build && bun publish"
9
10
  },
10
11
  "exports": {
11
12
  ".": {
@@ -15,7 +16,7 @@
15
16
  },
16
17
  "files": [
17
18
  "dist",
18
- "src",
19
+ "index.ts",
19
20
  "README.md",
20
21
  "LICENSE"
21
22
  ],