@fieldnotes/sync-server 0.12.0 → 0.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +265 -29
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +58 -13
- package/dist/index.d.ts +58 -13
- package/dist/index.js +271 -31
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -2,14 +2,19 @@
|
|
|
2
2
|
import {
|
|
3
3
|
parseEnvelope,
|
|
4
4
|
isValidElement,
|
|
5
|
-
isNewerLayerRecord
|
|
5
|
+
isNewerLayerRecord,
|
|
6
|
+
FogLedger as FogLedger2
|
|
6
7
|
} from "@fieldnotes/sync";
|
|
7
8
|
|
|
8
9
|
// src/memory-hub-backend.ts
|
|
9
|
-
import {
|
|
10
|
+
import {
|
|
11
|
+
applyOpToMap,
|
|
12
|
+
FogLedger
|
|
13
|
+
} from "@fieldnotes/sync";
|
|
10
14
|
var MemoryHubBackend = class {
|
|
11
15
|
rooms = /* @__PURE__ */ new Map();
|
|
12
16
|
roomLayers = /* @__PURE__ */ new Map();
|
|
17
|
+
roomFog = /* @__PURE__ */ new Map();
|
|
13
18
|
room(id) {
|
|
14
19
|
let r = this.rooms.get(id);
|
|
15
20
|
if (!r) {
|
|
@@ -48,6 +53,26 @@ var MemoryHubBackend = class {
|
|
|
48
53
|
async applyLayerRecord(room, record) {
|
|
49
54
|
this.layers(room).set(record.id, record);
|
|
50
55
|
}
|
|
56
|
+
fog(room) {
|
|
57
|
+
let ledger = this.roomFog.get(room);
|
|
58
|
+
if (!ledger) {
|
|
59
|
+
ledger = new FogLedger();
|
|
60
|
+
this.roomFog.set(room, ledger);
|
|
61
|
+
}
|
|
62
|
+
return ledger;
|
|
63
|
+
}
|
|
64
|
+
async fogSnapshot(room) {
|
|
65
|
+
return this.fog(room).snapshot();
|
|
66
|
+
}
|
|
67
|
+
async applyFogMeta(room, record) {
|
|
68
|
+
return this.fog(room).applyMeta(record);
|
|
69
|
+
}
|
|
70
|
+
async applyFogTile(room, record) {
|
|
71
|
+
return this.fog(room).applyTile(record);
|
|
72
|
+
}
|
|
73
|
+
async applyFogPatch(room, records) {
|
|
74
|
+
return this.fog(room).applyPatch(records);
|
|
75
|
+
}
|
|
51
76
|
};
|
|
52
77
|
|
|
53
78
|
// src/hub-fanout.ts
|
|
@@ -75,6 +100,7 @@ var DEFAULT_MAX_PENDING_AUTH_BYTES = 2 * 1024 * 1024;
|
|
|
75
100
|
var DEFAULT_MESSAGES_PER_SECOND = 120;
|
|
76
101
|
var DEFAULT_MESSAGE_BURST = 240;
|
|
77
102
|
var DEFAULT_PRESENCE_THROTTLE_MS = 50;
|
|
103
|
+
var DEFAULT_MAX_PRESENCE_LANES = 16;
|
|
78
104
|
function hasJsonDepthAtMost(message, maxDepth) {
|
|
79
105
|
let depth = 0;
|
|
80
106
|
let inString = false;
|
|
@@ -130,6 +156,16 @@ function isFanoutOp(op) {
|
|
|
130
156
|
if (o.kind === "remove") return typeof o.id === "string";
|
|
131
157
|
return o.kind === "clear";
|
|
132
158
|
}
|
|
159
|
+
var FALLBACK_PRESENCE_LANE = "";
|
|
160
|
+
var MAX_PRESENCE_LANE_LENGTH = 64;
|
|
161
|
+
function presenceLaneOf(data) {
|
|
162
|
+
if (typeof data !== "object" || data === null) return FALLBACK_PRESENCE_LANE;
|
|
163
|
+
const kind = data.kind;
|
|
164
|
+
if (typeof kind !== "string" || kind.length === 0 || kind.length > MAX_PRESENCE_LANE_LENGTH) {
|
|
165
|
+
return FALLBACK_PRESENCE_LANE;
|
|
166
|
+
}
|
|
167
|
+
return kind;
|
|
168
|
+
}
|
|
133
169
|
function isLayerOp(op) {
|
|
134
170
|
if (typeof op !== "object" || op === null) return false;
|
|
135
171
|
const k = op.kind;
|
|
@@ -151,6 +187,11 @@ function isPresenceOp(op) {
|
|
|
151
187
|
const k = op.kind;
|
|
152
188
|
return k === "presence" || k === "presence-leave";
|
|
153
189
|
}
|
|
190
|
+
function isFogOp(op) {
|
|
191
|
+
if (typeof op !== "object" || op === null) return false;
|
|
192
|
+
const k = op.kind;
|
|
193
|
+
return k === "fog-meta" || k === "fog-patch";
|
|
194
|
+
}
|
|
154
195
|
var SyncHub = class {
|
|
155
196
|
backend;
|
|
156
197
|
conns = /* @__PURE__ */ new Map();
|
|
@@ -164,22 +205,48 @@ var SyncHub = class {
|
|
|
164
205
|
fanoutUnsub;
|
|
165
206
|
authorize;
|
|
166
207
|
authorizeLayer;
|
|
208
|
+
authorizeFog;
|
|
167
209
|
canRead;
|
|
168
|
-
/** Fallback layer-record store for backends without layer persistence. */
|
|
169
210
|
memoryLayers = /* @__PURE__ */ new Map();
|
|
211
|
+
memoryFog = /* @__PURE__ */ new Map();
|
|
170
212
|
maxJsonDepth;
|
|
171
213
|
presenceThrottleMs;
|
|
172
|
-
|
|
173
|
-
|
|
214
|
+
maxPresenceLanes;
|
|
215
|
+
/**
|
|
216
|
+
* Presence throttle state keyed by connection, then by lane. A lane is the
|
|
217
|
+
* payload's `kind` (a non-empty string of at most 64 chars) or the reserved
|
|
218
|
+
* fallback lane `''`, so a rapid stream of one kind (awareness cursors) can
|
|
219
|
+
* never replace a pending frame of another kind (a ping, a path `cleared`).
|
|
220
|
+
* Within a lane the newest payload wins. The lane count per connection is
|
|
221
|
+
* capped by `maxPresenceLanes`, counting the fallback lane, so a client
|
|
222
|
+
* cannot mint timers by varying `kind`.
|
|
223
|
+
*/
|
|
224
|
+
presenceLanes = /* @__PURE__ */ new Map();
|
|
174
225
|
constructor(options = {}) {
|
|
175
226
|
this.backend = options.backend ?? new MemoryHubBackend();
|
|
227
|
+
const fogMethods = [
|
|
228
|
+
this.backend.fogSnapshot,
|
|
229
|
+
this.backend.applyFogMeta,
|
|
230
|
+
this.backend.applyFogPatch
|
|
231
|
+
];
|
|
232
|
+
if ((fogMethods.some(Boolean) || Boolean(this.backend.applyFogTile)) && !fogMethods.every(Boolean)) {
|
|
233
|
+
throw new Error(
|
|
234
|
+
"HubBackend fog support is an all-or-none capability: fogSnapshot, applyFogMeta, and applyFogPatch are required"
|
|
235
|
+
);
|
|
236
|
+
}
|
|
176
237
|
this.instanceId = options.instanceId ?? generateInstanceId();
|
|
177
238
|
this.fanout = options.fanout ?? new InMemoryHubFanout();
|
|
178
239
|
this.authorize = options.authorize;
|
|
179
240
|
this.authorizeLayer = options.authorizeLayer;
|
|
241
|
+
this.authorizeFog = options.authorizeFog;
|
|
180
242
|
this.canRead = options.canRead;
|
|
181
243
|
this.maxJsonDepth = options.maxJsonDepth ?? DEFAULT_MAX_JSON_DEPTH;
|
|
182
244
|
this.presenceThrottleMs = options.presenceThrottleMs ?? DEFAULT_PRESENCE_THROTTLE_MS;
|
|
245
|
+
const maxPresenceLanes = options.maxPresenceLanes ?? DEFAULT_MAX_PRESENCE_LANES;
|
|
246
|
+
if (!Number.isFinite(maxPresenceLanes) || maxPresenceLanes < 1) {
|
|
247
|
+
throw new RangeError("maxPresenceLanes must be a finite number of at least 1");
|
|
248
|
+
}
|
|
249
|
+
this.maxPresenceLanes = Math.floor(maxPresenceLanes);
|
|
183
250
|
this.fanoutUnsub = this.fanout.subscribe((payload) => this.onFanout(payload));
|
|
184
251
|
}
|
|
185
252
|
addConnection(conn) {
|
|
@@ -197,10 +264,7 @@ var SyncHub = class {
|
|
|
197
264
|
this.conns.delete(connId);
|
|
198
265
|
const room = conn.room;
|
|
199
266
|
const hadPresence = this.presenceConnections.delete(connId);
|
|
200
|
-
this.
|
|
201
|
-
const pendingPresence = this.pendingPresence.get(connId);
|
|
202
|
-
if (pendingPresence) clearTimeout(pendingPresence.timer);
|
|
203
|
-
this.pendingPresence.delete(connId);
|
|
267
|
+
this.clearPresenceLanes(connId);
|
|
204
268
|
const members = this.rooms.get(room);
|
|
205
269
|
if (members) {
|
|
206
270
|
members.delete(connId);
|
|
@@ -251,16 +315,19 @@ var SyncHub = class {
|
|
|
251
315
|
const all = await this.backend.snapshot(conn.room);
|
|
252
316
|
const elements = this.canRead ? all.filter((el) => this.mayRead(conn, el.audience)) : all;
|
|
253
317
|
const layers = await this.getLayerRecords(conn.room);
|
|
254
|
-
conn.
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
);
|
|
318
|
+
const fog = await this.getFogSnapshot(conn.room);
|
|
319
|
+
const snapshotOp = {
|
|
320
|
+
kind: "snapshot",
|
|
321
|
+
to: env.from,
|
|
322
|
+
elements
|
|
323
|
+
};
|
|
324
|
+
if (layers.length > 0) snapshotOp["layers"] = layers;
|
|
325
|
+
if (fog) snapshotOp["fog"] = fog;
|
|
326
|
+
conn.send(JSON.stringify({ from: HUB_FROM, op: snapshotOp }));
|
|
262
327
|
} else if (op.kind === "layer-upsert" || op.kind === "layer-remove") {
|
|
263
328
|
await this.processLayerOp(conn, op);
|
|
329
|
+
} else if (op.kind === "fog-meta" || op.kind === "fog-patch") {
|
|
330
|
+
await this.processFogOp(conn, op);
|
|
264
331
|
} else if (op.kind === "upsert" || op.kind === "remove" || op.kind === "clear") {
|
|
265
332
|
const id = op.kind === "upsert" ? op.element.id : op.kind === "remove" ? op.id : void 0;
|
|
266
333
|
const needCurrent = (this.authorize || this.canRead) && id !== void 0;
|
|
@@ -366,6 +433,121 @@ var SyncHub = class {
|
|
|
366
433
|
}
|
|
367
434
|
map.set(record.id, record);
|
|
368
435
|
}
|
|
436
|
+
// ── Fog processing ──
|
|
437
|
+
fogBackend() {
|
|
438
|
+
const { fogSnapshot, applyFogMeta, applyFogPatch } = this.backend;
|
|
439
|
+
if (!fogSnapshot || !applyFogMeta || !applyFogPatch) return null;
|
|
440
|
+
return {
|
|
441
|
+
fogSnapshot: fogSnapshot.bind(this.backend),
|
|
442
|
+
applyFogMeta: applyFogMeta.bind(this.backend),
|
|
443
|
+
applyFogPatch: applyFogPatch.bind(this.backend)
|
|
444
|
+
};
|
|
445
|
+
}
|
|
446
|
+
getFogLedger(room) {
|
|
447
|
+
let ledger = this.memoryFog.get(room);
|
|
448
|
+
if (!ledger) {
|
|
449
|
+
ledger = new FogLedger2();
|
|
450
|
+
this.memoryFog.set(room, ledger);
|
|
451
|
+
}
|
|
452
|
+
return ledger;
|
|
453
|
+
}
|
|
454
|
+
async getFogSnapshot(room) {
|
|
455
|
+
const backend = this.fogBackend();
|
|
456
|
+
if (backend) return backend.fogSnapshot(room);
|
|
457
|
+
return this.getFogLedger(room).snapshot();
|
|
458
|
+
}
|
|
459
|
+
async applyFogMeta(room, record) {
|
|
460
|
+
const backend = this.fogBackend();
|
|
461
|
+
if (backend) return backend.applyFogMeta(room, record);
|
|
462
|
+
return this.getFogLedger(room).applyMeta(record);
|
|
463
|
+
}
|
|
464
|
+
async applyFogPatch(room, records) {
|
|
465
|
+
const backend = this.fogBackend();
|
|
466
|
+
if (backend) return backend.applyFogPatch(room, records);
|
|
467
|
+
return this.getFogLedger(room).applyPatch(records);
|
|
468
|
+
}
|
|
469
|
+
async processFogOp(conn, op) {
|
|
470
|
+
const current = await this.getFogSnapshot(conn.room);
|
|
471
|
+
if (this.authorizeFog) {
|
|
472
|
+
const allowed = await this.authorizeFog({
|
|
473
|
+
userId: conn.userId,
|
|
474
|
+
role: conn.role,
|
|
475
|
+
room: conn.room,
|
|
476
|
+
op,
|
|
477
|
+
current
|
|
478
|
+
});
|
|
479
|
+
if (!allowed) {
|
|
480
|
+
if (op.kind === "fog-meta") {
|
|
481
|
+
const correction = current?.meta ?? { version: 1, editor: HUB_FROM };
|
|
482
|
+
conn.send(
|
|
483
|
+
JSON.stringify({ from: HUB_FROM, op: { kind: "fog-meta", record: correction } })
|
|
484
|
+
);
|
|
485
|
+
} else if (current?.meta.definition) {
|
|
486
|
+
const corrections = op.tiles.map((t) => {
|
|
487
|
+
const existing = current.tiles.find((ct) => ct.x === t.x && ct.y === t.y);
|
|
488
|
+
return existing ?? {
|
|
489
|
+
generation: current.meta.definition?.generation ?? op.generation,
|
|
490
|
+
x: t.x,
|
|
491
|
+
y: t.y,
|
|
492
|
+
version: 1,
|
|
493
|
+
editor: HUB_FROM
|
|
494
|
+
};
|
|
495
|
+
});
|
|
496
|
+
conn.send(
|
|
497
|
+
JSON.stringify({
|
|
498
|
+
from: HUB_FROM,
|
|
499
|
+
op: {
|
|
500
|
+
kind: "fog-patch",
|
|
501
|
+
generation: current.meta.definition.generation,
|
|
502
|
+
tiles: corrections
|
|
503
|
+
}
|
|
504
|
+
})
|
|
505
|
+
);
|
|
506
|
+
} else {
|
|
507
|
+
conn.send(
|
|
508
|
+
JSON.stringify({
|
|
509
|
+
from: HUB_FROM,
|
|
510
|
+
op: {
|
|
511
|
+
kind: "fog-meta",
|
|
512
|
+
record: current?.meta ?? { version: 1, editor: HUB_FROM }
|
|
513
|
+
}
|
|
514
|
+
})
|
|
515
|
+
);
|
|
516
|
+
}
|
|
517
|
+
return;
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
let outbound;
|
|
521
|
+
if (op.kind === "fog-meta") {
|
|
522
|
+
const result = await this.applyFogMeta(conn.room, op.record);
|
|
523
|
+
if (!result.accepted) {
|
|
524
|
+
if (result.correction) {
|
|
525
|
+
conn.send(
|
|
526
|
+
JSON.stringify({ from: HUB_FROM, op: { kind: "fog-meta", record: result.correction } })
|
|
527
|
+
);
|
|
528
|
+
}
|
|
529
|
+
return;
|
|
530
|
+
}
|
|
531
|
+
outbound = op;
|
|
532
|
+
} else {
|
|
533
|
+
const { accepted, corrections } = await this.applyFogPatch(conn.room, op.tiles);
|
|
534
|
+
if (corrections.length > 0) {
|
|
535
|
+
const correctionGeneration = corrections[0]?.generation ?? op.generation;
|
|
536
|
+
conn.send(
|
|
537
|
+
JSON.stringify({
|
|
538
|
+
from: HUB_FROM,
|
|
539
|
+
op: { kind: "fog-patch", generation: correctionGeneration, tiles: corrections }
|
|
540
|
+
})
|
|
541
|
+
);
|
|
542
|
+
}
|
|
543
|
+
if (accepted.length === 0) return;
|
|
544
|
+
outbound = { kind: "fog-patch", generation: op.generation, tiles: accepted };
|
|
545
|
+
}
|
|
546
|
+
await this.fanout.publish(
|
|
547
|
+
JSON.stringify({ o: this.instanceId, room: conn.room, from: conn.id, op: outbound })
|
|
548
|
+
);
|
|
549
|
+
this.relayToRoom(conn.room, conn.id, JSON.stringify({ from: conn.id, op: outbound }));
|
|
550
|
+
}
|
|
369
551
|
mayRead(conn, audience) {
|
|
370
552
|
if (!this.canRead) return true;
|
|
371
553
|
return this.canRead({ userId: conn.userId, role: conn.role, room: conn.room, audience });
|
|
@@ -406,34 +588,63 @@ var SyncHub = class {
|
|
|
406
588
|
})
|
|
407
589
|
);
|
|
408
590
|
}
|
|
591
|
+
clearPresenceLanes(connId) {
|
|
592
|
+
const lanes = this.presenceLanes.get(connId);
|
|
593
|
+
if (!lanes) return;
|
|
594
|
+
for (const lane of lanes.values()) {
|
|
595
|
+
if (lane.pending) clearTimeout(lane.pending.timer);
|
|
596
|
+
}
|
|
597
|
+
this.presenceLanes.delete(connId);
|
|
598
|
+
}
|
|
409
599
|
schedulePresence(conn, data) {
|
|
410
600
|
if (this.presenceThrottleMs <= 0) {
|
|
411
601
|
this.broadcastClientPresence(conn, data);
|
|
412
602
|
return;
|
|
413
603
|
}
|
|
604
|
+
const lane = this.presenceLaneFor(conn.id, presenceLaneOf(data));
|
|
414
605
|
const now = Date.now();
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
606
|
+
if (lane.lastSentAt === void 0 || now - lane.lastSentAt >= this.presenceThrottleMs) {
|
|
607
|
+
if (lane.pending) {
|
|
608
|
+
clearTimeout(lane.pending.timer);
|
|
609
|
+
lane.pending = null;
|
|
610
|
+
}
|
|
611
|
+
lane.lastSentAt = now;
|
|
418
612
|
this.broadcastClientPresence(conn, data);
|
|
419
613
|
return;
|
|
420
614
|
}
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
existing.data = data;
|
|
615
|
+
if (lane.pending) {
|
|
616
|
+
lane.pending.data = data;
|
|
424
617
|
return;
|
|
425
618
|
}
|
|
426
619
|
const timer = setTimeout(
|
|
427
620
|
() => {
|
|
428
|
-
const pending =
|
|
429
|
-
|
|
621
|
+
const pending = lane.pending;
|
|
622
|
+
lane.pending = null;
|
|
430
623
|
if (!pending || !this.conns.has(conn.id)) return;
|
|
431
|
-
|
|
624
|
+
lane.lastSentAt = Date.now();
|
|
432
625
|
this.broadcastClientPresence(conn, pending.data);
|
|
433
626
|
},
|
|
434
|
-
this.presenceThrottleMs - (now - lastSentAt)
|
|
627
|
+
this.presenceThrottleMs - (now - lane.lastSentAt)
|
|
435
628
|
);
|
|
436
|
-
|
|
629
|
+
lane.pending = { data, timer };
|
|
630
|
+
}
|
|
631
|
+
presenceLaneFor(connId, requested) {
|
|
632
|
+
let lanes = this.presenceLanes.get(connId);
|
|
633
|
+
if (!lanes) {
|
|
634
|
+
lanes = /* @__PURE__ */ new Map();
|
|
635
|
+
this.presenceLanes.set(connId, lanes);
|
|
636
|
+
}
|
|
637
|
+
let key = requested;
|
|
638
|
+
if (key !== FALLBACK_PRESENCE_LANE && !lanes.has(key)) {
|
|
639
|
+
const named = lanes.size - (lanes.has(FALLBACK_PRESENCE_LANE) ? 1 : 0);
|
|
640
|
+
if (named >= this.maxPresenceLanes - 1) key = FALLBACK_PRESENCE_LANE;
|
|
641
|
+
}
|
|
642
|
+
let lane = lanes.get(key);
|
|
643
|
+
if (!lane) {
|
|
644
|
+
lane = { lastSentAt: void 0, pending: null };
|
|
645
|
+
lanes.set(key, lane);
|
|
646
|
+
}
|
|
647
|
+
return lane;
|
|
437
648
|
}
|
|
438
649
|
broadcastLeave(room, from) {
|
|
439
650
|
const message = JSON.stringify({ from, op: { kind: "presence-leave" } });
|
|
@@ -517,6 +728,25 @@ var SyncHub = class {
|
|
|
517
728
|
this.relayToRoom(env.room, void 0, JSON.stringify({ from: env.from, op }));
|
|
518
729
|
return;
|
|
519
730
|
}
|
|
731
|
+
if (isFogOp(op)) {
|
|
732
|
+
const previous = this.roomQueues.get(env.room) ?? Promise.resolve();
|
|
733
|
+
const operation = previous.then(async () => {
|
|
734
|
+
const accepted = await this.applyFanoutFogOp(env.room, op);
|
|
735
|
+
if (accepted) {
|
|
736
|
+
this.relayToRoom(
|
|
737
|
+
env.room,
|
|
738
|
+
void 0,
|
|
739
|
+
JSON.stringify({ from: env.from, op: accepted })
|
|
740
|
+
);
|
|
741
|
+
}
|
|
742
|
+
});
|
|
743
|
+
this.roomQueues.set(
|
|
744
|
+
env.room,
|
|
745
|
+
operation.catch(() => {
|
|
746
|
+
})
|
|
747
|
+
);
|
|
748
|
+
return;
|
|
749
|
+
}
|
|
520
750
|
if (!isFanoutOp(op)) return;
|
|
521
751
|
const prevAudience = typeof env.prev === "string" ? env.prev : void 0;
|
|
522
752
|
const prevExisted = env.existed === true;
|
|
@@ -528,9 +758,17 @@ var SyncHub = class {
|
|
|
528
758
|
if (current && !isNewerLayerRecord(record, current)) return;
|
|
529
759
|
await this.applyLayerRecord(room, record);
|
|
530
760
|
}
|
|
761
|
+
async applyFanoutFogOp(room, op) {
|
|
762
|
+
if (this.backend.sharedAcrossInstances === true && this.fogBackend()) return op;
|
|
763
|
+
if (op.kind === "fog-meta") {
|
|
764
|
+
const result = await this.applyFogMeta(room, op.record);
|
|
765
|
+
return result.accepted ? op : null;
|
|
766
|
+
}
|
|
767
|
+
const { accepted } = await this.applyFogPatch(room, op.tiles);
|
|
768
|
+
return accepted.length > 0 ? { ...op, tiles: accepted } : null;
|
|
769
|
+
}
|
|
531
770
|
close() {
|
|
532
|
-
for (const
|
|
533
|
-
this.pendingPresence.clear();
|
|
771
|
+
for (const connId of [...this.presenceLanes.keys()]) this.clearPresenceLanes(connId);
|
|
534
772
|
this.fanoutUnsub();
|
|
535
773
|
}
|
|
536
774
|
};
|
|
@@ -619,9 +857,11 @@ function createSyncServer(options = {}) {
|
|
|
619
857
|
instanceId: options.instanceId,
|
|
620
858
|
authorize: options.authorize,
|
|
621
859
|
authorizeLayer: options.authorizeLayer,
|
|
860
|
+
authorizeFog: options.authorizeFog,
|
|
622
861
|
canRead: options.canRead,
|
|
623
862
|
maxJsonDepth: options.maxJsonDepth ?? DEFAULT_MAX_JSON_DEPTH,
|
|
624
|
-
presenceThrottleMs: options.presenceThrottleMs ?? DEFAULT_PRESENCE_THROTTLE_MS
|
|
863
|
+
presenceThrottleMs: options.presenceThrottleMs ?? DEFAULT_PRESENCE_THROTTLE_MS,
|
|
864
|
+
maxPresenceLanes: options.maxPresenceLanes
|
|
625
865
|
});
|
|
626
866
|
const maxMessageBytes = options.maxMessageBytes ?? DEFAULT_MAX_MESSAGE_BYTES;
|
|
627
867
|
const wss = options.server ? new WebSocketServer({ server: options.server, maxPayload: maxMessageBytes }) : new WebSocketServer({ port: options.port ?? 0, maxPayload: maxMessageBytes });
|