@rool-dev/client 0.1.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.
@@ -0,0 +1,129 @@
1
+ import { EventEmitter } from './event-emitter.js';
2
+ import { RoolGraph } from './graph.js';
3
+ import type { RoolClientConfig, RoolClientEvents, RoolGraphInfo, Account, UserResult, UserInfo } from './types.js';
4
+ /**
5
+ * Rool Client - Manages authentication, graph lifecycle, and shared infrastructure.
6
+ *
7
+ * The client is lightweight - most graph operations happen on Graph instances
8
+ * obtained via openGraph() or createGraph().
9
+ *
10
+ * Features:
11
+ * - Authentication (login, logout, token management)
12
+ * - Graph lifecycle (list, open, create, delete)
13
+ * - Shared SSE subscription with event routing
14
+ * - Media operations
15
+ * - AI operations (promptGraph, image generation)
16
+ */
17
+ export declare class RoolClient extends EventEmitter<RoolClientEvents> {
18
+ private config;
19
+ private authManager;
20
+ private graphqlClient;
21
+ private subscriptionManager;
22
+ private connectionId;
23
+ private subscribedGraphs;
24
+ constructor(config: RoolClientConfig);
25
+ /**
26
+ * Initialize the client - should be called on app startup.
27
+ * Processes any auth callback in the URL and sets up auto-refresh.
28
+ * @returns true if an auth callback was processed
29
+ */
30
+ initialize(): boolean;
31
+ /**
32
+ * Clean up resources - call when destroying the client.
33
+ */
34
+ destroy(): void;
35
+ /**
36
+ * Initiate login by redirecting to auth page.
37
+ */
38
+ login(): void;
39
+ /**
40
+ * Logout - clear all tokens and state.
41
+ */
42
+ logout(): void;
43
+ /**
44
+ * Process auth callback from URL fragment.
45
+ * Called automatically by initialize(), but can be called manually.
46
+ */
47
+ processAuthCallback(): boolean;
48
+ /**
49
+ * Check if user is currently authenticated.
50
+ */
51
+ isAuthenticated(): boolean;
52
+ /**
53
+ * Get current access token.
54
+ * Returns undefined if not authenticated.
55
+ */
56
+ getToken(): Promise<string | undefined>;
57
+ /**
58
+ * Get user info decoded from JWT token.
59
+ */
60
+ getUser(): UserInfo;
61
+ /**
62
+ * List all graphs accessible to the user.
63
+ */
64
+ listGraphs(): Promise<RoolGraphInfo[]>;
65
+ /**
66
+ * Open an existing graph.
67
+ * Loads the graph data from the server and returns a Graph instance.
68
+ */
69
+ openGraph(graphId: string): Promise<RoolGraph>;
70
+ /**
71
+ * Create a new graph.
72
+ * Creates on server and returns a Graph instance.
73
+ */
74
+ createGraph(name?: string): Promise<RoolGraph>;
75
+ /**
76
+ * Delete a graph.
77
+ * Note: This does not affect any open Graph instances - they become stale.
78
+ */
79
+ deleteGraph(graphId: string): Promise<void>;
80
+ /**
81
+ * Get account info from server (plan, credits, etc).
82
+ */
83
+ getAccount(): Promise<Account>;
84
+ /**
85
+ * Search for a user by email.
86
+ */
87
+ searchUser(email: string): Promise<UserResult | null>;
88
+ private get mediaClient();
89
+ /**
90
+ * Subscribe to real-time events.
91
+ * Client receives graph lifecycle events; graph-level events are routed
92
+ * to subscribed Graph instances.
93
+ */
94
+ subscribe(): Promise<void>;
95
+ /**
96
+ * Unsubscribe from real-time events.
97
+ */
98
+ unsubscribe(): void;
99
+ /**
100
+ * Check if currently subscribed to events.
101
+ */
102
+ isSubscribed(): boolean;
103
+ /**
104
+ * Get the connection ID used for subscriptions.
105
+ * Events triggered by this connection won't be echoed back.
106
+ */
107
+ getConnectionId(): string;
108
+ /**
109
+ * Generate a unique entity ID.
110
+ * 6-character alphanumeric string (62^6 = 56.8 billion possible values).
111
+ * Also available as top-level generateEntityId() export.
112
+ */
113
+ static generateId(): string;
114
+ /**
115
+ * Execute an arbitrary GraphQL query or mutation.
116
+ * Use this escape hatch for app-specific operations not covered by the typed API.
117
+ *
118
+ * @example
119
+ * const result = await client.graphql<{ lastMessages: Message[] }>(
120
+ * `query LastMessages($graphId: String!) { lastMessages(graphId: $graphId) }`,
121
+ * { graphId: 'abc123' }
122
+ * );
123
+ */
124
+ graphql<T>(query: string, variables?: Record<string, unknown>): Promise<T>;
125
+ private registerGraphForEvents;
126
+ private unregisterGraphFromEvents;
127
+ private handleGraphEvent;
128
+ }
129
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAKlD,OAAO,EAAE,SAAS,EAAoB,MAAM,YAAY,CAAC;AACzD,OAAO,KAAK,EACV,gBAAgB,EAChB,gBAAgB,EAEhB,aAAa,EAEb,OAAO,EACP,UAAU,EACV,QAAQ,EAET,MAAM,YAAY,CAAC;AASpB;;;;;;;;;;;;GAYG;AACH,qBAAa,UAAW,SAAQ,YAAY,CAAC,gBAAgB,CAAC;IAC5D,OAAO,CAAC,MAAM,CAAiB;IAC/B,OAAO,CAAC,WAAW,CAAc;IACjC,OAAO,CAAC,aAAa,CAAgB;IACrC,OAAO,CAAC,mBAAmB,CAAoC;IAC/D,OAAO,CAAC,YAAY,CAAS;IAG7B,OAAO,CAAC,gBAAgB,CAAgC;gBAE5C,MAAM,EAAE,gBAAgB;IA8BpC;;;;OAIG;IACH,UAAU,IAAI,OAAO;IAIrB;;OAEG;IACH,OAAO,IAAI,IAAI;IAiBf;;OAEG;IACH,KAAK,IAAI,IAAI;IAIb;;OAEG;IACH,MAAM,IAAI,IAAI;IAWd;;;OAGG;IACH,mBAAmB,IAAI,OAAO;IAI9B;;OAEG;IACH,eAAe,IAAI,OAAO;IAI1B;;;OAGG;IACG,QAAQ,IAAI,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC;IAI7C;;OAEG;IACH,OAAO,IAAI,QAAQ;IAQnB;;OAEG;IACG,UAAU,IAAI,OAAO,CAAC,aAAa,EAAE,CAAC;IAI5C;;;OAGG;IACG,SAAS,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC;IAoBpD;;;OAGG;IACG,WAAW,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC;IAoBpD;;;OAGG;IACG,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IASjD;;OAEG;IACG,UAAU,IAAI,OAAO,CAAC,OAAO,CAAC;IAIpC;;OAEG;IACG,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC;IAQ3D,OAAO,KAAK,WAAW,GAKtB;IAMD;;;;OAIG;IACG,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC;IAmBhC;;OAEG;IACH,WAAW,IAAI,IAAI;IAOnB;;OAEG;IACH,YAAY,IAAI,OAAO;IAQvB;;;OAGG;IACH,eAAe,IAAI,MAAM;IAIzB;;;;OAIG;IACH,MAAM,CAAC,UAAU,IAAI,MAAM;IAI3B;;;;;;;;;OASG;IACG,OAAO,CAAC,CAAC,EACb,KAAK,EAAE,MAAM,EACb,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAClC,OAAO,CAAC,CAAC,CAAC;IAQb,OAAO,CAAC,sBAAsB;IAI9B,OAAO,CAAC,yBAAyB;IAQjC,OAAO,CAAC,gBAAgB;CA6DzB"}
package/dist/client.js ADDED
@@ -0,0 +1,343 @@
1
+ // =============================================================================
2
+ // Rool Client
3
+ // Lightweight client for auth, graph lifecycle, and shared infrastructure
4
+ // =============================================================================
5
+ import { EventEmitter } from './event-emitter.js';
6
+ import { AuthManager } from './auth.js';
7
+ import { GraphQLClient } from './graphql.js';
8
+ import { SubscriptionManager } from './subscription.js';
9
+ import { MediaClient } from './media.js';
10
+ import { RoolGraph, generateEntityId } from './graph.js';
11
+ const DEFAULT_STORAGE_PREFIX = 'rool_';
12
+ /**
13
+ * Rool Client - Manages authentication, graph lifecycle, and shared infrastructure.
14
+ *
15
+ * The client is lightweight - most graph operations happen on Graph instances
16
+ * obtained via openGraph() or createGraph().
17
+ *
18
+ * Features:
19
+ * - Authentication (login, logout, token management)
20
+ * - Graph lifecycle (list, open, create, delete)
21
+ * - Shared SSE subscription with event routing
22
+ * - Media operations
23
+ * - AI operations (promptGraph, image generation)
24
+ */
25
+ export class RoolClient extends EventEmitter {
26
+ config;
27
+ authManager;
28
+ graphqlClient;
29
+ subscriptionManager = null;
30
+ connectionId;
31
+ // Registry of open graphs for event routing
32
+ subscribedGraphs = new Map();
33
+ constructor(config) {
34
+ super();
35
+ this.config = {
36
+ baseUrl: config.baseUrl.replace(/\/+$/, ''), // Remove trailing slashes
37
+ storagePrefix: config.storagePrefix ?? DEFAULT_STORAGE_PREFIX,
38
+ };
39
+ this.connectionId = generateEntityId();
40
+ this.authManager = new AuthManager({
41
+ baseUrl: this.config.baseUrl,
42
+ storagePrefix: this.config.storagePrefix,
43
+ authProvider: config.authProvider,
44
+ onAuthStateChanged: (authenticated) => {
45
+ this.emit('authStateChanged', authenticated);
46
+ },
47
+ });
48
+ this.graphqlClient = new GraphQLClient({
49
+ baseUrl: this.config.baseUrl,
50
+ authManager: this.authManager,
51
+ getConnectionId: () => this.connectionId,
52
+ });
53
+ }
54
+ // ===========================================================================
55
+ // Initialization
56
+ // ===========================================================================
57
+ /**
58
+ * Initialize the client - should be called on app startup.
59
+ * Processes any auth callback in the URL and sets up auto-refresh.
60
+ * @returns true if an auth callback was processed
61
+ */
62
+ initialize() {
63
+ return this.authManager.initialize();
64
+ }
65
+ /**
66
+ * Clean up resources - call when destroying the client.
67
+ */
68
+ destroy() {
69
+ this.authManager.destroy();
70
+ this.subscriptionManager?.destroy();
71
+ // Close all open graphs
72
+ for (const graph of this.subscribedGraphs.values()) {
73
+ graph.close();
74
+ }
75
+ this.subscribedGraphs.clear();
76
+ this.removeAllListeners();
77
+ }
78
+ // ===========================================================================
79
+ // Authentication
80
+ // ===========================================================================
81
+ /**
82
+ * Initiate login by redirecting to auth page.
83
+ */
84
+ login() {
85
+ this.authManager.login();
86
+ }
87
+ /**
88
+ * Logout - clear all tokens and state.
89
+ */
90
+ logout() {
91
+ this.authManager.logout();
92
+ this.unsubscribe();
93
+ // Close all open graphs
94
+ for (const graph of this.subscribedGraphs.values()) {
95
+ graph.close();
96
+ }
97
+ this.subscribedGraphs.clear();
98
+ }
99
+ /**
100
+ * Process auth callback from URL fragment.
101
+ * Called automatically by initialize(), but can be called manually.
102
+ */
103
+ processAuthCallback() {
104
+ return this.authManager.processCallback();
105
+ }
106
+ /**
107
+ * Check if user is currently authenticated.
108
+ */
109
+ isAuthenticated() {
110
+ return this.authManager.isAuthenticated();
111
+ }
112
+ /**
113
+ * Get current access token.
114
+ * Returns undefined if not authenticated.
115
+ */
116
+ async getToken() {
117
+ return this.authManager.getToken();
118
+ }
119
+ /**
120
+ * Get user info decoded from JWT token.
121
+ */
122
+ getUser() {
123
+ return this.authManager.getUser();
124
+ }
125
+ // ===========================================================================
126
+ // Graph Lifecycle
127
+ // ===========================================================================
128
+ /**
129
+ * List all graphs accessible to the user.
130
+ */
131
+ async listGraphs() {
132
+ return this.graphqlClient.listGraphs();
133
+ }
134
+ /**
135
+ * Open an existing graph.
136
+ * Loads the graph data from the server and returns a Graph instance.
137
+ */
138
+ async openGraph(graphId) {
139
+ const [graphData, graphList] = await Promise.all([
140
+ this.graphqlClient.getGraph(graphId),
141
+ this.graphqlClient.listGraphs(),
142
+ ]);
143
+ const graphInfo = graphList.find(g => g.id === graphId);
144
+ return new RoolGraph({
145
+ id: graphId,
146
+ name: graphInfo?.name ?? graphId,
147
+ role: graphInfo?.role ?? 'viewer',
148
+ initialData: graphData,
149
+ graphqlClient: this.graphqlClient,
150
+ mediaClient: this.mediaClient,
151
+ onRegisterForEvents: (id, graph) => this.registerGraphForEvents(id, graph),
152
+ onUnregisterFromEvents: (id) => this.unregisterGraphFromEvents(id),
153
+ });
154
+ }
155
+ /**
156
+ * Create a new graph.
157
+ * Creates on server and returns a Graph instance.
158
+ */
159
+ async createGraph(name) {
160
+ const graphId = generateEntityId();
161
+ const graphName = name ?? graphId;
162
+ const initialData = { nodes: {}, edges: {}, _meta: {} };
163
+ // Create on server
164
+ await this.graphqlClient.setGraph(graphId, initialData);
165
+ return new RoolGraph({
166
+ id: graphId,
167
+ name: graphName,
168
+ role: 'owner',
169
+ initialData,
170
+ graphqlClient: this.graphqlClient,
171
+ mediaClient: this.mediaClient,
172
+ onRegisterForEvents: (id, graph) => this.registerGraphForEvents(id, graph),
173
+ onUnregisterFromEvents: (id) => this.unregisterGraphFromEvents(id),
174
+ });
175
+ }
176
+ /**
177
+ * Delete a graph.
178
+ * Note: This does not affect any open Graph instances - they become stale.
179
+ */
180
+ async deleteGraph(graphId) {
181
+ await this.graphqlClient.deleteGraph(graphId);
182
+ // Client-level event will be emitted via SSE subscription
183
+ }
184
+ // ===========================================================================
185
+ // User Operations
186
+ // ===========================================================================
187
+ /**
188
+ * Get account info from server (plan, credits, etc).
189
+ */
190
+ async getAccount() {
191
+ return this.graphqlClient.getAccount();
192
+ }
193
+ /**
194
+ * Search for a user by email.
195
+ */
196
+ async searchUser(email) {
197
+ return this.graphqlClient.searchUser(email);
198
+ }
199
+ // ===========================================================================
200
+ // Media Client (used internally by Graph instances)
201
+ // ===========================================================================
202
+ get mediaClient() {
203
+ return new MediaClient({
204
+ baseUrl: this.config.baseUrl,
205
+ authManager: this.authManager,
206
+ });
207
+ }
208
+ // ===========================================================================
209
+ // Subscriptions
210
+ // ===========================================================================
211
+ /**
212
+ * Subscribe to real-time events.
213
+ * Client receives graph lifecycle events; graph-level events are routed
214
+ * to subscribed Graph instances.
215
+ */
216
+ async subscribe() {
217
+ if (this.subscriptionManager)
218
+ return;
219
+ this.subscriptionManager = new SubscriptionManager({
220
+ baseUrl: this.config.baseUrl,
221
+ authManager: this.authManager,
222
+ connectionId: this.connectionId,
223
+ onEvent: (event) => this.handleGraphEvent(event),
224
+ onConnectionStateChanged: (state) => {
225
+ this.emit('connectionStateChanged', state);
226
+ },
227
+ onError: (error) => {
228
+ this.emit('error', error, 'subscription');
229
+ },
230
+ });
231
+ await this.subscriptionManager.subscribe();
232
+ }
233
+ /**
234
+ * Unsubscribe from real-time events.
235
+ */
236
+ unsubscribe() {
237
+ if (this.subscriptionManager) {
238
+ this.subscriptionManager.destroy();
239
+ this.subscriptionManager = null;
240
+ }
241
+ }
242
+ /**
243
+ * Check if currently subscribed to events.
244
+ */
245
+ isSubscribed() {
246
+ return this.subscriptionManager?.isSubscribed ?? false;
247
+ }
248
+ // ===========================================================================
249
+ // Utilities
250
+ // ===========================================================================
251
+ /**
252
+ * Get the connection ID used for subscriptions.
253
+ * Events triggered by this connection won't be echoed back.
254
+ */
255
+ getConnectionId() {
256
+ return this.connectionId;
257
+ }
258
+ /**
259
+ * Generate a unique entity ID.
260
+ * 6-character alphanumeric string (62^6 = 56.8 billion possible values).
261
+ * Also available as top-level generateEntityId() export.
262
+ */
263
+ static generateId() {
264
+ return generateEntityId();
265
+ }
266
+ /**
267
+ * Execute an arbitrary GraphQL query or mutation.
268
+ * Use this escape hatch for app-specific operations not covered by the typed API.
269
+ *
270
+ * @example
271
+ * const result = await client.graphql<{ lastMessages: Message[] }>(
272
+ * `query LastMessages($graphId: String!) { lastMessages(graphId: $graphId) }`,
273
+ * { graphId: 'abc123' }
274
+ * );
275
+ */
276
+ async graphql(query, variables) {
277
+ return this.graphqlClient.query(query, variables);
278
+ }
279
+ // ===========================================================================
280
+ // Private Methods - Graph Registry
281
+ // ===========================================================================
282
+ registerGraphForEvents(graphId, graph) {
283
+ this.subscribedGraphs.set(graphId, graph);
284
+ }
285
+ unregisterGraphFromEvents(graphId) {
286
+ this.subscribedGraphs.delete(graphId);
287
+ }
288
+ // ===========================================================================
289
+ // Private Methods - Event Handling
290
+ // ===========================================================================
291
+ handleGraphEvent(event) {
292
+ const { type, graphId, patch } = event;
293
+ switch (type) {
294
+ case 'graph_patched':
295
+ // Route to subscribed Graph instance
296
+ if (patch) {
297
+ const graph = this.subscribedGraphs.get(graphId);
298
+ if (graph) {
299
+ graph.handleRemotePatch(patch);
300
+ }
301
+ }
302
+ break;
303
+ case 'graph_changed':
304
+ // Route to subscribed Graph instance
305
+ {
306
+ const graph = this.subscribedGraphs.get(graphId);
307
+ if (graph) {
308
+ // Reload graph data and notify
309
+ void this.graphqlClient.getGraph(graphId).then((data) => {
310
+ graph.handleRemoteChange(data);
311
+ });
312
+ }
313
+ }
314
+ break;
315
+ case 'graph_name_changed':
316
+ // Emit client-level event and update any open Graph
317
+ {
318
+ // Fetch updated name
319
+ void this.graphqlClient.listGraphs().then((graphs) => {
320
+ const info = graphs.find(g => g.id === graphId);
321
+ if (info) {
322
+ const graph = this.subscribedGraphs.get(graphId);
323
+ if (graph) {
324
+ graph.handleRemoteRename(info.name);
325
+ }
326
+ this.emit('graphRenamed', graphId, info.name);
327
+ }
328
+ });
329
+ }
330
+ break;
331
+ case 'graph_created':
332
+ // Client-level event only
333
+ this.emit('graphCreated', graphId);
334
+ break;
335
+ case 'graph_deleted':
336
+ // Client-level event
337
+ this.emit('graphDeleted', graphId);
338
+ // Note: Any open Graph instances become stale
339
+ break;
340
+ }
341
+ }
342
+ }
343
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Generic event map type - keys are event names, values are listener signatures
3
+ */
4
+ export type EventMap = Record<string, (...args: any[]) => void>;
5
+ /**
6
+ * Simple typed EventEmitter implementation.
7
+ * Works in browser and Node.js environments without dependencies.
8
+ */
9
+ export declare class EventEmitter<Events extends EventMap> {
10
+ private listeners;
11
+ /**
12
+ * Register an event listener.
13
+ * @returns Unsubscribe function
14
+ */
15
+ on<K extends keyof Events>(event: K, listener: Events[K]): () => void;
16
+ /**
17
+ * Remove an event listener.
18
+ */
19
+ off<K extends keyof Events>(event: K, listener: Events[K]): void;
20
+ /**
21
+ * Register a one-time event listener.
22
+ * @returns Unsubscribe function
23
+ */
24
+ once<K extends keyof Events>(event: K, listener: Events[K]): () => void;
25
+ /**
26
+ * Emit an event to all registered listeners.
27
+ */
28
+ protected emit<K extends keyof Events>(event: K, ...args: Parameters<Events[K]>): void;
29
+ /**
30
+ * Remove all listeners for a specific event, or all listeners if no event specified.
31
+ */
32
+ removeAllListeners<K extends keyof Events>(event?: K): void;
33
+ /**
34
+ * Get the number of listeners for a specific event.
35
+ */
36
+ listenerCount<K extends keyof Events>(event: K): number;
37
+ }
38
+ //# sourceMappingURL=event-emitter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"event-emitter.d.ts","sourceRoot":"","sources":["../src/event-emitter.ts"],"names":[],"mappings":"AAKA;;GAEG;AAEH,MAAM,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,IAAI,CAAC,CAAC;AAEhE;;;GAGG;AACH,qBAAa,YAAY,CAAC,MAAM,SAAS,QAAQ;IAC/C,OAAO,CAAC,SAAS,CAAsD;IAEvE;;;OAGG;IACH,EAAE,CAAC,CAAC,SAAS,MAAM,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC,CAAC,GAAG,MAAM,IAAI;IASrE;;OAEG;IACH,GAAG,CAAC,CAAC,SAAS,MAAM,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC,CAAC,GAAG,IAAI;IAUhE;;;OAGG;IACH,IAAI,CAAC,CAAC,SAAS,MAAM,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC,CAAC,GAAG,MAAM,IAAI;IASvE;;OAEG;IACH,SAAS,CAAC,IAAI,CAAC,CAAC,SAAS,MAAM,MAAM,EACnC,KAAK,EAAE,CAAC,EACR,GAAG,IAAI,EAAE,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,GAC7B,IAAI;IAcP;;OAEG;IACH,kBAAkB,CAAC,CAAC,SAAS,MAAM,MAAM,EAAE,KAAK,CAAC,EAAE,CAAC,GAAG,IAAI;IAQ3D;;OAEG;IACH,aAAa,CAAC,CAAC,SAAS,MAAM,MAAM,EAAE,KAAK,EAAE,CAAC,GAAG,MAAM;CAGxD"}
@@ -0,0 +1,80 @@
1
+ // =============================================================================
2
+ // Typed EventEmitter
3
+ // Framework-agnostic event emitter that works in browser and Node.js
4
+ // =============================================================================
5
+ /**
6
+ * Simple typed EventEmitter implementation.
7
+ * Works in browser and Node.js environments without dependencies.
8
+ */
9
+ export class EventEmitter {
10
+ listeners = new Map();
11
+ /**
12
+ * Register an event listener.
13
+ * @returns Unsubscribe function
14
+ */
15
+ on(event, listener) {
16
+ if (!this.listeners.has(event)) {
17
+ this.listeners.set(event, new Set());
18
+ }
19
+ this.listeners.get(event).add(listener);
20
+ return () => this.off(event, listener);
21
+ }
22
+ /**
23
+ * Remove an event listener.
24
+ */
25
+ off(event, listener) {
26
+ const set = this.listeners.get(event);
27
+ if (set) {
28
+ set.delete(listener);
29
+ if (set.size === 0) {
30
+ this.listeners.delete(event);
31
+ }
32
+ }
33
+ }
34
+ /**
35
+ * Register a one-time event listener.
36
+ * @returns Unsubscribe function
37
+ */
38
+ once(event, listener) {
39
+ const wrapper = ((...args) => {
40
+ this.off(event, wrapper);
41
+ listener(...args);
42
+ });
43
+ return this.on(event, wrapper);
44
+ }
45
+ /**
46
+ * Emit an event to all registered listeners.
47
+ */
48
+ emit(event, ...args) {
49
+ const set = this.listeners.get(event);
50
+ if (set) {
51
+ // Copy to array to allow listeners to unsubscribe during emit
52
+ for (const listener of Array.from(set)) {
53
+ try {
54
+ listener(...args);
55
+ }
56
+ catch (error) {
57
+ console.error(`Error in event listener for "${String(event)}":`, error);
58
+ }
59
+ }
60
+ }
61
+ }
62
+ /**
63
+ * Remove all listeners for a specific event, or all listeners if no event specified.
64
+ */
65
+ removeAllListeners(event) {
66
+ if (event !== undefined) {
67
+ this.listeners.delete(event);
68
+ }
69
+ else {
70
+ this.listeners.clear();
71
+ }
72
+ }
73
+ /**
74
+ * Get the number of listeners for a specific event.
75
+ */
76
+ listenerCount(event) {
77
+ return this.listeners.get(event)?.size ?? 0;
78
+ }
79
+ }
80
+ //# sourceMappingURL=event-emitter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"event-emitter.js","sourceRoot":"","sources":["../src/event-emitter.ts"],"names":[],"mappings":"AAAA,gFAAgF;AAChF,qBAAqB;AACrB,qEAAqE;AACrE,gFAAgF;AAQhF;;;GAGG;AACH,MAAM,OAAO,YAAY;IACf,SAAS,GAAG,IAAI,GAAG,EAA2C,CAAC;IAEvE;;;OAGG;IACH,EAAE,CAAyB,KAAQ,EAAE,QAAmB;QACtD,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;YAC/B,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,EAAE,IAAI,GAAG,EAAE,CAAC,CAAC;QACvC,CAAC;QACD,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,CAAE,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QAEzC,OAAO,GAAG,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;IACzC,CAAC;IAED;;OAEG;IACH,GAAG,CAAyB,KAAQ,EAAE,QAAmB;QACvD,MAAM,GAAG,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QACtC,IAAI,GAAG,EAAE,CAAC;YACR,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;YACrB,IAAI,GAAG,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC;gBACnB,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YAC/B,CAAC;QACH,CAAC;IACH,CAAC;IAED;;;OAGG;IACH,IAAI,CAAyB,KAAQ,EAAE,QAAmB;QACxD,MAAM,OAAO,GAAG,CAAC,CAAC,GAAG,IAA2B,EAAE,EAAE;YAClD,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,OAAoB,CAAC,CAAC;YACrC,QAAqD,CAAC,GAAG,IAAI,CAAC,CAAC;QAClE,CAAC,CAAc,CAAC;QAEhB,OAAO,IAAI,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;IACjC,CAAC;IAED;;OAEG;IACO,IAAI,CACZ,KAAQ,EACR,GAAG,IAA2B;QAE9B,MAAM,GAAG,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QACtC,IAAI,GAAG,EAAE,CAAC;YACR,8DAA8D;YAC9D,KAAK,MAAM,QAAQ,IAAI,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;gBACvC,IAAI,CAAC;oBACF,QAAqD,CAAC,GAAG,IAAI,CAAC,CAAC;gBAClE,CAAC;gBAAC,OAAO,KAAK,EAAE,CAAC;oBACf,OAAO,CAAC,KAAK,CAAC,gCAAgC,MAAM,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;gBAC1E,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;IAED;;OAEG;IACH,kBAAkB,CAAyB,KAAS;QAClD,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;YACxB,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QAC/B,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,CAAC;QACzB,CAAC;IACH,CAAC;IAED;;OAEG;IACH,aAAa,CAAyB,KAAQ;QAC5C,OAAO,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,IAAI,IAAI,CAAC,CAAC;IAC9C,CAAC;CACF"}