@fluid-experimental/oldest-client-observer 2.53.1 → 2.60.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/CHANGELOG.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # @fluid-experimental/oldest-client-observer
2
2
 
3
+ ## 2.60.0
4
+
5
+ Dependency updates only.
6
+
3
7
  ## 2.53.0
4
8
 
5
9
  Dependency updates only.
@@ -1,10 +1,10 @@
1
- ## Alpha API Report File for "@fluid-experimental/oldest-client-observer"
1
+ ## Beta API Report File for "@fluid-experimental/oldest-client-observer"
2
2
 
3
3
  > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/).
4
4
 
5
5
  ```ts
6
6
 
7
- // @alpha @legacy
7
+ // @beta @legacy
8
8
  export interface IOldestClientObservable extends IEventProvider<IOldestClientObservableEvents> {
9
9
  // (undocumented)
10
10
  attachState: AttachState;
@@ -16,7 +16,7 @@ export interface IOldestClientObservable extends IEventProvider<IOldestClientObs
16
16
  getQuorum(): IQuorumClients;
17
17
  }
18
18
 
19
- // @alpha @legacy
19
+ // @beta @legacy
20
20
  export interface IOldestClientObservableEvents extends IEvent {
21
21
  // (undocumented)
22
22
  (event: "connected", listener: () => void): any;
@@ -24,19 +24,19 @@ export interface IOldestClientObservableEvents extends IEvent {
24
24
  (event: "disconnected", listener: () => void): any;
25
25
  }
26
26
 
27
- // @alpha @legacy (undocumented)
27
+ // @beta @legacy (undocumented)
28
28
  export interface IOldestClientObserver extends IEventProvider<IOldestClientObserverEvents> {
29
29
  // (undocumented)
30
30
  isOldest(): boolean;
31
31
  }
32
32
 
33
- // @alpha @legacy
33
+ // @beta @legacy
34
34
  export interface IOldestClientObserverEvents extends IEvent {
35
35
  // (undocumented)
36
36
  (event: "becameOldest" | "lostOldest", listener: () => void): any;
37
37
  }
38
38
 
39
- // @alpha @legacy
39
+ // @beta @legacy
40
40
  export class OldestClientObserver extends TypedEventEmitter<IOldestClientObserverEvents> implements IOldestClientObserver {
41
41
  constructor(observable: IOldestClientObservable);
42
42
  // (undocumented)
@@ -8,7 +8,7 @@ import type { IQuorumClients } from "@fluidframework/driver-definitions";
8
8
  /**
9
9
  * Events emitted by {@link IOldestClientObservable}.
10
10
  * @legacy
11
- * @alpha
11
+ * @beta
12
12
  */
13
13
  export interface IOldestClientObservableEvents extends IEvent {
14
14
  (event: "connected", listener: () => void): any;
@@ -20,7 +20,7 @@ export interface IOldestClientObservableEvents extends IEvent {
20
20
  * since neither is really the source of truth (they are just the only currently-available plumbing options).
21
21
  * It's information about the connection, so the real source of truth is lower (at the connection layer).
22
22
  * @legacy
23
- * @alpha
23
+ * @beta
24
24
  */
25
25
  export interface IOldestClientObservable extends IEventProvider<IOldestClientObservableEvents> {
26
26
  getQuorum(): IQuorumClients;
@@ -31,14 +31,14 @@ export interface IOldestClientObservable extends IEventProvider<IOldestClientObs
31
31
  /**
32
32
  * Events emitted by {@link IOldestClientObservable}.
33
33
  * @legacy
34
- * @alpha
34
+ * @beta
35
35
  */
36
36
  export interface IOldestClientObserverEvents extends IEvent {
37
37
  (event: "becameOldest" | "lostOldest", listener: () => void): any;
38
38
  }
39
39
  /**
40
40
  * @legacy
41
- * @alpha
41
+ * @beta
42
42
  */
43
43
  export interface IOldestClientObserver extends IEventProvider<IOldestClientObserverEvents> {
44
44
  isOldest(): boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"interfaces.js","sourceRoot":"","sources":["../src/interfaces.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 { AttachState } from \"@fluidframework/container-definitions\";\nimport type { IEvent, IEventProvider } from \"@fluidframework/core-interfaces\";\nimport type { IQuorumClients } from \"@fluidframework/driver-definitions\";\n\n/**\n * Events emitted by {@link IOldestClientObservable}.\n * @legacy\n * @alpha\n */\nexport interface IOldestClientObservableEvents extends IEvent {\n\t(event: \"connected\", listener: () => void);\n\t(event: \"disconnected\", listener: () => void);\n}\n\n/**\n * This is to make OldestClientObserver work with either a IContainerRuntime or an IFluidDataStoreRuntime\n * (both expose the relevant API surface and eventing). However, really this info probably shouldn't live on either,\n * since neither is really the source of truth (they are just the only currently-available plumbing options).\n * It's information about the connection, so the real source of truth is lower (at the connection layer).\n * @legacy\n * @alpha\n */\nexport interface IOldestClientObservable\n\textends IEventProvider<IOldestClientObservableEvents> {\n\tgetQuorum(): IQuorumClients;\n\t// Generic usage of attachState is a little unusual here. We will treat ourselves as \"the oldest client that\n\t// has information about this [container | data store]\", which in the case of detached data store may disagree\n\t// with whether we're the oldest client on the connected container. So in the data store case, it's only\n\t// safe use this as an indicator about rights to tasks performed against this specific data store, and not\n\t// more broadly.\n\tattachState: AttachState;\n\tconnected: boolean;\n\tclientId: string | undefined;\n}\n\n/**\n * Events emitted by {@link IOldestClientObservable}.\n * @legacy\n * @alpha\n */\nexport interface IOldestClientObserverEvents extends IEvent {\n\t(event: \"becameOldest\" | \"lostOldest\", listener: () => void);\n}\n\n/**\n * @legacy\n * @alpha\n */\nexport interface IOldestClientObserver extends IEventProvider<IOldestClientObserverEvents> {\n\tisOldest(): boolean;\n}\n"]}
1
+ {"version":3,"file":"interfaces.js","sourceRoot":"","sources":["../src/interfaces.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 { AttachState } from \"@fluidframework/container-definitions\";\nimport type { IEvent, IEventProvider } from \"@fluidframework/core-interfaces\";\nimport type { IQuorumClients } from \"@fluidframework/driver-definitions\";\n\n/**\n * Events emitted by {@link IOldestClientObservable}.\n * @legacy\n * @beta\n */\nexport interface IOldestClientObservableEvents extends IEvent {\n\t(event: \"connected\", listener: () => void);\n\t(event: \"disconnected\", listener: () => void);\n}\n\n/**\n * This is to make OldestClientObserver work with either a IContainerRuntime or an IFluidDataStoreRuntime\n * (both expose the relevant API surface and eventing). However, really this info probably shouldn't live on either,\n * since neither is really the source of truth (they are just the only currently-available plumbing options).\n * It's information about the connection, so the real source of truth is lower (at the connection layer).\n * @legacy\n * @beta\n */\nexport interface IOldestClientObservable\n\textends IEventProvider<IOldestClientObservableEvents> {\n\tgetQuorum(): IQuorumClients;\n\t// Generic usage of attachState is a little unusual here. We will treat ourselves as \"the oldest client that\n\t// has information about this [container | data store]\", which in the case of detached data store may disagree\n\t// with whether we're the oldest client on the connected container. So in the data store case, it's only\n\t// safe use this as an indicator about rights to tasks performed against this specific data store, and not\n\t// more broadly.\n\tattachState: AttachState;\n\tconnected: boolean;\n\tclientId: string | undefined;\n}\n\n/**\n * Events emitted by {@link IOldestClientObservable}.\n * @legacy\n * @beta\n */\nexport interface IOldestClientObserverEvents extends IEvent {\n\t(event: \"becameOldest\" | \"lostOldest\", listener: () => void);\n}\n\n/**\n * @legacy\n * @beta\n */\nexport interface IOldestClientObserver extends IEventProvider<IOldestClientObserverEvents> {\n\tisOldest(): boolean;\n}\n"]}
@@ -59,7 +59,7 @@ import type { IOldestClientObservable, IOldestClientObserver, IOldestClientObser
59
59
  * });
60
60
  * ```
