@liveblocks/core 1.2.0-internal6 → 1.2.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/dist/index.d.mts +59 -28
- package/dist/index.d.ts +59 -28
- package/dist/index.js +966 -746
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +912 -692
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -1,3 +1,81 @@
|
|
|
1
|
+
// src/version.ts
|
|
2
|
+
var PKG_NAME = "@liveblocks/core";
|
|
3
|
+
var PKG_VERSION = "1.2.0";
|
|
4
|
+
var PKG_FORMAT = "esm";
|
|
5
|
+
|
|
6
|
+
// src/dupe-detection.ts
|
|
7
|
+
var g = typeof globalThis !== "undefined" ? globalThis : typeof window !== "undefined" ? window : typeof global !== "undefined" ? global : {};
|
|
8
|
+
var crossLinkedDocs = "https://liveblocks.io/docs/errors/cross-linked";
|
|
9
|
+
var dupesDocs = "https://liveblocks.io/docs/errors/dupes";
|
|
10
|
+
var SPACE = " ";
|
|
11
|
+
function error(msg) {
|
|
12
|
+
if (process.env.NODE_ENV === "production") {
|
|
13
|
+
console.error(msg);
|
|
14
|
+
} else {
|
|
15
|
+
throw new Error(msg);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
function detectDupes(pkgName, pkgVersion, pkgFormat) {
|
|
19
|
+
const pkgId = Symbol.for(pkgName);
|
|
20
|
+
const pkgBuildInfo = pkgFormat ? `${pkgVersion || "dev"} (${pkgFormat})` : pkgVersion || "dev";
|
|
21
|
+
if (!g[pkgId]) {
|
|
22
|
+
g[pkgId] = pkgBuildInfo;
|
|
23
|
+
} else if (g[pkgId] === pkgBuildInfo) {
|
|
24
|
+
} else {
|
|
25
|
+
const msg = [
|
|
26
|
+
`Multiple copies of Liveblocks are being loaded in your project. This will cause issues! See ${dupesDocs + SPACE}`,
|
|
27
|
+
"",
|
|
28
|
+
"Conflicts:",
|
|
29
|
+
`- ${pkgName} ${g[pkgId]} (already loaded)`,
|
|
30
|
+
`- ${pkgName} ${pkgBuildInfo} (trying to load this now)`
|
|
31
|
+
].join("\n");
|
|
32
|
+
error(msg);
|
|
33
|
+
}
|
|
34
|
+
if (pkgVersion && PKG_VERSION && pkgVersion !== PKG_VERSION) {
|
|
35
|
+
error(
|
|
36
|
+
[
|
|
37
|
+
`Cross-linked versions of Liveblocks found, which will cause issues! See ${crossLinkedDocs + SPACE}`,
|
|
38
|
+
"",
|
|
39
|
+
"Conflicts:",
|
|
40
|
+
`- ${PKG_NAME} is at ${PKG_VERSION}`,
|
|
41
|
+
`- ${pkgName} is at ${pkgVersion}`,
|
|
42
|
+
"",
|
|
43
|
+
"Always upgrade all Liveblocks packages to the same version number."
|
|
44
|
+
].join("\n")
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// src/lib/assert.ts
|
|
50
|
+
function assertNever(_value, errmsg) {
|
|
51
|
+
throw new Error(errmsg);
|
|
52
|
+
}
|
|
53
|
+
function assert(condition, errmsg) {
|
|
54
|
+
if (process.env.NODE_ENV !== "production") {
|
|
55
|
+
if (!condition) {
|
|
56
|
+
const err = new Error(errmsg);
|
|
57
|
+
err.name = "Assertion failure";
|
|
58
|
+
throw err;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
function nn(value, errmsg = "Expected value to be non-nullable") {
|
|
63
|
+
assert(value !== null && value !== void 0, errmsg);
|
|
64
|
+
return value;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// src/lib/controlledPromise.ts
|
|
68
|
+
function controlledPromise() {
|
|
69
|
+
let flagger;
|
|
70
|
+
const promise = new Promise((res) => {
|
|
71
|
+
flagger = res;
|
|
72
|
+
});
|
|
73
|
+
if (!flagger) {
|
|
74
|
+
throw new Error("Should never happen");
|
|
75
|
+
}
|
|
76
|
+
return [promise, flagger];
|
|
77
|
+
}
|
|
78
|
+
|
|
1
79
|
// src/lib/EventSource.ts
|
|
2
80
|
function makeEventSource() {
|
|
3
81
|
const _onetimeObservers = /* @__PURE__ */ new Set();
|
|
@@ -71,187 +149,6 @@ function makeEventSource() {
|
|
|
71
149
|
};
|
|
72
150
|
}
|
|
73
151
|
|
|
74
|
-
// src/devtools/bridge.ts
|
|
75
|
-
var _bridgeActive = false;
|
|
76
|
-
function activateBridge(allowed) {
|
|
77
|
-
_bridgeActive = allowed;
|
|
78
|
-
}
|
|
79
|
-
function sendToPanel(message, options) {
|
|
80
|
-
if (process.env.NODE_ENV === "production" || typeof window === "undefined") {
|
|
81
|
-
return;
|
|
82
|
-
}
|
|
83
|
-
const fullMsg = {
|
|
84
|
-
...message,
|
|
85
|
-
source: "liveblocks-devtools-client"
|
|
86
|
-
};
|
|
87
|
-
if (!(options?.force || _bridgeActive)) {
|
|
88
|
-
return;
|
|
89
|
-
}
|
|
90
|
-
window.postMessage(fullMsg, "*");
|
|
91
|
-
}
|
|
92
|
-
var eventSource = makeEventSource();
|
|
93
|
-
if (process.env.NODE_ENV !== "production" && typeof window !== "undefined") {
|
|
94
|
-
window.addEventListener("message", (event) => {
|
|
95
|
-
if (event.source === window && event.data?.source === "liveblocks-devtools-panel") {
|
|
96
|
-
eventSource.notify(event.data);
|
|
97
|
-
} else {
|
|
98
|
-
}
|
|
99
|
-
});
|
|
100
|
-
}
|
|
101
|
-
var onMessageFromPanel = eventSource.observable;
|
|
102
|
-
|
|
103
|
-
// src/devtools/index.ts
|
|
104
|
-
var VERSION = true ? (
|
|
105
|
-
/* istanbul ignore next */
|
|
106
|
-
"1.2.0-internal6"
|
|
107
|
-
) : "dev";
|
|
108
|
-
var _devtoolsSetupHasRun = false;
|
|
109
|
-
function setupDevTools(getAllRooms) {
|
|
110
|
-
if (process.env.NODE_ENV === "production" || typeof window === "undefined") {
|
|
111
|
-
return;
|
|
112
|
-
}
|
|
113
|
-
if (_devtoolsSetupHasRun) {
|
|
114
|
-
return;
|
|
115
|
-
}
|
|
116
|
-
_devtoolsSetupHasRun = true;
|
|
117
|
-
onMessageFromPanel.subscribe((msg) => {
|
|
118
|
-
switch (msg.msg) {
|
|
119
|
-
case "connect": {
|
|
120
|
-
activateBridge(true);
|
|
121
|
-
for (const roomId of getAllRooms()) {
|
|
122
|
-
sendToPanel({
|
|
123
|
-
msg: "room::available",
|
|
124
|
-
roomId,
|
|
125
|
-
clientVersion: VERSION
|
|
126
|
-
});
|
|
127
|
-
}
|
|
128
|
-
break;
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
});
|
|
132
|
-
sendToPanel({ msg: "wake-up-devtools" }, { force: true });
|
|
133
|
-
}
|
|
134
|
-
var unsubsByRoomId = /* @__PURE__ */ new Map();
|
|
135
|
-
function stopSyncStream(roomId) {
|
|
136
|
-
const unsubs = unsubsByRoomId.get(roomId) ?? [];
|
|
137
|
-
unsubsByRoomId.delete(roomId);
|
|
138
|
-
for (const unsub of unsubs) {
|
|
139
|
-
unsub();
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
function startSyncStream(room) {
|
|
143
|
-
stopSyncStream(room.id);
|
|
144
|
-
fullSync(room);
|
|
145
|
-
unsubsByRoomId.set(room.id, [
|
|
146
|
-
// When the connection status changes
|
|
147
|
-
room.events.status.subscribe(() => partialSyncConnection(room)),
|
|
148
|
-
// When storage initializes, send the update
|
|
149
|
-
room.events.storageDidLoad.subscribeOnce(() => partialSyncStorage(room)),
|
|
150
|
-
// Any time storage updates, send the new storage root
|
|
151
|
-
room.events.storage.subscribe(() => partialSyncStorage(room)),
|
|
152
|
-
// Any time "me" or "others" updates, send the new values accordingly
|
|
153
|
-
room.events.me.subscribe(() => partialSyncMe(room)),
|
|
154
|
-
room.events.others.subscribe(() => partialSyncOthers(room))
|
|
155
|
-
]);
|
|
156
|
-
}
|
|
157
|
-
function partialSyncConnection(room) {
|
|
158
|
-
sendToPanel({
|
|
159
|
-
msg: "room::sync::partial",
|
|
160
|
-
roomId: room.id,
|
|
161
|
-
status: room.getStatus()
|
|
162
|
-
});
|
|
163
|
-
}
|
|
164
|
-
function partialSyncStorage(room) {
|
|
165
|
-
const root = room.getStorageSnapshot();
|
|
166
|
-
if (root) {
|
|
167
|
-
sendToPanel({
|
|
168
|
-
msg: "room::sync::partial",
|
|
169
|
-
roomId: room.id,
|
|
170
|
-
storage: root.toTreeNode("root").payload
|
|
171
|
-
});
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
function partialSyncMe(room) {
|
|
175
|
-
const me = room.__internal.getSelf_forDevTools();
|
|
176
|
-
if (me) {
|
|
177
|
-
sendToPanel({
|
|
178
|
-
msg: "room::sync::partial",
|
|
179
|
-
roomId: room.id,
|
|
180
|
-
me
|
|
181
|
-
});
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
function partialSyncOthers(room) {
|
|
185
|
-
const others = room.__internal.getOthers_forDevTools();
|
|
186
|
-
if (others) {
|
|
187
|
-
sendToPanel({
|
|
188
|
-
msg: "room::sync::partial",
|
|
189
|
-
roomId: room.id,
|
|
190
|
-
others
|
|
191
|
-
});
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
function fullSync(room) {
|
|
195
|
-
const root = room.getStorageSnapshot();
|
|
196
|
-
const me = room.__internal.getSelf_forDevTools();
|
|
197
|
-
const others = room.__internal.getOthers_forDevTools();
|
|
198
|
-
sendToPanel({
|
|
199
|
-
msg: "room::sync::full",
|
|
200
|
-
roomId: room.id,
|
|
201
|
-
status: room.getStatus(),
|
|
202
|
-
storage: root?.toTreeNode("root").payload ?? null,
|
|
203
|
-
me,
|
|
204
|
-
others
|
|
205
|
-
});
|
|
206
|
-
}
|
|
207
|
-
var roomChannelListeners = /* @__PURE__ */ new Map();
|
|
208
|
-
function stopRoomChannelListener(roomId) {
|
|
209
|
-
const listener = roomChannelListeners.get(roomId);
|
|
210
|
-
roomChannelListeners.delete(roomId);
|
|
211
|
-
if (listener) {
|
|
212
|
-
listener();
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
function linkDevTools(roomId, room) {
|
|
216
|
-
if (process.env.NODE_ENV === "production" || typeof window === "undefined") {
|
|
217
|
-
return;
|
|
218
|
-
}
|
|
219
|
-
sendToPanel({ msg: "room::available", roomId, clientVersion: VERSION });
|
|
220
|
-
stopRoomChannelListener(roomId);
|
|
221
|
-
roomChannelListeners.set(
|
|
222
|
-
roomId,
|
|
223
|
-
// Returns the unsubscribe callback, that we store in the
|
|
224
|
-
// roomChannelListeners registry
|
|
225
|
-
onMessageFromPanel.subscribe((msg) => {
|
|
226
|
-
switch (msg.msg) {
|
|
227
|
-
case "room::subscribe": {
|
|
228
|
-
if (msg.roomId === roomId) {
|
|
229
|
-
startSyncStream(room);
|
|
230
|
-
}
|
|
231
|
-
break;
|
|
232
|
-
}
|
|
233
|
-
case "room::unsubscribe": {
|
|
234
|
-
if (msg.roomId === roomId) {
|
|
235
|
-
stopSyncStream(roomId);
|
|
236
|
-
}
|
|
237
|
-
break;
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
})
|
|
241
|
-
);
|
|
242
|
-
}
|
|
243
|
-
function unlinkDevTools(roomId) {
|
|
244
|
-
if (process.env.NODE_ENV === "production" || typeof window === "undefined") {
|
|
245
|
-
return;
|
|
246
|
-
}
|
|
247
|
-
stopSyncStream(roomId);
|
|
248
|
-
stopRoomChannelListener(roomId);
|
|
249
|
-
sendToPanel({
|
|
250
|
-
msg: "room::unavailable",
|
|
251
|
-
roomId
|
|
252
|
-
});
|
|
253
|
-
}
|
|
254
|
-
|
|
255
152
|
// src/lib/fancy-console.ts
|
|
256
153
|
var badge = "background:#0e0d12;border-radius:9999px;color:#fff;padding:3px 7px;font-family:sans-serif;font-weight:600;";
|
|
257
154
|
var bold = "font-weight:600";
|
|
@@ -262,7 +159,7 @@ function wrap(method) {
|
|
|
262
159
|
);
|
|
263
160
|
}
|
|
264
161
|
var warn = wrap("warn");
|
|
265
|
-
var
|
|
162
|
+
var error2 = wrap("error");
|
|
266
163
|
function wrapWithTitle(method) {
|
|
267
164
|
return typeof window === "undefined" || process.env.NODE_ENV === "test" ? console[method] : (
|
|
268
165
|
/* istanbul ignore next */
|
|
@@ -278,83 +175,32 @@ function wrapWithTitle(method) {
|
|
|
278
175
|
var warnWithTitle = wrapWithTitle("warn");
|
|
279
176
|
var errorWithTitle = wrapWithTitle("error");
|
|
280
177
|
|
|
281
|
-
// src/lib/
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
if (!_emittedDeprecationWarnings.has(key)) {
|
|
286
|
-
_emittedDeprecationWarnings.add(key);
|
|
287
|
-
errorWithTitle("Deprecation warning", message);
|
|
288
|
-
}
|
|
178
|
+
// src/lib/fsm.ts
|
|
179
|
+
function distance(state1, state2) {
|
|
180
|
+
if (state1 === state2) {
|
|
181
|
+
return [0, 0];
|
|
289
182
|
}
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
183
|
+
const chunks1 = state1.split(".");
|
|
184
|
+
const chunks2 = state2.split(".");
|
|
185
|
+
const minLen = Math.min(chunks1.length, chunks2.length);
|
|
186
|
+
let shared = 0;
|
|
187
|
+
for (; shared < minLen; shared++) {
|
|
188
|
+
if (chunks1[shared] !== chunks2[shared]) {
|
|
189
|
+
break;
|
|
295
190
|
}
|
|
296
191
|
}
|
|
192
|
+
const up = chunks1.length - shared;
|
|
193
|
+
const down = chunks2.length - shared;
|
|
194
|
+
return [up, down];
|
|
297
195
|
}
|
|
298
|
-
function
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
errorWithTitle("Usage error", message);
|
|
303
|
-
throw usageError;
|
|
196
|
+
function patterns(targetState, levels) {
|
|
197
|
+
const parts = targetState.split(".");
|
|
198
|
+
if (levels < 1 || levels > parts.length + 1) {
|
|
199
|
+
throw new Error("Invalid number of levels");
|
|
304
200
|
}
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
if (condition) {
|
|
309
|
-
throwUsageError(message);
|
|
310
|
-
}
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
// src/lib/assert.ts
|
|
315
|
-
function assertNever(_value, errmsg) {
|
|
316
|
-
throw new Error(errmsg);
|
|
317
|
-
}
|
|
318
|
-
function assert(condition, errmsg) {
|
|
319
|
-
if (process.env.NODE_ENV !== "production") {
|
|
320
|
-
if (!condition) {
|
|
321
|
-
const err = new Error(errmsg);
|
|
322
|
-
err.name = "Assertion failure";
|
|
323
|
-
throw err;
|
|
324
|
-
}
|
|
325
|
-
}
|
|
326
|
-
}
|
|
327
|
-
function nn(value, errmsg = "Expected value to be non-nullable") {
|
|
328
|
-
assert(value !== null && value !== void 0, errmsg);
|
|
329
|
-
return value;
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
// src/lib/fsm.ts
|
|
333
|
-
function distance(state1, state2) {
|
|
334
|
-
if (state1 === state2) {
|
|
335
|
-
return [0, 0];
|
|
336
|
-
}
|
|
337
|
-
const chunks1 = state1.split(".");
|
|
338
|
-
const chunks2 = state2.split(".");
|
|
339
|
-
const minLen = Math.min(chunks1.length, chunks2.length);
|
|
340
|
-
let shared = 0;
|
|
341
|
-
for (; shared < minLen; shared++) {
|
|
342
|
-
if (chunks1[shared] !== chunks2[shared]) {
|
|
343
|
-
break;
|
|
344
|
-
}
|
|
345
|
-
}
|
|
346
|
-
const up = chunks1.length - shared;
|
|
347
|
-
const down = chunks2.length - shared;
|
|
348
|
-
return [up, down];
|
|
349
|
-
}
|
|
350
|
-
function patterns(targetState, levels) {
|
|
351
|
-
const parts = targetState.split(".");
|
|
352
|
-
if (levels < 1 || levels > parts.length + 1) {
|
|
353
|
-
throw new Error("Invalid number of levels");
|
|
354
|
-
}
|
|
355
|
-
const result = [];
|
|
356
|
-
if (levels > parts.length) {
|
|
357
|
-
result.push("*");
|
|
201
|
+
const result = [];
|
|
202
|
+
if (levels > parts.length) {
|
|
203
|
+
result.push("*");
|
|
358
204
|
}
|
|
359
205
|
for (let i = parts.length - levels + 1; i < parts.length; i++) {
|
|
360
206
|
const slice = parts.slice(0, i);
|
|
@@ -726,6 +572,9 @@ function tryParseJson(rawMessage) {
|
|
|
726
572
|
return void 0;
|
|
727
573
|
}
|
|
728
574
|
}
|
|
575
|
+
function deepClone(items) {
|
|
576
|
+
return JSON.parse(JSON.stringify(items));
|
|
577
|
+
}
|
|
729
578
|
function b64decode(b64value) {
|
|
730
579
|
try {
|
|
731
580
|
const formattedValue = b64value.replace(/-/g, "+").replace(/_/g, "/");
|
|
@@ -764,6 +613,45 @@ async function withTimeout(promise, millis, errmsg = "Timed out") {
|
|
|
764
613
|
return Promise.race([promise, timer$]).finally(() => clearTimeout(timerID));
|
|
765
614
|
}
|
|
766
615
|
|
|
616
|
+
// src/protocol/ServerMsg.ts
|
|
617
|
+
var ServerMsgCode = /* @__PURE__ */ ((ServerMsgCode2) => {
|
|
618
|
+
ServerMsgCode2[ServerMsgCode2["UPDATE_PRESENCE"] = 100] = "UPDATE_PRESENCE";
|
|
619
|
+
ServerMsgCode2[ServerMsgCode2["USER_JOINED"] = 101] = "USER_JOINED";
|
|
620
|
+
ServerMsgCode2[ServerMsgCode2["USER_LEFT"] = 102] = "USER_LEFT";
|
|
621
|
+
ServerMsgCode2[ServerMsgCode2["BROADCASTED_EVENT"] = 103] = "BROADCASTED_EVENT";
|
|
622
|
+
ServerMsgCode2[ServerMsgCode2["ROOM_STATE"] = 104] = "ROOM_STATE";
|
|
623
|
+
ServerMsgCode2[ServerMsgCode2["INITIAL_STORAGE_STATE"] = 200] = "INITIAL_STORAGE_STATE";
|
|
624
|
+
ServerMsgCode2[ServerMsgCode2["UPDATE_STORAGE"] = 201] = "UPDATE_STORAGE";
|
|
625
|
+
ServerMsgCode2[ServerMsgCode2["REJECT_STORAGE_OP"] = 299] = "REJECT_STORAGE_OP";
|
|
626
|
+
ServerMsgCode2[ServerMsgCode2["UPDATE_YDOC"] = 300] = "UPDATE_YDOC";
|
|
627
|
+
return ServerMsgCode2;
|
|
628
|
+
})(ServerMsgCode || {});
|
|
629
|
+
|
|
630
|
+
// src/types/IWebSocket.ts
|
|
631
|
+
var WebsocketCloseCodes = /* @__PURE__ */ ((WebsocketCloseCodes2) => {
|
|
632
|
+
WebsocketCloseCodes2[WebsocketCloseCodes2["CLOSE_ABNORMAL"] = 1006] = "CLOSE_ABNORMAL";
|
|
633
|
+
WebsocketCloseCodes2[WebsocketCloseCodes2["UNEXPECTED_CONDITION"] = 1011] = "UNEXPECTED_CONDITION";
|
|
634
|
+
WebsocketCloseCodes2[WebsocketCloseCodes2["TRY_AGAIN_LATER"] = 1013] = "TRY_AGAIN_LATER";
|
|
635
|
+
WebsocketCloseCodes2[WebsocketCloseCodes2["INVALID_MESSAGE_FORMAT"] = 4e3] = "INVALID_MESSAGE_FORMAT";
|
|
636
|
+
WebsocketCloseCodes2[WebsocketCloseCodes2["NOT_ALLOWED"] = 4001] = "NOT_ALLOWED";
|
|
637
|
+
WebsocketCloseCodes2[WebsocketCloseCodes2["MAX_NUMBER_OF_MESSAGES_PER_SECONDS"] = 4002] = "MAX_NUMBER_OF_MESSAGES_PER_SECONDS";
|
|
638
|
+
WebsocketCloseCodes2[WebsocketCloseCodes2["MAX_NUMBER_OF_CONCURRENT_CONNECTIONS"] = 4003] = "MAX_NUMBER_OF_CONCURRENT_CONNECTIONS";
|
|
639
|
+
WebsocketCloseCodes2[WebsocketCloseCodes2["MAX_NUMBER_OF_MESSAGES_PER_DAY_PER_APP"] = 4004] = "MAX_NUMBER_OF_MESSAGES_PER_DAY_PER_APP";
|
|
640
|
+
WebsocketCloseCodes2[WebsocketCloseCodes2["MAX_NUMBER_OF_CONCURRENT_CONNECTIONS_PER_ROOM"] = 4005] = "MAX_NUMBER_OF_CONCURRENT_CONNECTIONS_PER_ROOM";
|
|
641
|
+
WebsocketCloseCodes2[WebsocketCloseCodes2["TOKEN_EXPIRED"] = 4109] = "TOKEN_EXPIRED";
|
|
642
|
+
WebsocketCloseCodes2[WebsocketCloseCodes2["CLOSE_WITHOUT_RETRY"] = 4999] = "CLOSE_WITHOUT_RETRY";
|
|
643
|
+
return WebsocketCloseCodes2;
|
|
644
|
+
})(WebsocketCloseCodes || {});
|
|
645
|
+
function shouldDisconnect(code) {
|
|
646
|
+
return code === 4999 /* CLOSE_WITHOUT_RETRY */ || code >= 4e3 && code < 4100;
|
|
647
|
+
}
|
|
648
|
+
function shouldReauth(code) {
|
|
649
|
+
return code >= 4100 && code < 4200;
|
|
650
|
+
}
|
|
651
|
+
function shouldRetryWithoutReauth(code) {
|
|
652
|
+
return code === 1013 /* TRY_AGAIN_LATER */ || code >= 4200 && code < 4300;
|
|
653
|
+
}
|
|
654
|
+
|
|
767
655
|
// src/connection.ts
|
|
768
656
|
function newToLegacyStatus(status) {
|
|
769
657
|
switch (status) {
|
|
@@ -833,7 +721,7 @@ function resetSuccessCount(context) {
|
|
|
833
721
|
context.patch({ successCount: 0 });
|
|
834
722
|
}
|
|
835
723
|
function log(level, message) {
|
|
836
|
-
const logger = level === 2 /* ERROR */ ?
|
|
724
|
+
const logger = level === 2 /* ERROR */ ? error2 : level === 1 /* WARN */ ? warn : (
|
|
837
725
|
/* black hole */
|
|
838
726
|
() => {
|
|
839
727
|
}
|
|
@@ -865,11 +753,8 @@ var logPermanentClose = log(
|
|
|
865
753
|
1 /* WARN */,
|
|
866
754
|
"Connection to WebSocket closed permanently. Won't retry."
|
|
867
755
|
);
|
|
868
|
-
function isCloseEvent(
|
|
869
|
-
return !(
|
|
870
|
-
}
|
|
871
|
-
function isCustomCloseEvent(error2) {
|
|
872
|
-
return isCloseEvent(error2) && error2.code >= 4e3 && error2.code < 4100;
|
|
756
|
+
function isCloseEvent(error3) {
|
|
757
|
+
return !(error3 instanceof Error) && error3.type === "close";
|
|
873
758
|
}
|
|
874
759
|
function enableTracing(machine) {
|
|
875
760
|
const start = (/* @__PURE__ */ new Date()).getTime();
|
|
@@ -921,13 +806,19 @@ function defineConnectivityEvents(machine) {
|
|
|
921
806
|
};
|
|
922
807
|
}
|
|
923
808
|
var assign = (patch) => (ctx) => ctx.patch(patch);
|
|
924
|
-
function createConnectionStateMachine(delegates,
|
|
809
|
+
function createConnectionStateMachine(delegates, options) {
|
|
925
810
|
const onMessage = makeEventSource();
|
|
926
811
|
onMessage.pause();
|
|
927
812
|
const onLiveblocksError = makeEventSource();
|
|
813
|
+
function fireErrorEvent(errmsg, errcode) {
|
|
814
|
+
return () => {
|
|
815
|
+
const err = new LiveblocksError(errmsg, errcode);
|
|
816
|
+
onLiveblocksError.notify(err);
|
|
817
|
+
};
|
|
818
|
+
}
|
|
928
819
|
const initialContext = {
|
|
929
820
|
successCount: 0,
|
|
930
|
-
|
|
821
|
+
authValue: null,
|
|
931
822
|
socket: null,
|
|
932
823
|
backoffDelay: RESET_DELAY
|
|
933
824
|
};
|
|
@@ -941,9 +832,9 @@ function createConnectionStateMachine(delegates, enableDebugLogging) {
|
|
|
941
832
|
});
|
|
942
833
|
machine.onEnter("@idle.*", resetSuccessCount).addTransitions("@idle.*", {
|
|
943
834
|
CONNECT: (_, ctx) => (
|
|
944
|
-
// If we still have a known
|
|
945
|
-
// otherwise, try to obtain a new
|
|
946
|
-
ctx.
|
|
835
|
+
// If we still have a known authValue, try to reconnect to the socket directly,
|
|
836
|
+
// otherwise, try to obtain a new authValue
|
|
837
|
+
ctx.authValue !== null ? "@connecting.busy" : "@auth.busy"
|
|
947
838
|
)
|
|
948
839
|
});
|
|
949
840
|
machine.addTransitions("@auth.backoff", {
|
|
@@ -962,7 +853,7 @@ function createConnectionStateMachine(delegates, enableDebugLogging) {
|
|
|
962
853
|
(okEvent) => ({
|
|
963
854
|
target: "@connecting.busy",
|
|
964
855
|
effect: assign({
|
|
965
|
-
|
|
856
|
+
authValue: okEvent.data,
|
|
966
857
|
backoffDelay: RESET_DELAY
|
|
967
858
|
})
|
|
968
859
|
}),
|
|
@@ -971,7 +862,10 @@ function createConnectionStateMachine(delegates, enableDebugLogging) {
|
|
|
971
862
|
if (failedEvent.reason instanceof StopRetrying) {
|
|
972
863
|
return {
|
|
973
864
|
target: "@idle.failed",
|
|
974
|
-
effect:
|
|
865
|
+
effect: [
|
|
866
|
+
log(2 /* ERROR */, failedEvent.reason.message),
|
|
867
|
+
fireErrorEvent(failedEvent.reason.message, -1)
|
|
868
|
+
]
|
|
975
869
|
};
|
|
976
870
|
}
|
|
977
871
|
return {
|
|
@@ -1021,16 +915,29 @@ function createConnectionStateMachine(delegates, enableDebugLogging) {
|
|
|
1021
915
|
let capturedPrematureEvent = null;
|
|
1022
916
|
const connect$ = new Promise(
|
|
1023
917
|
(resolve, rej) => {
|
|
1024
|
-
if (ctx.
|
|
1025
|
-
throw new Error("No auth
|
|
918
|
+
if (ctx.authValue === null) {
|
|
919
|
+
throw new Error("No auth authValue");
|
|
1026
920
|
}
|
|
1027
|
-
const socket = delegates.createSocket(ctx.
|
|
921
|
+
const socket = delegates.createSocket(ctx.authValue);
|
|
1028
922
|
function reject(event) {
|
|
1029
923
|
capturedPrematureEvent = event;
|
|
1030
924
|
socket.removeEventListener("message", onSocketMessage);
|
|
1031
925
|
rej(event);
|
|
1032
926
|
}
|
|
927
|
+
const [actor$, didReceiveActor] = controlledPromise();
|
|
928
|
+
if (!options.waitForActorId) {
|
|
929
|
+
didReceiveActor();
|
|
930
|
+
}
|
|
931
|
+
function waitForActorId(event) {
|
|
932
|
+
const serverMsg = tryParseJson(event.data);
|
|
933
|
+
if (serverMsg?.type === 104 /* ROOM_STATE */) {
|
|
934
|
+
didReceiveActor();
|
|
935
|
+
}
|
|
936
|
+
}
|
|
1033
937
|
socket.addEventListener("message", onSocketMessage);
|
|
938
|
+
if (options.waitForActorId) {
|
|
939
|
+
socket.addEventListener("message", waitForActorId);
|
|
940
|
+
}
|
|
1034
941
|
socket.addEventListener("error", reject);
|
|
1035
942
|
socket.addEventListener("close", reject);
|
|
1036
943
|
socket.addEventListener("open", () => {
|
|
@@ -1039,8 +946,11 @@ function createConnectionStateMachine(delegates, enableDebugLogging) {
|
|
|
1039
946
|
const unsub = () => {
|
|
1040
947
|
socket.removeEventListener("error", reject);
|
|
1041
948
|
socket.removeEventListener("close", reject);
|
|
949
|
+
socket.removeEventListener("message", waitForActorId);
|
|
1042
950
|
};
|
|
1043
|
-
|
|
951
|
+
void actor$.then(() => {
|
|
952
|
+
resolve([socket, unsub]);
|
|
953
|
+
});
|
|
1044
954
|
});
|
|
1045
955
|
}
|
|
1046
956
|
);
|
|
@@ -1081,24 +991,35 @@ function createConnectionStateMachine(delegates, enableDebugLogging) {
|
|
|
1081
991
|
if (err instanceof StopRetrying) {
|
|
1082
992
|
return {
|
|
1083
993
|
target: "@idle.failed",
|
|
1084
|
-
effect: log(2 /* ERROR */, err.message)
|
|
1085
|
-
};
|
|
1086
|
-
}
|
|
1087
|
-
if (isCloseEvent(err) && err.code === 4999) {
|
|
1088
|
-
return {
|
|
1089
|
-
target: "@idle.failed",
|
|
1090
|
-
effect: log(2 /* ERROR */, err.reason)
|
|
1091
|
-
};
|
|
1092
|
-
}
|
|
1093
|
-
if (isCustomCloseEvent(err) && err.code !== 4001) {
|
|
1094
|
-
return {
|
|
1095
|
-
target: "@connecting.backoff",
|
|
1096
994
|
effect: [
|
|
1097
|
-
|
|
1098
|
-
|
|
995
|
+
log(2 /* ERROR */, err.message),
|
|
996
|
+
fireErrorEvent(err.message, -1)
|
|
1099
997
|
]
|
|
1100
998
|
};
|
|
1101
999
|
}
|
|
1000
|
+
if (isCloseEvent(err)) {
|
|
1001
|
+
if (err.code === 4109 /* TOKEN_EXPIRED */) {
|
|
1002
|
+
return "@auth.busy";
|
|
1003
|
+
}
|
|
1004
|
+
if (shouldRetryWithoutReauth(err.code)) {
|
|
1005
|
+
return {
|
|
1006
|
+
target: "@connecting.backoff",
|
|
1007
|
+
effect: [
|
|
1008
|
+
increaseBackoffDelayAggressively,
|
|
1009
|
+
logPrematureErrorOrCloseEvent(err)
|
|
1010
|
+
]
|
|
1011
|
+
};
|
|
1012
|
+
}
|
|
1013
|
+
if (shouldDisconnect(err.code)) {
|
|
1014
|
+
return {
|
|
1015
|
+
target: "@idle.failed",
|
|
1016
|
+
effect: [
|
|
1017
|
+
log(2 /* ERROR */, err.reason),
|
|
1018
|
+
fireErrorEvent(err.reason, err.code)
|
|
1019
|
+
]
|
|
1020
|
+
};
|
|
1021
|
+
}
|
|
1022
|
+
}
|
|
1102
1023
|
return {
|
|
1103
1024
|
target: "@auth.backoff",
|
|
1104
1025
|
effect: [increaseBackoffDelay, logPrematureErrorOrCloseEvent(err)]
|
|
@@ -1152,29 +1073,29 @@ function createConnectionStateMachine(delegates, enableDebugLogging) {
|
|
|
1152
1073
|
};
|
|
1153
1074
|
},
|
|
1154
1075
|
EXPLICIT_SOCKET_CLOSE: (e) => {
|
|
1155
|
-
if (e.event.code
|
|
1076
|
+
if (shouldDisconnect(e.event.code)) {
|
|
1156
1077
|
return {
|
|
1157
1078
|
target: "@idle.failed",
|
|
1158
|
-
effect:
|
|
1079
|
+
effect: [
|
|
1080
|
+
logPermanentClose,
|
|
1081
|
+
fireErrorEvent(e.event.reason, e.event.code)
|
|
1082
|
+
]
|
|
1159
1083
|
};
|
|
1160
1084
|
}
|
|
1161
|
-
if (e.event.code
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1085
|
+
if (shouldReauth(e.event.code)) {
|
|
1086
|
+
if (e.event.code === 4109 /* TOKEN_EXPIRED */) {
|
|
1087
|
+
return "@auth.busy";
|
|
1088
|
+
} else {
|
|
1089
|
+
return {
|
|
1090
|
+
target: "@auth.backoff",
|
|
1091
|
+
effect: [increaseBackoffDelay, logCloseEvent(e.event)]
|
|
1092
|
+
};
|
|
1093
|
+
}
|
|
1166
1094
|
}
|
|
1167
|
-
if (
|
|
1095
|
+
if (shouldRetryWithoutReauth(e.event.code)) {
|
|
1168
1096
|
return {
|
|
1169
1097
|
target: "@connecting.backoff",
|
|
1170
|
-
effect: [
|
|
1171
|
-
increaseBackoffDelayAggressively,
|
|
1172
|
-
logCloseEvent(e.event),
|
|
1173
|
-
() => {
|
|
1174
|
-
const err = new LiveblocksError(e.event.reason, e.event.code);
|
|
1175
|
-
onLiveblocksError.notify(err);
|
|
1176
|
-
}
|
|
1177
|
-
]
|
|
1098
|
+
effect: [increaseBackoffDelayAggressively, logCloseEvent(e.event)]
|
|
1178
1099
|
};
|
|
1179
1100
|
}
|
|
1180
1101
|
return {
|
|
@@ -1213,7 +1134,7 @@ function createConnectionStateMachine(delegates, enableDebugLogging) {
|
|
|
1213
1134
|
const cleanups = [];
|
|
1214
1135
|
const { statusDidChange, didConnect, didDisconnect, unsubscribe } = defineConnectivityEvents(machine);
|
|
1215
1136
|
cleanups.push(unsubscribe);
|
|
1216
|
-
if (enableDebugLogging) {
|
|
1137
|
+
if (options.enableDebugLogging) {
|
|
1217
1138
|
cleanups.push(enableTracing(machine));
|
|
1218
1139
|
}
|
|
1219
1140
|
machine.start();
|
|
@@ -1234,7 +1155,7 @@ var ManagedSocket = class {
|
|
|
1234
1155
|
constructor(delegates, enableDebugLogging = false) {
|
|
1235
1156
|
const { machine, events, cleanups } = createConnectionStateMachine(
|
|
1236
1157
|
delegates,
|
|
1237
|
-
enableDebugLogging
|
|
1158
|
+
{ waitForActorId: true, enableDebugLogging }
|
|
1238
1159
|
);
|
|
1239
1160
|
this.machine = machine;
|
|
1240
1161
|
this.events = events;
|
|
@@ -1251,10 +1172,10 @@ var ManagedSocket = class {
|
|
|
1251
1172
|
}
|
|
1252
1173
|
}
|
|
1253
1174
|
/**
|
|
1254
|
-
* Returns the current auth
|
|
1175
|
+
* Returns the current auth authValue.
|
|
1255
1176
|
*/
|
|
1256
|
-
get
|
|
1257
|
-
return this.machine.context.
|
|
1177
|
+
get authValue() {
|
|
1178
|
+
return this.machine.context.authValue;
|
|
1258
1179
|
}
|
|
1259
1180
|
/**
|
|
1260
1181
|
* Call this method to try to connect to a WebSocket. This only has an effect
|
|
@@ -1265,7 +1186,7 @@ var ManagedSocket = class {
|
|
|
1265
1186
|
}
|
|
1266
1187
|
/**
|
|
1267
1188
|
* If idle, will try to connect. Otherwise, it will attempt to reconnect to
|
|
1268
|
-
* the socket, potentially obtaining a new
|
|
1189
|
+
* the socket, potentially obtaining a new authValue first, if needed.
|
|
1269
1190
|
*/
|
|
1270
1191
|
reconnect() {
|
|
1271
1192
|
this.machine.send({ type: "RECONNECT" });
|
|
@@ -1289,28 +1210,444 @@ var ManagedSocket = class {
|
|
|
1289
1210
|
cleanup();
|
|
1290
1211
|
}
|
|
1291
1212
|
}
|
|
1292
|
-
/**
|
|
1293
|
-
* Safely send a message to the current WebSocket connection. Will emit a log
|
|
1294
|
-
* message if this is somehow impossible.
|
|
1295
|
-
*/
|
|
1296
|
-
send(data) {
|
|
1297
|
-
const socket = this.machine.context?.socket;
|
|
1298
|
-
if (socket === null) {
|
|
1299
|
-
warn("Cannot send: not connected yet", data);
|
|
1300
|
-
} else if (socket.readyState !== 1) {
|
|
1301
|
-
warn("Cannot send: WebSocket no longer open", data);
|
|
1302
|
-
} else {
|
|
1303
|
-
socket.send(data);
|
|
1213
|
+
/**
|
|
1214
|
+
* Safely send a message to the current WebSocket connection. Will emit a log
|
|
1215
|
+
* message if this is somehow impossible.
|
|
1216
|
+
*/
|
|
1217
|
+
send(data) {
|
|
1218
|
+
const socket = this.machine.context?.socket;
|
|
1219
|
+
if (socket === null) {
|
|
1220
|
+
warn("Cannot send: not connected yet", data);
|
|
1221
|
+
} else if (socket.readyState !== 1) {
|
|
1222
|
+
warn("Cannot send: WebSocket no longer open", data);
|
|
1223
|
+
} else {
|
|
1224
|
+
socket.send(data);
|
|
1225
|
+
}
|
|
1226
|
+
}
|
|
1227
|
+
/**
|
|
1228
|
+
* NOTE: Used by the E2E app only, to simulate explicit events.
|
|
1229
|
+
* Not ideal to keep exposed :(
|
|
1230
|
+
*/
|
|
1231
|
+
_privateSendMachineEvent(event) {
|
|
1232
|
+
this.machine.send(event);
|
|
1233
|
+
}
|
|
1234
|
+
};
|
|
1235
|
+
|
|
1236
|
+
// src/protocol/AuthToken.ts
|
|
1237
|
+
function canWriteStorage(scopes) {
|
|
1238
|
+
return scopes.includes("room:write" /* Write */);
|
|
1239
|
+
}
|
|
1240
|
+
function isValidAuthTokenPayload(data) {
|
|
1241
|
+
return isPlainObject(data) && (data.k === "acc" /* ACCESS_TOKEN */ || data.k === "id" /* ID_TOKEN */ || data.k === "sec-legacy" /* SECRET_LEGACY */);
|
|
1242
|
+
}
|
|
1243
|
+
function parseAuthToken(rawTokenString) {
|
|
1244
|
+
const tokenParts = rawTokenString.split(".");
|
|
1245
|
+
if (tokenParts.length !== 3) {
|
|
1246
|
+
throw new Error("Authentication error: invalid JWT token");
|
|
1247
|
+
}
|
|
1248
|
+
const payload = tryParseJson(b64decode(tokenParts[1]));
|
|
1249
|
+
if (!(payload && isValidAuthTokenPayload(payload))) {
|
|
1250
|
+
throw new Error(
|
|
1251
|
+
"Authentication error: expected a valid token but did not get one. Hint: if you are using a callback, ensure the room is passed when creating the token. For more information: https://liveblocks.io/docs/api-reference/liveblocks-client#createClientCallback"
|
|
1252
|
+
);
|
|
1253
|
+
}
|
|
1254
|
+
return {
|
|
1255
|
+
raw: rawTokenString,
|
|
1256
|
+
parsed: payload
|
|
1257
|
+
};
|
|
1258
|
+
}
|
|
1259
|
+
|
|
1260
|
+
// src/auth-manager.ts
|
|
1261
|
+
function createAuthManager(authOptions) {
|
|
1262
|
+
const authentication = prepareAuthentication(authOptions);
|
|
1263
|
+
const tokens = [];
|
|
1264
|
+
const expiryTimes = [];
|
|
1265
|
+
const requestPromises = /* @__PURE__ */ new Map();
|
|
1266
|
+
function hasCorrespondingScopes(requestedScope, scopes) {
|
|
1267
|
+
if (requestedScope === "comments:read") {
|
|
1268
|
+
return scopes.includes("comments:read" /* CommentsRead */) || scopes.includes("comments:write" /* CommentsWrite */) || scopes.includes("room:read" /* Read */) || scopes.includes("room:write" /* Write */);
|
|
1269
|
+
} else if (requestedScope === "room:read") {
|
|
1270
|
+
return scopes.includes("room:read" /* Read */) || scopes.includes("room:write" /* Write */);
|
|
1271
|
+
}
|
|
1272
|
+
return false;
|
|
1273
|
+
}
|
|
1274
|
+
function getCachedToken(requestedScope, roomId) {
|
|
1275
|
+
const now = Math.ceil(Date.now() / 1e3);
|
|
1276
|
+
for (let i = tokens.length - 1; i >= 0; i--) {
|
|
1277
|
+
const token = tokens[i];
|
|
1278
|
+
const expiresAt = expiryTimes[i];
|
|
1279
|
+
if (expiresAt <= now) {
|
|
1280
|
+
tokens.splice(i, 1);
|
|
1281
|
+
expiryTimes.splice(i, 1);
|
|
1282
|
+
continue;
|
|
1283
|
+
}
|
|
1284
|
+
if (token.parsed.k === "id" /* ID_TOKEN */) {
|
|
1285
|
+
return token;
|
|
1286
|
+
} else if (token.parsed.k === "sec-legacy" /* SECRET_LEGACY */) {
|
|
1287
|
+
return void 0;
|
|
1288
|
+
} else if (token.parsed.k === "acc" /* ACCESS_TOKEN */) {
|
|
1289
|
+
for (const [resource, scopes] of Object.entries(token.parsed.perms)) {
|
|
1290
|
+
if (resource.includes("*") && roomId.startsWith(resource.replace("*", "")) || roomId === resource && hasCorrespondingScopes(requestedScope, scopes)) {
|
|
1291
|
+
return token;
|
|
1292
|
+
}
|
|
1293
|
+
}
|
|
1294
|
+
}
|
|
1295
|
+
}
|
|
1296
|
+
return void 0;
|
|
1297
|
+
}
|
|
1298
|
+
async function makeAuthRequest(roomId) {
|
|
1299
|
+
const fetcher = authOptions.polyfills?.fetch ?? (typeof window === "undefined" ? void 0 : window.fetch);
|
|
1300
|
+
if (authentication.type === "private") {
|
|
1301
|
+
if (fetcher === void 0) {
|
|
1302
|
+
throw new StopRetrying(
|
|
1303
|
+
"To use Liveblocks client in a non-dom environment with a url as auth endpoint, you need to provide a fetch polyfill."
|
|
1304
|
+
);
|
|
1305
|
+
}
|
|
1306
|
+
const response = await fetchAuthEndpoint(fetcher, authentication.url, {
|
|
1307
|
+
room: roomId
|
|
1308
|
+
});
|
|
1309
|
+
return parseAuthToken(response.token);
|
|
1310
|
+
}
|
|
1311
|
+
if (authentication.type === "custom") {
|
|
1312
|
+
const response = await authentication.callback(roomId);
|
|
1313
|
+
if (!response || typeof response !== "object") {
|
|
1314
|
+
throw new Error(
|
|
1315
|
+
'We expect the authentication callback to return a token, but it does not. Hint: the return value should look like: { token: "..." }'
|
|
1316
|
+
);
|
|
1317
|
+
}
|
|
1318
|
+
if (typeof response.token === "string") {
|
|
1319
|
+
return parseAuthToken(response.token);
|
|
1320
|
+
} else if (typeof response.error === "string") {
|
|
1321
|
+
const reason = `Authentication failed: ${"reason" in response && typeof response.reason === "string" ? response.reason : "Forbidden"}`;
|
|
1322
|
+
if (response.error === "forbidden") {
|
|
1323
|
+
throw new StopRetrying(reason);
|
|
1324
|
+
} else {
|
|
1325
|
+
throw new Error(reason);
|
|
1326
|
+
}
|
|
1327
|
+
} else {
|
|
1328
|
+
throw new Error(
|
|
1329
|
+
'We expect the authentication callback to return a token, but it does not. Hint: the return value should look like: { token: "..." }'
|
|
1330
|
+
);
|
|
1331
|
+
}
|
|
1332
|
+
}
|
|
1333
|
+
throw new Error("Invalid Liveblocks client options");
|
|
1334
|
+
}
|
|
1335
|
+
async function getAuthValue(requestedScope, roomId) {
|
|
1336
|
+
if (authentication.type === "public") {
|
|
1337
|
+
return { type: "public", publicApiKey: authentication.publicApiKey };
|
|
1338
|
+
}
|
|
1339
|
+
const cachedToken = getCachedToken(requestedScope, roomId);
|
|
1340
|
+
if (cachedToken !== void 0) {
|
|
1341
|
+
return { type: "secret", token: cachedToken };
|
|
1342
|
+
}
|
|
1343
|
+
let currentPromise = requestPromises.get(roomId);
|
|
1344
|
+
if (currentPromise === void 0) {
|
|
1345
|
+
currentPromise = makeAuthRequest(roomId);
|
|
1346
|
+
requestPromises.set(roomId, currentPromise);
|
|
1347
|
+
}
|
|
1348
|
+
try {
|
|
1349
|
+
const token = await currentPromise;
|
|
1350
|
+
const BUFFER = 30;
|
|
1351
|
+
const expiresAt = Math.floor(Date.now() / 1e3) + (token.parsed.exp - token.parsed.iat) - BUFFER;
|
|
1352
|
+
tokens.push(token);
|
|
1353
|
+
expiryTimes.push(expiresAt);
|
|
1354
|
+
return { type: "secret", token };
|
|
1355
|
+
} finally {
|
|
1356
|
+
requestPromises.delete(roomId);
|
|
1357
|
+
}
|
|
1358
|
+
}
|
|
1359
|
+
return {
|
|
1360
|
+
getAuthValue
|
|
1361
|
+
};
|
|
1362
|
+
}
|
|
1363
|
+
function prepareAuthentication(authOptions) {
|
|
1364
|
+
const { publicApiKey, authEndpoint } = authOptions;
|
|
1365
|
+
if (authEndpoint !== void 0 && publicApiKey !== void 0) {
|
|
1366
|
+
throw new Error(
|
|
1367
|
+
"You cannot use both publicApiKey and authEndpoint. Please use either publicApiKey or authEndpoint, but not both. For more information: https://liveblocks.io/docs/api-reference/liveblocks-client#createClient"
|
|
1368
|
+
);
|
|
1369
|
+
}
|
|
1370
|
+
if (typeof publicApiKey === "string") {
|
|
1371
|
+
if (publicApiKey.startsWith("sk_")) {
|
|
1372
|
+
throw new Error(
|
|
1373
|
+
"Invalid publicApiKey. You are using the secret key which is not supported. Please use the public key instead. For more information: https://liveblocks.io/docs/api-reference/liveblocks-client#createClientPublicKey"
|
|
1374
|
+
);
|
|
1375
|
+
} else if (!publicApiKey.startsWith("pk_")) {
|
|
1376
|
+
throw new Error(
|
|
1377
|
+
"Invalid key. Please use the public key format: pk_<public key>. For more information: https://liveblocks.io/docs/api-reference/liveblocks-client#createClientPublicKey"
|
|
1378
|
+
);
|
|
1379
|
+
}
|
|
1380
|
+
return {
|
|
1381
|
+
type: "public",
|
|
1382
|
+
publicApiKey
|
|
1383
|
+
};
|
|
1384
|
+
}
|
|
1385
|
+
if (typeof authEndpoint === "string") {
|
|
1386
|
+
return {
|
|
1387
|
+
type: "private",
|
|
1388
|
+
url: authEndpoint
|
|
1389
|
+
};
|
|
1390
|
+
} else if (typeof authEndpoint === "function") {
|
|
1391
|
+
return {
|
|
1392
|
+
type: "custom",
|
|
1393
|
+
callback: authEndpoint
|
|
1394
|
+
};
|
|
1395
|
+
} else if (authEndpoint !== void 0) {
|
|
1396
|
+
throw new Error(
|
|
1397
|
+
"authEndpoint must be a string or a function. For more information: https://liveblocks.io/docs/api-reference/liveblocks-client#createClientAuthEndpoint"
|
|
1398
|
+
);
|
|
1399
|
+
}
|
|
1400
|
+
throw new Error(
|
|
1401
|
+
"Invalid Liveblocks client options. For more information: https://liveblocks.io/docs/api-reference/liveblocks-client#createClient"
|
|
1402
|
+
);
|
|
1403
|
+
}
|
|
1404
|
+
async function fetchAuthEndpoint(fetch2, endpoint, body) {
|
|
1405
|
+
const res = await fetch2(endpoint, {
|
|
1406
|
+
method: "POST",
|
|
1407
|
+
headers: {
|
|
1408
|
+
"Content-Type": "application/json"
|
|
1409
|
+
},
|
|
1410
|
+
body: JSON.stringify(body)
|
|
1411
|
+
});
|
|
1412
|
+
if (!res.ok) {
|
|
1413
|
+
const reason = `${(await res.text()).trim() || "reason not provided in auth response"} (${res.status} returned by POST ${endpoint})`;
|
|
1414
|
+
if (res.status === 401 || res.status === 403) {
|
|
1415
|
+
throw new StopRetrying(`Unauthorized: ${reason}`);
|
|
1416
|
+
} else {
|
|
1417
|
+
throw new Error(`Failed to authenticate: ${reason}`);
|
|
1418
|
+
}
|
|
1419
|
+
}
|
|
1420
|
+
let data;
|
|
1421
|
+
try {
|
|
1422
|
+
data = await res.json();
|
|
1423
|
+
} catch (er) {
|
|
1424
|
+
throw new Error(
|
|
1425
|
+
`Expected a JSON response when doing a POST request on "${endpoint}". ${String(
|
|
1426
|
+
er
|
|
1427
|
+
)}`
|
|
1428
|
+
);
|
|
1429
|
+
}
|
|
1430
|
+
if (!isPlainObject(data) || typeof data.token !== "string") {
|
|
1431
|
+
throw new Error(
|
|
1432
|
+
`Expected a JSON response of the form \`{ token: "..." }\` when doing a POST request on "${endpoint}", but got ${JSON.stringify(
|
|
1433
|
+
data
|
|
1434
|
+
)}`
|
|
1435
|
+
);
|
|
1436
|
+
}
|
|
1437
|
+
const { token } = data;
|
|
1438
|
+
return { token };
|
|
1439
|
+
}
|
|
1440
|
+
|
|
1441
|
+
// src/devtools/bridge.ts
|
|
1442
|
+
var _bridgeActive = false;
|
|
1443
|
+
function activateBridge(allowed) {
|
|
1444
|
+
_bridgeActive = allowed;
|
|
1445
|
+
}
|
|
1446
|
+
function sendToPanel(message, options) {
|
|
1447
|
+
if (process.env.NODE_ENV === "production" || typeof window === "undefined") {
|
|
1448
|
+
return;
|
|
1449
|
+
}
|
|
1450
|
+
const fullMsg = {
|
|
1451
|
+
...message,
|
|
1452
|
+
source: "liveblocks-devtools-client"
|
|
1453
|
+
};
|
|
1454
|
+
if (!(options?.force || _bridgeActive)) {
|
|
1455
|
+
return;
|
|
1456
|
+
}
|
|
1457
|
+
window.postMessage(fullMsg, "*");
|
|
1458
|
+
}
|
|
1459
|
+
var eventSource = makeEventSource();
|
|
1460
|
+
if (process.env.NODE_ENV !== "production" && typeof window !== "undefined") {
|
|
1461
|
+
window.addEventListener("message", (event) => {
|
|
1462
|
+
if (event.source === window && event.data?.source === "liveblocks-devtools-panel") {
|
|
1463
|
+
eventSource.notify(event.data);
|
|
1464
|
+
} else {
|
|
1465
|
+
}
|
|
1466
|
+
});
|
|
1467
|
+
}
|
|
1468
|
+
var onMessageFromPanel = eventSource.observable;
|
|
1469
|
+
|
|
1470
|
+
// src/devtools/index.ts
|
|
1471
|
+
var VERSION = PKG_VERSION || "dev";
|
|
1472
|
+
var _devtoolsSetupHasRun = false;
|
|
1473
|
+
function setupDevTools(getAllRooms) {
|
|
1474
|
+
if (process.env.NODE_ENV === "production" || typeof window === "undefined") {
|
|
1475
|
+
return;
|
|
1476
|
+
}
|
|
1477
|
+
if (_devtoolsSetupHasRun) {
|
|
1478
|
+
return;
|
|
1479
|
+
}
|
|
1480
|
+
_devtoolsSetupHasRun = true;
|
|
1481
|
+
onMessageFromPanel.subscribe((msg) => {
|
|
1482
|
+
switch (msg.msg) {
|
|
1483
|
+
case "connect": {
|
|
1484
|
+
activateBridge(true);
|
|
1485
|
+
for (const roomId of getAllRooms()) {
|
|
1486
|
+
sendToPanel({
|
|
1487
|
+
msg: "room::available",
|
|
1488
|
+
roomId,
|
|
1489
|
+
clientVersion: VERSION
|
|
1490
|
+
});
|
|
1491
|
+
}
|
|
1492
|
+
break;
|
|
1493
|
+
}
|
|
1494
|
+
}
|
|
1495
|
+
});
|
|
1496
|
+
sendToPanel({ msg: "wake-up-devtools" }, { force: true });
|
|
1497
|
+
}
|
|
1498
|
+
var unsubsByRoomId = /* @__PURE__ */ new Map();
|
|
1499
|
+
function stopSyncStream(roomId) {
|
|
1500
|
+
const unsubs = unsubsByRoomId.get(roomId) ?? [];
|
|
1501
|
+
unsubsByRoomId.delete(roomId);
|
|
1502
|
+
for (const unsub of unsubs) {
|
|
1503
|
+
unsub();
|
|
1504
|
+
}
|
|
1505
|
+
}
|
|
1506
|
+
function startSyncStream(room) {
|
|
1507
|
+
stopSyncStream(room.id);
|
|
1508
|
+
fullSync(room);
|
|
1509
|
+
unsubsByRoomId.set(room.id, [
|
|
1510
|
+
// When the connection status changes
|
|
1511
|
+
room.events.status.subscribe(() => partialSyncConnection(room)),
|
|
1512
|
+
// When storage initializes, send the update
|
|
1513
|
+
room.events.storageDidLoad.subscribeOnce(() => partialSyncStorage(room)),
|
|
1514
|
+
// Any time storage updates, send the new storage root
|
|
1515
|
+
room.events.storage.subscribe(() => partialSyncStorage(room)),
|
|
1516
|
+
// Any time "me" or "others" updates, send the new values accordingly
|
|
1517
|
+
room.events.self.subscribe(() => partialSyncMe(room)),
|
|
1518
|
+
room.events.others.subscribe(() => partialSyncOthers(room))
|
|
1519
|
+
]);
|
|
1520
|
+
}
|
|
1521
|
+
function partialSyncConnection(room) {
|
|
1522
|
+
sendToPanel({
|
|
1523
|
+
msg: "room::sync::partial",
|
|
1524
|
+
roomId: room.id,
|
|
1525
|
+
status: room.getStatus()
|
|
1526
|
+
});
|
|
1527
|
+
}
|
|
1528
|
+
function partialSyncStorage(room) {
|
|
1529
|
+
const root = room.getStorageSnapshot();
|
|
1530
|
+
if (root) {
|
|
1531
|
+
sendToPanel({
|
|
1532
|
+
msg: "room::sync::partial",
|
|
1533
|
+
roomId: room.id,
|
|
1534
|
+
storage: root.toTreeNode("root").payload
|
|
1535
|
+
});
|
|
1536
|
+
}
|
|
1537
|
+
}
|
|
1538
|
+
function partialSyncMe(room) {
|
|
1539
|
+
const me = room.__internal.getSelf_forDevTools();
|
|
1540
|
+
if (me) {
|
|
1541
|
+
sendToPanel({
|
|
1542
|
+
msg: "room::sync::partial",
|
|
1543
|
+
roomId: room.id,
|
|
1544
|
+
me
|
|
1545
|
+
});
|
|
1546
|
+
}
|
|
1547
|
+
}
|
|
1548
|
+
function partialSyncOthers(room) {
|
|
1549
|
+
const others = room.__internal.getOthers_forDevTools();
|
|
1550
|
+
if (others) {
|
|
1551
|
+
sendToPanel({
|
|
1552
|
+
msg: "room::sync::partial",
|
|
1553
|
+
roomId: room.id,
|
|
1554
|
+
others
|
|
1555
|
+
});
|
|
1556
|
+
}
|
|
1557
|
+
}
|
|
1558
|
+
function fullSync(room) {
|
|
1559
|
+
const root = room.getStorageSnapshot();
|
|
1560
|
+
const me = room.__internal.getSelf_forDevTools();
|
|
1561
|
+
const others = room.__internal.getOthers_forDevTools();
|
|
1562
|
+
sendToPanel({
|
|
1563
|
+
msg: "room::sync::full",
|
|
1564
|
+
roomId: room.id,
|
|
1565
|
+
status: room.getStatus(),
|
|
1566
|
+
storage: root?.toTreeNode("root").payload ?? null,
|
|
1567
|
+
me,
|
|
1568
|
+
others
|
|
1569
|
+
});
|
|
1570
|
+
}
|
|
1571
|
+
var roomChannelListeners = /* @__PURE__ */ new Map();
|
|
1572
|
+
function stopRoomChannelListener(roomId) {
|
|
1573
|
+
const listener = roomChannelListeners.get(roomId);
|
|
1574
|
+
roomChannelListeners.delete(roomId);
|
|
1575
|
+
if (listener) {
|
|
1576
|
+
listener();
|
|
1577
|
+
}
|
|
1578
|
+
}
|
|
1579
|
+
function linkDevTools(roomId, room) {
|
|
1580
|
+
if (process.env.NODE_ENV === "production" || typeof window === "undefined") {
|
|
1581
|
+
return;
|
|
1582
|
+
}
|
|
1583
|
+
sendToPanel({ msg: "room::available", roomId, clientVersion: VERSION });
|
|
1584
|
+
stopRoomChannelListener(roomId);
|
|
1585
|
+
roomChannelListeners.set(
|
|
1586
|
+
roomId,
|
|
1587
|
+
// Returns the unsubscribe callback, that we store in the
|
|
1588
|
+
// roomChannelListeners registry
|
|
1589
|
+
onMessageFromPanel.subscribe((msg) => {
|
|
1590
|
+
switch (msg.msg) {
|
|
1591
|
+
case "room::subscribe": {
|
|
1592
|
+
if (msg.roomId === roomId) {
|
|
1593
|
+
startSyncStream(room);
|
|
1594
|
+
}
|
|
1595
|
+
break;
|
|
1596
|
+
}
|
|
1597
|
+
case "room::unsubscribe": {
|
|
1598
|
+
if (msg.roomId === roomId) {
|
|
1599
|
+
stopSyncStream(roomId);
|
|
1600
|
+
}
|
|
1601
|
+
break;
|
|
1602
|
+
}
|
|
1603
|
+
}
|
|
1604
|
+
})
|
|
1605
|
+
);
|
|
1606
|
+
}
|
|
1607
|
+
function unlinkDevTools(roomId) {
|
|
1608
|
+
if (process.env.NODE_ENV === "production" || typeof window === "undefined") {
|
|
1609
|
+
return;
|
|
1610
|
+
}
|
|
1611
|
+
stopSyncStream(roomId);
|
|
1612
|
+
stopRoomChannelListener(roomId);
|
|
1613
|
+
sendToPanel({
|
|
1614
|
+
msg: "room::unavailable",
|
|
1615
|
+
roomId
|
|
1616
|
+
});
|
|
1617
|
+
}
|
|
1618
|
+
|
|
1619
|
+
// src/lib/deprecation.ts
|
|
1620
|
+
var _emittedDeprecationWarnings = /* @__PURE__ */ new Set();
|
|
1621
|
+
function deprecate(message, key = message) {
|
|
1622
|
+
if (process.env.NODE_ENV !== "production") {
|
|
1623
|
+
if (!_emittedDeprecationWarnings.has(key)) {
|
|
1624
|
+
_emittedDeprecationWarnings.add(key);
|
|
1625
|
+
errorWithTitle("Deprecation warning", message);
|
|
1626
|
+
}
|
|
1627
|
+
}
|
|
1628
|
+
}
|
|
1629
|
+
function deprecateIf(condition, message, key = message) {
|
|
1630
|
+
if (process.env.NODE_ENV !== "production") {
|
|
1631
|
+
if (condition) {
|
|
1632
|
+
deprecate(message, key);
|
|
1304
1633
|
}
|
|
1305
1634
|
}
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1635
|
+
}
|
|
1636
|
+
function throwUsageError(message) {
|
|
1637
|
+
if (process.env.NODE_ENV !== "production") {
|
|
1638
|
+
const usageError = new Error(message);
|
|
1639
|
+
usageError.name = "Usage error";
|
|
1640
|
+
errorWithTitle("Usage error", message);
|
|
1641
|
+
throw usageError;
|
|
1312
1642
|
}
|
|
1313
|
-
}
|
|
1643
|
+
}
|
|
1644
|
+
function errorIf(condition, message) {
|
|
1645
|
+
if (process.env.NODE_ENV !== "production") {
|
|
1646
|
+
if (condition) {
|
|
1647
|
+
throwUsageError(message);
|
|
1648
|
+
}
|
|
1649
|
+
}
|
|
1650
|
+
}
|
|
1314
1651
|
|
|
1315
1652
|
// src/lib/position.ts
|
|
1316
1653
|
var MIN_CODE = 32;
|
|
@@ -3844,35 +4181,6 @@ function isJsonObject(data) {
|
|
|
3844
4181
|
return !isJsonScalar(data) && !isJsonArray(data);
|
|
3845
4182
|
}
|
|
3846
4183
|
|
|
3847
|
-
// src/protocol/AuthToken.ts
|
|
3848
|
-
function isTokenExpired(token) {
|
|
3849
|
-
const now = Date.now() / 1e3;
|
|
3850
|
-
const valid = now <= token.exp - 300 && now >= token.iat - 300;
|
|
3851
|
-
return !valid;
|
|
3852
|
-
}
|
|
3853
|
-
function isStringList(value) {
|
|
3854
|
-
return Array.isArray(value) && value.every((i) => typeof i === "string");
|
|
3855
|
-
}
|
|
3856
|
-
function isMinimalTokenPayload(data) {
|
|
3857
|
-
return isPlainObject(data) && typeof data.iat === "number" && typeof data.exp === "number" && typeof data.actor === "number" && (data.id === void 0 || typeof data.id === "string") && isStringList(data.scopes);
|
|
3858
|
-
}
|
|
3859
|
-
function parseAuthToken(rawTokenString) {
|
|
3860
|
-
const tokenParts = rawTokenString.split(".");
|
|
3861
|
-
if (tokenParts.length !== 3) {
|
|
3862
|
-
throw new Error("Authentication error: invalid JWT token");
|
|
3863
|
-
}
|
|
3864
|
-
const payload = tryParseJson(b64decode(tokenParts[1]));
|
|
3865
|
-
if (!(payload && isMinimalTokenPayload(payload))) {
|
|
3866
|
-
throw new Error(
|
|
3867
|
-
"Authentication error: we expected a room token but did not get one. Hint: if you are using a callback, ensure the room is passed when creating the token. For more information: https://liveblocks.io/docs/api-reference/liveblocks-client#createClientCallback"
|
|
3868
|
-
);
|
|
3869
|
-
}
|
|
3870
|
-
return {
|
|
3871
|
-
raw: rawTokenString,
|
|
3872
|
-
parsed: payload
|
|
3873
|
-
};
|
|
3874
|
-
}
|
|
3875
|
-
|
|
3876
4184
|
// src/protocol/ClientMsg.ts
|
|
3877
4185
|
var ClientMsgCode = /* @__PURE__ */ ((ClientMsgCode2) => {
|
|
3878
4186
|
ClientMsgCode2[ClientMsgCode2["UPDATE_PRESENCE"] = 100] = "UPDATE_PRESENCE";
|
|
@@ -3884,20 +4192,6 @@ var ClientMsgCode = /* @__PURE__ */ ((ClientMsgCode2) => {
|
|
|
3884
4192
|
return ClientMsgCode2;
|
|
3885
4193
|
})(ClientMsgCode || {});
|
|
3886
4194
|
|
|
3887
|
-
// src/protocol/ServerMsg.ts
|
|
3888
|
-
var ServerMsgCode = /* @__PURE__ */ ((ServerMsgCode2) => {
|
|
3889
|
-
ServerMsgCode2[ServerMsgCode2["UPDATE_PRESENCE"] = 100] = "UPDATE_PRESENCE";
|
|
3890
|
-
ServerMsgCode2[ServerMsgCode2["USER_JOINED"] = 101] = "USER_JOINED";
|
|
3891
|
-
ServerMsgCode2[ServerMsgCode2["USER_LEFT"] = 102] = "USER_LEFT";
|
|
3892
|
-
ServerMsgCode2[ServerMsgCode2["BROADCASTED_EVENT"] = 103] = "BROADCASTED_EVENT";
|
|
3893
|
-
ServerMsgCode2[ServerMsgCode2["ROOM_STATE"] = 104] = "ROOM_STATE";
|
|
3894
|
-
ServerMsgCode2[ServerMsgCode2["INITIAL_STORAGE_STATE"] = 200] = "INITIAL_STORAGE_STATE";
|
|
3895
|
-
ServerMsgCode2[ServerMsgCode2["UPDATE_STORAGE"] = 201] = "UPDATE_STORAGE";
|
|
3896
|
-
ServerMsgCode2[ServerMsgCode2["REJECT_STORAGE_OP"] = 299] = "REJECT_STORAGE_OP";
|
|
3897
|
-
ServerMsgCode2[ServerMsgCode2["UPDATE_YDOC"] = 300] = "UPDATE_YDOC";
|
|
3898
|
-
return ServerMsgCode2;
|
|
3899
|
-
})(ServerMsgCode || {});
|
|
3900
|
-
|
|
3901
4195
|
// src/lib/LegacyArray.ts
|
|
3902
4196
|
function asArrayWithLegacyMethods(arr) {
|
|
3903
4197
|
Object.defineProperty(arr, "count", {
|
|
@@ -3949,7 +4243,19 @@ var ImmutableRef = class {
|
|
|
3949
4243
|
|
|
3950
4244
|
// src/refs/OthersRef.ts
|
|
3951
4245
|
function makeUser(conn, presence) {
|
|
3952
|
-
|
|
4246
|
+
const { connectionId, id, info } = conn;
|
|
4247
|
+
const canWrite = canWriteStorage(conn.scopes);
|
|
4248
|
+
return freeze(
|
|
4249
|
+
compactObject({
|
|
4250
|
+
connectionId,
|
|
4251
|
+
id,
|
|
4252
|
+
info,
|
|
4253
|
+
canWrite,
|
|
4254
|
+
isReadOnly: !canWrite,
|
|
4255
|
+
// Deprecated, kept for backward-compatibility
|
|
4256
|
+
presence
|
|
4257
|
+
})
|
|
4258
|
+
);
|
|
3953
4259
|
}
|
|
3954
4260
|
var OthersRef = class extends ImmutableRef {
|
|
3955
4261
|
//
|
|
@@ -3957,50 +4263,53 @@ var OthersRef = class extends ImmutableRef {
|
|
|
3957
4263
|
//
|
|
3958
4264
|
constructor() {
|
|
3959
4265
|
super();
|
|
3960
|
-
this._connections =
|
|
3961
|
-
this._presences =
|
|
3962
|
-
this._users =
|
|
4266
|
+
this._connections = /* @__PURE__ */ new Map();
|
|
4267
|
+
this._presences = /* @__PURE__ */ new Map();
|
|
4268
|
+
this._users = /* @__PURE__ */ new Map();
|
|
4269
|
+
}
|
|
4270
|
+
connectionIds() {
|
|
4271
|
+
return this._connections.keys();
|
|
3963
4272
|
}
|
|
3964
4273
|
/** @internal */
|
|
3965
4274
|
_toImmutable() {
|
|
3966
4275
|
const users = compact(
|
|
3967
|
-
|
|
4276
|
+
Array.from(this._presences.keys()).map(
|
|
3968
4277
|
(connectionId) => this.getUser(Number(connectionId))
|
|
3969
4278
|
)
|
|
3970
4279
|
);
|
|
3971
4280
|
return asArrayWithLegacyMethods(users);
|
|
3972
4281
|
}
|
|
3973
4282
|
clearOthers() {
|
|
3974
|
-
this._connections =
|
|
3975
|
-
this._presences =
|
|
3976
|
-
this._users =
|
|
4283
|
+
this._connections = /* @__PURE__ */ new Map();
|
|
4284
|
+
this._presences = /* @__PURE__ */ new Map();
|
|
4285
|
+
this._users = /* @__PURE__ */ new Map();
|
|
3977
4286
|
this.invalidate();
|
|
3978
4287
|
}
|
|
3979
4288
|
/** @internal */
|
|
3980
4289
|
_getUser(connectionId) {
|
|
3981
|
-
const conn = this._connections
|
|
3982
|
-
const presence = this._presences
|
|
4290
|
+
const conn = this._connections.get(connectionId);
|
|
4291
|
+
const presence = this._presences.get(connectionId);
|
|
3983
4292
|
if (conn !== void 0 && presence !== void 0) {
|
|
3984
4293
|
return makeUser(conn, presence);
|
|
3985
4294
|
}
|
|
3986
4295
|
return void 0;
|
|
3987
4296
|
}
|
|
3988
4297
|
getUser(connectionId) {
|
|
3989
|
-
const cachedUser = this._users
|
|
4298
|
+
const cachedUser = this._users.get(connectionId);
|
|
3990
4299
|
if (cachedUser) {
|
|
3991
4300
|
return cachedUser;
|
|
3992
4301
|
}
|
|
3993
4302
|
const computedUser = this._getUser(connectionId);
|
|
3994
4303
|
if (computedUser) {
|
|
3995
|
-
this._users
|
|
4304
|
+
this._users.set(connectionId, computedUser);
|
|
3996
4305
|
return computedUser;
|
|
3997
4306
|
}
|
|
3998
4307
|
return void 0;
|
|
3999
4308
|
}
|
|
4000
4309
|
/** @internal */
|
|
4001
4310
|
_invalidateUser(connectionId) {
|
|
4002
|
-
if (this._users
|
|
4003
|
-
|
|
4311
|
+
if (this._users.has(connectionId)) {
|
|
4312
|
+
this._users.delete(connectionId);
|
|
4004
4313
|
}
|
|
4005
4314
|
this.invalidate();
|
|
4006
4315
|
}
|
|
@@ -4008,14 +4317,17 @@ var OthersRef = class extends ImmutableRef {
|
|
|
4008
4317
|
* Records a known connection. This records the connection ID and the
|
|
4009
4318
|
* associated metadata.
|
|
4010
4319
|
*/
|
|
4011
|
-
setConnection(connectionId, metaUserId, metaUserInfo,
|
|
4012
|
-
this._connections
|
|
4320
|
+
setConnection(connectionId, metaUserId, metaUserInfo, scopes) {
|
|
4321
|
+
this._connections.set(
|
|
4013
4322
|
connectionId,
|
|
4014
|
-
|
|
4015
|
-
|
|
4016
|
-
|
|
4017
|
-
|
|
4018
|
-
|
|
4323
|
+
freeze({
|
|
4324
|
+
connectionId,
|
|
4325
|
+
id: metaUserId,
|
|
4326
|
+
info: metaUserInfo,
|
|
4327
|
+
scopes
|
|
4328
|
+
})
|
|
4329
|
+
);
|
|
4330
|
+
if (this._presences.has(connectionId)) {
|
|
4019
4331
|
this._invalidateUser(connectionId);
|
|
4020
4332
|
}
|
|
4021
4333
|
}
|
|
@@ -4024,8 +4336,8 @@ var OthersRef = class extends ImmutableRef {
|
|
|
4024
4336
|
* the presence information.
|
|
4025
4337
|
*/
|
|
4026
4338
|
removeConnection(connectionId) {
|
|
4027
|
-
|
|
4028
|
-
|
|
4339
|
+
this._connections.delete(connectionId);
|
|
4340
|
+
this._presences.delete(connectionId);
|
|
4029
4341
|
this._invalidateUser(connectionId);
|
|
4030
4342
|
}
|
|
4031
4343
|
/**
|
|
@@ -4033,8 +4345,8 @@ var OthersRef = class extends ImmutableRef {
|
|
|
4033
4345
|
* its known presence data is overwritten.
|
|
4034
4346
|
*/
|
|
4035
4347
|
setOther(connectionId, presence) {
|
|
4036
|
-
this._presences
|
|
4037
|
-
if (this._connections
|
|
4348
|
+
this._presences.set(connectionId, freeze(compactObject(presence)));
|
|
4349
|
+
if (this._connections.has(connectionId)) {
|
|
4038
4350
|
this._invalidateUser(connectionId);
|
|
4039
4351
|
}
|
|
4040
4352
|
}
|
|
@@ -4044,13 +4356,13 @@ var OthersRef = class extends ImmutableRef {
|
|
|
4044
4356
|
* full .setOther() call first.
|
|
4045
4357
|
*/
|
|
4046
4358
|
patchOther(connectionId, patch) {
|
|
4047
|
-
const oldPresence = this._presences
|
|
4359
|
+
const oldPresence = this._presences.get(connectionId);
|
|
4048
4360
|
if (oldPresence === void 0) {
|
|
4049
4361
|
return;
|
|
4050
4362
|
}
|
|
4051
4363
|
const newPresence = merge(oldPresence, patch);
|
|
4052
4364
|
if (oldPresence !== newPresence) {
|
|
4053
|
-
this._presences
|
|
4365
|
+
this._presences.set(connectionId, freeze(newPresence));
|
|
4054
4366
|
this._invalidateUser(connectionId);
|
|
4055
4367
|
}
|
|
4056
4368
|
}
|
|
@@ -4070,10 +4382,10 @@ var PatchableRef = class extends ImmutableRef {
|
|
|
4070
4382
|
* Patches the current object.
|
|
4071
4383
|
*/
|
|
4072
4384
|
patch(patch) {
|
|
4073
|
-
const
|
|
4074
|
-
const
|
|
4075
|
-
if (
|
|
4076
|
-
this._data = freeze(
|
|
4385
|
+
const oldData = this._data;
|
|
4386
|
+
const newData = merge(oldData, patch);
|
|
4387
|
+
if (oldData !== newData) {
|
|
4388
|
+
this._data = freeze(newData);
|
|
4077
4389
|
this.invalidate();
|
|
4078
4390
|
}
|
|
4079
4391
|
}
|
|
@@ -4130,17 +4442,7 @@ function userToTreeNode(key, user) {
|
|
|
4130
4442
|
function createRoom(options, config) {
|
|
4131
4443
|
const initialPresence = typeof options.initialPresence === "function" ? options.initialPresence(config.roomId) : options.initialPresence;
|
|
4132
4444
|
const initialStorage = typeof options.initialStorage === "function" ? options.initialStorage(config.roomId) : options.initialStorage;
|
|
4133
|
-
const delegates = config.delegates
|
|
4134
|
-
authenticate: makeAuthDelegateForRoom(
|
|
4135
|
-
config.roomId,
|
|
4136
|
-
config.authentication,
|
|
4137
|
-
config.polyfills?.fetch
|
|
4138
|
-
),
|
|
4139
|
-
createSocket: makeCreateSocketDelegateForRoom(
|
|
4140
|
-
config.liveblocksServer,
|
|
4141
|
-
config.polyfills?.WebSocket
|
|
4142
|
-
)
|
|
4143
|
-
};
|
|
4445
|
+
const delegates = config.delegates;
|
|
4144
4446
|
const managedSocket = new ManagedSocket(
|
|
4145
4447
|
delegates,
|
|
4146
4448
|
config.enableDebugLogging
|
|
@@ -4149,7 +4451,7 @@ function createRoom(options, config) {
|
|
|
4149
4451
|
buffer: {
|
|
4150
4452
|
flushTimerID: void 0,
|
|
4151
4453
|
lastFlushedAt: 0,
|
|
4152
|
-
|
|
4454
|
+
presenceUpdates: (
|
|
4153
4455
|
// Queue up the initial presence message as a Full Presence™ update
|
|
4154
4456
|
{
|
|
4155
4457
|
type: "full",
|
|
@@ -4159,8 +4461,9 @@ function createRoom(options, config) {
|
|
|
4159
4461
|
messages: [],
|
|
4160
4462
|
storageOperations: []
|
|
4161
4463
|
},
|
|
4162
|
-
|
|
4163
|
-
|
|
4464
|
+
staticSessionInfo: new ValueRef(null),
|
|
4465
|
+
dynamicSessionInfo: new ValueRef(null),
|
|
4466
|
+
myPresence: new PatchableRef(initialPresence),
|
|
4164
4467
|
others: new OthersRef(),
|
|
4165
4468
|
initialStorage,
|
|
4166
4469
|
idFactory: null,
|
|
@@ -4179,22 +4482,31 @@ function createRoom(options, config) {
|
|
|
4179
4482
|
};
|
|
4180
4483
|
const doNotBatchUpdates = (cb) => cb();
|
|
4181
4484
|
const batchUpdates = config.unstable_batchedUpdates ?? doNotBatchUpdates;
|
|
4182
|
-
let
|
|
4485
|
+
let lastTokenKey;
|
|
4183
4486
|
function onStatusDidChange(newStatus) {
|
|
4184
|
-
const
|
|
4185
|
-
if (
|
|
4186
|
-
|
|
4187
|
-
|
|
4188
|
-
|
|
4189
|
-
|
|
4190
|
-
|
|
4191
|
-
|
|
4192
|
-
|
|
4193
|
-
|
|
4487
|
+
const authValue = managedSocket.authValue;
|
|
4488
|
+
if (authValue !== null) {
|
|
4489
|
+
const tokenKey = authValue.type === "secret" ? authValue.token.raw : authValue.publicApiKey;
|
|
4490
|
+
if (tokenKey !== lastTokenKey) {
|
|
4491
|
+
lastTokenKey = tokenKey;
|
|
4492
|
+
if (authValue.type === "secret") {
|
|
4493
|
+
const token = authValue.token.parsed;
|
|
4494
|
+
context.staticSessionInfo.set({
|
|
4495
|
+
userId: token.k === "sec-legacy" /* SECRET_LEGACY */ ? token.id : token.uid,
|
|
4496
|
+
userInfo: token.k === "sec-legacy" /* SECRET_LEGACY */ ? token.info : token.ui
|
|
4497
|
+
});
|
|
4498
|
+
} else {
|
|
4499
|
+
context.staticSessionInfo.set({
|
|
4500
|
+
userId: void 0,
|
|
4501
|
+
userInfo: void 0
|
|
4502
|
+
});
|
|
4503
|
+
}
|
|
4504
|
+
}
|
|
4194
4505
|
}
|
|
4195
4506
|
batchUpdates(() => {
|
|
4196
4507
|
eventHub.status.notify(newStatus);
|
|
4197
4508
|
eventHub.connection.notify(newToLegacyStatus(newStatus));
|
|
4509
|
+
notifySelfChanged(doNotBatchUpdates);
|
|
4198
4510
|
});
|
|
4199
4511
|
}
|
|
4200
4512
|
let _connectionLossTimerId;
|
|
@@ -4226,20 +4538,15 @@ function createRoom(options, config) {
|
|
|
4226
4538
|
}
|
|
4227
4539
|
}
|
|
4228
4540
|
function onDidConnect() {
|
|
4229
|
-
|
|
4230
|
-
if (sessionInfo === null) {
|
|
4231
|
-
throw new Error("Unexpected missing session info");
|
|
4232
|
-
}
|
|
4233
|
-
context.buffer.me = {
|
|
4541
|
+
context.buffer.presenceUpdates = {
|
|
4234
4542
|
type: "full",
|
|
4235
4543
|
data: (
|
|
4236
4544
|
// Because context.me.current is a readonly object, we'll have to
|
|
4237
4545
|
// make a copy here. Otherwise, type errors happen later when
|
|
4238
4546
|
// "patching" my presence.
|
|
4239
|
-
{ ...context.
|
|
4547
|
+
{ ...context.myPresence.current }
|
|
4240
4548
|
)
|
|
4241
4549
|
};
|
|
4242
|
-
context.idFactory = makeIdFactory(sessionInfo.actor);
|
|
4243
4550
|
if (_getStorage$ !== null) {
|
|
4244
4551
|
refreshStorage({ flush: false });
|
|
4245
4552
|
}
|
|
@@ -4256,7 +4563,7 @@ function createRoom(options, config) {
|
|
|
4256
4563
|
managedSocket.events.onLiveblocksError.subscribe((err) => {
|
|
4257
4564
|
batchUpdates(() => {
|
|
4258
4565
|
if (process.env.NODE_ENV !== "production") {
|
|
4259
|
-
|
|
4566
|
+
error2(
|
|
4260
4567
|
`Connection to websocket server closed. Reason: ${err.message} (code: ${err.code}).`
|
|
4261
4568
|
);
|
|
4262
4569
|
}
|
|
@@ -4304,7 +4611,12 @@ function createRoom(options, config) {
|
|
|
4304
4611
|
}
|
|
4305
4612
|
},
|
|
4306
4613
|
assertStorageIsWritable: () => {
|
|
4307
|
-
|
|
4614
|
+
const scopes = context.dynamicSessionInfo.current?.scopes;
|
|
4615
|
+
if (scopes === void 0) {
|
|
4616
|
+
return;
|
|
4617
|
+
}
|
|
4618
|
+
const canWrite = canWriteStorage(scopes);
|
|
4619
|
+
if (!canWrite) {
|
|
4308
4620
|
throw new Error(
|
|
4309
4621
|
"Cannot write to storage with a read only user, please ensure the user has write permissions"
|
|
4310
4622
|
);
|
|
@@ -4318,7 +4630,8 @@ function createRoom(options, config) {
|
|
|
4318
4630
|
// New/recommended API
|
|
4319
4631
|
lostConnection: makeEventSource(),
|
|
4320
4632
|
customEvent: makeEventSource(),
|
|
4321
|
-
|
|
4633
|
+
self: makeEventSource(),
|
|
4634
|
+
myPresence: makeEventSource(),
|
|
4322
4635
|
others: makeEventSource(),
|
|
4323
4636
|
error: makeEventSource(),
|
|
4324
4637
|
storage: makeEventSource(),
|
|
@@ -4331,16 +4644,18 @@ function createRoom(options, config) {
|
|
|
4331
4644
|
const message = JSON.stringify(messageOrMessages);
|
|
4332
4645
|
if (config.unstable_fallbackToHTTP) {
|
|
4333
4646
|
const size = new TextEncoder().encode(message).length;
|
|
4334
|
-
if (size > MAX_MESSAGE_SIZE &&
|
|
4335
|
-
|
|
4336
|
-
return managedSocket.reconnect();
|
|
4337
|
-
}
|
|
4647
|
+
if (size > MAX_MESSAGE_SIZE && // TODO: support public api key auth in REST API
|
|
4648
|
+
managedSocket.authValue?.type === "secret" && config.httpSendEndpoint) {
|
|
4338
4649
|
void httpSend(
|
|
4339
4650
|
message,
|
|
4340
|
-
managedSocket.token.raw,
|
|
4651
|
+
managedSocket.authValue.token.raw,
|
|
4341
4652
|
config.httpSendEndpoint,
|
|
4342
4653
|
config.polyfills?.fetch
|
|
4343
|
-
)
|
|
4654
|
+
).then((resp) => {
|
|
4655
|
+
if (!resp.ok && resp.status === 403) {
|
|
4656
|
+
managedSocket.reconnect();
|
|
4657
|
+
}
|
|
4658
|
+
});
|
|
4344
4659
|
warn(
|
|
4345
4660
|
"Message was too large for websockets and sent over HTTP instead"
|
|
4346
4661
|
);
|
|
@@ -4350,18 +4665,36 @@ function createRoom(options, config) {
|
|
|
4350
4665
|
managedSocket.send(message);
|
|
4351
4666
|
}
|
|
4352
4667
|
const self = new DerivedRef(
|
|
4353
|
-
context.
|
|
4354
|
-
context.
|
|
4355
|
-
|
|
4356
|
-
|
|
4357
|
-
|
|
4358
|
-
|
|
4359
|
-
|
|
4360
|
-
|
|
4361
|
-
|
|
4362
|
-
|
|
4668
|
+
context.staticSessionInfo,
|
|
4669
|
+
context.dynamicSessionInfo,
|
|
4670
|
+
context.myPresence,
|
|
4671
|
+
(staticSession, dynamicSession, myPresence) => {
|
|
4672
|
+
if (staticSession === null || dynamicSession === null) {
|
|
4673
|
+
return null;
|
|
4674
|
+
} else {
|
|
4675
|
+
const canWrite = canWriteStorage(dynamicSession.scopes);
|
|
4676
|
+
return {
|
|
4677
|
+
connectionId: dynamicSession.actor,
|
|
4678
|
+
id: staticSession.userId,
|
|
4679
|
+
info: staticSession.userInfo,
|
|
4680
|
+
presence: myPresence,
|
|
4681
|
+
canWrite,
|
|
4682
|
+
isReadOnly: !canWrite
|
|
4683
|
+
// Deprecated, kept for backward-compatibility
|
|
4684
|
+
};
|
|
4685
|
+
}
|
|
4363
4686
|
}
|
|
4364
4687
|
);
|
|
4688
|
+
let _lastSelf;
|
|
4689
|
+
function notifySelfChanged(batchedUpdatesWrapper) {
|
|
4690
|
+
const currSelf = self.current;
|
|
4691
|
+
if (currSelf !== null && currSelf !== _lastSelf) {
|
|
4692
|
+
batchedUpdatesWrapper(() => {
|
|
4693
|
+
eventHub.self.notify(currSelf);
|
|
4694
|
+
});
|
|
4695
|
+
_lastSelf = currSelf;
|
|
4696
|
+
}
|
|
4697
|
+
}
|
|
4365
4698
|
const selfAsTreeNode = new DerivedRef(
|
|
4366
4699
|
self,
|
|
4367
4700
|
(me) => me !== null ? userToTreeNode("Me", me) : null
|
|
@@ -4420,16 +4753,18 @@ function createRoom(options, config) {
|
|
|
4420
4753
|
}
|
|
4421
4754
|
}
|
|
4422
4755
|
if (presence) {
|
|
4423
|
-
|
|
4756
|
+
notifySelfChanged(doNotBatchUpdates);
|
|
4757
|
+
eventHub.myPresence.notify(context.myPresence.current);
|
|
4424
4758
|
}
|
|
4425
4759
|
if (storageUpdates.size > 0) {
|
|
4426
4760
|
const updates = Array.from(storageUpdates.values());
|
|
4427
4761
|
eventHub.storage.notify(updates);
|
|
4428
4762
|
}
|
|
4763
|
+
notifyStorageStatus();
|
|
4429
4764
|
});
|
|
4430
4765
|
}
|
|
4431
4766
|
function getConnectionId() {
|
|
4432
|
-
const info = context.
|
|
4767
|
+
const info = context.dynamicSessionInfo.current;
|
|
4433
4768
|
if (info) {
|
|
4434
4769
|
return info.actor;
|
|
4435
4770
|
}
|
|
@@ -4458,14 +4793,14 @@ function createRoom(options, config) {
|
|
|
4458
4793
|
data: {}
|
|
4459
4794
|
};
|
|
4460
4795
|
for (const key in op.data) {
|
|
4461
|
-
reverse.data[key] = context.
|
|
4796
|
+
reverse.data[key] = context.myPresence.current[key];
|
|
4462
4797
|
}
|
|
4463
|
-
context.
|
|
4464
|
-
if (context.buffer.
|
|
4465
|
-
context.buffer.
|
|
4798
|
+
context.myPresence.patch(op.data);
|
|
4799
|
+
if (context.buffer.presenceUpdates === null) {
|
|
4800
|
+
context.buffer.presenceUpdates = { type: "partial", data: op.data };
|
|
4466
4801
|
} else {
|
|
4467
4802
|
for (const key in op.data) {
|
|
4468
|
-
context.buffer.
|
|
4803
|
+
context.buffer.presenceUpdates.data[key] = op.data[key];
|
|
4469
4804
|
}
|
|
4470
4805
|
}
|
|
4471
4806
|
output.reverse.unshift(reverse);
|
|
@@ -4501,7 +4836,6 @@ function createRoom(options, config) {
|
|
|
4501
4836
|
}
|
|
4502
4837
|
}
|
|
4503
4838
|
}
|
|
4504
|
-
notifyStorageStatus();
|
|
4505
4839
|
return {
|
|
4506
4840
|
ops,
|
|
4507
4841
|
reverse: output.reverse,
|
|
@@ -4556,8 +4890,8 @@ function createRoom(options, config) {
|
|
|
4556
4890
|
}
|
|
4557
4891
|
function updatePresence(patch, options2) {
|
|
4558
4892
|
const oldValues = {};
|
|
4559
|
-
if (context.buffer.
|
|
4560
|
-
context.buffer.
|
|
4893
|
+
if (context.buffer.presenceUpdates === null) {
|
|
4894
|
+
context.buffer.presenceUpdates = {
|
|
4561
4895
|
type: "partial",
|
|
4562
4896
|
data: {}
|
|
4563
4897
|
};
|
|
@@ -4567,10 +4901,10 @@ function createRoom(options, config) {
|
|
|
4567
4901
|
if (overrideValue === void 0) {
|
|
4568
4902
|
continue;
|
|
4569
4903
|
}
|
|
4570
|
-
context.buffer.
|
|
4571
|
-
oldValues[key] = context.
|
|
4904
|
+
context.buffer.presenceUpdates.data[key] = overrideValue;
|
|
4905
|
+
oldValues[key] = context.myPresence.current[key];
|
|
4572
4906
|
}
|
|
4573
|
-
context.
|
|
4907
|
+
context.myPresence.patch(patch);
|
|
4574
4908
|
if (context.activeBatch) {
|
|
4575
4909
|
if (options2?.addToHistory) {
|
|
4576
4910
|
context.activeBatch.reverseOps.unshift({
|
|
@@ -4592,9 +4926,6 @@ function createRoom(options, config) {
|
|
|
4592
4926
|
});
|
|
4593
4927
|
}
|
|
4594
4928
|
}
|
|
4595
|
-
function isStorageReadOnly(scopes) {
|
|
4596
|
-
return scopes.includes("room:read" /* Read */) && scopes.includes("room:presence:write" /* PresenceWrite */) && !scopes.includes("room:write" /* Write */);
|
|
4597
|
-
}
|
|
4598
4929
|
function onUpdatePresenceMessage(message) {
|
|
4599
4930
|
if (message.targetActor !== void 0) {
|
|
4600
4931
|
const oldUser = context.others.getUser(message.actor);
|
|
@@ -4625,11 +4956,17 @@ function createRoom(options, config) {
|
|
|
4625
4956
|
}
|
|
4626
4957
|
return null;
|
|
4627
4958
|
}
|
|
4628
|
-
function onRoomStateMessage(message) {
|
|
4629
|
-
|
|
4959
|
+
function onRoomStateMessage(message, batchedUpdatesWrapper) {
|
|
4960
|
+
context.dynamicSessionInfo.set({
|
|
4961
|
+
actor: message.actor,
|
|
4962
|
+
scopes: message.scopes
|
|
4963
|
+
});
|
|
4964
|
+
context.idFactory = makeIdFactory(message.actor);
|
|
4965
|
+
notifySelfChanged(batchedUpdatesWrapper);
|
|
4966
|
+
for (const connectionId of context.others.connectionIds()) {
|
|
4630
4967
|
const user = message.users[connectionId];
|
|
4631
4968
|
if (user === void 0) {
|
|
4632
|
-
context.others.removeConnection(
|
|
4969
|
+
context.others.removeConnection(connectionId);
|
|
4633
4970
|
}
|
|
4634
4971
|
}
|
|
4635
4972
|
for (const key in message.users) {
|
|
@@ -4639,7 +4976,7 @@ function createRoom(options, config) {
|
|
|
4639
4976
|
connectionId,
|
|
4640
4977
|
user.id,
|
|
4641
4978
|
user.info,
|
|
4642
|
-
|
|
4979
|
+
user.scopes
|
|
4643
4980
|
);
|
|
4644
4981
|
}
|
|
4645
4982
|
return { type: "reset" };
|
|
@@ -4660,11 +4997,11 @@ function createRoom(options, config) {
|
|
|
4660
4997
|
message.actor,
|
|
4661
4998
|
message.id,
|
|
4662
4999
|
message.info,
|
|
4663
|
-
|
|
5000
|
+
message.scopes
|
|
4664
5001
|
);
|
|
4665
5002
|
context.buffer.messages.push({
|
|
4666
5003
|
type: 100 /* UPDATE_PRESENCE */,
|
|
4667
|
-
data: context.
|
|
5004
|
+
data: context.myPresence.current,
|
|
4668
5005
|
targetActor: message.actor
|
|
4669
5006
|
});
|
|
4670
5007
|
flushNowOrSoon();
|
|
@@ -4749,7 +5086,7 @@ function createRoom(options, config) {
|
|
|
4749
5086
|
break;
|
|
4750
5087
|
}
|
|
4751
5088
|
case 104 /* ROOM_STATE */: {
|
|
4752
|
-
updates.others.push(onRoomStateMessage(message));
|
|
5089
|
+
updates.others.push(onRoomStateMessage(message, doNotBatchUpdates));
|
|
4753
5090
|
break;
|
|
4754
5091
|
}
|
|
4755
5092
|
case 200 /* INITIAL_STORAGE_STATE */: {
|
|
@@ -4828,7 +5165,7 @@ ${Array.from(traces).join("\n\n")}`
|
|
|
4828
5165
|
lastFlushedAt: now,
|
|
4829
5166
|
messages: [],
|
|
4830
5167
|
storageOperations: [],
|
|
4831
|
-
|
|
5168
|
+
presenceUpdates: null
|
|
4832
5169
|
};
|
|
4833
5170
|
} else {
|
|
4834
5171
|
clearTimeout(context.buffer.flushTimerID);
|
|
@@ -4840,18 +5177,18 @@ ${Array.from(traces).join("\n\n")}`
|
|
|
4840
5177
|
}
|
|
4841
5178
|
function serializeBuffer() {
|
|
4842
5179
|
const messages = [];
|
|
4843
|
-
if (context.buffer.
|
|
5180
|
+
if (context.buffer.presenceUpdates) {
|
|
4844
5181
|
messages.push(
|
|
4845
|
-
context.buffer.
|
|
5182
|
+
context.buffer.presenceUpdates.type === "full" ? {
|
|
4846
5183
|
type: 100 /* UPDATE_PRESENCE */,
|
|
4847
5184
|
// Populating the `targetActor` field turns this message into
|
|
4848
5185
|
// a Full Presence™ update message (not a patch), which will get
|
|
4849
5186
|
// interpreted by other clients as such.
|
|
4850
5187
|
targetActor: -1,
|
|
4851
|
-
data: context.buffer.
|
|
5188
|
+
data: context.buffer.presenceUpdates.data
|
|
4852
5189
|
} : {
|
|
4853
5190
|
type: 100 /* UPDATE_PRESENCE */,
|
|
4854
|
-
data: context.buffer.
|
|
5191
|
+
data: context.buffer.presenceUpdates.data
|
|
4855
5192
|
}
|
|
4856
5193
|
);
|
|
4857
5194
|
}
|
|
@@ -5053,7 +5390,8 @@ ${Array.from(traces).join("\n\n")}`
|
|
|
5053
5390
|
lostConnection: eventHub.lostConnection.observable,
|
|
5054
5391
|
customEvent: eventHub.customEvent.observable,
|
|
5055
5392
|
others: eventHub.others.observable,
|
|
5056
|
-
|
|
5393
|
+
self: eventHub.self.observable,
|
|
5394
|
+
myPresence: eventHub.myPresence.observable,
|
|
5057
5395
|
error: eventHub.error.observable,
|
|
5058
5396
|
storage: eventHub.storage.observable,
|
|
5059
5397
|
history: eventHub.history.observable,
|
|
@@ -5061,65 +5399,70 @@ ${Array.from(traces).join("\n\n")}`
|
|
|
5061
5399
|
storageStatus: eventHub.storageStatus.observable,
|
|
5062
5400
|
ydoc: eventHub.ydoc.observable
|
|
5063
5401
|
};
|
|
5064
|
-
return
|
|
5065
|
-
|
|
5066
|
-
|
|
5067
|
-
|
|
5068
|
-
|
|
5069
|
-
|
|
5070
|
-
|
|
5071
|
-
|
|
5072
|
-
|
|
5402
|
+
return Object.defineProperty(
|
|
5403
|
+
{
|
|
5404
|
+
/* NOTE: Exposing __internal here only to allow testing implementation details in unit tests */
|
|
5405
|
+
__internal: {
|
|
5406
|
+
get presenceBuffer() {
|
|
5407
|
+
return deepClone(context.buffer.presenceUpdates?.data ?? null);
|
|
5408
|
+
},
|
|
5409
|
+
// prettier-ignore
|
|
5410
|
+
get undoStack() {
|
|
5411
|
+
return deepClone(context.undoStack);
|
|
5412
|
+
},
|
|
5413
|
+
// prettier-ignore
|
|
5414
|
+
get nodeCount() {
|
|
5415
|
+
return context.nodes.size;
|
|
5416
|
+
},
|
|
5417
|
+
// prettier-ignore
|
|
5418
|
+
// Support for the Liveblocks browser extension
|
|
5419
|
+
getSelf_forDevTools: () => selfAsTreeNode.current,
|
|
5420
|
+
getOthers_forDevTools: () => others_forDevTools.current,
|
|
5421
|
+
// prettier-ignore
|
|
5422
|
+
send: {
|
|
5423
|
+
// These exist only for our E2E testing app
|
|
5424
|
+
explicitClose: (event) => managedSocket._privateSendMachineEvent({ type: "EXPLICIT_SOCKET_CLOSE", event }),
|
|
5425
|
+
implicitClose: () => managedSocket._privateSendMachineEvent({ type: "NAVIGATOR_OFFLINE" })
|
|
5426
|
+
}
|
|
5073
5427
|
},
|
|
5074
|
-
|
|
5075
|
-
|
|
5076
|
-
|
|
5428
|
+
id: config.roomId,
|
|
5429
|
+
subscribe: makeClassicSubscribeFn(events),
|
|
5430
|
+
connect: () => managedSocket.connect(),
|
|
5431
|
+
reconnect: () => managedSocket.reconnect(),
|
|
5432
|
+
disconnect: () => managedSocket.disconnect(),
|
|
5433
|
+
destroy: () => managedSocket.destroy(),
|
|
5434
|
+
// Presence
|
|
5435
|
+
updatePresence,
|
|
5436
|
+
updateYDoc,
|
|
5437
|
+
broadcastEvent,
|
|
5438
|
+
// Storage
|
|
5439
|
+
batch,
|
|
5440
|
+
history: {
|
|
5441
|
+
undo,
|
|
5442
|
+
redo,
|
|
5443
|
+
canUndo,
|
|
5444
|
+
canRedo,
|
|
5445
|
+
pause: pauseHistory,
|
|
5446
|
+
resume: resumeHistory
|
|
5077
5447
|
},
|
|
5078
|
-
|
|
5079
|
-
|
|
5080
|
-
|
|
5081
|
-
|
|
5082
|
-
|
|
5083
|
-
|
|
5084
|
-
|
|
5085
|
-
|
|
5086
|
-
|
|
5087
|
-
|
|
5088
|
-
|
|
5089
|
-
|
|
5090
|
-
subscribe: makeClassicSubscribeFn(events),
|
|
5091
|
-
connect: () => managedSocket.connect(),
|
|
5092
|
-
reconnect: () => managedSocket.reconnect(),
|
|
5093
|
-
disconnect: () => managedSocket.disconnect(),
|
|
5094
|
-
destroy: () => managedSocket.destroy(),
|
|
5095
|
-
// Presence
|
|
5096
|
-
updatePresence,
|
|
5097
|
-
updateYDoc,
|
|
5098
|
-
broadcastEvent,
|
|
5099
|
-
// Storage
|
|
5100
|
-
batch,
|
|
5101
|
-
history: {
|
|
5102
|
-
undo,
|
|
5103
|
-
redo,
|
|
5104
|
-
canUndo,
|
|
5105
|
-
canRedo,
|
|
5106
|
-
pause: pauseHistory,
|
|
5107
|
-
resume: resumeHistory
|
|
5448
|
+
fetchYDoc,
|
|
5449
|
+
getStorage,
|
|
5450
|
+
getStorageSnapshot,
|
|
5451
|
+
getStorageStatus,
|
|
5452
|
+
events,
|
|
5453
|
+
// Core
|
|
5454
|
+
getStatus: () => managedSocket.getStatus(),
|
|
5455
|
+
getConnectionState: () => managedSocket.getLegacyStatus(),
|
|
5456
|
+
getSelf: () => self.current,
|
|
5457
|
+
// Presence
|
|
5458
|
+
getPresence: () => context.myPresence.current,
|
|
5459
|
+
getOthers: () => context.others.current
|
|
5108
5460
|
},
|
|
5109
|
-
|
|
5110
|
-
|
|
5111
|
-
|
|
5112
|
-
|
|
5113
|
-
|
|
5114
|
-
// Core
|
|
5115
|
-
getStatus: () => managedSocket.getStatus(),
|
|
5116
|
-
getConnectionState: () => managedSocket.getLegacyStatus(),
|
|
5117
|
-
isSelfAware: () => context.sessionInfo.current !== null,
|
|
5118
|
-
getSelf: () => self.current,
|
|
5119
|
-
// Presence
|
|
5120
|
-
getPresence: () => context.me.current,
|
|
5121
|
-
getOthers: () => context.others.current
|
|
5122
|
-
};
|
|
5461
|
+
// Explictly make the __internal field non-enumerable, to avoid aggressive
|
|
5462
|
+
// freezing when used with Immer
|
|
5463
|
+
"__internal",
|
|
5464
|
+
{ enumerable: false }
|
|
5465
|
+
);
|
|
5123
5466
|
}
|
|
5124
5467
|
function makeClassicSubscribeFn(events) {
|
|
5125
5468
|
function subscribeToLiveStructureDeeply(node, callback) {
|
|
@@ -5153,7 +5496,7 @@ function makeClassicSubscribeFn(events) {
|
|
|
5153
5496
|
callback
|
|
5154
5497
|
);
|
|
5155
5498
|
case "my-presence":
|
|
5156
|
-
return events.
|
|
5499
|
+
return events.myPresence.subscribe(callback);
|
|
5157
5500
|
case "others": {
|
|
5158
5501
|
const cb = callback;
|
|
5159
5502
|
return events.others.subscribe(
|
|
@@ -5207,24 +5550,30 @@ function makeClassicSubscribeFn(events) {
|
|
|
5207
5550
|
function isRoomEventName(value) {
|
|
5208
5551
|
return value === "my-presence" || value === "others" || value === "event" || value === "error" || value === "history" || value === "status" || value === "storage-status" || value === "lost-connection" || value === "connection";
|
|
5209
5552
|
}
|
|
5210
|
-
function
|
|
5211
|
-
return (
|
|
5553
|
+
function makeAuthDelegateForRoom(roomId, authManager) {
|
|
5554
|
+
return async () => {
|
|
5555
|
+
return authManager.getAuthValue("room:read", roomId);
|
|
5556
|
+
};
|
|
5557
|
+
}
|
|
5558
|
+
function makeCreateSocketDelegateForRoom(roomId, liveblocksServer, WebSocketPolyfill) {
|
|
5559
|
+
return (authValue) => {
|
|
5212
5560
|
const ws = WebSocketPolyfill ?? (typeof WebSocket === "undefined" ? void 0 : WebSocket);
|
|
5213
5561
|
if (ws === void 0) {
|
|
5214
5562
|
throw new StopRetrying(
|
|
5215
5563
|
"To use Liveblocks client in a non-dom environment, you need to provide a WebSocket polyfill."
|
|
5216
5564
|
);
|
|
5217
5565
|
}
|
|
5218
|
-
const
|
|
5219
|
-
|
|
5220
|
-
|
|
5221
|
-
|
|
5222
|
-
|
|
5223
|
-
|
|
5224
|
-
|
|
5225
|
-
|
|
5226
|
-
|
|
5227
|
-
);
|
|
5566
|
+
const url = new URL(liveblocksServer);
|
|
5567
|
+
url.searchParams.set("roomId", roomId);
|
|
5568
|
+
if (authValue.type === "secret") {
|
|
5569
|
+
url.searchParams.set("tok", authValue.token.raw);
|
|
5570
|
+
} else if (authValue.type === "public") {
|
|
5571
|
+
url.searchParams.set("pubkey", authValue.publicApiKey);
|
|
5572
|
+
} else {
|
|
5573
|
+
return assertNever(authValue, "Unhandled case");
|
|
5574
|
+
}
|
|
5575
|
+
url.searchParams.set("version", PKG_VERSION || "dev");
|
|
5576
|
+
return new ws(url.toString());
|
|
5228
5577
|
};
|
|
5229
5578
|
}
|
|
5230
5579
|
async function httpSend(message, token, endpoint, fetchPolyfill) {
|
|
@@ -5239,83 +5588,6 @@ async function httpSend(message, token, endpoint, fetchPolyfill) {
|
|
|
5239
5588
|
body: message
|
|
5240
5589
|
});
|
|
5241
5590
|
}
|
|
5242
|
-
function makeAuthDelegateForRoom(roomId, authentication, fetchPolyfill) {
|
|
5243
|
-
const fetcher = fetchPolyfill ?? (typeof window === "undefined" ? void 0 : window.fetch);
|
|
5244
|
-
if (authentication.type === "public") {
|
|
5245
|
-
return async () => {
|
|
5246
|
-
if (fetcher === void 0) {
|
|
5247
|
-
throw new StopRetrying(
|
|
5248
|
-
"To use Liveblocks client in a non-dom environment with a publicApiKey, you need to provide a fetch polyfill."
|
|
5249
|
-
);
|
|
5250
|
-
}
|
|
5251
|
-
return fetchAuthEndpoint(fetcher, authentication.url, {
|
|
5252
|
-
room: roomId,
|
|
5253
|
-
publicApiKey: authentication.publicApiKey
|
|
5254
|
-
}).then(({ token }) => parseAuthToken(token));
|
|
5255
|
-
};
|
|
5256
|
-
} else if (authentication.type === "private") {
|
|
5257
|
-
return async () => {
|
|
5258
|
-
if (fetcher === void 0) {
|
|
5259
|
-
throw new StopRetrying(
|
|
5260
|
-
"To use Liveblocks client in a non-dom environment with a url as auth endpoint, you need to provide a fetch polyfill."
|
|
5261
|
-
);
|
|
5262
|
-
}
|
|
5263
|
-
return fetchAuthEndpoint(fetcher, authentication.url, {
|
|
5264
|
-
room: roomId
|
|
5265
|
-
}).then(({ token }) => parseAuthToken(token));
|
|
5266
|
-
};
|
|
5267
|
-
} else if (authentication.type === "custom") {
|
|
5268
|
-
return async () => {
|
|
5269
|
-
const response = await authentication.callback(roomId);
|
|
5270
|
-
if (!response || !response.token) {
|
|
5271
|
-
throw new Error(
|
|
5272
|
-
'We expect the authentication callback to return a token, but it does not. Hint: the return value should look like: { token: "..." }'
|
|
5273
|
-
);
|
|
5274
|
-
}
|
|
5275
|
-
return parseAuthToken(response.token);
|
|
5276
|
-
};
|
|
5277
|
-
} else {
|
|
5278
|
-
throw new Error("Internal error. Unexpected authentication type");
|
|
5279
|
-
}
|
|
5280
|
-
}
|
|
5281
|
-
async function fetchAuthEndpoint(fetch2, endpoint, body) {
|
|
5282
|
-
const res = await fetch2(endpoint, {
|
|
5283
|
-
method: "POST",
|
|
5284
|
-
headers: {
|
|
5285
|
-
"Content-Type": "application/json"
|
|
5286
|
-
},
|
|
5287
|
-
// Credentials are needed to support authentication with cookies
|
|
5288
|
-
credentials: "include",
|
|
5289
|
-
body: JSON.stringify(body)
|
|
5290
|
-
});
|
|
5291
|
-
if (!res.ok) {
|
|
5292
|
-
const reason = `${(await res.text()).trim() || "reason not provided in auth response"} (${res.status} returned by POST ${endpoint})`;
|
|
5293
|
-
if (res.status === 401 || res.status === 403) {
|
|
5294
|
-
throw new StopRetrying(`Unauthorized: ${reason}`);
|
|
5295
|
-
} else {
|
|
5296
|
-
throw new Error(`Failed to authenticate: ${reason}`);
|
|
5297
|
-
}
|
|
5298
|
-
}
|
|
5299
|
-
let data;
|
|
5300
|
-
try {
|
|
5301
|
-
data = await res.json();
|
|
5302
|
-
} catch (er) {
|
|
5303
|
-
throw new Error(
|
|
5304
|
-
`Expected a JSON response when doing a POST request on "${endpoint}". ${String(
|
|
5305
|
-
er
|
|
5306
|
-
)}`
|
|
5307
|
-
);
|
|
5308
|
-
}
|
|
5309
|
-
if (!isPlainObject(data) || typeof data.token !== "string") {
|
|
5310
|
-
throw new Error(
|
|
5311
|
-
`Expected a JSON response of the form \`{ token: "..." }\` when doing a POST request on "${endpoint}", but got ${JSON.stringify(
|
|
5312
|
-
data
|
|
5313
|
-
)}`
|
|
5314
|
-
);
|
|
5315
|
-
}
|
|
5316
|
-
const { token } = data;
|
|
5317
|
-
return { token };
|
|
5318
|
-
}
|
|
5319
5591
|
|
|
5320
5592
|
// src/client.ts
|
|
5321
5593
|
var MIN_THROTTLE = 16;
|
|
@@ -5327,7 +5599,7 @@ var MAX_LOST_CONNECTION_TIMEOUT = 3e4;
|
|
|
5327
5599
|
var DEFAULT_LOST_CONNECTION_TIMEOUT = 5e3;
|
|
5328
5600
|
function getServerFromClientOptions(clientOptions) {
|
|
5329
5601
|
const rawOptions = clientOptions;
|
|
5330
|
-
return typeof rawOptions.liveblocksServer === "string" ? rawOptions.liveblocksServer : "wss://api.liveblocks.io/
|
|
5602
|
+
return typeof rawOptions.liveblocksServer === "string" ? rawOptions.liveblocksServer : "wss://api.liveblocks.io/v7";
|
|
5331
5603
|
}
|
|
5332
5604
|
function createClient(options) {
|
|
5333
5605
|
const clientOptions = options;
|
|
@@ -5335,6 +5607,7 @@ function createClient(options) {
|
|
|
5335
5607
|
const lostConnectionTimeout = getLostConnectionTimeout(
|
|
5336
5608
|
clientOptions.lostConnectionTimeout ?? DEFAULT_LOST_CONNECTION_TIMEOUT
|
|
5337
5609
|
);
|
|
5610
|
+
const authManager = createAuthManager(options);
|
|
5338
5611
|
const rooms = /* @__PURE__ */ new Map();
|
|
5339
5612
|
function getRoom(roomId) {
|
|
5340
5613
|
const room = rooms.get(roomId);
|
|
@@ -5359,11 +5632,17 @@ function createClient(options) {
|
|
|
5359
5632
|
throttleDelay,
|
|
5360
5633
|
lostConnectionTimeout,
|
|
5361
5634
|
polyfills: clientOptions.polyfills,
|
|
5362
|
-
delegates: clientOptions.mockedDelegates
|
|
5635
|
+
delegates: clientOptions.mockedDelegates ?? {
|
|
5636
|
+
createSocket: makeCreateSocketDelegateForRoom(
|
|
5637
|
+
roomId,
|
|
5638
|
+
getServerFromClientOptions(clientOptions),
|
|
5639
|
+
clientOptions.polyfills?.WebSocket
|
|
5640
|
+
),
|
|
5641
|
+
authenticate: makeAuthDelegateForRoom(roomId, authManager)
|
|
5642
|
+
},
|
|
5363
5643
|
enableDebugLogging: clientOptions.enableDebugLogging,
|
|
5364
5644
|
unstable_batchedUpdates: options2?.unstable_batchedUpdates,
|
|
5365
5645
|
liveblocksServer: getServerFromClientOptions(clientOptions),
|
|
5366
|
-
authentication: prepareAuthentication(clientOptions, roomId),
|
|
5367
5646
|
httpSendEndpoint: buildLiveblocksHttpSendEndpoint(
|
|
5368
5647
|
clientOptions,
|
|
5369
5648
|
roomId
|
|
@@ -5422,48 +5701,6 @@ function getLostConnectionTimeout(value) {
|
|
|
5422
5701
|
RECOMMENDED_MIN_LOST_CONNECTION_TIMEOUT
|
|
5423
5702
|
);
|
|
5424
5703
|
}
|
|
5425
|
-
function prepareAuthentication(clientOptions, roomId) {
|
|
5426
|
-
const { publicApiKey, authEndpoint } = clientOptions;
|
|
5427
|
-
if (authEndpoint !== void 0 && publicApiKey !== void 0) {
|
|
5428
|
-
throw new Error(
|
|
5429
|
-
"You cannot use both publicApiKey and authEndpoint. Please use either publicApiKey or authEndpoint, but not both. For more information: https://liveblocks.io/docs/api-reference/liveblocks-client#createClient"
|
|
5430
|
-
);
|
|
5431
|
-
}
|
|
5432
|
-
if (typeof publicApiKey === "string") {
|
|
5433
|
-
if (publicApiKey.startsWith("sk_")) {
|
|
5434
|
-
throw new Error(
|
|
5435
|
-
"Invalid publicApiKey. You are using the secret key which is not supported. Please use the public key instead. For more information: https://liveblocks.io/docs/api-reference/liveblocks-client#createClientPublicKey"
|
|
5436
|
-
);
|
|
5437
|
-
} else if (!publicApiKey.startsWith("pk_")) {
|
|
5438
|
-
throw new Error(
|
|
5439
|
-
"Invalid key. Please use the public key format: pk_<public key>. For more information: https://liveblocks.io/docs/api-reference/liveblocks-client#createClientPublicKey"
|
|
5440
|
-
);
|
|
5441
|
-
}
|
|
5442
|
-
return {
|
|
5443
|
-
type: "public",
|
|
5444
|
-
publicApiKey,
|
|
5445
|
-
url: buildLiveblocksPublicAuthorizeEndpoint(clientOptions, roomId)
|
|
5446
|
-
};
|
|
5447
|
-
}
|
|
5448
|
-
if (typeof authEndpoint === "string") {
|
|
5449
|
-
return {
|
|
5450
|
-
type: "private",
|
|
5451
|
-
url: authEndpoint
|
|
5452
|
-
};
|
|
5453
|
-
} else if (typeof authEndpoint === "function") {
|
|
5454
|
-
return {
|
|
5455
|
-
type: "custom",
|
|
5456
|
-
callback: authEndpoint
|
|
5457
|
-
};
|
|
5458
|
-
} else if (authEndpoint !== void 0) {
|
|
5459
|
-
throw new Error(
|
|
5460
|
-
"authEndpoint must be a string or a function. For more information: https://liveblocks.io/docs/api-reference/liveblocks-client#createClientAuthEndpoint"
|
|
5461
|
-
);
|
|
5462
|
-
}
|
|
5463
|
-
throw new Error(
|
|
5464
|
-
"Invalid Liveblocks client options. For more information: https://liveblocks.io/docs/api-reference/liveblocks-client#createClient"
|
|
5465
|
-
);
|
|
5466
|
-
}
|
|
5467
5704
|
function buildLiveblocksHttpSendEndpoint(options, roomId) {
|
|
5468
5705
|
if (options.httpSendEndpoint) {
|
|
5469
5706
|
return options.httpSendEndpoint.replace("{roomId}", roomId);
|
|
@@ -5472,14 +5709,6 @@ function buildLiveblocksHttpSendEndpoint(options, roomId) {
|
|
|
5472
5709
|
roomId
|
|
5473
5710
|
)}/send-message`;
|
|
5474
5711
|
}
|
|
5475
|
-
function buildLiveblocksPublicAuthorizeEndpoint(options, roomId) {
|
|
5476
|
-
if (options.publicAuthorizeEndpoint) {
|
|
5477
|
-
return options.publicAuthorizeEndpoint.replace("{roomId}", roomId);
|
|
5478
|
-
}
|
|
5479
|
-
return `https://api.liveblocks.io/v2/rooms/${encodeURIComponent(
|
|
5480
|
-
roomId
|
|
5481
|
-
)}/public/authorize`;
|
|
5482
|
-
}
|
|
5483
5712
|
|
|
5484
5713
|
// src/crdts/utils.ts
|
|
5485
5714
|
function toPlainLson(lson) {
|
|
@@ -5638,7 +5867,7 @@ function patchLiveObjectKey(liveObject, key, prev, next) {
|
|
|
5638
5867
|
if (process.env.NODE_ENV !== "production") {
|
|
5639
5868
|
const nonSerializableValue = findNonSerializableValue(next);
|
|
5640
5869
|
if (nonSerializableValue) {
|
|
5641
|
-
|
|
5870
|
+
error2(
|
|
5642
5871
|
`New state path: '${nonSerializableValue.path}' value: '${String(
|
|
5643
5872
|
nonSerializableValue.value
|
|
5644
5873
|
)}' is not serializable.
|
|
@@ -5852,18 +6081,8 @@ function shallow(a, b) {
|
|
|
5852
6081
|
return shallowObj(a, b);
|
|
5853
6082
|
}
|
|
5854
6083
|
|
|
5855
|
-
// src/
|
|
5856
|
-
|
|
5857
|
-
WebsocketCloseCodes2[WebsocketCloseCodes2["CLOSE_ABNORMAL"] = 1006] = "CLOSE_ABNORMAL";
|
|
5858
|
-
WebsocketCloseCodes2[WebsocketCloseCodes2["INVALID_MESSAGE_FORMAT"] = 4e3] = "INVALID_MESSAGE_FORMAT";
|
|
5859
|
-
WebsocketCloseCodes2[WebsocketCloseCodes2["NOT_ALLOWED"] = 4001] = "NOT_ALLOWED";
|
|
5860
|
-
WebsocketCloseCodes2[WebsocketCloseCodes2["MAX_NUMBER_OF_MESSAGES_PER_SECONDS"] = 4002] = "MAX_NUMBER_OF_MESSAGES_PER_SECONDS";
|
|
5861
|
-
WebsocketCloseCodes2[WebsocketCloseCodes2["MAX_NUMBER_OF_CONCURRENT_CONNECTIONS"] = 4003] = "MAX_NUMBER_OF_CONCURRENT_CONNECTIONS";
|
|
5862
|
-
WebsocketCloseCodes2[WebsocketCloseCodes2["MAX_NUMBER_OF_MESSAGES_PER_DAY_PER_APP"] = 4004] = "MAX_NUMBER_OF_MESSAGES_PER_DAY_PER_APP";
|
|
5863
|
-
WebsocketCloseCodes2[WebsocketCloseCodes2["MAX_NUMBER_OF_CONCURRENT_CONNECTIONS_PER_ROOM"] = 4005] = "MAX_NUMBER_OF_CONCURRENT_CONNECTIONS_PER_ROOM";
|
|
5864
|
-
WebsocketCloseCodes2[WebsocketCloseCodes2["CLOSE_WITHOUT_RETRY"] = 4999] = "CLOSE_WITHOUT_RETRY";
|
|
5865
|
-
return WebsocketCloseCodes2;
|
|
5866
|
-
})(WebsocketCloseCodes || {});
|
|
6084
|
+
// src/index.ts
|
|
6085
|
+
detectDupes(PKG_NAME, PKG_VERSION, PKG_FORMAT);
|
|
5867
6086
|
export {
|
|
5868
6087
|
ClientMsgCode,
|
|
5869
6088
|
CrdtType,
|
|
@@ -5881,6 +6100,7 @@ export {
|
|
|
5881
6100
|
createClient,
|
|
5882
6101
|
deprecate,
|
|
5883
6102
|
deprecateIf,
|
|
6103
|
+
detectDupes,
|
|
5884
6104
|
errorIf,
|
|
5885
6105
|
freeze,
|
|
5886
6106
|
isChildCrdt,
|