@rool-dev/sdk 0.1.0-dev.8511433

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 (54) hide show
  1. package/README.md +1070 -0
  2. package/dist/apps.d.ts +29 -0
  3. package/dist/apps.d.ts.map +1 -0
  4. package/dist/apps.js +88 -0
  5. package/dist/apps.js.map +1 -0
  6. package/dist/auth-browser.d.ts +80 -0
  7. package/dist/auth-browser.d.ts.map +1 -0
  8. package/dist/auth-browser.js +370 -0
  9. package/dist/auth-browser.js.map +1 -0
  10. package/dist/auth-node.d.ts +46 -0
  11. package/dist/auth-node.d.ts.map +1 -0
  12. package/dist/auth-node.js +315 -0
  13. package/dist/auth-node.js.map +1 -0
  14. package/dist/auth.d.ts +56 -0
  15. package/dist/auth.d.ts.map +1 -0
  16. package/dist/auth.js +96 -0
  17. package/dist/auth.js.map +1 -0
  18. package/dist/client.d.ts +202 -0
  19. package/dist/client.d.ts.map +1 -0
  20. package/dist/client.js +472 -0
  21. package/dist/client.js.map +1 -0
  22. package/dist/event-emitter.d.ts +38 -0
  23. package/dist/event-emitter.d.ts.map +1 -0
  24. package/dist/event-emitter.js +80 -0
  25. package/dist/event-emitter.js.map +1 -0
  26. package/dist/graphql.d.ts +71 -0
  27. package/dist/graphql.d.ts.map +1 -0
  28. package/dist/graphql.js +487 -0
  29. package/dist/graphql.js.map +1 -0
  30. package/dist/index.d.ts +6 -0
  31. package/dist/index.d.ts.map +1 -0
  32. package/dist/index.js +11 -0
  33. package/dist/index.js.map +1 -0
  34. package/dist/jsonld.d.ts +47 -0
  35. package/dist/jsonld.d.ts.map +1 -0
  36. package/dist/jsonld.js +137 -0
  37. package/dist/jsonld.js.map +1 -0
  38. package/dist/media.d.ts +52 -0
  39. package/dist/media.d.ts.map +1 -0
  40. package/dist/media.js +173 -0
  41. package/dist/media.js.map +1 -0
  42. package/dist/space.d.ts +358 -0
  43. package/dist/space.d.ts.map +1 -0
  44. package/dist/space.js +1121 -0
  45. package/dist/space.js.map +1 -0
  46. package/dist/subscription.d.ts +57 -0
  47. package/dist/subscription.d.ts.map +1 -0
  48. package/dist/subscription.js +296 -0
  49. package/dist/subscription.js.map +1 -0
  50. package/dist/types.d.ts +409 -0
  51. package/dist/types.d.ts.map +1 -0
  52. package/dist/types.js +6 -0
  53. package/dist/types.js.map +1 -0
  54. package/package.json +65 -0
