@fluid-experimental/data-objects 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.
package/.eslintrc.js CHANGED
@@ -4,24 +4,24 @@
4
4
  */
5
5
 
6
6
  module.exports = {
7
- extends: [require.resolve("@fluidframework/eslint-config-fluid/minimal"), "prettier"],
8
- parserOptions: {
9
- project: ["./tsconfig.json"],
10
- },
11
- rules: {
12
- "@typescript-eslint/strict-boolean-expressions": "off",
7
+ extends: [require.resolve("@fluidframework/eslint-config-fluid/minimal"), "prettier"],
8
+ parserOptions: {
9
+ project: ["./tsconfig.json"],
10
+ },
11
+ rules: {
12
+ "@typescript-eslint/strict-boolean-expressions": "off",
13
13
 
14
- // This library is used in the browser, so we don't want dependencies on most node libraries.
15
- "import/no-nodejs-modules": ["error", { allow: ["events"] }],
16
- },
17
- overrides: [
18
- {
19
- // Rules only for test files
20
- files: ["*.spec.ts", "src/test/**"],
21
- rules: {
22
- // Test files are run in node only so additional node libraries can be used.
23
- "import/no-nodejs-modules": ["error", { allow: ["assert", "events"] }],
24
- },
25
- },
26
- ],
14
+ // This library is used in the browser, so we don't want dependencies on most node libraries.
15
+ "import/no-nodejs-modules": ["error", { allow: ["events"] }],
16
+ },
17
+ overrides: [
18
+ {
19
+ // Rules only for test files
20
+ files: ["*.spec.ts", "src/test/**"],
21
+ rules: {
22
+ // Test files are run in node only so additional node libraries can be used.
23
+ "import/no-nodejs-modules": ["error", { allow: ["assert", "events"] }],
24
+ },
25
+ },
26
+ ],
27
27
  };
@@ -1,4 +1,4 @@
1
1
  {
2
- "$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json",
3
- "extends": "@fluidframework/build-common/api-extractor-common.json"
2
+ "$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json",
3
+ "extends": "@fluidframework/build-common/api-extractor-common.json"
4
4
  }
@@ -24,12 +24,12 @@ export interface ISignaler {
24
24
  */
25
25
  onSignal(signalName: string, listener: SignalListener): ISignaler;
26
26
  /**
27
- * Remove a listener for the specified signal. It behaves in the same way as EventEmitter's
28
- * `off` method regarding multiple registrations, removal order, etc.
29
- * @param signalName - The name of the signal
30
- * @param listener - The callback signal handler to remove
31
- * @returns This ISignaler
32
- */
27
+ * Remove a listener for the specified signal. It behaves in the same way as EventEmitter's
28
+ * `off` method regarding multiple registrations, removal order, etc.
29
+ * @param signalName - The name of the signal
30
+ * @param listener - The callback signal handler to remove
31
+ * @returns This ISignaler
32
+ */
33
33
  offSignal(signalName: string, listener: SignalListener): ISignaler;
34
34
  /**
35
35
  * Send a signal with payload to its connected listeners.
@@ -1 +1 @@
1
- {"version":3,"file":"signaler.d.ts","sourceRoot":"","sources":["../../src/signaler/signaler.ts"],"names":[],"mappings":"AAAA;;;GAGG;;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AACtC,OAAO,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AACzE,OAAO,EAAE,WAAW,EAAE,MAAM,oCAAoC,CAAC;AAEjE,OAAO,EAAE,QAAQ,EAAE,MAAM,uCAAuC,CAAC;AACjE,OAAO,EAAE,qBAAqB,EAAE,MAAM,qCAAqC,CAAC;AAM5E,oBAAY,cAAc,GAAG,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,QAAQ,KAAK,IAAI,CAAC;AAE3F;;;;GAIG;AACH,MAAM,WAAW,SAAS;IACtB;;;;;;OAMG;IACH,QAAQ,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,cAAc,GAAG,SAAS,CAAC;IACjE;;;;;;MAME;IACH,SAAS,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,cAAc,GAAG,SAAS,CAAC;IACnE;;;;OAIG;IACH,YAAY,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,QAAQ,OAAE;CACxD;AAED;;;GAGG;AACH,MAAM,WAAW,gBAAgB;IAC7B,SAAS,EAAE,OAAO,CAAC;IACnB,EAAE,CAAC,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC,OAAO,EAAE,qBAAqB,EAAE,KAAK,EAAE,OAAO,KAAK,IAAI,OAAE;IACxF,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,GAAG,IAAI,CAAC;CAClD;AA+ED;;;GAGG;AACH,qBAAa,QAAS,SAAQ,UAAU,CAAC;IAAE,MAAM,EAAE,WAAW,CAAC;CAAE,CAAE,YAAW,YAAY,EAAE,SAAS;IACjG,OAAO,CAAC,SAAS,CAA+B;IAChD,OAAO,KAAK,QAAQ,GAGnB;IAED,WAAkB,IAAI,WAAwC;IAE9D,gBAAuB,OAAO;gBATiB,WAAW;OAcxD;cAEc,cAAc;IASvB,QAAQ,CACX,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,cAAc,GACzB,SAAS;IAKL,SAAS,CACZ,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,cAAc,GACzB,SAAS;IAKL,YAAY,CACf,UAAU,EAAE,MAAM,EAClB,OAAO,CAAC,EAAE,QAAQ;CAIzB"}
1
+ {"version":3,"file":"signaler.d.ts","sourceRoot":"","sources":["../../src/signaler/signaler.ts"],"names":[],"mappings":"AAAA;;;GAGG;;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AACtC,OAAO,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AACzE,OAAO,EAAE,WAAW,EAAE,MAAM,oCAAoC,CAAC;AAEjE,OAAO,EAAE,QAAQ,EAAE,MAAM,uCAAuC,CAAC;AACjE,OAAO,EAAE,qBAAqB,EAAE,MAAM,qCAAqC,CAAC;AAM5E,oBAAY,cAAc,GAAG,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,QAAQ,KAAK,IAAI,CAAC;AAE3F;;;;GAIG;AACH,MAAM,WAAW,SAAS;IACzB;;;;;;OAMG;IACH,QAAQ,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,cAAc,GAAG,SAAS,CAAC;IAClE;;;;;;OAMG;IACH,SAAS,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,cAAc,GAAG,SAAS,CAAC;IACnE;;;;OAIG;IACH,YAAY,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,QAAQ,OAAE;CACrD;AAED;;;GAGG;AACH,MAAM,WAAW,gBAAgB;IAChC,SAAS,EAAE,OAAO,CAAC;IACnB,EAAE,CAAC,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC,OAAO,EAAE,qBAAqB,EAAE,KAAK,EAAE,OAAO,KAAK,IAAI,OAAE;IACxF,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,GAAG,IAAI,CAAC;CAC/C;AAsED;;;GAGG;AACH,qBAAa,QACZ,SAAQ,UAAU,CAAC;IAAE,MAAM,EAAE,WAAW,CAAA;CAAE,CAC1C,YAAW,YAAY,EAAE,SAAS;IAElC,OAAO,CAAC,SAAS,CAA+B;IAChD,OAAO,KAAK,QAAQ,GAGnB;IAED,WAAkB,IAAI,WAErB;IAED,gBAAuB,OAAO;gBAbD,WAAW;OAagD;cAExE,cAAc;IASvB,QAAQ,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,cAAc,GAAG,SAAS;IAKjE,SAAS,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,cAAc,GAAG,SAAS;IAKlE,YAAY,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,QAAQ;CAG1D"}
@@ -73,7 +73,9 @@ export class Signaler extends DataObject {
73
73
  assert(this._signaler !== undefined, 0x24b /* "internal signaler should be defined" */);
74
74
  return this._signaler;
75
75
  }
