@fluidframework/presence 2.32.0 → 2.33.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/README.md +24 -24
- package/dist/alpha.d.ts +29 -25
- package/dist/baseTypes.d.ts +3 -3
- package/dist/baseTypes.js.map +1 -1
- package/dist/broadcastControls.d.ts +1 -1
- package/dist/broadcastControls.js.map +1 -1
- package/dist/datastorePresenceManagerFactory.d.ts +5 -5
- package/dist/datastorePresenceManagerFactory.d.ts.map +1 -1
- package/dist/datastorePresenceManagerFactory.js +6 -6
- package/dist/datastorePresenceManagerFactory.js.map +1 -1
- package/dist/experimentalAccess.d.ts +4 -4
- package/dist/experimentalAccess.d.ts.map +1 -1
- package/dist/experimentalAccess.js +6 -6
- package/dist/experimentalAccess.js.map +1 -1
- package/dist/exposedInternalTypes.d.ts +1 -1
- package/dist/exposedInternalTypes.js.map +1 -1
- package/dist/exposedUtilityTypes.d.ts +0 -8
- package/dist/exposedUtilityTypes.d.ts.map +1 -1
- package/dist/exposedUtilityTypes.js.map +1 -1
- package/dist/index.d.ts +8 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -8
- package/dist/index.js.map +1 -1
- package/dist/internalTypes.d.ts +3 -3
- package/dist/internalTypes.d.ts.map +1 -1
- package/dist/internalTypes.js.map +1 -1
- package/dist/internalUtils.d.ts +5 -0
- package/dist/internalUtils.d.ts.map +1 -1
- package/dist/internalUtils.js +8 -1
- package/dist/internalUtils.js.map +1 -1
- package/dist/latestMapValueManager.d.ts +57 -39
- package/dist/latestMapValueManager.d.ts.map +1 -1
- package/dist/latestMapValueManager.js +48 -39
- package/dist/latestMapValueManager.js.map +1 -1
- package/dist/latestValueManager.d.ts +38 -20
- package/dist/latestValueManager.d.ts.map +1 -1
- package/dist/latestValueManager.js +32 -28
- package/dist/latestValueManager.js.map +1 -1
- package/dist/latestValueTypes.d.ts +9 -10
- package/dist/latestValueTypes.d.ts.map +1 -1
- package/dist/latestValueTypes.js.map +1 -1
- package/dist/notificationsManager.d.ts +15 -10
- package/dist/notificationsManager.d.ts.map +1 -1
- package/dist/notificationsManager.js +8 -5
- package/dist/notificationsManager.js.map +1 -1
- package/dist/presence.d.ts +82 -66
- package/dist/presence.d.ts.map +1 -1
- package/dist/presence.js +5 -5
- package/dist/presence.js.map +1 -1
- package/dist/presenceDatastoreManager.d.ts +12 -10
- package/dist/presenceDatastoreManager.d.ts.map +1 -1
- package/dist/presenceDatastoreManager.js +10 -8
- package/dist/presenceDatastoreManager.js.map +1 -1
- package/dist/presenceManager.d.ts +2 -2
- package/dist/presenceManager.d.ts.map +1 -1
- package/dist/presenceManager.js +14 -22
- package/dist/presenceManager.js.map +1 -1
- package/dist/presenceStates.d.ts +14 -13
- package/dist/presenceStates.d.ts.map +1 -1
- package/dist/presenceStates.js +22 -15
- package/dist/presenceStates.js.map +1 -1
- package/dist/stateDatastore.d.ts +5 -4
- package/dist/stateDatastore.d.ts.map +1 -1
- package/dist/stateDatastore.js.map +1 -1
- package/dist/stateFactory.d.ts +22 -0
- package/dist/stateFactory.d.ts.map +1 -0
- package/dist/stateFactory.js +25 -0
- package/dist/stateFactory.js.map +1 -0
- package/dist/systemWorkspace.d.ts +7 -7
- package/dist/systemWorkspace.d.ts.map +1 -1
- package/dist/systemWorkspace.js +23 -23
- package/dist/systemWorkspace.js.map +1 -1
- package/dist/types.d.ts +50 -33
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +0 -1
- package/dist/types.js.map +1 -1
- package/lib/alpha.d.ts +29 -25
- package/lib/baseTypes.d.ts +3 -3
- package/lib/baseTypes.js.map +1 -1
- package/lib/broadcastControls.d.ts +1 -1
- package/lib/broadcastControls.js.map +1 -1
- package/lib/datastorePresenceManagerFactory.d.ts +5 -5
- package/lib/datastorePresenceManagerFactory.d.ts.map +1 -1
- package/lib/datastorePresenceManagerFactory.js +4 -4
- package/lib/datastorePresenceManagerFactory.js.map +1 -1
- package/lib/experimentalAccess.d.ts +4 -4
- package/lib/experimentalAccess.d.ts.map +1 -1
- package/lib/experimentalAccess.js +4 -4
- package/lib/experimentalAccess.js.map +1 -1
- package/lib/exposedInternalTypes.d.ts +1 -1
- package/lib/exposedInternalTypes.js.map +1 -1
- package/lib/exposedUtilityTypes.d.ts +0 -8
- package/lib/exposedUtilityTypes.d.ts.map +1 -1
- package/lib/exposedUtilityTypes.js.map +1 -1
- package/lib/index.d.ts +8 -7
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +4 -5
- package/lib/index.js.map +1 -1
- package/lib/internalTypes.d.ts +3 -3
- package/lib/internalTypes.d.ts.map +1 -1
- package/lib/internalTypes.js.map +1 -1
- package/lib/internalUtils.d.ts +5 -0
- package/lib/internalUtils.d.ts.map +1 -1
- package/lib/internalUtils.js +6 -0
- package/lib/internalUtils.js.map +1 -1
- package/lib/latestMapValueManager.d.ts +57 -39
- package/lib/latestMapValueManager.d.ts.map +1 -1
- package/lib/latestMapValueManager.js +47 -38
- package/lib/latestMapValueManager.js.map +1 -1
- package/lib/latestValueManager.d.ts +38 -20
- package/lib/latestValueManager.d.ts.map +1 -1
- package/lib/latestValueManager.js +31 -27
- package/lib/latestValueManager.js.map +1 -1
- package/lib/latestValueTypes.d.ts +9 -10
- package/lib/latestValueTypes.d.ts.map +1 -1
- package/lib/latestValueTypes.js.map +1 -1
- package/lib/notificationsManager.d.ts +15 -10
- package/lib/notificationsManager.d.ts.map +1 -1
- package/lib/notificationsManager.js +8 -5
- package/lib/notificationsManager.js.map +1 -1
- package/lib/presence.d.ts +82 -66
- package/lib/presence.d.ts.map +1 -1
- package/lib/presence.js +4 -4
- package/lib/presence.js.map +1 -1
- package/lib/presenceDatastoreManager.d.ts +12 -10
- package/lib/presenceDatastoreManager.d.ts.map +1 -1
- package/lib/presenceDatastoreManager.js +10 -8
- package/lib/presenceDatastoreManager.js.map +1 -1
- package/lib/presenceManager.d.ts +2 -2
- package/lib/presenceManager.d.ts.map +1 -1
- package/lib/presenceManager.js +14 -22
- package/lib/presenceManager.js.map +1 -1
- package/lib/presenceStates.d.ts +14 -13
- package/lib/presenceStates.d.ts.map +1 -1
- package/lib/presenceStates.js +22 -15
- package/lib/presenceStates.js.map +1 -1
- package/lib/stateDatastore.d.ts +5 -4
- package/lib/stateDatastore.d.ts.map +1 -1
- package/lib/stateDatastore.js.map +1 -1
- package/lib/stateFactory.d.ts +22 -0
- package/lib/stateFactory.d.ts.map +1 -0
- package/lib/stateFactory.js +22 -0
- package/lib/stateFactory.js.map +1 -0
- package/lib/systemWorkspace.d.ts +7 -7
- package/lib/systemWorkspace.d.ts.map +1 -1
- package/lib/systemWorkspace.js +24 -24
- package/lib/systemWorkspace.js.map +1 -1
- package/lib/tsdoc-metadata.json +1 -1
- package/lib/types.d.ts +50 -33
- package/lib/types.d.ts.map +1 -1
- package/lib/types.js +0 -1
- package/lib/types.js.map +1 -1
- package/package.json +19 -19
|
@@ -3,46 +3,49 @@
|
|
|
3
3
|
* Licensed under the MIT License.
|
|
4
4
|
*/
|
|
5
5
|
import type { Listenable } from "@fluidframework/core-interfaces";
|
|
6
|
-
import type { JsonDeserialized, JsonSerializable } from "@fluidframework/core-interfaces/internal/exposedUtilityTypes";
|
|
6
|
+
import type { DeepReadonly, JsonDeserialized, JsonSerializable } from "@fluidframework/core-interfaces/internal/exposedUtilityTypes";
|
|
7
7
|
import type { BroadcastControls, BroadcastControlSettings } from "./broadcastControls.js";
|
|
8
8
|
import type { InternalTypes } from "./exposedInternalTypes.js";
|
|
9
|
-
import type {
|
|
10
|
-
import type {
|
|
11
|
-
import type { ISessionClient } from "./presence.js";
|
|
9
|
+
import type { LatestClientData, LatestData } from "./latestValueTypes.js";
|
|
10
|
+
import type { Attendee, Presence } from "./presence.js";
|
|
12
11
|
/**
|
|
13
12
|
* @sealed
|
|
14
13
|
* @alpha
|
|
15
14
|
*/
|
|
16
|
-
export interface
|
|
15
|
+
export interface LatestRawEvents<T> {
|
|
17
16
|
/**
|
|
18
17
|
* Raised when remote client's value is updated, which may be the same value.
|
|
19
18
|
*
|
|
20
19
|
* @eventProperty
|
|
21
20
|
*/
|
|
22
|
-
|
|
21
|
+
remoteUpdated: (update: LatestClientData<T>) => void;
|
|
23
22
|
/**
|
|
24
23
|
* Raised when local client's value is updated, which may be the same value.
|
|
25
24
|
*
|
|
26
25
|
* @eventProperty
|
|
27
26
|
*/
|
|
28
27
|
localUpdated: (update: {
|
|
29
|
-
value:
|
|
28
|
+
value: DeepReadonly<JsonSerializable<T> & JsonDeserialized<T>>;
|
|
30
29
|
}) => void;
|
|
31
30
|
}
|
|
32
31
|
/**
|
|
33
|
-
*
|
|
32
|
+
* State that provides the latest known value from this client to others and read access to their values.
|
|
34
33
|
* All participant clients must provide a value.
|
|
35
34
|
*
|
|
36
|
-
* @remarks Create using {@link
|
|
35
|
+
* @remarks Create using {@link StateFactory.latest} registered to {@link StatesWorkspace}.
|
|
37
36
|
*
|
|
38
37
|
* @sealed
|
|
39
38
|
* @alpha
|
|
40
39
|
*/
|
|
41
|
-
export interface
|
|
40
|
+
export interface LatestRaw<T> {
|
|
42
41
|
/**
|
|
43
|
-
*
|
|
42
|
+
* Containing {@link Presence}
|
|
44
43
|
*/
|
|
45
|
-
readonly
|
|
44
|
+
readonly presence: Presence;
|
|
45
|
+
/**
|
|
46
|
+
* Events for LatestRaw.
|
|
47
|
+
*/
|
|
48
|
+
readonly events: Listenable<LatestRawEvents<T>>;
|
|
46
49
|
/**
|
|
47
50
|
* Controls for management of sending updates.
|
|
48
51
|
*/
|
|
@@ -53,25 +56,40 @@ export interface LatestValueManager<T> {
|
|
|
53
56
|
* @remarks Manager assumes ownership of the value and its references. Make a deep clone before
|
|
54
57
|
* setting, if needed. No comparison is done to detect changes; all sets are transmitted.
|
|
55
58
|
*/
|
|
56
|
-
get local():
|
|
59
|
+
get local(): DeepReadonly<JsonDeserialized<T>>;
|
|
57
60
|
set local(value: JsonSerializable<T> & JsonDeserialized<T>);
|
|
58
61
|
/**
|
|
59
62
|
* Iterable access to remote clients' values.
|
|
60
63
|
*/
|
|
61
|
-
|
|
64
|
+
getRemotes(): IterableIterator<LatestClientData<T>>;
|
|
65
|
+
/**
|
|
66
|
+
* Array of {@link Attendee}s that have provided states.
|
|
67
|
+
*/
|
|
68
|
+
getStateAttendees(): Attendee[];
|
|
69
|
+
/**
|
|
70
|
+
* Access to a specific attendee's value.
|
|
71
|
+
*/
|
|
72
|
+
getRemote(attendee: Attendee): LatestData<T>;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Arguments that are passed to the {@link StateFactory.latest} function.
|
|
76
|
+
*
|
|
77
|
+
* @alpha
|
|
78
|
+
*/
|
|
79
|
+
export interface LatestArguments<T extends object | null> {
|
|
62
80
|
/**
|
|
63
|
-
*
|
|
81
|
+
* The initial value of the local state.
|
|
64
82
|
*/
|
|
65
|
-
|
|
83
|
+
local: JsonSerializable<T> & JsonDeserialized<T> & (object | null);
|
|
66
84
|
/**
|
|
67
|
-
*
|
|
85
|
+
* See {@link BroadcastControlSettings}.
|
|
68
86
|
*/
|
|
69
|
-
|
|
87
|
+
settings?: BroadcastControlSettings | undefined;
|
|
70
88
|
}
|
|
71
89
|
/**
|
|
72
|
-
* Factory for creating a {@link
|
|
90
|
+
* Factory for creating a {@link LatestRaw} State object.
|
|
73
91
|
*
|
|
74
92
|
* @alpha
|
|
75
93
|
*/
|
|
76
|
-
export declare function
|
|
94
|
+
export declare function latest<T extends object | null, Key extends string = string>(args: LatestArguments<T>): InternalTypes.ManagerFactory<Key, InternalTypes.ValueRequiredState<T>, LatestRaw<T>>;
|
|
77
95
|
//# sourceMappingURL=latestValueManager.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"latestValueManager.d.ts","sourceRoot":"","sources":["../src/latestValueManager.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iCAAiC,CAAC;AAClE,OAAO,KAAK,EACX,gBAAgB,EAChB,gBAAgB,EAChB,MAAM,8DAA8D,CAAC;AAGtE,OAAO,KAAK,EAAE,iBAAiB,EAAE,wBAAwB,EAAE,MAAM,wBAAwB,CAAC;AAE1F,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;
|
|
1
|
+
{"version":3,"file":"latestValueManager.d.ts","sourceRoot":"","sources":["../src/latestValueManager.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iCAAiC,CAAC;AAClE,OAAO,KAAK,EACX,YAAY,EACZ,gBAAgB,EAChB,gBAAgB,EAChB,MAAM,8DAA8D,CAAC;AAGtE,OAAO,KAAK,EAAE,iBAAiB,EAAE,wBAAwB,EAAE,MAAM,wBAAwB,CAAC;AAE1F,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAG/D,OAAO,KAAK,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAC1E,OAAO,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAIxD;;;GAGG;AACH,MAAM,WAAW,eAAe,CAAC,CAAC;IACjC;;;;OAIG;IACH,aAAa,EAAE,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC;IAErD;;;;OAIG;IACH,YAAY,EAAE,CAAC,MAAM,EAAE;QACtB,KAAK,EAAE,YAAY,CAAC,gBAAgB,CAAC,CAAC,CAAC,GAAG,gBAAgB,CAAC,CAAC,CAAC,CAAC,CAAC;KAC/D,KAAK,IAAI,CAAC;CACX;AAED;;;;;;;;GAQG;AACH,MAAM,WAAW,SAAS,CAAC,CAAC;IAC3B;;OAEG;IACH,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC;IAE5B;;OAEG;IACH,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC,CAAC;IAEhD;;OAEG;IACH,QAAQ,CAAC,QAAQ,EAAE,iBAAiB,CAAC;IAErC;;;;;OAKG;IACH,IAAI,KAAK,IAAI,YAAY,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,CAAC;IAC/C,IAAI,KAAK,CAAC,KAAK,EAAE,gBAAgB,CAAC,CAAC,CAAC,GAAG,gBAAgB,CAAC,CAAC,CAAC,EAAE;IAE5D;;OAEG;IACH,UAAU,IAAI,gBAAgB,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,CAAC;IACpD;;OAEG;IACH,iBAAiB,IAAI,QAAQ,EAAE,CAAC;IAChC;;OAEG;IACH,SAAS,CAAC,QAAQ,EAAE,QAAQ,GAAG,UAAU,CAAC,CAAC,CAAC,CAAC;CAC7C;AA2FD;;;;GAIG;AACH,MAAM,WAAW,eAAe,CAAC,CAAC,SAAS,MAAM,GAAG,IAAI;IACvD;;OAEG;IAEH,KAAK,EAAE,gBAAgB,CAAC,CAAC,CAAC,GAAG,gBAAgB,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAEnE;;OAEG;IACH,QAAQ,CAAC,EAAE,wBAAwB,GAAG,SAAS,CAAC;CAChD;AAED;;;;GAIG;AACH,wBAAgB,MAAM,CAAC,CAAC,SAAS,MAAM,GAAG,IAAI,EAAE,GAAG,SAAS,MAAM,GAAG,MAAM,EAC1E,IAAI,EAAE,eAAe,CAAC,CAAC,CAAC,GACtB,aAAa,CAAC,cAAc,CAAC,GAAG,EAAE,aAAa,CAAC,kBAAkB,CAAC,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC,CAAC,CAAC,CA0BtF"}
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
import { createEmitter } from "@fluid-internal/client-utils";
|
|
6
6
|
import { shallowCloneObject } from "@fluidframework/core-utils/internal";
|
|
7
7
|
import { OptionalBroadcastControl } from "./broadcastControls.js";
|
|
8
|
-
import { objectEntries } from "./internalUtils.js";
|
|
8
|
+
import { asDeeplyReadonly, objectEntries } from "./internalUtils.js";
|
|
9
9
|
import { datastoreFromHandle } from "./stateDatastore.js";
|
|
10
10
|
import { brandIVM } from "./valueManager.js";
|
|
11
11
|
class LatestValueManagerImpl {
|
|
@@ -16,8 +16,11 @@ class LatestValueManagerImpl {
|
|
|
16
16
|
this.events = createEmitter();
|
|
17
17
|
this.controls = new OptionalBroadcastControl(controlSettings);
|
|
18
18
|
}
|
|
19
|
+
get presence() {
|
|
20
|
+
return this.datastore.presence;
|
|
21
|
+
}
|
|
19
22
|
get local() {
|
|
20
|
-
return this.value.value;
|
|
23
|
+
return asDeeplyReadonly(this.value.value);
|
|
21
24
|
}
|
|
22
25
|
set local(value) {
|
|
23
26
|
this.value.rev += 1;
|
|
@@ -26,70 +29,71 @@ class LatestValueManagerImpl {
|
|
|
26
29
|
this.datastore.localUpdate(this.key, this.value, {
|
|
27
30
|
allowableUpdateLatencyMs: this.controls.allowableUpdateLatencyMs,
|
|
28
31
|
});
|
|
29
|
-
this.events.emit("localUpdated", { value });
|
|
32
|
+
this.events.emit("localUpdated", { value: asDeeplyReadonly(value) });
|
|
30
33
|
}
|
|
31
|
-
*
|
|
34
|
+
*getRemotes() {
|
|
32
35
|
const allKnownStates = this.datastore.knownValues(this.key);
|
|
33
|
-
for (const [
|
|
34
|
-
if (
|
|
36
|
+
for (const [attendeeId, value] of objectEntries(allKnownStates.states)) {
|
|
37
|
+
if (attendeeId !== allKnownStates.self) {
|
|
35
38
|
yield {
|
|
36
|
-
|
|
37
|
-
value: value.value,
|
|
39
|
+
attendee: this.datastore.lookupClient(attendeeId),
|
|
40
|
+
value: asDeeplyReadonly(value.value),
|
|
38
41
|
metadata: { revision: value.rev, timestamp: value.timestamp },
|
|
39
42
|
};
|
|
40
43
|
}
|
|
41
44
|
}
|
|
42
45
|
}
|
|
43
|
-
|
|
46
|
+
getStateAttendees() {
|
|
44
47
|
const allKnownStates = this.datastore.knownValues(this.key);
|
|
45
48
|
return Object.keys(allKnownStates.states)
|
|
46
|
-
.filter((
|
|
47
|
-
.map((
|
|
49
|
+
.filter((attendeeId) => attendeeId !== allKnownStates.self)
|
|
50
|
+
.map((attendeeId) => this.datastore.lookupClient(attendeeId));
|
|
48
51
|
}
|
|
49
|
-
|
|
52
|
+
getRemote(attendee) {
|
|
50
53
|
const allKnownStates = this.datastore.knownValues(this.key);
|
|
51
|
-
const clientState = allKnownStates.states[
|
|
54
|
+
const clientState = allKnownStates.states[attendee.attendeeId];
|
|
52
55
|
if (clientState === undefined) {
|
|
53
56
|
throw new Error("No entry for clientId");
|
|
54
57
|
}
|
|
55
58
|
return {
|
|
56
|
-
value: clientState.value,
|
|
59
|
+
value: asDeeplyReadonly(clientState.value),
|
|
57
60
|
metadata: { revision: clientState.rev, timestamp: Date.now() },
|
|
58
61
|
};
|
|
59
62
|
}
|
|
60
|
-
update(
|
|
63
|
+
update(attendee, _received, value) {
|
|
61
64
|
const allKnownStates = this.datastore.knownValues(this.key);
|
|
62
|
-
const
|
|
63
|
-
const currentState = allKnownStates.states[
|
|
65
|
+
const attendeeId = attendee.attendeeId;
|
|
66
|
+
const currentState = allKnownStates.states[attendeeId];
|
|
64
67
|
if (currentState !== undefined && currentState.rev >= value.rev) {
|
|
65
68
|
return [];
|
|
66
69
|
}
|
|
67
|
-
this.datastore.update(this.key,
|
|
70
|
+
this.datastore.update(this.key, attendeeId, value);
|
|
68
71
|
return [
|
|
69
|
-
() => this.events.emit("
|
|
70
|
-
|
|
71
|
-
value: value.value,
|
|
72
|
+
() => this.events.emit("remoteUpdated", {
|
|
73
|
+
attendee,
|
|
74
|
+
value: asDeeplyReadonly(value.value),
|
|
72
75
|
metadata: { revision: value.rev, timestamp: value.timestamp },
|
|
73
76
|
}),
|
|
74
77
|
];
|
|
75
78
|
}
|
|
76
79
|
}
|
|
77
80
|
/**
|
|
78
|
-
* Factory for creating a {@link
|
|
81
|
+
* Factory for creating a {@link LatestRaw} State object.
|
|
79
82
|
*
|
|
80
83
|
* @alpha
|
|
81
84
|
*/
|
|
82
|
-
export function
|
|
83
|
-
|
|
85
|
+
export function latest(args) {
|
|
86
|
+
const { local, settings } = args;
|
|
87
|
+
// Latest takes ownership of the initial local value but makes a shallow
|
|
84
88
|
// copy for basic protection.
|
|
85
89
|
const value = {
|
|
86
90
|
rev: 0,
|
|
87
91
|
timestamp: Date.now(),
|
|
88
|
-
value: shallowCloneObject(
|
|
92
|
+
value: local === null ? local : shallowCloneObject(local),
|
|
89
93
|
};
|
|
90
94
|
const factory = (key, datastoreHandle) => ({
|
|
91
|
-
initialData: { value, allowableUpdateLatencyMs:
|
|
92
|
-
manager: brandIVM(new LatestValueManagerImpl(key, datastoreFromHandle(datastoreHandle), value,
|
|
95
|
+
initialData: { value, allowableUpdateLatencyMs: settings?.allowableUpdateLatencyMs },
|
|
96
|
+
manager: brandIVM(new LatestValueManagerImpl(key, datastoreFromHandle(datastoreHandle), value, settings)),
|
|
93
97
|
});
|
|
94
98
|
return Object.assign(factory, { instanceBase: LatestValueManagerImpl });
|
|
95
99
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"latestValueManager.js","sourceRoot":"","sources":["../src/latestValueManager.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { createEmitter } from \"@fluid-internal/client-utils\";\nimport type { Listenable } from \"@fluidframework/core-interfaces\";\nimport type {\n\tJsonDeserialized,\n\tJsonSerializable,\n} from \"@fluidframework/core-interfaces/internal/exposedUtilityTypes\";\nimport { shallowCloneObject } from \"@fluidframework/core-utils/internal\";\n\nimport type { BroadcastControls, BroadcastControlSettings } from \"./broadcastControls.js\";\nimport { OptionalBroadcastControl } from \"./broadcastControls.js\";\nimport type { InternalTypes } from \"./exposedInternalTypes.js\";\nimport type { InternalUtilityTypes } from \"./exposedUtilityTypes.js\";\nimport type { PostUpdateAction, ValueManager } from \"./internalTypes.js\";\nimport { objectEntries } from \"./internalUtils.js\";\nimport type { LatestValueClientData, LatestValueData } from \"./latestValueTypes.js\";\nimport type { ISessionClient } from \"./presence.js\";\nimport { datastoreFromHandle, type StateDatastore } from \"./stateDatastore.js\";\nimport { brandIVM } from \"./valueManager.js\";\n\n/**\n * @sealed\n * @alpha\n */\nexport interface LatestValueManagerEvents<T> {\n\t/**\n\t * Raised when remote client's value is updated, which may be the same value.\n\t *\n\t * @eventProperty\n\t */\n\tupdated: (update: LatestValueClientData<T>) => void;\n\n\t/**\n\t * Raised when local client's value is updated, which may be the same value.\n\t *\n\t * @eventProperty\n\t */\n\tlocalUpdated: (update: {\n\t\tvalue: InternalUtilityTypes.FullyReadonly<JsonSerializable<T> & JsonDeserialized<T>>;\n\t}) => void;\n}\n\n/**\n * Value manager that provides the latest known value from this client to others and read access to their values.\n * All participant clients must provide a value.\n *\n * @remarks Create using {@link Latest} registered to {@link PresenceStates}.\n *\n * @sealed\n * @alpha\n */\nexport interface LatestValueManager<T> {\n\t/**\n\t * Events for Latest value manager.\n\t */\n\treadonly events: Listenable<LatestValueManagerEvents<T>>;\n\n\t/**\n\t * Controls for management of sending updates.\n\t */\n\treadonly controls: BroadcastControls;\n\n\t/**\n\t * Current state for this client.\n\t * State for this client that will be transmitted to all other connected clients.\n\t * @remarks Manager assumes ownership of the value and its references. Make a deep clone before\n\t * setting, if needed. No comparison is done to detect changes; all sets are transmitted.\n\t */\n\tget local(): InternalUtilityTypes.FullyReadonly<JsonDeserialized<T>>;\n\tset local(value: JsonSerializable<T> & JsonDeserialized<T>);\n\n\t/**\n\t * Iterable access to remote clients' values.\n\t */\n\tclientValues(): IterableIterator<LatestValueClientData<T>>;\n\t/**\n\t * Array of known remote clients.\n\t */\n\tclients(): ISessionClient[];\n\t/**\n\t * Access to a specific client's value.\n\t */\n\tclientValue(client: ISessionClient): LatestValueData<T>;\n}\n\nclass LatestValueManagerImpl<T, Key extends string>\n\timplements\n\t\tLatestValueManager<T>,\n\t\tRequired<ValueManager<T, InternalTypes.ValueRequiredState<T>>>\n{\n\tpublic readonly events = createEmitter<LatestValueManagerEvents<T>>();\n\tpublic readonly controls: OptionalBroadcastControl;\n\n\tpublic constructor(\n\t\tprivate readonly key: Key,\n\t\tprivate readonly datastore: StateDatastore<Key, InternalTypes.ValueRequiredState<T>>,\n\t\tpublic readonly value: InternalTypes.ValueRequiredState<T>,\n\t\tcontrolSettings: BroadcastControlSettings | undefined,\n\t) {\n\t\tthis.controls = new OptionalBroadcastControl(controlSettings);\n\t}\n\n\tpublic get local(): InternalUtilityTypes.FullyReadonly<JsonDeserialized<T>> {\n\t\treturn this.value.value;\n\t}\n\n\tpublic set local(value: JsonSerializable<T> & JsonDeserialized<T>) {\n\t\tthis.value.rev += 1;\n\t\tthis.value.timestamp = Date.now();\n\t\tthis.value.value = value;\n\t\tthis.datastore.localUpdate(this.key, this.value, {\n\t\t\tallowableUpdateLatencyMs: this.controls.allowableUpdateLatencyMs,\n\t\t});\n\n\t\tthis.events.emit(\"localUpdated\", { value });\n\t}\n\n\tpublic *clientValues(): IterableIterator<LatestValueClientData<T>> {\n\t\tconst allKnownStates = this.datastore.knownValues(this.key);\n\t\tfor (const [clientSessionId, value] of objectEntries(allKnownStates.states)) {\n\t\t\tif (clientSessionId !== allKnownStates.self) {\n\t\t\t\tyield {\n\t\t\t\t\tclient: this.datastore.lookupClient(clientSessionId),\n\t\t\t\t\tvalue: value.value,\n\t\t\t\t\tmetadata: { revision: value.rev, timestamp: value.timestamp },\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic clients(): ISessionClient[] {\n\t\tconst allKnownStates = this.datastore.knownValues(this.key);\n\t\treturn Object.keys(allKnownStates.states)\n\t\t\t.filter((clientSessionId) => clientSessionId !== allKnownStates.self)\n\t\t\t.map((clientSessionId) => this.datastore.lookupClient(clientSessionId));\n\t}\n\n\tpublic clientValue(client: ISessionClient): LatestValueData<T> {\n\t\tconst allKnownStates = this.datastore.knownValues(this.key);\n\t\tconst clientState = allKnownStates.states[client.sessionId];\n\t\tif (clientState === undefined) {\n\t\t\tthrow new Error(\"No entry for clientId\");\n\t\t}\n\t\treturn {\n\t\t\tvalue: clientState.value,\n\t\t\tmetadata: { revision: clientState.rev, timestamp: Date.now() },\n\t\t};\n\t}\n\n\tpublic update(\n\t\tclient: ISessionClient,\n\t\t_received: number,\n\t\tvalue: InternalTypes.ValueRequiredState<T>,\n\t): PostUpdateAction[] {\n\t\tconst allKnownStates = this.datastore.knownValues(this.key);\n\t\tconst clientSessionId = client.sessionId;\n\t\tconst currentState = allKnownStates.states[clientSessionId];\n\t\tif (currentState !== undefined && currentState.rev >= value.rev) {\n\t\t\treturn [];\n\t\t}\n\t\tthis.datastore.update(this.key, clientSessionId, value);\n\t\treturn [\n\t\t\t() =>\n\t\t\t\tthis.events.emit(\"updated\", {\n\t\t\t\t\tclient,\n\t\t\t\t\tvalue: value.value,\n\t\t\t\t\tmetadata: { revision: value.rev, timestamp: value.timestamp },\n\t\t\t\t}),\n\t\t];\n\t}\n}\n\n/**\n * Factory for creating a {@link LatestValueManager}.\n *\n * @alpha\n */\nexport function Latest<T extends object, Key extends string = string>(\n\tinitialValue: JsonSerializable<T> & JsonDeserialized<T> & object,\n\tcontrols?: BroadcastControlSettings,\n): InternalTypes.ManagerFactory<\n\tKey,\n\tInternalTypes.ValueRequiredState<T>,\n\tLatestValueManager<T>\n> {\n\t// LatestValueManager takes ownership of initialValue but makes a shallow\n\t// copy for basic protection.\n\tconst value: InternalTypes.ValueRequiredState<T> = {\n\t\trev: 0,\n\t\ttimestamp: Date.now(),\n\t\tvalue: shallowCloneObject(initialValue),\n\t};\n\tconst factory = (\n\t\tkey: Key,\n\t\tdatastoreHandle: InternalTypes.StateDatastoreHandle<\n\t\t\tKey,\n\t\t\tInternalTypes.ValueRequiredState<T>\n\t\t>,\n\t): {\n\t\tinitialData: { value: typeof value; allowableUpdateLatencyMs: number | undefined };\n\t\tmanager: InternalTypes.StateValue<LatestValueManager<T>>;\n\t} => ({\n\t\tinitialData: { value, allowableUpdateLatencyMs: controls?.allowableUpdateLatencyMs },\n\t\tmanager: brandIVM<LatestValueManagerImpl<T, Key>, T, InternalTypes.ValueRequiredState<T>>(\n\t\t\tnew LatestValueManagerImpl(key, datastoreFromHandle(datastoreHandle), value, controls),\n\t\t),\n\t});\n\treturn Object.assign(factory, { instanceBase: LatestValueManagerImpl });\n}\n"]}
|
|
1
|
+
{"version":3,"file":"latestValueManager.js","sourceRoot":"","sources":["../src/latestValueManager.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { createEmitter } from \"@fluid-internal/client-utils\";\nimport type { Listenable } from \"@fluidframework/core-interfaces\";\nimport type {\n\tDeepReadonly,\n\tJsonDeserialized,\n\tJsonSerializable,\n} from \"@fluidframework/core-interfaces/internal/exposedUtilityTypes\";\nimport { shallowCloneObject } from \"@fluidframework/core-utils/internal\";\n\nimport type { BroadcastControls, BroadcastControlSettings } from \"./broadcastControls.js\";\nimport { OptionalBroadcastControl } from \"./broadcastControls.js\";\nimport type { InternalTypes } from \"./exposedInternalTypes.js\";\nimport type { PostUpdateAction, ValueManager } from \"./internalTypes.js\";\nimport { asDeeplyReadonly, objectEntries } from \"./internalUtils.js\";\nimport type { LatestClientData, LatestData } from \"./latestValueTypes.js\";\nimport type { Attendee, Presence } from \"./presence.js\";\nimport { datastoreFromHandle, type StateDatastore } from \"./stateDatastore.js\";\nimport { brandIVM } from \"./valueManager.js\";\n\n/**\n * @sealed\n * @alpha\n */\nexport interface LatestRawEvents<T> {\n\t/**\n\t * Raised when remote client's value is updated, which may be the same value.\n\t *\n\t * @eventProperty\n\t */\n\tremoteUpdated: (update: LatestClientData<T>) => void;\n\n\t/**\n\t * Raised when local client's value is updated, which may be the same value.\n\t *\n\t * @eventProperty\n\t */\n\tlocalUpdated: (update: {\n\t\tvalue: DeepReadonly<JsonSerializable<T> & JsonDeserialized<T>>;\n\t}) => void;\n}\n\n/**\n * State that provides the latest known value from this client to others and read access to their values.\n * All participant clients must provide a value.\n *\n * @remarks Create using {@link StateFactory.latest} registered to {@link StatesWorkspace}.\n *\n * @sealed\n * @alpha\n */\nexport interface LatestRaw<T> {\n\t/**\n\t * Containing {@link Presence}\n\t */\n\treadonly presence: Presence;\n\n\t/**\n\t * Events for LatestRaw.\n\t */\n\treadonly events: Listenable<LatestRawEvents<T>>;\n\n\t/**\n\t * Controls for management of sending updates.\n\t */\n\treadonly controls: BroadcastControls;\n\n\t/**\n\t * Current state for this client.\n\t * State for this client that will be transmitted to all other connected clients.\n\t * @remarks Manager assumes ownership of the value and its references. Make a deep clone before\n\t * setting, if needed. No comparison is done to detect changes; all sets are transmitted.\n\t */\n\tget local(): DeepReadonly<JsonDeserialized<T>>;\n\tset local(value: JsonSerializable<T> & JsonDeserialized<T>);\n\n\t/**\n\t * Iterable access to remote clients' values.\n\t */\n\tgetRemotes(): IterableIterator<LatestClientData<T>>;\n\t/**\n\t * Array of {@link Attendee}s that have provided states.\n\t */\n\tgetStateAttendees(): Attendee[];\n\t/**\n\t * Access to a specific attendee's value.\n\t */\n\tgetRemote(attendee: Attendee): LatestData<T>;\n}\n\nclass LatestValueManagerImpl<T, Key extends string>\n\timplements LatestRaw<T>, Required<ValueManager<T, InternalTypes.ValueRequiredState<T>>>\n{\n\tpublic readonly events = createEmitter<LatestRawEvents<T>>();\n\tpublic readonly controls: OptionalBroadcastControl;\n\n\tpublic constructor(\n\t\tprivate readonly key: Key,\n\t\tprivate readonly datastore: StateDatastore<Key, InternalTypes.ValueRequiredState<T>>,\n\t\tpublic readonly value: InternalTypes.ValueRequiredState<T>,\n\t\tcontrolSettings: BroadcastControlSettings | undefined,\n\t) {\n\t\tthis.controls = new OptionalBroadcastControl(controlSettings);\n\t}\n\n\tpublic get presence(): Presence {\n\t\treturn this.datastore.presence;\n\t}\n\n\tpublic get local(): DeepReadonly<JsonDeserialized<T>> {\n\t\treturn asDeeplyReadonly(this.value.value);\n\t}\n\n\tpublic set local(value: JsonSerializable<T> & JsonDeserialized<T>) {\n\t\tthis.value.rev += 1;\n\t\tthis.value.timestamp = Date.now();\n\t\tthis.value.value = value;\n\t\tthis.datastore.localUpdate(this.key, this.value, {\n\t\t\tallowableUpdateLatencyMs: this.controls.allowableUpdateLatencyMs,\n\t\t});\n\n\t\tthis.events.emit(\"localUpdated\", { value: asDeeplyReadonly(value) });\n\t}\n\n\tpublic *getRemotes(): IterableIterator<LatestClientData<T>> {\n\t\tconst allKnownStates = this.datastore.knownValues(this.key);\n\t\tfor (const [attendeeId, value] of objectEntries(allKnownStates.states)) {\n\t\t\tif (attendeeId !== allKnownStates.self) {\n\t\t\t\tyield {\n\t\t\t\t\tattendee: this.datastore.lookupClient(attendeeId),\n\t\t\t\t\tvalue: asDeeplyReadonly(value.value),\n\t\t\t\t\tmetadata: { revision: value.rev, timestamp: value.timestamp },\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic getStateAttendees(): Attendee[] {\n\t\tconst allKnownStates = this.datastore.knownValues(this.key);\n\t\treturn Object.keys(allKnownStates.states)\n\t\t\t.filter((attendeeId) => attendeeId !== allKnownStates.self)\n\t\t\t.map((attendeeId) => this.datastore.lookupClient(attendeeId));\n\t}\n\n\tpublic getRemote(attendee: Attendee): LatestData<T> {\n\t\tconst allKnownStates = this.datastore.knownValues(this.key);\n\t\tconst clientState = allKnownStates.states[attendee.attendeeId];\n\t\tif (clientState === undefined) {\n\t\t\tthrow new Error(\"No entry for clientId\");\n\t\t}\n\t\treturn {\n\t\t\tvalue: asDeeplyReadonly(clientState.value),\n\t\t\tmetadata: { revision: clientState.rev, timestamp: Date.now() },\n\t\t};\n\t}\n\n\tpublic update(\n\t\tattendee: Attendee,\n\t\t_received: number,\n\t\tvalue: InternalTypes.ValueRequiredState<T>,\n\t): PostUpdateAction[] {\n\t\tconst allKnownStates = this.datastore.knownValues(this.key);\n\t\tconst attendeeId = attendee.attendeeId;\n\t\tconst currentState = allKnownStates.states[attendeeId];\n\t\tif (currentState !== undefined && currentState.rev >= value.rev) {\n\t\t\treturn [];\n\t\t}\n\t\tthis.datastore.update(this.key, attendeeId, value);\n\t\treturn [\n\t\t\t() =>\n\t\t\t\tthis.events.emit(\"remoteUpdated\", {\n\t\t\t\t\tattendee,\n\t\t\t\t\tvalue: asDeeplyReadonly(value.value),\n\t\t\t\t\tmetadata: { revision: value.rev, timestamp: value.timestamp },\n\t\t\t\t}),\n\t\t];\n\t}\n}\n\n/**\n * Arguments that are passed to the {@link StateFactory.latest} function.\n *\n * @alpha\n */\nexport interface LatestArguments<T extends object | null> {\n\t/**\n\t * The initial value of the local state.\n\t */\n\t// eslint-disable-next-line @rushstack/no-new-null\n\tlocal: JsonSerializable<T> & JsonDeserialized<T> & (object | null);\n\n\t/**\n\t * See {@link BroadcastControlSettings}.\n\t */\n\tsettings?: BroadcastControlSettings | undefined;\n}\n\n/**\n * Factory for creating a {@link LatestRaw} State object.\n *\n * @alpha\n */\nexport function latest<T extends object | null, Key extends string = string>(\n\targs: LatestArguments<T>,\n): InternalTypes.ManagerFactory<Key, InternalTypes.ValueRequiredState<T>, LatestRaw<T>> {\n\tconst { local, settings } = args;\n\n\t// Latest takes ownership of the initial local value but makes a shallow\n\t// copy for basic protection.\n\tconst value: InternalTypes.ValueRequiredState<T> = {\n\t\trev: 0,\n\t\ttimestamp: Date.now(),\n\t\tvalue: local === null ? local : shallowCloneObject(local),\n\t};\n\tconst factory = (\n\t\tkey: Key,\n\t\tdatastoreHandle: InternalTypes.StateDatastoreHandle<\n\t\t\tKey,\n\t\t\tInternalTypes.ValueRequiredState<T>\n\t\t>,\n\t): {\n\t\tinitialData: { value: typeof value; allowableUpdateLatencyMs: number | undefined };\n\t\tmanager: InternalTypes.StateValue<LatestRaw<T>>;\n\t} => ({\n\t\tinitialData: { value, allowableUpdateLatencyMs: settings?.allowableUpdateLatencyMs },\n\t\tmanager: brandIVM<LatestValueManagerImpl<T, Key>, T, InternalTypes.ValueRequiredState<T>>(\n\t\t\tnew LatestValueManagerImpl(key, datastoreFromHandle(datastoreHandle), value, settings),\n\t\t),\n\t});\n\treturn Object.assign(factory, { instanceBase: LatestValueManagerImpl });\n}\n"]}
|
|
@@ -2,16 +2,15 @@
|
|
|
2
2
|
* Copyright (c) Microsoft Corporation and contributors. All rights reserved.
|
|
3
3
|
* Licensed under the MIT License.
|
|
4
4
|
*/
|
|
5
|
-
import type { JsonDeserialized } from "@fluidframework/core-interfaces/internal/exposedUtilityTypes";
|
|
6
|
-
import type {
|
|
7
|
-
import type { ISessionClient } from "./presence.js";
|
|
5
|
+
import type { DeepReadonly, JsonDeserialized } from "@fluidframework/core-interfaces/internal/exposedUtilityTypes";
|
|
6
|
+
import type { Attendee } from "./presence.js";
|
|
8
7
|
/**
|
|
9
8
|
* Metadata for the value state.
|
|
10
9
|
*
|
|
11
10
|
* @sealed
|
|
12
11
|
* @alpha
|
|
13
12
|
*/
|
|
14
|
-
export interface
|
|
13
|
+
export interface LatestMetadata {
|
|
15
14
|
/**
|
|
16
15
|
* The revision number for value that increases as value is changed.
|
|
17
16
|
*/
|
|
@@ -28,17 +27,17 @@ export interface LatestValueMetadata {
|
|
|
28
27
|
* @sealed
|
|
29
28
|
* @alpha
|
|
30
29
|
*/
|
|
31
|
-
export interface
|
|
32
|
-
value:
|
|
33
|
-
metadata:
|
|
30
|
+
export interface LatestData<T> {
|
|
31
|
+
value: DeepReadonly<JsonDeserialized<T>>;
|
|
32
|
+
metadata: LatestMetadata;
|
|
34
33
|
}
|
|
35
34
|
/**
|
|
36
|
-
* State of a specific
|
|
35
|
+
* State of a specific attendee's value and its metadata.
|
|
37
36
|
*
|
|
38
37
|
* @sealed
|
|
39
38
|
* @alpha
|
|
40
39
|
*/
|
|
41
|
-
export interface
|
|
42
|
-
|
|
40
|
+
export interface LatestClientData<T> extends LatestData<T> {
|
|
41
|
+
attendee: Attendee;
|
|
43
42
|
}
|
|
44
43
|
//# sourceMappingURL=latestValueTypes.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"latestValueTypes.d.ts","sourceRoot":"","sources":["../src/latestValueTypes.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"latestValueTypes.d.ts","sourceRoot":"","sources":["../src/latestValueTypes.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EACX,YAAY,EACZ,gBAAgB,EAChB,MAAM,8DAA8D,CAAC;AAEtE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAE9C;;;;;GAKG;AACH,MAAM,WAAW,cAAc;IAC9B;;OAEG;IACH,QAAQ,EAAE,MAAM,CAAC;IACjB;;;OAGG;IACH,SAAS,EAAE,MAAM,CAAC;CAClB;AAED;;;;;GAKG;AACH,MAAM,WAAW,UAAU,CAAC,CAAC;IAC5B,KAAK,EAAE,YAAY,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,CAAC;IACzC,QAAQ,EAAE,cAAc,CAAC;CACzB;AAED;;;;;GAKG;AACH,MAAM,WAAW,gBAAgB,CAAC,CAAC,CAAE,SAAQ,UAAU,CAAC,CAAC,CAAC;IACzD,QAAQ,EAAE,QAAQ,CAAC;CACnB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"latestValueTypes.js","sourceRoot":"","sources":["../src/latestValueTypes.ts"],"names":[],"mappings":"AAAA;;;GAGG","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport type {
|
|
1
|
+
{"version":3,"file":"latestValueTypes.js","sourceRoot":"","sources":["../src/latestValueTypes.ts"],"names":[],"mappings":"AAAA;;;GAGG","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport type {\n\tDeepReadonly,\n\tJsonDeserialized,\n} from \"@fluidframework/core-interfaces/internal/exposedUtilityTypes\";\n\nimport type { Attendee } from \"./presence.js\";\n\n/**\n * Metadata for the value state.\n *\n * @sealed\n * @alpha\n */\nexport interface LatestMetadata {\n\t/**\n\t * The revision number for value that increases as value is changed.\n\t */\n\trevision: number;\n\t/**\n\t * Local time when the value was last updated.\n\t * @remarks Currently this is a placeholder for future implementation.\n\t */\n\ttimestamp: number;\n}\n\n/**\n * State of a value and its metadata.\n *\n * @sealed\n * @alpha\n */\nexport interface LatestData<T> {\n\tvalue: DeepReadonly<JsonDeserialized<T>>;\n\tmetadata: LatestMetadata;\n}\n\n/**\n * State of a specific attendee's value and its metadata.\n *\n * @sealed\n * @alpha\n */\nexport interface LatestClientData<T> extends LatestData<T> {\n\tattendee: Attendee;\n}\n"]}
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
import type { Listenable, Off } from "@fluidframework/core-interfaces";
|
|
6
6
|
import type { InternalTypes } from "./exposedInternalTypes.js";
|
|
7
7
|
import type { InternalUtilityTypes } from "./exposedUtilityTypes.js";
|
|
8
|
-
import type {
|
|
8
|
+
import type { Attendee, Presence } from "./presence.js";
|
|
9
9
|
/**
|
|
10
10
|
* @sealed
|
|
11
11
|
* @alpha
|
|
@@ -16,7 +16,7 @@ export interface NotificationsManagerEvents {
|
|
|
16
16
|
*
|
|
17
17
|
* @eventProperty
|
|
18
18
|
*/
|
|
19
|
-
unattendedNotification: (name: string, sender:
|
|
19
|
+
unattendedNotification: (name: string, sender: Attendee, ...content: unknown[]) => void;
|
|
20
20
|
}
|
|
21
21
|
/**
|
|
22
22
|
* An object which allows the registration of listeners so that subscribers can be
|
|
@@ -37,7 +37,7 @@ export interface NotificationListenable<TListeners extends InternalUtilityTypes.
|
|
|
37
37
|
* @remarks Registering the exact same `listener` object for the same notification more than once will throw an error.
|
|
38
38
|
* If registering the same listener for the same notification multiple times is desired, consider using a wrapper function for the second subscription.
|
|
39
39
|
*/
|
|
40
|
-
on<K extends keyof InternalUtilityTypes.NotificationListeners<TListeners>>(notificationName: K, listener: (sender:
|
|
40
|
+
on<K extends keyof InternalUtilityTypes.NotificationListeners<TListeners>>(notificationName: K, listener: (sender: Attendee, ...args: InternalUtilityTypes.JsonDeserializedParameters<TListeners[K]>) => void): Off;
|
|
41
41
|
/**
|
|
42
42
|
* Deregister notification listener.
|
|
43
43
|
* @param notificationName - The name of the notification.
|
|
@@ -46,7 +46,7 @@ export interface NotificationListenable<TListeners extends InternalUtilityTypes.
|
|
|
46
46
|
*
|
|
47
47
|
* Listeners may also be deregistered by calling the {@link @fluidframework/core-interfaces#Off | deregistration function} returned when they are {@link NotificationListenable.on | registered}.
|
|
48
48
|
*/
|
|
49
|
-
off<K extends keyof InternalUtilityTypes.NotificationListeners<TListeners>>(notificationName: K, listener: (sender:
|
|
49
|
+
off<K extends keyof InternalUtilityTypes.NotificationListeners<TListeners>>(notificationName: K, listener: (sender: Attendee, ...args: InternalUtilityTypes.JsonDeserializedParameters<TListeners[K]>) => void): void;
|
|
50
50
|
}
|
|
51
51
|
/**
|
|
52
52
|
* Record of notification subscriptions.
|
|
@@ -55,7 +55,7 @@ export interface NotificationListenable<TListeners extends InternalUtilityTypes.
|
|
|
55
55
|
* @alpha
|
|
56
56
|
*/
|
|
57
57
|
export type NotificationSubscriptions<E extends InternalUtilityTypes.NotificationListeners<E>> = {
|
|
58
|
-
[K in string & keyof InternalUtilityTypes.NotificationListeners<E>]: (sender:
|
|
58
|
+
[K in string & keyof InternalUtilityTypes.NotificationListeners<E>]: (sender: Attendee, ...args: InternalUtilityTypes.JsonDeserializedParameters<E[K]>) => void;
|
|
59
59
|
};
|
|
60
60
|
/**
|
|
61
61
|
* Interface for a notification emitter that can send typed notification to other clients.
|
|
@@ -71,23 +71,28 @@ export interface NotificationEmitter<E extends InternalUtilityTypes.Notification
|
|
|
71
71
|
*/
|
|
72
72
|
broadcast<K extends string & keyof InternalUtilityTypes.NotificationListeners<E>>(notificationName: K, ...args: Parameters<E[K]>): void;
|
|
73
73
|
/**
|
|
74
|
-
* Emits a notification with the specified name and arguments, notifying a single
|
|
74
|
+
* Emits a notification with the specified name and arguments, notifying a single attendee.
|
|
75
75
|
* @param notificationName - the name of the notification to fire
|
|
76
|
-
* @param
|
|
76
|
+
* @param targetAttendee - the single attendee to notify
|
|
77
77
|
* @param args - the arguments sent with the notification
|
|
78
78
|
*/
|
|
79
|
-
unicast<K extends string & keyof InternalUtilityTypes.NotificationListeners<E>>(notificationName: K,
|
|
79
|
+
unicast<K extends string & keyof InternalUtilityTypes.NotificationListeners<E>>(notificationName: K, targetAttendee: Attendee, ...args: Parameters<E[K]>): void;
|
|
80
80
|
}
|
|
81
81
|
/**
|
|
82
|
-
*
|
|
82
|
+
* Provides notifications from this client to others and subscription
|
|
83
83
|
* to their notifications.
|
|
84
84
|
*
|
|
85
|
-
* @remarks Create using {@link Notifications} registered to
|
|
85
|
+
* @remarks Create using {@link Notifications} registered to
|
|
86
|
+
* {@link NotificationsWorkspace} or {@link StatesWorkspace}.
|
|
86
87
|
*
|
|
87
88
|
* @sealed
|
|
88
89
|
* @alpha
|
|
89
90
|
*/
|
|
90
91
|
export interface NotificationsManager<T extends InternalUtilityTypes.NotificationListeners<T>> {
|
|
92
|
+
/**
|
|
93
|
+
* Containing {@link Presence}
|
|
94
|
+
*/
|
|
95
|
+
readonly presence: Presence;
|
|
91
96
|
/**
|
|
92
97
|
* Events for Notifications manager.
|
|
93
98
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"notificationsManager.d.ts","sourceRoot":"","sources":["../src/notificationsManager.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,KAAK,EAAa,UAAU,EAAE,GAAG,EAAE,MAAM,iCAAiC,CAAC;AAGlF,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAC/D,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AAErE,OAAO,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"notificationsManager.d.ts","sourceRoot":"","sources":["../src/notificationsManager.ts"],"names":[],"mappings":"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"}
|
|
@@ -26,7 +26,7 @@ class NotificationsManagerImpl {
|
|
|
26
26
|
// This is a notification, so we want to send it immediately.
|
|
27
27
|
{ allowableUpdateLatencyMs: 0 });
|
|
28
28
|
},
|
|
29
|
-
unicast: (name,
|
|
29
|
+
unicast: (name, targetAttendee, ...args) => {
|
|
30
30
|
this.datastore.localUpdate(this.key, {
|
|
31
31
|
rev: 0,
|
|
32
32
|
timestamp: 0,
|
|
@@ -34,7 +34,7 @@ class NotificationsManagerImpl {
|
|
|
34
34
|
ignoreUnmonitored: true,
|
|
35
35
|
},
|
|
36
36
|
// This is a notification, so we want to send it immediately.
|
|
37
|
-
{ allowableUpdateLatencyMs: 0, targetClientId:
|
|
37
|
+
{ allowableUpdateLatencyMs: 0, targetClientId: targetAttendee.getConnectionId() });
|
|
38
38
|
},
|
|
39
39
|
};
|
|
40
40
|
// Workaround for types
|
|
@@ -55,17 +55,20 @@ class NotificationsManagerImpl {
|
|
|
55
55
|
}
|
|
56
56
|
}
|
|
57
57
|
}
|
|
58
|
-
|
|
58
|
+
get presence() {
|
|
59
|
+
return this.datastore.presence;
|
|
60
|
+
}
|
|
61
|
+
update(attendee, _received, value) {
|
|
59
62
|
const postUpdateActions = [];
|
|
60
63
|
const eventName = value.value.name;
|
|
61
64
|
if (this.notificationsInternal.hasListeners(eventName)) {
|
|
62
65
|
// Without schema validation, we don't know that the args are the correct type.
|
|
63
66
|
// For now we assume the user is sending the correct types and there is no corruption along the way.
|
|
64
|
-
const args = [
|
|
67
|
+
const args = [attendee, ...value.value.args];
|
|
65
68
|
postUpdateActions.push(() => this.notificationsInternal.emit(eventName, ...args));
|
|
66
69
|
}
|
|
67
70
|
else {
|
|
68
|
-
postUpdateActions.push(() => this.events.emit("unattendedNotification", value.value.name,
|
|
71
|
+
postUpdateActions.push(() => this.events.emit("unattendedNotification", value.value.name, attendee, ...value.value.args));
|
|
69
72
|
}
|
|
70
73
|
return postUpdateActions;
|
|
71
74
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"notificationsManager.js","sourceRoot":"","sources":["../src/notificationsManager.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { createEmitter } from \"@fluid-internal/client-utils\";\nimport type { Listeners, Listenable, Off } from \"@fluidframework/core-interfaces\";\nimport type { JsonTypeWith } from \"@fluidframework/core-interfaces/internal\";\n\nimport type { InternalTypes } from \"./exposedInternalTypes.js\";\nimport type { InternalUtilityTypes } from \"./exposedUtilityTypes.js\";\nimport type { PostUpdateAction, ValueManager } from \"./internalTypes.js\";\nimport type { ISessionClient } from \"./presence.js\";\nimport { datastoreFromHandle, type StateDatastore } from \"./stateDatastore.js\";\nimport { brandIVM } from \"./valueManager.js\";\n\n/**\n * @sealed\n * @alpha\n */\nexport interface NotificationsManagerEvents {\n\t/**\n\t * Raised when notification is received, but no subscribers were found.\n\t *\n\t * @eventProperty\n\t */\n\tunattendedNotification: (\n\t\tname: string,\n\t\tsender: ISessionClient,\n\t\t...content: unknown[]\n\t) => void;\n}\n\n/**\n * An object which allows the registration of listeners so that subscribers can be\n * notified when a notification happens.\n *\n * @sealed\n * @alpha\n */\nexport interface NotificationListenable<\n\tTListeners extends InternalUtilityTypes.NotificationListeners<TListeners>,\n> {\n\t/**\n\t * Register a notification listener.\n\t * @param notificationName - the name of the notification\n\t * @param listener - The listener function to run when the notification is fired.\n\t * @returns A {@link @fluidframework/core-interfaces#Off | function} which will deregister the listener when called.\n\t * Calling the deregistration function more than once will have no effect.\n\t *\n\t * Listeners may also be deregistered by passing the listener to {@link NotificationListenable.off | off()}.\n\t * @remarks Registering the exact same `listener` object for the same notification more than once will throw an error.\n\t * If registering the same listener for the same notification multiple times is desired, consider using a wrapper function for the second subscription.\n\t */\n\ton<K extends keyof InternalUtilityTypes.NotificationListeners<TListeners>>(\n\t\tnotificationName: K,\n\t\tlistener: (\n\t\t\tsender: ISessionClient,\n\t\t\t...args: InternalUtilityTypes.JsonDeserializedParameters<TListeners[K]>\n\t\t) => void,\n\t): Off;\n\n\t/**\n\t * Deregister notification listener.\n\t * @param notificationName - The name of the notification.\n\t * @param listener - The listener function to remove from the current set of notification listeners.\n\t * @remarks If `listener` is not currently registered, this method will have no effect.\n\t *\n\t * Listeners may also be deregistered by calling the {@link @fluidframework/core-interfaces#Off | deregistration function} returned when they are {@link NotificationListenable.on | registered}.\n\t */\n\toff<K extends keyof InternalUtilityTypes.NotificationListeners<TListeners>>(\n\t\tnotificationName: K,\n\t\tlistener: (\n\t\t\tsender: ISessionClient,\n\t\t\t...args: InternalUtilityTypes.JsonDeserializedParameters<TListeners[K]>\n\t\t) => void,\n\t): void;\n}\n\n/**\n * Record of notification subscriptions.\n *\n * @sealed\n * @alpha\n */\nexport type NotificationSubscriptions<\n\tE extends InternalUtilityTypes.NotificationListeners<E>,\n> = {\n\t[K in string & keyof InternalUtilityTypes.NotificationListeners<E>]: (\n\t\tsender: ISessionClient,\n\t\t...args: InternalUtilityTypes.JsonDeserializedParameters<E[K]>\n\t) => void;\n};\n\n/**\n * Interface for a notification emitter that can send typed notification to other clients.\n *\n * @sealed\n * @alpha\n */\nexport interface NotificationEmitter<E extends InternalUtilityTypes.NotificationListeners<E>> {\n\t/**\n\t * Emits a notification with the specified name and arguments, notifying all clients.\n\t * @param notificationName - the name of the notification to fire\n\t * @param args - the arguments sent with the notification\n\t */\n\tbroadcast<K extends string & keyof InternalUtilityTypes.NotificationListeners<E>>(\n\t\tnotificationName: K,\n\t\t...args: Parameters<E[K]>\n\t): void;\n\n\t/**\n\t * Emits a notification with the specified name and arguments, notifying a single client.\n\t * @param notificationName - the name of the notification to fire\n\t * @param targetClient - the single client to notify\n\t * @param args - the arguments sent with the notification\n\t */\n\tunicast<K extends string & keyof InternalUtilityTypes.NotificationListeners<E>>(\n\t\tnotificationName: K,\n\t\ttargetClient: ISessionClient,\n\t\t...args: Parameters<E[K]>\n\t): void;\n}\n\n/**\n * Value manager that provides notifications from this client to others and subscription\n * to their notifications.\n *\n * @remarks Create using {@link Notifications} registered to {@link PresenceStates}.\n *\n * @sealed\n * @alpha\n */\nexport interface NotificationsManager<\n\tT extends InternalUtilityTypes.NotificationListeners<T>,\n> {\n\t/**\n\t * Events for Notifications manager.\n\t */\n\treadonly events: Listenable<NotificationsManagerEvents>;\n\n\t/**\n\t * Send notifications to other clients.\n\t */\n\treadonly emit: NotificationEmitter<T>;\n\n\t/**\n\t * Provides subscription to notifications from other clients.\n\t */\n\treadonly notifications: NotificationListenable<T>;\n}\n\n/**\n * Object.keys retyped to support specific records keys and\n * branded string-based keys.\n */\nconst recordKeys = Object.keys as <K extends string>(o: Partial<Record<K, unknown>>) => K[];\n\nclass NotificationsManagerImpl<\n\tT extends InternalUtilityTypes.NotificationListeners<T>,\n\tKey extends string,\n> implements\n\t\tNotificationsManager<T>,\n\t\tValueManager<\n\t\t\tInternalTypes.NotificationType,\n\t\t\tInternalTypes.ValueRequiredState<InternalTypes.NotificationType>\n\t\t>\n{\n\tpublic readonly events = createEmitter<NotificationsManagerEvents>();\n\n\tpublic readonly emit: NotificationEmitter<T> = {\n\t\tbroadcast: (name, ...args) => {\n\t\t\tthis.datastore.localUpdate(\n\t\t\t\tthis.key,\n\t\t\t\t{\n\t\t\t\t\trev: 0,\n\t\t\t\t\ttimestamp: 0,\n\t\t\t\t\tvalue: { name, args: [...(args as JsonTypeWith<never>[])] },\n\t\t\t\t\tignoreUnmonitored: true,\n\t\t\t\t},\n\t\t\t\t// This is a notification, so we want to send it immediately.\n\t\t\t\t{ allowableUpdateLatencyMs: 0 },\n\t\t\t);\n\t\t},\n\t\tunicast: (name, targetClient, ...args) => {\n\t\t\tthis.datastore.localUpdate(\n\t\t\t\tthis.key,\n\t\t\t\t{\n\t\t\t\t\trev: 0,\n\t\t\t\t\ttimestamp: 0,\n\t\t\t\t\tvalue: { name, args: [...(args as JsonTypeWith<never>[])] },\n\t\t\t\t\tignoreUnmonitored: true,\n\t\t\t\t},\n\t\t\t\t// This is a notification, so we want to send it immediately.\n\t\t\t\t{ allowableUpdateLatencyMs: 0, targetClientId: targetClient.getConnectionId() },\n\t\t\t);\n\t\t},\n\t};\n\n\t// Workaround for types\n\tprivate readonly notificationsInternal = createEmitter<NotificationSubscriptions<T>>();\n\n\t// @ts-expect-error TODO\n\tpublic readonly notifications: NotificationListenable<T> = this.notificationsInternal;\n\n\tpublic constructor(\n\t\tprivate readonly key: Key,\n\t\tprivate readonly datastore: StateDatastore<\n\t\t\tKey,\n\t\t\tInternalTypes.ValueRequiredState<InternalTypes.NotificationType>\n\t\t>,\n\t\tinitialSubscriptions: Partial<NotificationSubscriptions<T>>,\n\t) {\n\t\t// Add event listeners provided at instantiation\n\t\tfor (const subscriptionName of recordKeys(initialSubscriptions)) {\n\t\t\t// Lingering Event typing issues with Notifications specialization requires\n\t\t\t// this cast. The only thing that really matters is that name is a string.\n\t\t\tconst name = subscriptionName as keyof Listeners<NotificationSubscriptions<T>>;\n\t\t\tconst value = initialSubscriptions[subscriptionName];\n\t\t\t// This check should not be needed while using exactOptionalPropertyTypes, but\n\t\t\t// typescript appears to ignore that with Partial<>. Good to be defensive\n\t\t\t// against callers sending `undefined` anyway.\n\t\t\tif (value !== undefined) {\n\t\t\t\tthis.notificationsInternal.on(name, value);\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic update(\n\t\tclient: ISessionClient,\n\t\t_received: number,\n\t\tvalue: InternalTypes.ValueRequiredState<InternalTypes.NotificationType>,\n\t): PostUpdateAction[] {\n\t\tconst postUpdateActions: PostUpdateAction[] = [];\n\t\tconst eventName = value.value.name as keyof Listeners<NotificationSubscriptions<T>>;\n\t\tif (this.notificationsInternal.hasListeners(eventName)) {\n\t\t\t// Without schema validation, we don't know that the args are the correct type.\n\t\t\t// For now we assume the user is sending the correct types and there is no corruption along the way.\n\t\t\tconst args = [client, ...value.value.args] as Parameters<\n\t\t\t\tNotificationSubscriptions<T>[typeof eventName]\n\t\t\t>;\n\t\t\tpostUpdateActions.push(() => this.notificationsInternal.emit(eventName, ...args));\n\t\t} else {\n\t\t\tpostUpdateActions.push(() =>\n\t\t\t\tthis.events.emit(\n\t\t\t\t\t\"unattendedNotification\",\n\t\t\t\t\tvalue.value.name,\n\t\t\t\t\tclient,\n\t\t\t\t\t...value.value.args,\n\t\t\t\t),\n\t\t\t);\n\t\t}\n\t\treturn postUpdateActions;\n\t}\n}\n\n/**\n * Factory for creating a {@link NotificationsManager}.\n *\n * @remarks\n * Typescript inference for `Notifications` is not working correctly yet.\n * Explicitly specify generics to make result types usable.\n *\n * @alpha\n */\nexport function Notifications<\n\tT extends InternalUtilityTypes.NotificationListeners<T>,\n\tKey extends string = string,\n>(\n\tinitialSubscriptions: Partial<NotificationSubscriptions<T>>,\n): InternalTypes.ManagerFactory<\n\tKey,\n\tInternalTypes.ValueRequiredState<InternalTypes.NotificationType>,\n\tNotificationsManager<T>\n> {\n\tconst factory = (\n\t\tkey: Key,\n\t\tdatastoreHandle: InternalTypes.StateDatastoreHandle<\n\t\t\tKey,\n\t\t\tInternalTypes.ValueRequiredState<InternalTypes.NotificationType>\n\t\t>,\n\t): {\n\t\tmanager: InternalTypes.StateValue<NotificationsManager<T>>;\n\t} => ({\n\t\tmanager: brandIVM<\n\t\t\tNotificationsManagerImpl<T, Key>,\n\t\t\tInternalTypes.NotificationType,\n\t\t\tInternalTypes.ValueRequiredState<InternalTypes.NotificationType>\n\t\t>(\n\t\t\tnew NotificationsManagerImpl(\n\t\t\t\tkey,\n\t\t\t\tdatastoreFromHandle(datastoreHandle),\n\t\t\t\tinitialSubscriptions,\n\t\t\t),\n\t\t),\n\t});\n\treturn Object.assign(factory, { instanceBase: NotificationsManagerImpl });\n}\n"]}
|
|
1
|
+
{"version":3,"file":"notificationsManager.js","sourceRoot":"","sources":["../src/notificationsManager.ts"],"names":[],"mappings":"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","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { createEmitter } from \"@fluid-internal/client-utils\";\nimport type { Listeners, Listenable, Off } from \"@fluidframework/core-interfaces\";\nimport type { JsonTypeWith } from \"@fluidframework/core-interfaces/internal\";\n\nimport type { InternalTypes } from \"./exposedInternalTypes.js\";\nimport type { InternalUtilityTypes } from \"./exposedUtilityTypes.js\";\nimport type { PostUpdateAction, ValueManager } from \"./internalTypes.js\";\nimport type { Attendee, Presence } from \"./presence.js\";\nimport { datastoreFromHandle, type StateDatastore } from \"./stateDatastore.js\";\nimport { brandIVM } from \"./valueManager.js\";\n\n/**\n * @sealed\n * @alpha\n */\nexport interface NotificationsManagerEvents {\n\t/**\n\t * Raised when notification is received, but no subscribers were found.\n\t *\n\t * @eventProperty\n\t */\n\tunattendedNotification: (name: string, sender: Attendee, ...content: unknown[]) => void;\n}\n\n/**\n * An object which allows the registration of listeners so that subscribers can be\n * notified when a notification happens.\n *\n * @sealed\n * @alpha\n */\nexport interface NotificationListenable<\n\tTListeners extends InternalUtilityTypes.NotificationListeners<TListeners>,\n> {\n\t/**\n\t * Register a notification listener.\n\t * @param notificationName - the name of the notification\n\t * @param listener - The listener function to run when the notification is fired.\n\t * @returns A {@link @fluidframework/core-interfaces#Off | function} which will deregister the listener when called.\n\t * Calling the deregistration function more than once will have no effect.\n\t *\n\t * Listeners may also be deregistered by passing the listener to {@link NotificationListenable.off | off()}.\n\t * @remarks Registering the exact same `listener` object for the same notification more than once will throw an error.\n\t * If registering the same listener for the same notification multiple times is desired, consider using a wrapper function for the second subscription.\n\t */\n\ton<K extends keyof InternalUtilityTypes.NotificationListeners<TListeners>>(\n\t\tnotificationName: K,\n\t\tlistener: (\n\t\t\tsender: Attendee,\n\t\t\t...args: InternalUtilityTypes.JsonDeserializedParameters<TListeners[K]>\n\t\t) => void,\n\t): Off;\n\n\t/**\n\t * Deregister notification listener.\n\t * @param notificationName - The name of the notification.\n\t * @param listener - The listener function to remove from the current set of notification listeners.\n\t * @remarks If `listener` is not currently registered, this method will have no effect.\n\t *\n\t * Listeners may also be deregistered by calling the {@link @fluidframework/core-interfaces#Off | deregistration function} returned when they are {@link NotificationListenable.on | registered}.\n\t */\n\toff<K extends keyof InternalUtilityTypes.NotificationListeners<TListeners>>(\n\t\tnotificationName: K,\n\t\tlistener: (\n\t\t\tsender: Attendee,\n\t\t\t...args: InternalUtilityTypes.JsonDeserializedParameters<TListeners[K]>\n\t\t) => void,\n\t): void;\n}\n\n/**\n * Record of notification subscriptions.\n *\n * @sealed\n * @alpha\n */\nexport type NotificationSubscriptions<\n\tE extends InternalUtilityTypes.NotificationListeners<E>,\n> = {\n\t[K in string & keyof InternalUtilityTypes.NotificationListeners<E>]: (\n\t\tsender: Attendee,\n\t\t...args: InternalUtilityTypes.JsonDeserializedParameters<E[K]>\n\t) => void;\n};\n\n/**\n * Interface for a notification emitter that can send typed notification to other clients.\n *\n * @sealed\n * @alpha\n */\nexport interface NotificationEmitter<E extends InternalUtilityTypes.NotificationListeners<E>> {\n\t/**\n\t * Emits a notification with the specified name and arguments, notifying all clients.\n\t * @param notificationName - the name of the notification to fire\n\t * @param args - the arguments sent with the notification\n\t */\n\tbroadcast<K extends string & keyof InternalUtilityTypes.NotificationListeners<E>>(\n\t\tnotificationName: K,\n\t\t...args: Parameters<E[K]>\n\t): void;\n\n\t/**\n\t * Emits a notification with the specified name and arguments, notifying a single attendee.\n\t * @param notificationName - the name of the notification to fire\n\t * @param targetAttendee - the single attendee to notify\n\t * @param args - the arguments sent with the notification\n\t */\n\tunicast<K extends string & keyof InternalUtilityTypes.NotificationListeners<E>>(\n\t\tnotificationName: K,\n\t\ttargetAttendee: Attendee,\n\t\t...args: Parameters<E[K]>\n\t): void;\n}\n\n/**\n * Provides notifications from this client to others and subscription\n * to their notifications.\n *\n * @remarks Create using {@link Notifications} registered to\n * {@link NotificationsWorkspace} or {@link StatesWorkspace}.\n *\n * @sealed\n * @alpha\n */\nexport interface NotificationsManager<\n\tT extends InternalUtilityTypes.NotificationListeners<T>,\n> {\n\t/**\n\t * Containing {@link Presence}\n\t */\n\treadonly presence: Presence;\n\n\t/**\n\t * Events for Notifications manager.\n\t */\n\treadonly events: Listenable<NotificationsManagerEvents>;\n\n\t/**\n\t * Send notifications to other clients.\n\t */\n\treadonly emit: NotificationEmitter<T>;\n\n\t/**\n\t * Provides subscription to notifications from other clients.\n\t */\n\treadonly notifications: NotificationListenable<T>;\n}\n\n/**\n * Object.keys retyped to support specific records keys and\n * branded string-based keys.\n */\nconst recordKeys = Object.keys as <K extends string>(o: Partial<Record<K, unknown>>) => K[];\n\nclass NotificationsManagerImpl<\n\tT extends InternalUtilityTypes.NotificationListeners<T>,\n\tKey extends string,\n> implements\n\t\tNotificationsManager<T>,\n\t\tValueManager<\n\t\t\tInternalTypes.NotificationType,\n\t\t\tInternalTypes.ValueRequiredState<InternalTypes.NotificationType>\n\t\t>\n{\n\tpublic readonly events = createEmitter<NotificationsManagerEvents>();\n\n\tpublic readonly emit: NotificationEmitter<T> = {\n\t\tbroadcast: (name, ...args) => {\n\t\t\tthis.datastore.localUpdate(\n\t\t\t\tthis.key,\n\t\t\t\t{\n\t\t\t\t\trev: 0,\n\t\t\t\t\ttimestamp: 0,\n\t\t\t\t\tvalue: { name, args: [...(args as JsonTypeWith<never>[])] },\n\t\t\t\t\tignoreUnmonitored: true,\n\t\t\t\t},\n\t\t\t\t// This is a notification, so we want to send it immediately.\n\t\t\t\t{ allowableUpdateLatencyMs: 0 },\n\t\t\t);\n\t\t},\n\t\tunicast: (name, targetAttendee, ...args) => {\n\t\t\tthis.datastore.localUpdate(\n\t\t\t\tthis.key,\n\t\t\t\t{\n\t\t\t\t\trev: 0,\n\t\t\t\t\ttimestamp: 0,\n\t\t\t\t\tvalue: { name, args: [...(args as JsonTypeWith<never>[])] },\n\t\t\t\t\tignoreUnmonitored: true,\n\t\t\t\t},\n\t\t\t\t// This is a notification, so we want to send it immediately.\n\t\t\t\t{ allowableUpdateLatencyMs: 0, targetClientId: targetAttendee.getConnectionId() },\n\t\t\t);\n\t\t},\n\t};\n\n\t// Workaround for types\n\tprivate readonly notificationsInternal = createEmitter<NotificationSubscriptions<T>>();\n\n\t// @ts-expect-error TODO\n\tpublic readonly notifications: NotificationListenable<T> = this.notificationsInternal;\n\n\tpublic constructor(\n\t\tprivate readonly key: Key,\n\t\tprivate readonly datastore: StateDatastore<\n\t\t\tKey,\n\t\t\tInternalTypes.ValueRequiredState<InternalTypes.NotificationType>\n\t\t>,\n\t\tinitialSubscriptions: Partial<NotificationSubscriptions<T>>,\n\t) {\n\t\t// Add event listeners provided at instantiation\n\t\tfor (const subscriptionName of recordKeys(initialSubscriptions)) {\n\t\t\t// Lingering Event typing issues with Notifications specialization requires\n\t\t\t// this cast. The only thing that really matters is that name is a string.\n\t\t\tconst name = subscriptionName as keyof Listeners<NotificationSubscriptions<T>>;\n\t\t\tconst value = initialSubscriptions[subscriptionName];\n\t\t\t// This check should not be needed while using exactOptionalPropertyTypes, but\n\t\t\t// typescript appears to ignore that with Partial<>. Good to be defensive\n\t\t\t// against callers sending `undefined` anyway.\n\t\t\tif (value !== undefined) {\n\t\t\t\tthis.notificationsInternal.on(name, value);\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic get presence(): Presence {\n\t\treturn this.datastore.presence;\n\t}\n\n\tpublic update(\n\t\tattendee: Attendee,\n\t\t_received: number,\n\t\tvalue: InternalTypes.ValueRequiredState<InternalTypes.NotificationType>,\n\t): PostUpdateAction[] {\n\t\tconst postUpdateActions: PostUpdateAction[] = [];\n\t\tconst eventName = value.value.name as keyof Listeners<NotificationSubscriptions<T>>;\n\t\tif (this.notificationsInternal.hasListeners(eventName)) {\n\t\t\t// Without schema validation, we don't know that the args are the correct type.\n\t\t\t// For now we assume the user is sending the correct types and there is no corruption along the way.\n\t\t\tconst args = [attendee, ...value.value.args] as Parameters<\n\t\t\t\tNotificationSubscriptions<T>[typeof eventName]\n\t\t\t>;\n\t\t\tpostUpdateActions.push(() => this.notificationsInternal.emit(eventName, ...args));\n\t\t} else {\n\t\t\tpostUpdateActions.push(() =>\n\t\t\t\tthis.events.emit(\n\t\t\t\t\t\"unattendedNotification\",\n\t\t\t\t\tvalue.value.name,\n\t\t\t\t\tattendee,\n\t\t\t\t\t...value.value.args,\n\t\t\t\t),\n\t\t\t);\n\t\t}\n\t\treturn postUpdateActions;\n\t}\n}\n\n/**\n * Factory for creating a {@link NotificationsManager}.\n *\n * @remarks\n * Typescript inference for `Notifications` is not working correctly yet.\n * Explicitly specify generics to make result types usable.\n *\n * @alpha\n */\nexport function Notifications<\n\tT extends InternalUtilityTypes.NotificationListeners<T>,\n\tKey extends string = string,\n>(\n\tinitialSubscriptions: Partial<NotificationSubscriptions<T>>,\n): InternalTypes.ManagerFactory<\n\tKey,\n\tInternalTypes.ValueRequiredState<InternalTypes.NotificationType>,\n\tNotificationsManager<T>\n> {\n\tconst factory = (\n\t\tkey: Key,\n\t\tdatastoreHandle: InternalTypes.StateDatastoreHandle<\n\t\t\tKey,\n\t\t\tInternalTypes.ValueRequiredState<InternalTypes.NotificationType>\n\t\t>,\n\t): {\n\t\tmanager: InternalTypes.StateValue<NotificationsManager<T>>;\n\t} => ({\n\t\tmanager: brandIVM<\n\t\t\tNotificationsManagerImpl<T, Key>,\n\t\t\tInternalTypes.NotificationType,\n\t\t\tInternalTypes.ValueRequiredState<InternalTypes.NotificationType>\n\t\t>(\n\t\t\tnew NotificationsManagerImpl(\n\t\t\t\tkey,\n\t\t\t\tdatastoreFromHandle(datastoreHandle),\n\t\t\t\tinitialSubscriptions,\n\t\t\t),\n\t\t),\n\t});\n\treturn Object.assign(factory, { instanceBase: NotificationsManagerImpl });\n}\n"]}
|