@@ -0,0 +1,202 @@
1
+ import { EventEmitter } from './event-emitter.js';
2
+ import { RoolSpace } from './space.js';
3
+ import type { RoolClientConfig, RoolClientEvents, RoolSpaceInfo, CurrentUser, UserResult, AuthUser, PublishedAppInfo, PublishAppOptions } from './types.js';
4
+ /**
5
+ * Rool Client - Manages authentication, space lifecycle, and shared infrastructure.
6
+ *
7
+ * The client is lightweight - most operations happen on Space instances
8
+ *
9
+ * Features:
10
+ * - Authentication (login, logout, token management)
11
+ * - Spaces lifecycle (list, open, create, delete)
12
+ * - Client-level subscription for lifecycle events
13
+ * - Media operations
14
+ * - AI operations (prompt, image generation)
15
+ */
16
+ export declare class RoolClient extends EventEmitter<RoolClientEvents> {
17
+ private urls;
18
+ private authManager;
19
+ private graphqlClient;
20
+ private subscriptionManager;
21
+ private openSpaces;
22
+ private _storageCache;
23
+ constructor(config: RoolClientConfig);
24
+ /**
25
+ * Initialize the client - should be called on app startup.
26
+ * Processes any auth callback in the URL, sets up auto-refresh,
27
+ * and starts real-time event subscription if authenticated.
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
+ * @param appName - The name of the application requesting login (displayed on auth page)
38
+ */
39
+ login(appName: string): Promise<void>;
40
+ /**
41
+ * Logout - clear all tokens and state.
42
+ */
43
+ logout(): void;
44
+ /**
45
+ * Process auth callback from URL fragment.
46
+ * Called automatically by initialize(), but can be called manually.
47
+ */
48
+ processAuthCallback(): boolean;
49
+ /**
50
+ * Check if user is currently authenticated (validates token is usable).
51
+ */
52
+ isAuthenticated(): Promise<boolean>;
53
+ /**
54
+ * Get current access token.
55
+ * Returns undefined if not authenticated.
56
+ */
57
+ getToken(): Promise<string | undefined>;
58
+ /**
59
+ * Get auth identity decoded from JWT token.
60
+ * For the Rool user (with server-assigned id), use getCurrentUser().
61
+ */
62
+ getAuthUser(): AuthUser;
63
+ /**
64
+ * List all spaces accessible to the user.
65
+ */
66
+ listSpaces(): Promise<RoolSpaceInfo[]>;
67
+ /**
68
+ * Open an existing space.
69
+ * Loads the space data from the server and returns a Space instance.
70
+ * The space manages its own real-time subscription.
71
+ *
72
+ * @param spaceId - The ID of the space to open
73
+ * @param options.conversationId - Optional conversation ID for AI context continuity. If not provided, a new conversation is created.
74
+ */
75
+ openSpace(spaceId: string, options?: {
76
+ conversationId?: string;
77
+ }): Promise<RoolSpace>;
78
+ /**
79
+ * Create a new space.
80
+ * Creates on server and returns a Space instance.
81
+ * The space manages its own real-time subscription.
82
+ *
83
+ * @param name - Optional name for the space
84
+ * @param options.conversationId - Optional conversation ID for AI context continuity. If not provided, a new conversation is created.
85
+ */
86
+ createSpace(name?: string, options?: {
87
+ conversationId?: string;
88
+ }): Promise<RoolSpace>;
89
+ /**
90
+ * Delete a space.
91
+ * Note: This does not affect any open Space instances - they become stale.
92
+ */
93
+ deleteSpace(spaceId: string): Promise<void>;
94
+ /**
95
+ * Get the current Rool user from the server.
96
+ * Returns the user's server-assigned id, email, plan, and credits.
97
+ */
98
+ getCurrentUser(): Promise<CurrentUser>;
99
+ /**
100
+ * Search for a user by email.
101
+ */
102
+ searchUser(email: string): Promise<UserResult | null>;
103
+ /**
104
+ * Set the current user's slug (used in app publishing URLs).
105
+ * Slug must be 3-32 characters, start with a letter, and contain only
106
+ * lowercase letters, numbers, hyphens, and underscores.
107
+ * Cannot be changed if the user has published apps.
108
+ */
109
+ setSlug(slug: string): Promise<void>;
110
+ /**
111
+ * Publish an app. The app will be accessible at:
112
+ * https://rool.app/{user_slug}/{appId}/
113
+ *
114
+ * @param appId - URL-safe identifier (alphanumeric, hyphens, underscores)
115
+ * @param options - App name, bundle (zip file), and optional SPA flag
116
+ */
117
+ publishApp(appId: string, options: PublishAppOptions): Promise<PublishedAppInfo>;
118
+ /**
119
+ * Unpublish an app.
120
+ */
121
+ unpublishApp(appId: string): Promise<void>;
122
+ /**
123
+ * List all published apps for the current user.
124
+ */
125
+ listApps(): Promise<PublishedAppInfo[]>;
126
+ /**
127
+ * Get info for a specific published app.
128
+ * Returns null if the app doesn't exist.
129
+ */
130
+ getAppInfo(appId: string): Promise<PublishedAppInfo | null>;
131
+ /**
132
+ * Get a value from user storage (sync read from local cache).
133
+ * Returns undefined if key doesn't exist.
134
+ */
135
+ getUserStorage<T = unknown>(key: string): T | undefined;
136
+ /**
137
+ * Set a value in user storage.
138
+ * Updates local cache immediately, then syncs to server.
139
+ * Pass undefined/null to delete the key.
140
+ * Storage is limited to 10MB total.
141
+ */
142
+ setUserStorage(key: string, value: unknown): void;
143
+ /**
144
+ * Get all user storage data (sync read from local cache).
145
+ */
146
+ getAllUserStorage(): Record<string, unknown>;
147
+ private get mediaClient();
148
+ private get appsClient();
149
+ /**
150
+ * Ensure the client-level event subscription is active.
151
+ * Called automatically when opening spaces.
152
+ * Also fetches and caches the current user ID.
153
+ * @internal
154
+ */
155
+ private ensureSubscribed;
156
+ /**
157
+ * Disconnect from real-time events.
158
+ * @internal
159
+ */
160
+ private unsubscribe;
161
+ /**
162
+ * Generate a unique entity ID.
163
+ * 6-character alphanumeric string (62^6 = 56.8 billion possible values).
164
+ * Also available as top-level generateEntityId() export.
165
+ */
166
+ static generateId(): string;
167
+ /**
168
+ * Execute an arbitrary GraphQL query or mutation.
169
+ * Use this escape hatch for app-specific operations not covered by the typed API.
170
+ *
171
+ * @example
172
+ * const result = await client.graphql<{ lastMessages: Message[] }>(
173
+ * `query trace($spaceId: String!) { trace(spaceId: $spaceId) }`,
174
+ * { spaceId: 'abc123' }
175
+ * );
176
+ */
177
+ graphql<T>(query: string, variables?: Record<string, unknown>): Promise<T>;
178
+ private registerSpace;
179
+ private unregisterSpace;
180
+ /**
181
+ * Handle a client-level event from the subscription.
182
+ * @internal
183
+ */
184
+ private handleClientEvent;
185
+ /**
186
+ * Load storage cache from auth provider.
187
+ * @internal
188
+ */
189
+ private loadStorageCache;
190
+ /**
191
+ * Save storage cache via auth provider.
192
+ * @internal
193
+ */
194
+ private saveStorageCache;
195
+ /**
196
+ * Handle a user storage change from SSE (remote update).
197
+ * Updates cache and emits event if value actually changed.
198
+ * @internal
199
+ */
200
+ private handleUserStorageChanged;
201
+ }
202
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
package/dist/client.js ADDED
@@ -0,0 +1,472 @@
1
+ // =============================================================================
2
+ // Rool Client
3
+ // =============================================================================
4
+ import { EventEmitter } from './event-emitter.js';
5
+ import { AuthManager } from './auth.js';
6
+ import { GraphQLClient } from './graphql.js';
7
+ import { ClientSubscriptionManager } from './subscription.js';
8
+ import { MediaClient } from './media.js';
9
+ import { AppsClient } from './apps.js';
10
+ import { RoolSpace, generateEntityId } from './space.js';
11
+ /**
12
+ * Rool Client - Manages authentication, space lifecycle, and shared infrastructure.
13
+ *
14
+ * The client is lightweight - most operations happen on Space instances
15
+ *
16
+ * Features:
17
+ * - Authentication (login, logout, token management)
18
+ * - Spaces lifecycle (list, open, create, delete)
19
+ * - Client-level subscription for lifecycle events
20
+ * - Media operations
21
+ * - AI operations (prompt, image generation)
22
+ */
23
+ export class RoolClient extends EventEmitter {
24
+ urls;
25
+ authManager;
26
+ graphqlClient;
27
+ subscriptionManager = null;
28
+ // Registry of open spaces (for cleanup on logout/destroy)
29
+ openSpaces = new Map();
30
+ // User storage cache (synced to localStorage)
31
+ _storageCache = {};
32
+ constructor(config) {
33
+ super();
34
+ const baseUrl = config.baseUrl.replace(/\/+$/, ''); // Remove trailing slashes
35
+ this.urls = {
36
+ graphql: config.graphqlUrl ?? `${baseUrl}/graphql`,
37
+ media: config.mediaUrl ?? `${baseUrl}/media`,
38
+ auth: config.authUrl ?? `${baseUrl}/auth`,
39
+ apps: `${baseUrl}/apps`,
40
+ };
41
+ this.authManager = new AuthManager({
42
+ authUrl: this.urls.auth,
43
+ authProvider: config.authProvider,
44
+ onAuthStateChanged: (authenticated) => {
45
+ this.emit('authStateChanged', authenticated);
46
+ },
47
+ });
48
+ this.graphqlClient = new GraphQLClient({
49
+ graphqlUrl: this.urls.graphql,
50
+ authManager: this.authManager,
51
+ });
52
+ // Load storage cache from localStorage
53
+ this.loadStorageCache();
54
+ }
55
+ // ===========================================================================
56
+ // Initialization
57
+ // ===========================================================================
58
+ /**
59
+ * Initialize the client - should be called on app startup.
60
+ * Processes any auth callback in the URL, sets up auto-refresh,
61
+ * and starts real-time event subscription if authenticated.
62
+ * @returns true if an auth callback was processed
63
+ */
64
+ initialize() {
65
+ const result = this.authManager.initialize();
66
+ // Auto-subscribe to real-time events if authenticated (fire-and-forget)
67
+ void this.authManager.isAuthenticated().then(authenticated => {
68
+ if (authenticated) {
69
+ void this.ensureSubscribed();
70
+ }
71
+ });
72
+ return result;
73
+ }
74
+ /**
75
+ * Clean up resources - call when destroying the client.
76
+ */
77
+ destroy() {
78
+ this.authManager.destroy();
79
+ this.subscriptionManager?.destroy();
80
+ // Close all open spaces
81
+ for (const space of this.openSpaces.values()) {
82
+ space.close();
83
+ }
84
+ this.openSpaces.clear();
85
+ this.removeAllListeners();
86
+ }
87
+ // ===========================================================================
88
+ // Authentication
89
+ // ===========================================================================
90
+ /**
91
+ * Initiate login by redirecting to auth page.
92
+ * @param appName - The name of the application requesting login (displayed on auth page)
93
+ */
94
+ async login(appName) {
95
+ return this.authManager.login(appName);
96
+ }
97
+ /**
98
+ * Logout - clear all tokens and state.
99
+ */
100
+ logout() {
101
+ this.authManager.logout();
102
+ this.unsubscribe();
103
+ // Close all open spaces
104
+ for (const space of this.openSpaces.values()) {
105
+ space.close();
106
+ }
107
+ this.openSpaces.clear();
108
+ }
109
+ /**
110
+ * Process auth callback from URL fragment.
111
+ * Called automatically by initialize(), but can be called manually.
112
+ */
113
+ processAuthCallback() {
114
+ return this.authManager.processCallback();
115
+ }
116
+ /**
117
+ * Check if user is currently authenticated (validates token is usable).
118
+ */
119
+ async isAuthenticated() {
120
+ return this.authManager.isAuthenticated();
121
+ }
122
+ /**
123
+ * Get current access token.
124
+ * Returns undefined if not authenticated.
125
+ */
126
+ async getToken() {
127
+ return this.authManager.getToken();
128
+ }
129
+ /**
130
+ * Get auth identity decoded from JWT token.
131
+ * For the Rool user (with server-assigned id), use getCurrentUser().
132
+ */
133
+ getAuthUser() {
134
+ return this.authManager.getAuthUser();
135
+ }
136
+ // ===========================================================================
137
+ // Space Lifecycle
138
+ // ===========================================================================
139
+ /**
140
+ * List all spaces accessible to the user.
141
+ */
142
+ async listSpaces() {
143
+ return this.graphqlClient.listSpaces();
144
+ }
145
+ /**
146
+ * Open an existing space.
147
+ * Loads the space data from the server and returns a Space instance.
148
+ * The space manages its own real-time subscription.
149
+ *
150
+ * @param spaceId - The ID of the space to open
151
+ * @param options.conversationId - Optional conversation ID for AI context continuity. If not provided, a new conversation is created.
152
+ */
153
+ async openSpace(spaceId, options) {
154
+ // Ensure client subscription is active (for lifecycle events)
155
+ void this.ensureSubscribed();
156
+ const { data, name, role, userId } = await this.graphqlClient.getSpace(spaceId);
157
+ const space = new RoolSpace({
158
+ id: spaceId,
159
+ name,
160
+ role: role,
161
+ userId,
162
+ initialData: data,
163
+ conversationId: options?.conversationId,
164
+ graphqlClient: this.graphqlClient,
165
+ mediaClient: this.mediaClient,
166
+ graphqlUrl: this.urls.graphql,
167
+ authManager: this.authManager,
168
+ onClose: (id) => this.unregisterSpace(id),
169
+ });
170
+ // Register for cleanup
171
+ this.registerSpace(spaceId, space);
172
+ return space;
173
+ }
174
+ /**
175
+ * Create a new space.
176
+ * Creates on server and returns a Space instance.
177
+ * The space manages its own real-time subscription.
178
+ *
179
+ * @param name - Optional name for the space
180
+ * @param options.conversationId - Optional conversation ID for AI context continuity. If not provided, a new conversation is created.
181
+ */
182
+ async createSpace(name, options) {
183
+ // Ensure client subscription is active (for lifecycle events)
184
+ void this.ensureSubscribed();
185
+ const spaceId = generateEntityId();
186
+ const spaceName = name ?? spaceId;
187
+ // Create on server with name (use a temporary conversationId for lifecycle ops)
188
+ await this.graphqlClient.createSpace(spaceId, spaceName, generateEntityId());
189
+ // Fetch the created space to get userId (server assigns it)
190
+ const { data, userId } = await this.graphqlClient.getSpace(spaceId);
191
+ const space = new RoolSpace({
192
+ id: spaceId,
193
+ name: spaceName,
194
+ role: 'owner',
195
+ userId,
196
+ initialData: data,
197
+ conversationId: options?.conversationId,
198
+ graphqlClient: this.graphqlClient,
199
+ mediaClient: this.mediaClient,
200
+ graphqlUrl: this.urls.graphql,
201
+ authManager: this.authManager,
202
+ onClose: (id) => this.unregisterSpace(id),
203
+ });
204
+ // Register for cleanup
205
+ this.registerSpace(spaceId, space);
206
+ return space;
207
+ }
208
+ /**
209
+ * Delete a space.
210
+ * Note: This does not affect any open Space instances - they become stale.
211
+ */
212
+ async deleteSpace(spaceId) {
213
+ // Use a temporary conversationId for lifecycle ops
214
+ await this.graphqlClient.deleteSpace(spaceId, generateEntityId());
215
+ // Client-level event will be emitted via SSE subscription
216
+ }
217
+ // ===========================================================================
218
+ // User Operations
219
+ // ===========================================================================
220
+ /**
221
+ * Get the current Rool user from the server.
222
+ * Returns the user's server-assigned id, email, plan, and credits.
223
+ */
224
+ async getCurrentUser() {
225
+ return this.graphqlClient.getAccount();
226
+ }
227
+ /**
228
+ * Search for a user by email.
229
+ */
230
+ async searchUser(email) {
231
+ return this.graphqlClient.searchUser(email);
232
+ }
233
+ /**
234
+ * Set the current user's slug (used in app publishing URLs).
235
+ * Slug must be 3-32 characters, start with a letter, and contain only
236
+ * lowercase letters, numbers, hyphens, and underscores.
237
+ * Cannot be changed if the user has published apps.
238
+ */
239
+ async setSlug(slug) {
240
+ return this.graphqlClient.setSlug(slug);
241
+ }
242
+ // ===========================================================================
243
+ // App Publishing
244
+ // ===========================================================================
245
+ /**
246
+ * Publish an app. The app will be accessible at:
247
+ * https://rool.app/{user_slug}/{appId}/
248
+ *
249
+ * @param appId - URL-safe identifier (alphanumeric, hyphens, underscores)
250
+ * @param options - App name, bundle (zip file), and optional SPA flag
251
+ */
252
+ async publishApp(appId, options) {
253
+ return this.appsClient.publish(appId, options);
254
+ }
255
+ /**
256
+ * Unpublish an app.
257
+ */
258
+ async unpublishApp(appId) {
259
+ return this.appsClient.unpublish(appId);
260
+ }
261
+ /**
262
+ * List all published apps for the current user.
263
+ */
264
+ async listApps() {
265
+ return this.appsClient.list();
266
+ }
267
+ /**
268
+ * Get info for a specific published app.
269
+ * Returns null if the app doesn't exist.
270
+ */
271
+ async getAppInfo(appId) {
272
+ return this.appsClient.get(appId);
273
+ }
274
+ // ===========================================================================
275
+ // User Storage (server-side localStorage equivalent)
276
+ // ===========================================================================
277
+ /**
278
+ * Get a value from user storage (sync read from local cache).
279
+ * Returns undefined if key doesn't exist.
280
+ */
281
+ getUserStorage(key) {
282
+ return this._storageCache[key];
283
+ }
284
+ /**
285
+ * Set a value in user storage.
286
+ * Updates local cache immediately, then syncs to server.
287
+ * Pass undefined/null to delete the key.
288
+ * Storage is limited to 10MB total.
289
+ */
290
+ setUserStorage(key, value) {
291
+ // Update local cache
292
+ if (value === null || value === undefined) {
293
+ delete this._storageCache[key];
294
+ }
295
+ else {
296
+ this._storageCache[key] = value;
297
+ }
298
+ // Persist to localStorage
299
+ this.saveStorageCache();
300
+ // Emit event (local source)
301
+ this.emit('userStorageChanged', { key, value: value ?? null, source: 'local' });
302
+ // Fire-and-forget server sync
303
+ this.graphqlClient.setUserStorage(key, value).catch((error) => {
304
+ console.error('[RoolClient] Failed to sync user storage:', error);
305
+ this.emit('error', error instanceof Error ? error : new Error(String(error)), 'userStorage');
306
+ });
307
+ }
308
+ /**
309
+ * Get all user storage data (sync read from local cache).
310
+ */
311
+ getAllUserStorage() {
312
+ return { ...this._storageCache };
313
+ }
314
+ // ===========================================================================
315
+ // Media Client (used internally by Space instances)
316
+ // ===========================================================================
317
+ get mediaClient() {
318
+ return new MediaClient({
319
+ mediaUrl: this.urls.media,
320
+ backendOrigin: new URL(this.urls.media).origin,
321
+ authManager: this.authManager,
322
+ });
323
+ }
324
+ // ===========================================================================
325
+ // Apps Client (used for app publishing)
326
+ // ===========================================================================
327
+ get appsClient() {
328
+ return new AppsClient({
329
+ appsUrl: this.urls.apps,
330
+ authManager: this.authManager,
331
+ });
332
+ }
333
+ // ===========================================================================
334
+ // Subscriptions (internal - auto-managed)
335
+ // ===========================================================================
336
+ /**
337
+ * Ensure the client-level event subscription is active.
338
+ * Called automatically when opening spaces.
339
+ * Also fetches and caches the current user ID.
340
+ * @internal
341
+ */
342
+ async ensureSubscribed() {
343
+ if (this.subscriptionManager)
344
+ return;
345
+ this.subscriptionManager = new ClientSubscriptionManager({
346
+ graphqlUrl: this.urls.graphql,
347
+ authManager: this.authManager,
348
+ onEvent: (event) => this.handleClientEvent(event),
349
+ onConnectionStateChanged: (state) => {
350
+ this.emit('connectionStateChanged', state);
351
+ },
352
+ onError: (error) => {
353
+ this.emit('error', error, 'subscription');
354
+ },
355
+ });
356
+ await this.subscriptionManager.subscribe();
357
+ }
358
+ /**
359
+ * Disconnect from real-time events.
360
+ * @internal
361
+ */
362
+ unsubscribe() {
363
+ if (this.subscriptionManager) {
364
+ this.subscriptionManager.destroy();
365
+ this.subscriptionManager = null;
366
+ }
367
+ }
368
+ // ===========================================================================
369
+ // Utilities
370
+ // ===========================================================================
371
+ /**
372
+ * Generate a unique entity ID.
373
+ * 6-character alphanumeric string (62^6 = 56.8 billion possible values).
374
+ * Also available as top-level generateEntityId() export.
375
+ */
376
+ static generateId() {
377
+ return generateEntityId();
378
+ }
379
+ /**
380
+ * Execute an arbitrary GraphQL query or mutation.
381
+ * Use this escape hatch for app-specific operations not covered by the typed API.
382
+ *
383
+ * @example
384
+ * const result = await client.graphql<{ lastMessages: Message[] }>(
385
+ * `query trace($spaceId: String!) { trace(spaceId: $spaceId) }`,
386
+ * { spaceId: 'abc123' }
387
+ * );
388
+ */
389
+ async graphql(query, variables) {
390
+ return this.graphqlClient.query(query, variables);
391
+ }
392
+ // ===========================================================================
393
+ // Private Methods - Space Registry
394
+ // ===========================================================================
395
+ registerSpace(spaceId, space) {
396
+ this.openSpaces.set(spaceId, space);
397
+ }
398
+ unregisterSpace(spaceId) {
399
+ this.openSpaces.delete(spaceId);
400
+ }
401
+ // ===========================================================================
402
+ // Private Methods - Event Handling
403
+ // ===========================================================================
404
+ /**
405
+ * Handle a client-level event from the subscription.
406
+ * @internal
407
+ */
408
+ handleClientEvent(event) {
409
+ switch (event.type) {
410
+ case 'space_created':
411
+ this.emit('spaceCreated', {
412
+ id: event.spaceId,
413
+ name: event.name ?? event.spaceId,
414
+ role: event.role ?? 'owner',
415
+ ownerId: event.ownerId ?? '',
416
+ size: event.size ?? 0,
417
+ createdAt: event.createdAt ?? new Date().toISOString(),
418
+ updatedAt: event.updatedAt ?? new Date().toISOString(),
419
+ });
420
+ break;
421
+ case 'space_deleted':
422
+ this.emit('spaceDeleted', event.spaceId);
423
+ break;
424
+ case 'space_renamed':
425
+ this.emit('spaceRenamed', event.spaceId, event.name ?? event.spaceId);
426
+ break;
427
+ case 'user_storage_changed':
428
+ this.handleUserStorageChanged(event.key, event.value);
429
+ break;
430
+ }
431
+ }
432
+ // ===========================================================================
433
+ // Private Methods - User Storage Cache
434
+ // ===========================================================================
435
+ /**
436
+ * Load storage cache from auth provider.
437
+ * @internal
438
+ */
439
+ loadStorageCache() {
440
+ const cached = this.authManager.getStorage();
441
+ if (cached) {
442
+ this._storageCache = cached;
443
+ }
444
+ }
445
+ /**
446
+ * Save storage cache via auth provider.
447
+ * @internal
448
+ */
449
+ saveStorageCache() {
450
+ this.authManager.setStorage(this._storageCache);
451
+ }
452
+ /**
453
+ * Handle a user storage change from SSE (remote update).
454
+ * Updates cache and emits event if value actually changed.
455
+ * @internal
456
+ */
457
+ handleUserStorageChanged(key, value) {
458
+ const currentValue = this._storageCache[key];
459
+ // Only update and emit if value actually changed
460
+ if (JSON.stringify(currentValue) !== JSON.stringify(value)) {
461
+ if (value === null || value === undefined) {
462
+ delete this._storageCache[key];
463
+ }
464
+ else {
465
+ this._storageCache[key] = value;
466
+ }
467
+ this.saveStorageCache();
468
+ this.emit('userStorageChanged', { key, value: value ?? null, source: 'remote' });
469
+ }
470
+ }
471
+ }
472
+ //# sourceMappingURL=client.js.map