@fluidframework/fluid-static 2.0.0-internal.3.0.2 → 2.0.0-internal.3.2.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.
Files changed (45) hide show
  1. package/.eslintrc.js +8 -10
  2. package/.mocharc.js +2 -2
  3. package/CHANGELOG.md +48 -51
  4. package/api-extractor.json +2 -2
  5. package/dist/fluidContainer.d.ts.map +1 -1
  6. package/dist/fluidContainer.js.map +1 -1
  7. package/dist/index.d.ts +1 -1
  8. package/dist/index.d.ts.map +1 -1
  9. package/dist/index.js.map +1 -1
  10. package/dist/rootDataObject.d.ts.map +1 -1
  11. package/dist/rootDataObject.js +3 -1
  12. package/dist/rootDataObject.js.map +1 -1
  13. package/dist/serviceAudience.d.ts.map +1 -1
  14. package/dist/serviceAudience.js.map +1 -1
  15. package/dist/types.d.ts.map +1 -1
  16. package/dist/types.js.map +1 -1
  17. package/dist/utils.d.ts.map +1 -1
  18. package/dist/utils.js +1 -4
  19. package/dist/utils.js.map +1 -1
  20. package/lib/fluidContainer.d.ts.map +1 -1
  21. package/lib/fluidContainer.js.map +1 -1
  22. package/lib/index.d.ts +1 -1
  23. package/lib/index.d.ts.map +1 -1
  24. package/lib/index.js +1 -1
  25. package/lib/index.js.map +1 -1
  26. package/lib/rootDataObject.d.ts.map +1 -1
  27. package/lib/rootDataObject.js +3 -1
  28. package/lib/rootDataObject.js.map +1 -1
  29. package/lib/serviceAudience.d.ts.map +1 -1
  30. package/lib/serviceAudience.js.map +1 -1
  31. package/lib/types.d.ts.map +1 -1
  32. package/lib/types.js.map +1 -1
  33. package/lib/utils.d.ts.map +1 -1
  34. package/lib/utils.js +1 -4
  35. package/lib/utils.js.map +1 -1
  36. package/package.json +48 -47
  37. package/prettier.config.cjs +1 -1
  38. package/src/fluidContainer.ts +264 -260
  39. package/src/index.ts +6 -2
  40. package/src/rootDataObject.ts +152 -148
  41. package/src/serviceAudience.ts +137 -134
  42. package/src/types.ts +105 -101
  43. package/src/utils.ts +30 -42
  44. package/tsconfig.esnext.json +5 -5
  45. package/tsconfig.json +10 -16
@@ -3,22 +3,22 @@
3
3
  * Licensed under the MIT License.
4
4
  */
5
5
  import {
6
- BaseContainerRuntimeFactory,
7
- DataObject,
8
- DataObjectFactory,
9
- defaultRouteRequestHandler,
6
+ BaseContainerRuntimeFactory,
7
+ DataObject,
8
+ DataObjectFactory,
9
+ defaultRouteRequestHandler,
10
10
  } from "@fluidframework/aqueduct";
11
11
  import { IContainerRuntime } from "@fluidframework/container-runtime-definitions";
12
12
  import { IFluidLoadable } from "@fluidframework/core-interfaces";
13
13
  import { FlushMode } from "@fluidframework/runtime-definitions";
14
14
  import {
15
- ContainerSchema,
16
- DataObjectClass,
17
- IRootDataObject,
18
- LoadableObjectClass,
19
- LoadableObjectClassRecord,
20
- LoadableObjectRecord,
21
- SharedObjectClass,
15
+ ContainerSchema,
16
+ DataObjectClass,
17
+ IRootDataObject,
18
+ LoadableObjectClass,
19
+ LoadableObjectClassRecord,
20
+ LoadableObjectRecord,
21
+ SharedObjectClass,
22
22
  } from "./types";
