@hypen-space/core 0.2.12 → 0.3.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.
- package/README.md +182 -11
- package/dist/src/app.js +470 -44
- package/dist/src/app.js.map +7 -5
- package/dist/src/components/builtin.js +470 -44
- package/dist/src/components/builtin.js.map +7 -5
- package/dist/src/discovery.js +559 -65
- package/dist/src/discovery.js.map +8 -6
- package/dist/src/engine.js +18 -9
- package/dist/src/engine.js.map +3 -3
- package/dist/src/index.browser.js +862 -81
- package/dist/src/index.browser.js.map +10 -6
- package/dist/src/index.js +1590 -124
- package/dist/src/index.js.map +16 -9
- package/dist/src/remote/client.js +525 -35
- package/dist/src/remote/client.js.map +7 -4
- package/dist/src/remote/index.js +1796 -35
- package/dist/src/remote/index.js.map +13 -4
- package/dist/src/router.js +55 -29
- package/dist/src/router.js.map +3 -3
- package/dist/src/state.js +57 -29
- package/dist/src/state.js.map +3 -3
- package/package.json +8 -2
- package/src/app.ts +292 -13
- package/src/discovery.ts +123 -18
- package/src/disposable.ts +281 -0
- package/src/engine.ts +29 -10
- package/src/hypen.ts +209 -0
- package/src/index.ts +147 -11
- package/src/logger.ts +338 -0
- package/src/remote/client.ts +263 -56
- package/src/remote/index.ts +25 -1
- package/src/remote/server.ts +652 -0
- package/src/remote/session.ts +256 -0
- package/src/remote/types.ts +68 -1
- package/src/result.ts +260 -0
- package/src/retry.ts +306 -0
- package/src/state.ts +103 -45
- package/wasm-browser/README.md +4 -0
- package/wasm-browser/hypen_engine_bg.wasm +0 -0
- package/wasm-browser/package.json +1 -1
- package/wasm-node/README.md +4 -0
- package/wasm-node/hypen_engine_bg.wasm +0 -0
- package/wasm-node/package.json +1 -1
- package/wasm-browser/hypen_engine_bg.js +0 -736
- package/wasm-node/hypen_engine_bg.js +0 -736
package/src/remote/client.ts
CHANGED
|
@@ -1,6 +1,30 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* RemoteEngine - Connect to a remote Hypen app over WebSocket
|
|
3
3
|
* Platform-agnostic client (uses standard WebSocket API)
|
|
4
|
+
*
|
|
5
|
+
* Usage:
|
|
6
|
+
* ```typescript
|
|
7
|
+
* const engine = new RemoteEngine("ws://localhost:3000", {
|
|
8
|
+
* session: {
|
|
9
|
+
* id: localStorage.getItem("sessionId") ?? undefined,
|
|
10
|
+
* props: { platform: "web", version: "1.0" }
|
|
11
|
+
* }
|
|
12
|
+
* });
|
|
13
|
+
*
|
|
14
|
+
* engine.onSessionEstablished(({ sessionId, isNew, isRestored }) => {
|
|
15
|
+
* localStorage.setItem("sessionId", sessionId);
|
|
16
|
+
* console.log(isRestored ? "Welcome back!" : "New session");
|
|
17
|
+
* });
|
|
18
|
+
*
|
|
19
|
+
* engine.onSessionExpired((reason) => {
|
|
20
|
+
* localStorage.removeItem("sessionId");
|
|
21
|
+
* });
|
|
22
|
+
*
|
|
23
|
+
* const result = await engine.connect();
|
|
24
|
+
* if (!result.ok) {
|
|
25
|
+
* console.error("Connection failed:", result.error);
|
|
26
|
+
* }
|
|
27
|
+
* ```
|
|
4
28
|
*/
|
|
5
29
|
|
|
6
30
|
import type {
|
|
@@ -8,37 +32,95 @@ import type {
|
|
|
8
32
|
InitialTreeMessage,
|
|
9
33
|
PatchMessage,
|
|
10
34
|
DispatchActionMessage,
|
|
35
|
+
HelloMessage,
|
|
36
|
+
SessionAckMessage,
|
|
37
|
+
SessionExpiredMessage,
|
|
11
38
|
} from "./types.js";
|
|
12
39
|
import type { Patch } from "../engine.js";
|
|
40
|
+
import {
|
|
41
|
+
type Result,
|
|
42
|
+
Ok,
|
|
43
|
+
Err,
|
|
44
|
+
ConnectionError,
|
|
45
|
+
} from "../result.js";
|
|
46
|
+
import {
|
|
47
|
+
type Disposable,
|
|
48
|
+
DisposableStack,
|
|
49
|
+
disposableTimeout,
|
|
50
|
+
disposableWebSocket,
|
|
51
|
+
disposableListener,
|
|
52
|
+
} from "../disposable.js";
|
|
53
|
+
import { retry, type RetryOptions } from "../retry.js";
|
|
54
|
+
|
|
55
|
+
export type RemoteConnectionState =
|
|
56
|
+
| "disconnected"
|
|
57
|
+
| "connecting"
|
|
58
|
+
| "connected"
|
|
59
|
+
| "error";
|
|
13
60
|
|
|
14
|
-
|
|
61
|
+
/**
|
|
62
|
+
* Session configuration for the client
|
|
63
|
+
*/
|
|
64
|
+
export interface SessionOptions {
|
|
65
|
+
/** Session ID to resume (omit for new session) */
|
|
66
|
+
id?: string;
|
|
67
|
+
/** Client metadata (platform, version, userId, etc.) */
|
|
68
|
+
props?: Record<string, unknown>;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Session information received from server
|
|
73
|
+
*/
|
|
74
|
+
export interface SessionInfo {
|
|
75
|
+
sessionId: string;
|
|
76
|
+
isNew: boolean;
|
|
77
|
+
isRestored: boolean;
|
|
78
|
+
}
|
|
15
79
|
|
|
16
80
|
export interface RemoteEngineOptions {
|
|
17
81
|
autoReconnect?: boolean;
|
|
18
82
|
reconnectInterval?: number;
|
|
19
83
|
maxReconnectAttempts?: number;
|
|
84
|
+
/** Session configuration */
|
|
85
|
+
session?: SessionOptions;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
interface RequiredOptions {
|
|
89
|
+
autoReconnect: boolean;
|
|
90
|
+
reconnectInterval: number;
|
|
91
|
+
maxReconnectAttempts: number;
|
|
92
|
+
session?: SessionOptions;
|
|
20
93
|
}
|
|
21
94
|
|
|
22
95
|
/**
|
|
23
96
|
* Client-side engine that connects to a remote Hypen app
|
|
24
97
|
*/
|
|
25
|
-
export class RemoteEngine {
|
|
98
|
+
export class RemoteEngine implements Disposable {
|
|
26
99
|
private ws: WebSocket | null = null;
|
|
27
|
-
private url: string;
|
|
100
|
+
private readonly url: string;
|
|
28
101
|
private state: RemoteConnectionState = "disconnected";
|
|
29
|
-
private options:
|
|
102
|
+
private readonly options: RequiredOptions;
|
|
30
103
|
private reconnectAttempts = 0;
|
|
31
|
-
|
|
104
|
+
|
|
105
|
+
// Resource management
|
|
106
|
+
private readonly disposables = new DisposableStack();
|
|
107
|
+
private reconnectDisposable: Disposable | null = null;
|
|
108
|
+
|
|
109
|
+
// Session state
|
|
110
|
+
private currentSessionId: string | null = null;
|
|
111
|
+
private readonly sessionOptions?: SessionOptions;
|
|
32
112
|
|
|
33
113
|
// Callbacks
|
|
34
|
-
private patchCallbacks: Array<(patches: Patch[]) => void> = [];
|
|
35
|
-
private stateCallbacks: Array<(state:
|
|
36
|
-
private connectionCallbacks: Array<() => void> = [];
|
|
37
|
-
private disconnectionCallbacks: Array<() => void> = [];
|
|
38
|
-
private errorCallbacks: Array<(error: Error) => void> = [];
|
|
114
|
+
private readonly patchCallbacks: Array<(patches: Patch[]) => void> = [];
|
|
115
|
+
private readonly stateCallbacks: Array<(state: unknown) => void> = [];
|
|
116
|
+
private readonly connectionCallbacks: Array<() => void> = [];
|
|
117
|
+
private readonly disconnectionCallbacks: Array<() => void> = [];
|
|
118
|
+
private readonly errorCallbacks: Array<(error: Error) => void> = [];
|
|
119
|
+
private readonly sessionEstablishedCallbacks: Array<(info: SessionInfo) => void> = [];
|
|
120
|
+
private readonly sessionExpiredCallbacks: Array<(reason: string) => void> = [];
|
|
39
121
|
|
|
40
122
|
// State
|
|
41
|
-
private currentState:
|
|
123
|
+
private currentState: unknown = null;
|
|
42
124
|
private currentRevision = 0;
|
|
43
125
|
private moduleName: string = "";
|
|
44
126
|
|
|
@@ -48,74 +130,136 @@ export class RemoteEngine {
|
|
|
48
130
|
autoReconnect: options.autoReconnect ?? true,
|
|
49
131
|
reconnectInterval: options.reconnectInterval ?? 3000,
|
|
50
132
|
maxReconnectAttempts: options.maxReconnectAttempts ?? 10,
|
|
133
|
+
session: options.session,
|
|
51
134
|
};
|
|
135
|
+
this.sessionOptions = options.session;
|
|
136
|
+
|
|
137
|
+
// If session ID was provided, use it as current
|
|
138
|
+
if (options.session?.id) {
|
|
139
|
+
this.currentSessionId = options.session.id;
|
|
140
|
+
}
|
|
52
141
|
}
|
|
53
142
|
|
|
54
143
|
/**
|
|
55
144
|
* Connect to the remote server
|
|
145
|
+
* Returns a Result indicating success or failure
|
|
56
146
|
*/
|
|
57
|
-
async connect(): Promise<void
|
|
147
|
+
async connect(): Promise<Result<void, ConnectionError>> {
|
|
58
148
|
if (this.state === "connected" || this.state === "connecting") {
|
|
59
|
-
return;
|
|
149
|
+
return Ok(undefined);
|
|
60
150
|
}
|
|
61
151
|
|
|
62
152
|
this.state = "connecting";
|
|
63
153
|
|
|
64
|
-
return new Promise((resolve
|
|
154
|
+
return new Promise((resolve) => {
|
|
65
155
|
try {
|
|
66
156
|
this.ws = new WebSocket(this.url);
|
|
67
157
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
this.reconnectAttempts = 0;
|
|
71
|
-
this.connectionCallbacks.forEach((cb) => cb());
|
|
72
|
-
resolve();
|
|
73
|
-
};
|
|
158
|
+
// Track the WebSocket for cleanup
|
|
159
|
+
this.disposables.add(disposableWebSocket(this.ws));
|
|
74
160
|
|
|
75
|
-
|
|
161
|
+
// Set up message handler
|
|
162
|
+
const messageHandler = (event: MessageEvent) => {
|
|
76
163
|
this.handleMessage(event.data);
|
|
77
164
|
};
|
|
165
|
+
this.disposables.add(
|
|
166
|
+
disposableListener(this.ws, "message", messageHandler as EventListener)
|
|
167
|
+
);
|
|
78
168
|
|
|
79
|
-
|
|
169
|
+
// Set up error handler
|
|
170
|
+
const errorHandler = () => {
|
|
80
171
|
this.state = "error";
|
|
81
|
-
const error = new Error("WebSocket error");
|
|
172
|
+
const error = new ConnectionError(this.url, new Error("WebSocket error"));
|
|
82
173
|
this.errorCallbacks.forEach((cb) => cb(error));
|
|
83
|
-
|
|
174
|
+
resolve(Err(error));
|
|
84
175
|
};
|
|
176
|
+
this.disposables.add(
|
|
177
|
+
disposableListener(this.ws, "error", errorHandler)
|
|
178
|
+
);
|
|
85
179
|
|
|
86
|
-
|
|
180
|
+
// Set up close handler
|
|
181
|
+
const closeHandler = () => {
|
|
87
182
|
this.state = "disconnected";
|
|
88
183
|
this.disconnectionCallbacks.forEach((cb) => cb());
|
|
89
184
|
this.attemptReconnect();
|
|
90
185
|
};
|
|
91
|
-
|
|
186
|
+
this.disposables.add(
|
|
187
|
+
disposableListener(this.ws, "close", closeHandler)
|
|
188
|
+
);
|
|
189
|
+
|
|
190
|
+
// Set up open handler
|
|
191
|
+
this.ws.onopen = () => {
|
|
192
|
+
this.state = "connected";
|
|
193
|
+
this.reconnectAttempts = 0;
|
|
194
|
+
|
|
195
|
+
// Cancel any pending reconnect
|
|
196
|
+
if (this.reconnectDisposable) {
|
|
197
|
+
this.reconnectDisposable.dispose();
|
|
198
|
+
this.reconnectDisposable = null;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// Send hello message with session info
|
|
202
|
+
this.sendHello();
|
|
203
|
+
|
|
204
|
+
this.connectionCallbacks.forEach((cb) => cb());
|
|
205
|
+
resolve(Ok(undefined));
|
|
206
|
+
};
|
|
207
|
+
} catch (e) {
|
|
92
208
|
this.state = "error";
|
|
93
|
-
|
|
209
|
+
const error = new ConnectionError(this.url, e);
|
|
210
|
+
resolve(Err(error));
|
|
94
211
|
}
|
|
95
212
|
});
|
|
96
213
|
}
|
|
97
214
|
|
|
98
215
|
/**
|
|
99
|
-
*
|
|
216
|
+
* Send hello message to establish session
|
|
217
|
+
*/
|
|
218
|
+
private sendHello(): void {
|
|
219
|
+
if (!this.ws || this.ws.readyState !== WebSocket.OPEN) return;
|
|
220
|
+
|
|
221
|
+
const hello: HelloMessage = {
|
|
222
|
+
type: "hello",
|
|
223
|
+
sessionId: this.currentSessionId ?? this.sessionOptions?.id,
|
|
224
|
+
props: this.sessionOptions?.props,
|
|
225
|
+
};
|
|
226
|
+
|
|
227
|
+
this.ws.send(JSON.stringify(hello));
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Disconnect from the remote server and clean up resources
|
|
100
232
|
*/
|
|
101
233
|
disconnect(): void {
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
this.
|
|
234
|
+
// Cancel any pending reconnect
|
|
235
|
+
if (this.reconnectDisposable) {
|
|
236
|
+
this.reconnectDisposable.dispose();
|
|
237
|
+
this.reconnectDisposable = null;
|
|
105
238
|
}
|
|
106
239
|
|
|
240
|
+
// Close WebSocket if open
|
|
107
241
|
if (this.ws) {
|
|
108
|
-
this.ws.
|
|
242
|
+
if (this.ws.readyState === WebSocket.OPEN || this.ws.readyState === WebSocket.CONNECTING) {
|
|
243
|
+
this.ws.close();
|
|
244
|
+
}
|
|
109
245
|
this.ws = null;
|
|
110
246
|
}
|
|
111
247
|
|
|
112
248
|
this.state = "disconnected";
|
|
113
249
|
}
|
|
114
250
|
|
|
251
|
+
/**
|
|
252
|
+
* Dispose all resources (alias for disconnect)
|
|
253
|
+
*/
|
|
254
|
+
dispose(): void {
|
|
255
|
+
this.disconnect();
|
|
256
|
+
this.disposables.dispose();
|
|
257
|
+
}
|
|
258
|
+
|
|
115
259
|
/**
|
|
116
260
|
* Dispatch an action to the remote server
|
|
117
261
|
*/
|
|
118
|
-
dispatchAction(action: string, payload?:
|
|
262
|
+
dispatchAction(action: string, payload?: unknown): void {
|
|
119
263
|
if (this.state !== "connected" || !this.ws) {
|
|
120
264
|
console.warn("Cannot dispatch action: not connected");
|
|
121
265
|
return;
|
|
@@ -142,7 +286,7 @@ export class RemoteEngine {
|
|
|
142
286
|
/**
|
|
143
287
|
* Register callback for state updates
|
|
144
288
|
*/
|
|
145
|
-
onStateUpdate(callback: (state:
|
|
289
|
+
onStateUpdate(callback: (state: unknown) => void): this {
|
|
146
290
|
this.stateCallbacks.push(callback);
|
|
147
291
|
return this;
|
|
148
292
|
}
|
|
@@ -171,6 +315,24 @@ export class RemoteEngine {
|
|
|
171
315
|
return this;
|
|
172
316
|
}
|
|
173
317
|
|
|
318
|
+
/**
|
|
319
|
+
* Register callback for session establishment
|
|
320
|
+
* Called when server confirms session (new or resumed)
|
|
321
|
+
*/
|
|
322
|
+
onSessionEstablished(callback: (info: SessionInfo) => void): this {
|
|
323
|
+
this.sessionEstablishedCallbacks.push(callback);
|
|
324
|
+
return this;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
/**
|
|
328
|
+
* Register callback for session expiration
|
|
329
|
+
* Called when session is kicked or expires
|
|
330
|
+
*/
|
|
331
|
+
onSessionExpired(callback: (reason: string) => void): this {
|
|
332
|
+
this.sessionExpiredCallbacks.push(callback);
|
|
333
|
+
return this;
|
|
334
|
+
}
|
|
335
|
+
|
|
174
336
|
/**
|
|
175
337
|
* Get current connection state
|
|
176
338
|
*/
|
|
@@ -181,7 +343,7 @@ export class RemoteEngine {
|
|
|
181
343
|
/**
|
|
182
344
|
* Get current app state
|
|
183
345
|
*/
|
|
184
|
-
getCurrentState():
|
|
346
|
+
getCurrentState(): unknown {
|
|
185
347
|
return this.currentState;
|
|
186
348
|
}
|
|
187
349
|
|
|
@@ -192,11 +354,26 @@ export class RemoteEngine {
|
|
|
192
354
|
return this.currentRevision;
|
|
193
355
|
}
|
|
194
356
|
|
|
357
|
+
/**
|
|
358
|
+
* Get current session ID
|
|
359
|
+
*/
|
|
360
|
+
getSessionId(): string | null {
|
|
361
|
+
return this.currentSessionId;
|
|
362
|
+
}
|
|
363
|
+
|
|
195
364
|
private handleMessage(data: string): void {
|
|
196
365
|
try {
|
|
197
366
|
const message = JSON.parse(data) as RemoteMessage;
|
|
198
367
|
|
|
199
368
|
switch (message.type) {
|
|
369
|
+
case "sessionAck":
|
|
370
|
+
this.handleSessionAck(message as SessionAckMessage);
|
|
371
|
+
break;
|
|
372
|
+
|
|
373
|
+
case "sessionExpired":
|
|
374
|
+
this.handleSessionExpired(message as SessionExpiredMessage);
|
|
375
|
+
break;
|
|
376
|
+
|
|
200
377
|
case "initialTree":
|
|
201
378
|
this.handleInitialTree(message as InitialTreeMessage);
|
|
202
379
|
break;
|
|
@@ -206,18 +383,34 @@ export class RemoteEngine {
|
|
|
206
383
|
break;
|
|
207
384
|
|
|
208
385
|
case "stateUpdate":
|
|
209
|
-
this.currentState = message.state;
|
|
210
|
-
this.stateCallbacks.forEach((cb) => cb(
|
|
386
|
+
this.currentState = (message as { state: unknown }).state;
|
|
387
|
+
this.stateCallbacks.forEach((cb) => cb(this.currentState));
|
|
211
388
|
break;
|
|
212
389
|
}
|
|
213
|
-
} catch (
|
|
214
|
-
console.error("Error handling remote message:",
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
);
|
|
390
|
+
} catch (e) {
|
|
391
|
+
console.error("Error handling remote message:", e);
|
|
392
|
+
const error = e instanceof Error ? e : new Error(String(e));
|
|
393
|
+
this.errorCallbacks.forEach((cb) => cb(error));
|
|
218
394
|
}
|
|
219
395
|
}
|
|
220
396
|
|
|
397
|
+
private handleSessionAck(message: SessionAckMessage): void {
|
|
398
|
+
this.currentSessionId = message.sessionId;
|
|
399
|
+
|
|
400
|
+
const info: SessionInfo = {
|
|
401
|
+
sessionId: message.sessionId,
|
|
402
|
+
isNew: message.isNew,
|
|
403
|
+
isRestored: message.isRestored,
|
|
404
|
+
};
|
|
405
|
+
|
|
406
|
+
this.sessionEstablishedCallbacks.forEach((cb) => cb(info));
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
private handleSessionExpired(message: SessionExpiredMessage): void {
|
|
410
|
+
this.currentSessionId = null;
|
|
411
|
+
this.sessionExpiredCallbacks.forEach((cb) => cb(message.reason));
|
|
412
|
+
}
|
|
413
|
+
|
|
221
414
|
private handleInitialTree(message: InitialTreeMessage): void {
|
|
222
415
|
this.moduleName = message.module;
|
|
223
416
|
this.currentState = message.state;
|
|
@@ -254,20 +447,34 @@ export class RemoteEngine {
|
|
|
254
447
|
return;
|
|
255
448
|
}
|
|
256
449
|
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
450
|
+
// Use disposable timeout to start reconnection after initial delay
|
|
451
|
+
this.reconnectDisposable = disposableTimeout(() => {
|
|
452
|
+
this.reconnectDisposable = null;
|
|
453
|
+
|
|
454
|
+
retry(
|
|
455
|
+
async () => {
|
|
456
|
+
const result = await this.connect();
|
|
457
|
+
if (!result.ok) {
|
|
458
|
+
throw result.error;
|
|
459
|
+
}
|
|
460
|
+
},
|
|
461
|
+
{
|
|
462
|
+
maxAttempts: this.options.maxReconnectAttempts,
|
|
463
|
+
delayMs: this.options.reconnectInterval,
|
|
464
|
+
backoff: "exponential",
|
|
465
|
+
maxDelayMs: 30000,
|
|
466
|
+
jitter: 0.1,
|
|
467
|
+
onRetry: (attempt, error) => {
|
|
468
|
+
console.log(
|
|
469
|
+
`Reconnection attempt ${attempt}/${this.options.maxReconnectAttempts} failed: ${error.message}`
|
|
470
|
+
);
|
|
471
|
+
},
|
|
472
|
+
}
|
|
473
|
+
).catch((error) => {
|
|
474
|
+
console.error("Max reconnection attempts reached:", error.message);
|
|
475
|
+
this.errorCallbacks.forEach((cb) =>
|
|
476
|
+
cb(new ConnectionError(this.url, error, this.options.maxReconnectAttempts))
|
|
477
|
+
);
|
|
271
478
|
});
|
|
272
479
|
}, this.options.reconnectInterval);
|
|
273
480
|
}
|
package/src/remote/index.ts
CHANGED
|
@@ -2,16 +2,40 @@
|
|
|
2
2
|
* Remote UI streaming for Hypen
|
|
3
3
|
*
|
|
4
4
|
* Client-side: Connect to remote Hypen apps
|
|
5
|
+
* Server-side: Stream Hypen apps over WebSocket
|
|
5
6
|
*/
|
|
6
7
|
|
|
8
|
+
// Client
|
|
7
9
|
export { RemoteEngine } from "./client.js";
|
|
10
|
+
export type {
|
|
11
|
+
RemoteConnectionState,
|
|
12
|
+
RemoteEngineOptions,
|
|
13
|
+
SessionOptions,
|
|
14
|
+
SessionInfo,
|
|
15
|
+
} from "./client.js";
|
|
16
|
+
|
|
17
|
+
// Server
|
|
18
|
+
export { RemoteServer, serve } from "./server.js";
|
|
19
|
+
|
|
20
|
+
// Session Management
|
|
21
|
+
export { SessionManager } from "./session.js";
|
|
22
|
+
export type { SessionExpireCallback } from "./session.js";
|
|
23
|
+
|
|
24
|
+
// Types
|
|
8
25
|
export type {
|
|
9
26
|
RemoteMessage,
|
|
10
27
|
InitialTreeMessage,
|
|
11
28
|
PatchMessage,
|
|
12
29
|
DispatchActionMessage,
|
|
13
30
|
StateUpdateMessage,
|
|
31
|
+
HelloMessage,
|
|
32
|
+
SessionAckMessage,
|
|
33
|
+
SessionExpiredMessage,
|
|
34
|
+
Session,
|
|
35
|
+
SessionConfig,
|
|
14
36
|
RemoteClient,
|
|
15
37
|
RemoteServerConfig,
|
|
16
38
|
} from "./types.js";
|
|
17
|
-
|
|
39
|
+
|
|
40
|
+
// Re-export Patch type from engine
|
|
41
|
+
export type { Patch } from "../engine.js";
|