@pluv/client 1.0.1 → 2.0.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.mjs CHANGED
@@ -38,13 +38,6 @@ var __async = (__this, __arguments, generator) => {
38
38
  });
39
39
  };
40
40
 
41
- // src/AbstractRoom.ts
42
- var AbstractRoom = class {
43
- constructor(room) {
44
- this.id = room;
45
- }
46
- };
47
-
48
41
  // src/AbstractStorageStore.ts
49
42
  var AbstractStorageStore = class {
50
43
  constructor(room) {
@@ -90,6 +83,9 @@ var PluvProcedure = class _PluvProcedure {
90
83
  }
91
84
  };
92
85
 
86
+ // src/PluvRoom.ts
87
+ import { ConnectionState } from "@pluv/types";
88
+
93
89
  // src/CrdtManager.ts
94
90
  import { noop } from "@pluv/crdt";
95
91
  var CrdtManager = class {
@@ -105,14 +101,17 @@ var CrdtManager = class {
105
101
  * update. This unfortunately means updates can be dropped entirely
106
102
  * @date June 13, 2023
107
103
  */
108
- applyUpdate(update, origin) {
104
+ applyUpdate(params) {
105
+ const { origin, update } = params;
109
106
  if (!this.initialized) return this;
110
107
  const updates = typeof update === "string" ? [update] : update;
111
108
  this._applyDocUpdates(this.doc, updates, origin);
112
109
  return this;
113
110
  }
114
111
  destroy() {
112
+ this.initialized = false;
115
113
  this.doc.destroy();
114
+ this.doc = this._docFactory.getEmpty();
116
115
  }
117
116
  get(key) {
118
117
  return this.doc.get(key);
@@ -121,7 +120,7 @@ var CrdtManager = class {
121
120
  return this._docFactory.getInitialized().getEncodedState();
122
121
  }
123
122
  initialize(params) {
124
- const { onInitialized, origin, update } = params;
123
+ const { origin, update } = params;
125
124
  if (this.initialized) {
126
125
  console.warn("Attempted to initialize storage multiple times");
127
126
  return this;
@@ -141,9 +140,11 @@ var CrdtManager = class {
141
140
  ).rebuildStorage(reference);
142
141
  this.initialized = true;
143
142
  initialized.destroy();
144
- onInitialized == null ? void 0 : onInitialized(this.doc.getEncodedState());
145
143
  return this;
146
144
  }
145
+ resolveEncodedState(updates) {
146
+ return this._docFactory.resolveEncodedState(updates);
147
+ }
147
148
  _applyDocUpdates(doc, updates, origin) {
148
149
  doc.transact(() => {
149
150
  doc.batchApplyEncodedState({ origin, updates });
@@ -518,15 +519,6 @@ var UsersManager = class {
518
519
  }
519
520
  };
520
521
 
521
- // src/enums.ts
522
- var ConnectionState = {
523
- Closed: "Closed",
524
- Connecting: "Connecting",
525
- Open: "Open",
526
- Unavailable: "Unavailable",
527
- Untouched: "Untouched"
528
- };
529
-
530
522
  // src/PluvRoom.ts
531
523
  var ADD_TO_STORAGE_STATE_DEBOUNCE_MS = 1e3;
532
524
  var HEARTBEAT_INTERVAL_MS = 1e4;
@@ -539,24 +531,8 @@ var ORIGIN_STORAGE_UPDATED = "$storageUpdated";
539
531
  var DEFAULT_PLUV_CLIENT_ADDON = (input) => ({
540
532
  storage: new StorageStore(input.room.id)
541
533
  });
542
- var PluvRoom = class extends AbstractRoom {
534
+ var PluvRoom = class {
543
535
  constructor(room, options) {
544
- const {
545
- addons = [],
546
- authEndpoint,
547
- debug = false,
548
- initialPresence,
549
- initialStorage,
550
- limits,
551
- metadata,
552
- onAuthorizationFail,
553
- presence,
554
- publicKey,
555
- reconnectTimeoutMs = RECONNECT_TIMEOUT_MS,
556
- router,
557
- wsEndpoint
558
- } = options;
559
- super(room);
560
536
  this._crdtNotifier = new CrdtNotifier();
561
537
  this._eventNotifier = new EventNotifier();
562
538
  this._intervals = {
@@ -703,7 +679,7 @@ var PluvRoom = class extends AbstractRoom {
703
679
  const shouldFlatten = yield this._storageStore.getShouldFlatten();
704
680
  if (!shouldFlatten) return;
705
681
  const updates = yield this._storageStore.getUpdates();
706
- this._crdtManager.applyUpdate(updates, this);
682
+ this._crdtManager.applyUpdate({ update: updates, origin: this });
707
683
  const encodedState = this._crdtManager.doc.getEncodedState();
708
684
  yield this._storageStore.flatten(encodedState);
709
685
  }),
@@ -715,14 +691,30 @@ var PluvRoom = class extends AbstractRoom {
715
691
  DEFAULT_PLUV_CLIENT_ADDON
716
692
  );
717
693
  };
694
+ const {
695
+ addons = [],
696
+ authEndpoint,
697
+ debug = false,
698
+ initialPresence,
699
+ initialStorage,
700
+ limits,
701
+ metadata,
702
+ onAuthorizationFail,
703
+ presence,
704
+ publicKey,
705
+ reconnectTimeoutMs = RECONNECT_TIMEOUT_MS,
706
+ router,
707
+ wsEndpoint
708
+ } = options;
709
+ this.id = room;
710
+ this.metadata = metadata;
718
711
  const addon = this._getAddon(addons);
719
712
  const { storage } = __spreadValues(__spreadValues({}, DEFAULT_PLUV_CLIENT_ADDON({ room: this })), addon({ room: this }));
720
- this.metadata = metadata;
713
+ this._storageStore = storage;
721
714
  this._debug = debug;
722
715
  this._endpoints = { authEndpoint, wsEndpoint };
723
716
  this._limits = limits;
724
717
  this._reconnectTimeoutMs = reconnectTimeoutMs;
725
- this._storageStore = storage;
726
718
  if (!!publicKey) this._publicKey = publicKey;
727
719
  this._listeners = {
728
720
  onAuthorizationFail: (error) => {
@@ -760,6 +752,7 @@ var PluvRoom = class extends AbstractRoom {
760
752
  oldState.connection.state = ConnectionState.Connecting;
761
753
  return oldState;
762
754
  });
755
+ yield this._storageStore.initialize();
763
756
  const wsEndpoint = this._getWsEndpoint(this.id, params);
764
757
  const url = new URL(wsEndpoint);
765
758
  let authToken = null;
@@ -824,20 +817,6 @@ var PluvRoom = class extends AbstractRoom {
824
817
  this._flattenStorageStore();
825
818
  });
826
819
  }
827
- _applyStorageStore() {
828
- return __async(this, null, function* () {
829
- const updates = yield this._storageStore.getUpdates();
830
- this._crdtManager.initialize({
831
- onInitialized: () => {
832
- this._stateNotifier.subjects["storage-loaded"].next(true);
833
- this._observeCrdt();
834
- },
835
- origin: ORIGIN_INITIALIZED,
836
- update: updates
837
- });
838
- this._emitSharedTypes();
839
- });
840
- }
841
820
  _attachWindowListeners() {
842
821
  if (typeof window === "undefined") return;
843
822
  if (typeof document === "undefined") return;
@@ -1028,33 +1007,35 @@ var PluvRoom = class extends AbstractRoom {
1028
1007
  this._stateNotifier.subjects.others.next(others);
1029
1008
  }
1030
1009
  _handleRegisteredMessage(message) {
1031
- var _a, _b;
1032
- const { connectionId } = message;
1033
- const user = message.user;
1034
- if (!connectionId) return;
1035
- if (!this._state.webSocket) throw new Error("Could not find WebSocket");
1036
- const data = message.data;
1037
- const state = data.state;
1038
- this._updateState((oldState) => {
1039
- oldState.connection.count += 1;
1040
- oldState.connection.id = connectionId;
1041
- oldState.authorization.user = user != null ? user : null;
1042
- return oldState;
1043
- });
1044
- const userInfo = {
1045
- connectionId,
1046
- presence: (_a = data.presence) != null ? _a : void 0,
1047
- user
1048
- };
1049
- this._usersManager.setMyself(userInfo);
1050
- const presence = this._usersManager.myPresence;
1051
- const myself = (_b = this._usersManager.myself) != null ? _b : null;
1052
- this._stateNotifier.subjects["my-presence"].next(presence);
1053
- this._stateNotifier.subjects.myself.next(myself);
1054
- const update = this._crdtManager.getInitialState();
1055
- this._sendMessage({
1056
- type: "$initializeSession",
1057
- data: { presence, update }
1010
+ return __async(this, null, function* () {
1011
+ var _a, _b, _c;
1012
+ const { connectionId } = message;
1013
+ const user = message.user;
1014
+ if (!connectionId) return;
1015
+ if (!this._state.webSocket) throw new Error("Could not find WebSocket");
1016
+ const data = message.data;
1017
+ const state = data.state;
1018
+ this._updateState((oldState) => {
1019
+ oldState.connection.count += 1;
1020
+ oldState.connection.id = connectionId;
1021
+ oldState.authorization.user = user != null ? user : null;
1022
+ return oldState;
1023
+ });
1024
+ const userInfo = {
1025
+ connectionId,
1026
+ presence: (_a = data.presence) != null ? _a : void 0,
1027
+ user
1028
+ };
1029
+ this._usersManager.setMyself(userInfo);
1030
+ const presence = this._usersManager.myPresence;
1031
+ const myself = (_b = this._usersManager.myself) != null ? _b : null;
1032
+ this._stateNotifier.subjects["my-presence"].next(presence);
1033
+ this._stateNotifier.subjects.myself.next(myself);
1034
+ const update = !state ? (_c = this._crdtManager.resolveEncodedState(yield this._storageStore.getUpdates())) != null ? _c : this._crdtManager.getInitialState() : this._crdtManager.getInitialState();
1035
+ this._sendMessage({
1036
+ type: "$initializeSession",
1037
+ data: { presence, update }
1038
+ });
1058
1039
  });
1059
1040
  }
1060
1041
  _handleStorageReceivedMessage(message) {
@@ -1063,24 +1044,34 @@ var PluvRoom = class extends AbstractRoom {
1063
1044
  if (!connectionId) return;
1064
1045
  if (!this._state.webSocket) throw new Error("Could not find WebSocket");
1065
1046
  const data = message.data;
1047
+ const changeKind = data.changeKind;
1066
1048
  const state = data.state;
1067
- this._crdtManager.initialize({
1068
- onInitialized: (update2) => __async(this, null, function* () {
1069
- yield this._addToStorageStore(update2);
1070
- this._emitSharedTypes();
1071
- this._observeCrdt();
1072
- this._stateNotifier.subjects["storage-loaded"].next(true);
1073
- }),
1074
- origin: ORIGIN_INITIALIZED,
1075
- update: state
1076
- });
1049
+ const updates = yield this._storageStore.getUpdates();
1050
+ const origin = ORIGIN_INITIALIZED;
1051
+ const updateStorage = (update) => {
1052
+ return this._crdtManager.initialized ? this._crdtManager.applyUpdate({ update, origin }) : this._crdtManager.initialize({ update, origin });
1053
+ };
1054
+ if (changeKind === "empty") {
1055
+ this._crdtManager.destroy();
1056
+ this._crdtManager.initialize({ origin, update: updates });
1057
+ } else if (changeKind === "initialized") {
1058
+ if (!!updates.length) updateStorage(updates);
1059
+ else {
1060
+ const encodedState2 = updateStorage(state).doc.getEncodedState();
1061
+ this._addToStorageStore(encodedState2);
1062
+ }
1063
+ } else {
1064
+ const encodedState2 = updateStorage(state).doc.getEncodedState();
1065
+ if (!!updates.length) this._crdtManager.applyUpdate({ update: updates, origin });
1066
+ yield this._addToStorageStore(encodedState2);
1067
+ }
1077
1068
  const encodedState = this._crdtManager.doc.getEncodedState();
1078
- yield this._storageStore.addUpdate(encodedState);
1079
1069
  this._emitSharedTypes();
1080
- const update = this._crdtManager.doc.getEncodedState();
1070
+ this._observeCrdt();
1071
+ this._stateNotifier.subjects["storage-loaded"].next(true);
1081
1072
  this._sendMessage({
1082
1073
  type: "$updateStorage",
1083
- data: { origin: ORIGIN_INITIALIZED, update }
1074
+ data: { origin, update: encodedState }
1084
1075
  });
1085
1076
  });
1086
1077
  }
@@ -1150,7 +1141,8 @@ var PluvRoom = class extends AbstractRoom {
1150
1141
  }
1151
1142
  _logDebug(...data) {
1152
1143
  var _a;
1153
- if (((_a = process == null ? void 0 : process.env) == null ? void 0 : _a.NODE_ENV) === "production") return;
1144
+ if (typeof process === "undefined") return;
1145
+ if (((_a = process.env) == null ? void 0 : _a.NODE_ENV) === "production") return;
1154
1146
  if (this._debug) console.log(...data);
1155
1147
  }
1156
1148
  _observeCrdt() {
@@ -1159,11 +1151,11 @@ var PluvRoom = class extends AbstractRoom {
1159
1151
  const unsubscribe = this._crdtManager.doc.subscribe((event) => {
1160
1152
  var _a2;
1161
1153
  const origin = (_a2 = event.origin) != null ? _a2 : null;
1162
- this._addToStorageStore(event.update);
1163
1154
  this._emitSharedTypes();
1164
1155
  if (origin === ORIGIN_INITIALIZED || origin === ORIGIN_STORAGE_UPDATED) {
1165
1156
  return;
1166
1157
  }
1158
+ this._addToStorageStore(event.update);
1167
1159
  if (!this._state.webSocket) return;
1168
1160
  if (!this._state.connection.id) return;
1169
1161
  if (this._state.webSocket.readyState !== WebSocket.OPEN) return;
@@ -1410,7 +1402,7 @@ var PluvClient = class {
1410
1402
  this._publicKey = null;
1411
1403
  this._rooms = /* @__PURE__ */ new Map();
1412
1404
  this.createRoom = (room, options = {}) => {
1413
- var _a;
1405
+ var _a, _b;
1414
1406
  const oldRoom = this.getRoom(room);
1415
1407
  if (oldRoom) return oldRoom;
1416
1408
  const newRoom = new PluvRoom(room, {
@@ -1418,12 +1410,12 @@ var PluvClient = class {
1418
1410
  authEndpoint: this._authEndpoint,
1419
1411
  debug: options.debug,
1420
1412
  initialPresence: options.initialPresence,
1421
- initialStorage: this._initialStorage,
1413
+ initialStorage: (_a = options.initialStorage) != null ? _a : this._initialStorage,
1422
1414
  limits: this._limits,
1423
1415
  metadata: this.metadata,
1424
1416
  onAuthorizationFail: options.onAuthorizationFail,
1425
1417
  presence: this._presence,
1426
- publicKey: (_a = this._publicKey) != null ? _a : void 0,
1418
+ publicKey: (_b = this._publicKey) != null ? _b : void 0,
1427
1419
  reconnectTimeoutMs: options.reconnectTimeoutMs,
1428
1420
  router: options.router,
1429
1421
  wsEndpoint: this._wsEndpoint
@@ -1492,7 +1484,8 @@ var PluvClient = class {
1492
1484
  }
1493
1485
  _logDebug(...data) {
1494
1486
  var _a;
1495
- if (((_a = process == null ? void 0 : process.env) == null ? void 0 : _a.NODE_ENV) === "production") return;
1487
+ if (typeof process === "undefined") return;
1488
+ if (((_a = process.env) == null ? void 0 : _a.NODE_ENV) === "production") return;
1496
1489
  if (this._debug) console.log(...data);
1497
1490
  }
1498
1491
  };
@@ -1506,10 +1499,9 @@ var createClient = (options) => {
1506
1499
  var infer = (callback) => callback;
1507
1500
 
1508
1501
  // src/MockedRoom.ts
1509
- var MockedRoom = class extends AbstractRoom {
1502
+ import { ConnectionState as ConnectionState2 } from "@pluv/types";
1503
+ var MockedRoom = class {
1510
1504
  constructor(room, options) {
1511
- const { events, initialPresence, initialStorage, limits, presence, router } = options;
1512
- super(room);
1513
1505
  this._crdtNotifier = new CrdtNotifier();
1514
1506
  this._eventNotifier = new EventNotifier();
1515
1507
  this._otherNotifier = new OtherNotifier();
@@ -1522,7 +1514,7 @@ var MockedRoom = class extends AbstractRoom {
1522
1514
  attempts: 0,
1523
1515
  count: 1,
1524
1516
  id: null,
1525
- state: ConnectionState.Untouched
1517
+ state: ConnectionState2.Untouched
1526
1518
  },
1527
1519
  webSocket: null
1528
1520
  };
@@ -1533,7 +1525,7 @@ var MockedRoom = class extends AbstractRoom {
1533
1525
  this.broadcast = new Proxy(
1534
1526
  (event, data) => __async(this, null, function* () {
1535
1527
  if (!this._state.webSocket) return;
1536
- if (this._state.connection.state !== ConnectionState.Open) return;
1528
+ if (this._state.connection.state !== ConnectionState2.Open) return;
1537
1529
  const type = event.toString();
1538
1530
  const procedure = this._router._defs.events[type];
1539
1531
  if (!(procedure == null ? void 0 : procedure.config.broadcast)) {
@@ -1635,6 +1627,8 @@ var MockedRoom = class extends AbstractRoom {
1635
1627
  this._stateNotifier.subjects["my-presence"].next(myPresence);
1636
1628
  if (!!myself) this._stateNotifier.subjects["myself"].next(myself);
1637
1629
  };
1630
+ const { events, initialPresence, initialStorage, limits, presence, router } = options;
1631
+ this.id = room;
1638
1632
  this._events = events;
1639
1633
  this._limits = __spreadValues({
1640
1634
  presenceMaxSize: MAX_PRESENCE_SIZE_BYTES
@@ -1710,9 +1704,7 @@ var register = (params) => {
1710
1704
  };
1711
1705
  };
1712
1706
  export {
1713
- AbstractRoom,
1714
1707
  AbstractStorageStore,
1715
- ConnectionState,
1716
1708
  MockedRoom,
1717
1709
  PluvClient,
1718
1710
  PluvRoom,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pluv/client",
3
- "version": "1.0.1",
3
+ "version": "2.0.0",
4
4
  "description": "@pluv/io frontend client",
5
5
  "author": "leedavidcs",
6
6
  "license": "MIT",
@@ -18,15 +18,15 @@
18
18
  },
19
19
  "dependencies": {
20
20
  "wonka": "^6.3.5",
21
- "@pluv/crdt": "^1.0.1",
22
- "@pluv/types": "^1.0.1"
21
+ "@pluv/crdt": "^2.0.0",
22
+ "@pluv/types": "^2.0.0"
23
23
  },
24
24
  "devDependencies": {
25
25
  "eslint": "^9.25.1",
26
26
  "tsup": "^8.4.0",
27
27
  "typescript": "^5.8.3",
28
- "@pluv/tsconfig": "^1.0.1",
29
- "eslint-config-pluv": "^1.0.1"
28
+ "@pluv/tsconfig": "^2.0.0",
29
+ "eslint-config-pluv": "^2.0.0"
30
30
  },
31
31
  "scripts": {
32
32
  "build": "tsup src/index.ts --format esm,cjs --dts",
@@ -1,24 +1,28 @@
1
- import type { AbstractCrdtDoc, AbstractCrdtDocFactory, CrdtType } from "@pluv/crdt";
1
+ import type { AbstractCrdtDocFactory, CrdtDocLike, CrdtType, InferStorage } from "@pluv/crdt";
2
2
  import { noop } from "@pluv/crdt";
3
3
 
4
- export type CrdtManagerOptions<TStorage extends Record<string, CrdtType<any, any>> = {}> = {
5
- initialStorage?: AbstractCrdtDocFactory<TStorage>;
4
+ export type CrdtManagerOptions<TCrdt extends AbstractCrdtDocFactory<any>> = {
5
+ initialStorage?: TCrdt;
6
6
  };
7
7
 
8
- type CrdtManagerInitializeParams = {
9
- onInitialized?: (state: string) => void;
8
+ interface CrdtManagerInitializeParams {
10
9
  origin?: any;
11
10
  update: string | readonly string[];
12
- };
11
+ }
12
+
13
+ interface ApplyUpdateParams {
14
+ origin?: any;
15
+ update: string | readonly string[];
16
+ }
13
17
 
14
- export class CrdtManager<TStorage extends Record<string, CrdtType<any, any>>> {
15
- public doc: AbstractCrdtDoc<TStorage>;
18
+ export class CrdtManager<TCrdt extends AbstractCrdtDocFactory<any>> {
19
+ public doc: CrdtDocLike<InferStorage<TCrdt>>;
16
20
  public initialized: boolean = false;
17
21
 
18
- private readonly _docFactory: AbstractCrdtDocFactory<TStorage>;
22
+ private readonly _docFactory: TCrdt;
19
23
 
20
- constructor(options: CrdtManagerOptions<TStorage>) {
21
- const { initialStorage = noop.doc({}) as AbstractCrdtDocFactory<TStorage> } = options;
24
+ constructor(options: CrdtManagerOptions<TCrdt>) {
25
+ const { initialStorage = noop.doc({}) as any } = options;
22
26
 
23
27
  this._docFactory = initialStorage;
24
28
  this.doc = initialStorage.getEmpty();
@@ -30,7 +34,9 @@ export class CrdtManager<TStorage extends Record<string, CrdtType<any, any>>> {
30
34
  * update. This unfortunately means updates can be dropped entirely
31
35
  * @date June 13, 2023
32
36
  */
33
- public applyUpdate(update: string | readonly string[], origin?: any): this {
37
+ public applyUpdate(params: ApplyUpdateParams): this {
38
+ const { origin, update } = params;
39
+
34
40
  if (!this.initialized) return this;
35
41
 
36
42
  const updates: readonly string[] = typeof update === "string" ? [update] : update;
@@ -41,10 +47,12 @@ export class CrdtManager<TStorage extends Record<string, CrdtType<any, any>>> {
41
47
  }
42
48
 
43
49
  public destroy(): void {
50
+ this.initialized = false;
44
51
  this.doc.destroy();
52
+ this.doc = this._docFactory.getEmpty();
45
53
  }
46
54
 
47
- public get<TKey extends keyof TStorage>(key: TKey): TStorage[TKey] {
55
+ public get<TKey extends keyof InferStorage<TCrdt>>(key: TKey): InferStorage<TCrdt>[TKey] {
48
56
  return this.doc.get(key);
49
57
  }
50
58
 
@@ -53,7 +61,7 @@ export class CrdtManager<TStorage extends Record<string, CrdtType<any, any>>> {
53
61
  }
54
62
 
55
63
  public initialize(params: CrdtManagerInitializeParams): this {
56
- const { onInitialized, origin, update } = params;
64
+ const { origin, update } = params;
57
65
 
58
66
  /**
59
67
  * !HACK
@@ -94,16 +102,18 @@ export class CrdtManager<TStorage extends Record<string, CrdtType<any, any>>> {
94
102
  this.initialized = true;
95
103
  initialized.destroy();
96
104
 
97
- onInitialized?.(this.doc.getEncodedState());
98
-
99
105
  return this;
100
106
  }
101
107
 
108
+ public resolveEncodedState(updates: string | string[] | readonly string[]): string | null {
109
+ return this._docFactory.resolveEncodedState(updates);
110
+ }
111
+
102
112
  private _applyDocUpdates(
103
- doc: AbstractCrdtDoc<TStorage>,
113
+ doc: CrdtDocLike<InferStorage<TCrdt>>,
104
114
  updates: readonly string[],
105
115
  origin?: string,
106
- ): AbstractCrdtDoc<TStorage> {
116
+ ): CrdtDocLike<InferStorage<TCrdt>> {
107
117
  doc.transact(() => {
108
118
  doc.batchApplyEncodedState({ origin, updates });
109
119
  }, origin);
@@ -1,12 +1,13 @@
1
- import type { Id, InferIOOutput, IOEventMessage, IOLike } from "@pluv/types";
1
+ import type {
2
+ EventNotifierSubscriptionCallback,
3
+ Id,
4
+ InferIOOutput,
5
+ IOEventMessage,
6
+ IOLike,
7
+ } from "@pluv/types";
2
8
  import type { Subject } from "wonka";
3
9
  import { makeSubject, subscribe } from "wonka";
4
10
 
5
- export type EventNotifierSubscriptionCallback<
6
- TIO extends IOLike<any, any>,
7
- TEvent extends keyof InferIOOutput<TIO>,
8
- > = (value: Id<IOEventMessage<TIO, TEvent>>) => void;
9
-
10
11
  export class EventNotifier<TIO extends IOLike> {
11
12
  public subjects = new Map<
12
13
  keyof InferIOOutput<TIO>,