76
- static get Name() { return "@fluid-example/signaler"; }
76
+ static get Name() {
77
+ return "@fluid-example/signaler";
78
+ }
77
79
  async hasInitialized() {
78
80
  this._signaler = new InternalSignaler(this.runtime);
79
81
  this.signaler.on("error", (error) => {
@@ -1 +1 @@
1
- {"version":3,"file":"signaler.js","sourceRoot":"","sources":["../../src/signaler/signaler.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 { EventEmitter } from \"events\";\nimport { DataObject, DataObjectFactory } from \"@fluidframework/aqueduct\";\nimport { IErrorEvent } from \"@fluidframework/common-definitions\";\nimport { assert, TypedEventEmitter } from \"@fluidframework/common-utils\";\nimport { Jsonable } from \"@fluidframework/datastore-definitions\";\nimport { IInboundSignalMessage } from \"@fluidframework/runtime-definitions\";\n\n// TODO:\n// add way to mark with current sequence number for ordering signals relative to ops\n// throttling and batching\n\nexport type SignalListener = (clientId: string, local: boolean, payload: Jsonable) => void;\n\n/**\n * ISignaler defines an interface for working with signals that is similar to the more common\n * eventing patterns of EventEmitter. In addition to sending and responding to signals, it\n * provides explicit methods around signal requests to other connected clients.\n */\nexport interface ISignaler {\n /**\n * Adds a listener for the specified signal. It behaves in the same way as EventEmitter's `on`\n * method regarding multiple registrations, callback order, etc.\n * @param signalName - The name of the signal\n * @param listener - The callback signal handler to add\n * @returns This ISignaler\n */\n onSignal(signalName: string, listener: SignalListener): ISignaler;\n /**\n * Remove a listener for the specified signal. It behaves in the same way as EventEmitter's\n * `off` method regarding multiple registrations, removal order, etc.\n * @param signalName - The name of the signal\n * @param listener - The callback signal handler to remove\n * @returns This ISignaler\n */\n offSignal(signalName: string, listener: SignalListener): ISignaler;\n /**\n * Send a signal with payload to its connected listeners.\n * @param signalName - The name of the signal\n * @param payload - The data to send with the signal\n */\n submitSignal(signalName: string, payload?: Jsonable);\n}\n\n/**\n * Duck type of something that provides the expected signalling functionality:\n * A way to verify we can signal, a way to send a signal, and a way to listen for incoming signals\n */\nexport interface IRuntimeSignaler {\n connected: boolean;\n on(event: \"signal\", listener: (message: IInboundSignalMessage, local: boolean) => void);\n submitSignal(type: string, content: any): void;\n}\n\n/**\n * Note: currently experimental and under development\n *\n * Helper class to assist common scenarios around working with signals. InternalSignaler wraps a runtime\n * object with signaling functionality (e.g. ContainerRuntime or FluidDataStoreRuntime) and can\n * then be used in place of the original signaler. It uses a separate internal EventEmitter to\n * manage callbacks, and thus will reflect that behavior with regards to callback registration and\n * deregistration.\n */\n class InternalSignaler extends TypedEventEmitter<IErrorEvent> implements ISignaler {\n private readonly emitter = new EventEmitter();\n\n private readonly signalerId: string | undefined;\n\n constructor(\n /**\n * Object to wrap that can submit and listen to signals\n */\n private readonly signaler: IRuntimeSignaler,\n /**\n * Optional id to assign to this manager that will be attached to\n * signal names. Useful to avoid collisions if there are multiple\n * signal users at the Container level\n */\n signalerId?: string,\n ) {\n super();\n this.emitter.on(\"error\", (error) => {\n this.emit(\"error\", error);\n });\n this.signalerId = signalerId ? `#${signalerId}` : undefined;\n this.signaler.on(\"signal\", (message: IInboundSignalMessage, local: boolean) => {\n const clientId = message.clientId;\n // Only call listeners when the runtime is connected and if the signal has an\n // identifiable sender clientId. The listener is responsible for deciding how\n // it wants to handle local/remote signals\n if (this.signaler.connected && clientId !== null) {\n this.emitter.emit(message.type, clientId, local, message.content);\n }\n });\n }\n\n private getSignalerSignalName(signalName: string): string {\n return this.signalerId ? `${signalName}${this.signalerId}` : signalName;\n }\n\n // ISignaler methods\n\n public onSignal(\n signalName: string,\n listener: SignalListener,\n ): ISignaler {\n const signalerSignalName = this.getSignalerSignalName(signalName);\n this.emitter.on(signalerSignalName, listener);\n return this;\n }\n\n public offSignal(\n signalName: string,\n listener: SignalListener,\n ): ISignaler {\n const signalerSignalName = this.getSignalerSignalName(signalName);\n this.emitter.off(signalerSignalName, listener);\n return this;\n }\n\n public submitSignal(\n signalName: string,\n payload?: Jsonable,\n ) {\n const signalerSignalName = this.getSignalerSignalName(signalName);\n if (this.signaler.connected) {\n this.signaler.submitSignal(signalerSignalName, payload);\n }\n }\n}\n\n/**\n * DataObject implementation of ISignaler for fluid-static plug-and-play. Allows fluid-static\n * users to get an ISignaler without a custom DO.\n */\nexport class Signaler extends DataObject<{ Events: IErrorEvent; }> implements EventEmitter, ISignaler {\n private _signaler: InternalSignaler | undefined;\n private get signaler(): InternalSignaler {\n assert(this._signaler !== undefined, 0x24b /* \"internal signaler should be defined\" */);\n return this._signaler;\n }\n\n public static get Name() { return \"@fluid-example/signaler\"; }\n\n public static readonly factory = new DataObjectFactory(\n Signaler.Name,\n Signaler,\n [],\n {},\n );\n\n protected async hasInitialized() {\n this._signaler = new InternalSignaler(this.runtime);\n this.signaler.on(\"error\", (error) => {\n this.emit(\"error\", error);\n });\n }\n\n // ISignaler methods Note these are all passthroughs\n\n public onSignal(\n signalName: string,\n listener: SignalListener,\n ): ISignaler {\n this.signaler.onSignal(signalName, listener);\n return this;\n }\n\n public offSignal(\n signalName: string,\n listener: SignalListener,\n ): ISignaler {\n this.signaler.offSignal(signalName, listener);\n return this;\n }\n\n public submitSignal(\n signalName: string,\n payload?: Jsonable,\n ) {\n this.signaler.submitSignal(signalName, payload);\n }\n}\n"]}
1
+ {"version":3,"file":"signaler.js","sourceRoot":"","sources":["../../src/signaler/signaler.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 { EventEmitter } from \"events\";\nimport { DataObject, DataObjectFactory } from \"@fluidframework/aqueduct\";\nimport { IErrorEvent } from \"@fluidframework/common-definitions\";\nimport { assert, TypedEventEmitter } from \"@fluidframework/common-utils\";\nimport { Jsonable } from \"@fluidframework/datastore-definitions\";\nimport { IInboundSignalMessage } from \"@fluidframework/runtime-definitions\";\n\n// TODO:\n// add way to mark with current sequence number for ordering signals relative to ops\n// throttling and batching\n\nexport type SignalListener = (clientId: string, local: boolean, payload: Jsonable) => void;\n\n/**\n * ISignaler defines an interface for working with signals that is similar to the more common\n * eventing patterns of EventEmitter. In addition to sending and responding to signals, it\n * provides explicit methods around signal requests to other connected clients.\n */\nexport interface ISignaler {\n\t/**\n\t * Adds a listener for the specified signal. It behaves in the same way as EventEmitter's `on`\n\t * method regarding multiple registrations, callback order, etc.\n\t * @param signalName - The name of the signal\n\t * @param listener - The callback signal handler to add\n\t * @returns This ISignaler\n\t */\n\tonSignal(signalName: string, listener: SignalListener): ISignaler;\n\t/**\n\t * Remove a listener for the specified signal. It behaves in the same way as EventEmitter's\n\t * `off` method regarding multiple registrations, removal order, etc.\n\t * @param signalName - The name of the signal\n\t * @param listener - The callback signal handler to remove\n\t * @returns This ISignaler\n\t */\n\toffSignal(signalName: string, listener: SignalListener): ISignaler;\n\t/**\n\t * Send a signal with payload to its connected listeners.\n\t * @param signalName - The name of the signal\n\t * @param payload - The data to send with the signal\n\t */\n\tsubmitSignal(signalName: string, payload?: Jsonable);\n}\n\n/**\n * Duck type of something that provides the expected signalling functionality:\n * A way to verify we can signal, a way to send a signal, and a way to listen for incoming signals\n */\nexport interface IRuntimeSignaler {\n\tconnected: boolean;\n\ton(event: \"signal\", listener: (message: IInboundSignalMessage, local: boolean) => void);\n\tsubmitSignal(type: string, content: any): void;\n}\n\n/**\n * Note: currently experimental and under development\n *\n * Helper class to assist common scenarios around working with signals. InternalSignaler wraps a runtime\n * object with signaling functionality (e.g. ContainerRuntime or FluidDataStoreRuntime) and can\n * then be used in place of the original signaler. It uses a separate internal EventEmitter to\n * manage callbacks, and thus will reflect that behavior with regards to callback registration and\n * deregistration.\n */\nclass InternalSignaler extends TypedEventEmitter<IErrorEvent> implements ISignaler {\n\tprivate readonly emitter = new EventEmitter();\n\n\tprivate readonly signalerId: string | undefined;\n\n\tconstructor(\n\t\t/**\n\t\t * Object to wrap that can submit and listen to signals\n\t\t */\n\t\tprivate readonly signaler: IRuntimeSignaler,\n\t\t/**\n\t\t * Optional id to assign to this manager that will be attached to\n\t\t * signal names. Useful to avoid collisions if there are multiple\n\t\t * signal users at the Container level\n\t\t */\n\t\tsignalerId?: string,\n\t) {\n\t\tsuper();\n\t\tthis.emitter.on(\"error\", (error) => {\n\t\t\tthis.emit(\"error\", error);\n\t\t});\n\t\tthis.signalerId = signalerId ? `#${signalerId}` : undefined;\n\t\tthis.signaler.on(\"signal\", (message: IInboundSignalMessage, local: boolean) => {\n\t\t\tconst clientId = message.clientId;\n\t\t\t// Only call listeners when the runtime is connected and if the signal has an\n\t\t\t// identifiable sender clientId. The listener is responsible for deciding how\n\t\t\t// it wants to handle local/remote signals\n\t\t\tif (this.signaler.connected && clientId !== null) {\n\t\t\t\tthis.emitter.emit(message.type, clientId, local, message.content);\n\t\t\t}\n\t\t});\n\t}\n\n\tprivate getSignalerSignalName(signalName: string): string {\n\t\treturn this.signalerId ? `${signalName}${this.signalerId}` : signalName;\n\t}\n\n\t// ISignaler methods\n\n\tpublic onSignal(signalName: string, listener: SignalListener): ISignaler {\n\t\tconst signalerSignalName = this.getSignalerSignalName(signalName);\n\t\tthis.emitter.on(signalerSignalName, listener);\n\t\treturn this;\n\t}\n\n\tpublic offSignal(signalName: string, listener: SignalListener): ISignaler {\n\t\tconst signalerSignalName = this.getSignalerSignalName(signalName);\n\t\tthis.emitter.off(signalerSignalName, listener);\n\t\treturn this;\n\t}\n\n\tpublic submitSignal(signalName: string, payload?: Jsonable) {\n\t\tconst signalerSignalName = this.getSignalerSignalName(signalName);\n\t\tif (this.signaler.connected) {\n\t\t\tthis.signaler.submitSignal(signalerSignalName, payload);\n\t\t}\n\t}\n}\n\n/**\n * DataObject implementation of ISignaler for fluid-static plug-and-play. Allows fluid-static\n * users to get an ISignaler without a custom DO.\n */\nexport class Signaler\n\textends DataObject<{ Events: IErrorEvent }>\n\timplements EventEmitter, ISignaler\n{\n\tprivate _signaler: InternalSignaler | undefined;\n\tprivate get signaler(): InternalSignaler {\n\t\tassert(this._signaler !== undefined, 0x24b /* \"internal signaler should be defined\" */);\n\t\treturn this._signaler;\n\t}\n\n\tpublic static get Name() {\n\t\treturn \"@fluid-example/signaler\";\n\t}\n\n\tpublic static readonly factory = new DataObjectFactory(Signaler.Name, Signaler, [], {});\n\n\tprotected async hasInitialized() {\n\t\tthis._signaler = new InternalSignaler(this.runtime);\n\t\tthis.signaler.on(\"error\", (error) => {\n\t\t\tthis.emit(\"error\", error);\n\t\t});\n\t}\n\n\t// ISignaler methods Note these are all passthroughs\n\n\tpublic onSignal(signalName: string, listener: SignalListener): ISignaler {\n\t\tthis.signaler.onSignal(signalName, listener);\n\t\treturn this;\n\t}\n\n\tpublic offSignal(signalName: string, listener: SignalListener): ISignaler {\n\t\tthis.signaler.offSignal(signalName, listener);\n\t\treturn this;\n\t}\n\n\tpublic submitSignal(signalName: string, payload?: Jsonable) {\n\t\tthis.signaler.submitSignal(signalName, payload);\n\t}\n}\n"]}
@@ -24,12 +24,12 @@ export interface ISignaler {
24
24
  */
25
25
  onSignal(signalName: string, listener: SignalListener): ISignaler;
26
26
  /**
27
- * Remove a listener for the specified signal. It behaves in the same way as EventEmitter's
28
- * `off` method regarding multiple registrations, removal order, etc.
29
- * @param signalName - The name of the signal
30
- * @param listener - The callback signal handler to remove
31
- * @returns This ISignaler
32
- */
27
+ * Remove a listener for the specified signal. It behaves in the same way as EventEmitter's
28
+ * `off` method regarding multiple registrations, removal order, etc.
29
+ * @param signalName - The name of the signal
30
+ * @param listener - The callback signal handler to remove
31
+ * @returns This ISignaler
32
+ */
33
33
  offSignal(signalName: string, listener: SignalListener): ISignaler;
34
34
  /**
35
35
  * Send a signal with payload to its connected listeners.
@@ -1 +1 @@
1
- {"version":3,"file":"signaler.d.ts","sourceRoot":"","sources":["../../src/signaler/signaler.ts"],"names":[],"mappings":"AAAA;;;GAGG;;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AACtC,OAAO,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AACzE,OAAO,EAAE,WAAW,EAAE,MAAM,oCAAoC,CAAC;AAEjE,OAAO,EAAE,QAAQ,EAAE,MAAM,uCAAuC,CAAC;AACjE,OAAO,EAAE,qBAAqB,EAAE,MAAM,qCAAqC,CAAC;AAM5E,oBAAY,cAAc,GAAG,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,QAAQ,KAAK,IAAI,CAAC;AAE3F;;;;GAIG;AACH,MAAM,WAAW,SAAS;IACtB;;;;;;OAMG;IACH,QAAQ,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,cAAc,GAAG,SAAS,CAAC;IACjE;;;;;;MAME;IACH,SAAS,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,cAAc,GAAG,SAAS,CAAC;IACnE;;;;OAIG;IACH,YAAY,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,QAAQ,OAAE;CACxD;AAED;;;GAGG;AACH,MAAM,WAAW,gBAAgB;IAC7B,SAAS,EAAE,OAAO,CAAC;IACnB,EAAE,CAAC,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC,OAAO,EAAE,qBAAqB,EAAE,KAAK,EAAE,OAAO,KAAK,IAAI,OAAE;IACxF,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,GAAG,IAAI,CAAC;CAClD;AA+ED;;;GAGG;AACH,qBAAa,QAAS,SAAQ,UAAU,CAAC;IAAE,MAAM,EAAE,WAAW,CAAC;CAAE,CAAE,YAAW,YAAY,EAAE,SAAS;IACjG,OAAO,CAAC,SAAS,CAA+B;IAChD,OAAO,KAAK,QAAQ,GAGnB;IAED,WAAkB,IAAI,WAAwC;IAE9D,gBAAuB,OAAO;gBATiB,WAAW;OAcxD;cAEc,cAAc;IASvB,QAAQ,CACX,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,cAAc,GACzB,SAAS;IAKL,SAAS,CACZ,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,cAAc,GACzB,SAAS;IAKL,YAAY,CACf,UAAU,EAAE,MAAM,EAClB,OAAO,CAAC,EAAE,QAAQ;CAIzB"}
1
+ {"version":3,"file":"signaler.d.ts","sourceRoot":"","sources":["../../src/signaler/signaler.ts"],"names":[],"mappings":"AAAA;;;GAGG;;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AACtC,OAAO,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AACzE,OAAO,EAAE,WAAW,EAAE,MAAM,oCAAoC,CAAC;AAEjE,OAAO,EAAE,QAAQ,EAAE,MAAM,uCAAuC,CAAC;AACjE,OAAO,EAAE,qBAAqB,EAAE,MAAM,qCAAqC,CAAC;AAM5E,oBAAY,cAAc,GAAG,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,QAAQ,KAAK,IAAI,CAAC;AAE3F;;;;GAIG;AACH,MAAM,WAAW,SAAS;IACzB;;;;;;OAMG;IACH,QAAQ,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,cAAc,GAAG,SAAS,CAAC;IAClE;;;;;;OAMG;IACH,SAAS,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,cAAc,GAAG,SAAS,CAAC;IACnE;;;;OAIG;IACH,YAAY,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,QAAQ,OAAE;CACrD;AAED;;;GAGG;AACH,MAAM,WAAW,gBAAgB;IAChC,SAAS,EAAE,OAAO,CAAC;IACnB,EAAE,CAAC,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC,OAAO,EAAE,qBAAqB,EAAE,KAAK,EAAE,OAAO,KAAK,IAAI,OAAE;IACxF,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,GAAG,IAAI,CAAC;CAC/C;AAsED;;;GAGG;AACH,qBAAa,QACZ,SAAQ,UAAU,CAAC;IAAE,MAAM,EAAE,WAAW,CAAA;CAAE,CAC1C,YAAW,YAAY,EAAE,SAAS;IAElC,OAAO,CAAC,SAAS,CAA+B;IAChD,OAAO,KAAK,QAAQ,GAGnB;IAED,WAAkB,IAAI,WAErB;IAED,gBAAuB,OAAO;gBAbD,WAAW;OAagD;cAExE,cAAc;IASvB,QAAQ,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,cAAc,GAAG,SAAS;IAKjE,SAAS,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,cAAc,GAAG,SAAS;IAKlE,YAAY,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,QAAQ;CAG1D"}
@@ -73,7 +73,9 @@ export class Signaler extends DataObject {
73
73
  assert(this._signaler !== undefined, 0x24b /* "internal signaler should be defined" */);
74
74
  return this._signaler;
75
75
  }
76
- static get Name() { return "@fluid-example/signaler"; }
76
+ static get Name() {
77
+ return "@fluid-example/signaler";
78
+ }
77
79
  async hasInitialized() {
78
80
  this._signaler = new InternalSignaler(this.runtime);
79
81
  this.signaler.on("error", (error) => {
@@ -1 +1 @@
1
- {"version":3,"file":"signaler.js","sourceRoot":"","sources":["../../src/signaler/signaler.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AACtC,OAAO,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAEzE,OAAO,EAAE,MAAM,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AAkDzE;;;;;;;;GAQG;AACF,MAAM,gBAAiB,SAAQ,iBAA8B;IAK1D;IACI;;OAEG;IACc,QAA0B;IAC3C;;;;OAIG;IACH,UAAmB;QAEnB,KAAK,EAAE,CAAC;QARS,aAAQ,GAAR,QAAQ,CAAkB;QAR9B,YAAO,GAAG,IAAI,YAAY,EAAE,CAAC;QAiB1C,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE;YAC/B,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;QAC9B,CAAC,CAAC,CAAC;QACH,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC,CAAC,CAAC,IAAI,UAAU,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;QAC5D,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,QAAQ,EAAE,CAAC,OAA8B,EAAE,KAAc,EAAE,EAAE;YAC1E,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC;YAClC,6EAA6E;YAC7E,8EAA8E;YAC9E,0CAA0C;YAC1C,IAAI,IAAI,CAAC,QAAQ,CAAC,SAAS,IAAI,QAAQ,KAAK,IAAI,EAAE;gBAC9C,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC;aACrE;QACL,CAAC,CAAC,CAAC;IACP,CAAC;IAEO,qBAAqB,CAAC,UAAkB;QAC5C,OAAO,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,GAAG,UAAU,GAAG,IAAI,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC;IAC5E,CAAC;IAED,oBAAoB;IAEb,QAAQ,CACX,UAAkB,EAClB,QAAwB;QAExB,MAAM,kBAAkB,GAAG,IAAI,CAAC,qBAAqB,CAAC,UAAU,CAAC,CAAC;QAClE,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,kBAAkB,EAAE,QAAQ,CAAC,CAAC;QAC9C,OAAO,IAAI,CAAC;IAChB,CAAC;IAEM,SAAS,CACZ,UAAkB,EAClB,QAAwB;QAExB,MAAM,kBAAkB,GAAG,IAAI,CAAC,qBAAqB,CAAC,UAAU,CAAC,CAAC;QAClE,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,kBAAkB,EAAE,QAAQ,CAAC,CAAC;QAC/C,OAAO,IAAI,CAAC;IAChB,CAAC;IAEM,YAAY,CACf,UAAkB,EAClB,OAAkB;QAElB,MAAM,kBAAkB,GAAG,IAAI,CAAC,qBAAqB,CAAC,UAAU,CAAC,CAAC;QAClE,IAAI,IAAI,CAAC,QAAQ,CAAC,SAAS,EAAE;YACzB,IAAI,CAAC,QAAQ,CAAC,YAAY,CAAC,kBAAkB,EAAE,OAAO,CAAC,CAAC;SAC3D;IACL,CAAC;CACJ;AAED;;;GAGG;AACH,MAAM,OAAO,QAAS,SAAQ,UAAoC;IAE9D,IAAY,QAAQ;QAChB,MAAM,CAAC,IAAI,CAAC,SAAS,KAAK,SAAS,EAAE,KAAK,CAAC,2CAA2C,CAAC,CAAC;QACxF,OAAO,IAAI,CAAC,SAAS,CAAC;IAC1B,CAAC;IAEM,MAAM,KAAK,IAAI,KAAK,OAAO,yBAAyB,CAAC,CAAC,CAAC;IASpD,KAAK,CAAC,cAAc;QAC1B,IAAI,CAAC,SAAS,GAAG,IAAI,gBAAgB,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACpD,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE;YAChC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;QAC9B,CAAC,CAAC,CAAC;IACP,CAAC;IAED,qDAAqD;IAE9C,QAAQ,CACX,UAAkB,EAClB,QAAwB;QAExB,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;QAC7C,OAAO,IAAI,CAAC;IAChB,CAAC;IAEM,SAAS,CACZ,UAAkB,EAClB,QAAwB;QAExB,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;QAC9C,OAAO,IAAI,CAAC;IAChB,CAAC;IAEM,YAAY,CACf,UAAkB,EAClB,OAAkB;QAElB,IAAI,CAAC,QAAQ,CAAC,YAAY,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;IACpD,CAAC;;AArCsB,gBAAO,GAAG,IAAI,iBAAiB,CAClD,QAAQ,CAAC,IAAI,EACb,QAAQ,EACR,EAAE,EACF,EAAE,CACL,CAAC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nimport { EventEmitter } from \"events\";\nimport { DataObject, DataObjectFactory } from \"@fluidframework/aqueduct\";\nimport { IErrorEvent } from \"@fluidframework/common-definitions\";\nimport { assert, TypedEventEmitter } from \"@fluidframework/common-utils\";\nimport { Jsonable } from \"@fluidframework/datastore-definitions\";\nimport { IInboundSignalMessage } from \"@fluidframework/runtime-definitions\";\n\n// TODO:\n// add way to mark with current sequence number for ordering signals relative to ops\n// throttling and batching\n\nexport type SignalListener = (clientId: string, local: boolean, payload: Jsonable) => void;\n\n/**\n * ISignaler defines an interface for working with signals that is similar to the more common\n * eventing patterns of EventEmitter. In addition to sending and responding to signals, it\n * provides explicit methods around signal requests to other connected clients.\n */\nexport interface ISignaler {\n /**\n * Adds a listener for the specified signal. It behaves in the same way as EventEmitter's `on`\n * method regarding multiple registrations, callback order, etc.\n * @param signalName - The name of the signal\n * @param listener - The callback signal handler to add\n * @returns This ISignaler\n */\n onSignal(signalName: string, listener: SignalListener): ISignaler;\n /**\n * Remove a listener for the specified signal. It behaves in the same way as EventEmitter's\n * `off` method regarding multiple registrations, removal order, etc.\n * @param signalName - The name of the signal\n * @param listener - The callback signal handler to remove\n * @returns This ISignaler\n */\n offSignal(signalName: string, listener: SignalListener): ISignaler;\n /**\n * Send a signal with payload to its connected listeners.\n * @param signalName - The name of the signal\n * @param payload - The data to send with the signal\n */\n submitSignal(signalName: string, payload?: Jsonable);\n}\n\n/**\n * Duck type of something that provides the expected signalling functionality:\n * A way to verify we can signal, a way to send a signal, and a way to listen for incoming signals\n */\nexport interface IRuntimeSignaler {\n connected: boolean;\n on(event: \"signal\", listener: (message: IInboundSignalMessage, local: boolean) => void);\n submitSignal(type: string, content: any): void;\n}\n\n/**\n * Note: currently experimental and under development\n *\n * Helper class to assist common scenarios around working with signals. InternalSignaler wraps a runtime\n * object with signaling functionality (e.g. ContainerRuntime or FluidDataStoreRuntime) and can\n * then be used in place of the original signaler. It uses a separate internal EventEmitter to\n * manage callbacks, and thus will reflect that behavior with regards to callback registration and\n * deregistration.\n */\n class InternalSignaler extends TypedEventEmitter<IErrorEvent> implements ISignaler {\n private readonly emitter = new EventEmitter();\n\n private readonly signalerId: string | undefined;\n\n constructor(\n /**\n * Object to wrap that can submit and listen to signals\n */\n private readonly signaler: IRuntimeSignaler,\n /**\n * Optional id to assign to this manager that will be attached to\n * signal names. Useful to avoid collisions if there are multiple\n * signal users at the Container level\n */\n signalerId?: string,\n ) {\n super();\n this.emitter.on(\"error\", (error) => {\n this.emit(\"error\", error);\n });\n this.signalerId = signalerId ? `#${signalerId}` : undefined;\n this.signaler.on(\"signal\", (message: IInboundSignalMessage, local: boolean) => {\n const clientId = message.clientId;\n // Only call listeners when the runtime is connected and if the signal has an\n // identifiable sender clientId. The listener is responsible for deciding how\n // it wants to handle local/remote signals\n if (this.signaler.connected && clientId !== null) {\n this.emitter.emit(message.type, clientId, local, message.content);\n }\n });\n }\n\n private getSignalerSignalName(signalName: string): string {\n return this.signalerId ? `${signalName}${this.signalerId}` : signalName;\n }\n\n // ISignaler methods\n\n public onSignal(\n signalName: string,\n listener: SignalListener,\n ): ISignaler {\n const signalerSignalName = this.getSignalerSignalName(signalName);\n this.emitter.on(signalerSignalName, listener);\n return this;\n }\n\n public offSignal(\n signalName: string,\n listener: SignalListener,\n ): ISignaler {\n const signalerSignalName = this.getSignalerSignalName(signalName);\n this.emitter.off(signalerSignalName, listener);\n return this;\n }\n\n public submitSignal(\n signalName: string,\n payload?: Jsonable,\n ) {\n const signalerSignalName = this.getSignalerSignalName(signalName);\n if (this.signaler.connected) {\n this.signaler.submitSignal(signalerSignalName, payload);\n }\n }\n}\n\n/**\n * DataObject implementation of ISignaler for fluid-static plug-and-play. Allows fluid-static\n * users to get an ISignaler without a custom DO.\n */\nexport class Signaler extends DataObject<{ Events: IErrorEvent; }> implements EventEmitter, ISignaler {\n private _signaler: InternalSignaler | undefined;\n private get signaler(): InternalSignaler {\n assert(this._signaler !== undefined, 0x24b /* \"internal signaler should be defined\" */);\n return this._signaler;\n }\n\n public static get Name() { return \"@fluid-example/signaler\"; }\n\n public static readonly factory = new DataObjectFactory(\n Signaler.Name,\n Signaler,\n [],\n {},\n );\n\n protected async hasInitialized() {\n this._signaler = new InternalSignaler(this.runtime);\n this.signaler.on(\"error\", (error) => {\n this.emit(\"error\", error);\n });\n }\n\n // ISignaler methods Note these are all passthroughs\n\n public onSignal(\n signalName: string,\n listener: SignalListener,\n ): ISignaler {\n this.signaler.onSignal(signalName, listener);\n return this;\n }\n\n public offSignal(\n signalName: string,\n listener: SignalListener,\n ): ISignaler {\n this.signaler.offSignal(signalName, listener);\n return this;\n }\n\n public submitSignal(\n signalName: string,\n payload?: Jsonable,\n ) {\n this.signaler.submitSignal(signalName, payload);\n }\n}\n"]}
1
+ {"version":3,"file":"signaler.js","sourceRoot":"","sources":["../../src/signaler/signaler.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 { EventEmitter } from \"events\";\nimport { DataObject, DataObjectFactory } from \"@fluidframework/aqueduct\";\nimport { IErrorEvent } from \"@fluidframework/common-definitions\";\nimport { assert, TypedEventEmitter } from \"@fluidframework/common-utils\";\nimport { Jsonable } from \"@fluidframework/datastore-definitions\";\nimport { IInboundSignalMessage } from \"@fluidframework/runtime-definitions\";\n\n// TODO:\n// add way to mark with current sequence number for ordering signals relative to ops\n// throttling and batching\n\nexport type SignalListener = (clientId: string, local: boolean, payload: Jsonable) => void;\n\n/**\n * ISignaler defines an interface for working with signals that is similar to the more common\n * eventing patterns of EventEmitter. In addition to sending and responding to signals, it\n * provides explicit methods around signal requests to other connected clients.\n */\nexport interface ISignaler {\n\t/**\n\t * Adds a listener for the specified signal. It behaves in the same way as EventEmitter's `on`\n\t * method regarding multiple registrations, callback order, etc.\n\t * @param signalName - The name of the signal\n\t * @param listener - The callback signal handler to add\n\t * @returns This ISignaler\n\t */\n\tonSignal(signalName: string, listener: SignalListener): ISignaler;\n\t/**\n\t * Remove a listener for the specified signal. It behaves in the same way as EventEmitter's\n\t * `off` method regarding multiple registrations, removal order, etc.\n\t * @param signalName - The name of the signal\n\t * @param listener - The callback signal handler to remove\n\t * @returns This ISignaler\n\t */\n\toffSignal(signalName: string, listener: SignalListener): ISignaler;\n\t/**\n\t * Send a signal with payload to its connected listeners.\n\t * @param signalName - The name of the signal\n\t * @param payload - The data to send with the signal\n\t */\n\tsubmitSignal(signalName: string, payload?: Jsonable);\n}\n\n/**\n * Duck type of something that provides the expected signalling functionality:\n * A way to verify we can signal, a way to send a signal, and a way to listen for incoming signals\n */\nexport interface IRuntimeSignaler {\n\tconnected: boolean;\n\ton(event: \"signal\", listener: (message: IInboundSignalMessage, local: boolean) => void);\n\tsubmitSignal(type: string, content: any): void;\n}\n\n/**\n * Note: currently experimental and under development\n *\n * Helper class to assist common scenarios around working with signals. InternalSignaler wraps a runtime\n * object with signaling functionality (e.g. ContainerRuntime or FluidDataStoreRuntime) and can\n * then be used in place of the original signaler. It uses a separate internal EventEmitter to\n * manage callbacks, and thus will reflect that behavior with regards to callback registration and\n * deregistration.\n */\nclass InternalSignaler extends TypedEventEmitter<IErrorEvent> implements ISignaler {\n\tprivate readonly emitter = new EventEmitter();\n\n\tprivate readonly signalerId: string | undefined;\n\n\tconstructor(\n\t\t/**\n\t\t * Object to wrap that can submit and listen to signals\n\t\t */\n\t\tprivate readonly signaler: IRuntimeSignaler,\n\t\t/**\n\t\t * Optional id to assign to this manager that will be attached to\n\t\t * signal names. Useful to avoid collisions if there are multiple\n\t\t * signal users at the Container level\n\t\t */\n\t\tsignalerId?: string,\n\t) {\n\t\tsuper();\n\t\tthis.emitter.on(\"error\", (error) => {\n\t\t\tthis.emit(\"error\", error);\n\t\t});\n\t\tthis.signalerId = signalerId ? `#${signalerId}` : undefined;\n\t\tthis.signaler.on(\"signal\", (message: IInboundSignalMessage, local: boolean) => {\n\t\t\tconst clientId = message.clientId;\n\t\t\t// Only call listeners when the runtime is connected and if the signal has an\n\t\t\t// identifiable sender clientId. The listener is responsible for deciding how\n\t\t\t// it wants to handle local/remote signals\n\t\t\tif (this.signaler.connected && clientId !== null) {\n\t\t\t\tthis.emitter.emit(message.type, clientId, local, message.content);\n\t\t\t}\n\t\t});\n\t}\n\n\tprivate getSignalerSignalName(signalName: string): string {\n\t\treturn this.signalerId ? `${signalName}${this.signalerId}` : signalName;\n\t}\n\n\t// ISignaler methods\n\n\tpublic onSignal(signalName: string, listener: SignalListener): ISignaler {\n\t\tconst signalerSignalName = this.getSignalerSignalName(signalName);\n\t\tthis.emitter.on(signalerSignalName, listener);\n\t\treturn this;\n\t}\n\n\tpublic offSignal(signalName: string, listener: SignalListener): ISignaler {\n\t\tconst signalerSignalName = this.getSignalerSignalName(signalName);\n\t\tthis.emitter.off(signalerSignalName, listener);\n\t\treturn this;\n\t}\n\n\tpublic submitSignal(signalName: string, payload?: Jsonable) {\n\t\tconst signalerSignalName = this.getSignalerSignalName(signalName);\n\t\tif (this.signaler.connected) {\n\t\t\tthis.signaler.submitSignal(signalerSignalName, payload);\n\t\t}\n\t}\n}\n\n/**\n * DataObject implementation of ISignaler for fluid-static plug-and-play. Allows fluid-static\n * users to get an ISignaler without a custom DO.\n */\nexport class Signaler\n\textends DataObject<{ Events: IErrorEvent }>\n\timplements EventEmitter, ISignaler\n{\n\tprivate _signaler: InternalSignaler | undefined;\n\tprivate get signaler(): InternalSignaler {\n\t\tassert(this._signaler !== undefined, 0x24b /* \"internal signaler should be defined\" */);\n\t\treturn this._signaler;\n\t}\n\n\tpublic static get Name() {\n\t\treturn \"@fluid-example/signaler\";\n\t}\n\n\tpublic static readonly factory = new DataObjectFactory(Signaler.Name, Signaler, [], {});\n\n\tprotected async hasInitialized() {\n\t\tthis._signaler = new InternalSignaler(this.runtime);\n\t\tthis.signaler.on(\"error\", (error) => {\n\t\t\tthis.emit(\"error\", error);\n\t\t});\n\t}\n\n\t// ISignaler methods Note these are all passthroughs\n\n\tpublic onSignal(signalName: string, listener: SignalListener): ISignaler {\n\t\tthis.signaler.onSignal(signalName, listener);\n\t\treturn this;\n\t}\n\n\tpublic offSignal(signalName: string, listener: SignalListener): ISignaler {\n\t\tthis.signaler.offSignal(signalName, listener);\n\t\treturn this;\n\t}\n\n\tpublic submitSignal(signalName: string, payload?: Jsonable) {\n\t\tthis.signaler.submitSignal(signalName, payload);\n\t}\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fluid-experimental/data-objects",
3
- "version": "2.0.0-internal.3.0.2",
3
+ "version": "2.0.0-internal.3.2.0",
4
4
  "description": "A collection of ready to use Fluid Data Objects",
5
5
  "homepage": "https://fluidframework.com",
6
6
  "repository": {
@@ -14,34 +14,17 @@
14
14
  "main": "dist/index.js",
15
15
  "module": "lib/index.js",
16
16
  "types": "dist/index.d.ts",
17
- "scripts": {
18
- "build": "concurrently npm:build:compile npm:lint",
19
- "build:commonjs": "npm run tsc",
20
- "build:compile": "concurrently npm:build:commonjs npm:build:esnext",
21
- "build:esnext": "tsc --project ./tsconfig.esnext.json",
22
- "build:full": "npm run build",
23
- "build:full:compile": "npm run build:compile",
24
- "clean": "rimraf dist *.tsbuildinfo *.build.log",
25
- "eslint": "eslint --format stylish src",
26
- "eslint:fix": "eslint --format stylish src --fix --fix-type problem,suggestion,layout",
27
- "format": "npm run prettier:fix",
28
- "lint": "npm run eslint",
29
- "lint:fix": "npm run eslint:fix",
30
- "prettier": "prettier --check . --ignore-path ../../../.prettierignore",
31
- "prettier:fix": "prettier --write . --ignore-path ../../../.prettierignore",
32
- "tsc": "tsc"
33
- },
34
17
  "dependencies": {
35
- "@fluidframework/aqueduct": ">=2.0.0-internal.3.0.2 <2.0.0-internal.4.0.0",
18
+ "@fluidframework/aqueduct": ">=2.0.0-internal.3.2.0 <2.0.0-internal.4.0.0",
36
19
  "@fluidframework/common-definitions": "^0.20.1",
37
- "@fluidframework/common-utils": "^1.0.0",
38
- "@fluidframework/datastore-definitions": ">=2.0.0-internal.3.0.2 <2.0.0-internal.4.0.0",
39
- "@fluidframework/map": ">=2.0.0-internal.3.0.2 <2.0.0-internal.4.0.0",
40
- "@fluidframework/runtime-definitions": ">=2.0.0-internal.3.0.2 <2.0.0-internal.4.0.0",
20
+ "@fluidframework/common-utils": "^1.1.1",
21
+ "@fluidframework/datastore-definitions": ">=2.0.0-internal.3.2.0 <2.0.0-internal.4.0.0",
22
+ "@fluidframework/map": ">=2.0.0-internal.3.2.0 <2.0.0-internal.4.0.0",
23
+ "@fluidframework/runtime-definitions": ">=2.0.0-internal.3.2.0 <2.0.0-internal.4.0.0",
41
24
  "events": "^3.1.0"
42
25
  },
43
26
  "devDependencies": {
44
- "@fluid-tools/build-cli": "^0.8.0",
27
+ "@fluid-tools/build-cli": "^0.10.0",
45
28
  "@fluidframework/build-common": "^1.1.0",
46
29
  "@fluidframework/eslint-config-fluid": "^2.0.0",
47
30
  "@microsoft/api-extractor": "^7.22.2",
@@ -59,5 +42,22 @@
59
42
  "disabled": true,
60
43
  "version": "2.0.0-internal.2.1.0",
61
44
  "broken": {}
45
+ },
46
+ "scripts": {
47
+ "build": "concurrently npm:build:compile npm:lint",
48
+ "build:commonjs": "npm run tsc",
49
+ "build:compile": "concurrently npm:build:commonjs npm:build:esnext",
50
+ "build:esnext": "tsc --project ./tsconfig.esnext.json",
51
+ "build:full": "npm run build",
52
+ "build:full:compile": "npm run build:compile",
53
+ "clean": "rimraf dist lib *.tsbuildinfo *.build.log",
54
+ "eslint": "eslint --format stylish src",
55
+ "eslint:fix": "eslint --format stylish src --fix --fix-type problem,suggestion,layout",
56
+ "format": "npm run prettier:fix",
57
+ "lint": "npm run prettier && npm run eslint",
58
+ "lint:fix": "npm run prettier:fix && npm run eslint:fix",
59
+ "prettier": "prettier --check . --ignore-path ../../../.prettierignore",
60
+ "prettier:fix": "prettier --write . --ignore-path ../../../.prettierignore",
61
+ "tsc": "tsc"
62
62
  }
63
- }
63
+ }
@@ -4,5 +4,5 @@
4
4
  */
5
5
 
6
6
  module.exports = {
7
- ...require("@fluidframework/build-common/prettier.config.cjs"),
7
+ ...require("@fluidframework/build-common/prettier.config.cjs"),
8
8
  };
@@ -1,19 +1,22 @@
1
1
  # Signaler
2
+
2
3
  The `Signaler` is a DataObject that can be used to communicate transient data via signals. Transient data refers to impermanent information that is not persisted with the container.
3
4
 
4
5
  ## Usage
6
+
5
7
  User presence scenarios are well-suited for `Signaler`, as users are required to tell other users their own information and their past data is mostly irrelavant. Using `Signaler` over other distributed data structures in these scenarios is beneficial, as its usage does not result in the storage of data that is not useful in the long-term.
6
8
 
7
9
  ### Creation
10
+
8
11
  Just like with DDSes, you can include `Signaler` as a shared object you would like to load in your `FluidContainer` schema.
9
12
 
10
13
  Here is a look at how you would go about loading `Signaler` as part of the initial objects of the container:
11
14
 
12
15
  ```typescript
13
16
  const containerSchema: ContainerSchema = {
14
- initialObjects: {
15
- signaler: Signaler,
16
- },
17
+ initialObjects: {
18
+ signaler: Signaler,
19
+ },
17
20
  };
18
21
 
19
22
  const { container, services } = await client.createContainer(containerSchema);
@@ -26,14 +29,18 @@ const signaler = container.initialObjects.signaler as Signaler;
26
29
  For more information on using `ContainerSchema` to create objects please see [Data modeling](https://fluidframework.com/docs/build/data-modeling/).
27
30
 
28
31
  ## API
32
+
29
33
  `Signaler` provides a few simple methods to send signals and add/remove listeners to specific signals as well:
30
- - `submitSignal(signalName: string, payload?: Jsonable)` - Sends a signal with a payload to its connected listeners
31
- - `onSignal(signalName: string, listener: SignalListener)` - Adds a listener for the specified signal. Same behavior as EventEmitter's `on` method.
32
- - `offSignal(signalName: string, listener: SignalListener)` - Removes a listener for the specified signal. Same behavior as EventEmitter's `off` method.
34
+
35
+ - `submitSignal(signalName: string, payload?: Jsonable)` - Sends a signal with a payload to its connected listeners
36
+ - `onSignal(signalName: string, listener: SignalListener)` - Adds a listener for the specified signal. Same behavior as EventEmitter's `on` method.
37
+ - `offSignal(signalName: string, listener: SignalListener)` - Removes a listener for the specified signal. Same behavior as EventEmitter's `off` method.
33
38
 
34
39
  ## Common Patterns
40
+
35
41
  ### Signal Request
36
- When a client joins a collaboration session, they may need to receive pertinent information immediately after connecting the container. To support this, they can request a specific signal be sent to them from other connected clients within the application. For example, in the [PresenceTracker](https://github.com/microsoft/FluidFramework/tree/main/examples/data-objects/presence-tracker) we define a "focusRequest" signal type that a newly joining client uses to request the focus-state of each currently connected client:
42
+
43
+ When a client joins a collaboration session, they may need to receive pertinent information immediately after connecting the container. To support this, they can request a specific signal be sent to them from other connected clients within the application. For example, in the [PresenceTracker](https://github.com/microsoft/FluidFramework/tree/main/examples/data-objects/presence-tracker) we define a "focusRequest" signal type that a newly joining client uses to request the focus-state of each currently connected client:
37
44
 
38
45
  ```typescript
39
46
  private static readonly focusRequestType = "focusRequest";
@@ -41,7 +48,7 @@ private static readonly focusRequestType = "focusRequest";
41
48
 
42
49
  ```typescript
43
50
  container.on("connected", () => {
44
- this.signaler.submitSignal(FocusTracker.focusRequestType);
51
+ this.signaler.submitSignal(FocusTracker.focusRequestType);
45
52
  });
46
53
  ```
47
54
 
@@ -49,19 +56,21 @@ The connected clients are listening to this focus request signal, and they respo
49
56
 
50
57
  ```typescript
51
58
  this.signaler.onSignal(FocusTracker.focusRequestType, () => {
52
- this.sendFocusSignal(document.hasFocus());
59
+ this.sendFocusSignal(document.hasFocus());
53
60
  });
54
61
  ```
62
+
55
63
  When there are a lot of connected clients, usage of this request pattern can lead to high signal costs incurred from large amounts of signals being submitted all at the same time. While this pattern is helpful when a client is in need of relevant information, to limit signal costs it would be beneficial to examine whether or not the requested data will be quickly avaiable from other events being listened to within the application. The mouse tracking in [PresenceTracker](https://github.com/microsoft/FluidFramework/tree/main/examples/data-objects/presence-tracker) is an example where a newly connecting client is not required to request a signal to receive every current mouse position on the document. Since mouse movements are frequent, the newly connecting client can simply wait to recieve other users mouse positions on their mousemove events.
64
+
56
65
  ### Grouping Signal Types
57
66
 
58
67
  Rather than submitting multiple signal types in response to one specific event, it is more cost-effective to create one seperate signal type for that particular event and listen to that single signal instead. For example, imagine an application using the `Signal Request` pattern where a newly connected client requests for the color, focus state, and currently selected object of every other connected client on the page. If you submit a signal for each type of data requested, it would look something like this:
59
68
 
60
69
  ```typescript
61
70
  container.on("connected", () => {
62
- this.signaler.submitSignal("colorRequest");
63
- this.signaler.submitSignal("focusRequest");
64
- this.signaler.submitSignal("currentlySelectedObjectRequest");
71
+ this.signaler.submitSignal("colorRequest");
72
+ this.signaler.submitSignal("focusRequest");
73
+ this.signaler.submitSignal("currentlySelectedObjectRequest");
65
74
  });
66
75
  ```
67
76
 
@@ -69,7 +78,7 @@ This approach is costly since the amount of signals sent back on request grows l
69
78
 
70
79
  ```typescript
71
80
  container.on("connected", () => {
72
- this.signaler.submitSignal("connectRequest");
81
+ this.signaler.submitSignal("connectRequest");
73
82
  });
74
83
  ```
75
84
 
@@ -22,28 +22,28 @@ export type SignalListener = (clientId: string, local: boolean, payload: Jsonabl
22
22
  * provides explicit methods around signal requests to other connected clients.
23
23
  */
24
24
  export interface ISignaler {
25
- /**
26
- * Adds a listener for the specified signal. It behaves in the same way as EventEmitter's `on`
27
- * method regarding multiple registrations, callback order, etc.
28
- * @param signalName - The name of the signal
29
- * @param listener - The callback signal handler to add
30
- * @returns This ISignaler
31
- */
32
- onSignal(signalName: string, listener: SignalListener): ISignaler;
33
- /**
34
- * Remove a listener for the specified signal. It behaves in the same way as EventEmitter's
35
- * `off` method regarding multiple registrations, removal order, etc.
36
- * @param signalName - The name of the signal
37
- * @param listener - The callback signal handler to remove
38
- * @returns This ISignaler
39
- */
40
- offSignal(signalName: string, listener: SignalListener): ISignaler;
41
- /**
42
- * Send a signal with payload to its connected listeners.
43
- * @param signalName - The name of the signal
44
- * @param payload - The data to send with the signal
45
- */
46
- submitSignal(signalName: string, payload?: Jsonable);
25
+ /**
26
+ * Adds a listener for the specified signal. It behaves in the same way as EventEmitter's `on`
27
+ * method regarding multiple registrations, callback order, etc.
28
+ * @param signalName - The name of the signal
29
+ * @param listener - The callback signal handler to add
30
+ * @returns This ISignaler
31
+ */
32
+ onSignal(signalName: string, listener: SignalListener): ISignaler;
33
+ /**
34
+ * Remove a listener for the specified signal. It behaves in the same way as EventEmitter's
35
+ * `off` method regarding multiple registrations, removal order, etc.
36
+ * @param signalName - The name of the signal
37
+ * @param listener - The callback signal handler to remove
38
+ * @returns This ISignaler
39
+ */
40
+ offSignal(signalName: string, listener: SignalListener): ISignaler;
41
+ /**
42
+ * Send a signal with payload to its connected listeners.
43
+ * @param signalName - The name of the signal
44
+ * @param payload - The data to send with the signal
45
+ */
46
+ submitSignal(signalName: string, payload?: Jsonable);
47
47
  }
48
48
 
49
49
  /**
@@ -51,9 +51,9 @@ export interface ISignaler {
51
51
  * A way to verify we can signal, a way to send a signal, and a way to listen for incoming signals
52
52
  */
53
53
  export interface IRuntimeSignaler {
54
- connected: boolean;
55
- on(event: "signal", listener: (message: IInboundSignalMessage, local: boolean) => void);
56
- submitSignal(type: string, content: any): void;
54
+ connected: boolean;
55
+ on(event: "signal", listener: (message: IInboundSignalMessage, local: boolean) => void);
56
+ submitSignal(type: string, content: any): void;
57
57
  }
58
58
 
59
59
  /**
@@ -65,123 +65,105 @@ export interface IRuntimeSignaler {
65
65
  * manage callbacks, and thus will reflect that behavior with regards to callback registration and
66
66
  * deregistration.
67
67
  */
68
- class InternalSignaler extends TypedEventEmitter<IErrorEvent> implements ISignaler {
69
- private readonly emitter = new EventEmitter();
70
-
71
- private readonly signalerId: string | undefined;
72
-
73
- constructor(
74
- /**
75
- * Object to wrap that can submit and listen to signals
76
- */
77
- private readonly signaler: IRuntimeSignaler,
78
- /**
79
- * Optional id to assign to this manager that will be attached to
80
- * signal names. Useful to avoid collisions if there are multiple
81
- * signal users at the Container level
82
- */
83
- signalerId?: string,
84
- ) {
85
- super();
86
- this.emitter.on("error", (error) => {
87
- this.emit("error", error);
88
- });
89
- this.signalerId = signalerId ? `#${signalerId}` : undefined;
90
- this.signaler.on("signal", (message: IInboundSignalMessage, local: boolean) => {
91
- const clientId = message.clientId;
92
- // Only call listeners when the runtime is connected and if the signal has an
93
- // identifiable sender clientId. The listener is responsible for deciding how
94
- // it wants to handle local/remote signals
95
- if (this.signaler.connected && clientId !== null) {
96
- this.emitter.emit(message.type, clientId, local, message.content);
97
- }
98
- });
99
- }
100
-
101
- private getSignalerSignalName(signalName: string): string {
102
- return this.signalerId ? `${signalName}${this.signalerId}` : signalName;
103
- }
104
-
105
- // ISignaler methods
106
-
107
- public onSignal(
108
- signalName: string,
109
- listener: SignalListener,
110
- ): ISignaler {
111
- const signalerSignalName = this.getSignalerSignalName(signalName);
112
- this.emitter.on(signalerSignalName, listener);
113
- return this;
114
- }
115
-
116
- public offSignal(
117
- signalName: string,
118
- listener: SignalListener,
119
- ): ISignaler {
120
- const signalerSignalName = this.getSignalerSignalName(signalName);
121
- this.emitter.off(signalerSignalName, listener);
122
- return this;
123
- }
124
-
125
- public submitSignal(
126
- signalName: string,
127
- payload?: Jsonable,
128
- ) {
129
- const signalerSignalName = this.getSignalerSignalName(signalName);
130
- if (this.signaler.connected) {
131
- this.signaler.submitSignal(signalerSignalName, payload);
132
- }
133
- }
68
+ class InternalSignaler extends TypedEventEmitter<IErrorEvent> implements ISignaler {
69
+ private readonly emitter = new EventEmitter();
70
+
71
+ private readonly signalerId: string | undefined;
72
+
73
+ constructor(
74
+ /**
75
+ * Object to wrap that can submit and listen to signals
76
+ */
77
+ private readonly signaler: IRuntimeSignaler,
78
+ /**
79
+ * Optional id to assign to this manager that will be attached to
80
+ * signal names. Useful to avoid collisions if there are multiple
81
+ * signal users at the Container level
82
+ */
83
+ signalerId?: string,
84
+ ) {
85
+ super();
86
+ this.emitter.on("error", (error) => {
87
+ this.emit("error", error);
88
+ });
89
+ this.signalerId = signalerId ? `#${signalerId}` : undefined;
90
+ this.signaler.on("signal", (message: IInboundSignalMessage, local: boolean) => {
91
+ const clientId = message.clientId;
92
+ // Only call listeners when the runtime is connected and if the signal has an
93
+ // identifiable sender clientId. The listener is responsible for deciding how
94
+ // it wants to handle local/remote signals
95
+ if (this.signaler.connected && clientId !== null) {
96
+ this.emitter.emit(message.type, clientId, local, message.content);
97
+ }
98
+ });
99
+ }
100
+
101
+ private getSignalerSignalName(signalName: string): string {
102
+ return this.signalerId ? `${signalName}${this.signalerId}` : signalName;
103
+ }
104
+
105
+ // ISignaler methods
106
+
107
+ public onSignal(signalName: string, listener: SignalListener): ISignaler {
108
+ const signalerSignalName = this.getSignalerSignalName(signalName);
109
+ this.emitter.on(signalerSignalName, listener);
110
+ return this;
111
+ }
112
+
113
+ public offSignal(signalName: string, listener: SignalListener): ISignaler {
114
+ const signalerSignalName = this.getSignalerSignalName(signalName);
115
+ this.emitter.off(signalerSignalName, listener);
116
+ return this;
117
+ }
118
+
119
+ public submitSignal(signalName: string, payload?: Jsonable) {
120
+ const signalerSignalName = this.getSignalerSignalName(signalName);
121
+ if (this.signaler.connected) {
122
+ this.signaler.submitSignal(signalerSignalName, payload);
123
+ }
124
+ }
134
125
  }
135
126
 
136
127
  /**
137
128
  * DataObject implementation of ISignaler for fluid-static plug-and-play. Allows fluid-static
138
129
  * users to get an ISignaler without a custom DO.
139
130
  */
140
- export class Signaler extends DataObject<{ Events: IErrorEvent; }> implements EventEmitter, ISignaler {
141
- private _signaler: InternalSignaler | undefined;
142
- private get signaler(): InternalSignaler {
143
- assert(this._signaler !== undefined, 0x24b /* "internal signaler should be defined" */);
144
- return this._signaler;
145
- }
146
-
147
- public static get Name() { return "@fluid-example/signaler"; }
148
-
149
- public static readonly factory = new DataObjectFactory(
150
- Signaler.Name,
151
- Signaler,
152
- [],
153
- {},
154
- );
155
-
156
- protected async hasInitialized() {
157
- this._signaler = new InternalSignaler(this.runtime);
158
- this.signaler.on("error", (error) => {
159
- this.emit("error", error);
160
- });
161
- }
162
-
163
- // ISignaler methods Note these are all passthroughs
164
-
165
- public onSignal(
166
- signalName: string,
167
- listener: SignalListener,
168
- ): ISignaler {
169
- this.signaler.onSignal(signalName, listener);
170
- return this;
171
- }
172
-
173
- public offSignal(
174
- signalName: string,
175
- listener: SignalListener,
176
- ): ISignaler {
177
- this.signaler.offSignal(signalName, listener);
178
- return this;
179
- }
180
-
181
- public submitSignal(
182
- signalName: string,
183
- payload?: Jsonable,
184
- ) {
185
- this.signaler.submitSignal(signalName, payload);
186
- }
131
+ export class Signaler
132
+ extends DataObject<{ Events: IErrorEvent }>
133
+ implements EventEmitter, ISignaler
134
+ {
135
+ private _signaler: InternalSignaler | undefined;
136
+ private get signaler(): InternalSignaler {
137
+ assert(this._signaler !== undefined, 0x24b /* "internal signaler should be defined" */);
138
+ return this._signaler;
139
+ }
140
+
141
+ public static get Name() {
142
+ return "@fluid-example/signaler";
143
+ }
144
+
145
+ public static readonly factory = new DataObjectFactory(Signaler.Name, Signaler, [], {});
146
+
147
+ protected async hasInitialized() {
148
+ this._signaler = new InternalSignaler(this.runtime);
149
+ this.signaler.on("error", (error) => {
150
+ this.emit("error", error);
151
+ });
152
+ }
153
+
154
+ // ISignaler methods Note these are all passthroughs
155
+
156
+ public onSignal(signalName: string, listener: SignalListener): ISignaler {
157
+ this.signaler.onSignal(signalName, listener);
158
+ return this;
159
+ }
160
+
161
+ public offSignal(signalName: string, listener: SignalListener): ISignaler {
162
+ this.signaler.offSignal(signalName, listener);
163
+ return this;
164
+ }
165
+
166
+ public submitSignal(signalName: string, payload?: Jsonable) {
167
+ this.signaler.submitSignal(signalName, payload);
168
+ }
187
169
  }
@@ -1,7 +1,7 @@
1
1
  {
2
- "extends": "./tsconfig.json",
3
- "compilerOptions": {
4
- "outDir": "./lib",
5
- "module": "esnext"
6
- },
2
+ "extends": "./tsconfig.json",
3
+ "compilerOptions": {
4
+ "outDir": "./lib",
5
+ "module": "esnext",
6
+ },
7
7
  }
package/tsconfig.json CHANGED
@@ -1,19 +1,12 @@
1
1
  {
2
- "extends": "@fluidframework/build-common/ts-common-config.json",
3
- "exclude": [
4
- "dist",
5
- "node_modules"
6
- ],
7
- "compilerOptions": {
8
- "module": "esnext",
9
- "rootDir": "./src",
10
- "outDir": "./dist",
11
- "composite": true,
12
- "types": [
13
- "node"
14
- ]
15
- },
16
- "include": [
17
- "src/**/*"
18
- ]
2
+ "extends": "@fluidframework/build-common/ts-common-config.json",
3
+ "exclude": ["dist", "node_modules"],
4
+ "compilerOptions": {
5
+ "module": "esnext",
6
+ "rootDir": "./src",
7
+ "outDir": "./dist",
8
+ "composite": true,
9
+ "types": ["node"],
10
+ },
11
+ "include": ["src/**/*"],
19
12
  }