@fluid-experimental/data-objects 1.2.2 → 2.0.0-internal.1.0.0.82159
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/{lib/signalManager → dist/signaler}/index.d.ts +1 -1
- package/dist/signaler/index.d.ts.map +1 -0
- package/dist/{signalManager → signaler}/index.js +1 -1
- package/dist/signaler/index.js.map +1 -0
- package/dist/{signalManager/signalManager.d.ts → signaler/signaler.d.ts} +6 -42
- package/dist/signaler/signaler.d.ts.map +1 -0
- package/dist/{signalManager/signalManager.js → signaler/signaler.js} +25 -28
- package/dist/signaler/signaler.js.map +1 -0
- package/lib/index.d.ts +1 -1
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +1 -1
- package/lib/index.js.map +1 -1
- package/lib/signaler/index.d.ts +6 -0
- package/lib/signaler/index.d.ts.map +1 -0
- package/lib/{signalManager → signaler}/index.js +1 -1
- package/lib/signaler/index.js.map +1 -0
- package/lib/{signalManager/signalManager.d.ts → signaler/signaler.d.ts} +6 -42
- package/lib/signaler/signaler.d.ts.map +1 -0
- package/lib/{signalManager/signalManager.js → signaler/signaler.js} +25 -28
- package/lib/signaler/signaler.js.map +1 -0
- package/package.json +5 -5
- package/src/index.ts +1 -1
- package/src/signaler/README.md +76 -0
- package/src/{signalManager → signaler}/index.ts +1 -1
- package/src/{signalManager/signalManager.ts → signaler/signaler.ts} +27 -30
- package/dist/signalManager/index.d.ts +0 -6
- package/dist/signalManager/index.d.ts.map +0 -1
- package/dist/signalManager/index.js.map +0 -1
- package/dist/signalManager/signalManager.d.ts.map +0 -1
- package/dist/signalManager/signalManager.js.map +0 -1
- package/lib/signalManager/index.d.ts.map +0 -1
- package/lib/signalManager/index.js.map +0 -1
- package/lib/signalManager/signalManager.d.ts.map +0 -1
- package/lib/signalManager/signalManager.js.map +0 -1
- package/src/signalManager/README.md +0 -55
package/dist/index.d.ts
CHANGED
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,cAAc,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,cAAc,YAAY,CAAC"}
|
package/dist/index.js
CHANGED
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,cAAc,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,cAAc,YAAY,CAAC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nexport * from \"./signaler\";\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/signaler/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,cAAc,YAAY,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/signaler/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,cAAc,YAAY,CAAC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nexport * from \"./signaler\";\n"]}
|
|
@@ -6,7 +6,6 @@
|
|
|
6
6
|
import { EventEmitter } from "events";
|
|
7
7
|
import { DataObject, DataObjectFactory } from "@fluidframework/aqueduct";
|
|
8
8
|
import { IErrorEvent } from "@fluidframework/common-definitions";
|
|
9
|
-
import { TypedEventEmitter } from "@fluidframework/common-utils";
|
|
10
9
|
import { Jsonable } from "@fluidframework/datastore-definitions";
|
|
11
10
|
import { IInboundSignalMessage } from "@fluidframework/runtime-definitions";
|
|
12
11
|
export declare type SignalListener = (clientId: string, local: boolean, payload: Jsonable) => void;
|
|
@@ -49,51 +48,16 @@ export interface IRuntimeSignaler {
|
|
|
49
48
|
submitSignal(type: string, content: any): void;
|
|
50
49
|
}
|
|
51
50
|
/**
|
|
52
|
-
* Note: currently experimental and under development
|
|
53
|
-
*
|
|
54
|
-
* Helper class to assist common scenarios around working with signals. Signaler wraps a runtime
|
|
55
|
-
* object with signaling functionality (e.g. ContainerRuntime or FluidDataStoreRuntime) and can
|
|
56
|
-
* then be used in place of the original signaler. It uses a separate internal EventEmitter to
|
|
57
|
-
* manage callbacks, and thus will reflect that behavior with regards to callback registration and
|
|
58
|
-
* deregistration.
|
|
59
|
-
*/
|
|
60
|
-
export declare class Signaler extends TypedEventEmitter<IErrorEvent> implements ISignaler {
|
|
61
|
-
/**
|
|
62
|
-
* Object to wrap that can submit and listen to signals
|
|
63
|
-
*/
|
|
64
|
-
private readonly signaler;
|
|
65
|
-
private readonly emitter;
|
|
66
|
-
private readonly managerId;
|
|
67
|
-
constructor(
|
|
68
|
-
/**
|
|
69
|
-
* Object to wrap that can submit and listen to signals
|
|
70
|
-
*/
|
|
71
|
-
signaler: IRuntimeSignaler,
|
|
72
|
-
/**
|
|
73
|
-
* Optional id to assign to this manager that will be attached to
|
|
74
|
-
* signal names. Useful to avoid collisions if there are multiple
|
|
75
|
-
* signal users at the Container level
|
|
76
|
-
*/
|
|
77
|
-
managerId?: string);
|
|
78
|
-
private getManagerSignalName;
|
|
79
|
-
onSignal(signalName: string, listener: SignalListener): ISignaler;
|
|
80
|
-
offSignal(signalName: string, listener: SignalListener): ISignaler;
|
|
81
|
-
submitSignal(signalName: string, payload?: Jsonable): void;
|
|
82
|
-
}
|
|
83
|
-
/**
|
|
84
|
-
* Note: currently experimental and under development
|
|
85
|
-
*
|
|
86
51
|
* DataObject implementation of ISignaler for fluid-static plug-and-play. Allows fluid-static
|
|
87
|
-
* users to get an ISignaler without a custom DO.
|
|
88
|
-
* create a Signaler themselves instead of using the DO wrapper to avoid the DO overhead.
|
|
52
|
+
* users to get an ISignaler without a custom DO.
|
|
89
53
|
*/
|
|
90
|
-
export declare class
|
|
54
|
+
export declare class Signaler extends DataObject<{
|
|
91
55
|
Events: IErrorEvent;
|
|
92
56
|
}> implements EventEmitter, ISignaler {
|
|
93
|
-
private
|
|
94
|
-
private get
|
|
57
|
+
private _signaler;
|
|
58
|
+
private get signaler();
|
|
95
59
|
static get Name(): string;
|
|
96
|
-
static readonly factory: DataObjectFactory<
|
|
60
|
+
static readonly factory: DataObjectFactory<Signaler, {
|
|
97
61
|
Events: IErrorEvent;
|
|
98
62
|
}>;
|
|
99
63
|
protected hasInitialized(): Promise<void>;
|
|
@@ -101,4 +65,4 @@ export declare class SignalManager extends DataObject<{
|
|
|
101
65
|
offSignal(signalName: string, listener: SignalListener): ISignaler;
|
|
102
66
|
submitSignal(signalName: string, payload?: Jsonable): void;
|
|
103
67
|
}
|
|
104
|
-
//# sourceMappingURL=
|
|
68
|
+
//# sourceMappingURL=signaler.d.ts.map
|
|
@@ -0,0 +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"}
|
|
@@ -8,13 +8,13 @@ import { assert, TypedEventEmitter } from "@fluidframework/common-utils";
|
|
|
8
8
|
/**
|
|
9
9
|
* Note: currently experimental and under development
|
|
10
10
|
*
|
|
11
|
-
* Helper class to assist common scenarios around working with signals.
|
|
11
|
+
* Helper class to assist common scenarios around working with signals. InternalSignaler wraps a runtime
|
|
12
12
|
* object with signaling functionality (e.g. ContainerRuntime or FluidDataStoreRuntime) and can
|
|
13
13
|
* then be used in place of the original signaler. It uses a separate internal EventEmitter to
|
|
14
14
|
* manage callbacks, and thus will reflect that behavior with regards to callback registration and
|
|
15
15
|
* deregistration.
|
|
16
16
|
*/
|
|
17
|
-
|
|
17
|
+
class InternalSignaler extends TypedEventEmitter {
|
|
18
18
|
constructor(
|
|
19
19
|
/**
|
|
20
20
|
* Object to wrap that can submit and listen to signals
|
|
@@ -25,14 +25,14 @@ export class Signaler extends TypedEventEmitter {
|
|
|
25
25
|
* signal names. Useful to avoid collisions if there are multiple
|
|
26
26
|
* signal users at the Container level
|
|
27
27
|
*/
|
|
28
|
-
|
|
28
|
+
signalerId) {
|
|
29
29
|
super();
|
|
30
30
|
this.signaler = signaler;
|
|
31
31
|
this.emitter = new EventEmitter();
|
|
32
32
|
this.emitter.on("error", (error) => {
|
|
33
33
|
this.emit("error", error);
|
|
34
34
|
});
|
|
35
|
-
this.
|
|
35
|
+
this.signalerId = signalerId ? `#${signalerId}` : undefined;
|
|
36
36
|
this.signaler.on("signal", (message, local) => {
|
|
37
37
|
const clientId = message.clientId;
|
|
38
38
|
// Only call listeners when the runtime is connected and if the signal has an
|
|
@@ -43,58 +43,55 @@ export class Signaler extends TypedEventEmitter {
|
|
|
43
43
|
}
|
|
44
44
|
});
|
|
45
45
|
}
|
|
46
|
-
|
|
47
|
-
return this.
|
|
46
|
+
getSignalerSignalName(signalName) {
|
|
47
|
+
return this.signalerId ? `${signalName}${this.signalerId}` : signalName;
|
|
48
48
|
}
|
|
49
49
|
// ISignaler methods
|
|
50
50
|
onSignal(signalName, listener) {
|
|
51
|
-
const
|
|
52
|
-
this.emitter.on(
|
|
51
|
+
const signalerSignalName = this.getSignalerSignalName(signalName);
|
|
52
|
+
this.emitter.on(signalerSignalName, listener);
|
|
53
53
|
return this;
|
|
54
54
|
}
|
|
55
55
|
offSignal(signalName, listener) {
|
|
56
|
-
const
|
|
57
|
-
this.emitter.off(
|
|
56
|
+
const signalerSignalName = this.getSignalerSignalName(signalName);
|
|
57
|
+
this.emitter.off(signalerSignalName, listener);
|
|
58
58
|
return this;
|
|
59
59
|
}
|
|
60
60
|
submitSignal(signalName, payload) {
|
|
61
|
-
const
|
|
61
|
+
const signalerSignalName = this.getSignalerSignalName(signalName);
|
|
62
62
|
if (this.signaler.connected) {
|
|
63
|
-
this.signaler.submitSignal(
|
|
63
|
+
this.signaler.submitSignal(signalerSignalName, payload);
|
|
64
64
|
}
|
|
65
65
|
}
|
|
66
66
|
}
|
|
67
67
|
/**
|
|
68
|
-
* Note: currently experimental and under development
|
|
69
|
-
*
|
|
70
68
|
* DataObject implementation of ISignaler for fluid-static plug-and-play. Allows fluid-static
|
|
71
|
-
* users to get an ISignaler without a custom DO.
|
|
72
|
-
* create a Signaler themselves instead of using the DO wrapper to avoid the DO overhead.
|
|
69
|
+
* users to get an ISignaler without a custom DO.
|
|
73
70
|
*/
|
|
74
|
-
export class
|
|
75
|
-
get
|
|
76
|
-
assert(this.
|
|
77
|
-
return this.
|
|
71
|
+
export class Signaler extends DataObject {
|
|
72
|
+
get signaler() {
|
|
73
|
+
assert(this._signaler !== undefined, 0x24b /* "internal signaler should be defined" */);
|
|
74
|
+
return this._signaler;
|
|
78
75
|
}
|
|
79
|
-
static get Name() { return "@fluid-example/
|
|
76
|
+
static get Name() { return "@fluid-example/signaler"; }
|
|
80
77
|
async hasInitialized() {
|
|
81
|
-
this.
|
|
82
|
-
this.
|
|
78
|
+
this._signaler = new InternalSignaler(this.runtime);
|
|
79
|
+
this.signaler.on("error", (error) => {
|
|
83
80
|
this.emit("error", error);
|
|
84
81
|
});
|
|
85
82
|
}
|
|
86
83
|
// ISignaler methods Note these are all passthroughs
|
|
87
84
|
onSignal(signalName, listener) {
|
|
88
|
-
this.
|
|
85
|
+
this.signaler.onSignal(signalName, listener);
|
|
89
86
|
return this;
|
|
90
87
|
}
|
|
91
88
|
offSignal(signalName, listener) {
|
|
92
|
-
this.
|
|
89
|
+
this.signaler.offSignal(signalName, listener);
|
|
93
90
|
return this;
|
|
94
91
|
}
|
|
95
92
|
submitSignal(signalName, payload) {
|
|
96
|
-
this.
|
|
93
|
+
this.signaler.submitSignal(signalName, payload);
|
|
97
94
|
}
|
|
98
95
|
}
|
|
99
|
-
|
|
100
|
-
//# sourceMappingURL=
|
|
96
|
+
Signaler.factory = new DataObjectFactory(Signaler.Name, Signaler, [], {});
|
|
97
|
+
//# sourceMappingURL=signaler.js.map
|
|
@@ -0,0 +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"]}
|
package/lib/index.d.ts
CHANGED
package/lib/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,cAAc,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,cAAc,YAAY,CAAC"}
|
package/lib/index.js
CHANGED
package/lib/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,cAAc,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,cAAc,YAAY,CAAC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nexport * from \"./signaler\";\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/signaler/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,cAAc,YAAY,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/signaler/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,cAAc,YAAY,CAAC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nexport * from \"./signaler\";\n"]}
|
|
@@ -6,7 +6,6 @@
|
|
|
6
6
|
import { EventEmitter } from "events";
|
|
7
7
|
import { DataObject, DataObjectFactory } from "@fluidframework/aqueduct";
|
|
8
8
|
import { IErrorEvent } from "@fluidframework/common-definitions";
|
|
9
|
-
import { TypedEventEmitter } from "@fluidframework/common-utils";
|
|
10
9
|
import { Jsonable } from "@fluidframework/datastore-definitions";
|
|
11
10
|
import { IInboundSignalMessage } from "@fluidframework/runtime-definitions";
|
|
12
11
|
export declare type SignalListener = (clientId: string, local: boolean, payload: Jsonable) => void;
|
|
@@ -49,51 +48,16 @@ export interface IRuntimeSignaler {
|
|
|
49
48
|
submitSignal(type: string, content: any): void;
|
|
50
49
|
}
|
|
51
50
|
/**
|
|
52
|
-
* Note: currently experimental and under development
|
|
53
|
-
*
|
|
54
|
-
* Helper class to assist common scenarios around working with signals. Signaler wraps a runtime
|
|
55
|
-
* object with signaling functionality (e.g. ContainerRuntime or FluidDataStoreRuntime) and can
|
|
56
|
-
* then be used in place of the original signaler. It uses a separate internal EventEmitter to
|
|
57
|
-
* manage callbacks, and thus will reflect that behavior with regards to callback registration and
|
|
58
|
-
* deregistration.
|
|
59
|
-
*/
|
|
60
|
-
export declare class Signaler extends TypedEventEmitter<IErrorEvent> implements ISignaler {
|
|
61
|
-
/**
|
|
62
|
-
* Object to wrap that can submit and listen to signals
|
|
63
|
-
*/
|
|
64
|
-
private readonly signaler;
|
|
65
|
-
private readonly emitter;
|
|
66
|
-
private readonly managerId;
|
|
67
|
-
constructor(
|
|
68
|
-
/**
|
|
69
|
-
* Object to wrap that can submit and listen to signals
|
|
70
|
-
*/
|
|
71
|
-
signaler: IRuntimeSignaler,
|
|
72
|
-
/**
|
|
73
|
-
* Optional id to assign to this manager that will be attached to
|
|
74
|
-
* signal names. Useful to avoid collisions if there are multiple
|
|
75
|
-
* signal users at the Container level
|
|
76
|
-
*/
|
|
77
|
-
managerId?: string);
|
|
78
|
-
private getManagerSignalName;
|
|
79
|
-
onSignal(signalName: string, listener: SignalListener): ISignaler;
|
|
80
|
-
offSignal(signalName: string, listener: SignalListener): ISignaler;
|
|
81
|
-
submitSignal(signalName: string, payload?: Jsonable): void;
|
|
82
|
-
}
|
|
83
|
-
/**
|
|
84
|
-
* Note: currently experimental and under development
|
|
85
|
-
*
|
|
86
51
|
* DataObject implementation of ISignaler for fluid-static plug-and-play. Allows fluid-static
|
|
87
|
-
* users to get an ISignaler without a custom DO.
|
|
88
|
-
* create a Signaler themselves instead of using the DO wrapper to avoid the DO overhead.
|
|
52
|
+
* users to get an ISignaler without a custom DO.
|
|
89
53
|
*/
|
|
90
|
-
export declare class
|
|
54
|
+
export declare class Signaler extends DataObject<{
|
|
91
55
|
Events: IErrorEvent;
|
|
92
56
|
}> implements EventEmitter, ISignaler {
|
|
93
|
-
private
|
|
94
|
-
private get
|
|
57
|
+
private _signaler;
|
|
58
|
+
private get signaler();
|
|
95
59
|
static get Name(): string;
|
|
96
|
-
static readonly factory: DataObjectFactory<
|
|
60
|
+
static readonly factory: DataObjectFactory<Signaler, {
|
|
97
61
|
Events: IErrorEvent;
|
|
98
62
|
}>;
|
|
99
63
|
protected hasInitialized(): Promise<void>;
|
|
@@ -101,4 +65,4 @@ export declare class SignalManager extends DataObject<{
|
|
|
101
65
|
offSignal(signalName: string, listener: SignalListener): ISignaler;
|
|
102
66
|
submitSignal(signalName: string, payload?: Jsonable): void;
|
|
103
67
|
}
|
|
104
|
-
//# sourceMappingURL=
|
|
68
|
+
//# sourceMappingURL=signaler.d.ts.map
|
|
@@ -0,0 +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"}
|
|
@@ -8,13 +8,13 @@ import { assert, TypedEventEmitter } from "@fluidframework/common-utils";
|
|
|
8
8
|
/**
|
|
9
9
|
* Note: currently experimental and under development
|
|
10
10
|
*
|
|
11
|
-
* Helper class to assist common scenarios around working with signals.
|
|
11
|
+
* Helper class to assist common scenarios around working with signals. InternalSignaler wraps a runtime
|
|
12
12
|
* object with signaling functionality (e.g. ContainerRuntime or FluidDataStoreRuntime) and can
|
|
13
13
|
* then be used in place of the original signaler. It uses a separate internal EventEmitter to
|
|
14
14
|
* manage callbacks, and thus will reflect that behavior with regards to callback registration and
|
|
15
15
|
* deregistration.
|
|
16
16
|
*/
|
|
17
|
-
|
|
17
|
+
class InternalSignaler extends TypedEventEmitter {
|
|
18
18
|
constructor(
|
|
19
19
|
/**
|
|
20
20
|
* Object to wrap that can submit and listen to signals
|
|
@@ -25,14 +25,14 @@ export class Signaler extends TypedEventEmitter {
|
|
|
25
25
|
* signal names. Useful to avoid collisions if there are multiple
|
|
26
26
|
* signal users at the Container level
|
|
27
27
|
*/
|
|
28
|
-
|
|
28
|
+
signalerId) {
|
|
29
29
|
super();
|
|
30
30
|
this.signaler = signaler;
|
|
31
31
|
this.emitter = new EventEmitter();
|
|
32
32
|
this.emitter.on("error", (error) => {
|
|
33
33
|
this.emit("error", error);
|
|
34
34
|
});
|
|
35
|
-
this.
|
|
35
|
+
this.signalerId = signalerId ? `#${signalerId}` : undefined;
|
|
36
36
|
this.signaler.on("signal", (message, local) => {
|
|
37
37
|
const clientId = message.clientId;
|
|
38
38
|
// Only call listeners when the runtime is connected and if the signal has an
|
|
@@ -43,58 +43,55 @@ export class Signaler extends TypedEventEmitter {
|
|
|
43
43
|
}
|
|
44
44
|
});
|
|
45
45
|
}
|
|
46
|
-
|
|
47
|
-
return this.
|
|
46
|
+
getSignalerSignalName(signalName) {
|
|
47
|
+
return this.signalerId ? `${signalName}${this.signalerId}` : signalName;
|
|
48
48
|
}
|
|
49
49
|
// ISignaler methods
|
|
50
50
|
onSignal(signalName, listener) {
|
|
51
|
-
const
|
|
52
|
-
this.emitter.on(
|
|
51
|
+
const signalerSignalName = this.getSignalerSignalName(signalName);
|
|
52
|
+
this.emitter.on(signalerSignalName, listener);
|
|
53
53
|
return this;
|
|
54
54
|
}
|
|
55
55
|
offSignal(signalName, listener) {
|
|
56
|
-
const
|
|
57
|
-
this.emitter.off(
|
|
56
|
+
const signalerSignalName = this.getSignalerSignalName(signalName);
|
|
57
|
+
this.emitter.off(signalerSignalName, listener);
|
|
58
58
|
return this;
|
|
59
59
|
}
|
|
60
60
|
submitSignal(signalName, payload) {
|
|
61
|
-
const
|
|
61
|
+
const signalerSignalName = this.getSignalerSignalName(signalName);
|
|
62
62
|
if (this.signaler.connected) {
|
|
63
|
-
this.signaler.submitSignal(
|
|
63
|
+
this.signaler.submitSignal(signalerSignalName, payload);
|
|
64
64
|
}
|
|
65
65
|
}
|
|
66
66
|
}
|
|
67
67
|
/**
|
|
68
|
-
* Note: currently experimental and under development
|
|
69
|
-
*
|
|
70
68
|
* DataObject implementation of ISignaler for fluid-static plug-and-play. Allows fluid-static
|
|
71
|
-
* users to get an ISignaler without a custom DO.
|
|
72
|
-
* create a Signaler themselves instead of using the DO wrapper to avoid the DO overhead.
|
|
69
|
+
* users to get an ISignaler without a custom DO.
|
|
73
70
|
*/
|
|
74
|
-
export class
|
|
75
|
-
get
|
|
76
|
-
assert(this.
|
|
77
|
-
return this.
|
|
71
|
+
export class Signaler extends DataObject {
|
|
72
|
+
get signaler() {
|
|
73
|
+
assert(this._signaler !== undefined, 0x24b /* "internal signaler should be defined" */);
|
|
74
|
+
return this._signaler;
|
|
78
75
|
}
|
|
79
|
-
static get Name() { return "@fluid-example/
|
|
76
|
+
static get Name() { return "@fluid-example/signaler"; }
|
|
80
77
|
async hasInitialized() {
|
|
81
|
-
this.
|
|
82
|
-
this.
|
|
78
|
+
this._signaler = new InternalSignaler(this.runtime);
|
|
79
|
+
this.signaler.on("error", (error) => {
|
|
83
80
|
this.emit("error", error);
|
|
84
81
|
});
|
|
85
82
|
}
|
|
86
83
|
// ISignaler methods Note these are all passthroughs
|
|
87
84
|
onSignal(signalName, listener) {
|
|
88
|
-
this.
|
|
85
|
+
this.signaler.onSignal(signalName, listener);
|
|
89
86
|
return this;
|
|
90
87
|
}
|
|
91
88
|
offSignal(signalName, listener) {
|
|
92
|
-
this.
|
|
89
|
+
this.signaler.offSignal(signalName, listener);
|
|
93
90
|
return this;
|
|
94
91
|
}
|
|
95
92
|
submitSignal(signalName, payload) {
|
|
96
|
-
this.
|
|
93
|
+
this.signaler.submitSignal(signalName, payload);
|
|
97
94
|
}
|
|
98
95
|
}
|
|
99
|
-
|
|
100
|
-
//# sourceMappingURL=
|
|
96
|
+
Signaler.factory = new DataObjectFactory(Signaler.Name, Signaler, [], {});
|
|
97
|
+
//# sourceMappingURL=signaler.js.map
|
|
@@ -0,0 +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"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fluid-experimental/data-objects",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "2.0.0-internal.1.0.0.82159",
|
|
4
4
|
"description": "A collection of ready to use Fluid Data Objects",
|
|
5
5
|
"homepage": "https://fluidframework.com",
|
|
6
6
|
"repository": {
|
|
@@ -31,12 +31,12 @@
|
|
|
31
31
|
"tsfmt:fix": "tsfmt --replace"
|
|
32
32
|
},
|
|
33
33
|
"dependencies": {
|
|
34
|
-
"@fluidframework/aqueduct": "
|
|
34
|
+
"@fluidframework/aqueduct": "2.0.0-internal.1.0.0.82159",
|
|
35
35
|
"@fluidframework/common-definitions": "^0.20.1",
|
|
36
36
|
"@fluidframework/common-utils": "^0.32.1",
|
|
37
|
-
"@fluidframework/datastore-definitions": "
|
|
38
|
-
"@fluidframework/map": "
|
|
39
|
-
"@fluidframework/runtime-definitions": "
|
|
37
|
+
"@fluidframework/datastore-definitions": "2.0.0-internal.1.0.0.82159",
|
|
38
|
+
"@fluidframework/map": "2.0.0-internal.1.0.0.82159",
|
|
39
|
+
"@fluidframework/runtime-definitions": "2.0.0-internal.1.0.0.82159"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
42
42
|
"@fluidframework/build-common": "^0.24.0",
|
package/src/index.ts
CHANGED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# Signaler
|
|
2
|
+
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
|
+
## Usage
|
|
5
|
+
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
|
+
|
|
7
|
+
### Creation
|
|
8
|
+
Just like with DDSes, you can include `Signaler` as a shared object you would like to load in your `FluidContainer` schema.
|
|
9
|
+
|
|
10
|
+
Here is a look at how you would go about loading `Signaler` as part of the initial objects of the container:
|
|
11
|
+
|
|
12
|
+
```typescript
|
|
13
|
+
const containerSchema: ContainerSchema = {
|
|
14
|
+
initialObjects: {
|
|
15
|
+
signaler: Signaler,
|
|
16
|
+
},
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const { container, services } = await client.createContainer(containerSchema);
|
|
20
|
+
|
|
21
|
+
const signaler = container.initialObjects.signaler as Signaler;
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
`signaler` can then be directly used in your Fluid application!
|
|
25
|
+
|
|
26
|
+
For more information on using `ContainerSchema` to create objects please see [Data modeling](https://fluidframework.com/docs/build/data-modeling/).
|
|
27
|
+
|
|
28
|
+
## API
|
|
29
|
+
`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.
|
|
33
|
+
|
|
34
|
+
## Common Patterns
|
|
35
|
+
### 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:
|
|
37
|
+
|
|
38
|
+
```typescript
|
|
39
|
+
private static readonly focusRequestType = "focusRequest";
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
```typescript
|
|
43
|
+
container.on("connected", () => {
|
|
44
|
+
this.signaler.submitSignal(FocusTracker.focusRequestType);
|
|
45
|
+
});
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
The connected clients are listening to this focus request signal, and they respond with their current focus state:
|
|
49
|
+
|
|
50
|
+
```typescript
|
|
51
|
+
this.signaler.onSignal(FocusTracker.focusRequestType, () => {
|
|
52
|
+
this.sendFocusSignal(document.hasFocus());
|
|
53
|
+
});
|
|
54
|
+
```
|
|
55
|
+
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.
|
|
56
|
+
### Grouping Signal Types
|
|
57
|
+
|
|
58
|
+
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
|
+
|
|
60
|
+
```typescript
|
|
61
|
+
container.on("connected", () => {
|
|
62
|
+
this.signaler.submitSignal("colorRequest");
|
|
63
|
+
this.signaler.submitSignal("focusRequest");
|
|
64
|
+
this.signaler.submitSignal("currentlySelectedObjectRequest");
|
|
65
|
+
});
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
This approach is costly since the amount of signals sent back on request grows linearly with the amount of connected users. So if there are three signals requested as opposed to one, there are 3 times as many total signals being submitted on every connect. To avoid this costly scenario we can group the signal types into one single signal that captures all the required information:
|
|
69
|
+
|
|
70
|
+
```typescript
|
|
71
|
+
container.on("connected", () => {
|
|
72
|
+
this.signaler.submitSignal("connectRequest");
|
|
73
|
+
});
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
The idea is that the payload sent back on the `connectRequest` will include all the relevant information the newly connected user needs.
|
|
@@ -59,16 +59,16 @@ export interface IRuntimeSignaler {
|
|
|
59
59
|
/**
|
|
60
60
|
* Note: currently experimental and under development
|
|
61
61
|
*
|
|
62
|
-
* Helper class to assist common scenarios around working with signals.
|
|
62
|
+
* Helper class to assist common scenarios around working with signals. InternalSignaler wraps a runtime
|
|
63
63
|
* object with signaling functionality (e.g. ContainerRuntime or FluidDataStoreRuntime) and can
|
|
64
64
|
* then be used in place of the original signaler. It uses a separate internal EventEmitter to
|
|
65
65
|
* manage callbacks, and thus will reflect that behavior with regards to callback registration and
|
|
66
66
|
* deregistration.
|
|
67
67
|
*/
|
|
68
|
-
|
|
68
|
+
class InternalSignaler extends TypedEventEmitter<IErrorEvent> implements ISignaler {
|
|
69
69
|
private readonly emitter = new EventEmitter();
|
|
70
70
|
|
|
71
|
-
private readonly
|
|
71
|
+
private readonly signalerId: string | undefined;
|
|
72
72
|
|
|
73
73
|
constructor(
|
|
74
74
|
/**
|
|
@@ -80,13 +80,13 @@ export class Signaler extends TypedEventEmitter<IErrorEvent> implements ISignale
|
|
|
80
80
|
* signal names. Useful to avoid collisions if there are multiple
|
|
81
81
|
* signal users at the Container level
|
|
82
82
|
*/
|
|
83
|
-
|
|
83
|
+
signalerId?: string,
|
|
84
84
|
) {
|
|
85
85
|
super();
|
|
86
86
|
this.emitter.on("error", (error) => {
|
|
87
87
|
this.emit("error", error);
|
|
88
88
|
});
|
|
89
|
-
this.
|
|
89
|
+
this.signalerId = signalerId ? `#${signalerId}` : undefined;
|
|
90
90
|
this.signaler.on("signal", (message: IInboundSignalMessage, local: boolean) => {
|
|
91
91
|
const clientId = message.clientId;
|
|
92
92
|
// Only call listeners when the runtime is connected and if the signal has an
|
|
@@ -98,8 +98,8 @@ export class Signaler extends TypedEventEmitter<IErrorEvent> implements ISignale
|
|
|
98
98
|
});
|
|
99
99
|
}
|
|
100
100
|
|
|
101
|
-
private
|
|
102
|
-
return this.
|
|
101
|
+
private getSignalerSignalName(signalName: string): string {
|
|
102
|
+
return this.signalerId ? `${signalName}${this.signalerId}` : signalName;
|
|
103
103
|
}
|
|
104
104
|
|
|
105
105
|
// ISignaler methods
|
|
@@ -108,8 +108,8 @@ export class Signaler extends TypedEventEmitter<IErrorEvent> implements ISignale
|
|
|
108
108
|
signalName: string,
|
|
109
109
|
listener: SignalListener,
|
|
110
110
|
): ISignaler {
|
|
111
|
-
const
|
|
112
|
-
this.emitter.on(
|
|
111
|
+
const signalerSignalName = this.getSignalerSignalName(signalName);
|
|
112
|
+
this.emitter.on(signalerSignalName, listener);
|
|
113
113
|
return this;
|
|
114
114
|
}
|
|
115
115
|
|
|
@@ -117,8 +117,8 @@ export class Signaler extends TypedEventEmitter<IErrorEvent> implements ISignale
|
|
|
117
117
|
signalName: string,
|
|
118
118
|
listener: SignalListener,
|
|
119
119
|
): ISignaler {
|
|
120
|
-
const
|
|
121
|
-
this.emitter.off(
|
|
120
|
+
const signalerSignalName = this.getSignalerSignalName(signalName);
|
|
121
|
+
this.emitter.off(signalerSignalName, listener);
|
|
122
122
|
return this;
|
|
123
123
|
}
|
|
124
124
|
|
|
@@ -126,39 +126,36 @@ export class Signaler extends TypedEventEmitter<IErrorEvent> implements ISignale
|
|
|
126
126
|
signalName: string,
|
|
127
127
|
payload?: Jsonable,
|
|
128
128
|
) {
|
|
129
|
-
const
|
|
129
|
+
const signalerSignalName = this.getSignalerSignalName(signalName);
|
|
130
130
|
if (this.signaler.connected) {
|
|
131
|
-
this.signaler.submitSignal(
|
|
131
|
+
this.signaler.submitSignal(signalerSignalName, payload);
|
|
132
132
|
}
|
|
133
133
|
}
|
|
134
134
|
}
|
|
135
135
|
|
|
136
136
|
/**
|
|
137
|
-
* Note: currently experimental and under development
|
|
138
|
-
*
|
|
139
137
|
* DataObject implementation of ISignaler for fluid-static plug-and-play. Allows fluid-static
|
|
140
|
-
* users to get an ISignaler without a custom DO.
|
|
141
|
-
* create a Signaler themselves instead of using the DO wrapper to avoid the DO overhead.
|
|
138
|
+
* users to get an ISignaler without a custom DO.
|
|
142
139
|
*/
|
|
143
|
-
export class
|
|
144
|
-
private
|
|
145
|
-
private get
|
|
146
|
-
assert(this.
|
|
147
|
-
return this.
|
|
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;
|
|
148
145
|
}
|
|
149
146
|
|
|
150
|
-
public static get Name() { return "@fluid-example/
|
|
147
|
+
public static get Name() { return "@fluid-example/signaler"; }
|
|
151
148
|
|
|
152
149
|
public static readonly factory = new DataObjectFactory(
|
|
153
|
-
|
|
154
|
-
|
|
150
|
+
Signaler.Name,
|
|
151
|
+
Signaler,
|
|
155
152
|
[],
|
|
156
153
|
{},
|
|
157
154
|
);
|
|
158
155
|
|
|
159
156
|
protected async hasInitialized() {
|
|
160
|
-
this.
|
|
161
|
-
this.
|
|
157
|
+
this._signaler = new InternalSignaler(this.runtime);
|
|
158
|
+
this.signaler.on("error", (error) => {
|
|
162
159
|
this.emit("error", error);
|
|
163
160
|
});
|
|
164
161
|
}
|
|
@@ -169,7 +166,7 @@ export class SignalManager extends DataObject<{ Events: IErrorEvent; }> implemen
|
|
|
169
166
|
signalName: string,
|
|
170
167
|
listener: SignalListener,
|
|
171
168
|
): ISignaler {
|
|
172
|
-
this.
|
|
169
|
+
this.signaler.onSignal(signalName, listener);
|
|
173
170
|
return this;
|
|
174
171
|
}
|
|
175
172
|
|
|
@@ -177,7 +174,7 @@ export class SignalManager extends DataObject<{ Events: IErrorEvent; }> implemen
|
|
|
177
174
|
signalName: string,
|
|
178
175
|
listener: SignalListener,
|
|
179
176
|
): ISignaler {
|
|
180
|
-
this.
|
|
177
|
+
this.signaler.offSignal(signalName, listener);
|
|
181
178
|
return this;
|
|
182
179
|
}
|
|
183
180
|
|
|
@@ -185,6 +182,6 @@ export class SignalManager extends DataObject<{ Events: IErrorEvent; }> implemen
|
|
|
185
182
|
signalName: string,
|
|
186
183
|
payload?: Jsonable,
|
|
187
184
|
) {
|
|
188
|
-
this.
|
|
185
|
+
this.signaler.submitSignal(signalName, payload);
|
|
189
186
|
}
|
|
190
187
|
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/signalManager/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,cAAc,iBAAiB,CAAC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/signalManager/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,cAAc,iBAAiB,CAAC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nexport * from \"./signalManager\";\n"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"signalManager.d.ts","sourceRoot":"","sources":["../../src/signalManager/signalManager.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;AACjE,OAAO,EAAU,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AACzE,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;AAED;;;;;;;;GAQG;AACH,qBAAa,QAAS,SAAQ,iBAAiB,CAAC,WAAW,CAAE,YAAW,SAAS;IAMzE;;OAEG;IACH,OAAO,CAAC,QAAQ,CAAC,QAAQ;IAR7B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAsB;IAE9C,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAqB;;IAG3C;;OAEG;IACc,QAAQ,EAAE,gBAAgB;IAC3C;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM;IAkBtB,OAAO,CAAC,oBAAoB;IAMrB,QAAQ,CACX,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,cAAc,GACzB,SAAS;IAML,SAAS,CACZ,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,cAAc,GACzB,SAAS;IAML,YAAY,CACf,UAAU,EAAE,MAAM,EAClB,OAAO,CAAC,EAAE,QAAQ;CAOzB;AAED;;;;;;GAMG;AACH,qBAAa,aAAc,SAAQ,UAAU,CAAC;IAAE,MAAM,EAAE,WAAW,CAAC;CAAE,CAAE,YAAW,YAAY,EAAE,SAAS;IACtG,OAAO,CAAC,QAAQ,CAAuB;IACvC,OAAO,KAAK,OAAO,GAGlB;IAED,WAAkB,IAAI,WAA8C;IAEpE,gBAAuB,OAAO;gBATsB,WAAW;OAc7D;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 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"signalManager.js","sourceRoot":"","sources":["../../src/signalManager/signalManager.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. Signaler 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 */\nexport class Signaler extends TypedEventEmitter<IErrorEvent> implements ISignaler {\n private readonly emitter = new EventEmitter();\n\n private readonly managerId: 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 managerId?: string,\n ) {\n super();\n this.emitter.on(\"error\", (error) => {\n this.emit(\"error\", error);\n });\n this.managerId = managerId ? `#${managerId}` : 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 getManagerSignalName(signalName: string): string {\n return this.managerId ? `${signalName}${this.managerId}` : signalName;\n }\n\n // ISignaler methods\n\n public onSignal(\n signalName: string,\n listener: SignalListener,\n ): ISignaler {\n const managerSignalName = this.getManagerSignalName(signalName);\n this.emitter.on(managerSignalName, listener);\n return this;\n }\n\n public offSignal(\n signalName: string,\n listener: SignalListener,\n ): ISignaler {\n const managerSignalName = this.getManagerSignalName(signalName);\n this.emitter.off(managerSignalName, listener);\n return this;\n }\n\n public submitSignal(\n signalName: string,\n payload?: Jsonable,\n ) {\n const managerSignalName = this.getManagerSignalName(signalName);\n if (this.signaler.connected) {\n this.signaler.submitSignal(managerSignalName, payload);\n }\n }\n}\n\n/**\n * Note: currently experimental and under development\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. Where possible, consumers should instead\n * create a Signaler themselves instead of using the DO wrapper to avoid the DO overhead.\n */\nexport class SignalManager extends DataObject<{ Events: IErrorEvent; }> implements EventEmitter, ISignaler {\n private _manager: Signaler | undefined;\n private get manager(): Signaler {\n assert(this._manager !== undefined, 0x24b /* \"internal signaler should be defined\" */);\n return this._manager;\n }\n\n public static get Name() { return \"@fluid-example/signal-manager\"; }\n\n public static readonly factory = new DataObjectFactory(\n SignalManager.Name,\n SignalManager,\n [],\n {},\n );\n\n protected async hasInitialized() {\n this._manager = new Signaler(this.runtime);\n this.manager.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.manager.onSignal(signalName, listener);\n return this;\n }\n\n public offSignal(\n signalName: string,\n listener: SignalListener,\n ): ISignaler {\n this.manager.offSignal(signalName, listener);\n return this;\n }\n\n public submitSignal(\n signalName: string,\n payload?: Jsonable,\n ) {\n this.manager.submitSignal(signalName, payload);\n }\n}\n"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/signalManager/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,cAAc,iBAAiB,CAAC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/signalManager/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,cAAc,iBAAiB,CAAC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nexport * from \"./signalManager\";\n"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"signalManager.d.ts","sourceRoot":"","sources":["../../src/signalManager/signalManager.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;AACjE,OAAO,EAAU,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AACzE,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;AAED;;;;;;;;GAQG;AACH,qBAAa,QAAS,SAAQ,iBAAiB,CAAC,WAAW,CAAE,YAAW,SAAS;IAMzE;;OAEG;IACH,OAAO,CAAC,QAAQ,CAAC,QAAQ;IAR7B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAsB;IAE9C,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAqB;;IAG3C;;OAEG;IACc,QAAQ,EAAE,gBAAgB;IAC3C;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM;IAkBtB,OAAO,CAAC,oBAAoB;IAMrB,QAAQ,CACX,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,cAAc,GACzB,SAAS;IAML,SAAS,CACZ,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,cAAc,GACzB,SAAS;IAML,YAAY,CACf,UAAU,EAAE,MAAM,EAClB,OAAO,CAAC,EAAE,QAAQ;CAOzB;AAED;;;;;;GAMG;AACH,qBAAa,aAAc,SAAQ,UAAU,CAAC;IAAE,MAAM,EAAE,WAAW,CAAC;CAAE,CAAE,YAAW,YAAY,EAAE,SAAS;IACtG,OAAO,CAAC,QAAQ,CAAuB;IACvC,OAAO,KAAK,OAAO,GAGlB;IAED,WAAkB,IAAI,WAA8C;IAEpE,gBAAuB,OAAO;gBATsB,WAAW;OAc7D;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 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"signalManager.js","sourceRoot":"","sources":["../../src/signalManager/signalManager.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. Signaler 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 */\nexport class Signaler extends TypedEventEmitter<IErrorEvent> implements ISignaler {\n private readonly emitter = new EventEmitter();\n\n private readonly managerId: 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 managerId?: string,\n ) {\n super();\n this.emitter.on(\"error\", (error) => {\n this.emit(\"error\", error);\n });\n this.managerId = managerId ? `#${managerId}` : 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 getManagerSignalName(signalName: string): string {\n return this.managerId ? `${signalName}${this.managerId}` : signalName;\n }\n\n // ISignaler methods\n\n public onSignal(\n signalName: string,\n listener: SignalListener,\n ): ISignaler {\n const managerSignalName = this.getManagerSignalName(signalName);\n this.emitter.on(managerSignalName, listener);\n return this;\n }\n\n public offSignal(\n signalName: string,\n listener: SignalListener,\n ): ISignaler {\n const managerSignalName = this.getManagerSignalName(signalName);\n this.emitter.off(managerSignalName, listener);\n return this;\n }\n\n public submitSignal(\n signalName: string,\n payload?: Jsonable,\n ) {\n const managerSignalName = this.getManagerSignalName(signalName);\n if (this.signaler.connected) {\n this.signaler.submitSignal(managerSignalName, payload);\n }\n }\n}\n\n/**\n * Note: currently experimental and under development\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. Where possible, consumers should instead\n * create a Signaler themselves instead of using the DO wrapper to avoid the DO overhead.\n */\nexport class SignalManager extends DataObject<{ Events: IErrorEvent; }> implements EventEmitter, ISignaler {\n private _manager: Signaler | undefined;\n private get manager(): Signaler {\n assert(this._manager !== undefined, 0x24b /* \"internal signaler should be defined\" */);\n return this._manager;\n }\n\n public static get Name() { return \"@fluid-example/signal-manager\"; }\n\n public static readonly factory = new DataObjectFactory(\n SignalManager.Name,\n SignalManager,\n [],\n {},\n );\n\n protected async hasInitialized() {\n this._manager = new Signaler(this.runtime);\n this.manager.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.manager.onSignal(signalName, listener);\n return this;\n }\n\n public offSignal(\n signalName: string,\n listener: SignalListener,\n ): ISignaler {\n this.manager.offSignal(signalName, listener);\n return this;\n }\n\n public submitSignal(\n signalName: string,\n payload?: Jsonable,\n ) {\n this.manager.submitSignal(signalName, payload);\n }\n}\n"]}
|
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
# SignalManager
|
|
2
|
-
The `SignalManager` 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
|
-
## Usage
|
|
5
|
-
User presence scenarios are well-suited for `SignalManager`, as users are required to tell other users their own information and their past data is mostly irrelavant. Using `SignalManager` 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
|
-
|
|
7
|
-
### Creation
|
|
8
|
-
Just like with DDSes, you can include `SignalManager` as a shared object you would like to load in your `FluidContainer` schema.
|
|
9
|
-
|
|
10
|
-
Here is a look at how you would go about loading `SignalManager` as part of the initial objects of the container:
|
|
11
|
-
|
|
12
|
-
```typescript
|
|
13
|
-
const containerSchema: ContainerSchema = {
|
|
14
|
-
initialObjects: {
|
|
15
|
-
signalManager: SignalManager,
|
|
16
|
-
},
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
const { container, services } = await client.createContainer(containerSchema);
|
|
20
|
-
|
|
21
|
-
const signalManager = container.initialObjects.signalManager as SignalManager;
|
|
22
|
-
```
|
|
23
|
-
|
|
24
|
-
`signalManager` can then be directly used in your Fluid application!
|
|
25
|
-
|
|
26
|
-
For more information on using `ContainerSchema` to create objects please see [Data modeling](https://fluidframework.com/docs/build/data-modeling/).
|
|
27
|
-
|
|
28
|
-
## API
|
|
29
|
-
`SignalManager` 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.
|
|
33
|
-
|
|
34
|
-
## Common Patterns
|
|
35
|
-
### 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 [FocusTracker](https://github.com/microsoft/FluidFramework/tree/main/examples/data-objects/focus-tracker) we define a "focusRequest" signal type that a newly joining client uses to request the focus-state of each currently connected client:
|
|
37
|
-
|
|
38
|
-
```typescript
|
|
39
|
-
private static readonly focusRequestType = "focusRequest";
|
|
40
|
-
```
|
|
41
|
-
|
|
42
|
-
```typescript
|
|
43
|
-
container.on("connected", () => {
|
|
44
|
-
this.signalManager.submitSignal(FocusTracker.focusRequestType);
|
|
45
|
-
});
|
|
46
|
-
```
|
|
47
|
-
|
|
48
|
-
The connected clients are listening to this focus request signal, and they respond with their current focus state:
|
|
49
|
-
|
|
50
|
-
```typescript
|
|
51
|
-
this.signalManager.onSignal(FocusTracker.focusRequestType, () => {
|
|
52
|
-
this.sendFocusSignal(document.hasFocus());
|
|
53
|
-
});
|
|
54
|
-
```
|
|
55
|
-
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. Take [MouseTracker]() as an example, 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.
|