23
23
  import { isDataObjectClass, isSharedObjectClass, parseDataObjectsFromSharedObjects } from "./utils";
24
24
 
@@ -26,111 +26,114 @@ import { isDataObjectClass, isSharedObjectClass, parseDataObjectsFromSharedObjec
26
26
  * Input props for {@link RootDataObject.initializingFirstTime}.
27
27
  */
28
28
  export interface RootDataObjectProps {
29
- /**
30
- * Initial object structure with which the {@link RootDataObject} will be first-time initialized.
31
- *
32
- * @see {@link RootDataObject.initializingFirstTime}
33
- */
34
- initialObjects: LoadableObjectClassRecord;
29
+ /**
30
+ * Initial object structure with which the {@link RootDataObject} will be first-time initialized.
31
+ *
32
+ * @see {@link RootDataObject.initializingFirstTime}
33
+ */
34
+ initialObjects: LoadableObjectClassRecord;
35
35
  }
36
36
 
37
37
  /**
38
38
  * The entry-point/root collaborative object of the {@link IFluidContainer | Fluid Container}.
39
39
  * Abstracts the dynamic code required to build a Fluid Container into a static representation for end customers.
40
40
  */
41
- export class RootDataObject extends DataObject<{ InitialState: RootDataObjectProps; }> implements IRootDataObject {
42
- private readonly initialObjectsDirKey = "initial-objects-key";
43
- private readonly _initialObjects: LoadableObjectRecord = {};
44
-
45
- private get initialObjectsDir() {
46
- const dir = this.root.getSubDirectory(this.initialObjectsDirKey);
47
- if (dir === undefined) {
48
- throw new Error("InitialObjects sub-directory was not initialized");
49
- }
50
- return dir;
51
- }
52
-
53
- /**
54
- * The first time this object is initialized, creates each object identified in
55
- * {@link RootDataObjectProps.initialObjects} and stores them as unique values in the root directory.
56
- *
57
- * @see {@link @fluidframework/aqueduct#PureDataObject.initializingFirstTime}
58
- */
59
- protected async initializingFirstTime(props: RootDataObjectProps) {
60
- this.root.createSubDirectory(this.initialObjectsDirKey);
61
-
62
- // Create initial objects provided by the developer
63
- const initialObjectsP: Promise<void>[] = [];
64
- Object.entries(props.initialObjects).forEach(([id, objectClass]) => {
65
- const createObject = async () => {
66
- const obj = await this.create(objectClass);
67
- this.initialObjectsDir.set(id, obj.handle);
68
- };
69
- initialObjectsP.push(createObject());
70
- });
71
-
72
- await Promise.all(initialObjectsP);
73
- }
74
-
75
- /**
76
- * Every time an instance is initialized, loads all of the initial objects in the root directory so they can be
77
- * accessed immediately.
78
- *
79
- * @see {@link @fluidframework/aqueduct#PureDataObject.hasInitialized}
80
- */
81
- protected async hasInitialized() {
82
- // We will always load the initial objects so they are available to the developer
83
- const loadInitialObjectsP: Promise<void>[] = [];
84
- for (const [key, value] of Array.from(this.initialObjectsDir.entries())) {
85
- const loadDir = async () => {
86
- const obj = await value.get();
87
- Object.assign(this._initialObjects, { [key]: obj });
88
- };
89
- loadInitialObjectsP.push(loadDir());
90
- }
91
-
92
- await Promise.all(loadInitialObjectsP);
93
- }
94
-
95
- /**
96
- * {@inheritDoc IRootDataObject.initialObjects}
97
- */
98
- public get initialObjects(): LoadableObjectRecord {
99
- if (Object.keys(this._initialObjects).length === 0) {
100
- throw new Error("Initial Objects were not correctly initialized");
101
- }
102
- return this._initialObjects;
103
- }
104
-
105
- /**
106
- * {@inheritDoc IRootDataObject.create}
107
- */
108
- public async create<T extends IFluidLoadable>(
109
- objectClass: LoadableObjectClass<T>,
110
- ): Promise<T> {
111
- if (isDataObjectClass(objectClass)) {
112
- return this.createDataObject<T>(objectClass);
113
- } else if (isSharedObjectClass(objectClass)) {
114
- return this.createSharedObject<T>(objectClass);
115
- }
116
- throw new Error("Could not create new Fluid object because an unknown object was passed");
117
- }
118
-
119
- private async createDataObject<T extends IFluidLoadable>(dataObjectClass: DataObjectClass<T>): Promise<T> {
120
- const factory = dataObjectClass.factory;
121
- const packagePath = [...this.context.packagePath, factory.type];
122
- const dataStore = await this.context.containerRuntime.createDataStore(packagePath);
123
- const entryPoint = await dataStore.entryPoint?.get();
124
- return entryPoint as unknown as T;
125
- }
126
-
127
- private createSharedObject<T extends IFluidLoadable>(
128
- sharedObjectClass: SharedObjectClass<T>,
129
- ): T {
130
- const factory = sharedObjectClass.getFactory();
131
- const obj = this.runtime.createChannel(undefined, factory.type);
132
- return obj as unknown as T;
133
- }
41
+ export class RootDataObject
42
+ extends DataObject<{ InitialState: RootDataObjectProps }>
43
+ implements IRootDataObject
44
+ {
45
+ private readonly initialObjectsDirKey = "initial-objects-key";
46
+ private readonly _initialObjects: LoadableObjectRecord = {};
47
+
48
+ private get initialObjectsDir() {
49
+ const dir = this.root.getSubDirectory(this.initialObjectsDirKey);
50
+ if (dir === undefined) {
51
+ throw new Error("InitialObjects sub-directory was not initialized");
52
+ }
53
+ return dir;
54
+ }
55
+
56
+ /**
57
+ * The first time this object is initialized, creates each object identified in
58
+ * {@link RootDataObjectProps.initialObjects} and stores them as unique values in the root directory.
59
+ *
60
+ * @see {@link @fluidframework/aqueduct#PureDataObject.initializingFirstTime}
61
+ */
62
+ protected async initializingFirstTime(props: RootDataObjectProps) {
63
+ this.root.createSubDirectory(this.initialObjectsDirKey);
64
+
65
+ // Create initial objects provided by the developer
66
+ const initialObjectsP: Promise<void>[] = [];
67
+ Object.entries(props.initialObjects).forEach(([id, objectClass]) => {
68
+ const createObject = async () => {
69
+ const obj = await this.create(objectClass);
70
+ this.initialObjectsDir.set(id, obj.handle);
71
+ };
72
+ initialObjectsP.push(createObject());
73
+ });
74
+
75
+ await Promise.all(initialObjectsP);
76
+ }
77
+
78
+ /**
79
+ * Every time an instance is initialized, loads all of the initial objects in the root directory so they can be
80
+ * accessed immediately.
81
+ *
82
+ * @see {@link @fluidframework/aqueduct#PureDataObject.hasInitialized}
83
+ */
84
+ protected async hasInitialized() {
85
+ // We will always load the initial objects so they are available to the developer
86
+ const loadInitialObjectsP: Promise<void>[] = [];
87
+ for (const [key, value] of Array.from(this.initialObjectsDir.entries())) {
88
+ const loadDir = async () => {
89
+ const obj = await value.get();
90
+ Object.assign(this._initialObjects, { [key]: obj });
91
+ };
92
+ loadInitialObjectsP.push(loadDir());
93
+ }
94
+
95
+ await Promise.all(loadInitialObjectsP);
96
+ }
97
+
98
+ /**
99
+ * {@inheritDoc IRootDataObject.initialObjects}
100
+ */
101
+ public get initialObjects(): LoadableObjectRecord {
102
+ if (Object.keys(this._initialObjects).length === 0) {
103
+ throw new Error("Initial Objects were not correctly initialized");
104
+ }
105
+ return this._initialObjects;
106
+ }
107
+
108
+ /**
109
+ * {@inheritDoc IRootDataObject.create}
110
+ */
111
+ public async create<T extends IFluidLoadable>(objectClass: LoadableObjectClass<T>): Promise<T> {
112
+ if (isDataObjectClass(objectClass)) {
113
+ return this.createDataObject<T>(objectClass);
114
+ } else if (isSharedObjectClass(objectClass)) {
115
+ return this.createSharedObject<T>(objectClass);
116
+ }
117
+ throw new Error("Could not create new Fluid object because an unknown object was passed");
118
+ }
119
+
120
+ private async createDataObject<T extends IFluidLoadable>(
121
+ dataObjectClass: DataObjectClass<T>,
122
+ ): Promise<T> {
123
+ const factory = dataObjectClass.factory;
124
+ const packagePath = [...this.context.packagePath, factory.type];
125
+ const dataStore = await this.context.containerRuntime.createDataStore(packagePath);
126
+ const entryPoint = await dataStore.entryPoint?.get();
127
+ return entryPoint as unknown as T;
128
+ }
129
+
130
+ private createSharedObject<T extends IFluidLoadable>(
131
+ sharedObjectClass: SharedObjectClass<T>,
132
+ ): T {
133
+ const factory = sharedObjectClass.getFactory();
134
+ const obj = this.runtime.createChannel(undefined, factory.type);
135
+ return obj as unknown as T;
136
+ }
134
137
  }
135
138
 
136
139
  const rootDataStoreId = "rootDOId";
@@ -144,42 +147,43 @@ const rootDataStoreId = "rootDOId";
144
147
  * to the container runtime factory.
145
148
  */
146
149
  export class DOProviderContainerRuntimeFactory extends BaseContainerRuntimeFactory {
147
- private readonly rootDataObjectFactory: DataObjectFactory<RootDataObject, {
148
- InitialState: RootDataObjectProps;
149
- }>;
150
-
151
- private readonly initialObjects: LoadableObjectClassRecord;
152
-
153
- constructor(schema: ContainerSchema) {
154
- const [registryEntries, sharedObjects] = parseDataObjectsFromSharedObjects(schema);
155
- const rootDataObjectFactory =
156
- new DataObjectFactory(
157
- "rootDO",
158
- RootDataObject,
159
- sharedObjects,
160
- {},
161
- registryEntries,
162
- );
163
- super(
164
- [rootDataObjectFactory.registryEntry],
165
- undefined,
166
- [defaultRouteRequestHandler(rootDataStoreId)],
167
- // temporary workaround to disable message batching until the message batch size issue is resolved
168
- // resolution progress is tracked by the Feature 465 work item in AzDO
169
- { flushMode: FlushMode.Immediate },
170
- );
171
- this.rootDataObjectFactory = rootDataObjectFactory;
172
- this.initialObjects = schema.initialObjects;
173
- }
174
-
175
- /**
176
- * {@inheritDoc @fluidframework/aqueduct#BaseContainerRuntimeFactory.containerInitializingFirstTime}
177
- */
178
- protected async containerInitializingFirstTime(runtime: IContainerRuntime) {
179
- // The first time we create the container we create the RootDataObject
180
- await this.rootDataObjectFactory.createRootInstance(
181
- rootDataStoreId,
182
- runtime,
183
- { initialObjects: this.initialObjects });
184
- }
150
+ private readonly rootDataObjectFactory: DataObjectFactory<
151
+ RootDataObject,
152
+ {
153
+ InitialState: RootDataObjectProps;
154
+ }
155
+ >;
156
+
157
+ private readonly initialObjects: LoadableObjectClassRecord;
158
+
159
+ constructor(schema: ContainerSchema) {
160
+ const [registryEntries, sharedObjects] = parseDataObjectsFromSharedObjects(schema);
161
+ const rootDataObjectFactory = new DataObjectFactory(
162
+ "rootDO",
163
+ RootDataObject,
164
+ sharedObjects,
165
+ {},
166
+ registryEntries,
167
+ );
168
+ super(
169
+ [rootDataObjectFactory.registryEntry],
170
+ undefined,
171
+ [defaultRouteRequestHandler(rootDataStoreId)],
172
+ // temporary workaround to disable message batching until the message batch size issue is resolved
173
+ // resolution progress is tracked by the Feature 465 work item in AzDO
174
+ { flushMode: FlushMode.Immediate },
175
+ );
176
+ this.rootDataObjectFactory = rootDataObjectFactory;
177
+ this.initialObjects = schema.initialObjects;
178
+ }
179
+
180
+ /**
181
+ * {@inheritDoc @fluidframework/aqueduct#BaseContainerRuntimeFactory.containerInitializingFirstTime}
182
+ */
183
+ protected async containerInitializingFirstTime(runtime: IContainerRuntime) {
184
+ // The first time we create the container we create the RootDataObject
185
+ await this.rootDataObjectFactory.createRootInstance(rootDataStoreId, runtime, {
186
+ initialObjects: this.initialObjects,
187
+ });
188
+ }
185
189
  }
@@ -19,138 +19,141 @@ import { IServiceAudience, IServiceAudienceEvents, IMember, Myself } from "./typ
19
19
  * @typeParam M - A service-specific {@link IMember} implementation.
20
20
  */
21
21
  export abstract class ServiceAudience<M extends IMember = IMember>
22
- extends TypedEventEmitter<IServiceAudienceEvents<M>>
23
- implements IServiceAudience<M> {
24
- /**
25
- * Audience object which includes all the existing members of the {@link IFluidContainer | container}.
26
- */
27
- protected readonly audience: IAudience;
28
-
29
- /**
30
- * Retain the most recent member list.
31
- *
32
- * @remarks
33
- *
34
- * This is so we have more information about a member leaving the audience in the `removeMember` event.
35
- *
36
- * It allows us to match the behavior of the `addMember` event where it only fires on a change to the members this
37
- * class exposes (and would actually produce a change in what `getMembers` returns).
38
- *
39
- * It also allows us to provide the client details in the event which makes it easier to find that client connection
40
- * in a map keyed on the `userId` and not `clientId`.
41
- *
42
- * This map will always be up-to-date in a `removeMember` event because it is set once at construction and in
43
- * every `addMember` event. It is mapped `clientId` to `M` to be better work with what the {@link IServiceAudience}
44
- * events provide.
45
- */
46
- protected lastMembers: Map<string, M> = new Map();
47
-
48
- constructor(
49
- /**
50
- * Fluid Container to read the audience from.
51
- */
52
- protected readonly container: IContainer,
53
- ) {
54
- super();
55
- this.audience = container.audience;
56
-
57
- // getMembers will assign lastMembers so the removeMember event has what it needs
58
- // in case it would fire before getMembers otherwise gets called the first time
59
- this.getMembers();
60
-
61
- this.audience.on("addMember", (clientId: string, details: IClient) => {
62
- if (this.shouldIncludeAsMember(details)) {
63
- const member = this.getMember(clientId);
64
- this.emit("memberAdded", clientId, member);
65
- this.emit("membersChanged");
66
- }
67
- });
68
-
69
- this.audience.on("removeMember", (clientId: string) => {
70
- if (this.lastMembers.has(clientId)) {
71
- this.emit("memberRemoved", clientId, this.lastMembers.get(clientId));
72
- this.emit("membersChanged");
73
- }
74
- });
75
-
76
- this.container.on("connected", () => this.emit("membersChanged"));
77
- }
78
-
79
- /**
80
- * Provides ability for inheriting class to modify/extend the audience object.
81
- *
82
- * @param audienceMember - Record of a specific audience member.
83
- */
84
- protected abstract createServiceMember(audienceMember: IClient): M;
85
-
86
- /**
87
- * {@inheritDoc IServiceAudience.getMembers}
88
- */
89
- public getMembers(): Map<string, M> {
90
- const users = new Map<string, M>();
91
- const clientMemberMap = new Map<string, M>();
92
- // Iterate through the members and get the user specifics.
93
- this.audience.getMembers().forEach((member: IClient, clientId: string) => {
94
- if (this.shouldIncludeAsMember(member)) {
95
- const userId = member.user.id;
96
- // Ensure we're tracking the user
97
- let user = users.get(userId);
98
- if (user === undefined) {
99
- user = this.createServiceMember(member);
100
- users.set(userId, user);
101
- }
102
-
103
- // Add this connection to their collection
104
- user.connections.push({ id: clientId, mode: member.mode });
105
- clientMemberMap.set(clientId, user);
106
- }
107
- });
108
- this.lastMembers = clientMemberMap;
109
- return users;
110
- }
111
-
112
- /**
113
- * {@inheritDoc IServiceAudience.getMyself}
114
- */
115
- public getMyself(): Myself<M> | undefined {
116
- const clientId = this.container.clientId;
117
- if (clientId === undefined) {
118
- return undefined;
119
- }
120
-
121
- const member = this.getMember(clientId);
122
- if (member === undefined) {
123
- return undefined;
124
- }
125
-
126
- const myself: Myself<M> = { ...member, currentConnection: clientId };
127
-
128
- return myself;
129
- }
130
-
131
- private getMember(clientId: string): M | undefined {
132
- // Fetch the user ID assoicated with this client ID from the runtime
133
- const internalAudienceMember = this.audience.getMember(clientId);
134
- if (internalAudienceMember === undefined) {
135
- return undefined;
136
- }
137
- // Return the member object with any other clients associated for this user
138
- const allMembers = this.getMembers();
139
- const member = allMembers.get(internalAudienceMember?.user.id);
140
- if (member === undefined) {
141
- throw Error(`Attempted to fetch client ${clientId} that is not part of the current member list`);
142
- }
143
- return member;
144
- }
145
-
146
- /**
147
- * Provides ability for the inheriting class to include/omit specific members.
148
- * An example use case is omitting the summarizer client.
149
- *
150
- * @param member - Member to be included/omitted.
151
- */
152
- protected shouldIncludeAsMember(member: IClient): boolean {
153
- // Include only human members
154
- return member.details.capabilities.interactive;
155
- }
22
+ extends TypedEventEmitter<IServiceAudienceEvents<M>>
23
+ implements IServiceAudience<M>
24
+ {
25
+ /**
26
+ * Audience object which includes all the existing members of the {@link IFluidContainer | container}.
27
+ */
28
+ protected readonly audience: IAudience;
29
+
30
+ /**
31
+ * Retain the most recent member list.
32
+ *
33
+ * @remarks
34
+ *
35
+ * This is so we have more information about a member leaving the audience in the `removeMember` event.
36
+ *
37
+ * It allows us to match the behavior of the `addMember` event where it only fires on a change to the members this
38
+ * class exposes (and would actually produce a change in what `getMembers` returns).
39
+ *
40
+ * It also allows us to provide the client details in the event which makes it easier to find that client connection
41
+ * in a map keyed on the `userId` and not `clientId`.
42
+ *
43
+ * This map will always be up-to-date in a `removeMember` event because it is set once at construction and in
44
+ * every `addMember` event. It is mapped `clientId` to `M` to be better work with what the {@link IServiceAudience}
45
+ * events provide.
46
+ */
47
+ protected lastMembers: Map<string, M> = new Map();
48
+
49
+ constructor(
50
+ /**
51
+ * Fluid Container to read the audience from.
52
+ */
53
+ protected readonly container: IContainer,
54
+ ) {
55
+ super();
56
+ this.audience = container.audience;
57
+
58
+ // getMembers will assign lastMembers so the removeMember event has what it needs
59
+ // in case it would fire before getMembers otherwise gets called the first time
60
+ this.getMembers();
61
+
62
+ this.audience.on("addMember", (clientId: string, details: IClient) => {
63
+ if (this.shouldIncludeAsMember(details)) {
64
+ const member = this.getMember(clientId);
65
+ this.emit("memberAdded", clientId, member);
66
+ this.emit("membersChanged");
67
+ }
68
+ });
69
+
70
+ this.audience.on("removeMember", (clientId: string) => {
71
+ if (this.lastMembers.has(clientId)) {
72
+ this.emit("memberRemoved", clientId, this.lastMembers.get(clientId));
73
+ this.emit("membersChanged");
74
+ }
75
+ });
76
+
77
+ this.container.on("connected", () => this.emit("membersChanged"));
78
+ }
79
+
80
+ /**
81
+ * Provides ability for inheriting class to modify/extend the audience object.
82
+ *
83
+ * @param audienceMember - Record of a specific audience member.
84
+ */
85
+ protected abstract createServiceMember(audienceMember: IClient): M;
86
+
87
+ /**
88
+ * {@inheritDoc IServiceAudience.getMembers}
89
+ */
90
+ public getMembers(): Map<string, M> {
91
+ const users = new Map<string, M>();
92
+ const clientMemberMap = new Map<string, M>();
93
+ // Iterate through the members and get the user specifics.
94
+ this.audience.getMembers().forEach((member: IClient, clientId: string) => {
95
+ if (this.shouldIncludeAsMember(member)) {
96
+ const userId = member.user.id;
97
+ // Ensure we're tracking the user
98
+ let user = users.get(userId);
99
+ if (user === undefined) {
100
+ user = this.createServiceMember(member);
101
+ users.set(userId, user);
102
+ }
103
+
104
+ // Add this connection to their collection
105
+ user.connections.push({ id: clientId, mode: member.mode });
106
+ clientMemberMap.set(clientId, user);
107
+ }
108
+ });
109
+ this.lastMembers = clientMemberMap;
110
+ return users;
111
+ }
112
+
113
+ /**
114
+ * {@inheritDoc IServiceAudience.getMyself}
115
+ */
116
+ public getMyself(): Myself<M> | undefined {
117
+ const clientId = this.container.clientId;
118
+ if (clientId === undefined) {
119
+ return undefined;
120
+ }
121
+
122
+ const member = this.getMember(clientId);
123
+ if (member === undefined) {
124
+ return undefined;
125
+ }
126
+
127
+ const myself: Myself<M> = { ...member, currentConnection: clientId };
128
+
129
+ return myself;
130
+ }
131
+
132
+ private getMember(clientId: string): M | undefined {
133
+ // Fetch the user ID assoicated with this client ID from the runtime
134
+ const internalAudienceMember = this.audience.getMember(clientId);
135
+ if (internalAudienceMember === undefined) {
136
+ return undefined;
137
+ }
138
+ // Return the member object with any other clients associated for this user
139
+ const allMembers = this.getMembers();
140
+ const member = allMembers.get(internalAudienceMember?.user.id);
141
+ if (member === undefined) {
142
+ throw Error(
143
+ `Attempted to fetch client ${clientId} that is not part of the current member list`,
144
+ );
145
+ }
146
+ return member;
147
+ }
148
+
149
+ /**
150
+ * Provides ability for the inheriting class to include/omit specific members.
151
+ * An example use case is omitting the summarizer client.
152
+ *
153
+ * @param member - Member to be included/omitted.
154
+ */
155
+ protected shouldIncludeAsMember(member: IClient): boolean {
156
+ // Include only human members
157
+ return member.details.capabilities.interactive;
158
+ }
156
159
  }