@fieldnotes/sync-server 0.16.0 → 0.17.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +291 -283
- package/dist/index.cjs +145 -180
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +43 -69
- package/dist/index.d.ts +43 -69
- package/dist/index.js +147 -186
- package/dist/index.js.map +1 -1
- package/package.json +7 -4
package/dist/index.cjs
CHANGED
|
@@ -36,7 +36,6 @@ var import_sync = require("@fieldnotes/sync");
|
|
|
36
36
|
var MemoryHubBackend = class {
|
|
37
37
|
rooms = /* @__PURE__ */ new Map();
|
|
38
38
|
roomLayers = /* @__PURE__ */ new Map();
|
|
39
|
-
roomFog = /* @__PURE__ */ new Map();
|
|
40
39
|
room(id) {
|
|
41
40
|
let r = this.rooms.get(id);
|
|
42
41
|
if (!r) {
|
|
@@ -75,26 +74,6 @@ var MemoryHubBackend = class {
|
|
|
75
74
|
async applyLayerRecord(room, record) {
|
|
76
75
|
this.layers(room).set(record.id, record);
|
|
77
76
|
}
|
|
78
|
-
fog(room) {
|
|
79
|
-
let ledger = this.roomFog.get(room);
|
|
80
|
-
if (!ledger) {
|
|
81
|
-
ledger = new import_sync.FogLedger();
|
|
82
|
-
this.roomFog.set(room, ledger);
|
|
83
|
-
}
|
|
84
|
-
return ledger;
|
|
85
|
-
}
|
|
86
|
-
async fogSnapshot(room) {
|
|
87
|
-
return this.fog(room).snapshot();
|
|
88
|
-
}
|
|
89
|
-
async applyFogMeta(room, record) {
|
|
90
|
-
return this.fog(room).applyMeta(record);
|
|
91
|
-
}
|
|
92
|
-
async applyFogTile(room, record) {
|
|
93
|
-
return this.fog(room).applyTile(record);
|
|
94
|
-
}
|
|
95
|
-
async applyFogPatch(room, records) {
|
|
96
|
-
return this.fog(room).applyPatch(records);
|
|
97
|
-
}
|
|
98
77
|
};
|
|
99
78
|
|
|
100
79
|
// src/hub-fanout.ts
|
|
@@ -114,6 +93,46 @@ var InMemoryHubFanout = class {
|
|
|
114
93
|
}
|
|
115
94
|
};
|
|
116
95
|
|
|
96
|
+
// src/sync-plugin.ts
|
|
97
|
+
var ServerPluginRegistry = class {
|
|
98
|
+
byName = /* @__PURE__ */ new Map();
|
|
99
|
+
legacyOwners = /* @__PURE__ */ new Map();
|
|
100
|
+
extensions = /* @__PURE__ */ new Map();
|
|
101
|
+
constructor(plugins) {
|
|
102
|
+
for (const plugin of plugins) this.register(plugin);
|
|
103
|
+
}
|
|
104
|
+
register(plugin) {
|
|
105
|
+
if (this.byName.has(plugin.name))
|
|
106
|
+
throw new Error(`Server plugin "${plugin.name}" is duplicated`);
|
|
107
|
+
this.byName.set(plugin.name, plugin);
|
|
108
|
+
for (const kind of plugin.ownedLegacyKinds ?? []) {
|
|
109
|
+
if (this.legacyOwners.has(kind)) throw new Error(`Sync op kind "${kind}" has two owners`);
|
|
110
|
+
this.legacyOwners.set(kind, plugin);
|
|
111
|
+
}
|
|
112
|
+
plugin.registerExtensionKinds?.({
|
|
113
|
+
register: (kind, handler) => {
|
|
114
|
+
if (this.extensions.has(kind.extensionKind)) {
|
|
115
|
+
throw new Error(`Extension kind "${kind.extensionKind}" has two owners`);
|
|
116
|
+
}
|
|
117
|
+
this.extensions.set(kind.extensionKind, {
|
|
118
|
+
kind,
|
|
119
|
+
plugin,
|
|
120
|
+
handler
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
get plugins() {
|
|
126
|
+
return [...this.byName.values()];
|
|
127
|
+
}
|
|
128
|
+
ownerOf(kind) {
|
|
129
|
+
return this.legacyOwners.get(kind);
|
|
130
|
+
}
|
|
131
|
+
extension(extensionKind) {
|
|
132
|
+
return this.extensions.get(extensionKind);
|
|
133
|
+
}
|
|
134
|
+
};
|
|
135
|
+
|
|
117
136
|
// src/resource-limits.ts
|
|
118
137
|
var DEFAULT_MAX_MESSAGE_BYTES = 1024 * 1024;
|
|
119
138
|
var DEFAULT_MAX_JSON_DEPTH = 32;
|
|
@@ -209,11 +228,6 @@ function isPresenceOp(op) {
|
|
|
209
228
|
const k = op.kind;
|
|
210
229
|
return k === "presence" || k === "presence-leave";
|
|
211
230
|
}
|
|
212
|
-
function isFogOp(op) {
|
|
213
|
-
if (typeof op !== "object" || op === null) return false;
|
|
214
|
-
const k = op.kind;
|
|
215
|
-
return k === "fog-meta" || k === "fog-patch";
|
|
216
|
-
}
|
|
217
231
|
var SyncHub = class {
|
|
218
232
|
backend;
|
|
219
233
|
conns = /* @__PURE__ */ new Map();
|
|
@@ -227,10 +241,9 @@ var SyncHub = class {
|
|
|
227
241
|
fanoutUnsub;
|
|
228
242
|
authorize;
|
|
229
243
|
authorizeLayer;
|
|
230
|
-
|
|
244
|
+
pluginRegistry;
|
|
231
245
|
canRead;
|
|
232
246
|
memoryLayers = /* @__PURE__ */ new Map();
|
|
233
|
-
memoryFog = /* @__PURE__ */ new Map();
|
|
234
247
|
maxJsonDepth;
|
|
235
248
|
presenceThrottleMs;
|
|
236
249
|
maxPresenceLanes;
|
|
@@ -246,21 +259,11 @@ var SyncHub = class {
|
|
|
246
259
|
presenceLanes = /* @__PURE__ */ new Map();
|
|
247
260
|
constructor(options = {}) {
|
|
248
261
|
this.backend = options.backend ?? new MemoryHubBackend();
|
|
249
|
-
|
|
250
|
-
this.backend.fogSnapshot,
|
|
251
|
-
this.backend.applyFogMeta,
|
|
252
|
-
this.backend.applyFogPatch
|
|
253
|
-
];
|
|
254
|
-
if ((fogMethods.some(Boolean) || Boolean(this.backend.applyFogTile)) && !fogMethods.every(Boolean)) {
|
|
255
|
-
throw new Error(
|
|
256
|
-
"HubBackend fog support is an all-or-none capability: fogSnapshot, applyFogMeta, and applyFogPatch are required"
|
|
257
|
-
);
|
|
258
|
-
}
|
|
262
|
+
this.pluginRegistry = new ServerPluginRegistry(options.plugins ?? []);
|
|
259
263
|
this.instanceId = options.instanceId ?? generateInstanceId();
|
|
260
264
|
this.fanout = options.fanout ?? new InMemoryHubFanout();
|
|
261
265
|
this.authorize = options.authorize;
|
|
262
266
|
this.authorizeLayer = options.authorizeLayer;
|
|
263
|
-
this.authorizeFog = options.authorizeFog;
|
|
264
267
|
this.canRead = options.canRead;
|
|
265
268
|
this.maxJsonDepth = options.maxJsonDepth ?? DEFAULT_MAX_JSON_DEPTH;
|
|
266
269
|
this.presenceThrottleMs = options.presenceThrottleMs ?? DEFAULT_PRESENCE_THROTTLE_MS;
|
|
@@ -337,19 +340,39 @@ var SyncHub = class {
|
|
|
337
340
|
const all = await this.backend.snapshot(conn.room);
|
|
338
341
|
const elements = this.canRead ? all.filter((el) => this.mayRead(conn, el.audience)) : all;
|
|
339
342
|
const layers = await this.getLayerRecords(conn.room);
|
|
340
|
-
const fog = await this.getFogSnapshot(conn.room);
|
|
341
343
|
const snapshotOp = {
|
|
342
344
|
kind: "snapshot",
|
|
343
345
|
to: env.from,
|
|
344
346
|
elements
|
|
345
347
|
};
|
|
346
348
|
if (layers.length > 0) snapshotOp["layers"] = layers;
|
|
347
|
-
|
|
349
|
+
const extensions = {};
|
|
350
|
+
for (const plugin of this.pluginRegistry.plugins) {
|
|
351
|
+
let snapshot = await plugin.snapshot?.(conn.room, this.backend);
|
|
352
|
+
if (!snapshot) continue;
|
|
353
|
+
if (plugin.filterSnapshot) {
|
|
354
|
+
snapshot = plugin.filterSnapshot(snapshot, { userId: conn.userId, role: conn.role }) ?? void 0;
|
|
355
|
+
}
|
|
356
|
+
if (!snapshot) continue;
|
|
357
|
+
if (plugin.legacySnapshotKey) snapshotOp[plugin.legacySnapshotKey] = snapshot.data;
|
|
358
|
+
else extensions[plugin.name] = snapshot;
|
|
359
|
+
}
|
|
360
|
+
if (Object.keys(extensions).length > 0) snapshotOp["extensions"] = extensions;
|
|
348
361
|
conn.send(JSON.stringify({ from: HUB_FROM, op: snapshotOp }));
|
|
349
362
|
} else if (op.kind === "layer-upsert" || op.kind === "layer-remove") {
|
|
350
363
|
await this.processLayerOp(conn, op);
|
|
351
|
-
} else if (op.kind === "
|
|
352
|
-
|
|
364
|
+
} else if (op.kind === "extension") {
|
|
365
|
+
const entry = this.pluginRegistry.extension(op.extensionKind);
|
|
366
|
+
if (!entry || !entry.kind.codec.validate(op.payload)) return;
|
|
367
|
+
await this.deliverPluginResult(conn, await entry.handler(op, this.pluginContext(conn)));
|
|
368
|
+
} else if (this.pluginRegistry.ownerOf(op.kind)) {
|
|
369
|
+
const owner = this.pluginRegistry.ownerOf(op.kind);
|
|
370
|
+
if (!owner?.process) return;
|
|
371
|
+
const result = await owner.process(op, this.pluginContext(conn), async () => ({
|
|
372
|
+
accepted: null,
|
|
373
|
+
corrections: []
|
|
374
|
+
}));
|
|
375
|
+
await this.deliverPluginResult(conn, result);
|
|
353
376
|
} else if (op.kind === "upsert" || op.kind === "remove" || op.kind === "clear") {
|
|
354
377
|
const id = op.kind === "upsert" ? op.element.id : op.kind === "remove" ? op.id : void 0;
|
|
355
378
|
const needCurrent = (this.authorize || this.canRead) && id !== void 0;
|
|
@@ -373,21 +396,80 @@ var SyncHub = class {
|
|
|
373
396
|
outboundOp = { kind: "upsert", element: stampedElement };
|
|
374
397
|
}
|
|
375
398
|
}
|
|
376
|
-
await this.backend.apply(conn.room, outboundOp);
|
|
377
399
|
const prevExisted = current !== void 0;
|
|
378
400
|
const prevAudience = current?.audience;
|
|
401
|
+
const result = await this.runCorePlugins(conn, outboundOp);
|
|
402
|
+
for (const correction of result.corrections) {
|
|
403
|
+
conn.send(JSON.stringify({ from: HUB_FROM, op: correction }));
|
|
404
|
+
}
|
|
405
|
+
const accepted = result.accepted;
|
|
406
|
+
if (accepted && (accepted.kind === "upsert" || accepted.kind === "remove" || accepted.kind === "clear")) {
|
|
407
|
+
if (result.locality !== "local") {
|
|
408
|
+
await this.fanout.publish(
|
|
409
|
+
JSON.stringify({
|
|
410
|
+
o: this.instanceId,
|
|
411
|
+
room: conn.room,
|
|
412
|
+
from: conn.id,
|
|
413
|
+
op: accepted,
|
|
414
|
+
prev: prevAudience,
|
|
415
|
+
existed: prevExisted
|
|
416
|
+
})
|
|
417
|
+
);
|
|
418
|
+
}
|
|
419
|
+
this.deliverToRoom(conn.room, conn.id, conn.id, accepted, prevAudience, prevExisted);
|
|
420
|
+
}
|
|
421
|
+
for (const broadcast of result.broadcast ?? []) {
|
|
422
|
+
await this.publishPluginOp(conn, broadcast, result.locality);
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
pluginContext(conn) {
|
|
427
|
+
return {
|
|
428
|
+
room: conn.room,
|
|
429
|
+
connectionId: conn.id,
|
|
430
|
+
userId: conn.userId,
|
|
431
|
+
role: conn.role,
|
|
432
|
+
backend: this.backend,
|
|
433
|
+
backendPlugin: (key) => this.backend.getService?.(key)
|
|
434
|
+
};
|
|
435
|
+
}
|
|
436
|
+
async runCorePlugins(conn, op) {
|
|
437
|
+
const middleware = this.pluginRegistry.plugins.filter((plugin) => plugin.process);
|
|
438
|
+
const context = this.pluginContext(conn);
|
|
439
|
+
const dispatch = async (index, current) => {
|
|
440
|
+
const plugin = middleware[index];
|
|
441
|
+
if (!plugin?.process) {
|
|
442
|
+
await this.backend.apply(conn.room, current);
|
|
443
|
+
return { accepted: current, corrections: [] };
|
|
444
|
+
}
|
|
445
|
+
let called = false;
|
|
446
|
+
return plugin.process(current, context, async (nextOp, nextContext) => {
|
|
447
|
+
if (called) throw new Error(`Server plugin "${plugin.name}" called next() more than once`);
|
|
448
|
+
if (nextContext !== context) {
|
|
449
|
+
throw new Error(`Server plugin "${plugin.name}" replaced the operation context`);
|
|
450
|
+
}
|
|
451
|
+
called = true;
|
|
452
|
+
return dispatch(index + 1, nextOp);
|
|
453
|
+
});
|
|
454
|
+
};
|
|
455
|
+
return dispatch(0, op);
|
|
456
|
+
}
|
|
457
|
+
async deliverPluginResult(conn, result) {
|
|
458
|
+
for (const correction of result.corrections) {
|
|
459
|
+
conn.send(JSON.stringify({ from: HUB_FROM, op: correction }));
|
|
460
|
+
}
|
|
461
|
+
if (result.accepted) await this.publishPluginOp(conn, result.accepted, result.locality);
|
|
462
|
+
for (const broadcast of result.broadcast ?? []) {
|
|
463
|
+
await this.publishPluginOp(conn, broadcast, result.locality);
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
async publishPluginOp(conn, op, locality) {
|
|
467
|
+
if (locality !== "local") {
|
|
379
468
|
await this.fanout.publish(
|
|
380
|
-
JSON.stringify({
|
|
381
|
-
o: this.instanceId,
|
|
382
|
-
room: conn.room,
|
|
383
|
-
from: conn.id,
|
|
384
|
-
op: outboundOp,
|
|
385
|
-
prev: prevAudience,
|
|
386
|
-
existed: prevExisted
|
|
387
|
-
})
|
|
469
|
+
JSON.stringify({ o: this.instanceId, room: conn.room, from: conn.id, op })
|
|
388
470
|
);
|
|
389
|
-
this.deliverToRoom(conn.room, conn.id, conn.id, outboundOp, prevAudience, prevExisted);
|
|
390
471
|
}
|
|
472
|
+
this.relayToRoom(conn.room, conn.id, JSON.stringify({ from: conn.id, op }));
|
|
391
473
|
}
|
|
392
474
|
/**
|
|
393
475
|
* Applies a layer-definition edit on the room's serial queue. Convergence is
|
|
@@ -455,121 +537,6 @@ var SyncHub = class {
|
|
|
455
537
|
}
|
|
456
538
|
map.set(record.id, record);
|
|
457
539
|
}
|
|
458
|
-
// ── Fog processing ──
|
|
459
|
-
fogBackend() {
|
|
460
|
-
const { fogSnapshot, applyFogMeta, applyFogPatch } = this.backend;
|
|
461
|
-
if (!fogSnapshot || !applyFogMeta || !applyFogPatch) return null;
|
|
462
|
-
return {
|
|
463
|
-
fogSnapshot: fogSnapshot.bind(this.backend),
|
|
464
|
-
applyFogMeta: applyFogMeta.bind(this.backend),
|
|
465
|
-
applyFogPatch: applyFogPatch.bind(this.backend)
|
|
466
|
-
};
|
|
467
|
-
}
|
|
468
|
-
getFogLedger(room) {
|
|
469
|
-
let ledger = this.memoryFog.get(room);
|
|
470
|
-
if (!ledger) {
|
|
471
|
-
ledger = new import_sync2.FogLedger();
|
|
472
|
-
this.memoryFog.set(room, ledger);
|
|
473
|
-
}
|
|
474
|
-
return ledger;
|
|
475
|
-
}
|
|
476
|
-
async getFogSnapshot(room) {
|
|
477
|
-
const backend = this.fogBackend();
|
|
478
|
-
if (backend) return backend.fogSnapshot(room);
|
|
479
|
-
return this.getFogLedger(room).snapshot();
|
|
480
|
-
}
|
|
481
|
-
async applyFogMeta(room, record) {
|
|
482
|
-
const backend = this.fogBackend();
|
|
483
|
-
if (backend) return backend.applyFogMeta(room, record);
|
|
484
|
-
return this.getFogLedger(room).applyMeta(record);
|
|
485
|
-
}
|
|
486
|
-
async applyFogPatch(room, records) {
|
|
487
|
-
const backend = this.fogBackend();
|
|
488
|
-
if (backend) return backend.applyFogPatch(room, records);
|
|
489
|
-
return this.getFogLedger(room).applyPatch(records);
|
|
490
|
-
}
|
|
491
|
-
async processFogOp(conn, op) {
|
|
492
|
-
const current = await this.getFogSnapshot(conn.room);
|
|
493
|
-
if (this.authorizeFog) {
|
|
494
|
-
const allowed = await this.authorizeFog({
|
|
495
|
-
userId: conn.userId,
|
|
496
|
-
role: conn.role,
|
|
497
|
-
room: conn.room,
|
|
498
|
-
op,
|
|
499
|
-
current
|
|
500
|
-
});
|
|
501
|
-
if (!allowed) {
|
|
502
|
-
if (op.kind === "fog-meta") {
|
|
503
|
-
const correction = current?.meta ?? { version: 1, editor: HUB_FROM };
|
|
504
|
-
conn.send(
|
|
505
|
-
JSON.stringify({ from: HUB_FROM, op: { kind: "fog-meta", record: correction } })
|
|
506
|
-
);
|
|
507
|
-
} else if (current?.meta.definition) {
|
|
508
|
-
const corrections = op.tiles.map((t) => {
|
|
509
|
-
const existing = current.tiles.find((ct) => ct.x === t.x && ct.y === t.y);
|
|
510
|
-
return existing ?? {
|
|
511
|
-
generation: current.meta.definition?.generation ?? op.generation,
|
|
512
|
-
x: t.x,
|
|
513
|
-
y: t.y,
|
|
514
|
-
version: 1,
|
|
515
|
-
editor: HUB_FROM
|
|
516
|
-
};
|
|
517
|
-
});
|
|
518
|
-
conn.send(
|
|
519
|
-
JSON.stringify({
|
|
520
|
-
from: HUB_FROM,
|
|
521
|
-
op: {
|
|
522
|
-
kind: "fog-patch",
|
|
523
|
-
generation: current.meta.definition.generation,
|
|
524
|
-
tiles: corrections
|
|
525
|
-
}
|
|
526
|
-
})
|
|
527
|
-
);
|
|
528
|
-
} else {
|
|
529
|
-
conn.send(
|
|
530
|
-
JSON.stringify({
|
|
531
|
-
from: HUB_FROM,
|
|
532
|
-
op: {
|
|
533
|
-
kind: "fog-meta",
|
|
534
|
-
record: current?.meta ?? { version: 1, editor: HUB_FROM }
|
|
535
|
-
}
|
|
536
|
-
})
|
|
537
|
-
);
|
|
538
|
-
}
|
|
539
|
-
return;
|
|
540
|
-
}
|
|
541
|
-
}
|
|
542
|
-
let outbound;
|
|
543
|
-
if (op.kind === "fog-meta") {
|
|
544
|
-
const result = await this.applyFogMeta(conn.room, op.record);
|
|
545
|
-
if (!result.accepted) {
|
|
546
|
-
if (result.correction) {
|
|
547
|
-
conn.send(
|
|
548
|
-
JSON.stringify({ from: HUB_FROM, op: { kind: "fog-meta", record: result.correction } })
|
|
549
|
-
);
|
|
550
|
-
}
|
|
551
|
-
return;
|
|
552
|
-
}
|
|
553
|
-
outbound = op;
|
|
554
|
-
} else {
|
|
555
|
-
const { accepted, corrections } = await this.applyFogPatch(conn.room, op.tiles);
|
|
556
|
-
if (corrections.length > 0) {
|
|
557
|
-
const correctionGeneration = corrections[0]?.generation ?? op.generation;
|
|
558
|
-
conn.send(
|
|
559
|
-
JSON.stringify({
|
|
560
|
-
from: HUB_FROM,
|
|
561
|
-
op: { kind: "fog-patch", generation: correctionGeneration, tiles: corrections }
|
|
562
|
-
})
|
|
563
|
-
);
|
|
564
|
-
}
|
|
565
|
-
if (accepted.length === 0) return;
|
|
566
|
-
outbound = { kind: "fog-patch", generation: op.generation, tiles: accepted };
|
|
567
|
-
}
|
|
568
|
-
await this.fanout.publish(
|
|
569
|
-
JSON.stringify({ o: this.instanceId, room: conn.room, from: conn.id, op: outbound })
|
|
570
|
-
);
|
|
571
|
-
this.relayToRoom(conn.room, conn.id, JSON.stringify({ from: conn.id, op: outbound }));
|
|
572
|
-
}
|
|
573
540
|
mayRead(conn, audience) {
|
|
574
541
|
if (!this.canRead) return true;
|
|
575
542
|
return this.canRead({ userId: conn.userId, role: conn.role, room: conn.room, audience });
|
|
@@ -750,10 +717,17 @@ var SyncHub = class {
|
|
|
750
717
|
this.relayToRoom(env.room, void 0, JSON.stringify({ from: env.from, op }));
|
|
751
718
|
return;
|
|
752
719
|
}
|
|
753
|
-
|
|
720
|
+
const plugin = typeof op === "object" && op !== null && op.kind === "extension" ? this.pluginRegistry.extension(op.extensionKind ?? "")?.plugin : typeof op === "object" && op !== null && typeof op.kind === "string" ? this.pluginRegistry.ownerOf(op.kind) : void 0;
|
|
721
|
+
if (plugin && typeof op === "object" && op !== null) {
|
|
754
722
|
const previous = this.roomQueues.get(env.room) ?? Promise.resolve();
|
|
755
723
|
const operation = previous.then(async () => {
|
|
756
|
-
const
|
|
724
|
+
const context = {
|
|
725
|
+
room: env.room,
|
|
726
|
+
connectionId: env.from,
|
|
727
|
+
backend: this.backend,
|
|
728
|
+
backendPlugin: (key) => this.backend.getService?.(key)
|
|
729
|
+
};
|
|
730
|
+
const accepted = plugin.applyFanout ? await plugin.applyFanout(op, context) : op;
|
|
757
731
|
if (accepted) {
|
|
758
732
|
this.relayToRoom(
|
|
759
733
|
env.room,
|
|
@@ -780,15 +754,6 @@ var SyncHub = class {
|
|
|
780
754
|
if (current && !(0, import_sync2.isNewerLayerRecord)(record, current)) return;
|
|
781
755
|
await this.applyLayerRecord(room, record);
|
|
782
756
|
}
|
|
783
|
-
async applyFanoutFogOp(room, op) {
|
|
784
|
-
if (this.backend.sharedAcrossInstances === true && this.fogBackend()) return op;
|
|
785
|
-
if (op.kind === "fog-meta") {
|
|
786
|
-
const result = await this.applyFogMeta(room, op.record);
|
|
787
|
-
return result.accepted ? op : null;
|
|
788
|
-
}
|
|
789
|
-
const { accepted } = await this.applyFogPatch(room, op.tiles);
|
|
790
|
-
return accepted.length > 0 ? { ...op, tiles: accepted } : null;
|
|
791
|
-
}
|
|
792
757
|
close() {
|
|
793
758
|
for (const connId of [...this.presenceLanes.keys()]) this.clearPresenceLanes(connId);
|
|
794
759
|
this.fanoutUnsub();
|
|
@@ -879,7 +844,7 @@ function createSyncServer(options = {}) {
|
|
|
879
844
|
instanceId: options.instanceId,
|
|
880
845
|
authorize: options.authorize,
|
|
881
846
|
authorizeLayer: options.authorizeLayer,
|
|
882
|
-
|
|
847
|
+
plugins: options.plugins,
|
|
883
848
|
canRead: options.canRead,
|
|
884
849
|
maxJsonDepth: options.maxJsonDepth ?? DEFAULT_MAX_JSON_DEPTH,
|
|
885
850
|
presenceThrottleMs: options.presenceThrottleMs ?? DEFAULT_PRESENCE_THROTTLE_MS,
|