61
61
  * @legacy
62
- * @alpha
62
+ * @beta
63
63
  */
64
64
  export declare class OldestClientObserver extends TypedEventEmitter<IOldestClientObserverEvents> implements IOldestClientObserver {
65
65
  private readonly observable;
@@ -63,7 +63,7 @@ const internal_1 = require("@fluidframework/core-utils/internal");
63
63
  * });
64
64
  * ```
65
65
  * @legacy
66
- * @alpha
66
+ * @beta
67
67
  */
68
68
  class OldestClientObserver extends client_utils_1.TypedEventEmitter {
69
69
  constructor(observable) {
@@ -1 +1 @@
1
- {"version":3,"file":"oldestClientObserver.js","sourceRoot":"","sources":["../src/oldestClientObserver.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;AAEH,+DAAiE;AACjE,iFAAoE;AACpE,kEAA6D;AAS7D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwDG;AACH,MAAa,oBACZ,SAAQ,gCAA8C;IAKtD,YAA6B,UAAmC;QAC/D,KAAK,EAAE,CAAC;QADoB,eAAU,GAAV,UAAU,CAAyB;QADxD,oBAAe,GAAY,KAAK,CAAC;QAexB,iBAAY,GAAG,GAAS,EAAE;YAC1C,MAAM,MAAM,GAAG,IAAI,CAAC,eAAe,EAAE,CAAC;YACtC,IAAI,IAAI,CAAC,eAAe,KAAK,MAAM,EAAE,CAAC;gBACrC,IAAI,CAAC,eAAe,GAAG,MAAM,CAAC;gBAC9B,IAAI,MAAM,EAAE,CAAC;oBACZ,IAAI,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;gBAC3B,CAAC;qBAAM,CAAC;oBACP,IAAI,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;gBACzB,CAAC;YACF,CAAC;QACF,CAAC,CAAC;QAtBD,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,UAAU,CAAC,SAAS,EAAE,CAAC;QAC1C,IAAI,CAAC,eAAe,GAAG,IAAI,CAAC,eAAe,EAAE,CAAC;QAC9C,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,WAAW,EAAE,IAAI,CAAC,YAAY,CAAC,CAAC;QAC/C,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,cAAc,EAAE,IAAI,CAAC,YAAY,CAAC,CAAC;QAClD,UAAU,CAAC,EAAE,CAAC,WAAW,EAAE,IAAI,CAAC,YAAY,CAAC,CAAC;QAC9C,UAAU,CAAC,EAAE,CAAC,cAAc,EAAE,IAAI,CAAC,YAAY,CAAC,CAAC;IAClD,CAAC;IAEM,QAAQ;QACd,OAAO,IAAI,CAAC,eAAe,CAAC;IAC7B,CAAC;IAcO,eAAe;QACtB,uGAAuG;QACvG,IAAI,IAAI,CAAC,UAAU,CAAC,WAAW,KAAK,mCAAW,CAAC,QAAQ,EAAE,CAAC;YAC1D,OAAO,IAAI,CAAC;QACb,CAAC;QAED,kEAAkE;QAClE,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,SAAS,EAAE,CAAC;YAChC,OAAO,KAAK,CAAC;QACd,CAAC;QAED,yDAAyD;QACzD,IAAA,iBAAM,EACL,IAAI,CAAC,UAAU,CAAC,QAAQ,KAAK,SAAS,EAEtC,KAAK,CAAC,4CAA4C,CAClD,CAAC;QAEF,MAAM,mBAAmB,GAAG,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;QAC5E,wEAAwE;QACxE,IAAI,mBAAmB,KAAK,SAAS,EAAE,CAAC;YACvC,OAAO,KAAK,CAAC;QACd,CAAC;QAED,MAAM,OAAO,GAAG,IAAI,CAAC,MAAM,CAAC,UAAU,EAAE,CAAC;QACzC,KAAK,MAAM,eAAe,IAAI,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC;YAChD,IAAI,eAAe,CAAC,cAAc,GAAG,mBAAmB,CAAC,cAAc,EAAE,CAAC;gBACzE,OAAO,KAAK,CAAC;YACd,CAAC;QACF,CAAC;QAED,oCAAoC;QACpC,OAAO,IAAI,CAAC;IACb,CAAC;CACD;AAlED,oDAkEC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { TypedEventEmitter } from \"@fluid-internal/client-utils\";\nimport { AttachState } from \"@fluidframework/container-definitions\";\nimport { assert } from \"@fluidframework/core-utils/internal\";\nimport type { IQuorumClients } from \"@fluidframework/driver-definitions\";\n\nimport type {\n\tIOldestClientObservable,\n\tIOldestClientObserver,\n\tIOldestClientObserverEvents,\n} from \"./interfaces.js\";\n\n/**\n * The `OldestClientObserver` is a utility inspect if the local client is the oldest amongst connected clients (in\n * terms of when they connected) and watch for changes.\n *\n * It is still experimental and under development. Please do try it out, but expect breaking changes in the future.\n *\n * @remarks\n * ### Creation\n *\n * The `OldestClientObserver` constructor takes an `IOldestClientObservable`. This is most easily satisfied with\n * either an `IContainerRuntime` or an `IFluidDataStoreRuntime`:\n *\n * ```typescript\n * // E.g. from within a BaseContainerRuntimeFactory:\n * protected async containerHasInitialized(runtime: IContainerRuntime) {\n * const oldestClientObserver = new OldestClientObserver(runtime);\n * // ...\n * }\n * ```\n *\n * ```typescript\n * // From within a DataObject\n * protected async hasInitialized() {\n * const oldestClientObserver = new OldestClientObserver(this.runtime);\n * // ...\n * }\n * ```\n *\n * ### Usage\n *\n * To check if the local client is the oldest, use the `isOldest()` method.\n *\n * ```typescript\n * if (oldestClientObserver.isOldest()) {\n * console.log(\"I'm the oldest\");\n * } else {\n * console.log(\"Someone else is older\");\n * }\n * ```\n *\n * ### Eventing\n *\n * `OldestClientObserver` is an `EventEmitter`, and will emit events when the local client becomes the oldest and when\n * it is no longer the oldest.\n *\n * ```typescript\n * oldestClientObserver.on(\"becameOldest\", () => {\n * console.log(\"I'm the oldest now\");\n * });\n *\n * oldestClientObserver.on(\"lostOldest\", () => {\n * console.log(\"I'm not the oldest anymore\");\n * });\n * ```\n * @legacy\n * @alpha\n */\nexport class OldestClientObserver\n\textends TypedEventEmitter<IOldestClientObserverEvents>\n\timplements IOldestClientObserver\n{\n\tprivate readonly quorum: IQuorumClients;\n\tprivate currentIsOldest: boolean = false;\n\tconstructor(private readonly observable: IOldestClientObservable) {\n\t\tsuper();\n\t\tthis.quorum = this.observable.getQuorum();\n\t\tthis.currentIsOldest = this.computeIsOldest();\n\t\tthis.quorum.on(\"addMember\", this.updateOldest);\n\t\tthis.quorum.on(\"removeMember\", this.updateOldest);\n\t\tobservable.on(\"connected\", this.updateOldest);\n\t\tobservable.on(\"disconnected\", this.updateOldest);\n\t}\n\n\tpublic isOldest(): boolean {\n\t\treturn this.currentIsOldest;\n\t}\n\n\tprivate readonly updateOldest = (): void => {\n\t\tconst oldest = this.computeIsOldest();\n\t\tif (this.currentIsOldest !== oldest) {\n\t\t\tthis.currentIsOldest = oldest;\n\t\t\tif (oldest) {\n\t\t\t\tthis.emit(\"becameOldest\");\n\t\t\t} else {\n\t\t\t\tthis.emit(\"lostOldest\");\n\t\t\t}\n\t\t}\n\t};\n\n\tprivate computeIsOldest(): boolean {\n\t\t// If the container is detached, we are the only ones that know about it and are the oldest by default.\n\t\tif (this.observable.attachState === AttachState.Detached) {\n\t\t\treturn true;\n\t\t}\n\n\t\t// If we're not connected we can't be the oldest connected client.\n\t\tif (!this.observable.connected) {\n\t\t\treturn false;\n\t\t}\n\n\t\t// TODO: Clean up error code linter violations repo-wide.\n\t\tassert(\n\t\t\tthis.observable.clientId !== undefined,\n\n\t\t\t0x1da /* \"Client id should be set if connected\" */,\n\t\t);\n\n\t\tconst selfSequencedClient = this.quorum.getMember(this.observable.clientId);\n\t\t// When in readonly mode our clientId will not be present in the quorum.\n\t\tif (selfSequencedClient === undefined) {\n\t\t\treturn false;\n\t\t}\n\n\t\tconst members = this.quorum.getMembers();\n\t\tfor (const sequencedClient of members.values()) {\n\t\t\tif (sequencedClient.sequenceNumber < selfSequencedClient.sequenceNumber) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\n\t\t// No member of the quorum was older\n\t\treturn true;\n\t}\n}\n"]}
1
+ {"version":3,"file":"oldestClientObserver.js","sourceRoot":"","sources":["../src/oldestClientObserver.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;AAEH,+DAAiE;AACjE,iFAAoE;AACpE,kEAA6D;AAS7D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwDG;AACH,MAAa,oBACZ,SAAQ,gCAA8C;IAKtD,YAA6B,UAAmC;QAC/D,KAAK,EAAE,CAAC;QADoB,eAAU,GAAV,UAAU,CAAyB;QADxD,oBAAe,GAAY,KAAK,CAAC;QAexB,iBAAY,GAAG,GAAS,EAAE;YAC1C,MAAM,MAAM,GAAG,IAAI,CAAC,eAAe,EAAE,CAAC;YACtC,IAAI,IAAI,CAAC,eAAe,KAAK,MAAM,EAAE,CAAC;gBACrC,IAAI,CAAC,eAAe,GAAG,MAAM,CAAC;gBAC9B,IAAI,MAAM,EAAE,CAAC;oBACZ,IAAI,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;gBAC3B,CAAC;qBAAM,CAAC;oBACP,IAAI,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;gBACzB,CAAC;YACF,CAAC;QACF,CAAC,CAAC;QAtBD,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,UAAU,CAAC,SAAS,EAAE,CAAC;QAC1C,IAAI,CAAC,eAAe,GAAG,IAAI,CAAC,eAAe,EAAE,CAAC;QAC9C,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,WAAW,EAAE,IAAI,CAAC,YAAY,CAAC,CAAC;QAC/C,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,cAAc,EAAE,IAAI,CAAC,YAAY,CAAC,CAAC;QAClD,UAAU,CAAC,EAAE,CAAC,WAAW,EAAE,IAAI,CAAC,YAAY,CAAC,CAAC;QAC9C,UAAU,CAAC,EAAE,CAAC,cAAc,EAAE,IAAI,CAAC,YAAY,CAAC,CAAC;IAClD,CAAC;IAEM,QAAQ;QACd,OAAO,IAAI,CAAC,eAAe,CAAC;IAC7B,CAAC;IAcO,eAAe;QACtB,uGAAuG;QACvG,IAAI,IAAI,CAAC,UAAU,CAAC,WAAW,KAAK,mCAAW,CAAC,QAAQ,EAAE,CAAC;YAC1D,OAAO,IAAI,CAAC;QACb,CAAC;QAED,kEAAkE;QAClE,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,SAAS,EAAE,CAAC;YAChC,OAAO,KAAK,CAAC;QACd,CAAC;QAED,yDAAyD;QACzD,IAAA,iBAAM,EACL,IAAI,CAAC,UAAU,CAAC,QAAQ,KAAK,SAAS,EAEtC,KAAK,CAAC,4CAA4C,CAClD,CAAC;QAEF,MAAM,mBAAmB,GAAG,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;QAC5E,wEAAwE;QACxE,IAAI,mBAAmB,KAAK,SAAS,EAAE,CAAC;YACvC,OAAO,KAAK,CAAC;QACd,CAAC;QAED,MAAM,OAAO,GAAG,IAAI,CAAC,MAAM,CAAC,UAAU,EAAE,CAAC;QACzC,KAAK,MAAM,eAAe,IAAI,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC;YAChD,IAAI,eAAe,CAAC,cAAc,GAAG,mBAAmB,CAAC,cAAc,EAAE,CAAC;gBACzE,OAAO,KAAK,CAAC;YACd,CAAC;QACF,CAAC;QAED,oCAAoC;QACpC,OAAO,IAAI,CAAC;IACb,CAAC;CACD;AAlED,oDAkEC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { TypedEventEmitter } from \"@fluid-internal/client-utils\";\nimport { AttachState } from \"@fluidframework/container-definitions\";\nimport { assert } from \"@fluidframework/core-utils/internal\";\nimport type { IQuorumClients } from \"@fluidframework/driver-definitions\";\n\nimport type {\n\tIOldestClientObservable,\n\tIOldestClientObserver,\n\tIOldestClientObserverEvents,\n} from \"./interfaces.js\";\n\n/**\n * The `OldestClientObserver` is a utility inspect if the local client is the oldest amongst connected clients (in\n * terms of when they connected) and watch for changes.\n *\n * It is still experimental and under development. Please do try it out, but expect breaking changes in the future.\n *\n * @remarks\n * ### Creation\n *\n * The `OldestClientObserver` constructor takes an `IOldestClientObservable`. This is most easily satisfied with\n * either an `IContainerRuntime` or an `IFluidDataStoreRuntime`:\n *\n * ```typescript\n * // E.g. from within a BaseContainerRuntimeFactory:\n * protected async containerHasInitialized(runtime: IContainerRuntime) {\n * const oldestClientObserver = new OldestClientObserver(runtime);\n * // ...\n * }\n * ```\n *\n * ```typescript\n * // From within a DataObject\n * protected async hasInitialized() {\n * const oldestClientObserver = new OldestClientObserver(this.runtime);\n * // ...\n * }\n * ```\n *\n * ### Usage\n *\n * To check if the local client is the oldest, use the `isOldest()` method.\n *\n * ```typescript\n * if (oldestClientObserver.isOldest()) {\n * console.log(\"I'm the oldest\");\n * } else {\n * console.log(\"Someone else is older\");\n * }\n * ```\n *\n * ### Eventing\n *\n * `OldestClientObserver` is an `EventEmitter`, and will emit events when the local client becomes the oldest and when\n * it is no longer the oldest.\n *\n * ```typescript\n * oldestClientObserver.on(\"becameOldest\", () => {\n * console.log(\"I'm the oldest now\");\n * });\n *\n * oldestClientObserver.on(\"lostOldest\", () => {\n * console.log(\"I'm not the oldest anymore\");\n * });\n * ```\n * @legacy\n * @beta\n */\nexport class OldestClientObserver\n\textends TypedEventEmitter<IOldestClientObserverEvents>\n\timplements IOldestClientObserver\n{\n\tprivate readonly quorum: IQuorumClients;\n\tprivate currentIsOldest: boolean = false;\n\tconstructor(private readonly observable: IOldestClientObservable) {\n\t\tsuper();\n\t\tthis.quorum = this.observable.getQuorum();\n\t\tthis.currentIsOldest = this.computeIsOldest();\n\t\tthis.quorum.on(\"addMember\", this.updateOldest);\n\t\tthis.quorum.on(\"removeMember\", this.updateOldest);\n\t\tobservable.on(\"connected\", this.updateOldest);\n\t\tobservable.on(\"disconnected\", this.updateOldest);\n\t}\n\n\tpublic isOldest(): boolean {\n\t\treturn this.currentIsOldest;\n\t}\n\n\tprivate readonly updateOldest = (): void => {\n\t\tconst oldest = this.computeIsOldest();\n\t\tif (this.currentIsOldest !== oldest) {\n\t\t\tthis.currentIsOldest = oldest;\n\t\t\tif (oldest) {\n\t\t\t\tthis.emit(\"becameOldest\");\n\t\t\t} else {\n\t\t\t\tthis.emit(\"lostOldest\");\n\t\t\t}\n\t\t}\n\t};\n\n\tprivate computeIsOldest(): boolean {\n\t\t// If the container is detached, we are the only ones that know about it and are the oldest by default.\n\t\tif (this.observable.attachState === AttachState.Detached) {\n\t\t\treturn true;\n\t\t}\n\n\t\t// If we're not connected we can't be the oldest connected client.\n\t\tif (!this.observable.connected) {\n\t\t\treturn false;\n\t\t}\n\n\t\t// TODO: Clean up error code linter violations repo-wide.\n\t\tassert(\n\t\t\tthis.observable.clientId !== undefined,\n\n\t\t\t0x1da /* \"Client id should be set if connected\" */,\n\t\t);\n\n\t\tconst selfSequencedClient = this.quorum.getMember(this.observable.clientId);\n\t\t// When in readonly mode our clientId will not be present in the quorum.\n\t\tif (selfSequencedClient === undefined) {\n\t\t\treturn false;\n\t\t}\n\n\t\tconst members = this.quorum.getMembers();\n\t\tfor (const sequencedClient of members.values()) {\n\t\t\tif (sequencedClient.sequenceNumber < selfSequencedClient.sequenceNumber) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\n\t\t// No member of the quorum was older\n\t\treturn true;\n\t}\n}\n"]}
@@ -8,7 +8,7 @@ import type { IQuorumClients } from "@fluidframework/driver-definitions";
8
8
  /**
9
9
  * Events emitted by {@link IOldestClientObservable}.
10
10
  * @legacy
11
- * @alpha
11
+ * @beta
12
12
  */
