@liveblocks/core 1.1.8 → 1.2.0-comments1
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 +577 -175
- package/dist/index.d.ts +577 -175
- package/dist/index.js +1395 -761
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1315 -681
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __export = (target, all) => {
|
|
3
|
+
for (var name in all)
|
|
4
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
5
|
+
};
|
|
6
|
+
|
|
1
7
|
// src/version.ts
|
|
2
8
|
var PKG_NAME = "@liveblocks/core";
|
|
3
|
-
var PKG_VERSION = "1.
|
|
9
|
+
var PKG_VERSION = "1.2.0-comments1";
|
|
4
10
|
var PKG_FORMAT = "esm";
|
|
5
11
|
|
|
6
12
|
// src/dupe-detection.ts
|
|
@@ -46,6 +52,36 @@ function detectDupes(pkgName, pkgVersion, pkgFormat) {
|
|
|
46
52
|
}
|
|
47
53
|
}
|
|
48
54
|
|
|
55
|
+
// src/lib/assert.ts
|
|
56
|
+
function assertNever(_value, errmsg) {
|
|
57
|
+
throw new Error(errmsg);
|
|
58
|
+
}
|
|
59
|
+
function assert(condition, errmsg) {
|
|
60
|
+
if (process.env.NODE_ENV !== "production") {
|
|
61
|
+
if (!condition) {
|
|
62
|
+
const err = new Error(errmsg);
|
|
63
|
+
err.name = "Assertion failure";
|
|
64
|
+
throw err;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
function nn(value, errmsg = "Expected value to be non-nullable") {
|
|
69
|
+
assert(value !== null && value !== void 0, errmsg);
|
|
70
|
+
return value;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// src/lib/controlledPromise.ts
|
|
74
|
+
function controlledPromise() {
|
|
75
|
+
let flagger;
|
|
76
|
+
const promise = new Promise((res) => {
|
|
77
|
+
flagger = res;
|
|
78
|
+
});
|
|
79
|
+
if (!flagger) {
|
|
80
|
+
throw new Error("Should never happen");
|
|
81
|
+
}
|
|
82
|
+
return [promise, flagger];
|
|
83
|
+
}
|
|
84
|
+
|
|
49
85
|
// src/lib/EventSource.ts
|
|
50
86
|
function makeEventSource() {
|
|
51
87
|
const _onetimeObservers = /* @__PURE__ */ new Set();
|
|
@@ -119,185 +155,14 @@ function makeEventSource() {
|
|
|
119
155
|
};
|
|
120
156
|
}
|
|
121
157
|
|
|
122
|
-
// src/devtools/bridge.ts
|
|
123
|
-
var _bridgeActive = false;
|
|
124
|
-
function activateBridge(allowed) {
|
|
125
|
-
_bridgeActive = allowed;
|
|
126
|
-
}
|
|
127
|
-
function sendToPanel(message, options) {
|
|
128
|
-
if (process.env.NODE_ENV === "production" || typeof window === "undefined") {
|
|
129
|
-
return;
|
|
130
|
-
}
|
|
131
|
-
const fullMsg = {
|
|
132
|
-
...message,
|
|
133
|
-
source: "liveblocks-devtools-client"
|
|
134
|
-
};
|
|
135
|
-
if (!(options?.force || _bridgeActive)) {
|
|
136
|
-
return;
|
|
137
|
-
}
|
|
138
|
-
window.postMessage(fullMsg, "*");
|
|
139
|
-
}
|
|
140
|
-
var eventSource = makeEventSource();
|
|
141
|
-
if (process.env.NODE_ENV !== "production" && typeof window !== "undefined") {
|
|
142
|
-
window.addEventListener("message", (event) => {
|
|
143
|
-
if (event.source === window && event.data?.source === "liveblocks-devtools-panel") {
|
|
144
|
-
eventSource.notify(event.data);
|
|
145
|
-
} else {
|
|
146
|
-
}
|
|
147
|
-
});
|
|
148
|
-
}
|
|
149
|
-
var onMessageFromPanel = eventSource.observable;
|
|
150
|
-
|
|
151
|
-
// src/devtools/index.ts
|
|
152
|
-
var VERSION = PKG_VERSION || "dev";
|
|
153
|
-
var _devtoolsSetupHasRun = false;
|
|
154
|
-
function setupDevTools(getAllRooms) {
|
|
155
|
-
if (process.env.NODE_ENV === "production" || typeof window === "undefined") {
|
|
156
|
-
return;
|
|
157
|
-
}
|
|
158
|
-
if (_devtoolsSetupHasRun) {
|
|
159
|
-
return;
|
|
160
|
-
}
|
|
161
|
-
_devtoolsSetupHasRun = true;
|
|
162
|
-
onMessageFromPanel.subscribe((msg) => {
|
|
163
|
-
switch (msg.msg) {
|
|
164
|
-
case "connect": {
|
|
165
|
-
activateBridge(true);
|
|
166
|
-
for (const roomId of getAllRooms()) {
|
|
167
|
-
sendToPanel({
|
|
168
|
-
msg: "room::available",
|
|
169
|
-
roomId,
|
|
170
|
-
clientVersion: VERSION
|
|
171
|
-
});
|
|
172
|
-
}
|
|
173
|
-
break;
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
});
|
|
177
|
-
sendToPanel({ msg: "wake-up-devtools" }, { force: true });
|
|
178
|
-
}
|
|
179
|
-
var unsubsByRoomId = /* @__PURE__ */ new Map();
|
|
180
|
-
function stopSyncStream(roomId) {
|
|
181
|
-
const unsubs = unsubsByRoomId.get(roomId) ?? [];
|
|
182
|
-
unsubsByRoomId.delete(roomId);
|
|
183
|
-
for (const unsub of unsubs) {
|
|
184
|
-
unsub();
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
function startSyncStream(room) {
|
|
188
|
-
stopSyncStream(room.id);
|
|
189
|
-
fullSync(room);
|
|
190
|
-
unsubsByRoomId.set(room.id, [
|
|
191
|
-
// When the connection status changes
|
|
192
|
-
room.events.status.subscribe(() => partialSyncConnection(room)),
|
|
193
|
-
// When storage initializes, send the update
|
|
194
|
-
room.events.storageDidLoad.subscribeOnce(() => partialSyncStorage(room)),
|
|
195
|
-
// Any time storage updates, send the new storage root
|
|
196
|
-
room.events.storage.subscribe(() => partialSyncStorage(room)),
|
|
197
|
-
// Any time "me" or "others" updates, send the new values accordingly
|
|
198
|
-
room.events.self.subscribe(() => partialSyncMe(room)),
|
|
199
|
-
room.events.others.subscribe(() => partialSyncOthers(room))
|
|
200
|
-
]);
|
|
201
|
-
}
|
|
202
|
-
function partialSyncConnection(room) {
|
|
203
|
-
sendToPanel({
|
|
204
|
-
msg: "room::sync::partial",
|
|
205
|
-
roomId: room.id,
|
|
206
|
-
status: room.getStatus()
|
|
207
|
-
});
|
|
208
|
-
}
|
|
209
|
-
function partialSyncStorage(room) {
|
|
210
|
-
const root = room.getStorageSnapshot();
|
|
211
|
-
if (root) {
|
|
212
|
-
sendToPanel({
|
|
213
|
-
msg: "room::sync::partial",
|
|
214
|
-
roomId: room.id,
|
|
215
|
-
storage: root.toTreeNode("root").payload
|
|
216
|
-
});
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
function partialSyncMe(room) {
|
|
220
|
-
const me = room.__internal.getSelf_forDevTools();
|
|
221
|
-
if (me) {
|
|
222
|
-
sendToPanel({
|
|
223
|
-
msg: "room::sync::partial",
|
|
224
|
-
roomId: room.id,
|
|
225
|
-
me
|
|
226
|
-
});
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
function partialSyncOthers(room) {
|
|
230
|
-
const others = room.__internal.getOthers_forDevTools();
|
|
231
|
-
if (others) {
|
|
232
|
-
sendToPanel({
|
|
233
|
-
msg: "room::sync::partial",
|
|
234
|
-
roomId: room.id,
|
|
235
|
-
others
|
|
236
|
-
});
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
function fullSync(room) {
|
|
240
|
-
const root = room.getStorageSnapshot();
|
|
241
|
-
const me = room.__internal.getSelf_forDevTools();
|
|
242
|
-
const others = room.__internal.getOthers_forDevTools();
|
|
243
|
-
sendToPanel({
|
|
244
|
-
msg: "room::sync::full",
|
|
245
|
-
roomId: room.id,
|
|
246
|
-
status: room.getStatus(),
|
|
247
|
-
storage: root?.toTreeNode("root").payload ?? null,
|
|
248
|
-
me,
|
|
249
|
-
others
|
|
250
|
-
});
|
|
251
|
-
}
|
|
252
|
-
var roomChannelListeners = /* @__PURE__ */ new Map();
|
|
253
|
-
function stopRoomChannelListener(roomId) {
|
|
254
|
-
const listener = roomChannelListeners.get(roomId);
|
|
255
|
-
roomChannelListeners.delete(roomId);
|
|
256
|
-
if (listener) {
|
|
257
|
-
listener();
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
function linkDevTools(roomId, room) {
|
|
261
|
-
if (process.env.NODE_ENV === "production" || typeof window === "undefined") {
|
|
262
|
-
return;
|
|
263
|
-
}
|
|
264
|
-
sendToPanel({ msg: "room::available", roomId, clientVersion: VERSION });
|
|
265
|
-
stopRoomChannelListener(roomId);
|
|
266
|
-
roomChannelListeners.set(
|
|
267
|
-
roomId,
|
|
268
|
-
// Returns the unsubscribe callback, that we store in the
|
|
269
|
-
// roomChannelListeners registry
|
|
270
|
-
onMessageFromPanel.subscribe((msg) => {
|
|
271
|
-
switch (msg.msg) {
|
|
272
|
-
case "room::subscribe": {
|
|
273
|
-
if (msg.roomId === roomId) {
|
|
274
|
-
startSyncStream(room);
|
|
275
|
-
}
|
|
276
|
-
break;
|
|
277
|
-
}
|
|
278
|
-
case "room::unsubscribe": {
|
|
279
|
-
if (msg.roomId === roomId) {
|
|
280
|
-
stopSyncStream(roomId);
|
|
281
|
-
}
|
|
282
|
-
break;
|
|
283
|
-
}
|
|
284
|
-
}
|
|
285
|
-
})
|
|
286
|
-
);
|
|
287
|
-
}
|
|
288
|
-
function unlinkDevTools(roomId) {
|
|
289
|
-
if (process.env.NODE_ENV === "production" || typeof window === "undefined") {
|
|
290
|
-
return;
|
|
291
|
-
}
|
|
292
|
-
stopSyncStream(roomId);
|
|
293
|
-
stopRoomChannelListener(roomId);
|
|
294
|
-
sendToPanel({
|
|
295
|
-
msg: "room::unavailable",
|
|
296
|
-
roomId
|
|
297
|
-
});
|
|
298
|
-
}
|
|
299
|
-
|
|
300
158
|
// src/lib/fancy-console.ts
|
|
159
|
+
var fancy_console_exports = {};
|
|
160
|
+
__export(fancy_console_exports, {
|
|
161
|
+
error: () => error2,
|
|
162
|
+
errorWithTitle: () => errorWithTitle,
|
|
163
|
+
warn: () => warn,
|
|
164
|
+
warnWithTitle: () => warnWithTitle
|
|
165
|
+
});
|
|
301
166
|
var badge = "background:#0e0d12;border-radius:9999px;color:#fff;padding:3px 7px;font-family:sans-serif;font-weight:600;";
|
|
302
167
|
var bold = "font-weight:600";
|
|
303
168
|
function wrap(method) {
|
|
@@ -323,57 +188,6 @@ function wrapWithTitle(method) {
|
|
|
323
188
|
var warnWithTitle = wrapWithTitle("warn");
|
|
324
189
|
var errorWithTitle = wrapWithTitle("error");
|
|
325
190
|
|
|
326
|
-
// src/lib/deprecation.ts
|
|
327
|
-
var _emittedDeprecationWarnings = /* @__PURE__ */ new Set();
|
|
328
|
-
function deprecate(message, key = message) {
|
|
329
|
-
if (process.env.NODE_ENV !== "production") {
|
|
330
|
-
if (!_emittedDeprecationWarnings.has(key)) {
|
|
331
|
-
_emittedDeprecationWarnings.add(key);
|
|
332
|
-
errorWithTitle("Deprecation warning", message);
|
|
333
|
-
}
|
|
334
|
-
}
|
|
335
|
-
}
|
|
336
|
-
function deprecateIf(condition, message, key = message) {
|
|
337
|
-
if (process.env.NODE_ENV !== "production") {
|
|
338
|
-
if (condition) {
|
|
339
|
-
deprecate(message, key);
|
|
340
|
-
}
|
|
341
|
-
}
|
|
342
|
-
}
|
|
343
|
-
function throwUsageError(message) {
|
|
344
|
-
if (process.env.NODE_ENV !== "production") {
|
|
345
|
-
const usageError = new Error(message);
|
|
346
|
-
usageError.name = "Usage error";
|
|
347
|
-
errorWithTitle("Usage error", message);
|
|
348
|
-
throw usageError;
|
|
349
|
-
}
|
|
350
|
-
}
|
|
351
|
-
function errorIf(condition, message) {
|
|
352
|
-
if (process.env.NODE_ENV !== "production") {
|
|
353
|
-
if (condition) {
|
|
354
|
-
throwUsageError(message);
|
|
355
|
-
}
|
|
356
|
-
}
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
// src/lib/assert.ts
|
|
360
|
-
function assertNever(_value, errmsg) {
|
|
361
|
-
throw new Error(errmsg);
|
|
362
|
-
}
|
|
363
|
-
function assert(condition, errmsg) {
|
|
364
|
-
if (process.env.NODE_ENV !== "production") {
|
|
365
|
-
if (!condition) {
|
|
366
|
-
const err = new Error(errmsg);
|
|
367
|
-
err.name = "Assertion failure";
|
|
368
|
-
throw err;
|
|
369
|
-
}
|
|
370
|
-
}
|
|
371
|
-
}
|
|
372
|
-
function nn(value, errmsg = "Expected value to be non-nullable") {
|
|
373
|
-
assert(value !== null && value !== void 0, errmsg);
|
|
374
|
-
return value;
|
|
375
|
-
}
|
|
376
|
-
|
|
377
191
|
// src/lib/fsm.ts
|
|
378
192
|
function distance(state1, state2) {
|
|
379
193
|
if (state1 === state2) {
|
|
@@ -812,9 +626,48 @@ async function withTimeout(promise, millis, errmsg = "Timed out") {
|
|
|
812
626
|
return Promise.race([promise, timer$]).finally(() => clearTimeout(timerID));
|
|
813
627
|
}
|
|
814
628
|
|
|
815
|
-
// src/
|
|
816
|
-
|
|
817
|
-
|
|
629
|
+
// src/protocol/ServerMsg.ts
|
|
630
|
+
var ServerMsgCode = /* @__PURE__ */ ((ServerMsgCode2) => {
|
|
631
|
+
ServerMsgCode2[ServerMsgCode2["UPDATE_PRESENCE"] = 100] = "UPDATE_PRESENCE";
|
|
632
|
+
ServerMsgCode2[ServerMsgCode2["USER_JOINED"] = 101] = "USER_JOINED";
|
|
633
|
+
ServerMsgCode2[ServerMsgCode2["USER_LEFT"] = 102] = "USER_LEFT";
|
|
634
|
+
ServerMsgCode2[ServerMsgCode2["BROADCASTED_EVENT"] = 103] = "BROADCASTED_EVENT";
|
|
635
|
+
ServerMsgCode2[ServerMsgCode2["ROOM_STATE"] = 104] = "ROOM_STATE";
|
|
636
|
+
ServerMsgCode2[ServerMsgCode2["INITIAL_STORAGE_STATE"] = 200] = "INITIAL_STORAGE_STATE";
|
|
637
|
+
ServerMsgCode2[ServerMsgCode2["UPDATE_STORAGE"] = 201] = "UPDATE_STORAGE";
|
|
638
|
+
ServerMsgCode2[ServerMsgCode2["REJECT_STORAGE_OP"] = 299] = "REJECT_STORAGE_OP";
|
|
639
|
+
ServerMsgCode2[ServerMsgCode2["UPDATE_YDOC"] = 300] = "UPDATE_YDOC";
|
|
640
|
+
return ServerMsgCode2;
|
|
641
|
+
})(ServerMsgCode || {});
|
|
642
|
+
|
|
643
|
+
// src/types/IWebSocket.ts
|
|
644
|
+
var WebsocketCloseCodes = /* @__PURE__ */ ((WebsocketCloseCodes2) => {
|
|
645
|
+
WebsocketCloseCodes2[WebsocketCloseCodes2["CLOSE_ABNORMAL"] = 1006] = "CLOSE_ABNORMAL";
|
|
646
|
+
WebsocketCloseCodes2[WebsocketCloseCodes2["UNEXPECTED_CONDITION"] = 1011] = "UNEXPECTED_CONDITION";
|
|
647
|
+
WebsocketCloseCodes2[WebsocketCloseCodes2["TRY_AGAIN_LATER"] = 1013] = "TRY_AGAIN_LATER";
|
|
648
|
+
WebsocketCloseCodes2[WebsocketCloseCodes2["INVALID_MESSAGE_FORMAT"] = 4e3] = "INVALID_MESSAGE_FORMAT";
|
|
649
|
+
WebsocketCloseCodes2[WebsocketCloseCodes2["NOT_ALLOWED"] = 4001] = "NOT_ALLOWED";
|
|
650
|
+
WebsocketCloseCodes2[WebsocketCloseCodes2["MAX_NUMBER_OF_MESSAGES_PER_SECONDS"] = 4002] = "MAX_NUMBER_OF_MESSAGES_PER_SECONDS";
|
|
651
|
+
WebsocketCloseCodes2[WebsocketCloseCodes2["MAX_NUMBER_OF_CONCURRENT_CONNECTIONS"] = 4003] = "MAX_NUMBER_OF_CONCURRENT_CONNECTIONS";
|
|
652
|
+
WebsocketCloseCodes2[WebsocketCloseCodes2["MAX_NUMBER_OF_MESSAGES_PER_DAY_PER_APP"] = 4004] = "MAX_NUMBER_OF_MESSAGES_PER_DAY_PER_APP";
|
|
653
|
+
WebsocketCloseCodes2[WebsocketCloseCodes2["MAX_NUMBER_OF_CONCURRENT_CONNECTIONS_PER_ROOM"] = 4005] = "MAX_NUMBER_OF_CONCURRENT_CONNECTIONS_PER_ROOM";
|
|
654
|
+
WebsocketCloseCodes2[WebsocketCloseCodes2["TOKEN_EXPIRED"] = 4109] = "TOKEN_EXPIRED";
|
|
655
|
+
WebsocketCloseCodes2[WebsocketCloseCodes2["CLOSE_WITHOUT_RETRY"] = 4999] = "CLOSE_WITHOUT_RETRY";
|
|
656
|
+
return WebsocketCloseCodes2;
|
|
657
|
+
})(WebsocketCloseCodes || {});
|
|
658
|
+
function shouldDisconnect(code) {
|
|
659
|
+
return code === 4999 /* CLOSE_WITHOUT_RETRY */ || code >= 4e3 && code < 4100;
|
|
660
|
+
}
|
|
661
|
+
function shouldReauth(code) {
|
|
662
|
+
return code >= 4100 && code < 4200;
|
|
663
|
+
}
|
|
664
|
+
function shouldRetryWithoutReauth(code) {
|
|
665
|
+
return code === 1013 /* TRY_AGAIN_LATER */ || code >= 4200 && code < 4300;
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
// src/connection.ts
|
|
669
|
+
function newToLegacyStatus(status) {
|
|
670
|
+
switch (status) {
|
|
818
671
|
case "connecting":
|
|
819
672
|
return "connecting";
|
|
820
673
|
case "connected":
|
|
@@ -916,9 +769,6 @@ var logPermanentClose = log(
|
|
|
916
769
|
function isCloseEvent(error3) {
|
|
917
770
|
return !(error3 instanceof Error) && error3.type === "close";
|
|
918
771
|
}
|
|
919
|
-
function isCustomCloseEvent(error3) {
|
|
920
|
-
return isCloseEvent(error3) && error3.code >= 4e3 && error3.code < 4100;
|
|
921
|
-
}
|
|
922
772
|
function enableTracing(machine) {
|
|
923
773
|
const start = (/* @__PURE__ */ new Date()).getTime();
|
|
924
774
|
function log2(...args) {
|
|
@@ -969,13 +819,19 @@ function defineConnectivityEvents(machine) {
|
|
|
969
819
|
};
|
|
970
820
|
}
|
|
971
821
|
var assign = (patch) => (ctx) => ctx.patch(patch);
|
|
972
|
-
function createConnectionStateMachine(delegates,
|
|
822
|
+
function createConnectionStateMachine(delegates, options) {
|
|
973
823
|
const onMessage = makeEventSource();
|
|
974
824
|
onMessage.pause();
|
|
975
825
|
const onLiveblocksError = makeEventSource();
|
|
826
|
+
function fireErrorEvent(errmsg, errcode) {
|
|
827
|
+
return () => {
|
|
828
|
+
const err = new LiveblocksError(errmsg, errcode);
|
|
829
|
+
onLiveblocksError.notify(err);
|
|
830
|
+
};
|
|
831
|
+
}
|
|
976
832
|
const initialContext = {
|
|
977
833
|
successCount: 0,
|
|
978
|
-
|
|
834
|
+
authValue: null,
|
|
979
835
|
socket: null,
|
|
980
836
|
backoffDelay: RESET_DELAY
|
|
981
837
|
};
|
|
@@ -989,9 +845,9 @@ function createConnectionStateMachine(delegates, enableDebugLogging) {
|
|
|
989
845
|
});
|
|
990
846
|
machine.onEnter("@idle.*", resetSuccessCount).addTransitions("@idle.*", {
|
|
991
847
|
CONNECT: (_, ctx) => (
|
|
992
|
-
// If we still have a known
|
|
993
|
-
// otherwise, try to obtain a new
|
|
994
|
-
ctx.
|
|
848
|
+
// If we still have a known authValue, try to reconnect to the socket directly,
|
|
849
|
+
// otherwise, try to obtain a new authValue
|
|
850
|
+
ctx.authValue !== null ? "@connecting.busy" : "@auth.busy"
|
|
995
851
|
)
|
|
996
852
|
});
|
|
997
853
|
machine.addTransitions("@auth.backoff", {
|
|
@@ -1010,7 +866,7 @@ function createConnectionStateMachine(delegates, enableDebugLogging) {
|
|
|
1010
866
|
(okEvent) => ({
|
|
1011
867
|
target: "@connecting.busy",
|
|
1012
868
|
effect: assign({
|
|
1013
|
-
|
|
869
|
+
authValue: okEvent.data,
|
|
1014
870
|
backoffDelay: RESET_DELAY
|
|
1015
871
|
})
|
|
1016
872
|
}),
|
|
@@ -1019,7 +875,10 @@ function createConnectionStateMachine(delegates, enableDebugLogging) {
|
|
|
1019
875
|
if (failedEvent.reason instanceof StopRetrying) {
|
|
1020
876
|
return {
|
|
1021
877
|
target: "@idle.failed",
|
|
1022
|
-
effect:
|
|
878
|
+
effect: [
|
|
879
|
+
log(2 /* ERROR */, failedEvent.reason.message),
|
|
880
|
+
fireErrorEvent(failedEvent.reason.message, -1)
|
|
881
|
+
]
|
|
1023
882
|
};
|
|
1024
883
|
}
|
|
1025
884
|
return {
|
|
@@ -1069,16 +928,29 @@ function createConnectionStateMachine(delegates, enableDebugLogging) {
|
|
|
1069
928
|
let capturedPrematureEvent = null;
|
|
1070
929
|
const connect$ = new Promise(
|
|
1071
930
|
(resolve, rej) => {
|
|
1072
|
-
if (ctx.
|
|
1073
|
-
throw new Error("No auth
|
|
931
|
+
if (ctx.authValue === null) {
|
|
932
|
+
throw new Error("No auth authValue");
|
|
1074
933
|
}
|
|
1075
|
-
const socket = delegates.createSocket(ctx.
|
|
934
|
+
const socket = delegates.createSocket(ctx.authValue);
|
|
1076
935
|
function reject(event) {
|
|
1077
936
|
capturedPrematureEvent = event;
|
|
1078
937
|
socket.removeEventListener("message", onSocketMessage);
|
|
1079
938
|
rej(event);
|
|
1080
939
|
}
|
|
940
|
+
const [actor$, didReceiveActor] = controlledPromise();
|
|
941
|
+
if (!options.waitForActorId) {
|
|
942
|
+
didReceiveActor();
|
|
943
|
+
}
|
|
944
|
+
function waitForActorId(event) {
|
|
945
|
+
const serverMsg = tryParseJson(event.data);
|
|
946
|
+
if (serverMsg?.type === 104 /* ROOM_STATE */) {
|
|
947
|
+
didReceiveActor();
|
|
948
|
+
}
|
|
949
|
+
}
|
|
1081
950
|
socket.addEventListener("message", onSocketMessage);
|
|
951
|
+
if (options.waitForActorId) {
|
|
952
|
+
socket.addEventListener("message", waitForActorId);
|
|
953
|
+
}
|
|
1082
954
|
socket.addEventListener("error", reject);
|
|
1083
955
|
socket.addEventListener("close", reject);
|
|
1084
956
|
socket.addEventListener("open", () => {
|
|
@@ -1087,8 +959,11 @@ function createConnectionStateMachine(delegates, enableDebugLogging) {
|
|
|
1087
959
|
const unsub = () => {
|
|
1088
960
|
socket.removeEventListener("error", reject);
|
|
1089
961
|
socket.removeEventListener("close", reject);
|
|
962
|
+
socket.removeEventListener("message", waitForActorId);
|
|
1090
963
|
};
|
|
1091
|
-
|
|
964
|
+
void actor$.then(() => {
|
|
965
|
+
resolve([socket, unsub]);
|
|
966
|
+
});
|
|
1092
967
|
});
|
|
1093
968
|
}
|
|
1094
969
|
);
|
|
@@ -1129,24 +1004,35 @@ function createConnectionStateMachine(delegates, enableDebugLogging) {
|
|
|
1129
1004
|
if (err instanceof StopRetrying) {
|
|
1130
1005
|
return {
|
|
1131
1006
|
target: "@idle.failed",
|
|
1132
|
-
effect: log(2 /* ERROR */, err.message)
|
|
1133
|
-
};
|
|
1134
|
-
}
|
|
1135
|
-
if (isCloseEvent(err) && err.code === 4999) {
|
|
1136
|
-
return {
|
|
1137
|
-
target: "@idle.failed",
|
|
1138
|
-
effect: log(2 /* ERROR */, err.reason)
|
|
1139
|
-
};
|
|
1140
|
-
}
|
|
1141
|
-
if (isCustomCloseEvent(err) && err.code !== 4001) {
|
|
1142
|
-
return {
|
|
1143
|
-
target: "@connecting.backoff",
|
|
1144
1007
|
effect: [
|
|
1145
|
-
|
|
1146
|
-
|
|
1008
|
+
log(2 /* ERROR */, err.message),
|
|
1009
|
+
fireErrorEvent(err.message, -1)
|
|
1147
1010
|
]
|
|
1148
1011
|
};
|
|
1149
1012
|
}
|
|
1013
|
+
if (isCloseEvent(err)) {
|
|
1014
|
+
if (err.code === 4109 /* TOKEN_EXPIRED */) {
|
|
1015
|
+
return "@auth.busy";
|
|
1016
|
+
}
|
|
1017
|
+
if (shouldRetryWithoutReauth(err.code)) {
|
|
1018
|
+
return {
|
|
1019
|
+
target: "@connecting.backoff",
|
|
1020
|
+
effect: [
|
|
1021
|
+
increaseBackoffDelayAggressively,
|
|
1022
|
+
logPrematureErrorOrCloseEvent(err)
|
|
1023
|
+
]
|
|
1024
|
+
};
|
|
1025
|
+
}
|
|
1026
|
+
if (shouldDisconnect(err.code)) {
|
|
1027
|
+
return {
|
|
1028
|
+
target: "@idle.failed",
|
|
1029
|
+
effect: [
|
|
1030
|
+
log(2 /* ERROR */, err.reason),
|
|
1031
|
+
fireErrorEvent(err.reason, err.code)
|
|
1032
|
+
]
|
|
1033
|
+
};
|
|
1034
|
+
}
|
|
1035
|
+
}
|
|
1150
1036
|
return {
|
|
1151
1037
|
target: "@auth.backoff",
|
|
1152
1038
|
effect: [increaseBackoffDelay, logPrematureErrorOrCloseEvent(err)]
|
|
@@ -1200,29 +1086,29 @@ function createConnectionStateMachine(delegates, enableDebugLogging) {
|
|
|
1200
1086
|
};
|
|
1201
1087
|
},
|
|
1202
1088
|
EXPLICIT_SOCKET_CLOSE: (e) => {
|
|
1203
|
-
if (e.event.code
|
|
1089
|
+
if (shouldDisconnect(e.event.code)) {
|
|
1204
1090
|
return {
|
|
1205
1091
|
target: "@idle.failed",
|
|
1206
|
-
effect:
|
|
1092
|
+
effect: [
|
|
1093
|
+
logPermanentClose,
|
|
1094
|
+
fireErrorEvent(e.event.reason, e.event.code)
|
|
1095
|
+
]
|
|
1207
1096
|
};
|
|
1208
1097
|
}
|
|
1209
|
-
if (e.event.code
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1098
|
+
if (shouldReauth(e.event.code)) {
|
|
1099
|
+
if (e.event.code === 4109 /* TOKEN_EXPIRED */) {
|
|
1100
|
+
return "@auth.busy";
|
|
1101
|
+
} else {
|
|
1102
|
+
return {
|
|
1103
|
+
target: "@auth.backoff",
|
|
1104
|
+
effect: [increaseBackoffDelay, logCloseEvent(e.event)]
|
|
1105
|
+
};
|
|
1106
|
+
}
|
|
1214
1107
|
}
|
|
1215
|
-
if (
|
|
1108
|
+
if (shouldRetryWithoutReauth(e.event.code)) {
|
|
1216
1109
|
return {
|
|
1217
1110
|
target: "@connecting.backoff",
|
|
1218
|
-
effect: [
|
|
1219
|
-
increaseBackoffDelayAggressively,
|
|
1220
|
-
logCloseEvent(e.event),
|
|
1221
|
-
() => {
|
|
1222
|
-
const err = new LiveblocksError(e.event.reason, e.event.code);
|
|
1223
|
-
onLiveblocksError.notify(err);
|
|
1224
|
-
}
|
|
1225
|
-
]
|
|
1111
|
+
effect: [increaseBackoffDelayAggressively, logCloseEvent(e.event)]
|
|
1226
1112
|
};
|
|
1227
1113
|
}
|
|
1228
1114
|
return {
|
|
@@ -1261,7 +1147,7 @@ function createConnectionStateMachine(delegates, enableDebugLogging) {
|
|
|
1261
1147
|
const cleanups = [];
|
|
1262
1148
|
const { statusDidChange, didConnect, didDisconnect, unsubscribe } = defineConnectivityEvents(machine);
|
|
1263
1149
|
cleanups.push(unsubscribe);
|
|
1264
|
-
if (enableDebugLogging) {
|
|
1150
|
+
if (options.enableDebugLogging) {
|
|
1265
1151
|
cleanups.push(enableTracing(machine));
|
|
1266
1152
|
}
|
|
1267
1153
|
machine.start();
|
|
@@ -1279,10 +1165,10 @@ function createConnectionStateMachine(delegates, enableDebugLogging) {
|
|
|
1279
1165
|
};
|
|
1280
1166
|
}
|
|
1281
1167
|
var ManagedSocket = class {
|
|
1282
|
-
constructor(delegates, enableDebugLogging = false) {
|
|
1168
|
+
constructor(delegates, enableDebugLogging = false, waitForActorId = true) {
|
|
1283
1169
|
const { machine, events, cleanups } = createConnectionStateMachine(
|
|
1284
1170
|
delegates,
|
|
1285
|
-
enableDebugLogging
|
|
1171
|
+
{ waitForActorId, enableDebugLogging }
|
|
1286
1172
|
);
|
|
1287
1173
|
this.machine = machine;
|
|
1288
1174
|
this.events = events;
|
|
@@ -1299,10 +1185,10 @@ var ManagedSocket = class {
|
|
|
1299
1185
|
}
|
|
1300
1186
|
}
|
|
1301
1187
|
/**
|
|
1302
|
-
* Returns the current auth
|
|
1188
|
+
* Returns the current auth authValue.
|
|
1303
1189
|
*/
|
|
1304
|
-
get
|
|
1305
|
-
return this.machine.context.
|
|
1190
|
+
get authValue() {
|
|
1191
|
+
return this.machine.context.authValue;
|
|
1306
1192
|
}
|
|
1307
1193
|
/**
|
|
1308
1194
|
* Call this method to try to connect to a WebSocket. This only has an effect
|
|
@@ -1313,7 +1199,7 @@ var ManagedSocket = class {
|
|
|
1313
1199
|
}
|
|
1314
1200
|
/**
|
|
1315
1201
|
* If idle, will try to connect. Otherwise, it will attempt to reconnect to
|
|
1316
|
-
* the socket, potentially obtaining a new
|
|
1202
|
+
* the socket, potentially obtaining a new authValue first, if needed.
|
|
1317
1203
|
*/
|
|
1318
1204
|
reconnect() {
|
|
1319
1205
|
this.machine.send({ type: "RECONNECT" });
|
|
@@ -1360,124 +1246,635 @@ var ManagedSocket = class {
|
|
|
1360
1246
|
}
|
|
1361
1247
|
};
|
|
1362
1248
|
|
|
1363
|
-
// src/
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
var NUM_DIGITS = MAX_CODE - MIN_CODE + 1;
|
|
1367
|
-
var ZERO = nthDigit(0);
|
|
1368
|
-
var ONE = nthDigit(1);
|
|
1369
|
-
var ZERO_NINE = ZERO + nthDigit(-1);
|
|
1370
|
-
function nthDigit(n) {
|
|
1371
|
-
const code = MIN_CODE + (n < 0 ? NUM_DIGITS + n : n);
|
|
1372
|
-
if (code < MIN_CODE || code > MAX_CODE) {
|
|
1373
|
-
throw new Error(`Invalid n value: ${n}`);
|
|
1374
|
-
}
|
|
1375
|
-
return String.fromCharCode(code);
|
|
1249
|
+
// src/protocol/AuthToken.ts
|
|
1250
|
+
function canWriteStorage(scopes) {
|
|
1251
|
+
return scopes.includes("room:write" /* Write */);
|
|
1376
1252
|
}
|
|
1377
|
-
function
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
}
|
|
1385
|
-
|
|
1253
|
+
function isValidAuthTokenPayload(data) {
|
|
1254
|
+
return isPlainObject(data) && (data.k === "acc" /* ACCESS_TOKEN */ || data.k === "id" /* ID_TOKEN */ || data.k === "sec-legacy" /* SECRET_LEGACY */);
|
|
1255
|
+
}
|
|
1256
|
+
function parseAuthToken(rawTokenString) {
|
|
1257
|
+
const tokenParts = rawTokenString.split(".");
|
|
1258
|
+
if (tokenParts.length !== 3) {
|
|
1259
|
+
throw new Error("Authentication error: invalid JWT token");
|
|
1260
|
+
}
|
|
1261
|
+
const payload = tryParseJson(b64decode(tokenParts[1]));
|
|
1262
|
+
if (!(payload && isValidAuthTokenPayload(payload))) {
|
|
1263
|
+
throw new Error(
|
|
1264
|
+
"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"
|
|
1265
|
+
);
|
|
1386
1266
|
}
|
|
1267
|
+
return {
|
|
1268
|
+
raw: rawTokenString,
|
|
1269
|
+
parsed: payload
|
|
1270
|
+
};
|
|
1387
1271
|
}
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1272
|
+
|
|
1273
|
+
// src/auth-manager.ts
|
|
1274
|
+
function createAuthManager(authOptions) {
|
|
1275
|
+
const authentication = prepareAuthentication(authOptions);
|
|
1276
|
+
const tokens = [];
|
|
1277
|
+
const expiryTimes = [];
|
|
1278
|
+
const requestPromises = /* @__PURE__ */ new Map();
|
|
1279
|
+
function hasCorrespondingScopes(requestedScope, scopes) {
|
|
1280
|
+
if (requestedScope === "comments:read") {
|
|
1281
|
+
return scopes.includes("comments:read" /* CommentsRead */) || scopes.includes("comments:write" /* CommentsWrite */) || scopes.includes("room:read" /* Read */) || scopes.includes("room:write" /* Write */);
|
|
1282
|
+
} else if (requestedScope === "room:read") {
|
|
1283
|
+
return scopes.includes("room:read" /* Read */) || scopes.includes("room:write" /* Write */);
|
|
1394
1284
|
}
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1285
|
+
return false;
|
|
1286
|
+
}
|
|
1287
|
+
function getCachedToken(requestedScope, roomId) {
|
|
1288
|
+
const now = Math.ceil(Date.now() / 1e3);
|
|
1289
|
+
for (let i = tokens.length - 1; i >= 0; i--) {
|
|
1290
|
+
const token = tokens[i];
|
|
1291
|
+
const expiresAt = expiryTimes[i];
|
|
1292
|
+
if (expiresAt <= now) {
|
|
1293
|
+
tokens.splice(i, 1);
|
|
1294
|
+
expiryTimes.splice(i, 1);
|
|
1295
|
+
continue;
|
|
1296
|
+
}
|
|
1297
|
+
if (token.parsed.k === "id" /* ID_TOKEN */) {
|
|
1298
|
+
return token;
|
|
1299
|
+
} else if (token.parsed.k === "sec-legacy" /* SECRET_LEGACY */) {
|
|
1300
|
+
return void 0;
|
|
1301
|
+
} else if (token.parsed.k === "acc" /* ACCESS_TOKEN */) {
|
|
1302
|
+
for (const [resource, scopes] of Object.entries(token.parsed.perms)) {
|
|
1303
|
+
if (resource.includes("*") && roomId.startsWith(resource.replace("*", "")) || roomId === resource && hasCorrespondingScopes(requestedScope, scopes)) {
|
|
1304
|
+
return token;
|
|
1305
|
+
}
|
|
1306
|
+
}
|
|
1400
1307
|
}
|
|
1401
|
-
} else {
|
|
1402
|
-
return pos.substring(0, i + 1);
|
|
1403
1308
|
}
|
|
1309
|
+
return void 0;
|
|
1404
1310
|
}
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1311
|
+
async function makeAuthRequest(roomId) {
|
|
1312
|
+
const fetcher = authOptions.polyfills?.fetch ?? (typeof window === "undefined" ? void 0 : window.fetch);
|
|
1313
|
+
if (authentication.type === "private") {
|
|
1314
|
+
if (fetcher === void 0) {
|
|
1315
|
+
throw new StopRetrying(
|
|
1316
|
+
"To use Liveblocks client in a non-dom environment with a url as auth endpoint, you need to provide a fetch polyfill."
|
|
1317
|
+
);
|
|
1318
|
+
}
|
|
1319
|
+
const response = await fetchAuthEndpoint(fetcher, authentication.url, {
|
|
1320
|
+
room: roomId
|
|
1321
|
+
});
|
|
1322
|
+
return parseAuthToken(response.token);
|
|
1412
1323
|
}
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1324
|
+
if (authentication.type === "custom") {
|
|
1325
|
+
const response = await authentication.callback(roomId);
|
|
1326
|
+
if (!response || typeof response !== "object") {
|
|
1327
|
+
throw new Error(
|
|
1328
|
+
'We expect the authentication callback to return a token, but it does not. Hint: the return value should look like: { token: "..." }'
|
|
1329
|
+
);
|
|
1330
|
+
}
|
|
1331
|
+
if (typeof response.token === "string") {
|
|
1332
|
+
return parseAuthToken(response.token);
|
|
1333
|
+
} else if (typeof response.error === "string") {
|
|
1334
|
+
const reason = `Authentication failed: ${"reason" in response && typeof response.reason === "string" ? response.reason : "Forbidden"}`;
|
|
1335
|
+
if (response.error === "forbidden") {
|
|
1336
|
+
throw new StopRetrying(reason);
|
|
1337
|
+
} else {
|
|
1338
|
+
throw new Error(reason);
|
|
1339
|
+
}
|
|
1340
|
+
} else {
|
|
1341
|
+
throw new Error(
|
|
1342
|
+
'We expect the authentication callback to return a token, but it does not. Hint: the return value should look like: { token: "..." }'
|
|
1343
|
+
);
|
|
1344
|
+
}
|
|
1345
|
+
}
|
|
1346
|
+
throw new Error("Invalid Liveblocks client options");
|
|
1424
1347
|
}
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
const loLen = lo.length;
|
|
1429
|
-
const hiLen = hi.length;
|
|
1430
|
-
while (true) {
|
|
1431
|
-
const loCode = index < loLen ? lo.charCodeAt(index) : MIN_CODE;
|
|
1432
|
-
const hiCode = index < hiLen ? hi.charCodeAt(index) : MAX_CODE;
|
|
1433
|
-
if (loCode === hiCode) {
|
|
1434
|
-
index++;
|
|
1435
|
-
continue;
|
|
1348
|
+
async function getAuthValue(requestedScope, roomId) {
|
|
1349
|
+
if (authentication.type === "public") {
|
|
1350
|
+
return { type: "public", publicApiKey: authentication.publicApiKey };
|
|
1436
1351
|
}
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1352
|
+
const cachedToken = getCachedToken(requestedScope, roomId);
|
|
1353
|
+
if (cachedToken !== void 0) {
|
|
1354
|
+
return { type: "secret", token: cachedToken };
|
|
1355
|
+
}
|
|
1356
|
+
let currentPromise = requestPromises.get(roomId);
|
|
1357
|
+
if (currentPromise === void 0) {
|
|
1358
|
+
currentPromise = makeAuthRequest(roomId);
|
|
1359
|
+
requestPromises.set(roomId, currentPromise);
|
|
1360
|
+
}
|
|
1361
|
+
try {
|
|
1362
|
+
const token = await currentPromise;
|
|
1363
|
+
const BUFFER = 30;
|
|
1364
|
+
const expiresAt = Math.floor(Date.now() / 1e3) + (token.parsed.exp - token.parsed.iat) - BUFFER;
|
|
1365
|
+
tokens.push(token);
|
|
1366
|
+
expiryTimes.push(expiresAt);
|
|
1367
|
+
return { type: "secret", token };
|
|
1368
|
+
} finally {
|
|
1369
|
+
requestPromises.delete(roomId);
|
|
1444
1370
|
}
|
|
1445
1371
|
}
|
|
1372
|
+
return {
|
|
1373
|
+
getAuthValue
|
|
1374
|
+
};
|
|
1446
1375
|
}
|
|
1447
|
-
function
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
return false;
|
|
1454
|
-
}
|
|
1455
|
-
const lastIdx = str.length - 1;
|
|
1456
|
-
const last = str.charCodeAt(lastIdx);
|
|
1457
|
-
if (last < MIN_NON_ZERO_CODE || last > MAX_CODE) {
|
|
1458
|
-
return false;
|
|
1376
|
+
function prepareAuthentication(authOptions) {
|
|
1377
|
+
const { publicApiKey, authEndpoint } = authOptions;
|
|
1378
|
+
if (authEndpoint !== void 0 && publicApiKey !== void 0) {
|
|
1379
|
+
throw new Error(
|
|
1380
|
+
"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"
|
|
1381
|
+
);
|
|
1459
1382
|
}
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1383
|
+
if (typeof publicApiKey === "string") {
|
|
1384
|
+
if (publicApiKey.startsWith("sk_")) {
|
|
1385
|
+
throw new Error(
|
|
1386
|
+
"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"
|
|
1387
|
+
);
|
|
1388
|
+
} else if (!publicApiKey.startsWith("pk_")) {
|
|
1389
|
+
throw new Error(
|
|
1390
|
+
"Invalid key. Please use the public key format: pk_<public key>. For more information: https://liveblocks.io/docs/api-reference/liveblocks-client#createClientPublicKey"
|
|
1391
|
+
);
|
|
1464
1392
|
}
|
|
1393
|
+
return {
|
|
1394
|
+
type: "public",
|
|
1395
|
+
publicApiKey
|
|
1396
|
+
};
|
|
1465
1397
|
}
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1398
|
+
if (typeof authEndpoint === "string") {
|
|
1399
|
+
return {
|
|
1400
|
+
type: "private",
|
|
1401
|
+
url: authEndpoint
|
|
1402
|
+
};
|
|
1403
|
+
} else if (typeof authEndpoint === "function") {
|
|
1404
|
+
return {
|
|
1405
|
+
type: "custom",
|
|
1406
|
+
callback: authEndpoint
|
|
1407
|
+
};
|
|
1408
|
+
} else if (authEndpoint !== void 0) {
|
|
1409
|
+
throw new Error(
|
|
1410
|
+
"authEndpoint must be a string or a function. For more information: https://liveblocks.io/docs/api-reference/liveblocks-client#createClientAuthEndpoint"
|
|
1411
|
+
);
|
|
1476
1412
|
}
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1413
|
+
throw new Error(
|
|
1414
|
+
"Invalid Liveblocks client options. For more information: https://liveblocks.io/docs/api-reference/liveblocks-client#createClient"
|
|
1415
|
+
);
|
|
1416
|
+
}
|
|
1417
|
+
async function fetchAuthEndpoint(fetch2, endpoint, body) {
|
|
1418
|
+
const res = await fetch2(endpoint, {
|
|
1419
|
+
method: "POST",
|
|
1420
|
+
headers: {
|
|
1421
|
+
"Content-Type": "application/json"
|
|
1422
|
+
},
|
|
1423
|
+
body: JSON.stringify(body)
|
|
1424
|
+
});
|
|
1425
|
+
if (!res.ok) {
|
|
1426
|
+
const reason = `${(await res.text()).trim() || "reason not provided in auth response"} (${res.status} returned by POST ${endpoint})`;
|
|
1427
|
+
if (res.status === 401 || res.status === 403) {
|
|
1428
|
+
throw new StopRetrying(`Unauthorized: ${reason}`);
|
|
1429
|
+
} else {
|
|
1430
|
+
throw new Error(`Failed to authenticate: ${reason}`);
|
|
1431
|
+
}
|
|
1432
|
+
}
|
|
1433
|
+
let data;
|
|
1434
|
+
try {
|
|
1435
|
+
data = await res.json();
|
|
1436
|
+
} catch (er) {
|
|
1437
|
+
throw new Error(
|
|
1438
|
+
`Expected a JSON response when doing a POST request on "${endpoint}". ${String(
|
|
1439
|
+
er
|
|
1440
|
+
)}`
|
|
1441
|
+
);
|
|
1442
|
+
}
|
|
1443
|
+
if (!isPlainObject(data) || typeof data.token !== "string") {
|
|
1444
|
+
throw new Error(
|
|
1445
|
+
`Expected a JSON response of the form \`{ token: "..." }\` when doing a POST request on "${endpoint}", but got ${JSON.stringify(
|
|
1446
|
+
data
|
|
1447
|
+
)}`
|
|
1448
|
+
);
|
|
1449
|
+
}
|
|
1450
|
+
const { token } = data;
|
|
1451
|
+
return { token };
|
|
1452
|
+
}
|
|
1453
|
+
|
|
1454
|
+
// src/devtools/bridge.ts
|
|
1455
|
+
var _bridgeActive = false;
|
|
1456
|
+
function activateBridge(allowed) {
|
|
1457
|
+
_bridgeActive = allowed;
|
|
1458
|
+
}
|
|
1459
|
+
function sendToPanel(message, options) {
|
|
1460
|
+
if (process.env.NODE_ENV === "production" || typeof window === "undefined") {
|
|
1461
|
+
return;
|
|
1462
|
+
}
|
|
1463
|
+
const fullMsg = {
|
|
1464
|
+
...message,
|
|
1465
|
+
source: "liveblocks-devtools-client"
|
|
1466
|
+
};
|
|
1467
|
+
if (!(options?.force || _bridgeActive)) {
|
|
1468
|
+
return;
|
|
1469
|
+
}
|
|
1470
|
+
window.postMessage(fullMsg, "*");
|
|
1471
|
+
}
|
|
1472
|
+
var eventSource = makeEventSource();
|
|
1473
|
+
if (process.env.NODE_ENV !== "production" && typeof window !== "undefined") {
|
|
1474
|
+
window.addEventListener("message", (event) => {
|
|
1475
|
+
if (event.source === window && event.data?.source === "liveblocks-devtools-panel") {
|
|
1476
|
+
eventSource.notify(event.data);
|
|
1477
|
+
} else {
|
|
1478
|
+
}
|
|
1479
|
+
});
|
|
1480
|
+
}
|
|
1481
|
+
var onMessageFromPanel = eventSource.observable;
|
|
1482
|
+
|
|
1483
|
+
// src/devtools/index.ts
|
|
1484
|
+
var VERSION = PKG_VERSION || "dev";
|
|
1485
|
+
var _devtoolsSetupHasRun = false;
|
|
1486
|
+
function setupDevTools(getAllRooms) {
|
|
1487
|
+
if (process.env.NODE_ENV === "production" || typeof window === "undefined") {
|
|
1488
|
+
return;
|
|
1489
|
+
}
|
|
1490
|
+
if (_devtoolsSetupHasRun) {
|
|
1491
|
+
return;
|
|
1492
|
+
}
|
|
1493
|
+
_devtoolsSetupHasRun = true;
|
|
1494
|
+
onMessageFromPanel.subscribe((msg) => {
|
|
1495
|
+
switch (msg.msg) {
|
|
1496
|
+
case "connect": {
|
|
1497
|
+
activateBridge(true);
|
|
1498
|
+
for (const roomId of getAllRooms()) {
|
|
1499
|
+
sendToPanel({
|
|
1500
|
+
msg: "room::available",
|
|
1501
|
+
roomId,
|
|
1502
|
+
clientVersion: VERSION
|
|
1503
|
+
});
|
|
1504
|
+
}
|
|
1505
|
+
break;
|
|
1506
|
+
}
|
|
1507
|
+
}
|
|
1508
|
+
});
|
|
1509
|
+
sendToPanel({ msg: "wake-up-devtools" }, { force: true });
|
|
1510
|
+
}
|
|
1511
|
+
var unsubsByRoomId = /* @__PURE__ */ new Map();
|
|
1512
|
+
function stopSyncStream(roomId) {
|
|
1513
|
+
const unsubs = unsubsByRoomId.get(roomId) ?? [];
|
|
1514
|
+
unsubsByRoomId.delete(roomId);
|
|
1515
|
+
for (const unsub of unsubs) {
|
|
1516
|
+
unsub();
|
|
1517
|
+
}
|
|
1518
|
+
}
|
|
1519
|
+
function startSyncStream(room) {
|
|
1520
|
+
stopSyncStream(room.id);
|
|
1521
|
+
fullSync(room);
|
|
1522
|
+
unsubsByRoomId.set(room.id, [
|
|
1523
|
+
// When the connection status changes
|
|
1524
|
+
room.events.status.subscribe(() => partialSyncConnection(room)),
|
|
1525
|
+
// When storage initializes, send the update
|
|
1526
|
+
room.events.storageDidLoad.subscribeOnce(() => partialSyncStorage(room)),
|
|
1527
|
+
// Any time storage updates, send the new storage root
|
|
1528
|
+
room.events.storage.subscribe(() => partialSyncStorage(room)),
|
|
1529
|
+
// Any time "me" or "others" updates, send the new values accordingly
|
|
1530
|
+
room.events.self.subscribe(() => partialSyncMe(room)),
|
|
1531
|
+
room.events.others.subscribe(() => partialSyncOthers(room))
|
|
1532
|
+
]);
|
|
1533
|
+
}
|
|
1534
|
+
function partialSyncConnection(room) {
|
|
1535
|
+
sendToPanel({
|
|
1536
|
+
msg: "room::sync::partial",
|
|
1537
|
+
roomId: room.id,
|
|
1538
|
+
status: room.getStatus()
|
|
1539
|
+
});
|
|
1540
|
+
}
|
|
1541
|
+
function partialSyncStorage(room) {
|
|
1542
|
+
const root = room.getStorageSnapshot();
|
|
1543
|
+
if (root) {
|
|
1544
|
+
sendToPanel({
|
|
1545
|
+
msg: "room::sync::partial",
|
|
1546
|
+
roomId: room.id,
|
|
1547
|
+
storage: root.toTreeNode("root").payload
|
|
1548
|
+
});
|
|
1549
|
+
}
|
|
1550
|
+
}
|
|
1551
|
+
function partialSyncMe(room) {
|
|
1552
|
+
const me = room.__internal.getSelf_forDevTools();
|
|
1553
|
+
if (me) {
|
|
1554
|
+
sendToPanel({
|
|
1555
|
+
msg: "room::sync::partial",
|
|
1556
|
+
roomId: room.id,
|
|
1557
|
+
me
|
|
1558
|
+
});
|
|
1559
|
+
}
|
|
1560
|
+
}
|
|
1561
|
+
function partialSyncOthers(room) {
|
|
1562
|
+
const others = room.__internal.getOthers_forDevTools();
|
|
1563
|
+
if (others) {
|
|
1564
|
+
sendToPanel({
|
|
1565
|
+
msg: "room::sync::partial",
|
|
1566
|
+
roomId: room.id,
|
|
1567
|
+
others
|
|
1568
|
+
});
|
|
1569
|
+
}
|
|
1570
|
+
}
|
|
1571
|
+
function fullSync(room) {
|
|
1572
|
+
const root = room.getStorageSnapshot();
|
|
1573
|
+
const me = room.__internal.getSelf_forDevTools();
|
|
1574
|
+
const others = room.__internal.getOthers_forDevTools();
|
|
1575
|
+
sendToPanel({
|
|
1576
|
+
msg: "room::sync::full",
|
|
1577
|
+
roomId: room.id,
|
|
1578
|
+
status: room.getStatus(),
|
|
1579
|
+
storage: root?.toTreeNode("root").payload ?? null,
|
|
1580
|
+
me,
|
|
1581
|
+
others
|
|
1582
|
+
});
|
|
1583
|
+
}
|
|
1584
|
+
var roomChannelListeners = /* @__PURE__ */ new Map();
|
|
1585
|
+
function stopRoomChannelListener(roomId) {
|
|
1586
|
+
const listener = roomChannelListeners.get(roomId);
|
|
1587
|
+
roomChannelListeners.delete(roomId);
|
|
1588
|
+
if (listener) {
|
|
1589
|
+
listener();
|
|
1590
|
+
}
|
|
1591
|
+
}
|
|
1592
|
+
function linkDevTools(roomId, room) {
|
|
1593
|
+
if (process.env.NODE_ENV === "production" || typeof window === "undefined") {
|
|
1594
|
+
return;
|
|
1595
|
+
}
|
|
1596
|
+
sendToPanel({ msg: "room::available", roomId, clientVersion: VERSION });
|
|
1597
|
+
stopRoomChannelListener(roomId);
|
|
1598
|
+
roomChannelListeners.set(
|
|
1599
|
+
roomId,
|
|
1600
|
+
// Returns the unsubscribe callback, that we store in the
|
|
1601
|
+
// roomChannelListeners registry
|
|
1602
|
+
onMessageFromPanel.subscribe((msg) => {
|
|
1603
|
+
switch (msg.msg) {
|
|
1604
|
+
case "room::subscribe": {
|
|
1605
|
+
if (msg.roomId === roomId) {
|
|
1606
|
+
startSyncStream(room);
|
|
1607
|
+
}
|
|
1608
|
+
break;
|
|
1609
|
+
}
|
|
1610
|
+
case "room::unsubscribe": {
|
|
1611
|
+
if (msg.roomId === roomId) {
|
|
1612
|
+
stopSyncStream(roomId);
|
|
1613
|
+
}
|
|
1614
|
+
break;
|
|
1615
|
+
}
|
|
1616
|
+
}
|
|
1617
|
+
})
|
|
1618
|
+
);
|
|
1619
|
+
}
|
|
1620
|
+
function unlinkDevTools(roomId) {
|
|
1621
|
+
if (process.env.NODE_ENV === "production" || typeof window === "undefined") {
|
|
1622
|
+
return;
|
|
1623
|
+
}
|
|
1624
|
+
stopSyncStream(roomId);
|
|
1625
|
+
stopRoomChannelListener(roomId);
|
|
1626
|
+
sendToPanel({
|
|
1627
|
+
msg: "room::unavailable",
|
|
1628
|
+
roomId
|
|
1629
|
+
});
|
|
1630
|
+
}
|
|
1631
|
+
|
|
1632
|
+
// src/lib/deprecation.ts
|
|
1633
|
+
var _emittedDeprecationWarnings = /* @__PURE__ */ new Set();
|
|
1634
|
+
function deprecate(message, key = message) {
|
|
1635
|
+
if (process.env.NODE_ENV !== "production") {
|
|
1636
|
+
if (!_emittedDeprecationWarnings.has(key)) {
|
|
1637
|
+
_emittedDeprecationWarnings.add(key);
|
|
1638
|
+
errorWithTitle("Deprecation warning", message);
|
|
1639
|
+
}
|
|
1640
|
+
}
|
|
1641
|
+
}
|
|
1642
|
+
function deprecateIf(condition, message, key = message) {
|
|
1643
|
+
if (process.env.NODE_ENV !== "production") {
|
|
1644
|
+
if (condition) {
|
|
1645
|
+
deprecate(message, key);
|
|
1646
|
+
}
|
|
1647
|
+
}
|
|
1648
|
+
}
|
|
1649
|
+
function throwUsageError(message) {
|
|
1650
|
+
if (process.env.NODE_ENV !== "production") {
|
|
1651
|
+
const usageError = new Error(message);
|
|
1652
|
+
usageError.name = "Usage error";
|
|
1653
|
+
errorWithTitle("Usage error", message);
|
|
1654
|
+
throw usageError;
|
|
1655
|
+
}
|
|
1656
|
+
}
|
|
1657
|
+
function errorIf(condition, message) {
|
|
1658
|
+
if (process.env.NODE_ENV !== "production") {
|
|
1659
|
+
if (condition) {
|
|
1660
|
+
throwUsageError(message);
|
|
1661
|
+
}
|
|
1662
|
+
}
|
|
1663
|
+
}
|
|
1664
|
+
|
|
1665
|
+
// src/lib/Json.ts
|
|
1666
|
+
function isJsonScalar(data) {
|
|
1667
|
+
return data === null || typeof data === "string" || typeof data === "number" || typeof data === "boolean";
|
|
1668
|
+
}
|
|
1669
|
+
function isJsonArray(data) {
|
|
1670
|
+
return Array.isArray(data);
|
|
1671
|
+
}
|
|
1672
|
+
function isJsonObject(data) {
|
|
1673
|
+
return !isJsonScalar(data) && !isJsonArray(data);
|
|
1674
|
+
}
|
|
1675
|
+
|
|
1676
|
+
// src/realtime-client.ts
|
|
1677
|
+
function authValueToString(authValue) {
|
|
1678
|
+
return authValue.type === "secret" ? authValue.token.raw : authValue.publicApiKey;
|
|
1679
|
+
}
|
|
1680
|
+
function createRealtimeClient(authManager, serverEndpoint) {
|
|
1681
|
+
const eventHub = {
|
|
1682
|
+
error: makeEventSource(),
|
|
1683
|
+
connection: makeEventSource(),
|
|
1684
|
+
events: {}
|
|
1685
|
+
};
|
|
1686
|
+
let managedSocket = null;
|
|
1687
|
+
function createManagedSocket(roomId) {
|
|
1688
|
+
managedSocket = new ManagedSocket(
|
|
1689
|
+
{
|
|
1690
|
+
// TODO: We're trying to (re)connect based on the first roomId that the user is asking for
|
|
1691
|
+
// This is bad because the user might now have access to this room (anymore)
|
|
1692
|
+
// This prevent any future reconnection to the websocket server
|
|
1693
|
+
// We need to find a better way to handle the first (re)connection
|
|
1694
|
+
// (Could it be based on the current listeners)
|
|
1695
|
+
authenticate: () => authManager.getAuthValue("room:read", roomId),
|
|
1696
|
+
createSocket: (authValue) => new WebSocket(
|
|
1697
|
+
`${serverEndpoint}?token=${authValue.type === "secret" ? authValue.token.raw : authValue.publicApiKey}`
|
|
1698
|
+
)
|
|
1699
|
+
},
|
|
1700
|
+
true,
|
|
1701
|
+
false
|
|
1702
|
+
);
|
|
1703
|
+
managedSocket.events.statusDidChange.subscribe((status) => {
|
|
1704
|
+
if (status === "connected") {
|
|
1705
|
+
for (const roomId2 in eventHub.events) {
|
|
1706
|
+
const eventSource2 = eventHub.events[roomId2];
|
|
1707
|
+
if (eventSource2.count() > 0) {
|
|
1708
|
+
subscribeToRoomEvents(roomId2);
|
|
1709
|
+
}
|
|
1710
|
+
}
|
|
1711
|
+
}
|
|
1712
|
+
eventHub.connection.notify(status);
|
|
1713
|
+
});
|
|
1714
|
+
managedSocket.events.onLiveblocksError.subscribe(eventHub.error.notify);
|
|
1715
|
+
managedSocket.events.onMessage.subscribe((event) => {
|
|
1716
|
+
if (typeof event.data !== "string") {
|
|
1717
|
+
return;
|
|
1718
|
+
}
|
|
1719
|
+
const jsonEvent = tryParseJson(event.data);
|
|
1720
|
+
if (jsonEvent !== void 0 && isJsonObject(jsonEvent) && typeof jsonEvent.roomId === "string") {
|
|
1721
|
+
eventHub.events[jsonEvent.roomId].notify(jsonEvent);
|
|
1722
|
+
}
|
|
1723
|
+
});
|
|
1724
|
+
managedSocket.connect();
|
|
1725
|
+
}
|
|
1726
|
+
function getOrCreateEventSource(roomId) {
|
|
1727
|
+
let eventSource2 = eventHub.events[roomId];
|
|
1728
|
+
if (eventSource2 === void 0) {
|
|
1729
|
+
eventSource2 = makeEventSource();
|
|
1730
|
+
eventHub.events[roomId] = eventSource2;
|
|
1731
|
+
}
|
|
1732
|
+
return eventSource2;
|
|
1733
|
+
}
|
|
1734
|
+
async function subscribeToRoomEvents(roomId) {
|
|
1735
|
+
const authValue = await authManager.getAuthValue("room:read", roomId);
|
|
1736
|
+
if (managedSocket === null || managedSocket.getStatus() !== "connected") {
|
|
1737
|
+
return;
|
|
1738
|
+
}
|
|
1739
|
+
managedSocket.send(
|
|
1740
|
+
JSON.stringify({
|
|
1741
|
+
type: "subscribeToRooms",
|
|
1742
|
+
rooms: [roomId],
|
|
1743
|
+
token: authValueToString(authValue)
|
|
1744
|
+
})
|
|
1745
|
+
);
|
|
1746
|
+
}
|
|
1747
|
+
return {
|
|
1748
|
+
subscribeToEvents: (roomId, callback) => {
|
|
1749
|
+
if (!managedSocket) {
|
|
1750
|
+
createManagedSocket(roomId);
|
|
1751
|
+
}
|
|
1752
|
+
subscribeToRoomEvents(roomId);
|
|
1753
|
+
return getOrCreateEventSource(roomId).subscribe(callback);
|
|
1754
|
+
},
|
|
1755
|
+
error: eventHub.error.observable,
|
|
1756
|
+
connection: eventHub.connection.observable
|
|
1757
|
+
};
|
|
1758
|
+
}
|
|
1759
|
+
|
|
1760
|
+
// src/lib/position.ts
|
|
1761
|
+
var MIN_CODE = 32;
|
|
1762
|
+
var MAX_CODE = 126;
|
|
1763
|
+
var NUM_DIGITS = MAX_CODE - MIN_CODE + 1;
|
|
1764
|
+
var ZERO = nthDigit(0);
|
|
1765
|
+
var ONE = nthDigit(1);
|
|
1766
|
+
var ZERO_NINE = ZERO + nthDigit(-1);
|
|
1767
|
+
function nthDigit(n) {
|
|
1768
|
+
const code = MIN_CODE + (n < 0 ? NUM_DIGITS + n : n);
|
|
1769
|
+
if (code < MIN_CODE || code > MAX_CODE) {
|
|
1770
|
+
throw new Error(`Invalid n value: ${n}`);
|
|
1771
|
+
}
|
|
1772
|
+
return String.fromCharCode(code);
|
|
1773
|
+
}
|
|
1774
|
+
function makePosition(x, y) {
|
|
1775
|
+
if (x !== void 0 && y !== void 0) {
|
|
1776
|
+
return between(x, y);
|
|
1777
|
+
} else if (x !== void 0) {
|
|
1778
|
+
return after(x);
|
|
1779
|
+
} else if (y !== void 0) {
|
|
1780
|
+
return before(y);
|
|
1781
|
+
} else {
|
|
1782
|
+
return ONE;
|
|
1783
|
+
}
|
|
1784
|
+
}
|
|
1785
|
+
function before(pos) {
|
|
1786
|
+
const lastIndex = pos.length - 1;
|
|
1787
|
+
for (let i = 0; i <= lastIndex; i++) {
|
|
1788
|
+
const code = pos.charCodeAt(i);
|
|
1789
|
+
if (code <= MIN_CODE) {
|
|
1790
|
+
continue;
|
|
1791
|
+
}
|
|
1792
|
+
if (i === lastIndex) {
|
|
1793
|
+
if (code === MIN_CODE + 1) {
|
|
1794
|
+
return pos.substring(0, i) + ZERO_NINE;
|
|
1795
|
+
} else {
|
|
1796
|
+
return pos.substring(0, i) + String.fromCharCode(code - 1);
|
|
1797
|
+
}
|
|
1798
|
+
} else {
|
|
1799
|
+
return pos.substring(0, i + 1);
|
|
1800
|
+
}
|
|
1801
|
+
}
|
|
1802
|
+
return ONE;
|
|
1803
|
+
}
|
|
1804
|
+
function after(pos) {
|
|
1805
|
+
for (let i = 0; i <= pos.length - 1; i++) {
|
|
1806
|
+
const code = pos.charCodeAt(i);
|
|
1807
|
+
if (code >= MAX_CODE) {
|
|
1808
|
+
continue;
|
|
1809
|
+
}
|
|
1810
|
+
return pos.substring(0, i) + String.fromCharCode(code + 1);
|
|
1811
|
+
}
|
|
1812
|
+
return pos + ONE;
|
|
1813
|
+
}
|
|
1814
|
+
function between(lo, hi) {
|
|
1815
|
+
if (lo < hi) {
|
|
1816
|
+
return _between(lo, hi);
|
|
1817
|
+
} else if (lo > hi) {
|
|
1818
|
+
return _between(hi, lo);
|
|
1819
|
+
} else {
|
|
1820
|
+
throw new Error("Cannot compute value between two equal positions");
|
|
1821
|
+
}
|
|
1822
|
+
}
|
|
1823
|
+
function _between(lo, hi) {
|
|
1824
|
+
let index = 0;
|
|
1825
|
+
const loLen = lo.length;
|
|
1826
|
+
const hiLen = hi.length;
|
|
1827
|
+
while (true) {
|
|
1828
|
+
const loCode = index < loLen ? lo.charCodeAt(index) : MIN_CODE;
|
|
1829
|
+
const hiCode = index < hiLen ? hi.charCodeAt(index) : MAX_CODE;
|
|
1830
|
+
if (loCode === hiCode) {
|
|
1831
|
+
index++;
|
|
1832
|
+
continue;
|
|
1833
|
+
}
|
|
1834
|
+
if (hiCode - loCode === 1) {
|
|
1835
|
+
const prefix = lo.substring(0, index + 1);
|
|
1836
|
+
const suffix = lo.substring(index + 1);
|
|
1837
|
+
const nines = "";
|
|
1838
|
+
return prefix + _between(suffix, nines);
|
|
1839
|
+
} else {
|
|
1840
|
+
return takeN(lo, index) + String.fromCharCode(hiCode + loCode >> 1);
|
|
1841
|
+
}
|
|
1842
|
+
}
|
|
1843
|
+
}
|
|
1844
|
+
function takeN(pos, n) {
|
|
1845
|
+
return n < pos.length ? pos.substring(0, n) : pos + ZERO.repeat(n - pos.length);
|
|
1846
|
+
}
|
|
1847
|
+
var MIN_NON_ZERO_CODE = MIN_CODE + 1;
|
|
1848
|
+
function isPos(str) {
|
|
1849
|
+
if (str === "") {
|
|
1850
|
+
return false;
|
|
1851
|
+
}
|
|
1852
|
+
const lastIdx = str.length - 1;
|
|
1853
|
+
const last = str.charCodeAt(lastIdx);
|
|
1854
|
+
if (last < MIN_NON_ZERO_CODE || last > MAX_CODE) {
|
|
1855
|
+
return false;
|
|
1856
|
+
}
|
|
1857
|
+
for (let i = 0; i < lastIdx; i++) {
|
|
1858
|
+
const code = str.charCodeAt(i);
|
|
1859
|
+
if (code < MIN_CODE || code > MAX_CODE) {
|
|
1860
|
+
return false;
|
|
1861
|
+
}
|
|
1862
|
+
}
|
|
1863
|
+
return true;
|
|
1864
|
+
}
|
|
1865
|
+
function convertToPos(str) {
|
|
1866
|
+
const codes = [];
|
|
1867
|
+
for (let i = 0; i < str.length; i++) {
|
|
1868
|
+
const code = str.charCodeAt(i);
|
|
1869
|
+
codes.push(code < MIN_CODE ? MIN_CODE : code > MAX_CODE ? MAX_CODE : code);
|
|
1870
|
+
}
|
|
1871
|
+
while (codes.length > 0 && codes[codes.length - 1] === MIN_CODE) {
|
|
1872
|
+
codes.length--;
|
|
1873
|
+
}
|
|
1874
|
+
return codes.length > 0 ? String.fromCharCode(...codes) : (
|
|
1875
|
+
// Edge case: the str was a 0-only string, which is invalid. Default back to .1
|
|
1876
|
+
ONE
|
|
1877
|
+
);
|
|
1481
1878
|
}
|
|
1482
1879
|
function asPos(str) {
|
|
1483
1880
|
return isPos(str) ? str : convertToPos(str);
|
|
@@ -3881,41 +4278,6 @@ function captureStackTrace(msg, traceRoot) {
|
|
|
3881
4278
|
return errorLike.stack;
|
|
3882
4279
|
}
|
|
3883
4280
|
|
|
3884
|
-
// src/lib/Json.ts
|
|
3885
|
-
function isJsonScalar(data) {
|
|
3886
|
-
return data === null || typeof data === "string" || typeof data === "number" || typeof data === "boolean";
|
|
3887
|
-
}
|
|
3888
|
-
function isJsonArray(data) {
|
|
3889
|
-
return Array.isArray(data);
|
|
3890
|
-
}
|
|
3891
|
-
function isJsonObject(data) {
|
|
3892
|
-
return !isJsonScalar(data) && !isJsonArray(data);
|
|
3893
|
-
}
|
|
3894
|
-
|
|
3895
|
-
// src/protocol/AuthToken.ts
|
|
3896
|
-
function isStringList(value) {
|
|
3897
|
-
return Array.isArray(value) && value.every((i) => typeof i === "string");
|
|
3898
|
-
}
|
|
3899
|
-
function isMinimalTokenPayload(data) {
|
|
3900
|
-
return isPlainObject(data) && typeof data.actor === "number" && (data.id === void 0 || typeof data.id === "string") && isStringList(data.scopes);
|
|
3901
|
-
}
|
|
3902
|
-
function parseAuthToken(rawTokenString) {
|
|
3903
|
-
const tokenParts = rawTokenString.split(".");
|
|
3904
|
-
if (tokenParts.length !== 3) {
|
|
3905
|
-
throw new Error("Authentication error: invalid JWT token");
|
|
3906
|
-
}
|
|
3907
|
-
const payload = tryParseJson(b64decode(tokenParts[1]));
|
|
3908
|
-
if (!(payload && isMinimalTokenPayload(payload))) {
|
|
3909
|
-
throw new Error(
|
|
3910
|
-
"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"
|
|
3911
|
-
);
|
|
3912
|
-
}
|
|
3913
|
-
return {
|
|
3914
|
-
raw: rawTokenString,
|
|
3915
|
-
parsed: payload
|
|
3916
|
-
};
|
|
3917
|
-
}
|
|
3918
|
-
|
|
3919
4281
|
// src/protocol/ClientMsg.ts
|
|
3920
4282
|
var ClientMsgCode = /* @__PURE__ */ ((ClientMsgCode2) => {
|
|
3921
4283
|
ClientMsgCode2[ClientMsgCode2["UPDATE_PRESENCE"] = 100] = "UPDATE_PRESENCE";
|
|
@@ -3927,20 +4289,6 @@ var ClientMsgCode = /* @__PURE__ */ ((ClientMsgCode2) => {
|
|
|
3927
4289
|
return ClientMsgCode2;
|
|
3928
4290
|
})(ClientMsgCode || {});
|
|
3929
4291
|
|
|
3930
|
-
// src/protocol/ServerMsg.ts
|
|
3931
|
-
var ServerMsgCode = /* @__PURE__ */ ((ServerMsgCode2) => {
|
|
3932
|
-
ServerMsgCode2[ServerMsgCode2["UPDATE_PRESENCE"] = 100] = "UPDATE_PRESENCE";
|
|
3933
|
-
ServerMsgCode2[ServerMsgCode2["USER_JOINED"] = 101] = "USER_JOINED";
|
|
3934
|
-
ServerMsgCode2[ServerMsgCode2["USER_LEFT"] = 102] = "USER_LEFT";
|
|
3935
|
-
ServerMsgCode2[ServerMsgCode2["BROADCASTED_EVENT"] = 103] = "BROADCASTED_EVENT";
|
|
3936
|
-
ServerMsgCode2[ServerMsgCode2["ROOM_STATE"] = 104] = "ROOM_STATE";
|
|
3937
|
-
ServerMsgCode2[ServerMsgCode2["INITIAL_STORAGE_STATE"] = 200] = "INITIAL_STORAGE_STATE";
|
|
3938
|
-
ServerMsgCode2[ServerMsgCode2["UPDATE_STORAGE"] = 201] = "UPDATE_STORAGE";
|
|
3939
|
-
ServerMsgCode2[ServerMsgCode2["REJECT_STORAGE_OP"] = 299] = "REJECT_STORAGE_OP";
|
|
3940
|
-
ServerMsgCode2[ServerMsgCode2["UPDATE_YDOC"] = 300] = "UPDATE_YDOC";
|
|
3941
|
-
return ServerMsgCode2;
|
|
3942
|
-
})(ServerMsgCode || {});
|
|
3943
|
-
|
|
3944
4292
|
// src/lib/LegacyArray.ts
|
|
3945
4293
|
function asArrayWithLegacyMethods(arr) {
|
|
3946
4294
|
Object.defineProperty(arr, "count", {
|
|
@@ -3992,7 +4340,19 @@ var ImmutableRef = class {
|
|
|
3992
4340
|
|
|
3993
4341
|
// src/refs/OthersRef.ts
|
|
3994
4342
|
function makeUser(conn, presence) {
|
|
3995
|
-
|
|
4343
|
+
const { connectionId, id, info } = conn;
|
|
4344
|
+
const canWrite = canWriteStorage(conn.scopes);
|
|
4345
|
+
return freeze(
|
|
4346
|
+
compactObject({
|
|
4347
|
+
connectionId,
|
|
4348
|
+
id,
|
|
4349
|
+
info,
|
|
4350
|
+
canWrite,
|
|
4351
|
+
isReadOnly: !canWrite,
|
|
4352
|
+
// Deprecated, kept for backward-compatibility
|
|
4353
|
+
presence
|
|
4354
|
+
})
|
|
4355
|
+
);
|
|
3996
4356
|
}
|
|
3997
4357
|
var OthersRef = class extends ImmutableRef {
|
|
3998
4358
|
//
|
|
@@ -4000,50 +4360,53 @@ var OthersRef = class extends ImmutableRef {
|
|
|
4000
4360
|
//
|
|
4001
4361
|
constructor() {
|
|
4002
4362
|
super();
|
|
4003
|
-
this._connections =
|
|
4004
|
-
this._presences =
|
|
4005
|
-
this._users =
|
|
4363
|
+
this._connections = /* @__PURE__ */ new Map();
|
|
4364
|
+
this._presences = /* @__PURE__ */ new Map();
|
|
4365
|
+
this._users = /* @__PURE__ */ new Map();
|
|
4366
|
+
}
|
|
4367
|
+
connectionIds() {
|
|
4368
|
+
return this._connections.keys();
|
|
4006
4369
|
}
|
|
4007
4370
|
/** @internal */
|
|
4008
4371
|
_toImmutable() {
|
|
4009
4372
|
const users = compact(
|
|
4010
|
-
|
|
4373
|
+
Array.from(this._presences.keys()).map(
|
|
4011
4374
|
(connectionId) => this.getUser(Number(connectionId))
|
|
4012
4375
|
)
|
|
4013
4376
|
);
|
|
4014
4377
|
return asArrayWithLegacyMethods(users);
|
|
4015
4378
|
}
|
|
4016
4379
|
clearOthers() {
|
|
4017
|
-
this._connections =
|
|
4018
|
-
this._presences =
|
|
4019
|
-
this._users =
|
|
4380
|
+
this._connections = /* @__PURE__ */ new Map();
|
|
4381
|
+
this._presences = /* @__PURE__ */ new Map();
|
|
4382
|
+
this._users = /* @__PURE__ */ new Map();
|
|
4020
4383
|
this.invalidate();
|
|
4021
4384
|
}
|
|
4022
4385
|
/** @internal */
|
|
4023
4386
|
_getUser(connectionId) {
|
|
4024
|
-
const conn = this._connections
|
|
4025
|
-
const presence = this._presences
|
|
4387
|
+
const conn = this._connections.get(connectionId);
|
|
4388
|
+
const presence = this._presences.get(connectionId);
|
|
4026
4389
|
if (conn !== void 0 && presence !== void 0) {
|
|
4027
4390
|
return makeUser(conn, presence);
|
|
4028
4391
|
}
|
|
4029
4392
|
return void 0;
|
|
4030
4393
|
}
|
|
4031
4394
|
getUser(connectionId) {
|
|
4032
|
-
const cachedUser = this._users
|
|
4395
|
+
const cachedUser = this._users.get(connectionId);
|
|
4033
4396
|
if (cachedUser) {
|
|
4034
4397
|
return cachedUser;
|
|
4035
4398
|
}
|
|
4036
4399
|
const computedUser = this._getUser(connectionId);
|
|
4037
4400
|
if (computedUser) {
|
|
4038
|
-
this._users
|
|
4401
|
+
this._users.set(connectionId, computedUser);
|
|
4039
4402
|
return computedUser;
|
|
4040
4403
|
}
|
|
4041
4404
|
return void 0;
|
|
4042
4405
|
}
|
|
4043
4406
|
/** @internal */
|
|
4044
4407
|
_invalidateUser(connectionId) {
|
|
4045
|
-
if (this._users
|
|
4046
|
-
|
|
4408
|
+
if (this._users.has(connectionId)) {
|
|
4409
|
+
this._users.delete(connectionId);
|
|
4047
4410
|
}
|
|
4048
4411
|
this.invalidate();
|
|
4049
4412
|
}
|
|
@@ -4051,14 +4414,17 @@ var OthersRef = class extends ImmutableRef {
|
|
|
4051
4414
|
* Records a known connection. This records the connection ID and the
|
|
4052
4415
|
* associated metadata.
|
|
4053
4416
|
*/
|
|
4054
|
-
setConnection(connectionId, metaUserId, metaUserInfo,
|
|
4055
|
-
this._connections
|
|
4417
|
+
setConnection(connectionId, metaUserId, metaUserInfo, scopes) {
|
|
4418
|
+
this._connections.set(
|
|
4056
4419
|
connectionId,
|
|
4057
|
-
|
|
4058
|
-
|
|
4059
|
-
|
|
4060
|
-
|
|
4061
|
-
|
|
4420
|
+
freeze({
|
|
4421
|
+
connectionId,
|
|
4422
|
+
id: metaUserId,
|
|
4423
|
+
info: metaUserInfo,
|
|
4424
|
+
scopes
|
|
4425
|
+
})
|
|
4426
|
+
);
|
|
4427
|
+
if (this._presences.has(connectionId)) {
|
|
4062
4428
|
this._invalidateUser(connectionId);
|
|
4063
4429
|
}
|
|
4064
4430
|
}
|
|
@@ -4067,8 +4433,8 @@ var OthersRef = class extends ImmutableRef {
|
|
|
4067
4433
|
* the presence information.
|
|
4068
4434
|
*/
|
|
4069
4435
|
removeConnection(connectionId) {
|
|
4070
|
-
|
|
4071
|
-
|
|
4436
|
+
this._connections.delete(connectionId);
|
|
4437
|
+
this._presences.delete(connectionId);
|
|
4072
4438
|
this._invalidateUser(connectionId);
|
|
4073
4439
|
}
|
|
4074
4440
|
/**
|
|
@@ -4076,8 +4442,8 @@ var OthersRef = class extends ImmutableRef {
|
|
|
4076
4442
|
* its known presence data is overwritten.
|
|
4077
4443
|
*/
|
|
4078
4444
|
setOther(connectionId, presence) {
|
|
4079
|
-
this._presences
|
|
4080
|
-
if (this._connections
|
|
4445
|
+
this._presences.set(connectionId, freeze(compactObject(presence)));
|
|
4446
|
+
if (this._connections.has(connectionId)) {
|
|
4081
4447
|
this._invalidateUser(connectionId);
|
|
4082
4448
|
}
|
|
4083
4449
|
}
|
|
@@ -4087,13 +4453,13 @@ var OthersRef = class extends ImmutableRef {
|
|
|
4087
4453
|
* full .setOther() call first.
|
|
4088
4454
|
*/
|
|
4089
4455
|
patchOther(connectionId, patch) {
|
|
4090
|
-
const oldPresence = this._presences
|
|
4456
|
+
const oldPresence = this._presences.get(connectionId);
|
|
4091
4457
|
if (oldPresence === void 0) {
|
|
4092
4458
|
return;
|
|
4093
4459
|
}
|
|
4094
4460
|
const newPresence = merge(oldPresence, patch);
|
|
4095
4461
|
if (oldPresence !== newPresence) {
|
|
4096
|
-
this._presences
|
|
4462
|
+
this._presences.set(connectionId, freeze(newPresence));
|
|
4097
4463
|
this._invalidateUser(connectionId);
|
|
4098
4464
|
}
|
|
4099
4465
|
}
|
|
@@ -4173,17 +4539,7 @@ function userToTreeNode(key, user) {
|
|
|
4173
4539
|
function createRoom(options, config) {
|
|
4174
4540
|
const initialPresence = typeof options.initialPresence === "function" ? options.initialPresence(config.roomId) : options.initialPresence;
|
|
4175
4541
|
const initialStorage = typeof options.initialStorage === "function" ? options.initialStorage(config.roomId) : options.initialStorage;
|
|
4176
|
-
const delegates = config.delegates
|
|
4177
|
-
authenticate: makeAuthDelegateForRoom(
|
|
4178
|
-
config.roomId,
|
|
4179
|
-
config.authentication,
|
|
4180
|
-
config.polyfills?.fetch
|
|
4181
|
-
),
|
|
4182
|
-
createSocket: makeCreateSocketDelegateForRoom(
|
|
4183
|
-
config.liveblocksServer,
|
|
4184
|
-
config.polyfills?.WebSocket
|
|
4185
|
-
)
|
|
4186
|
-
};
|
|
4542
|
+
const delegates = config.delegates;
|
|
4187
4543
|
const managedSocket = new ManagedSocket(
|
|
4188
4544
|
delegates,
|
|
4189
4545
|
config.enableDebugLogging
|
|
@@ -4202,8 +4558,9 @@ function createRoom(options, config) {
|
|
|
4202
4558
|
messages: [],
|
|
4203
4559
|
storageOperations: []
|
|
4204
4560
|
},
|
|
4205
|
-
|
|
4206
|
-
|
|
4561
|
+
staticSessionInfo: new ValueRef(null),
|
|
4562
|
+
dynamicSessionInfo: new ValueRef(null),
|
|
4563
|
+
myPresence: new PatchableRef(initialPresence),
|
|
4207
4564
|
others: new OthersRef(),
|
|
4208
4565
|
initialStorage,
|
|
4209
4566
|
idFactory: null,
|
|
@@ -4222,18 +4579,26 @@ function createRoom(options, config) {
|
|
|
4222
4579
|
};
|
|
4223
4580
|
const doNotBatchUpdates = (cb) => cb();
|
|
4224
4581
|
const batchUpdates = config.unstable_batchedUpdates ?? doNotBatchUpdates;
|
|
4225
|
-
let
|
|
4582
|
+
let lastTokenKey;
|
|
4226
4583
|
function onStatusDidChange(newStatus) {
|
|
4227
|
-
const
|
|
4228
|
-
if (
|
|
4229
|
-
|
|
4230
|
-
|
|
4231
|
-
|
|
4232
|
-
|
|
4233
|
-
|
|
4234
|
-
|
|
4235
|
-
|
|
4236
|
-
|
|
4584
|
+
const authValue = managedSocket.authValue;
|
|
4585
|
+
if (authValue !== null) {
|
|
4586
|
+
const tokenKey = authValue.type === "secret" ? authValue.token.raw : authValue.publicApiKey;
|
|
4587
|
+
if (tokenKey !== lastTokenKey) {
|
|
4588
|
+
lastTokenKey = tokenKey;
|
|
4589
|
+
if (authValue.type === "secret") {
|
|
4590
|
+
const token = authValue.token.parsed;
|
|
4591
|
+
context.staticSessionInfo.set({
|
|
4592
|
+
userId: token.k === "sec-legacy" /* SECRET_LEGACY */ ? token.id : token.uid,
|
|
4593
|
+
userInfo: token.k === "sec-legacy" /* SECRET_LEGACY */ ? token.info : token.ui
|
|
4594
|
+
});
|
|
4595
|
+
} else {
|
|
4596
|
+
context.staticSessionInfo.set({
|
|
4597
|
+
userId: void 0,
|
|
4598
|
+
userInfo: void 0
|
|
4599
|
+
});
|
|
4600
|
+
}
|
|
4601
|
+
}
|
|
4237
4602
|
}
|
|
4238
4603
|
batchUpdates(() => {
|
|
4239
4604
|
eventHub.status.notify(newStatus);
|
|
@@ -4270,20 +4635,15 @@ function createRoom(options, config) {
|
|
|
4270
4635
|
}
|
|
4271
4636
|
}
|
|
4272
4637
|
function onDidConnect() {
|
|
4273
|
-
const sessionInfo = context.sessionInfo.current;
|
|
4274
|
-
if (sessionInfo === null) {
|
|
4275
|
-
throw new Error("Unexpected missing session info");
|
|
4276
|
-
}
|
|
4277
4638
|
context.buffer.presenceUpdates = {
|
|
4278
4639
|
type: "full",
|
|
4279
4640
|
data: (
|
|
4280
4641
|
// Because context.me.current is a readonly object, we'll have to
|
|
4281
4642
|
// make a copy here. Otherwise, type errors happen later when
|
|
4282
4643
|
// "patching" my presence.
|
|
4283
|
-
{ ...context.
|
|
4644
|
+
{ ...context.myPresence.current }
|
|
4284
4645
|
)
|
|
4285
4646
|
};
|
|
4286
|
-
context.idFactory = makeIdFactory(sessionInfo.actor);
|
|
4287
4647
|
if (_getStorage$ !== null) {
|
|
4288
4648
|
refreshStorage({ flush: false });
|
|
4289
4649
|
}
|
|
@@ -4348,7 +4708,12 @@ function createRoom(options, config) {
|
|
|
4348
4708
|
}
|
|
4349
4709
|
},
|
|
4350
4710
|
assertStorageIsWritable: () => {
|
|
4351
|
-
|
|
4711
|
+
const scopes = context.dynamicSessionInfo.current?.scopes;
|
|
4712
|
+
if (scopes === void 0) {
|
|
4713
|
+
return;
|
|
4714
|
+
}
|
|
4715
|
+
const canWrite = canWriteStorage(scopes);
|
|
4716
|
+
if (!canWrite) {
|
|
4352
4717
|
throw new Error(
|
|
4353
4718
|
"Cannot write to storage with a read only user, please ensure the user has write permissions"
|
|
4354
4719
|
);
|
|
@@ -4376,10 +4741,11 @@ function createRoom(options, config) {
|
|
|
4376
4741
|
const message = JSON.stringify(messageOrMessages);
|
|
4377
4742
|
if (config.unstable_fallbackToHTTP) {
|
|
4378
4743
|
const size = new TextEncoder().encode(message).length;
|
|
4379
|
-
if (size > MAX_MESSAGE_SIZE &&
|
|
4744
|
+
if (size > MAX_MESSAGE_SIZE && // TODO: support public api key auth in REST API
|
|
4745
|
+
managedSocket.authValue?.type === "secret" && config.httpSendEndpoint) {
|
|
4380
4746
|
void httpSend(
|
|
4381
4747
|
message,
|
|
4382
|
-
managedSocket.token.raw,
|
|
4748
|
+
managedSocket.authValue.token.raw,
|
|
4383
4749
|
config.httpSendEndpoint,
|
|
4384
4750
|
config.polyfills?.fetch
|
|
4385
4751
|
).then((resp) => {
|
|
@@ -4396,16 +4762,24 @@ function createRoom(options, config) {
|
|
|
4396
4762
|
managedSocket.send(message);
|
|
4397
4763
|
}
|
|
4398
4764
|
const self = new DerivedRef(
|
|
4399
|
-
context.
|
|
4400
|
-
context.
|
|
4401
|
-
|
|
4402
|
-
|
|
4403
|
-
|
|
4404
|
-
|
|
4405
|
-
|
|
4406
|
-
|
|
4407
|
-
|
|
4408
|
-
|
|
4765
|
+
context.staticSessionInfo,
|
|
4766
|
+
context.dynamicSessionInfo,
|
|
4767
|
+
context.myPresence,
|
|
4768
|
+
(staticSession, dynamicSession, myPresence) => {
|
|
4769
|
+
if (staticSession === null || dynamicSession === null) {
|
|
4770
|
+
return null;
|
|
4771
|
+
} else {
|
|
4772
|
+
const canWrite = canWriteStorage(dynamicSession.scopes);
|
|
4773
|
+
return {
|
|
4774
|
+
connectionId: dynamicSession.actor,
|
|
4775
|
+
id: staticSession.userId,
|
|
4776
|
+
info: staticSession.userInfo,
|
|
4777
|
+
presence: myPresence,
|
|
4778
|
+
canWrite,
|
|
4779
|
+
isReadOnly: !canWrite
|
|
4780
|
+
// Deprecated, kept for backward-compatibility
|
|
4781
|
+
};
|
|
4782
|
+
}
|
|
4409
4783
|
}
|
|
4410
4784
|
);
|
|
4411
4785
|
let _lastSelf;
|
|
@@ -4477,7 +4851,7 @@ function createRoom(options, config) {
|
|
|
4477
4851
|
}
|
|
4478
4852
|
if (presence) {
|
|
4479
4853
|
notifySelfChanged(doNotBatchUpdates);
|
|
4480
|
-
eventHub.myPresence.notify(context.
|
|
4854
|
+
eventHub.myPresence.notify(context.myPresence.current);
|
|
4481
4855
|
}
|
|
4482
4856
|
if (storageUpdates.size > 0) {
|
|
4483
4857
|
const updates = Array.from(storageUpdates.values());
|
|
@@ -4487,7 +4861,7 @@ function createRoom(options, config) {
|
|
|
4487
4861
|
});
|
|
4488
4862
|
}
|
|
4489
4863
|
function getConnectionId() {
|
|
4490
|
-
const info = context.
|
|
4864
|
+
const info = context.dynamicSessionInfo.current;
|
|
4491
4865
|
if (info) {
|
|
4492
4866
|
return info.actor;
|
|
4493
4867
|
}
|
|
@@ -4516,9 +4890,9 @@ function createRoom(options, config) {
|
|
|
4516
4890
|
data: {}
|
|
4517
4891
|
};
|
|
4518
4892
|
for (const key in op.data) {
|
|
4519
|
-
reverse.data[key] = context.
|
|
4893
|
+
reverse.data[key] = context.myPresence.current[key];
|
|
4520
4894
|
}
|
|
4521
|
-
context.
|
|
4895
|
+
context.myPresence.patch(op.data);
|
|
4522
4896
|
if (context.buffer.presenceUpdates === null) {
|
|
4523
4897
|
context.buffer.presenceUpdates = { type: "partial", data: op.data };
|
|
4524
4898
|
} else {
|
|
@@ -4625,9 +4999,9 @@ function createRoom(options, config) {
|
|
|
4625
4999
|
continue;
|
|
4626
5000
|
}
|
|
4627
5001
|
context.buffer.presenceUpdates.data[key] = overrideValue;
|
|
4628
|
-
oldValues[key] = context.
|
|
5002
|
+
oldValues[key] = context.myPresence.current[key];
|
|
4629
5003
|
}
|
|
4630
|
-
context.
|
|
5004
|
+
context.myPresence.patch(patch);
|
|
4631
5005
|
if (context.activeBatch) {
|
|
4632
5006
|
if (options2?.addToHistory) {
|
|
4633
5007
|
context.activeBatch.reverseOps.unshift({
|
|
@@ -4649,9 +5023,6 @@ function createRoom(options, config) {
|
|
|
4649
5023
|
});
|
|
4650
5024
|
}
|
|
4651
5025
|
}
|
|
4652
|
-
function isStorageReadOnly(scopes) {
|
|
4653
|
-
return scopes.includes("room:read" /* Read */) && scopes.includes("room:presence:write" /* PresenceWrite */) && !scopes.includes("room:write" /* Write */);
|
|
4654
|
-
}
|
|
4655
5026
|
function onUpdatePresenceMessage(message) {
|
|
4656
5027
|
if (message.targetActor !== void 0) {
|
|
4657
5028
|
const oldUser = context.others.getUser(message.actor);
|
|
@@ -4682,11 +5053,17 @@ function createRoom(options, config) {
|
|
|
4682
5053
|
}
|
|
4683
5054
|
return null;
|
|
4684
5055
|
}
|
|
4685
|
-
function onRoomStateMessage(message) {
|
|
4686
|
-
|
|
5056
|
+
function onRoomStateMessage(message, batchedUpdatesWrapper) {
|
|
5057
|
+
context.dynamicSessionInfo.set({
|
|
5058
|
+
actor: message.actor,
|
|
5059
|
+
scopes: message.scopes
|
|
5060
|
+
});
|
|
5061
|
+
context.idFactory = makeIdFactory(message.actor);
|
|
5062
|
+
notifySelfChanged(batchedUpdatesWrapper);
|
|
5063
|
+
for (const connectionId of context.others.connectionIds()) {
|
|
4687
5064
|
const user = message.users[connectionId];
|
|
4688
5065
|
if (user === void 0) {
|
|
4689
|
-
context.others.removeConnection(
|
|
5066
|
+
context.others.removeConnection(connectionId);
|
|
4690
5067
|
}
|
|
4691
5068
|
}
|
|
4692
5069
|
for (const key in message.users) {
|
|
@@ -4696,7 +5073,7 @@ function createRoom(options, config) {
|
|
|
4696
5073
|
connectionId,
|
|
4697
5074
|
user.id,
|
|
4698
5075
|
user.info,
|
|
4699
|
-
|
|
5076
|
+
user.scopes
|
|
4700
5077
|
);
|
|
4701
5078
|
}
|
|
4702
5079
|
return { type: "reset" };
|
|
@@ -4717,11 +5094,11 @@ function createRoom(options, config) {
|
|
|
4717
5094
|
message.actor,
|
|
4718
5095
|
message.id,
|
|
4719
5096
|
message.info,
|
|
4720
|
-
|
|
5097
|
+
message.scopes
|
|
4721
5098
|
);
|
|
4722
5099
|
context.buffer.messages.push({
|
|
4723
5100
|
type: 100 /* UPDATE_PRESENCE */,
|
|
4724
|
-
data: context.
|
|
5101
|
+
data: context.myPresence.current,
|
|
4725
5102
|
targetActor: message.actor
|
|
4726
5103
|
});
|
|
4727
5104
|
flushNowOrSoon();
|
|
@@ -4806,7 +5183,7 @@ function createRoom(options, config) {
|
|
|
4806
5183
|
break;
|
|
4807
5184
|
}
|
|
4808
5185
|
case 104 /* ROOM_STATE */: {
|
|
4809
|
-
updates.others.push(onRoomStateMessage(message));
|
|
5186
|
+
updates.others.push(onRoomStateMessage(message, doNotBatchUpdates));
|
|
4810
5187
|
break;
|
|
4811
5188
|
}
|
|
4812
5189
|
case 200 /* INITIAL_STORAGE_STATE */: {
|
|
@@ -5173,10 +5550,9 @@ ${Array.from(traces).join("\n\n")}`
|
|
|
5173
5550
|
// Core
|
|
5174
5551
|
getStatus: () => managedSocket.getStatus(),
|
|
5175
5552
|
getConnectionState: () => managedSocket.getLegacyStatus(),
|
|
5176
|
-
isSelfAware: () => context.sessionInfo.current !== null,
|
|
5177
5553
|
getSelf: () => self.current,
|
|
5178
5554
|
// Presence
|
|
5179
|
-
getPresence: () => context.
|
|
5555
|
+
getPresence: () => context.myPresence.current,
|
|
5180
5556
|
getOthers: () => context.others.current
|
|
5181
5557
|
},
|
|
5182
5558
|
// Explictly make the __internal field non-enumerable, to avoid aggressive
|
|
@@ -5271,18 +5647,30 @@ function makeClassicSubscribeFn(events) {
|
|
|
5271
5647
|
function isRoomEventName(value) {
|
|
5272
5648
|
return value === "my-presence" || value === "others" || value === "event" || value === "error" || value === "history" || value === "status" || value === "storage-status" || value === "lost-connection" || value === "connection";
|
|
5273
5649
|
}
|
|
5274
|
-
function
|
|
5275
|
-
return (
|
|
5650
|
+
function makeAuthDelegateForRoom(roomId, authManager) {
|
|
5651
|
+
return async () => {
|
|
5652
|
+
return authManager.getAuthValue("room:read", roomId);
|
|
5653
|
+
};
|
|
5654
|
+
}
|
|
5655
|
+
function makeCreateSocketDelegateForRoom(roomId, liveblocksServer, WebSocketPolyfill) {
|
|
5656
|
+
return (authValue) => {
|
|
5276
5657
|
const ws = WebSocketPolyfill ?? (typeof WebSocket === "undefined" ? void 0 : WebSocket);
|
|
5277
5658
|
if (ws === void 0) {
|
|
5278
5659
|
throw new StopRetrying(
|
|
5279
5660
|
"To use Liveblocks client in a non-dom environment, you need to provide a WebSocket polyfill."
|
|
5280
5661
|
);
|
|
5281
5662
|
}
|
|
5282
|
-
const
|
|
5283
|
-
|
|
5284
|
-
|
|
5285
|
-
|
|
5663
|
+
const url = new URL(liveblocksServer);
|
|
5664
|
+
url.searchParams.set("roomId", roomId);
|
|
5665
|
+
if (authValue.type === "secret") {
|
|
5666
|
+
url.searchParams.set("tok", authValue.token.raw);
|
|
5667
|
+
} else if (authValue.type === "public") {
|
|
5668
|
+
url.searchParams.set("pubkey", authValue.publicApiKey);
|
|
5669
|
+
} else {
|
|
5670
|
+
return assertNever(authValue, "Unhandled case");
|
|
5671
|
+
}
|
|
5672
|
+
url.searchParams.set("version", PKG_VERSION || "dev");
|
|
5673
|
+
return new ws(url.toString());
|
|
5286
5674
|
};
|
|
5287
5675
|
}
|
|
5288
5676
|
async function httpSend(message, token, endpoint, fetchPolyfill) {
|
|
@@ -5297,96 +5685,6 @@ async function httpSend(message, token, endpoint, fetchPolyfill) {
|
|
|
5297
5685
|
body: message
|
|
5298
5686
|
});
|
|
5299
5687
|
}
|
|
5300
|
-
function makeAuthDelegateForRoom(roomId, authentication, fetchPolyfill) {
|
|
5301
|
-
const fetcher = fetchPolyfill ?? (typeof window === "undefined" ? void 0 : window.fetch);
|
|
5302
|
-
if (authentication.type === "public") {
|
|
5303
|
-
return async () => {
|
|
5304
|
-
if (fetcher === void 0) {
|
|
5305
|
-
throw new StopRetrying(
|
|
5306
|
-
"To use Liveblocks client in a non-dom environment with a publicApiKey, you need to provide a fetch polyfill."
|
|
5307
|
-
);
|
|
5308
|
-
}
|
|
5309
|
-
return fetchAuthEndpoint(fetcher, authentication.url, {
|
|
5310
|
-
room: roomId,
|
|
5311
|
-
publicApiKey: authentication.publicApiKey
|
|
5312
|
-
}).then(({ token }) => parseAuthToken(token));
|
|
5313
|
-
};
|
|
5314
|
-
} else if (authentication.type === "private") {
|
|
5315
|
-
return async () => {
|
|
5316
|
-
if (fetcher === void 0) {
|
|
5317
|
-
throw new StopRetrying(
|
|
5318
|
-
"To use Liveblocks client in a non-dom environment with a url as auth endpoint, you need to provide a fetch polyfill."
|
|
5319
|
-
);
|
|
5320
|
-
}
|
|
5321
|
-
return fetchAuthEndpoint(fetcher, authentication.url, {
|
|
5322
|
-
room: roomId
|
|
5323
|
-
}).then(({ token }) => parseAuthToken(token));
|
|
5324
|
-
};
|
|
5325
|
-
} else if (authentication.type === "custom") {
|
|
5326
|
-
return async () => {
|
|
5327
|
-
const response = await authentication.callback(roomId);
|
|
5328
|
-
if (!response || typeof response !== "object") {
|
|
5329
|
-
throw new Error(
|
|
5330
|
-
'We expect the authentication callback to return a token, but it does not. Hint: the return value should look like: { token: "..." }'
|
|
5331
|
-
);
|
|
5332
|
-
}
|
|
5333
|
-
if (typeof response.token === "string") {
|
|
5334
|
-
return parseAuthToken(response.token);
|
|
5335
|
-
} else if (typeof response.error === "string") {
|
|
5336
|
-
const reason = `Authentication failed: ${"reason" in response && typeof response.reason === "string" ? response.reason : "Forbidden"}`;
|
|
5337
|
-
if (response.error === "forbidden") {
|
|
5338
|
-
throw new StopRetrying(reason);
|
|
5339
|
-
} else {
|
|
5340
|
-
throw new Error(reason);
|
|
5341
|
-
}
|
|
5342
|
-
} else {
|
|
5343
|
-
throw new Error(
|
|
5344
|
-
'We expect the authentication callback to return a token, but it does not. Hint: the return value should look like: { token: "..." }'
|
|
5345
|
-
);
|
|
5346
|
-
}
|
|
5347
|
-
};
|
|
5348
|
-
} else {
|
|
5349
|
-
throw new Error("Internal error. Unexpected authentication type");
|
|
5350
|
-
}
|
|
5351
|
-
}
|
|
5352
|
-
async function fetchAuthEndpoint(fetch2, endpoint, body) {
|
|
5353
|
-
const res = await fetch2(endpoint, {
|
|
5354
|
-
method: "POST",
|
|
5355
|
-
headers: {
|
|
5356
|
-
"Content-Type": "application/json"
|
|
5357
|
-
},
|
|
5358
|
-
// Credentials are needed to support authentication with cookies
|
|
5359
|
-
credentials: "include",
|
|
5360
|
-
body: JSON.stringify(body)
|
|
5361
|
-
});
|
|
5362
|
-
if (!res.ok) {
|
|
5363
|
-
const reason = `${(await res.text()).trim() || "reason not provided in auth response"} (${res.status} returned by POST ${endpoint})`;
|
|
5364
|
-
if (res.status === 401 || res.status === 403) {
|
|
5365
|
-
throw new StopRetrying(`Unauthorized: ${reason}`);
|
|
5366
|
-
} else {
|
|
5367
|
-
throw new Error(`Failed to authenticate: ${reason}`);
|
|
5368
|
-
}
|
|
5369
|
-
}
|
|
5370
|
-
let data;
|
|
5371
|
-
try {
|
|
5372
|
-
data = await res.json();
|
|
5373
|
-
} catch (er) {
|
|
5374
|
-
throw new Error(
|
|
5375
|
-
`Expected a JSON response when doing a POST request on "${endpoint}". ${String(
|
|
5376
|
-
er
|
|
5377
|
-
)}`
|
|
5378
|
-
);
|
|
5379
|
-
}
|
|
5380
|
-
if (!isPlainObject(data) || typeof data.token !== "string") {
|
|
5381
|
-
throw new Error(
|
|
5382
|
-
`Expected a JSON response of the form \`{ token: "..." }\` when doing a POST request on "${endpoint}", but got ${JSON.stringify(
|
|
5383
|
-
data
|
|
5384
|
-
)}`
|
|
5385
|
-
);
|
|
5386
|
-
}
|
|
5387
|
-
const { token } = data;
|
|
5388
|
-
return { token };
|
|
5389
|
-
}
|
|
5390
5688
|
|
|
5391
5689
|
// src/client.ts
|
|
5392
5690
|
var MIN_THROTTLE = 16;
|
|
@@ -5398,7 +5696,7 @@ var MAX_LOST_CONNECTION_TIMEOUT = 3e4;
|
|
|
5398
5696
|
var DEFAULT_LOST_CONNECTION_TIMEOUT = 5e3;
|
|
5399
5697
|
function getServerFromClientOptions(clientOptions) {
|
|
5400
5698
|
const rawOptions = clientOptions;
|
|
5401
|
-
return typeof rawOptions.liveblocksServer === "string" ? rawOptions.liveblocksServer : "wss://api.liveblocks.io/
|
|
5699
|
+
return typeof rawOptions.liveblocksServer === "string" ? rawOptions.liveblocksServer : "wss://api.liveblocks.io/v7";
|
|
5402
5700
|
}
|
|
5403
5701
|
function createClient(options) {
|
|
5404
5702
|
const clientOptions = options;
|
|
@@ -5406,6 +5704,7 @@ function createClient(options) {
|
|
|
5406
5704
|
const lostConnectionTimeout = getLostConnectionTimeout(
|
|
5407
5705
|
clientOptions.lostConnectionTimeout ?? DEFAULT_LOST_CONNECTION_TIMEOUT
|
|
5408
5706
|
);
|
|
5707
|
+
const authManager = createAuthManager(options);
|
|
5409
5708
|
const rooms = /* @__PURE__ */ new Map();
|
|
5410
5709
|
function getRoom(roomId) {
|
|
5411
5710
|
const room = rooms.get(roomId);
|
|
@@ -5430,11 +5729,17 @@ function createClient(options) {
|
|
|
5430
5729
|
throttleDelay,
|
|
5431
5730
|
lostConnectionTimeout,
|
|
5432
5731
|
polyfills: clientOptions.polyfills,
|
|
5433
|
-
delegates: clientOptions.mockedDelegates
|
|
5732
|
+
delegates: clientOptions.mockedDelegates ?? {
|
|
5733
|
+
createSocket: makeCreateSocketDelegateForRoom(
|
|
5734
|
+
roomId,
|
|
5735
|
+
getServerFromClientOptions(clientOptions),
|
|
5736
|
+
clientOptions.polyfills?.WebSocket
|
|
5737
|
+
),
|
|
5738
|
+
authenticate: makeAuthDelegateForRoom(roomId, authManager)
|
|
5739
|
+
},
|
|
5434
5740
|
enableDebugLogging: clientOptions.enableDebugLogging,
|
|
5435
5741
|
unstable_batchedUpdates: options2?.unstable_batchedUpdates,
|
|
5436
5742
|
liveblocksServer: getServerFromClientOptions(clientOptions),
|
|
5437
|
-
authentication: prepareAuthentication(clientOptions, roomId),
|
|
5438
5743
|
httpSendEndpoint: buildLiveblocksHttpSendEndpoint(
|
|
5439
5744
|
clientOptions,
|
|
5440
5745
|
roomId
|
|
@@ -5468,6 +5773,13 @@ function createClient(options) {
|
|
|
5468
5773
|
}
|
|
5469
5774
|
}
|
|
5470
5775
|
return {
|
|
5776
|
+
__internal: {
|
|
5777
|
+
getAuthValue: authManager.getAuthValue,
|
|
5778
|
+
realtimeClient: createRealtimeClient(
|
|
5779
|
+
authManager,
|
|
5780
|
+
getWsEventServerEndpoint(options)
|
|
5781
|
+
)
|
|
5782
|
+
},
|
|
5471
5783
|
getRoom,
|
|
5472
5784
|
enter,
|
|
5473
5785
|
leave
|
|
@@ -5493,48 +5805,6 @@ function getLostConnectionTimeout(value) {
|
|
|
5493
5805
|
RECOMMENDED_MIN_LOST_CONNECTION_TIMEOUT
|
|
5494
5806
|
);
|
|
5495
5807
|
}
|
|
5496
|
-
function prepareAuthentication(clientOptions, roomId) {
|
|
5497
|
-
const { publicApiKey, authEndpoint } = clientOptions;
|
|
5498
|
-
if (authEndpoint !== void 0 && publicApiKey !== void 0) {
|
|
5499
|
-
throw new Error(
|
|
5500
|
-
"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"
|
|
5501
|
-
);
|
|
5502
|
-
}
|
|
5503
|
-
if (typeof publicApiKey === "string") {
|
|
5504
|
-
if (publicApiKey.startsWith("sk_")) {
|
|
5505
|
-
throw new Error(
|
|
5506
|
-
"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"
|
|
5507
|
-
);
|
|
5508
|
-
} else if (!publicApiKey.startsWith("pk_")) {
|
|
5509
|
-
throw new Error(
|
|
5510
|
-
"Invalid key. Please use the public key format: pk_<public key>. For more information: https://liveblocks.io/docs/api-reference/liveblocks-client#createClientPublicKey"
|
|
5511
|
-
);
|
|
5512
|
-
}
|
|
5513
|
-
return {
|
|
5514
|
-
type: "public",
|
|
5515
|
-
publicApiKey,
|
|
5516
|
-
url: buildLiveblocksPublicAuthorizeEndpoint(clientOptions, roomId)
|
|
5517
|
-
};
|
|
5518
|
-
}
|
|
5519
|
-
if (typeof authEndpoint === "string") {
|
|
5520
|
-
return {
|
|
5521
|
-
type: "private",
|
|
5522
|
-
url: authEndpoint
|
|
5523
|
-
};
|
|
5524
|
-
} else if (typeof authEndpoint === "function") {
|
|
5525
|
-
return {
|
|
5526
|
-
type: "custom",
|
|
5527
|
-
callback: authEndpoint
|
|
5528
|
-
};
|
|
5529
|
-
} else if (authEndpoint !== void 0) {
|
|
5530
|
-
throw new Error(
|
|
5531
|
-
"authEndpoint must be a string or a function. For more information: https://liveblocks.io/docs/api-reference/liveblocks-client#createClientAuthEndpoint"
|
|
5532
|
-
);
|
|
5533
|
-
}
|
|
5534
|
-
throw new Error(
|
|
5535
|
-
"Invalid Liveblocks client options. For more information: https://liveblocks.io/docs/api-reference/liveblocks-client#createClient"
|
|
5536
|
-
);
|
|
5537
|
-
}
|
|
5538
5808
|
function buildLiveblocksHttpSendEndpoint(options, roomId) {
|
|
5539
5809
|
if (options.httpSendEndpoint) {
|
|
5540
5810
|
return options.httpSendEndpoint.replace("{roomId}", roomId);
|
|
@@ -5543,13 +5813,11 @@ function buildLiveblocksHttpSendEndpoint(options, roomId) {
|
|
|
5543
5813
|
roomId
|
|
5544
5814
|
)}/send-message`;
|
|
5545
5815
|
}
|
|
5546
|
-
function
|
|
5547
|
-
if (options.
|
|
5548
|
-
return options.
|
|
5816
|
+
function getWsEventServerEndpoint(options) {
|
|
5817
|
+
if (typeof options.eventsServerEndpoint === "string") {
|
|
5818
|
+
return options.eventsServerEndpoint;
|
|
5549
5819
|
}
|
|
5550
|
-
return `
|
|
5551
|
-
roomId
|
|
5552
|
-
)}/public/authorize`;
|
|
5820
|
+
return `wss://events.liveblocks.io/v1`;
|
|
5553
5821
|
}
|
|
5554
5822
|
|
|
5555
5823
|
// src/crdts/utils.ts
|
|
@@ -5923,18 +6191,378 @@ function shallow(a, b) {
|
|
|
5923
6191
|
return shallowObj(a, b);
|
|
5924
6192
|
}
|
|
5925
6193
|
|
|
5926
|
-
// src/
|
|
5927
|
-
var
|
|
5928
|
-
|
|
5929
|
-
|
|
5930
|
-
|
|
5931
|
-
|
|
5932
|
-
|
|
5933
|
-
|
|
5934
|
-
|
|
5935
|
-
|
|
5936
|
-
|
|
5937
|
-
|
|
6194
|
+
// src/lib/AsyncCache.ts
|
|
6195
|
+
var noop = () => {
|
|
6196
|
+
};
|
|
6197
|
+
function isShallowEqual(a, b) {
|
|
6198
|
+
if (a.isLoading !== b.isLoading || a.data === void 0 !== (b.data === void 0) || a.error === void 0 !== (b.error === void 0)) {
|
|
6199
|
+
return false;
|
|
6200
|
+
} else {
|
|
6201
|
+
return shallow(a.data, b.data) && shallow(a.error, b.error);
|
|
6202
|
+
}
|
|
6203
|
+
}
|
|
6204
|
+
function createCacheItem(key, defaultAsyncFunction, options) {
|
|
6205
|
+
let asyncFunction = defaultAsyncFunction;
|
|
6206
|
+
const context = {
|
|
6207
|
+
isInvalid: true
|
|
6208
|
+
};
|
|
6209
|
+
let state = { isLoading: false };
|
|
6210
|
+
let previousState = { isLoading: false };
|
|
6211
|
+
const eventSource2 = makeEventSource();
|
|
6212
|
+
function notify() {
|
|
6213
|
+
const isEqual = options?.isStateEqual ?? isShallowEqual;
|
|
6214
|
+
if (!isEqual(previousState, state)) {
|
|
6215
|
+
previousState = state;
|
|
6216
|
+
eventSource2.notify(state);
|
|
6217
|
+
}
|
|
6218
|
+
}
|
|
6219
|
+
async function resolve() {
|
|
6220
|
+
if (!context.promise) {
|
|
6221
|
+
return;
|
|
6222
|
+
}
|
|
6223
|
+
try {
|
|
6224
|
+
const data = await context.promise;
|
|
6225
|
+
context.isInvalid = false;
|
|
6226
|
+
state = {
|
|
6227
|
+
isLoading: false,
|
|
6228
|
+
data
|
|
6229
|
+
};
|
|
6230
|
+
} catch (error3) {
|
|
6231
|
+
state = {
|
|
6232
|
+
isLoading: false,
|
|
6233
|
+
data: state.data,
|
|
6234
|
+
error: error3
|
|
6235
|
+
};
|
|
6236
|
+
}
|
|
6237
|
+
context.promise = void 0;
|
|
6238
|
+
notify();
|
|
6239
|
+
}
|
|
6240
|
+
async function revalidate() {
|
|
6241
|
+
context.isInvalid = true;
|
|
6242
|
+
return get();
|
|
6243
|
+
}
|
|
6244
|
+
async function get() {
|
|
6245
|
+
if (context.isInvalid) {
|
|
6246
|
+
if (!context.promise) {
|
|
6247
|
+
context.isInvalid = true;
|
|
6248
|
+
context.promise = asyncFunction(key);
|
|
6249
|
+
state = { isLoading: true, data: state.data };
|
|
6250
|
+
notify();
|
|
6251
|
+
}
|
|
6252
|
+
await resolve();
|
|
6253
|
+
}
|
|
6254
|
+
return getState();
|
|
6255
|
+
}
|
|
6256
|
+
function getState() {
|
|
6257
|
+
return state;
|
|
6258
|
+
}
|
|
6259
|
+
function setAsyncFunction(overrideAsyncFunction) {
|
|
6260
|
+
asyncFunction = overrideAsyncFunction;
|
|
6261
|
+
}
|
|
6262
|
+
return {
|
|
6263
|
+
...eventSource2.observable,
|
|
6264
|
+
setAsyncFunction,
|
|
6265
|
+
get,
|
|
6266
|
+
getState,
|
|
6267
|
+
revalidate
|
|
6268
|
+
};
|
|
6269
|
+
}
|
|
6270
|
+
function createAsyncCache(asyncFunction, options) {
|
|
6271
|
+
const cache = /* @__PURE__ */ new Map();
|
|
6272
|
+
function create(key, overrideAsyncFunction) {
|
|
6273
|
+
let cacheItem = cache.get(key);
|
|
6274
|
+
if (cacheItem) {
|
|
6275
|
+
if (overrideAsyncFunction) {
|
|
6276
|
+
cacheItem.setAsyncFunction(overrideAsyncFunction);
|
|
6277
|
+
}
|
|
6278
|
+
return cacheItem;
|
|
6279
|
+
}
|
|
6280
|
+
cacheItem = createCacheItem(
|
|
6281
|
+
key,
|
|
6282
|
+
overrideAsyncFunction ?? asyncFunction,
|
|
6283
|
+
options
|
|
6284
|
+
);
|
|
6285
|
+
cache.set(key, cacheItem);
|
|
6286
|
+
return cacheItem;
|
|
6287
|
+
}
|
|
6288
|
+
function get(key) {
|
|
6289
|
+
return create(key).get();
|
|
6290
|
+
}
|
|
6291
|
+
function getState(key) {
|
|
6292
|
+
return cache.get(key)?.getState();
|
|
6293
|
+
}
|
|
6294
|
+
function revalidate(key) {
|
|
6295
|
+
return create(key).revalidate();
|
|
6296
|
+
}
|
|
6297
|
+
function subscribe(key, callback) {
|
|
6298
|
+
return create(key).subscribe(callback) ?? noop;
|
|
6299
|
+
}
|
|
6300
|
+
function subscribeOnce(key, callback) {
|
|
6301
|
+
return create(key).subscribeOnce(callback) ?? noop;
|
|
6302
|
+
}
|
|
6303
|
+
function has(key) {
|
|
6304
|
+
return cache.has(key);
|
|
6305
|
+
}
|
|
6306
|
+
function clear() {
|
|
6307
|
+
cache.clear();
|
|
6308
|
+
}
|
|
6309
|
+
return {
|
|
6310
|
+
create,
|
|
6311
|
+
get,
|
|
6312
|
+
getState,
|
|
6313
|
+
revalidate,
|
|
6314
|
+
subscribe,
|
|
6315
|
+
subscribeOnce,
|
|
6316
|
+
has,
|
|
6317
|
+
clear
|
|
6318
|
+
};
|
|
6319
|
+
}
|
|
6320
|
+
|
|
6321
|
+
// src/lib/Poller.ts
|
|
6322
|
+
function makePoller(callback) {
|
|
6323
|
+
let context = {
|
|
6324
|
+
state: "stopped",
|
|
6325
|
+
timeoutHandle: null,
|
|
6326
|
+
interval: null,
|
|
6327
|
+
lastScheduledAt: null,
|
|
6328
|
+
remainingInterval: null
|
|
6329
|
+
};
|
|
6330
|
+
function poll() {
|
|
6331
|
+
if (context.state === "running") {
|
|
6332
|
+
schedule(context.interval);
|
|
6333
|
+
}
|
|
6334
|
+
callback();
|
|
6335
|
+
}
|
|
6336
|
+
function schedule(interval) {
|
|
6337
|
+
context = {
|
|
6338
|
+
state: "running",
|
|
6339
|
+
interval: context.state !== "stopped" ? context.interval : interval,
|
|
6340
|
+
lastScheduledAt: performance.now(),
|
|
6341
|
+
timeoutHandle: setTimeout(poll, interval),
|
|
6342
|
+
remainingInterval: null
|
|
6343
|
+
};
|
|
6344
|
+
}
|
|
6345
|
+
function scheduleRemaining(remaining) {
|
|
6346
|
+
if (context.state !== "paused") {
|
|
6347
|
+
return;
|
|
6348
|
+
}
|
|
6349
|
+
context = {
|
|
6350
|
+
state: "running",
|
|
6351
|
+
interval: context.interval,
|
|
6352
|
+
lastScheduledAt: context.lastScheduledAt,
|
|
6353
|
+
timeoutHandle: setTimeout(poll, remaining),
|
|
6354
|
+
remainingInterval: null
|
|
6355
|
+
};
|
|
6356
|
+
}
|
|
6357
|
+
function start(interval) {
|
|
6358
|
+
if (context.state === "running") {
|
|
6359
|
+
return;
|
|
6360
|
+
}
|
|
6361
|
+
schedule(interval);
|
|
6362
|
+
}
|
|
6363
|
+
function restart(interval) {
|
|
6364
|
+
stop();
|
|
6365
|
+
start(interval);
|
|
6366
|
+
}
|
|
6367
|
+
function pause() {
|
|
6368
|
+
if (context.state !== "running") {
|
|
6369
|
+
return;
|
|
6370
|
+
}
|
|
6371
|
+
clearTimeout(context.timeoutHandle);
|
|
6372
|
+
context = {
|
|
6373
|
+
state: "paused",
|
|
6374
|
+
interval: context.interval,
|
|
6375
|
+
lastScheduledAt: context.lastScheduledAt,
|
|
6376
|
+
timeoutHandle: null,
|
|
6377
|
+
remainingInterval: context.interval - (performance.now() - context.lastScheduledAt)
|
|
6378
|
+
};
|
|
6379
|
+
}
|
|
6380
|
+
function resume() {
|
|
6381
|
+
if (context.state !== "paused") {
|
|
6382
|
+
return;
|
|
6383
|
+
}
|
|
6384
|
+
scheduleRemaining(context.remainingInterval);
|
|
6385
|
+
}
|
|
6386
|
+
function stop() {
|
|
6387
|
+
if (context.state === "stopped") {
|
|
6388
|
+
return;
|
|
6389
|
+
}
|
|
6390
|
+
if (context.timeoutHandle) {
|
|
6391
|
+
clearTimeout(context.timeoutHandle);
|
|
6392
|
+
}
|
|
6393
|
+
context = {
|
|
6394
|
+
state: "stopped",
|
|
6395
|
+
interval: null,
|
|
6396
|
+
lastScheduledAt: null,
|
|
6397
|
+
timeoutHandle: null,
|
|
6398
|
+
remainingInterval: null
|
|
6399
|
+
};
|
|
6400
|
+
}
|
|
6401
|
+
return {
|
|
6402
|
+
start,
|
|
6403
|
+
restart,
|
|
6404
|
+
pause,
|
|
6405
|
+
resume,
|
|
6406
|
+
stop
|
|
6407
|
+
};
|
|
6408
|
+
}
|
|
6409
|
+
|
|
6410
|
+
// src/comments/index.ts
|
|
6411
|
+
function createCommentsApi(client, { serverEndpoint }) {
|
|
6412
|
+
async function fetchJson(roomId, endpoint, options) {
|
|
6413
|
+
const response = await fetchApi(roomId, endpoint, options);
|
|
6414
|
+
if (!response.ok) {
|
|
6415
|
+
if (response.status >= 400 && response.status < 600) {
|
|
6416
|
+
let errorMessage = "";
|
|
6417
|
+
try {
|
|
6418
|
+
const errorBody = await response.json();
|
|
6419
|
+
errorMessage = errorBody.message;
|
|
6420
|
+
} catch (error3) {
|
|
6421
|
+
errorMessage = response.statusText;
|
|
6422
|
+
}
|
|
6423
|
+
throw new Error(
|
|
6424
|
+
`Request failed with status ${response.status}: ${errorMessage}`
|
|
6425
|
+
);
|
|
6426
|
+
}
|
|
6427
|
+
}
|
|
6428
|
+
let body;
|
|
6429
|
+
try {
|
|
6430
|
+
body = await response.json();
|
|
6431
|
+
} catch {
|
|
6432
|
+
body = {};
|
|
6433
|
+
}
|
|
6434
|
+
return body;
|
|
6435
|
+
}
|
|
6436
|
+
async function fetchApi(roomId, endpoint, options) {
|
|
6437
|
+
const authValue = await client.__internal.getAuthValue(
|
|
6438
|
+
"comments:read",
|
|
6439
|
+
// TODO: Use the right scope
|
|
6440
|
+
roomId
|
|
6441
|
+
);
|
|
6442
|
+
if (authValue.type !== "secret") {
|
|
6443
|
+
throw new Error("Only secret key are supported for client.");
|
|
6444
|
+
}
|
|
6445
|
+
const url = `${serverEndpoint}/rooms/${roomId}${endpoint}`;
|
|
6446
|
+
return await fetch(url, {
|
|
6447
|
+
...options,
|
|
6448
|
+
headers: {
|
|
6449
|
+
...options?.headers,
|
|
6450
|
+
Authorization: `Bearer ${authValue.token.raw}`
|
|
6451
|
+
}
|
|
6452
|
+
});
|
|
6453
|
+
}
|
|
6454
|
+
async function getThreads({
|
|
6455
|
+
roomId
|
|
6456
|
+
}) {
|
|
6457
|
+
const response = await fetchApi(roomId, "/threads");
|
|
6458
|
+
if (response.ok) {
|
|
6459
|
+
const json = await response.json();
|
|
6460
|
+
return json.data;
|
|
6461
|
+
} else if (response.status === 404) {
|
|
6462
|
+
return [];
|
|
6463
|
+
} else {
|
|
6464
|
+
throw new Error("FAIL");
|
|
6465
|
+
}
|
|
6466
|
+
}
|
|
6467
|
+
function createThread({
|
|
6468
|
+
roomId,
|
|
6469
|
+
metadata,
|
|
6470
|
+
body,
|
|
6471
|
+
commentId,
|
|
6472
|
+
threadId
|
|
6473
|
+
}) {
|
|
6474
|
+
return fetchJson(roomId, "/threads", {
|
|
6475
|
+
method: "POST",
|
|
6476
|
+
headers: {
|
|
6477
|
+
"Content-Type": "application/json"
|
|
6478
|
+
},
|
|
6479
|
+
body: JSON.stringify({
|
|
6480
|
+
id: threadId,
|
|
6481
|
+
comment: {
|
|
6482
|
+
id: commentId,
|
|
6483
|
+
body
|
|
6484
|
+
},
|
|
6485
|
+
metadata
|
|
6486
|
+
})
|
|
6487
|
+
});
|
|
6488
|
+
}
|
|
6489
|
+
function editThreadMetadata({
|
|
6490
|
+
roomId,
|
|
6491
|
+
metadata,
|
|
6492
|
+
threadId
|
|
6493
|
+
}) {
|
|
6494
|
+
return fetchJson(
|
|
6495
|
+
roomId,
|
|
6496
|
+
`/threads/${threadId}/metadata`,
|
|
6497
|
+
{
|
|
6498
|
+
method: "POST",
|
|
6499
|
+
headers: {
|
|
6500
|
+
"Content-Type": "application/json"
|
|
6501
|
+
},
|
|
6502
|
+
body: JSON.stringify(metadata)
|
|
6503
|
+
}
|
|
6504
|
+
);
|
|
6505
|
+
}
|
|
6506
|
+
function createComment({
|
|
6507
|
+
roomId,
|
|
6508
|
+
threadId,
|
|
6509
|
+
commentId,
|
|
6510
|
+
body
|
|
6511
|
+
}) {
|
|
6512
|
+
return fetchJson(roomId, `/threads/${threadId}/comments`, {
|
|
6513
|
+
method: "POST",
|
|
6514
|
+
headers: {
|
|
6515
|
+
"Content-Type": "application/json"
|
|
6516
|
+
},
|
|
6517
|
+
body: JSON.stringify({
|
|
6518
|
+
id: commentId,
|
|
6519
|
+
body
|
|
6520
|
+
})
|
|
6521
|
+
});
|
|
6522
|
+
}
|
|
6523
|
+
function editComment({
|
|
6524
|
+
roomId,
|
|
6525
|
+
threadId,
|
|
6526
|
+
commentId,
|
|
6527
|
+
body
|
|
6528
|
+
}) {
|
|
6529
|
+
return fetchJson(
|
|
6530
|
+
roomId,
|
|
6531
|
+
`/threads/${threadId}/comments/${commentId}`,
|
|
6532
|
+
{
|
|
6533
|
+
method: "POST",
|
|
6534
|
+
headers: {
|
|
6535
|
+
"Content-Type": "application/json"
|
|
6536
|
+
},
|
|
6537
|
+
body: JSON.stringify({
|
|
6538
|
+
body
|
|
6539
|
+
})
|
|
6540
|
+
}
|
|
6541
|
+
);
|
|
6542
|
+
}
|
|
6543
|
+
async function deleteComment({
|
|
6544
|
+
roomId,
|
|
6545
|
+
threadId,
|
|
6546
|
+
commentId
|
|
6547
|
+
}) {
|
|
6548
|
+
await fetchJson(roomId, `/threads/${threadId}/comments/${commentId}`, {
|
|
6549
|
+
method: "DELETE"
|
|
6550
|
+
});
|
|
6551
|
+
}
|
|
6552
|
+
return {
|
|
6553
|
+
getThreads,
|
|
6554
|
+
createThread,
|
|
6555
|
+
editThreadMetadata,
|
|
6556
|
+
createComment,
|
|
6557
|
+
editComment,
|
|
6558
|
+
deleteComment
|
|
6559
|
+
};
|
|
6560
|
+
}
|
|
6561
|
+
|
|
6562
|
+
// src/comments/utils.ts
|
|
6563
|
+
function isCommentBodyMention(element) {
|
|
6564
|
+
return "type" in element && element.type === "mention";
|
|
6565
|
+
}
|
|
5938
6566
|
|
|
5939
6567
|
// src/index.ts
|
|
5940
6568
|
detectDupes(PKG_NAME, PKG_VERSION, PKG_FORMAT);
|
|
@@ -5952,13 +6580,17 @@ export {
|
|
|
5952
6580
|
assert,
|
|
5953
6581
|
assertNever,
|
|
5954
6582
|
b64decode,
|
|
6583
|
+
fancy_console_exports as console,
|
|
6584
|
+
createAsyncCache,
|
|
5955
6585
|
createClient,
|
|
6586
|
+
createCommentsApi,
|
|
5956
6587
|
deprecate,
|
|
5957
6588
|
deprecateIf,
|
|
5958
6589
|
detectDupes,
|
|
5959
6590
|
errorIf,
|
|
5960
6591
|
freeze,
|
|
5961
6592
|
isChildCrdt,
|
|
6593
|
+
isCommentBodyMention,
|
|
5962
6594
|
isJsonArray,
|
|
5963
6595
|
isJsonObject,
|
|
5964
6596
|
isJsonScalar,
|
|
@@ -5966,6 +6598,8 @@ export {
|
|
|
5966
6598
|
isRootCrdt,
|
|
5967
6599
|
legacy_patchImmutableObject,
|
|
5968
6600
|
lsonToJson,
|
|
6601
|
+
makeEventSource,
|
|
6602
|
+
makePoller,
|
|
5969
6603
|
makePosition,
|
|
5970
6604
|
nn,
|
|
5971
6605
|
patchLiveObjectKey,
|