loro-repo 0.5.3 → 0.7.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 +42 -1
- package/dist/index.cjs +511 -97
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +56 -6
- package/dist/index.d.ts +56 -6
- package/dist/index.js +511 -97
- package/dist/index.js.map +1 -1
- package/dist/storage/filesystem.cjs +17 -0
- package/dist/storage/filesystem.cjs.map +1 -1
- package/dist/storage/filesystem.d.cts +2 -1
- package/dist/storage/filesystem.d.ts +2 -1
- package/dist/storage/filesystem.js +17 -0
- package/dist/storage/filesystem.js.map +1 -1
- package/dist/storage/indexeddb.cjs +4 -0
- package/dist/storage/indexeddb.cjs.map +1 -1
- package/dist/storage/indexeddb.d.cts +2 -1
- package/dist/storage/indexeddb.d.ts +2 -1
- package/dist/storage/indexeddb.js +4 -0
- package/dist/storage/indexeddb.js.map +1 -1
- package/dist/transport/broadcast-channel.cjs +131 -1
- package/dist/transport/broadcast-channel.cjs.map +1 -1
- package/dist/transport/broadcast-channel.d.cts +20 -3
- package/dist/transport/broadcast-channel.d.ts +20 -3
- package/dist/transport/broadcast-channel.js +130 -1
- package/dist/transport/broadcast-channel.js.map +1 -1
- package/dist/transport/websocket.cjs +348 -24
- package/dist/transport/websocket.cjs.map +1 -1
- package/dist/transport/websocket.d.cts +47 -5
- package/dist/transport/websocket.d.ts +47 -5
- package/dist/transport/websocket.js +349 -24
- package/dist/transport/websocket.js.map +1 -1
- package/dist/types.d.cts +116 -4
- package/dist/types.d.ts +116 -4
- package/package.json +7 -7
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
const require_chunk = require('../chunk.cjs');
|
|
2
2
|
let loro_adaptors_loro = require("loro-adaptors/loro");
|
|
3
3
|
loro_adaptors_loro = require_chunk.__toESM(loro_adaptors_loro);
|
|
4
|
-
let loro_protocol = require("loro-protocol");
|
|
5
|
-
loro_protocol = require_chunk.__toESM(loro_protocol);
|
|
6
4
|
let loro_websocket = require("loro-websocket");
|
|
7
5
|
loro_websocket = require_chunk.__toESM(loro_websocket);
|
|
8
6
|
let loro_adaptors_flock = require("loro-adaptors/flock");
|
|
@@ -63,14 +61,28 @@ function withTimeout(promise, timeoutMs) {
|
|
|
63
61
|
});
|
|
64
62
|
});
|
|
65
63
|
}
|
|
66
|
-
function
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
}
|
|
71
|
-
|
|
64
|
+
function createStatusEmitter(initial = "connecting") {
|
|
65
|
+
return {
|
|
66
|
+
status: initial,
|
|
67
|
+
listeners: /* @__PURE__ */ new Set()
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
function emitStatus(emitter, status) {
|
|
71
|
+
if (!emitter) return;
|
|
72
|
+
emitter.status = status;
|
|
73
|
+
const listeners = Array.from(emitter.listeners);
|
|
74
|
+
for (const cb of listeners) try {
|
|
75
|
+
cb(status);
|
|
76
|
+
} catch (error) {
|
|
77
|
+
debug("status listener error", error);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
function mapClientStatusToRoom(status) {
|
|
81
|
+
switch (status) {
|
|
82
|
+
case "connected": return "joined";
|
|
83
|
+
case "connecting": return "reconnecting";
|
|
84
|
+
default: return "disconnected";
|
|
72
85
|
}
|
|
73
|
-
return fallback;
|
|
74
86
|
}
|
|
75
87
|
function bytesEqual(a, b) {
|
|
76
88
|
if (a === b) return true;
|
|
@@ -86,24 +98,85 @@ function bytesEqual(a, b) {
|
|
|
86
98
|
var WebSocketTransportAdapter = class {
|
|
87
99
|
options;
|
|
88
100
|
client;
|
|
101
|
+
clientListeners = [];
|
|
89
102
|
metadataSession;
|
|
90
103
|
docSessions = /* @__PURE__ */ new Map();
|
|
104
|
+
ephemeralSessions = /* @__PURE__ */ new Map();
|
|
91
105
|
constructor(options) {
|
|
92
106
|
this.options = options;
|
|
93
107
|
}
|
|
94
|
-
|
|
108
|
+
trackClientListener(unsubscribe) {
|
|
109
|
+
this.clientListeners.push(unsubscribe);
|
|
110
|
+
}
|
|
111
|
+
propagateClientStatus(status) {
|
|
112
|
+
const mapped = mapClientStatusToRoom(status);
|
|
113
|
+
const meta = this.metadataSession;
|
|
114
|
+
if (meta) this.dispatchRoomStatus("meta", { roomId: meta.roomId }, mapped, meta.statusEmitter, meta.statusListener);
|
|
115
|
+
for (const session of this.docSessions.values()) this.dispatchRoomStatus("doc", {
|
|
116
|
+
roomId: session.roomId,
|
|
117
|
+
docId: session.docId
|
|
118
|
+
}, mapped, session.statusEmitter, session.statusListener);
|
|
119
|
+
for (const session of this.ephemeralSessions.values()) this.dispatchRoomStatus("ephemeral", { roomId: session.roomId }, mapped, session.statusEmitter, void 0);
|
|
120
|
+
}
|
|
121
|
+
dispatchRoomStatus(kind, payload, status, emitter, listener) {
|
|
122
|
+
emitStatus(emitter, status);
|
|
123
|
+
try {
|
|
124
|
+
listener?.(status);
|
|
125
|
+
} catch (error) {
|
|
126
|
+
debug("room listener error", error);
|
|
127
|
+
}
|
|
128
|
+
try {
|
|
129
|
+
this.options.onRoomStatusChange?.({
|
|
130
|
+
kind,
|
|
131
|
+
...payload,
|
|
132
|
+
status
|
|
133
|
+
});
|
|
134
|
+
} catch (error) {
|
|
135
|
+
debug("global room status listener error", error);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
cleanupClientListeners() {
|
|
139
|
+
while (this.clientListeners.length > 0) {
|
|
140
|
+
const unsubscribe = this.clientListeners.pop();
|
|
141
|
+
try {
|
|
142
|
+
unsubscribe?.();
|
|
143
|
+
} catch {}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
async connect(options) {
|
|
95
147
|
const client = this.ensureClient();
|
|
96
|
-
debug("connect requested", {
|
|
148
|
+
debug("connect requested", {
|
|
149
|
+
status: client.getStatus(),
|
|
150
|
+
resetBackoff: Boolean(options?.resetBackoff)
|
|
151
|
+
});
|
|
97
152
|
try {
|
|
98
|
-
await client.connect();
|
|
153
|
+
await client.connect.call(client, options?.resetBackoff ? { resetBackoff: options.resetBackoff } : void 0);
|
|
99
154
|
debug("client.connect resolved");
|
|
100
|
-
await client.waitConnected();
|
|
155
|
+
await withTimeout(client.waitConnected(), options?.timeout);
|
|
101
156
|
debug("client.waitConnected resolved", { status: client.getStatus() });
|
|
102
157
|
} catch (error) {
|
|
103
158
|
debug("connect failed", error);
|
|
104
159
|
throw error;
|
|
105
160
|
}
|
|
106
161
|
}
|
|
162
|
+
async reconnect(options) {
|
|
163
|
+
const client = this.ensureClient();
|
|
164
|
+
debug("reconnect requested", {
|
|
165
|
+
status: client.getStatus(),
|
|
166
|
+
resetBackoff: Boolean(options?.resetBackoff)
|
|
167
|
+
});
|
|
168
|
+
try {
|
|
169
|
+
const connectFn = client.connect;
|
|
170
|
+
if (options?.resetBackoff) await connectFn.call(client, { resetBackoff: true });
|
|
171
|
+
else if (client.retryNow) await client.retryNow.call(client);
|
|
172
|
+
else await connectFn.call(client);
|
|
173
|
+
await withTimeout(client.waitConnected(), options?.timeout);
|
|
174
|
+
debug("reconnect completed", { status: client.getStatus() });
|
|
175
|
+
} catch (error) {
|
|
176
|
+
debug("reconnect failed", error);
|
|
177
|
+
throw error;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
107
180
|
async close() {
|
|
108
181
|
debug("close requested", {
|
|
109
182
|
docSessions: this.docSessions.size,
|
|
@@ -111,10 +184,13 @@ var WebSocketTransportAdapter = class {
|
|
|
111
184
|
});
|
|
112
185
|
for (const [docId] of this.docSessions) await this.leaveDocSession(docId).catch(() => {});
|
|
113
186
|
this.docSessions.clear();
|
|
187
|
+
for (const [roomId] of this.ephemeralSessions) await this.leaveEphemeralSession(roomId).catch(() => {});
|
|
188
|
+
this.ephemeralSessions.clear();
|
|
114
189
|
await this.teardownMetadataSession().catch(() => {});
|
|
115
190
|
if (this.client) {
|
|
116
191
|
const client = this.client;
|
|
117
192
|
this.client = void 0;
|
|
193
|
+
this.cleanupClientListeners();
|
|
118
194
|
client.destroy();
|
|
119
195
|
debug("websocket client destroyed");
|
|
120
196
|
}
|
|
@@ -123,6 +199,30 @@ var WebSocketTransportAdapter = class {
|
|
|
123
199
|
isConnected() {
|
|
124
200
|
return this.client?.getStatus() === "connected";
|
|
125
201
|
}
|
|
202
|
+
getStatus() {
|
|
203
|
+
return this.ensureClient().getStatus();
|
|
204
|
+
}
|
|
205
|
+
getLatency() {
|
|
206
|
+
return this.ensureClient().getLatency?.();
|
|
207
|
+
}
|
|
208
|
+
onStatusChange(listener) {
|
|
209
|
+
const unsubscribe = this.ensureClient().onStatusChange(listener);
|
|
210
|
+
this.trackClientListener(unsubscribe);
|
|
211
|
+
return () => {
|
|
212
|
+
unsubscribe();
|
|
213
|
+
const idx = this.clientListeners.indexOf(unsubscribe);
|
|
214
|
+
if (idx >= 0) this.clientListeners.splice(idx, 1);
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
onLatency(listener) {
|
|
218
|
+
const unsubscribe = this.ensureClient().onLatency(listener);
|
|
219
|
+
this.trackClientListener(unsubscribe);
|
|
220
|
+
return () => {
|
|
221
|
+
unsubscribe();
|
|
222
|
+
const idx = this.clientListeners.indexOf(unsubscribe);
|
|
223
|
+
if (idx >= 0) this.clientListeners.splice(idx, 1);
|
|
224
|
+
};
|
|
225
|
+
}
|
|
126
226
|
async syncMeta(flock, options) {
|
|
127
227
|
debug("syncMeta requested", { roomId: this.options.metadataRoomId });
|
|
128
228
|
try {
|
|
@@ -141,9 +241,8 @@ var WebSocketTransportAdapter = class {
|
|
|
141
241
|
}
|
|
142
242
|
}
|
|
143
243
|
joinMetaRoom(flock, params) {
|
|
144
|
-
const
|
|
145
|
-
|
|
146
|
-
if (!roomId) throw new Error("Metadata room id not configured");
|
|
244
|
+
const roomId = this.options.metadataRoomId ?? "repo:meta";
|
|
245
|
+
let statusEmitterRef;
|
|
147
246
|
const session = (async () => {
|
|
148
247
|
let auth;
|
|
149
248
|
const authWay = params?.auth ?? this.options.metadataAuth;
|
|
@@ -153,10 +252,13 @@ var WebSocketTransportAdapter = class {
|
|
|
153
252
|
roomId,
|
|
154
253
|
hasAuth: Boolean(auth && auth.length)
|
|
155
254
|
});
|
|
156
|
-
|
|
255
|
+
const ensure = this.ensureMetadataSession(flock, {
|
|
157
256
|
roomId,
|
|
158
|
-
auth
|
|
257
|
+
auth,
|
|
258
|
+
onStatusChange: params?.onStatusChange
|
|
159
259
|
});
|
|
260
|
+
statusEmitterRef = (await ensure).statusEmitter;
|
|
261
|
+
return ensure;
|
|
160
262
|
})();
|
|
161
263
|
const firstSynced = session.then((session$1) => session$1.firstSynced);
|
|
162
264
|
const getConnected = () => this.isConnected();
|
|
@@ -177,6 +279,31 @@ var WebSocketTransportAdapter = class {
|
|
|
177
279
|
firstSyncedWithRemote: firstSynced,
|
|
178
280
|
get connected() {
|
|
179
281
|
return getConnected();
|
|
282
|
+
},
|
|
283
|
+
get status() {
|
|
284
|
+
return statusEmitterRef?.status ?? "connecting";
|
|
285
|
+
},
|
|
286
|
+
onStatusChange: (listener) => {
|
|
287
|
+
const attach = (emitter) => {
|
|
288
|
+
emitter.listeners.add(listener);
|
|
289
|
+
try {
|
|
290
|
+
listener(emitter.status);
|
|
291
|
+
} catch (error) {
|
|
292
|
+
debug("metadata onStatusChange listener error", error);
|
|
293
|
+
}
|
|
294
|
+
return () => emitter.listeners.delete(listener);
|
|
295
|
+
};
|
|
296
|
+
if (statusEmitterRef) {
|
|
297
|
+
const cleanup = attach(statusEmitterRef);
|
|
298
|
+
return () => cleanup();
|
|
299
|
+
}
|
|
300
|
+
let unsubscribed = false;
|
|
301
|
+
const cleanupPromise = session.then((resolved) => attach(resolved.statusEmitter));
|
|
302
|
+
return () => {
|
|
303
|
+
if (unsubscribed) return;
|
|
304
|
+
unsubscribed = true;
|
|
305
|
+
cleanupPromise.then((cleanup) => cleanup()).catch(() => {});
|
|
306
|
+
};
|
|
180
307
|
}
|
|
181
308
|
};
|
|
182
309
|
session.then((session$1) => {
|
|
@@ -209,10 +336,13 @@ var WebSocketTransportAdapter = class {
|
|
|
209
336
|
joinDocRoom(docId, doc, params) {
|
|
210
337
|
debug("joinDocRoom requested", {
|
|
211
338
|
docId,
|
|
212
|
-
roomParamType: params?.roomId ? typeof params.roomId === "string" ? "string" : "uint8array" : void 0,
|
|
213
339
|
hasAuthOverride: Boolean(params?.auth && params.auth.length)
|
|
214
340
|
});
|
|
215
|
-
|
|
341
|
+
let statusEmitterRef;
|
|
342
|
+
const ensure = this.ensureDocSession(docId, doc, params ?? {}).then((session) => {
|
|
343
|
+
statusEmitterRef = session.statusEmitter;
|
|
344
|
+
return session;
|
|
345
|
+
});
|
|
216
346
|
const firstSynced = ensure.then((session) => session.firstSynced);
|
|
217
347
|
const getConnected = () => this.isConnected();
|
|
218
348
|
const subscription = {
|
|
@@ -230,6 +360,31 @@ var WebSocketTransportAdapter = class {
|
|
|
230
360
|
firstSyncedWithRemote: firstSynced,
|
|
231
361
|
get connected() {
|
|
232
362
|
return getConnected();
|
|
363
|
+
},
|
|
364
|
+
get status() {
|
|
365
|
+
return statusEmitterRef?.status ?? "connecting";
|
|
366
|
+
},
|
|
367
|
+
onStatusChange: (listener) => {
|
|
368
|
+
const attach = (emitter) => {
|
|
369
|
+
emitter.listeners.add(listener);
|
|
370
|
+
try {
|
|
371
|
+
listener(emitter.status);
|
|
372
|
+
} catch (error) {
|
|
373
|
+
debug("doc onStatusChange listener error", error);
|
|
374
|
+
}
|
|
375
|
+
return () => emitter.listeners.delete(listener);
|
|
376
|
+
};
|
|
377
|
+
if (statusEmitterRef) {
|
|
378
|
+
const cleanup = attach(statusEmitterRef);
|
|
379
|
+
return () => cleanup();
|
|
380
|
+
}
|
|
381
|
+
let unsubscribed = false;
|
|
382
|
+
const cleanupPromise = ensure.then((session) => attach(session.statusEmitter));
|
|
383
|
+
return () => {
|
|
384
|
+
if (unsubscribed) return;
|
|
385
|
+
unsubscribed = true;
|
|
386
|
+
cleanupPromise.then((cleanup) => cleanup()).catch(() => {});
|
|
387
|
+
};
|
|
233
388
|
}
|
|
234
389
|
};
|
|
235
390
|
ensure.then((session) => {
|
|
@@ -242,6 +397,69 @@ var WebSocketTransportAdapter = class {
|
|
|
242
397
|
});
|
|
243
398
|
return subscription;
|
|
244
399
|
}
|
|
400
|
+
joinEphemeralRoom(roomId) {
|
|
401
|
+
debug("joinEphemeralRoom requested", { roomId });
|
|
402
|
+
let statusEmitterRef;
|
|
403
|
+
const ensure = this.ensureEphemeralSession(roomId).then((session) => {
|
|
404
|
+
statusEmitterRef = session.statusEmitter;
|
|
405
|
+
return session;
|
|
406
|
+
});
|
|
407
|
+
const store = this.ephemeralSessions.get(roomId)?.store;
|
|
408
|
+
if (!store) throw new Error("Failed to initialize ephemeral session");
|
|
409
|
+
const firstSynced = ensure.then((session) => session.firstSynced);
|
|
410
|
+
const getConnected = () => this.isConnected();
|
|
411
|
+
const subscription = {
|
|
412
|
+
store,
|
|
413
|
+
unsubscribe: () => {
|
|
414
|
+
ensure.then((session) => {
|
|
415
|
+
session.refCount = Math.max(0, session.refCount - 1);
|
|
416
|
+
debug("ephemeral session refCount decremented", {
|
|
417
|
+
roomId,
|
|
418
|
+
refCount: session.refCount
|
|
419
|
+
});
|
|
420
|
+
if (session.refCount === 0) this.leaveEphemeralSession(roomId).catch(() => {});
|
|
421
|
+
});
|
|
422
|
+
},
|
|
423
|
+
firstSyncedWithRemote: firstSynced,
|
|
424
|
+
get connected() {
|
|
425
|
+
return getConnected();
|
|
426
|
+
},
|
|
427
|
+
get status() {
|
|
428
|
+
return statusEmitterRef?.status ?? "connecting";
|
|
429
|
+
},
|
|
430
|
+
onStatusChange: (listener) => {
|
|
431
|
+
const attach = (emitter) => {
|
|
432
|
+
emitter.listeners.add(listener);
|
|
433
|
+
try {
|
|
434
|
+
listener(emitter.status);
|
|
435
|
+
} catch (error) {
|
|
436
|
+
debug("ephemeral onStatusChange listener error", error);
|
|
437
|
+
}
|
|
438
|
+
return () => emitter.listeners.delete(listener);
|
|
439
|
+
};
|
|
440
|
+
if (statusEmitterRef) {
|
|
441
|
+
const cleanup = attach(statusEmitterRef);
|
|
442
|
+
return () => cleanup();
|
|
443
|
+
}
|
|
444
|
+
let unsubscribed = false;
|
|
445
|
+
const cleanupPromise = ensure.then((session) => attach(session.statusEmitter));
|
|
446
|
+
return () => {
|
|
447
|
+
if (unsubscribed) return;
|
|
448
|
+
unsubscribed = true;
|
|
449
|
+
cleanupPromise.then((cleanup) => cleanup()).catch(() => {});
|
|
450
|
+
};
|
|
451
|
+
}
|
|
452
|
+
};
|
|
453
|
+
ensure.then((session) => {
|
|
454
|
+
subscription.store = session.store;
|
|
455
|
+
session.refCount += 1;
|
|
456
|
+
debug("ephemeral session refCount incremented", {
|
|
457
|
+
roomId,
|
|
458
|
+
refCount: session.refCount
|
|
459
|
+
});
|
|
460
|
+
});
|
|
461
|
+
return subscription;
|
|
462
|
+
}
|
|
245
463
|
ensureClient() {
|
|
246
464
|
if (this.client) {
|
|
247
465
|
debug("reusing websocket client", { status: this.client.getStatus() });
|
|
@@ -256,9 +474,18 @@ var WebSocketTransportAdapter = class {
|
|
|
256
474
|
url,
|
|
257
475
|
...clientOptions
|
|
258
476
|
});
|
|
477
|
+
this.trackClientListener(client.onStatusChange((status) => {
|
|
478
|
+
this.propagateClientStatus(status);
|
|
479
|
+
this.options.onStatusChange?.(status);
|
|
480
|
+
}));
|
|
481
|
+
if (this.options.onLatency) this.trackClientListener(client.onLatency(this.options.onLatency));
|
|
259
482
|
this.client = client;
|
|
483
|
+
this.propagateClientStatus(client.getStatus());
|
|
260
484
|
return client;
|
|
261
485
|
}
|
|
486
|
+
get websocketClient() {
|
|
487
|
+
return this.ensureClient();
|
|
488
|
+
}
|
|
262
489
|
async ensureMetadataSession(flock, params) {
|
|
263
490
|
debug("ensureMetadataSession invoked", {
|
|
264
491
|
roomId: params.roomId,
|
|
@@ -279,6 +506,9 @@ var WebSocketTransportAdapter = class {
|
|
|
279
506
|
await this.teardownMetadataSession(this.metadataSession).catch(() => {});
|
|
280
507
|
}
|
|
281
508
|
const adaptor = new loro_adaptors_flock.FlockAdaptor(flock, this.options.metadataAdaptorConfig);
|
|
509
|
+
const statusEmitter = createStatusEmitter("connecting");
|
|
510
|
+
if (params.onStatusChange) statusEmitter.listeners.add(params.onStatusChange);
|
|
511
|
+
this.dispatchRoomStatus("meta", { roomId: params.roomId }, "connecting", statusEmitter, params.onStatusChange);
|
|
282
512
|
debug("joining metadata room", {
|
|
283
513
|
roomId: params.roomId,
|
|
284
514
|
hasAuth: Boolean(params.auth && params.auth.length)
|
|
@@ -288,6 +518,7 @@ var WebSocketTransportAdapter = class {
|
|
|
288
518
|
crdtAdaptor: adaptor,
|
|
289
519
|
auth: params.auth
|
|
290
520
|
});
|
|
521
|
+
this.dispatchRoomStatus("meta", { roomId: params.roomId }, "joined", statusEmitter, params.onStatusChange);
|
|
291
522
|
const firstSynced = room.waitForReachingServerVersion();
|
|
292
523
|
firstSynced.then(() => {
|
|
293
524
|
debug("metadata session firstSynced resolved", { roomId: params.roomId });
|
|
@@ -304,7 +535,9 @@ var WebSocketTransportAdapter = class {
|
|
|
304
535
|
flock,
|
|
305
536
|
roomId: params.roomId,
|
|
306
537
|
auth: params.auth,
|
|
307
|
-
refCount: 0
|
|
538
|
+
refCount: 0,
|
|
539
|
+
statusEmitter,
|
|
540
|
+
statusListener: params.onStatusChange
|
|
308
541
|
};
|
|
309
542
|
this.metadataSession = session;
|
|
310
543
|
return session;
|
|
@@ -326,6 +559,7 @@ var WebSocketTransportAdapter = class {
|
|
|
326
559
|
await room.destroy().catch(() => {});
|
|
327
560
|
}
|
|
328
561
|
adaptor.destroy();
|
|
562
|
+
this.dispatchRoomStatus("meta", { roomId: target.roomId }, "disconnected", target.statusEmitter, target.statusListener);
|
|
329
563
|
debug("metadata session destroyed", { roomId: target.roomId });
|
|
330
564
|
}
|
|
331
565
|
async ensureDocSession(docId, doc, params) {
|
|
@@ -337,8 +571,7 @@ var WebSocketTransportAdapter = class {
|
|
|
337
571
|
status: client.getStatus()
|
|
338
572
|
});
|
|
339
573
|
const existing = this.docSessions.get(docId);
|
|
340
|
-
const
|
|
341
|
-
const roomId = normalizeRoomId(params.roomId, derivedRoomId);
|
|
574
|
+
const roomId = this.options.docRoomId?.(docId) ?? docId;
|
|
342
575
|
let auth;
|
|
343
576
|
auth = await (params.auth ?? this.options.docAuth?.(docId));
|
|
344
577
|
debug("doc session params resolved", {
|
|
@@ -363,6 +596,12 @@ var WebSocketTransportAdapter = class {
|
|
|
363
596
|
await this.leaveDocSession(docId).catch(() => {});
|
|
364
597
|
}
|
|
365
598
|
const adaptor = new loro_adaptors_loro.LoroAdaptor(doc);
|
|
599
|
+
const statusEmitter = createStatusEmitter("connecting");
|
|
600
|
+
if (params.onStatusChange) statusEmitter.listeners.add(params.onStatusChange);
|
|
601
|
+
this.dispatchRoomStatus("doc", {
|
|
602
|
+
roomId,
|
|
603
|
+
docId
|
|
604
|
+
}, "connecting", statusEmitter, params.onStatusChange);
|
|
366
605
|
debug("joining doc room", {
|
|
367
606
|
docId,
|
|
368
607
|
roomId,
|
|
@@ -373,6 +612,10 @@ var WebSocketTransportAdapter = class {
|
|
|
373
612
|
crdtAdaptor: adaptor,
|
|
374
613
|
auth
|
|
375
614
|
});
|
|
615
|
+
this.dispatchRoomStatus("doc", {
|
|
616
|
+
roomId,
|
|
617
|
+
docId
|
|
618
|
+
}, "joined", statusEmitter, params.onStatusChange);
|
|
376
619
|
const firstSynced = room.waitForReachingServerVersion();
|
|
377
620
|
firstSynced.then(() => {
|
|
378
621
|
debug("doc session firstSynced resolved", {
|
|
@@ -392,11 +635,66 @@ var WebSocketTransportAdapter = class {
|
|
|
392
635
|
firstSynced,
|
|
393
636
|
doc,
|
|
394
637
|
roomId,
|
|
395
|
-
|
|
638
|
+
docId,
|
|
639
|
+
refCount: 0,
|
|
640
|
+
statusEmitter,
|
|
641
|
+
statusListener: params.onStatusChange
|
|
396
642
|
};
|
|
397
643
|
this.docSessions.set(docId, session);
|
|
398
644
|
return session;
|
|
399
645
|
}
|
|
646
|
+
async ensureEphemeralSession(roomId) {
|
|
647
|
+
debug("ensureEphemeralSession invoked", { roomId });
|
|
648
|
+
const existing = this.ephemeralSessions.get(roomId);
|
|
649
|
+
if (existing) {
|
|
650
|
+
debug("reusing ephemeral session", {
|
|
651
|
+
roomId,
|
|
652
|
+
refCount: existing.refCount
|
|
653
|
+
});
|
|
654
|
+
return existing;
|
|
655
|
+
}
|
|
656
|
+
const adaptor = new loro_adaptors_loro.LoroEphemeralAdaptor();
|
|
657
|
+
const statusEmitter = createStatusEmitter("connecting");
|
|
658
|
+
this.dispatchRoomStatus("ephemeral", { roomId }, "connecting", statusEmitter, void 0);
|
|
659
|
+
const session = {
|
|
660
|
+
adaptor,
|
|
661
|
+
store: adaptor.getStore(),
|
|
662
|
+
roomId,
|
|
663
|
+
firstSynced: Promise.resolve(),
|
|
664
|
+
refCount: 0,
|
|
665
|
+
statusEmitter
|
|
666
|
+
};
|
|
667
|
+
this.ephemeralSessions.set(roomId, session);
|
|
668
|
+
const client = this.ensureClient();
|
|
669
|
+
await client.waitConnected();
|
|
670
|
+
debug("websocket client ready for ephemeral session", {
|
|
671
|
+
roomId,
|
|
672
|
+
status: client.getStatus()
|
|
673
|
+
});
|
|
674
|
+
try {
|
|
675
|
+
const room = await client.join({
|
|
676
|
+
roomId,
|
|
677
|
+
crdtAdaptor: adaptor
|
|
678
|
+
});
|
|
679
|
+
this.dispatchRoomStatus("ephemeral", { roomId }, "joined", statusEmitter, void 0);
|
|
680
|
+
const firstSynced = room.waitForReachingServerVersion();
|
|
681
|
+
firstSynced.then(() => {
|
|
682
|
+
debug("ephemeral session firstSynced resolved", { roomId });
|
|
683
|
+
}, (error) => {
|
|
684
|
+
debug("ephemeral session firstSynced rejected", {
|
|
685
|
+
roomId,
|
|
686
|
+
error
|
|
687
|
+
});
|
|
688
|
+
});
|
|
689
|
+
session.room = room;
|
|
690
|
+
session.firstSynced = firstSynced;
|
|
691
|
+
return session;
|
|
692
|
+
} catch (error) {
|
|
693
|
+
this.ephemeralSessions.delete(roomId);
|
|
694
|
+
adaptor.destroy();
|
|
695
|
+
throw error;
|
|
696
|
+
}
|
|
697
|
+
}
|
|
400
698
|
async leaveDocSession(docId) {
|
|
401
699
|
const session = this.docSessions.get(docId);
|
|
402
700
|
if (!session) {
|
|
@@ -423,11 +721,37 @@ var WebSocketTransportAdapter = class {
|
|
|
423
721
|
await session.room.destroy().catch(() => {});
|
|
424
722
|
}
|
|
425
723
|
session.adaptor.destroy();
|
|
724
|
+
this.dispatchRoomStatus("doc", {
|
|
725
|
+
roomId: session.roomId,
|
|
726
|
+
docId: session.docId
|
|
727
|
+
}, "disconnected", session.statusEmitter, session.statusListener);
|
|
426
728
|
debug("doc session destroyed", {
|
|
427
729
|
docId,
|
|
428
730
|
roomId: session.roomId
|
|
429
731
|
});
|
|
430
732
|
}
|
|
733
|
+
async leaveEphemeralSession(roomId) {
|
|
734
|
+
const session = this.ephemeralSessions.get(roomId);
|
|
735
|
+
if (!session) {
|
|
736
|
+
debug("leaveEphemeralSession invoked but no session found", { roomId });
|
|
737
|
+
return;
|
|
738
|
+
}
|
|
739
|
+
this.ephemeralSessions.delete(roomId);
|
|
740
|
+
debug("leaving ephemeral session", { roomId });
|
|
741
|
+
try {
|
|
742
|
+
await session.room?.leave();
|
|
743
|
+
debug("ephemeral room left", { roomId });
|
|
744
|
+
} catch (error) {
|
|
745
|
+
debug("ephemeral room leave failed; destroying", {
|
|
746
|
+
roomId,
|
|
747
|
+
error
|
|
748
|
+
});
|
|
749
|
+
await session.room?.destroy().catch(() => {});
|
|
750
|
+
}
|
|
751
|
+
session.adaptor.destroy();
|
|
752
|
+
this.dispatchRoomStatus("ephemeral", { roomId: session.roomId }, "disconnected", session.statusEmitter, void 0);
|
|
753
|
+
debug("ephemeral session destroyed", { roomId });
|
|
754
|
+
}
|
|
431
755
|
};
|
|
432
756
|
|
|
433
757
|
//#endregion
|