@fluid-internal/client-utils 2.10.0 → 2.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,20 @@
1
1
  # @fluid-internal/client-utils
2
2
 
3
+ ## 2.12.0
4
+
5
+ Dependency updates only.
6
+
7
+ ## 2.11.0
8
+
9
+ ### Minor Changes
10
+
11
+ - The events library has been moved from the tree package ([#23141](https://github.com/microsoft/FluidFramework/pull/23141)) [cae07b5c8c](https://github.com/microsoft/FluidFramework/commit/cae07b5c8c7904184b5fbf8c677f302da19cc697)
12
+
13
+ In previous releases, the `@fluidframework/tree` package contained an internal events library. The events-related types and interfaces have been moved to
14
+ `@fluidframework/core-interfaces`, while the implementation has been relocated to `@fluid-internal/client-utils`. There are
15
+ no changes to how the events library is used; the relocation simply organizes the library into more appropriate
16
+ packages. This change should have no impact on developers using the Fluid Framework.
17
+
3
18
  ## 2.10.0
4
19
 
5
20
  Dependency updates only.
@@ -0,0 +1,99 @@
1
+ /*!
2
+ * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
+ * Licensed under the MIT License.
4
+ */
5
+ import type { HasListeners, IEmitter, Listenable, Listeners, NoListenersCallback, Off } from "@fluidframework/core-interfaces/internal";
6
+ /**
7
+ * Provides an API for subscribing to and listening to events.
8
+ *
9
+ * @remarks Classes wishing to emit events may either extend this class, compose over it, or expose it as a property of type {@link @fluidframework/core-interfaces#Listenable}.
10
+ *
11
+ * @example Extending this class
12
+ *
13
+ * ```typescript
14
+ * interface MyEvents {
15
+ * loaded: () => void;
16
+ * computed: () => number;
17
+ * }
18
+ *
19
+ * class MyInheritanceClass extends EventEmitter<MyEvents> {
20
+ * private load() {
21
+ * this.emit("loaded");
22
+ * const results: number[] = this.emitAndCollect("computed");
23
+ * }
24
+ * }
25
+ * ```
26
+ *
27
+ * @example Composing over this class
28
+ *
29
+ * ```typescript
30
+ * class MyCompositionClass implements Listenable<MyEvents> {
31
+ * private readonly events = createEmitter<MyEvents>();
32
+ *
33
+ * private load() {
34
+ * this.events.emit("loaded");
35
+ * const results: number[] = this.events.emitAndCollect("computed");
36
+ * }
37
+ *
38
+ * public on<K extends keyof MyEvents>(eventName: K, listener: MyEvents[K]): () => void {
39
+ * return this.events.on(eventName, listener);
40
+ * }
41
+ * }
42
+ * ```
43
+ *
44
+ * @example Exposing this class as a property
45
+ *
46
+ * ```typescript
47
+ * class MyExposingClass {
48
+ * private readonly _events = createEmitter<MyEvents>();
49
+ * public readonly events: Listenable<MyEvents> = this._events;
50
+ *
51
+ * private load() {
52
+ * this._events.emit("loaded");
53
+ * const results: number[] = this._events.emitAndCollect("computed");
54
+ * }
55
+ * }
56
+ * ```
57
+ * @internal
58
+ */
59
+ export declare class CustomEventEmitter<TListeners extends Listeners<TListeners>> implements Listenable<TListeners>, HasListeners<TListeners> {
60
+ private readonly noListeners?;
61
+ protected readonly listeners: Map<keyof TListeners, Set<(...args: any[]) => TListeners[keyof TListeners]>>;
62
+ protected constructor(noListeners?: NoListenersCallback<TListeners> | undefined);
63
+ protected emit<K extends keyof TListeners>(eventName: K, ...args: Parameters<TListeners[K]>): void;
64
+ protected emitAndCollect<K extends keyof TListeners>(eventName: K, ...args: Parameters<TListeners[K]>): ReturnType<TListeners[K]>[];
65
+ on<K extends keyof Listeners<TListeners>>(eventName: K, listener: TListeners[K]): Off;
66
+ off<K extends keyof Listeners<TListeners>>(eventName: K, listener: TListeners[K]): void;
67
+ hasListeners(eventName?: keyof TListeners): boolean;
68
+ }
69
+ /**
70
+ * Create a {@link @fluidframework/core-interfaces#Listenable} that can be instructed to emit events via the {@link @fluidframework/core-interfaces#IEmitter} interface.
71
+ *
72
+ * A class can delegate handling {@link @fluidframework/core-interfaces#Listenable} to the returned value while using it to emit the events.
73
+ * See also CustomEventEmitter which be used as a base class to implement {@link @fluidframework/core-interfaces#Listenable} via extension.
74
+ * @example Forwarding events to the emitter
75
+ * ```typescript
76
+ * interface MyEvents {
77
+ * loaded(): void;
78
+ * }
79
+ *
80
+ * class MyClass implements Listenable<MyEvents> {
81
+ * private readonly events = createEmitter<MyEvents>();
82
+ *
83
+ * private load(): void {
84
+ * this.events.emit("loaded");
85
+ * }
86
+ *
87
+ * public on<K extends keyof MyEvents>(eventName: K, listener: MyEvents[K]): Off {
88
+ * return this.events.on(eventName, listener);
89
+ * }
90
+ *
91
+ * public off<K extends keyof MyEvents>(eventName: K, listener: MyEvents[K]): void {
92
+ * return this.events.off(eventName, listener);
93
+ * }
94
+ * }
95
+ * ```
96
+ * @internal
97
+ */
98
+ export declare function createEmitter<TListeners extends object>(noListeners?: NoListenersCallback<TListeners>): Listenable<TListeners> & IEmitter<TListeners> & HasListeners<TListeners>;
99
+ //# sourceMappingURL=emitter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"emitter.d.ts","sourceRoot":"","sources":["../../src/events/emitter.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EACX,YAAY,EACZ,QAAQ,EACR,UAAU,EACV,SAAS,EAET,mBAAmB,EACnB,GAAG,EACH,MAAM,0CAA0C,CAAC;AAmBlD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoDG;AACH,qBAAa,kBAAkB,CAAC,UAAU,SAAS,SAAS,CAAC,UAAU,CAAC,CACvE,YAAW,UAAU,CAAC,UAAU,CAAC,EAAE,YAAY,CAAC,UAAU,CAAC;IASrC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAC;IAPnD,SAAS,CAAC,QAAQ,CAAC,SAAS,sCAEb,GAAG,EAAE,KAAK,UAAU,CAAC,MAAM,UAAU,CAAC,GACjD;IAIJ,SAAS,aAA8B,WAAW,CAAC,6CAAiC;IAEpF,SAAS,CAAC,IAAI,CAAC,CAAC,SAAS,MAAM,UAAU,EACxC,SAAS,EAAE,CAAC,EACZ,GAAG,IAAI,EAAE,UAAU,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,GAChC,IAAI;IAeP,SAAS,CAAC,cAAc,CAAC,CAAC,SAAS,MAAM,UAAU,EAClD,SAAS,EAAE,CAAC,EACZ,GAAG,IAAI,EAAE,UAAU,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,GAChC,UAAU,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,EAAE;IAavB,EAAE,CAAC,CAAC,SAAS,MAAM,SAAS,CAAC,UAAU,CAAC,EAC9C,SAAS,EAAE,CAAC,EACZ,QAAQ,EAAE,UAAU,CAAC,CAAC,CAAC,GACrB,GAAG;IAcC,GAAG,CAAC,CAAC,SAAS,MAAM,SAAS,CAAC,UAAU,CAAC,EAC/C,SAAS,EAAE,CAAC,EACZ,QAAQ,EAAE,UAAU,CAAC,CAAC,CAAC,GACrB,IAAI;IAQA,YAAY,CAAC,SAAS,CAAC,EAAE,MAAM,UAAU,GAAG,OAAO;CAM1D;AA6BD;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,wBAAgB,aAAa,CAAC,UAAU,SAAS,MAAM,EACtD,WAAW,CAAC,EAAE,mBAAmB,CAAC,UAAU,CAAC,GAC3C,UAAU,CAAC,UAAU,CAAC,GAAG,QAAQ,CAAC,UAAU,CAAC,GAAG,YAAY,CAAC,UAAU,CAAC,CAE1E"}
@@ -0,0 +1,182 @@
1
+ "use strict";
2
+ /*!
3
+ * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
4
+ * Licensed under the MIT License.
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.createEmitter = exports.CustomEventEmitter = void 0;
8
+ /**
9
+ * Retrieve a value from a map with the given key, or create a new entry if the key is not in the map.
10
+ * @param map - The map to query/update
11
+ * @param key - The key to lookup in the map
12
+ * @param defaultValue - a function which returns a default value. This is called and used to set an initial value for the given key in the map if none exists
13
+ * @returns either the existing value for the given key, or the newly-created value (the result of `defaultValue`)
14
+ * @internal
15
+ */
16
+ function getOrCreate(map, key, defaultValue) {
17
+ let value = map.get(key);
18
+ if (value === undefined) {
19
+ value = defaultValue(key);
20
+ map.set(key, value);
21
+ }
22
+ return value;
23
+ }
24
+ /**
25
+ * Provides an API for subscribing to and listening to events.
26
+ *
27
+ * @remarks Classes wishing to emit events may either extend this class, compose over it, or expose it as a property of type {@link @fluidframework/core-interfaces#Listenable}.
28
+ *
29
+ * @example Extending this class
30
+ *
31
+ * ```typescript
32
+ * interface MyEvents {
33
+ * loaded: () => void;
34
+ * computed: () => number;
35
+ * }
36
+ *
37
+ * class MyInheritanceClass extends EventEmitter<MyEvents> {
38
+ * private load() {
39
+ * this.emit("loaded");
40
+ * const results: number[] = this.emitAndCollect("computed");
41
+ * }
42
+ * }
43
+ * ```
44
+ *
45
+ * @example Composing over this class
46
+ *
47
+ * ```typescript
48
+ * class MyCompositionClass implements Listenable<MyEvents> {
49
+ * private readonly events = createEmitter<MyEvents>();
50
+ *
51
+ * private load() {
52
+ * this.events.emit("loaded");
53
+ * const results: number[] = this.events.emitAndCollect("computed");
54
+ * }
55
+ *
56
+ * public on<K extends keyof MyEvents>(eventName: K, listener: MyEvents[K]): () => void {
57
+ * return this.events.on(eventName, listener);
58
+ * }
59
+ * }
60
+ * ```
61
+ *
62
+ * @example Exposing this class as a property
63
+ *
64
+ * ```typescript
65
+ * class MyExposingClass {
66
+ * private readonly _events = createEmitter<MyEvents>();
67
+ * public readonly events: Listenable<MyEvents> = this._events;
68
+ *
69
+ * private load() {
70
+ * this._events.emit("loaded");
71
+ * const results: number[] = this._events.emitAndCollect("computed");
72
+ * }
73
+ * }
74
+ * ```
75
+ * @internal
76
+ */
77
+ class CustomEventEmitter {
78
+ // Because this is protected and not public, calling this externally (not from a subclass) makes sending events to the constructed instance impossible.
79
+ // Instead, use the static `create` function to get an instance which allows emitting events.
80
+ constructor(noListeners) {
81
+ this.noListeners = noListeners;
82
+ this.listeners = new Map();
83
+ }
84
+ emit(eventName, ...args) {
85
+ const listeners = this.listeners.get(eventName);
86
+ if (listeners !== undefined) {
87
+ // Current tsc (5.4.5) cannot spread `args` into `listener()`.
88
+ const argArray = args;
89
+ // This explicitly copies listeners so that new listeners added during this call to emit will not receive this event.
90
+ for (const listener of [...listeners]) {
91
+ // If listener has been unsubscribed while invoking other listeners, skip it.
92
+ if (listeners.has(listener)) {
93
+ listener(...argArray);
94
+ }
95
+ }
96
+ }
97
+ }
98
+ emitAndCollect(eventName, ...args) {
99
+ const listeners = this.listeners.get(eventName);
100
+ if (listeners !== undefined) {
101
+ const argArray = args;
102
+ const resultArray = [];
103
+ for (const listener of [...listeners.values()]) {
104
+ resultArray.push(listener(...argArray));
105
+ }
106
+ return resultArray;
107
+ }
108
+ return [];
109
+ }
110
+ on(eventName, listener) {
111
+ const listeners = getOrCreate(this.listeners, eventName, () => new Set());
112
+ if (listeners.has(listener)) {
113
+ const eventDescription = typeof eventName === "symbol" ? eventName.description : String(eventName.toString());
114
+ throw new Error(`Attempted to register the same listener object twice for event ${eventDescription}`);
115
+ }
116
+ listeners.add(listener);
117
+ return () => this.off(eventName, listener);
118
+ }
119
+ off(eventName, listener) {
120
+ const listeners = this.listeners.get(eventName);
121
+ if (listeners?.delete(listener) === true && listeners.size === 0) {
122
+ this.listeners.delete(eventName);
123
+ this.noListeners?.(eventName);
124
+ }
125
+ }
126
+ hasListeners(eventName) {
127
+ if (eventName === undefined) {
128
+ return this.listeners.size > 0;
129
+ }
130
+ return this.listeners.has(eventName);
131
+ }
132
+ }
133
+ exports.CustomEventEmitter = CustomEventEmitter;
134
+ /**
135
+ * This class exposes the constructor and the `emit` method of `EventEmitter`, elevating them from protected to public
136
+ * @internal
137
+ */
138
+ class ComposableEventEmitter extends CustomEventEmitter {
139
+ constructor(noListeners) {
140
+ super(noListeners);
141
+ }
142
+ emit(eventName, ...args) {
143
+ return super.emit(eventName, ...args);
144
+ }
145
+ emitAndCollect(eventName, ...args) {
146
+ return super.emitAndCollect(eventName, ...args);
147
+ }
148
+ }
149
+ /**
150
+ * Create a {@link @fluidframework/core-interfaces#Listenable} that can be instructed to emit events via the {@link @fluidframework/core-interfaces#IEmitter} interface.
151
+ *
152
+ * A class can delegate handling {@link @fluidframework/core-interfaces#Listenable} to the returned value while using it to emit the events.
153
+ * See also CustomEventEmitter which be used as a base class to implement {@link @fluidframework/core-interfaces#Listenable} via extension.
154
+ * @example Forwarding events to the emitter
155
+ * ```typescript
156
+ * interface MyEvents {
157
+ * loaded(): void;
158
+ * }
159
+ *
160
+ * class MyClass implements Listenable<MyEvents> {
161
+ * private readonly events = createEmitter<MyEvents>();
162
+ *
163
+ * private load(): void {
164
+ * this.events.emit("loaded");
165
+ * }
166
+ *
167
+ * public on<K extends keyof MyEvents>(eventName: K, listener: MyEvents[K]): Off {
168
+ * return this.events.on(eventName, listener);
169
+ * }
170
+ *
171
+ * public off<K extends keyof MyEvents>(eventName: K, listener: MyEvents[K]): void {
172
+ * return this.events.off(eventName, listener);
173
+ * }
174
+ * }
175
+ * ```
176
+ * @internal
177
+ */
178
+ function createEmitter(noListeners) {
179
+ return new ComposableEventEmitter(noListeners);
180
+ }
181
+ exports.createEmitter = createEmitter;
182
+ //# sourceMappingURL=emitter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"emitter.js","sourceRoot":"","sources":["../../src/events/emitter.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 type {\n\tHasListeners,\n\tIEmitter,\n\tListenable,\n\tListeners,\n\tMapGetSet,\n\tNoListenersCallback,\n\tOff,\n} from \"@fluidframework/core-interfaces/internal\";\n\n/**\n * Retrieve a value from a map with the given key, or create a new entry if the key is not in the map.\n * @param map - The map to query/update\n * @param key - The key to lookup in the map\n * @param defaultValue - a function which returns a default value. This is called and used to set an initial value for the given key in the map if none exists\n * @returns either the existing value for the given key, or the newly-created value (the result of `defaultValue`)\n * @internal\n */\nfunction getOrCreate<K, V>(map: MapGetSet<K, V>, key: K, defaultValue: (key: K) => V): V {\n\tlet value = map.get(key);\n\tif (value === undefined) {\n\t\tvalue = defaultValue(key);\n\t\tmap.set(key, value);\n\t}\n\treturn value;\n}\n\n/**\n * Provides an API for subscribing to and listening to events.\n *\n * @remarks Classes wishing to emit events may either extend this class, compose over it, or expose it as a property of type {@link @fluidframework/core-interfaces#Listenable}.\n *\n * @example Extending this class\n *\n * ```typescript\n * interface MyEvents {\n * \tloaded: () => void;\n * \tcomputed: () => number;\n * }\n *\n * class MyInheritanceClass extends EventEmitter<MyEvents> {\n * \tprivate load() {\n * \t\tthis.emit(\"loaded\");\n * \t\tconst results: number[] = this.emitAndCollect(\"computed\");\n * \t}\n * }\n * ```\n *\n * @example Composing over this class\n *\n * ```typescript\n * class MyCompositionClass implements Listenable<MyEvents> {\n * \tprivate readonly events = createEmitter<MyEvents>();\n *\n * \tprivate load() {\n * \t\tthis.events.emit(\"loaded\");\n * \t\tconst results: number[] = this.events.emitAndCollect(\"computed\");\n * \t}\n *\n * \tpublic on<K extends keyof MyEvents>(eventName: K, listener: MyEvents[K]): () => void {\n * \t\treturn this.events.on(eventName, listener);\n * \t}\n * }\n * ```\n *\n * @example Exposing this class as a property\n *\n * ```typescript\n * class MyExposingClass {\n * \tprivate readonly _events = createEmitter<MyEvents>();\n * \tpublic readonly events: Listenable<MyEvents> = this._events;\n *\n * \tprivate load() {\n * \t\tthis._events.emit(\"loaded\");\n * \t\tconst results: number[] = this._events.emitAndCollect(\"computed\");\n * \t}\n * }\n * ```\n * @internal\n */\nexport class CustomEventEmitter<TListeners extends Listeners<TListeners>>\n\timplements Listenable<TListeners>, HasListeners<TListeners>\n{\n\tprotected readonly listeners = new Map<\n\t\tkeyof TListeners,\n\t\tSet<(...args: any[]) => TListeners[keyof TListeners]>\n\t>();\n\n\t// Because this is protected and not public, calling this externally (not from a subclass) makes sending events to the constructed instance impossible.\n\t// Instead, use the static `create` function to get an instance which allows emitting events.\n\tprotected constructor(private readonly noListeners?: NoListenersCallback<TListeners>) {}\n\n\tprotected emit<K extends keyof TListeners>(\n\t\teventName: K,\n\t\t...args: Parameters<TListeners[K]>\n\t): void {\n\t\tconst listeners = this.listeners.get(eventName);\n\t\tif (listeners !== undefined) {\n\t\t\t// Current tsc (5.4.5) cannot spread `args` into `listener()`.\n\t\t\tconst argArray: unknown[] = args;\n\t\t\t// This explicitly copies listeners so that new listeners added during this call to emit will not receive this event.\n\t\t\tfor (const listener of [...listeners]) {\n\t\t\t\t// If listener has been unsubscribed while invoking other listeners, skip it.\n\t\t\t\tif (listeners.has(listener)) {\n\t\t\t\t\tlistener(...argArray);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tprotected emitAndCollect<K extends keyof TListeners>(\n\t\teventName: K,\n\t\t...args: Parameters<TListeners[K]>\n\t): ReturnType<TListeners[K]>[] {\n\t\tconst listeners = this.listeners.get(eventName);\n\t\tif (listeners !== undefined) {\n\t\t\tconst argArray: unknown[] = args;\n\t\t\tconst resultArray: ReturnType<TListeners[K]>[] = [];\n\t\t\tfor (const listener of [...listeners.values()]) {\n\t\t\t\tresultArray.push(listener(...argArray));\n\t\t\t}\n\t\t\treturn resultArray;\n\t\t}\n\t\treturn [];\n\t}\n\n\tpublic on<K extends keyof Listeners<TListeners>>(\n\t\teventName: K,\n\t\tlistener: TListeners[K],\n\t): Off {\n\t\tconst listeners = getOrCreate(this.listeners, eventName, () => new Set());\n\t\tif (listeners.has(listener)) {\n\t\t\tconst eventDescription =\n\t\t\t\ttypeof eventName === \"symbol\" ? eventName.description : String(eventName.toString());\n\n\t\t\tthrow new Error(\n\t\t\t\t`Attempted to register the same listener object twice for event ${eventDescription}`,\n\t\t\t);\n\t\t}\n\t\tlisteners.add(listener);\n\t\treturn () => this.off(eventName, listener);\n\t}\n\n\tpublic off<K extends keyof Listeners<TListeners>>(\n\t\teventName: K,\n\t\tlistener: TListeners[K],\n\t): void {\n\t\tconst listeners = this.listeners.get(eventName);\n\t\tif (listeners?.delete(listener) === true && listeners.size === 0) {\n\t\t\tthis.listeners.delete(eventName);\n\t\t\tthis.noListeners?.(eventName);\n\t\t}\n\t}\n\n\tpublic hasListeners(eventName?: keyof TListeners): boolean {\n\t\tif (eventName === undefined) {\n\t\t\treturn this.listeners.size > 0;\n\t\t}\n\t\treturn this.listeners.has(eventName);\n\t}\n}\n\n/**\n * This class exposes the constructor and the `emit` method of `EventEmitter`, elevating them from protected to public\n * @internal\n */\nclass ComposableEventEmitter<TListeners extends Listeners<TListeners>>\n\textends CustomEventEmitter<TListeners>\n\timplements IEmitter<TListeners>\n{\n\tpublic constructor(noListeners?: NoListenersCallback<TListeners>) {\n\t\tsuper(noListeners);\n\t}\n\n\tpublic override emit<K extends keyof TListeners>(\n\t\teventName: K,\n\t\t...args: Parameters<TListeners[K]>\n\t): void {\n\t\treturn super.emit(eventName, ...args);\n\t}\n\n\tpublic override emitAndCollect<K extends keyof TListeners>(\n\t\teventName: K,\n\t\t...args: Parameters<TListeners[K]>\n\t): ReturnType<TListeners[K]>[] {\n\t\treturn super.emitAndCollect(eventName, ...args);\n\t}\n}\n\n/**\n * Create a {@link @fluidframework/core-interfaces#Listenable} that can be instructed to emit events via the {@link @fluidframework/core-interfaces#IEmitter} interface.\n *\n * A class can delegate handling {@link @fluidframework/core-interfaces#Listenable} to the returned value while using it to emit the events.\n * See also CustomEventEmitter which be used as a base class to implement {@link @fluidframework/core-interfaces#Listenable} via extension.\n * @example Forwarding events to the emitter\n * ```typescript\n * interface MyEvents {\n * \tloaded(): void;\n * }\n *\n * class MyClass implements Listenable<MyEvents> {\n * \tprivate readonly events = createEmitter<MyEvents>();\n *\n * \tprivate load(): void {\n * \t\tthis.events.emit(\"loaded\");\n * \t}\n *\n * \tpublic on<K extends keyof MyEvents>(eventName: K, listener: MyEvents[K]): Off {\n * \t\treturn this.events.on(eventName, listener);\n * \t}\n *\n * \tpublic off<K extends keyof MyEvents>(eventName: K, listener: MyEvents[K]): void {\n * \t\treturn this.events.off(eventName, listener);\n * \t}\n * }\n * ```\n * @internal\n */\nexport function createEmitter<TListeners extends object>(\n\tnoListeners?: NoListenersCallback<TListeners>,\n): Listenable<TListeners> & IEmitter<TListeners> & HasListeners<TListeners> {\n\treturn new ComposableEventEmitter<TListeners>(noListeners);\n}\n"]}
@@ -0,0 +1,6 @@
1
+ /*!
2
+ * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
+ * Licensed under the MIT License.
4
+ */
5
+ export { createEmitter, CustomEventEmitter } from "./emitter.js";
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/events/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,aAAa,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC"}
@@ -0,0 +1,11 @@
1
+ "use strict";
2
+ /*!
3
+ * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
4
+ * Licensed under the MIT License.
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.CustomEventEmitter = exports.createEmitter = void 0;
8
+ var emitter_js_1 = require("./emitter.js");
9
+ Object.defineProperty(exports, "createEmitter", { enumerable: true, get: function () { return emitter_js_1.createEmitter; } });
10
+ Object.defineProperty(exports, "CustomEventEmitter", { enumerable: true, get: function () { return emitter_js_1.CustomEventEmitter; } });
11
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/events/index.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;AAEH,2CAAiE;AAAxD,2GAAA,aAAa,OAAA;AAAE,gHAAA,kBAAkB,OAAA","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nexport { createEmitter, CustomEventEmitter } from \"./emitter.js\";\n"]}
@@ -11,4 +11,5 @@ export { EventEmitter } from "./eventEmitter.cjs";
11
11
  export { type IsomorphicPerformance } from "./performanceIsomorphic.js";
12
12
  export { type ITraceEvent, Trace } from "./trace.js";
13
13
  export { type EventEmitterEventType, TypedEventEmitter, type TypedEventTransform, } from "./typedEventEmitter.js";
14
+ export { createEmitter } from "./events/index.js";
14
15
  //# sourceMappingURL=indexBrowser.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"indexBrowser.d.ts","sourceRoot":"","sources":["../src/indexBrowser.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,OAAO,EACN,cAAc,EACd,aAAa,EACb,SAAS,EACT,cAAc,EACd,kBAAkB,GAClB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AAEzD,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,EAAE,MAAM,4BAA4B,CAAC;AACxF,OAAO,EAAE,uBAAuB,EAAE,MAAM,mBAAmB,CAAC;AAC5D,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,KAAK,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AACxE,OAAO,EAAE,KAAK,WAAW,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACrD,OAAO,EACN,KAAK,qBAAqB,EAC1B,iBAAiB,EACjB,KAAK,mBAAmB,GACxB,MAAM,wBAAwB,CAAC"}
1
+ {"version":3,"file":"indexBrowser.d.ts","sourceRoot":"","sources":["../src/indexBrowser.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,OAAO,EACN,cAAc,EACd,aAAa,EACb,SAAS,EACT,cAAc,EACd,kBAAkB,GAClB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AAEzD,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,EAAE,MAAM,4BAA4B,CAAC;AACxF,OAAO,EAAE,uBAAuB,EAAE,MAAM,mBAAmB,CAAC;AAC5D,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,KAAK,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AACxE,OAAO,EAAE,KAAK,WAAW,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACrD,OAAO,EACN,KAAK,qBAAqB,EAC1B,iBAAiB,EACjB,KAAK,mBAAmB,GACxB,MAAM,wBAAwB,CAAC;AAEhC,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC"}
@@ -4,7 +4,7 @@
4
4
  * Licensed under the MIT License.
5
5
  */
6
6
  Object.defineProperty(exports, "__esModule", { value: true });
7
- exports.TypedEventEmitter = exports.Trace = exports.EventEmitter = exports.Uint8ArrayToArrayBuffer = exports.toUtf8 = exports.fromUtf8ToBase64 = exports.fromBase64ToUtf8 = exports.performance = exports.hashFile = exports.gitHashFile = exports.Uint8ArrayToString = exports.stringToBuffer = exports.IsoBuffer = exports.isArrayBuffer = exports.bufferToString = void 0;
7
+ exports.createEmitter = exports.TypedEventEmitter = exports.Trace = exports.EventEmitter = exports.Uint8ArrayToArrayBuffer = exports.toUtf8 = exports.fromUtf8ToBase64 = exports.fromBase64ToUtf8 = exports.performance = exports.hashFile = exports.gitHashFile = exports.Uint8ArrayToString = exports.stringToBuffer = exports.IsoBuffer = exports.isArrayBuffer = exports.bufferToString = void 0;
8
8
  // Entrypoint for browser-specific code in the package.
9
9
  // (See 'Isomorphic Code' section in the package README.md.)
10
10
  var bufferBrowser_js_1 = require("./bufferBrowser.js");
@@ -30,4 +30,6 @@ var trace_js_1 = require("./trace.js");
30
30
  Object.defineProperty(exports, "Trace", { enumerable: true, get: function () { return trace_js_1.Trace; } });
31
31
  var typedEventEmitter_js_1 = require("./typedEventEmitter.js");
32
32
  Object.defineProperty(exports, "TypedEventEmitter", { enumerable: true, get: function () { return typedEventEmitter_js_1.TypedEventEmitter; } });
33
+ var index_js_1 = require("./events/index.js");
34
+ Object.defineProperty(exports, "createEmitter", { enumerable: true, get: function () { return index_js_1.createEmitter; } });
33
35
  //# sourceMappingURL=indexBrowser.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"indexBrowser.js","sourceRoot":"","sources":["../src/indexBrowser.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;AAEH,uDAAuD;AACvD,4DAA4D;AAE5D,uDAM4B;AAL3B,kHAAA,cAAc,OAAA;AACd,iHAAA,aAAa,OAAA;AACb,6GAAA,SAAS,OAAA;AACT,kHAAA,cAAc,OAAA;AACd,sHAAA,kBAAkB,OAAA;AAEnB,2DAA6D;AAApD,iHAAA,WAAW,OAAA;AAAE,8GAAA,QAAQ,OAAA;AAC9B,uEAAyD;AAAhD,uHAAA,WAAW,OAAA;AAEpB,uEAAwF;AAA/E,4HAAA,gBAAgB,OAAA;AAAE,4HAAA,gBAAgB,OAAA;AAAE,kHAAA,MAAM,OAAA;AACnD,qDAA4D;AAAnD,0HAAA,uBAAuB,OAAA;AAChC,uDAAkD;AAAzC,gHAAA,YAAY,OAAA;AAErB,uCAAqD;AAA1B,iGAAA,KAAK,OAAA;AAChC,+DAIgC;AAF/B,yHAAA,iBAAiB,OAAA","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\n// Entrypoint for browser-specific code in the package.\n// (See 'Isomorphic Code' section in the package README.md.)\n\nexport {\n\tbufferToString,\n\tisArrayBuffer,\n\tIsoBuffer,\n\tstringToBuffer,\n\tUint8ArrayToString,\n} from \"./bufferBrowser.js\";\nexport { gitHashFile, hashFile } from \"./hashFileBrowser.js\";\nexport { performance } from \"./performanceIsomorphic.js\";\n\nexport { fromBase64ToUtf8, fromUtf8ToBase64, toUtf8 } from \"./base64EncodingBrowser.js\";\nexport { Uint8ArrayToArrayBuffer } from \"./bufferShared.js\";\nexport { EventEmitter } from \"./eventEmitter.cjs\";\nexport { type IsomorphicPerformance } from \"./performanceIsomorphic.js\";\nexport { type ITraceEvent, Trace } from \"./trace.js\";\nexport {\n\ttype EventEmitterEventType,\n\tTypedEventEmitter,\n\ttype TypedEventTransform,\n} from \"./typedEventEmitter.js\";\n"]}
1
+ {"version":3,"file":"indexBrowser.js","sourceRoot":"","sources":["../src/indexBrowser.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;AAEH,uDAAuD;AACvD,4DAA4D;AAE5D,uDAM4B;AAL3B,kHAAA,cAAc,OAAA;AACd,iHAAA,aAAa,OAAA;AACb,6GAAA,SAAS,OAAA;AACT,kHAAA,cAAc,OAAA;AACd,sHAAA,kBAAkB,OAAA;AAEnB,2DAA6D;AAApD,iHAAA,WAAW,OAAA;AAAE,8GAAA,QAAQ,OAAA;AAC9B,uEAAyD;AAAhD,uHAAA,WAAW,OAAA;AAEpB,uEAAwF;AAA/E,4HAAA,gBAAgB,OAAA;AAAE,4HAAA,gBAAgB,OAAA;AAAE,kHAAA,MAAM,OAAA;AACnD,qDAA4D;AAAnD,0HAAA,uBAAuB,OAAA;AAChC,uDAAkD;AAAzC,gHAAA,YAAY,OAAA;AAErB,uCAAqD;AAA1B,iGAAA,KAAK,OAAA;AAChC,+DAIgC;AAF/B,yHAAA,iBAAiB,OAAA;AAIlB,8CAAkD;AAAzC,yGAAA,aAAa,OAAA","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\n// Entrypoint for browser-specific code in the package.\n// (See 'Isomorphic Code' section in the package README.md.)\n\nexport {\n\tbufferToString,\n\tisArrayBuffer,\n\tIsoBuffer,\n\tstringToBuffer,\n\tUint8ArrayToString,\n} from \"./bufferBrowser.js\";\nexport { gitHashFile, hashFile } from \"./hashFileBrowser.js\";\nexport { performance } from \"./performanceIsomorphic.js\";\n\nexport { fromBase64ToUtf8, fromUtf8ToBase64, toUtf8 } from \"./base64EncodingBrowser.js\";\nexport { Uint8ArrayToArrayBuffer } from \"./bufferShared.js\";\nexport { EventEmitter } from \"./eventEmitter.cjs\";\nexport { type IsomorphicPerformance } from \"./performanceIsomorphic.js\";\nexport { type ITraceEvent, Trace } from \"./trace.js\";\nexport {\n\ttype EventEmitterEventType,\n\tTypedEventEmitter,\n\ttype TypedEventTransform,\n} from \"./typedEventEmitter.js\";\n\nexport { createEmitter } from \"./events/index.js\";\n"]}
@@ -12,4 +12,5 @@ export { EventEmitter } from "./eventEmitter.cjs";
12
12
  export type { IsomorphicPerformance } from "./performanceIsomorphic.js";
13
13
  export { type ITraceEvent, Trace } from "./trace.js";
14
14
  export { type EventEmitterEventType, TypedEventEmitter, type TypedEventTransform, } from "./typedEventEmitter.js";
15
+ export { createEmitter } from "./events/index.js";
15
16
  //# sourceMappingURL=indexNode.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"indexNode.d.ts","sourceRoot":"","sources":["../src/indexNode.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,OAAO,EAAE,KAAK,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAC9C,OAAO,EACN,cAAc,EACd,SAAS,EACT,cAAc,EACd,kBAAkB,GAClB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAC1D,OAAO,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AAEzD,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AACrF,OAAO,EAAE,uBAAuB,EAAE,MAAM,mBAAmB,CAAC;AAC5D,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,YAAY,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AACxE,OAAO,EAAE,KAAK,WAAW,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACrD,OAAO,EACN,KAAK,qBAAqB,EAC1B,iBAAiB,EACjB,KAAK,mBAAmB,GACxB,MAAM,wBAAwB,CAAC"}
1
+ {"version":3,"file":"indexNode.d.ts","sourceRoot":"","sources":["../src/indexNode.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,OAAO,EAAE,KAAK,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAC9C,OAAO,EACN,cAAc,EACd,SAAS,EACT,cAAc,EACd,kBAAkB,GAClB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAC1D,OAAO,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AAEzD,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AACrF,OAAO,EAAE,uBAAuB,EAAE,MAAM,mBAAmB,CAAC;AAC5D,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,YAAY,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AACxE,OAAO,EAAE,KAAK,WAAW,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACrD,OAAO,EACN,KAAK,qBAAqB,EAC1B,iBAAiB,EACjB,KAAK,mBAAmB,GACxB,MAAM,wBAAwB,CAAC;AAEhC,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC"}
package/dist/indexNode.js CHANGED
@@ -4,7 +4,7 @@
4
4
  * Licensed under the MIT License.
5
5
  */
6
6
  Object.defineProperty(exports, "__esModule", { value: true });
7
- exports.TypedEventEmitter = exports.Trace = exports.EventEmitter = exports.Uint8ArrayToArrayBuffer = exports.toUtf8 = exports.fromUtf8ToBase64 = exports.fromBase64ToUtf8 = exports.performance = exports.hashFile = exports.gitHashFile = exports.Uint8ArrayToString = exports.stringToBuffer = exports.IsoBuffer = exports.bufferToString = void 0;
7
+ exports.createEmitter = exports.TypedEventEmitter = exports.Trace = exports.EventEmitter = exports.Uint8ArrayToArrayBuffer = exports.toUtf8 = exports.fromUtf8ToBase64 = exports.fromBase64ToUtf8 = exports.performance = exports.hashFile = exports.gitHashFile = exports.Uint8ArrayToString = exports.stringToBuffer = exports.IsoBuffer = exports.bufferToString = void 0;
8
8
  var bufferNode_js_1 = require("./bufferNode.js");
9
9
  Object.defineProperty(exports, "bufferToString", { enumerable: true, get: function () { return bufferNode_js_1.bufferToString; } });
10
10
  Object.defineProperty(exports, "IsoBuffer", { enumerable: true, get: function () { return bufferNode_js_1.IsoBuffer; } });
@@ -27,4 +27,6 @@ var trace_js_1 = require("./trace.js");
27
27
  Object.defineProperty(exports, "Trace", { enumerable: true, get: function () { return trace_js_1.Trace; } });
28
28
  var typedEventEmitter_js_1 = require("./typedEventEmitter.js");
29
29
  Object.defineProperty(exports, "TypedEventEmitter", { enumerable: true, get: function () { return typedEventEmitter_js_1.TypedEventEmitter; } });
30
+ var index_js_1 = require("./events/index.js");
31
+ Object.defineProperty(exports, "createEmitter", { enumerable: true, get: function () { return index_js_1.createEmitter; } });
30
32
  //# sourceMappingURL=indexNode.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"indexNode.js","sourceRoot":"","sources":["../src/indexNode.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;AAMH,iDAKyB;AAJxB,+GAAA,cAAc,OAAA;AACd,0GAAA,SAAS,OAAA;AACT,+GAAA,cAAc,OAAA;AACd,mHAAA,kBAAkB,OAAA;AAEnB,qDAA0D;AAAjD,8GAAA,WAAW,OAAA;AAAE,2GAAA,QAAQ,OAAA;AAC9B,uEAAyD;AAAhD,uHAAA,WAAW,OAAA;AAEpB,iEAAqF;AAA5E,yHAAA,gBAAgB,OAAA;AAAE,yHAAA,gBAAgB,OAAA;AAAE,+GAAA,MAAM,OAAA;AACnD,qDAA4D;AAAnD,0HAAA,uBAAuB,OAAA;AAChC,uDAAkD;AAAzC,gHAAA,YAAY,OAAA;AAErB,uCAAqD;AAA1B,iGAAA,KAAK,OAAA;AAChC,+DAIgC;AAF/B,yHAAA,iBAAiB,OAAA","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\n// Entrypoint for Node.js-specific code in the package.\n// (See 'Isomorphic Code' section in the package README.md.)\n\nexport { type Buffer } from \"./bufferNode.js\";\nexport {\n\tbufferToString,\n\tIsoBuffer,\n\tstringToBuffer,\n\tUint8ArrayToString,\n} from \"./bufferNode.js\";\nexport { gitHashFile, hashFile } from \"./hashFileNode.js\";\nexport { performance } from \"./performanceIsomorphic.js\";\n\nexport { fromBase64ToUtf8, fromUtf8ToBase64, toUtf8 } from \"./base64EncodingNode.js\";\nexport { Uint8ArrayToArrayBuffer } from \"./bufferShared.js\";\nexport { EventEmitter } from \"./eventEmitter.cjs\";\nexport type { IsomorphicPerformance } from \"./performanceIsomorphic.js\";\nexport { type ITraceEvent, Trace } from \"./trace.js\";\nexport {\n\ttype EventEmitterEventType,\n\tTypedEventEmitter,\n\ttype TypedEventTransform,\n} from \"./typedEventEmitter.js\";\n"]}
1
+ {"version":3,"file":"indexNode.js","sourceRoot":"","sources":["../src/indexNode.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;AAMH,iDAKyB;AAJxB,+GAAA,cAAc,OAAA;AACd,0GAAA,SAAS,OAAA;AACT,+GAAA,cAAc,OAAA;AACd,mHAAA,kBAAkB,OAAA;AAEnB,qDAA0D;AAAjD,8GAAA,WAAW,OAAA;AAAE,2GAAA,QAAQ,OAAA;AAC9B,uEAAyD;AAAhD,uHAAA,WAAW,OAAA;AAEpB,iEAAqF;AAA5E,yHAAA,gBAAgB,OAAA;AAAE,yHAAA,gBAAgB,OAAA;AAAE,+GAAA,MAAM,OAAA;AACnD,qDAA4D;AAAnD,0HAAA,uBAAuB,OAAA;AAChC,uDAAkD;AAAzC,gHAAA,YAAY,OAAA;AAErB,uCAAqD;AAA1B,iGAAA,KAAK,OAAA;AAChC,+DAIgC;AAF/B,yHAAA,iBAAiB,OAAA;AAIlB,8CAAkD;AAAzC,yGAAA,aAAa,OAAA","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\n// Entrypoint for Node.js-specific code in the package.\n// (See 'Isomorphic Code' section in the package README.md.)\n\nexport { type Buffer } from \"./bufferNode.js\";\nexport {\n\tbufferToString,\n\tIsoBuffer,\n\tstringToBuffer,\n\tUint8ArrayToString,\n} from \"./bufferNode.js\";\nexport { gitHashFile, hashFile } from \"./hashFileNode.js\";\nexport { performance } from \"./performanceIsomorphic.js\";\n\nexport { fromBase64ToUtf8, fromUtf8ToBase64, toUtf8 } from \"./base64EncodingNode.js\";\nexport { Uint8ArrayToArrayBuffer } from \"./bufferShared.js\";\nexport { EventEmitter } from \"./eventEmitter.cjs\";\nexport type { IsomorphicPerformance } from \"./performanceIsomorphic.js\";\nexport { type ITraceEvent, Trace } from \"./trace.js\";\nexport {\n\ttype EventEmitterEventType,\n\tTypedEventEmitter,\n\ttype TypedEventTransform,\n} from \"./typedEventEmitter.js\";\n\nexport { createEmitter } from \"./events/index.js\";\n"]}
@@ -0,0 +1,99 @@
1
+ /*!
2
+ * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
+ * Licensed under the MIT License.
4
+ */
5
+ import type { HasListeners, IEmitter, Listenable, Listeners, NoListenersCallback, Off } from "@fluidframework/core-interfaces/internal";
6
+ /**
7
+ * Provides an API for subscribing to and listening to events.
8
+ *
9
+ * @remarks Classes wishing to emit events may either extend this class, compose over it, or expose it as a property of type {@link @fluidframework/core-interfaces#Listenable}.
10
+ *
11
+ * @example Extending this class
12
+ *
13
+ * ```typescript
14
+ * interface MyEvents {
15
+ * loaded: () => void;
16
+ * computed: () => number;
17
+ * }
18
+ *
19
+ * class MyInheritanceClass extends EventEmitter<MyEvents> {
20
+ * private load() {
21
+ * this.emit("loaded");
22
+ * const results: number[] = this.emitAndCollect("computed");
23
+ * }
24
+ * }
25
+ * ```
26
+ *
27
+ * @example Composing over this class
28
+ *
29
+ * ```typescript
30
+ * class MyCompositionClass implements Listenable<MyEvents> {
31
+ * private readonly events = createEmitter<MyEvents>();
32
+ *
33
+ * private load() {
34
+ * this.events.emit("loaded");
35
+ * const results: number[] = this.events.emitAndCollect("computed");
36
+ * }
37
+ *
38
+ * public on<K extends keyof MyEvents>(eventName: K, listener: MyEvents[K]): () => void {
39
+ * return this.events.on(eventName, listener);
40
+ * }
41
+ * }
42
+ * ```
43
+ *
44
+ * @example Exposing this class as a property
45
+ *
46
+ * ```typescript
47
+ * class MyExposingClass {
48
+ * private readonly _events = createEmitter<MyEvents>();
49
+ * public readonly events: Listenable<MyEvents> = this._events;
50
+ *
51
+ * private load() {
52
+ * this._events.emit("loaded");
53
+ * const results: number[] = this._events.emitAndCollect("computed");
54
+ * }
55
+ * }
56
+ * ```
57
+ * @internal
58
+ */
59
+ export declare class CustomEventEmitter<TListeners extends Listeners<TListeners>> implements Listenable<TListeners>, HasListeners<TListeners> {
60
+ private readonly noListeners?;
61
+ protected readonly listeners: Map<keyof TListeners, Set<(...args: any[]) => TListeners[keyof TListeners]>>;
62
+ protected constructor(noListeners?: NoListenersCallback<TListeners> | undefined);
63
+ protected emit<K extends keyof TListeners>(eventName: K, ...args: Parameters<TListeners[K]>): void;
64
+ protected emitAndCollect<K extends keyof TListeners>(eventName: K, ...args: Parameters<TListeners[K]>): ReturnType<TListeners[K]>[];
65
+ on<K extends keyof Listeners<TListeners>>(eventName: K, listener: TListeners[K]): Off;
66
+ off<K extends keyof Listeners<TListeners>>(eventName: K, listener: TListeners[K]): void;
67
+ hasListeners(eventName?: keyof TListeners): boolean;
68
+ }
69
+ /**
70
+ * Create a {@link @fluidframework/core-interfaces#Listenable} that can be instructed to emit events via the {@link @fluidframework/core-interfaces#IEmitter} interface.
71
+ *
72
+ * A class can delegate handling {@link @fluidframework/core-interfaces#Listenable} to the returned value while using it to emit the events.
73
+ * See also CustomEventEmitter which be used as a base class to implement {@link @fluidframework/core-interfaces#Listenable} via extension.
74
+ * @example Forwarding events to the emitter
75
+ * ```typescript
76
+ * interface MyEvents {
77
+ * loaded(): void;
78
+ * }
79
+ *
80
+ * class MyClass implements Listenable<MyEvents> {
81
+ * private readonly events = createEmitter<MyEvents>();
82
+ *
83
+ * private load(): void {
84
+ * this.events.emit("loaded");
85
+ * }
86
+ *
87
+ * public on<K extends keyof MyEvents>(eventName: K, listener: MyEvents[K]): Off {
88
+ * return this.events.on(eventName, listener);
89
+ * }
90
+ *
91
+ * public off<K extends keyof MyEvents>(eventName: K, listener: MyEvents[K]): void {
92
+ * return this.events.off(eventName, listener);
93
+ * }
94
+ * }
95
+ * ```
96
+ * @internal
97
+ */
98
+ export declare function createEmitter<TListeners extends object>(noListeners?: NoListenersCallback<TListeners>): Listenable<TListeners> & IEmitter<TListeners> & HasListeners<TListeners>;
99
+ //# sourceMappingURL=emitter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"emitter.d.ts","sourceRoot":"","sources":["../../src/events/emitter.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EACX,YAAY,EACZ,QAAQ,EACR,UAAU,EACV,SAAS,EAET,mBAAmB,EACnB,GAAG,EACH,MAAM,0CAA0C,CAAC;AAmBlD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoDG;AACH,qBAAa,kBAAkB,CAAC,UAAU,SAAS,SAAS,CAAC,UAAU,CAAC,CACvE,YAAW,UAAU,CAAC,UAAU,CAAC,EAAE,YAAY,CAAC,UAAU,CAAC;IASrC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAC;IAPnD,SAAS,CAAC,QAAQ,CAAC,SAAS,sCAEb,GAAG,EAAE,KAAK,UAAU,CAAC,MAAM,UAAU,CAAC,GACjD;IAIJ,SAAS,aAA8B,WAAW,CAAC,6CAAiC;IAEpF,SAAS,CAAC,IAAI,CAAC,CAAC,SAAS,MAAM,UAAU,EACxC,SAAS,EAAE,CAAC,EACZ,GAAG,IAAI,EAAE,UAAU,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,GAChC,IAAI;IAeP,SAAS,CAAC,cAAc,CAAC,CAAC,SAAS,MAAM,UAAU,EAClD,SAAS,EAAE,CAAC,EACZ,GAAG,IAAI,EAAE,UAAU,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,GAChC,UAAU,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,EAAE;IAavB,EAAE,CAAC,CAAC,SAAS,MAAM,SAAS,CAAC,UAAU,CAAC,EAC9C,SAAS,EAAE,CAAC,EACZ,QAAQ,EAAE,UAAU,CAAC,CAAC,CAAC,GACrB,GAAG;IAcC,GAAG,CAAC,CAAC,SAAS,MAAM,SAAS,CAAC,UAAU,CAAC,EAC/C,SAAS,EAAE,CAAC,EACZ,QAAQ,EAAE,UAAU,CAAC,CAAC,CAAC,GACrB,IAAI;IAQA,YAAY,CAAC,SAAS,CAAC,EAAE,MAAM,UAAU,GAAG,OAAO;CAM1D;AA6BD;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,wBAAgB,aAAa,CAAC,UAAU,SAAS,MAAM,EACtD,WAAW,CAAC,EAAE,mBAAmB,CAAC,UAAU,CAAC,GAC3C,UAAU,CAAC,UAAU,CAAC,GAAG,QAAQ,CAAC,UAAU,CAAC,GAAG,YAAY,CAAC,UAAU,CAAC,CAE1E"}
@@ -0,0 +1,177 @@
1
+ /*!
2
+ * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
+ * Licensed under the MIT License.
4
+ */
5
+ /**
6
+ * Retrieve a value from a map with the given key, or create a new entry if the key is not in the map.
7
+ * @param map - The map to query/update
8
+ * @param key - The key to lookup in the map
9
+ * @param defaultValue - a function which returns a default value. This is called and used to set an initial value for the given key in the map if none exists
10
+ * @returns either the existing value for the given key, or the newly-created value (the result of `defaultValue`)
11
+ * @internal
12
+ */
13
+ function getOrCreate(map, key, defaultValue) {
14
+ let value = map.get(key);
15
+ if (value === undefined) {
16
+ value = defaultValue(key);
17
+ map.set(key, value);
18
+ }
19
+ return value;
20
+ }
21
+ /**
22
+ * Provides an API for subscribing to and listening to events.
23
+ *
24
+ * @remarks Classes wishing to emit events may either extend this class, compose over it, or expose it as a property of type {@link @fluidframework/core-interfaces#Listenable}.
25
+ *
26
+ * @example Extending this class
27
+ *
28
+ * ```typescript
29
+ * interface MyEvents {
30
+ * loaded: () => void;
31
+ * computed: () => number;
32
+ * }
33
+ *
34
+ * class MyInheritanceClass extends EventEmitter<MyEvents> {
35
+ * private load() {
36
+ * this.emit("loaded");
37
+ * const results: number[] = this.emitAndCollect("computed");
38
+ * }
39
+ * }
40
+ * ```
41
+ *
42
+ * @example Composing over this class
43
+ *
44
+ * ```typescript
45
+ * class MyCompositionClass implements Listenable<MyEvents> {
46
+ * private readonly events = createEmitter<MyEvents>();
47
+ *
48
+ * private load() {
49
+ * this.events.emit("loaded");
50
+ * const results: number[] = this.events.emitAndCollect("computed");
51
+ * }
52
+ *
53
+ * public on<K extends keyof MyEvents>(eventName: K, listener: MyEvents[K]): () => void {
54
+ * return this.events.on(eventName, listener);
55
+ * }
56
+ * }
57
+ * ```
58
+ *
59
+ * @example Exposing this class as a property
60
+ *
61
+ * ```typescript
62
+ * class MyExposingClass {
63
+ * private readonly _events = createEmitter<MyEvents>();
64
+ * public readonly events: Listenable<MyEvents> = this._events;
65
+ *
66
+ * private load() {
67
+ * this._events.emit("loaded");
68
+ * const results: number[] = this._events.emitAndCollect("computed");
69
+ * }
70
+ * }
71
+ * ```
72
+ * @internal
73
+ */
74
+ export class CustomEventEmitter {
75
+ // Because this is protected and not public, calling this externally (not from a subclass) makes sending events to the constructed instance impossible.
76
+ // Instead, use the static `create` function to get an instance which allows emitting events.
77
+ constructor(noListeners) {
78
+ this.noListeners = noListeners;
79
+ this.listeners = new Map();
80
+ }
81
+ emit(eventName, ...args) {
82
+ const listeners = this.listeners.get(eventName);
83
+ if (listeners !== undefined) {
84
+ // Current tsc (5.4.5) cannot spread `args` into `listener()`.
85
+ const argArray = args;
86
+ // This explicitly copies listeners so that new listeners added during this call to emit will not receive this event.
87
+ for (const listener of [...listeners]) {
88
+ // If listener has been unsubscribed while invoking other listeners, skip it.
89
+ if (listeners.has(listener)) {
90
+ listener(...argArray);
91
+ }
92
+ }
93
+ }
94
+ }
95
+ emitAndCollect(eventName, ...args) {
96
+ const listeners = this.listeners.get(eventName);
97
+ if (listeners !== undefined) {
98
+ const argArray = args;
99
+ const resultArray = [];
100
+ for (const listener of [...listeners.values()]) {
101
+ resultArray.push(listener(...argArray));
102
+ }
103
+ return resultArray;
104
+ }
105
+ return [];
106
+ }
107
+ on(eventName, listener) {
108
+ const listeners = getOrCreate(this.listeners, eventName, () => new Set());
109
+ if (listeners.has(listener)) {
110
+ const eventDescription = typeof eventName === "symbol" ? eventName.description : String(eventName.toString());
111
+ throw new Error(`Attempted to register the same listener object twice for event ${eventDescription}`);
112
+ }
113
+ listeners.add(listener);
114
+ return () => this.off(eventName, listener);
115
+ }
116
+ off(eventName, listener) {
117
+ const listeners = this.listeners.get(eventName);
118
+ if (listeners?.delete(listener) === true && listeners.size === 0) {
119
+ this.listeners.delete(eventName);
120
+ this.noListeners?.(eventName);
121
+ }
122
+ }
123
+ hasListeners(eventName) {
124
+ if (eventName === undefined) {
125
+ return this.listeners.size > 0;
126
+ }
127
+ return this.listeners.has(eventName);
128
+ }
129
+ }
130
+ /**
131
+ * This class exposes the constructor and the `emit` method of `EventEmitter`, elevating them from protected to public
132
+ * @internal
133
+ */
134
+ class ComposableEventEmitter extends CustomEventEmitter {
135
+ constructor(noListeners) {
136
+ super(noListeners);
137
+ }
138
+ emit(eventName, ...args) {
139
+ return super.emit(eventName, ...args);
140
+ }
141
+ emitAndCollect(eventName, ...args) {
142
+ return super.emitAndCollect(eventName, ...args);
143
+ }
144
+ }
145
+ /**
146
+ * Create a {@link @fluidframework/core-interfaces#Listenable} that can be instructed to emit events via the {@link @fluidframework/core-interfaces#IEmitter} interface.
147
+ *
148
+ * A class can delegate handling {@link @fluidframework/core-interfaces#Listenable} to the returned value while using it to emit the events.
149
+ * See also CustomEventEmitter which be used as a base class to implement {@link @fluidframework/core-interfaces#Listenable} via extension.
150
+ * @example Forwarding events to the emitter
151
+ * ```typescript
152
+ * interface MyEvents {
153
+ * loaded(): void;
154
+ * }
155
+ *
156
+ * class MyClass implements Listenable<MyEvents> {
157
+ * private readonly events = createEmitter<MyEvents>();
158
+ *
159
+ * private load(): void {
160
+ * this.events.emit("loaded");
161
+ * }
162
+ *
163
+ * public on<K extends keyof MyEvents>(eventName: K, listener: MyEvents[K]): Off {
164
+ * return this.events.on(eventName, listener);
165
+ * }
166
+ *
167
+ * public off<K extends keyof MyEvents>(eventName: K, listener: MyEvents[K]): void {
168
+ * return this.events.off(eventName, listener);
169
+ * }
170
+ * }
171
+ * ```
172
+ * @internal
173
+ */
174
+ export function createEmitter(noListeners) {
175
+ return new ComposableEventEmitter(noListeners);
176
+ }
177
+ //# sourceMappingURL=emitter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"emitter.js","sourceRoot":"","sources":["../../src/events/emitter.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 type {\n\tHasListeners,\n\tIEmitter,\n\tListenable,\n\tListeners,\n\tMapGetSet,\n\tNoListenersCallback,\n\tOff,\n} from \"@fluidframework/core-interfaces/internal\";\n\n/**\n * Retrieve a value from a map with the given key, or create a new entry if the key is not in the map.\n * @param map - The map to query/update\n * @param key - The key to lookup in the map\n * @param defaultValue - a function which returns a default value. This is called and used to set an initial value for the given key in the map if none exists\n * @returns either the existing value for the given key, or the newly-created value (the result of `defaultValue`)\n * @internal\n */\nfunction getOrCreate<K, V>(map: MapGetSet<K, V>, key: K, defaultValue: (key: K) => V): V {\n\tlet value = map.get(key);\n\tif (value === undefined) {\n\t\tvalue = defaultValue(key);\n\t\tmap.set(key, value);\n\t}\n\treturn value;\n}\n\n/**\n * Provides an API for subscribing to and listening to events.\n *\n * @remarks Classes wishing to emit events may either extend this class, compose over it, or expose it as a property of type {@link @fluidframework/core-interfaces#Listenable}.\n *\n * @example Extending this class\n *\n * ```typescript\n * interface MyEvents {\n * \tloaded: () => void;\n * \tcomputed: () => number;\n * }\n *\n * class MyInheritanceClass extends EventEmitter<MyEvents> {\n * \tprivate load() {\n * \t\tthis.emit(\"loaded\");\n * \t\tconst results: number[] = this.emitAndCollect(\"computed\");\n * \t}\n * }\n * ```\n *\n * @example Composing over this class\n *\n * ```typescript\n * class MyCompositionClass implements Listenable<MyEvents> {\n * \tprivate readonly events = createEmitter<MyEvents>();\n *\n * \tprivate load() {\n * \t\tthis.events.emit(\"loaded\");\n * \t\tconst results: number[] = this.events.emitAndCollect(\"computed\");\n * \t}\n *\n * \tpublic on<K extends keyof MyEvents>(eventName: K, listener: MyEvents[K]): () => void {\n * \t\treturn this.events.on(eventName, listener);\n * \t}\n * }\n * ```\n *\n * @example Exposing this class as a property\n *\n * ```typescript\n * class MyExposingClass {\n * \tprivate readonly _events = createEmitter<MyEvents>();\n * \tpublic readonly events: Listenable<MyEvents> = this._events;\n *\n * \tprivate load() {\n * \t\tthis._events.emit(\"loaded\");\n * \t\tconst results: number[] = this._events.emitAndCollect(\"computed\");\n * \t}\n * }\n * ```\n * @internal\n */\nexport class CustomEventEmitter<TListeners extends Listeners<TListeners>>\n\timplements Listenable<TListeners>, HasListeners<TListeners>\n{\n\tprotected readonly listeners = new Map<\n\t\tkeyof TListeners,\n\t\tSet<(...args: any[]) => TListeners[keyof TListeners]>\n\t>();\n\n\t// Because this is protected and not public, calling this externally (not from a subclass) makes sending events to the constructed instance impossible.\n\t// Instead, use the static `create` function to get an instance which allows emitting events.\n\tprotected constructor(private readonly noListeners?: NoListenersCallback<TListeners>) {}\n\n\tprotected emit<K extends keyof TListeners>(\n\t\teventName: K,\n\t\t...args: Parameters<TListeners[K]>\n\t): void {\n\t\tconst listeners = this.listeners.get(eventName);\n\t\tif (listeners !== undefined) {\n\t\t\t// Current tsc (5.4.5) cannot spread `args` into `listener()`.\n\t\t\tconst argArray: unknown[] = args;\n\t\t\t// This explicitly copies listeners so that new listeners added during this call to emit will not receive this event.\n\t\t\tfor (const listener of [...listeners]) {\n\t\t\t\t// If listener has been unsubscribed while invoking other listeners, skip it.\n\t\t\t\tif (listeners.has(listener)) {\n\t\t\t\t\tlistener(...argArray);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tprotected emitAndCollect<K extends keyof TListeners>(\n\t\teventName: K,\n\t\t...args: Parameters<TListeners[K]>\n\t): ReturnType<TListeners[K]>[] {\n\t\tconst listeners = this.listeners.get(eventName);\n\t\tif (listeners !== undefined) {\n\t\t\tconst argArray: unknown[] = args;\n\t\t\tconst resultArray: ReturnType<TListeners[K]>[] = [];\n\t\t\tfor (const listener of [...listeners.values()]) {\n\t\t\t\tresultArray.push(listener(...argArray));\n\t\t\t}\n\t\t\treturn resultArray;\n\t\t}\n\t\treturn [];\n\t}\n\n\tpublic on<K extends keyof Listeners<TListeners>>(\n\t\teventName: K,\n\t\tlistener: TListeners[K],\n\t): Off {\n\t\tconst listeners = getOrCreate(this.listeners, eventName, () => new Set());\n\t\tif (listeners.has(listener)) {\n\t\t\tconst eventDescription =\n\t\t\t\ttypeof eventName === \"symbol\" ? eventName.description : String(eventName.toString());\n\n\t\t\tthrow new Error(\n\t\t\t\t`Attempted to register the same listener object twice for event ${eventDescription}`,\n\t\t\t);\n\t\t}\n\t\tlisteners.add(listener);\n\t\treturn () => this.off(eventName, listener);\n\t}\n\n\tpublic off<K extends keyof Listeners<TListeners>>(\n\t\teventName: K,\n\t\tlistener: TListeners[K],\n\t): void {\n\t\tconst listeners = this.listeners.get(eventName);\n\t\tif (listeners?.delete(listener) === true && listeners.size === 0) {\n\t\t\tthis.listeners.delete(eventName);\n\t\t\tthis.noListeners?.(eventName);\n\t\t}\n\t}\n\n\tpublic hasListeners(eventName?: keyof TListeners): boolean {\n\t\tif (eventName === undefined) {\n\t\t\treturn this.listeners.size > 0;\n\t\t}\n\t\treturn this.listeners.has(eventName);\n\t}\n}\n\n/**\n * This class exposes the constructor and the `emit` method of `EventEmitter`, elevating them from protected to public\n * @internal\n */\nclass ComposableEventEmitter<TListeners extends Listeners<TListeners>>\n\textends CustomEventEmitter<TListeners>\n\timplements IEmitter<TListeners>\n{\n\tpublic constructor(noListeners?: NoListenersCallback<TListeners>) {\n\t\tsuper(noListeners);\n\t}\n\n\tpublic override emit<K extends keyof TListeners>(\n\t\teventName: K,\n\t\t...args: Parameters<TListeners[K]>\n\t): void {\n\t\treturn super.emit(eventName, ...args);\n\t}\n\n\tpublic override emitAndCollect<K extends keyof TListeners>(\n\t\teventName: K,\n\t\t...args: Parameters<TListeners[K]>\n\t): ReturnType<TListeners[K]>[] {\n\t\treturn super.emitAndCollect(eventName, ...args);\n\t}\n}\n\n/**\n * Create a {@link @fluidframework/core-interfaces#Listenable} that can be instructed to emit events via the {@link @fluidframework/core-interfaces#IEmitter} interface.\n *\n * A class can delegate handling {@link @fluidframework/core-interfaces#Listenable} to the returned value while using it to emit the events.\n * See also CustomEventEmitter which be used as a base class to implement {@link @fluidframework/core-interfaces#Listenable} via extension.\n * @example Forwarding events to the emitter\n * ```typescript\n * interface MyEvents {\n * \tloaded(): void;\n * }\n *\n * class MyClass implements Listenable<MyEvents> {\n * \tprivate readonly events = createEmitter<MyEvents>();\n *\n * \tprivate load(): void {\n * \t\tthis.events.emit(\"loaded\");\n * \t}\n *\n * \tpublic on<K extends keyof MyEvents>(eventName: K, listener: MyEvents[K]): Off {\n * \t\treturn this.events.on(eventName, listener);\n * \t}\n *\n * \tpublic off<K extends keyof MyEvents>(eventName: K, listener: MyEvents[K]): void {\n * \t\treturn this.events.off(eventName, listener);\n * \t}\n * }\n * ```\n * @internal\n */\nexport function createEmitter<TListeners extends object>(\n\tnoListeners?: NoListenersCallback<TListeners>,\n): Listenable<TListeners> & IEmitter<TListeners> & HasListeners<TListeners> {\n\treturn new ComposableEventEmitter<TListeners>(noListeners);\n}\n"]}
@@ -0,0 +1,6 @@
1
+ /*!
2
+ * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
+ * Licensed under the MIT License.
4
+ */
5
+ export { createEmitter, CustomEventEmitter } from "./emitter.js";
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/events/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,aAAa,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC"}
@@ -0,0 +1,6 @@
1
+ /*!
2
+ * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
+ * Licensed under the MIT License.
4
+ */
5
+ export { createEmitter, CustomEventEmitter } from "./emitter.js";
6
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/events/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,aAAa,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\nexport { createEmitter, CustomEventEmitter } from \"./emitter.js\";\n"]}
@@ -11,4 +11,5 @@ export { EventEmitter } from "./eventEmitter.cjs";
11
11
  export { type IsomorphicPerformance } from "./performanceIsomorphic.js";
12
12
  export { type ITraceEvent, Trace } from "./trace.js";
13
13
  export { type EventEmitterEventType, TypedEventEmitter, type TypedEventTransform, } from "./typedEventEmitter.js";
14
+ export { createEmitter } from "./events/index.js";
14
15
  //# sourceMappingURL=indexBrowser.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"indexBrowser.d.ts","sourceRoot":"","sources":["../src/indexBrowser.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,OAAO,EACN,cAAc,EACd,aAAa,EACb,SAAS,EACT,cAAc,EACd,kBAAkB,GAClB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AAEzD,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,EAAE,MAAM,4BAA4B,CAAC;AACxF,OAAO,EAAE,uBAAuB,EAAE,MAAM,mBAAmB,CAAC;AAC5D,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,KAAK,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AACxE,OAAO,EAAE,KAAK,WAAW,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACrD,OAAO,EACN,KAAK,qBAAqB,EAC1B,iBAAiB,EACjB,KAAK,mBAAmB,GACxB,MAAM,wBAAwB,CAAC"}
1
+ {"version":3,"file":"indexBrowser.d.ts","sourceRoot":"","sources":["../src/indexBrowser.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,OAAO,EACN,cAAc,EACd,aAAa,EACb,SAAS,EACT,cAAc,EACd,kBAAkB,GAClB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AAEzD,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,EAAE,MAAM,4BAA4B,CAAC;AACxF,OAAO,EAAE,uBAAuB,EAAE,MAAM,mBAAmB,CAAC;AAC5D,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,KAAK,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AACxE,OAAO,EAAE,KAAK,WAAW,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACrD,OAAO,EACN,KAAK,qBAAqB,EAC1B,iBAAiB,EACjB,KAAK,mBAAmB,GACxB,MAAM,wBAAwB,CAAC;AAEhC,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC"}
@@ -12,4 +12,5 @@ export { Uint8ArrayToArrayBuffer } from "./bufferShared.js";
12
12
  export { EventEmitter } from "./eventEmitter.cjs";
13
13
  export { Trace } from "./trace.js";
14
14
  export { TypedEventEmitter, } from "./typedEventEmitter.js";
15
+ export { createEmitter } from "./events/index.js";
15
16
  //# sourceMappingURL=indexBrowser.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"indexBrowser.js","sourceRoot":"","sources":["../src/indexBrowser.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,uDAAuD;AACvD,4DAA4D;AAE5D,OAAO,EACN,cAAc,EACd,aAAa,EACb,SAAS,EACT,cAAc,EACd,kBAAkB,GAClB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AAEzD,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,EAAE,MAAM,4BAA4B,CAAC;AACxF,OAAO,EAAE,uBAAuB,EAAE,MAAM,mBAAmB,CAAC;AAC5D,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAElD,OAAO,EAAoB,KAAK,EAAE,MAAM,YAAY,CAAC;AACrD,OAAO,EAEN,iBAAiB,GAEjB,MAAM,wBAAwB,CAAC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\n// Entrypoint for browser-specific code in the package.\n// (See 'Isomorphic Code' section in the package README.md.)\n\nexport {\n\tbufferToString,\n\tisArrayBuffer,\n\tIsoBuffer,\n\tstringToBuffer,\n\tUint8ArrayToString,\n} from \"./bufferBrowser.js\";\nexport { gitHashFile, hashFile } from \"./hashFileBrowser.js\";\nexport { performance } from \"./performanceIsomorphic.js\";\n\nexport { fromBase64ToUtf8, fromUtf8ToBase64, toUtf8 } from \"./base64EncodingBrowser.js\";\nexport { Uint8ArrayToArrayBuffer } from \"./bufferShared.js\";\nexport { EventEmitter } from \"./eventEmitter.cjs\";\nexport { type IsomorphicPerformance } from \"./performanceIsomorphic.js\";\nexport { type ITraceEvent, Trace } from \"./trace.js\";\nexport {\n\ttype EventEmitterEventType,\n\tTypedEventEmitter,\n\ttype TypedEventTransform,\n} from \"./typedEventEmitter.js\";\n"]}
1
+ {"version":3,"file":"indexBrowser.js","sourceRoot":"","sources":["../src/indexBrowser.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,uDAAuD;AACvD,4DAA4D;AAE5D,OAAO,EACN,cAAc,EACd,aAAa,EACb,SAAS,EACT,cAAc,EACd,kBAAkB,GAClB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AAEzD,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,EAAE,MAAM,4BAA4B,CAAC;AACxF,OAAO,EAAE,uBAAuB,EAAE,MAAM,mBAAmB,CAAC;AAC5D,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAElD,OAAO,EAAoB,KAAK,EAAE,MAAM,YAAY,CAAC;AACrD,OAAO,EAEN,iBAAiB,GAEjB,MAAM,wBAAwB,CAAC;AAEhC,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\n// Entrypoint for browser-specific code in the package.\n// (See 'Isomorphic Code' section in the package README.md.)\n\nexport {\n\tbufferToString,\n\tisArrayBuffer,\n\tIsoBuffer,\n\tstringToBuffer,\n\tUint8ArrayToString,\n} from \"./bufferBrowser.js\";\nexport { gitHashFile, hashFile } from \"./hashFileBrowser.js\";\nexport { performance } from \"./performanceIsomorphic.js\";\n\nexport { fromBase64ToUtf8, fromUtf8ToBase64, toUtf8 } from \"./base64EncodingBrowser.js\";\nexport { Uint8ArrayToArrayBuffer } from \"./bufferShared.js\";\nexport { EventEmitter } from \"./eventEmitter.cjs\";\nexport { type IsomorphicPerformance } from \"./performanceIsomorphic.js\";\nexport { type ITraceEvent, Trace } from \"./trace.js\";\nexport {\n\ttype EventEmitterEventType,\n\tTypedEventEmitter,\n\ttype TypedEventTransform,\n} from \"./typedEventEmitter.js\";\n\nexport { createEmitter } from \"./events/index.js\";\n"]}
@@ -12,4 +12,5 @@ export { EventEmitter } from "./eventEmitter.cjs";
12
12
  export type { IsomorphicPerformance } from "./performanceIsomorphic.js";
13
13
  export { type ITraceEvent, Trace } from "./trace.js";
14
14
  export { type EventEmitterEventType, TypedEventEmitter, type TypedEventTransform, } from "./typedEventEmitter.js";
15
+ export { createEmitter } from "./events/index.js";
15
16
  //# sourceMappingURL=indexNode.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"indexNode.d.ts","sourceRoot":"","sources":["../src/indexNode.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,OAAO,EAAE,KAAK,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAC9C,OAAO,EACN,cAAc,EACd,SAAS,EACT,cAAc,EACd,kBAAkB,GAClB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAC1D,OAAO,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AAEzD,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AACrF,OAAO,EAAE,uBAAuB,EAAE,MAAM,mBAAmB,CAAC;AAC5D,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,YAAY,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AACxE,OAAO,EAAE,KAAK,WAAW,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACrD,OAAO,EACN,KAAK,qBAAqB,EAC1B,iBAAiB,EACjB,KAAK,mBAAmB,GACxB,MAAM,wBAAwB,CAAC"}
1
+ {"version":3,"file":"indexNode.d.ts","sourceRoot":"","sources":["../src/indexNode.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,OAAO,EAAE,KAAK,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAC9C,OAAO,EACN,cAAc,EACd,SAAS,EACT,cAAc,EACd,kBAAkB,GAClB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAC1D,OAAO,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AAEzD,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AACrF,OAAO,EAAE,uBAAuB,EAAE,MAAM,mBAAmB,CAAC;AAC5D,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,YAAY,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AACxE,OAAO,EAAE,KAAK,WAAW,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACrD,OAAO,EACN,KAAK,qBAAqB,EAC1B,iBAAiB,EACjB,KAAK,mBAAmB,GACxB,MAAM,wBAAwB,CAAC;AAEhC,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC"}
package/lib/indexNode.js CHANGED
@@ -10,4 +10,5 @@ export { Uint8ArrayToArrayBuffer } from "./bufferShared.js";
10
10
  export { EventEmitter } from "./eventEmitter.cjs";
11
11
  export { Trace } from "./trace.js";
12
12
  export { TypedEventEmitter, } from "./typedEventEmitter.js";
13
+ export { createEmitter } from "./events/index.js";
13
14
  //# sourceMappingURL=indexNode.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"indexNode.js","sourceRoot":"","sources":["../src/indexNode.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAMH,OAAO,EACN,cAAc,EACd,SAAS,EACT,cAAc,EACd,kBAAkB,GAClB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAC1D,OAAO,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AAEzD,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AACrF,OAAO,EAAE,uBAAuB,EAAE,MAAM,mBAAmB,CAAC;AAC5D,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAElD,OAAO,EAAoB,KAAK,EAAE,MAAM,YAAY,CAAC;AACrD,OAAO,EAEN,iBAAiB,GAEjB,MAAM,wBAAwB,CAAC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\n// Entrypoint for Node.js-specific code in the package.\n// (See 'Isomorphic Code' section in the package README.md.)\n\nexport { type Buffer } from \"./bufferNode.js\";\nexport {\n\tbufferToString,\n\tIsoBuffer,\n\tstringToBuffer,\n\tUint8ArrayToString,\n} from \"./bufferNode.js\";\nexport { gitHashFile, hashFile } from \"./hashFileNode.js\";\nexport { performance } from \"./performanceIsomorphic.js\";\n\nexport { fromBase64ToUtf8, fromUtf8ToBase64, toUtf8 } from \"./base64EncodingNode.js\";\nexport { Uint8ArrayToArrayBuffer } from \"./bufferShared.js\";\nexport { EventEmitter } from \"./eventEmitter.cjs\";\nexport type { IsomorphicPerformance } from \"./performanceIsomorphic.js\";\nexport { type ITraceEvent, Trace } from \"./trace.js\";\nexport {\n\ttype EventEmitterEventType,\n\tTypedEventEmitter,\n\ttype TypedEventTransform,\n} from \"./typedEventEmitter.js\";\n"]}
1
+ {"version":3,"file":"indexNode.js","sourceRoot":"","sources":["../src/indexNode.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAMH,OAAO,EACN,cAAc,EACd,SAAS,EACT,cAAc,EACd,kBAAkB,GAClB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAC1D,OAAO,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AAEzD,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AACrF,OAAO,EAAE,uBAAuB,EAAE,MAAM,mBAAmB,CAAC;AAC5D,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAElD,OAAO,EAAoB,KAAK,EAAE,MAAM,YAAY,CAAC;AACrD,OAAO,EAEN,iBAAiB,GAEjB,MAAM,wBAAwB,CAAC;AAEhC,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC","sourcesContent":["/*!\n * Copyright (c) Microsoft Corporation and contributors. All rights reserved.\n * Licensed under the MIT License.\n */\n\n// Entrypoint for Node.js-specific code in the package.\n// (See 'Isomorphic Code' section in the package README.md.)\n\nexport { type Buffer } from \"./bufferNode.js\";\nexport {\n\tbufferToString,\n\tIsoBuffer,\n\tstringToBuffer,\n\tUint8ArrayToString,\n} from \"./bufferNode.js\";\nexport { gitHashFile, hashFile } from \"./hashFileNode.js\";\nexport { performance } from \"./performanceIsomorphic.js\";\n\nexport { fromBase64ToUtf8, fromUtf8ToBase64, toUtf8 } from \"./base64EncodingNode.js\";\nexport { Uint8ArrayToArrayBuffer } from \"./bufferShared.js\";\nexport { EventEmitter } from \"./eventEmitter.cjs\";\nexport type { IsomorphicPerformance } from \"./performanceIsomorphic.js\";\nexport { type ITraceEvent, Trace } from \"./trace.js\";\nexport {\n\ttype EventEmitterEventType,\n\tTypedEventEmitter,\n\ttype TypedEventTransform,\n} from \"./typedEventEmitter.js\";\n\nexport { createEmitter } from \"./events/index.js\";\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fluid-internal/client-utils",
3
- "version": "2.10.0",
3
+ "version": "2.12.0",
4
4
  "description": "Not intended for use outside the Fluid Framework.",
5
5
  "homepage": "https://fluidframework.com",
6
6
  "repository": {
@@ -69,8 +69,8 @@
69
69
  "temp-directory": "nyc/.nyc_output"
70
70
  },
71
71
  "dependencies": {
72
- "@fluidframework/core-interfaces": "~2.10.0",
73
- "@fluidframework/core-utils": "~2.10.0",
72
+ "@fluidframework/core-interfaces": "~2.12.0",
73
+ "@fluidframework/core-utils": "~2.12.0",
74
74
  "@types/events_pkg": "npm:@types/events@^3.0.0",
75
75
  "base64-js": "^1.5.1",
76
76
  "buffer": "^6.0.3",
@@ -78,18 +78,18 @@
78
78
  "sha.js": "^2.4.11"
79
79
  },
80
80
  "devDependencies": {
81
- "@arethetypeswrong/cli": "^0.16.4",
81
+ "@arethetypeswrong/cli": "^0.17.1",
82
82
  "@biomejs/biome": "~1.9.3",
83
- "@fluid-internal/client-utils-previous": "npm:@fluid-internal/client-utils@2.5.0",
84
- "@fluid-internal/mocha-test-setup": "~2.10.0",
83
+ "@fluid-internal/client-utils-previous": "npm:@fluid-internal/client-utils@2.11.0",
84
+ "@fluid-internal/mocha-test-setup": "~2.12.0",
85
85
  "@fluid-tools/build-cli": "^0.51.0",
86
86
  "@fluidframework/build-common": "^2.0.3",
87
87
  "@fluidframework/build-tools": "^0.51.0",
88
- "@fluidframework/eslint-config-fluid": "^5.4.0",
88
+ "@fluidframework/eslint-config-fluid": "^5.6.0",
89
89
  "@microsoft/api-extractor": "7.47.8",
90
90
  "@types/base64-js": "^1.3.0",
91
91
  "@types/jest": "29.5.3",
92
- "@types/jest-environment-puppeteer": "~2.10.0",
92
+ "@types/jest-environment-puppeteer": "~2.12.0",
93
93
  "@types/mocha": "^9.1.1",
94
94
  "@types/node": "^18.19.0",
95
95
  "@types/rewire": "^2.5.28",
@@ -0,0 +1,3 @@
1
+ # events
2
+
3
+ This module contains interfaces and helpers for objects that emit and subscribe to events.
@@ -0,0 +1,228 @@
1
+ /*!
2
+ * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
+ * Licensed under the MIT License.
4
+ */
5
+
6
+ import type {
7
+ HasListeners,
8
+ IEmitter,
9
+ Listenable,
10
+ Listeners,
11
+ MapGetSet,
12
+ NoListenersCallback,
13
+ Off,
14
+ } from "@fluidframework/core-interfaces/internal";
15
+
16
+ /**
17
+ * Retrieve a value from a map with the given key, or create a new entry if the key is not in the map.
18
+ * @param map - The map to query/update
19
+ * @param key - The key to lookup in the map
20
+ * @param defaultValue - a function which returns a default value. This is called and used to set an initial value for the given key in the map if none exists
21
+ * @returns either the existing value for the given key, or the newly-created value (the result of `defaultValue`)
22
+ * @internal
23
+ */
24
+ function getOrCreate<K, V>(map: MapGetSet<K, V>, key: K, defaultValue: (key: K) => V): V {
25
+ let value = map.get(key);
26
+ if (value === undefined) {
27
+ value = defaultValue(key);
28
+ map.set(key, value);
29
+ }
30
+ return value;
31
+ }
32
+
33
+ /**
34
+ * Provides an API for subscribing to and listening to events.
35
+ *
36
+ * @remarks Classes wishing to emit events may either extend this class, compose over it, or expose it as a property of type {@link @fluidframework/core-interfaces#Listenable}.
37
+ *
38
+ * @example Extending this class
39
+ *
40
+ * ```typescript
41
+ * interface MyEvents {
42
+ * loaded: () => void;
43
+ * computed: () => number;
44
+ * }
45
+ *
46
+ * class MyInheritanceClass extends EventEmitter<MyEvents> {
47
+ * private load() {
48
+ * this.emit("loaded");
49
+ * const results: number[] = this.emitAndCollect("computed");
50
+ * }
51
+ * }
52
+ * ```
53
+ *
54
+ * @example Composing over this class
55
+ *
56
+ * ```typescript
57
+ * class MyCompositionClass implements Listenable<MyEvents> {
58
+ * private readonly events = createEmitter<MyEvents>();
59
+ *
60
+ * private load() {
61
+ * this.events.emit("loaded");
62
+ * const results: number[] = this.events.emitAndCollect("computed");
63
+ * }
64
+ *
65
+ * public on<K extends keyof MyEvents>(eventName: K, listener: MyEvents[K]): () => void {
66
+ * return this.events.on(eventName, listener);
67
+ * }
68
+ * }
69
+ * ```
70
+ *
71
+ * @example Exposing this class as a property
72
+ *
73
+ * ```typescript
74
+ * class MyExposingClass {
75
+ * private readonly _events = createEmitter<MyEvents>();
76
+ * public readonly events: Listenable<MyEvents> = this._events;
77
+ *
78
+ * private load() {
79
+ * this._events.emit("loaded");
80
+ * const results: number[] = this._events.emitAndCollect("computed");
81
+ * }
82
+ * }
83
+ * ```
84
+ * @internal
85
+ */
86
+ export class CustomEventEmitter<TListeners extends Listeners<TListeners>>
87
+ implements Listenable<TListeners>, HasListeners<TListeners>
88
+ {
89
+ protected readonly listeners = new Map<
90
+ keyof TListeners,
91
+ Set<(...args: any[]) => TListeners[keyof TListeners]>
92
+ >();
93
+
94
+ // Because this is protected and not public, calling this externally (not from a subclass) makes sending events to the constructed instance impossible.
95
+ // Instead, use the static `create` function to get an instance which allows emitting events.
96
+ protected constructor(private readonly noListeners?: NoListenersCallback<TListeners>) {}
97
+
98
+ protected emit<K extends keyof TListeners>(
99
+ eventName: K,
100
+ ...args: Parameters<TListeners[K]>
101
+ ): void {
102
+ const listeners = this.listeners.get(eventName);
103
+ if (listeners !== undefined) {
104
+ // Current tsc (5.4.5) cannot spread `args` into `listener()`.
105
+ const argArray: unknown[] = args;
106
+ // This explicitly copies listeners so that new listeners added during this call to emit will not receive this event.
107
+ for (const listener of [...listeners]) {
108
+ // If listener has been unsubscribed while invoking other listeners, skip it.
109
+ if (listeners.has(listener)) {
110
+ listener(...argArray);
111
+ }
112
+ }
113
+ }
114
+ }
115
+
116
+ protected emitAndCollect<K extends keyof TListeners>(
117
+ eventName: K,
118
+ ...args: Parameters<TListeners[K]>
119
+ ): ReturnType<TListeners[K]>[] {
120
+ const listeners = this.listeners.get(eventName);
121
+ if (listeners !== undefined) {
122
+ const argArray: unknown[] = args;
123
+ const resultArray: ReturnType<TListeners[K]>[] = [];
124
+ for (const listener of [...listeners.values()]) {
125
+ resultArray.push(listener(...argArray));
126
+ }
127
+ return resultArray;
128
+ }
129
+ return [];
130
+ }
131
+
132
+ public on<K extends keyof Listeners<TListeners>>(
133
+ eventName: K,
134
+ listener: TListeners[K],
135
+ ): Off {
136
+ const listeners = getOrCreate(this.listeners, eventName, () => new Set());
137
+ if (listeners.has(listener)) {
138
+ const eventDescription =
139
+ typeof eventName === "symbol" ? eventName.description : String(eventName.toString());
140
+
141
+ throw new Error(
142
+ `Attempted to register the same listener object twice for event ${eventDescription}`,
143
+ );
144
+ }
145
+ listeners.add(listener);
146
+ return () => this.off(eventName, listener);
147
+ }
148
+
149
+ public off<K extends keyof Listeners<TListeners>>(
150
+ eventName: K,
151
+ listener: TListeners[K],
152
+ ): void {
153
+ const listeners = this.listeners.get(eventName);
154
+ if (listeners?.delete(listener) === true && listeners.size === 0) {
155
+ this.listeners.delete(eventName);
156
+ this.noListeners?.(eventName);
157
+ }
158
+ }
159
+
160
+ public hasListeners(eventName?: keyof TListeners): boolean {
161
+ if (eventName === undefined) {
162
+ return this.listeners.size > 0;
163
+ }
164
+ return this.listeners.has(eventName);
165
+ }
166
+ }
167
+
168
+ /**
169
+ * This class exposes the constructor and the `emit` method of `EventEmitter`, elevating them from protected to public
170
+ * @internal
171
+ */
172
+ class ComposableEventEmitter<TListeners extends Listeners<TListeners>>
173
+ extends CustomEventEmitter<TListeners>
174
+ implements IEmitter<TListeners>
175
+ {
176
+ public constructor(noListeners?: NoListenersCallback<TListeners>) {
177
+ super(noListeners);
178
+ }
179
+
180
+ public override emit<K extends keyof TListeners>(
181
+ eventName: K,
182
+ ...args: Parameters<TListeners[K]>
183
+ ): void {
184
+ return super.emit(eventName, ...args);
185
+ }
186
+
187
+ public override emitAndCollect<K extends keyof TListeners>(
188
+ eventName: K,
189
+ ...args: Parameters<TListeners[K]>
190
+ ): ReturnType<TListeners[K]>[] {
191
+ return super.emitAndCollect(eventName, ...args);
192
+ }
193
+ }
194
+
195
+ /**
196
+ * Create a {@link @fluidframework/core-interfaces#Listenable} that can be instructed to emit events via the {@link @fluidframework/core-interfaces#IEmitter} interface.
197
+ *
198
+ * A class can delegate handling {@link @fluidframework/core-interfaces#Listenable} to the returned value while using it to emit the events.
199
+ * See also CustomEventEmitter which be used as a base class to implement {@link @fluidframework/core-interfaces#Listenable} via extension.
200
+ * @example Forwarding events to the emitter
201
+ * ```typescript
202
+ * interface MyEvents {
203
+ * loaded(): void;
204
+ * }
205
+ *
206
+ * class MyClass implements Listenable<MyEvents> {
207
+ * private readonly events = createEmitter<MyEvents>();
208
+ *
209
+ * private load(): void {
210
+ * this.events.emit("loaded");
211
+ * }
212
+ *
213
+ * public on<K extends keyof MyEvents>(eventName: K, listener: MyEvents[K]): Off {
214
+ * return this.events.on(eventName, listener);
215
+ * }
216
+ *
217
+ * public off<K extends keyof MyEvents>(eventName: K, listener: MyEvents[K]): void {
218
+ * return this.events.off(eventName, listener);
219
+ * }
220
+ * }
221
+ * ```
222
+ * @internal
223
+ */
224
+ export function createEmitter<TListeners extends object>(
225
+ noListeners?: NoListenersCallback<TListeners>,
226
+ ): Listenable<TListeners> & IEmitter<TListeners> & HasListeners<TListeners> {
227
+ return new ComposableEventEmitter<TListeners>(noListeners);
228
+ }
@@ -0,0 +1,6 @@
1
+ /*!
2
+ * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
+ * Licensed under the MIT License.
4
+ */
5
+
6
+ export { createEmitter, CustomEventEmitter } from "./emitter.js";
@@ -26,3 +26,5 @@ export {
26
26
  TypedEventEmitter,
27
27
  type TypedEventTransform,
28
28
  } from "./typedEventEmitter.js";
29
+
30
+ export { createEmitter } from "./events/index.js";
package/src/indexNode.ts CHANGED
@@ -26,3 +26,5 @@ export {
26
26
  TypedEventEmitter,
27
27
  type TypedEventTransform,
28
28
  } from "./typedEventEmitter.js";
29
+
30
+ export { createEmitter } from "./events/index.js";