13
13
  export interface IOldestClientObservableEvents extends IEvent {
14
14
  (event: "connected", listener: () => void): any;
@@ -20,7 +20,7 @@ export interface IOldestClientObservableEvents extends IEvent {
20
20
  * since neither is really the source of truth (they are just the only currently-available plumbing options).
21
21
  * It's information about the connection, so the real source of truth is lower (at the connection layer).
22
22
  * @legacy
23
- * @alpha
23
+ * @beta
24
24
  */
25
25
  export interface IOldestClientObservable extends IEventProvider<IOldestClientObservableEvents> {
26
26
  getQuorum(): IQuorumClients;
@@ -31,14 +31,14 @@ export interface IOldestClientObservable extends IEventProvider<IOldestClientObs
31
31
  /**
32
32
  * Events emitted by {@link IOldestClientObservable}.
33
33
  * @legacy
34
- * @alpha
34
+ * @beta
35
35
  */
36
36
  export interface IOldestClientObserverEvents extends IEvent {
37
37
  (event: "becameOldest" | "lostOldest", listener: () => void): any;
38
38
  }
39
39
  /**
40
40
  * @legacy
41
- * @alpha
41
+ * @beta
42
42
  */
43
43
  export interface IOldestClientObserver extends IEventProvider<IOldestClientObserverEvents> {
44
44
  isOldest(): boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"interfaces.js","sourceRoot":"","sources":["../src/interfaces.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 { AttachState } from \"@fluidframework/container-definitions\";\nimport type { IEvent, IEventProvider } from \"@fluidframework/core-interfaces\";\nimport type { IQuorumClients } from \"@fluidframework/driver-definitions\";\n\n/**\n * Events emitted by {@link IOldestClientObservable}.\n * @legacy\n * @alpha\n */\nexport interface IOldestClientObservableEvents extends IEvent {\n\t(event: \"connected\", listener: () => void);\n\t(event: \"disconnected\", listener: () => void);\n}\n\n/**\n * This is to make OldestClientObserver work with either a IContainerRuntime or an IFluidDataStoreRuntime\n * (both expose the relevant API surface and eventing). However, really this info probably shouldn't live on either,\n * since neither is really the source of truth (they are just the only currently-available plumbing options).\n * It's information about the connection, so the real source of truth is lower (at the connection layer).\n * @legacy\n * @alpha\n */\nexport interface IOldestClientObservable\n\textends IEventProvider<IOldestClientObservableEvents> {\n\tgetQuorum(): IQuorumClients;\n\t// Generic usage of attachState is a little unusual here. We will treat ourselves as \"the oldest client that\n\t// has information about this [container | data store]\", which in the case of detached data store may disagree\n\t// with whether we're the oldest client on the connected container. So in the data store case, it's only\n\t// safe use this as an indicator about rights to tasks performed against this specific data store, and not\n\t// more broadly.\n\tattachState: AttachState;\n\tconnected: boolean;\n\tclientId: string | undefined;\n}\n\n/**\n * Events emitted by {@link IOldestClientObservable}.\n * @legacy\n * @alpha\n */\nexport interface IOldestClientObserverEvents extends IEvent {\n\t(event: \"becameOldest\" | \"lostOldest\", listener: () => void);\n}\n\n/**\n * @legacy\n * @alpha\n */\nexport interface IOldestClientObserver extends IEventProvider<IOldestClientObserverEvents> {\n\tisOldest(): boolean;\n}\n"]}
1
+ {"version":3,"file":"interfaces.js","sourceRoot":"","sources":["../src/interfaces.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 { AttachState } from \"@fluidframework/container-definitions\";\nimport type { IEvent, IEventProvider } from \"@fluidframework/core-interfaces\";\nimport type { IQuorumClients } from \"@fluidframework/driver-definitions\";\n\n/**\n * Events emitted by {@link IOldestClientObservable}.\n * @legacy\n * @beta\n */\nexport interface IOldestClientObservableEvents extends IEvent {\n\t(event: \"connected\", listener: () => void);\n\t(event: \"disconnected\", listener: () => void);\n}\n\n/**\n * This is to make OldestClientObserver work with either a IContainerRuntime or an IFluidDataStoreRuntime\n * (both expose the relevant API surface and eventing). However, really this info probably shouldn't live on either,\n * since neither is really the source of truth (they are just the only currently-available plumbing options).\n * It's information about the connection, so the real source of truth is lower (at the connection layer).\n * @legacy\n * @beta\n */\nexport interface IOldestClientObservable\n\textends IEventProvider<IOldestClientObservableEvents> {\n\tgetQuorum(): IQuorumClients;\n\t// Generic usage of attachState is a little unusual here. We will treat ourselves as \"the oldest client that\n\t// has information about this [container | data store]\", which in the case of detached data store may disagree\n\t// with whether we're the oldest client on the connected container. So in the data store case, it's only\n\t// safe use this as an indicator about rights to tasks performed against this specific data store, and not\n\t// more broadly.\n\tattachState: AttachState;\n\tconnected: boolean;\n\tclientId: string | undefined;\n}\n\n/**\n * Events emitted by {@link IOldestClientObservable}.\n * @legacy\n * @beta\n */\nexport interface IOldestClientObserverEvents extends IEvent {\n\t(event: \"becameOldest\" | \"lostOldest\", listener: () => void);\n}\n\n/**\n * @legacy\n * @beta\n */\nexport interface IOldestClientObserver extends IEventProvider<IOldestClientObserverEvents> {\n\tisOldest(): boolean;\n}\n"]}
@@ -59,7 +59,7 @@ import type { IOldestClientObservable, IOldestClientObserver, IOldestClientObser
59
59
  * });
60
60
  * ```
61
61
  * @legacy
62
- * @alpha
62
+ * @beta
63
63
  */
64
64
  export declare class OldestClientObserver extends TypedEventEmitter<IOldestClientObserverEvents> implements IOldestClientObserver {
65
65
  private readonly observable;
@@ -60,7 +60,7 @@ import { assert } from "@fluidframework/core-utils/internal";
60
60
  * });
61
61
  * ```
62
62
  * @legacy
63
- * @alpha
63
+ * @beta
64
64
  */
65
65
  export class OldestClientObserver extends TypedEventEmitter {
66
66
  constructor(observable) {
@@ -1 +1 @@
1
- {"version":3,"file":"oldestClientObserver.js","sourceRoot":"","sources":["../src/oldestClientObserver.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AACjE,OAAO,EAAE,WAAW,EAAE,MAAM,uCAAuC,CAAC;AACpE,OAAO,EAAE,MAAM,EAAE,MAAM,qCAAqC,CAAC;AAS7D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwDG;AACH,MAAM,OAAO,oBACZ,SAAQ,iBAA8C;IAKtD,YAA6B,UAAmC;QAC/D,KAAK,EAAE,CAAC;QADoB,eAAU,GAAV,UAAU,CAAyB;QADxD,oBAAe,GAAY,KAAK,CAAC;QAexB,iBAAY,GAAG,GAAS,EAAE;YAC1C,MAAM,MAAM,GAAG,IAAI,CAAC,eAAe,EAAE,CAAC;YACtC,IAAI,IAAI,CAAC,eAAe,KAAK,MAAM,EAAE,CAAC;gBACrC,IAAI,CAAC,eAAe,GAAG,MAAM,CAAC;gBAC9B,IAAI,MAAM,EAAE,CAAC;oBACZ,IAAI,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;gBAC3B,CAAC;qBAAM,CAAC;oBACP,IAAI,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;gBACzB,CAAC;YACF,CAAC;QACF,CAAC,CAAC;QAtBD,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,UAAU,CAAC,SAAS,EAAE,CAAC;QAC1C,IAAI,CAAC,eAAe,GAAG,IAAI,CAAC,eAAe,EAAE,CAAC;QAC9C,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,WAAW,EAAE,IAAI,CAAC,YAAY,CAAC,CAAC;QAC/C,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,cAAc,EAAE,IAAI,CAAC,YAAY,CAAC,CAAC;QAClD,UAAU,CAAC,EAAE,CAAC,WAAW,EAAE,IAAI,CAAC,YAAY,CAAC,CAAC;QAC9C,UAAU,CAAC,EAAE,CAAC,cAAc,EAAE,IAAI,CAAC,YAAY,CAAC,CAAC;IAClD,CAAC;IAEM,QAAQ;QACd,OAAO,IAAI,CAAC,eAAe,CAAC;IAC7B,CAAC;IAcO,eAAe;QACtB,uGAAuG;QACvG,IAAI,IAAI,CAAC,UAAU,CAAC,WAAW,KAAK,WAAW,CAAC,QAAQ,EAAE,CAAC;YAC1D,OAAO,IAAI,CAAC;QACb,CAAC;QAED,kEAAkE;QAClE,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,SAAS,EAAE,CAAC;YAChC,OAAO,KAAK,CAAC;QACd,CAAC;QAED,yDAAyD;QACzD,MAAM,CACL,IAAI,CAAC,UAAU,CAAC,QAAQ,KAAK,SAAS,EAEtC,KAAK,CAAC,4CAA4C,CAClD,CAAC;QAEF,MAAM,mBAAmB,GAAG,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;QAC5E,wEAAwE;QACxE,IAAI,mBAAmB,KAAK,SAAS,EAAE,CAAC;YACvC,OAAO,KAAK,CAAC;QACd,CAAC;QAED,MAAM,OAAO,GAAG,IAAI,CAAC,MAAM,CAAC,UAAU,EAAE,CAAC;QACzC,KAAK,MAAM,eAAe,IAAI,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC;YAChD,IAAI,eAAe,CAAC,cAAc,GAAG,mBAAmB,CAAC,cAAc,EAAE,CAAC;gBACzE,OAAO,KAAK,CAAC;YACd,CAAC;QACF,CAAC;QAED,oCAAoC;QACpC,OAAO,IAAI,CAAC;IACb,CAAC;CACD","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { TypedEventEmitter } from \"@fluid-internal/client-utils\";\nimport { AttachState } from \"@fluidframework/container-definitions\";\nimport { assert } from \"@fluidframework/core-utils/internal\";\nimport type { IQuorumClients } from \"@fluidframework/driver-definitions\";\n\nimport type {\n\tIOldestClientObservable,\n\tIOldestClientObserver,\n\tIOldestClientObserverEvents,\n} from \"./interfaces.js\";\n\n/**\n * The `OldestClientObserver` is a utility inspect if the local client is the oldest amongst connected clients (in\n * terms of when they connected) and watch for changes.\n *\n * It is still experimental and under development. Please do try it out, but expect breaking changes in the future.\n *\n * @remarks\n * ### Creation\n *\n * The `OldestClientObserver` constructor takes an `IOldestClientObservable`. This is most easily satisfied with\n * either an `IContainerRuntime` or an `IFluidDataStoreRuntime`:\n *\n * ```typescript\n * // E.g. from within a BaseContainerRuntimeFactory:\n * protected async containerHasInitialized(runtime: IContainerRuntime) {\n * const oldestClientObserver = new OldestClientObserver(runtime);\n * // ...\n * }\n * ```\n *\n * ```typescript\n * // From within a DataObject\n * protected async hasInitialized() {\n * const oldestClientObserver = new OldestClientObserver(this.runtime);\n * // ...\n * }\n * ```\n *\n * ### Usage\n *\n * To check if the local client is the oldest, use the `isOldest()` method.\n *\n * ```typescript\n * if (oldestClientObserver.isOldest()) {\n * console.log(\"I'm the oldest\");\n * } else {\n * console.log(\"Someone else is older\");\n * }\n * ```\n *\n * ### Eventing\n *\n * `OldestClientObserver` is an `EventEmitter`, and will emit events when the local client becomes the oldest and when\n * it is no longer the oldest.\n *\n * ```typescript\n * oldestClientObserver.on(\"becameOldest\", () => {\n * console.log(\"I'm the oldest now\");\n * });\n *\n * oldestClientObserver.on(\"lostOldest\", () => {\n * console.log(\"I'm not the oldest anymore\");\n * });\n * ```\n * @legacy\n * @alpha\n */\nexport class OldestClientObserver\n\textends TypedEventEmitter<IOldestClientObserverEvents>\n\timplements IOldestClientObserver\n{\n\tprivate readonly quorum: IQuorumClients;\n\tprivate currentIsOldest: boolean = false;\n\tconstructor(private readonly observable: IOldestClientObservable) {\n\t\tsuper();\n\t\tthis.quorum = this.observable.getQuorum();\n\t\tthis.currentIsOldest = this.computeIsOldest();\n\t\tthis.quorum.on(\"addMember\", this.updateOldest);\n\t\tthis.quorum.on(\"removeMember\", this.updateOldest);\n\t\tobservable.on(\"connected\", this.updateOldest);\n\t\tobservable.on(\"disconnected\", this.updateOldest);\n\t}\n\n\tpublic isOldest(): boolean {\n\t\treturn this.currentIsOldest;\n\t}\n\n\tprivate readonly updateOldest = (): void => {\n\t\tconst oldest = this.computeIsOldest();\n\t\tif (this.currentIsOldest !== oldest) {\n\t\t\tthis.currentIsOldest = oldest;\n\t\t\tif (oldest) {\n\t\t\t\tthis.emit(\"becameOldest\");\n\t\t\t} else {\n\t\t\t\tthis.emit(\"lostOldest\");\n\t\t\t}\n\t\t}\n\t};\n\n\tprivate computeIsOldest(): boolean {\n\t\t// If the container is detached, we are the only ones that know about it and are the oldest by default.\n\t\tif (this.observable.attachState === AttachState.Detached) {\n\t\t\treturn true;\n\t\t}\n\n\t\t// If we're not connected we can't be the oldest connected client.\n\t\tif (!this.observable.connected) {\n\t\t\treturn false;\n\t\t}\n\n\t\t// TODO: Clean up error code linter violations repo-wide.\n\t\tassert(\n\t\t\tthis.observable.clientId !== undefined,\n\n\t\t\t0x1da /* \"Client id should be set if connected\" */,\n\t\t);\n\n\t\tconst selfSequencedClient = this.quorum.getMember(this.observable.clientId);\n\t\t// When in readonly mode our clientId will not be present in the quorum.\n\t\tif (selfSequencedClient === undefined) {\n\t\t\treturn false;\n\t\t}\n\n\t\tconst members = this.quorum.getMembers();\n\t\tfor (const sequencedClient of members.values()) {\n\t\t\tif (sequencedClient.sequenceNumber < selfSequencedClient.sequenceNumber) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\n\t\t// No member of the quorum was older\n\t\treturn true;\n\t}\n}\n"]}
1
+ {"version":3,"file":"oldestClientObserver.js","sourceRoot":"","sources":["../src/oldestClientObserver.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AACjE,OAAO,EAAE,WAAW,EAAE,MAAM,uCAAuC,CAAC;AACpE,OAAO,EAAE,MAAM,EAAE,MAAM,qCAAqC,CAAC;AAS7D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwDG;AACH,MAAM,OAAO,oBACZ,SAAQ,iBAA8C;IAKtD,YAA6B,UAAmC;QAC/D,KAAK,EAAE,CAAC;QADoB,eAAU,GAAV,UAAU,CAAyB;QADxD,oBAAe,GAAY,KAAK,CAAC;QAexB,iBAAY,GAAG,GAAS,EAAE;YAC1C,MAAM,MAAM,GAAG,IAAI,CAAC,eAAe,EAAE,CAAC;YACtC,IAAI,IAAI,CAAC,eAAe,KAAK,MAAM,EAAE,CAAC;gBACrC,IAAI,CAAC,eAAe,GAAG,MAAM,CAAC;gBAC9B,IAAI,MAAM,EAAE,CAAC;oBACZ,IAAI,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;gBAC3B,CAAC;qBAAM,CAAC;oBACP,IAAI,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;gBACzB,CAAC;YACF,CAAC;QACF,CAAC,CAAC;QAtBD,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,UAAU,CAAC,SAAS,EAAE,CAAC;QAC1C,IAAI,CAAC,eAAe,GAAG,IAAI,CAAC,eAAe,EAAE,CAAC;QAC9C,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,WAAW,EAAE,IAAI,CAAC,YAAY,CAAC,CAAC;QAC/C,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,cAAc,EAAE,IAAI,CAAC,YAAY,CAAC,CAAC;QAClD,UAAU,CAAC,EAAE,CAAC,WAAW,EAAE,IAAI,CAAC,YAAY,CAAC,CAAC;QAC9C,UAAU,CAAC,EAAE,CAAC,cAAc,EAAE,IAAI,CAAC,YAAY,CAAC,CAAC;IAClD,CAAC;IAEM,QAAQ;QACd,OAAO,IAAI,CAAC,eAAe,CAAC;IAC7B,CAAC;IAcO,eAAe;QACtB,uGAAuG;QACvG,IAAI,IAAI,CAAC,UAAU,CAAC,WAAW,KAAK,WAAW,CAAC,QAAQ,EAAE,CAAC;YAC1D,OAAO,IAAI,CAAC;QACb,CAAC;QAED,kEAAkE;QAClE,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,SAAS,EAAE,CAAC;YAChC,OAAO,KAAK,CAAC;QACd,CAAC;QAED,yDAAyD;QACzD,MAAM,CACL,IAAI,CAAC,UAAU,CAAC,QAAQ,KAAK,SAAS,EAEtC,KAAK,CAAC,4CAA4C,CAClD,CAAC;QAEF,MAAM,mBAAmB,GAAG,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;QAC5E,wEAAwE;QACxE,IAAI,mBAAmB,KAAK,SAAS,EAAE,CAAC;YACvC,OAAO,KAAK,CAAC;QACd,CAAC;QAED,MAAM,OAAO,GAAG,IAAI,CAAC,MAAM,CAAC,UAAU,EAAE,CAAC;QACzC,KAAK,MAAM,eAAe,IAAI,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC;YAChD,IAAI,eAAe,CAAC,cAAc,GAAG,mBAAmB,CAAC,cAAc,EAAE,CAAC;gBACzE,OAAO,KAAK,CAAC;YACd,CAAC;QACF,CAAC;QAED,oCAAoC;QACpC,OAAO,IAAI,CAAC;IACb,CAAC;CACD","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { TypedEventEmitter } from \"@fluid-internal/client-utils\";\nimport { AttachState } from \"@fluidframework/container-definitions\";\nimport { assert } from \"@fluidframework/core-utils/internal\";\nimport type { IQuorumClients } from \"@fluidframework/driver-definitions\";\n\nimport type {\n\tIOldestClientObservable,\n\tIOldestClientObserver,\n\tIOldestClientObserverEvents,\n} from \"./interfaces.js\";\n\n/**\n * The `OldestClientObserver` is a utility inspect if the local client is the oldest amongst connected clients (in\n * terms of when they connected) and watch for changes.\n *\n * It is still experimental and under development. Please do try it out, but expect breaking changes in the future.\n *\n * @remarks\n * ### Creation\n *\n * The `OldestClientObserver` constructor takes an `IOldestClientObservable`. This is most easily satisfied with\n * either an `IContainerRuntime` or an `IFluidDataStoreRuntime`:\n *\n * ```typescript\n * // E.g. from within a BaseContainerRuntimeFactory:\n * protected async containerHasInitialized(runtime: IContainerRuntime) {\n * const oldestClientObserver = new OldestClientObserver(runtime);\n * // ...\n * }\n * ```\n *\n * ```typescript\n * // From within a DataObject\n * protected async hasInitialized() {\n * const oldestClientObserver = new OldestClientObserver(this.runtime);\n * // ...\n * }\n * ```\n *\n * ### Usage\n *\n * To check if the local client is the oldest, use the `isOldest()` method.\n *\n * ```typescript\n * if (oldestClientObserver.isOldest()) {\n * console.log(\"I'm the oldest\");\n * } else {\n * console.log(\"Someone else is older\");\n * }\n * ```\n *\n * ### Eventing\n *\n * `OldestClientObserver` is an `EventEmitter`, and will emit events when the local client becomes the oldest and when\n * it is no longer the oldest.\n *\n * ```typescript\n * oldestClientObserver.on(\"becameOldest\", () => {\n * console.log(\"I'm the oldest now\");\n * });\n *\n * oldestClientObserver.on(\"lostOldest\", () => {\n * console.log(\"I'm not the oldest anymore\");\n * });\n * ```\n * @legacy\n * @beta\n */\nexport class OldestClientObserver\n\textends TypedEventEmitter<IOldestClientObserverEvents>\n\timplements IOldestClientObserver\n{\n\tprivate readonly quorum: IQuorumClients;\n\tprivate currentIsOldest: boolean = false;\n\tconstructor(private readonly observable: IOldestClientObservable) {\n\t\tsuper();\n\t\tthis.quorum = this.observable.getQuorum();\n\t\tthis.currentIsOldest = this.computeIsOldest();\n\t\tthis.quorum.on(\"addMember\", this.updateOldest);\n\t\tthis.quorum.on(\"removeMember\", this.updateOldest);\n\t\tobservable.on(\"connected\", this.updateOldest);\n\t\tobservable.on(\"disconnected\", this.updateOldest);\n\t}\n\n\tpublic isOldest(): boolean {\n\t\treturn this.currentIsOldest;\n\t}\n\n\tprivate readonly updateOldest = (): void => {\n\t\tconst oldest = this.computeIsOldest();\n\t\tif (this.currentIsOldest !== oldest) {\n\t\t\tthis.currentIsOldest = oldest;\n\t\t\tif (oldest) {\n\t\t\t\tthis.emit(\"becameOldest\");\n\t\t\t} else {\n\t\t\t\tthis.emit(\"lostOldest\");\n\t\t\t}\n\t\t}\n\t};\n\n\tprivate computeIsOldest(): boolean {\n\t\t// If the container is detached, we are the only ones that know about it and are the oldest by default.\n\t\tif (this.observable.attachState === AttachState.Detached) {\n\t\t\treturn true;\n\t\t}\n\n\t\t// If we're not connected we can't be the oldest connected client.\n\t\tif (!this.observable.connected) {\n\t\t\treturn false;\n\t\t}\n\n\t\t// TODO: Clean up error code linter violations repo-wide.\n\t\tassert(\n\t\t\tthis.observable.clientId !== undefined,\n\n\t\t\t0x1da /* \"Client id should be set if connected\" */,\n\t\t);\n\n\t\tconst selfSequencedClient = this.quorum.getMember(this.observable.clientId);\n\t\t// When in readonly mode our clientId will not be present in the quorum.\n\t\tif (selfSequencedClient === undefined) {\n\t\t\treturn false;\n\t\t}\n\n\t\tconst members = this.quorum.getMembers();\n\t\tfor (const sequencedClient of members.values()) {\n\t\t\tif (sequencedClient.sequenceNumber < selfSequencedClient.sequenceNumber) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\n\t\t// No member of the quorum was older\n\t\treturn true;\n\t}\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fluid-experimental/oldest-client-observer",
3
- "version": "2.53.1",
3
+ "version": "2.60.0",
4
4
  "description": "Data object to determine if the local client is the oldest amongst connected clients",
5
5
  "homepage": "https://fluidframework.com",
6
6
  "repository": {
@@ -69,20 +69,20 @@
69
69
  "temp-directory": "nyc/.nyc_output"
70
70
  },
71
71
  "dependencies": {
72
- "@fluid-internal/client-utils": "~2.53.1",
73
- "@fluidframework/container-definitions": "~2.53.1",
74
- "@fluidframework/core-interfaces": "~2.53.1",
75
- "@fluidframework/core-utils": "~2.53.1",
76
- "@fluidframework/driver-definitions": "~2.53.1"
72
+ "@fluid-internal/client-utils": "~2.60.0",
73
+ "@fluidframework/container-definitions": "~2.60.0",
74
+ "@fluidframework/core-interfaces": "~2.60.0",
75
+ "@fluidframework/core-utils": "~2.60.0",
76
+ "@fluidframework/driver-definitions": "~2.60.0"
77
77
  },
78
78
  "devDependencies": {
79
79
  "@arethetypeswrong/cli": "^0.17.1",
80
80
  "@biomejs/biome": "~1.9.3",
81
- "@fluid-private/test-dds-utils": "~2.53.1",
81
+ "@fluid-private/test-dds-utils": "~2.60.0",
82
82
  "@fluid-tools/build-cli": "^0.57.0",
83
83
  "@fluidframework/build-common": "^2.0.3",
84
84
  "@fluidframework/build-tools": "^0.57.0",
85
- "@fluidframework/eslint-config-fluid": "^5.7.4",
85
+ "@fluidframework/eslint-config-fluid": "^6.0.0",
86
86
  "@microsoft/api-extractor": "7.52.8",
87
87
  "@types/node": "^18.19.0",
88
88
  "concurrently": "^8.2.1",
package/src/interfaces.ts CHANGED
@@ -10,7 +10,7 @@ import type { IQuorumClients } from "@fluidframework/driver-definitions";
10
10
  /**
11
11
  * Events emitted by {@link IOldestClientObservable}.
12
12
  * @legacy
13
- * @alpha
13
+ * @beta
14
14
  */
15
15
  export interface IOldestClientObservableEvents extends IEvent {
16
16
  (event: "connected", listener: () => void);
@@ -23,7 +23,7 @@ export interface IOldestClientObservableEvents extends IEvent {
23
23
  * since neither is really the source of truth (they are just the only currently-available plumbing options).
24
24
  * It's information about the connection, so the real source of truth is lower (at the connection layer).
25
25
  * @legacy
26
- * @alpha
26
+ * @beta
27
27
  */
28
28
  export interface IOldestClientObservable
29
29
  extends IEventProvider<IOldestClientObservableEvents> {
@@ -41,7 +41,7 @@ export interface IOldestClientObservable
41
41
  /**
42
42
  * Events emitted by {@link IOldestClientObservable}.
43
43
  * @legacy
44
- * @alpha
44
+ * @beta
45
45
  */
46
46
  export interface IOldestClientObserverEvents extends IEvent {
47
47
  (event: "becameOldest" | "lostOldest", listener: () => void);
@@ -49,7 +49,7 @@ export interface IOldestClientObserverEvents extends IEvent {
49
49
 
50
50
  /**
51
51
  * @legacy
52
- * @alpha
52
+ * @beta
53
53
  */
54
54
  export interface IOldestClientObserver extends IEventProvider<IOldestClientObserverEvents> {
55
55
  isOldest(): boolean;
@@ -69,7 +69,7 @@ import type {
69
69
  * });
70
70
  * ```
71
71
  * @legacy
72
- * @alpha
72
+ * @beta
73
73
  */
74
74
  export class OldestClientObserver
75
75
  extends TypedEventEmitter<IOldestClientObserverEvents>