Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Move events library to core-interfaces and core-utils #23037

Closed
Closed
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
8824e23
wip
sonalideshpandemsft Nov 7, 2024
5c5c343
comment tests and build core-interface
sonalideshpandemsft Nov 7, 2024
3375d11
wip
sonalideshpandemsft Nov 8, 2024
9bf973a
fix lock file
sonalideshpandemsft Nov 8, 2024
d2e6bc1
wip
sonalideshpandemsft Nov 12, 2024
d6fa82e
Merge remote-tracking branch 'origin' into mv-events-library
sonalideshpandemsft Nov 12, 2024
c03bcb5
wip
sonalideshpandemsft Nov 12, 2024
02d5f2f
move events to core-utils
sonalideshpandemsft Nov 12, 2024
b15eb27
Delete interop.ts as it's unused
sonalideshpandemsft Nov 12, 2024
43fbee8
fix CI failure
sonalideshpandemsft Nov 12, 2024
b4ea361
mark eventemitter as internal
sonalideshpandemsft Nov 12, 2024
fa229a3
update imports in presence package
sonalideshpandemsft Nov 12, 2024
744cef7
fix eslint errors
sonalideshpandemsft Nov 12, 2024
512667e
Merge branch 'main' into mv-events-library
sonalideshpandemsft Nov 12, 2024
50a415f
nits
sonalideshpandemsft Nov 12, 2024
28d5f65
feedback wip
sonalideshpandemsft Nov 13, 2024
9259cc8
feedback wip
sonalideshpandemsft Nov 13, 2024
9abc1ab
wip
sonalideshpandemsft Nov 13, 2024
c49f3c7
Merge remote-tracking branch 'origin' into mv-events-library
sonalideshpandemsft Nov 13, 2024
b84034c
move tests
sonalideshpandemsft Nov 14, 2024
3ec50b1
split tests
sonalideshpandemsft Nov 14, 2024
9f560f9
move impl to client-utils
sonalideshpandemsft Nov 15, 2024
135412a
Merge remote-tracking branch 'origin' into mv-events-library
sonalideshpandemsft Nov 15, 2024
9d254e9
Merge remote-tracking branch 'origin' into mv-events-library
sonalideshpandemsft Nov 15, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion layerInfo.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
},
"Core-Utils": {
"packages": ["@fluidframework/core-utils"],
"deps": []
"deps": ["Core-Interfaces"]
},
"Client-Utils": {
"packages": ["@fluid-internal/client-utils"],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,9 @@ export interface IResponse {
value: any;
}

// @public
export type IsListener<TListener> = TListener extends (...args: any[]) => void ? true : false;

// @public
export interface ITelemetryBaseEvent extends ITelemetryBaseProperties {
// (undocumented)
Expand All @@ -305,6 +308,17 @@ export interface ITelemetryBaseProperties {
[index: string]: TelemetryBaseEventPropertyType | Tagged<TelemetryBaseEventPropertyType>;
}

// @public @sealed
export interface Listenable<TListeners extends object> {
off<K extends keyof Listeners<TListeners>>(eventName: K, listener: TListeners[K]): void;
on<K extends keyof Listeners<TListeners>>(eventName: K, listener: TListeners[K]): Off;
}

// @public
export type Listeners<T extends object> = {
[P in (string | symbol) & keyof T as IsListener<T[P]> extends true ? P : never]: T[P];
};

// @public
export const LogLevel: {
readonly verbose: 10;
Expand All @@ -315,6 +329,9 @@ export const LogLevel: {
// @public
export type LogLevel = (typeof LogLevel)[keyof typeof LogLevel];

// @public
export type Off = () => void;

// @public
export type ReplaceIEventThisPlaceHolder<L extends any[], TThis> = L extends any[] ? {
[K in keyof L]: L[K] extends IEventThisPlaceHolder ? TThis : L[K];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,9 @@ export interface IResponse {
value: any;
}

// @public
export type IsListener<TListener> = TListener extends (...args: any[]) => void ? true : false;

// @public
export interface ITelemetryBaseEvent extends ITelemetryBaseProperties {
// (undocumented)
Expand All @@ -361,6 +364,17 @@ export interface IThrottlingWarning extends IErrorBase {
readonly retryAfterSeconds: number;
}

// @public @sealed
export interface Listenable<TListeners extends object> {
off<K extends keyof Listeners<TListeners>>(eventName: K, listener: TListeners[K]): void;
on<K extends keyof Listeners<TListeners>>(eventName: K, listener: TListeners[K]): Off;
}

// @public
export type Listeners<T extends object> = {
[P in (string | symbol) & keyof T as IsListener<T[P]> extends true ? P : never]: T[P];
};

// @public
export const LogLevel: {
readonly verbose: 10;
Expand All @@ -371,6 +385,9 @@ export const LogLevel: {
// @public
export type LogLevel = (typeof LogLevel)[keyof typeof LogLevel];

// @public
export type Off = () => void;

// @public
export type ReplaceIEventThisPlaceHolder<L extends any[], TThis> = L extends any[] ? {
[K in keyof L]: L[K] extends IEventThisPlaceHolder ? TThis : L[K];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,9 @@ export interface IResponse {
value: any;
}

// @public
export type IsListener<TListener> = TListener extends (...args: any[]) => void ? true : false;

// @public
export interface ITelemetryBaseEvent extends ITelemetryBaseProperties {
// (undocumented)
Expand All @@ -305,6 +308,17 @@ export interface ITelemetryBaseProperties {
[index: string]: TelemetryBaseEventPropertyType | Tagged<TelemetryBaseEventPropertyType>;
}

// @public @sealed
export interface Listenable<TListeners extends object> {
off<K extends keyof Listeners<TListeners>>(eventName: K, listener: TListeners[K]): void;
on<K extends keyof Listeners<TListeners>>(eventName: K, listener: TListeners[K]): Off;
}

// @public
export type Listeners<T extends object> = {
[P in (string | symbol) & keyof T as IsListener<T[P]> extends true ? P : never]: T[P];
};

// @public
export const LogLevel: {
readonly verbose: 10;
Expand All @@ -315,6 +329,9 @@ export const LogLevel: {
// @public
export type LogLevel = (typeof LogLevel)[keyof typeof LogLevel];

// @public
export type Off = () => void;

// @public
export type ReplaceIEventThisPlaceHolder<L extends any[], TThis> = L extends any[] ? {
[K in keyof L]: L[K] extends IEventThisPlaceHolder ? TThis : L[K];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,9 @@ export interface IResponse {
value: any;
}

// @public
export type IsListener<TListener> = TListener extends (...args: any[]) => void ? true : false;

// @public
export interface ITelemetryBaseEvent extends ITelemetryBaseProperties {
// (undocumented)
Expand All @@ -305,6 +308,17 @@ export interface ITelemetryBaseProperties {
[index: string]: TelemetryBaseEventPropertyType | Tagged<TelemetryBaseEventPropertyType>;
}

// @public @sealed
export interface Listenable<TListeners extends object> {
off<K extends keyof Listeners<TListeners>>(eventName: K, listener: TListeners[K]): void;
on<K extends keyof Listeners<TListeners>>(eventName: K, listener: TListeners[K]): Off;
}

// @public
export type Listeners<T extends object> = {
[P in (string | symbol) & keyof T as IsListener<T[P]> extends true ? P : never]: T[P];
};

// @public
export const LogLevel: {
readonly verbose: 10;
Expand All @@ -315,6 +329,9 @@ export const LogLevel: {
// @public
export type LogLevel = (typeof LogLevel)[keyof typeof LogLevel];

// @public
export type Off = () => void;

// @public
export type ReplaceIEventThisPlaceHolder<L extends any[], TThis> = L extends any[] ? {
[K in keyof L]: L[K] extends IEventThisPlaceHolder ? TThis : L[K];
Expand Down
82 changes: 82 additions & 0 deletions packages/common/core-interfaces/src/events/emitter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
/*!
* Copyright (c) Microsoft Corporation and contributors. All rights reserved.
* Licensed under the MIT License.
*/

import type { Listeners } from "./listeners.js";

/**
* Interface for an event emitter that can emit typed events to subscribed listeners.
* @internal
*/
export interface IEmitter<TListeners extends Listeners<TListeners>> {
/**
* Emits an event with the specified name and arguments, notifying all subscribers by calling their registered listener functions.
* @param eventName - the name of the event to fire
* @param args - the arguments passed to the event listener functions
*/
emit<K extends keyof Listeners<TListeners>>(
eventName: K,
...args: Parameters<TListeners[K]>
): void;

/**
* Emits an event with the specified name and arguments, notifying all subscribers by calling their registered listener functions.
* It also collects the return values of all listeners into an array.
*
* Warning: This method should be used with caution. It deviates from the standard event-based integration pattern as creates substantial coupling between the emitter and its listeners.
* For the majority of use-cases it is recommended to use the standard {@link IEmitter.emit} functionality.
* @param eventName - the name of the event to fire
* @param args - the arguments passed to the event listener functions
* @returns An array of the return values of each listener, preserving the order listeners were called.
*/
emitAndCollect<K extends keyof Listeners<TListeners>>(
eventName: K,
...args: Parameters<TListeners[K]>
): ReturnType<TListeners[K]>[];
}

/**
* Called when the last listener for `eventName` is removed.
* Useful for determining when to clean up resources related to detecting when the event might occurs.
* @internal
*/
export type NoListenersCallback<TListeners extends object> = (
eventName: keyof Listeners<TListeners>,
) => void;

/**
* Allows querying if an object has listeners.
* @sealed
* @internal
*/
export interface HasListeners<TListeners extends Listeners<TListeners>> {
/**
* When no `eventName` is provided, returns true iff there are any listeners.
*
* When `eventName` is provided, returns true iff there are listeners for that event.
*
* @remarks
* This can be used to know when its safe to cleanup data-structures which only exist to fire events for their listeners.
*/
hasListeners(eventName?: keyof Listeners<TListeners>): boolean;
}

/**
* Subset of Map interface.
* @internal
*/
export interface MapGetSet<K, V> {
get(key: K): V | undefined;
set(key: K, value: V): void;
}

/**
* A dictionary whose values are keyed off of two objects (key1, key2).
* As it is a nested map, size() will return the number of distinct key1s.
* If you need constant-time access to the number of values, use SizedNestedMap instead.
*
* This code assumes values will not be undefined (keys can be undefined).
* @internal
*/
export type NestedMap<Key1, Key2, Value> = Map<Key1, Map<Key2, Value>>;
19 changes: 19 additions & 0 deletions packages/common/core-interfaces/src/events/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/*!
* Copyright (c) Microsoft Corporation and contributors. All rights reserved.
* Licensed under the MIT License.
*/

export type {
IEmitter,
NoListenersCallback,
HasListeners,
MapGetSet,
NestedMap,
} from "./emitter.js";

export type {
Listeners,
Listenable,
Off,
IsListener,
} from "./listeners.js";
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
*/

/**
* `true` iff the given type is an acceptable shape for a {@link Listeners | event} listener
* `true` if the given type is an acceptable shape for a {@link Listeners | event} listener
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

iff might have been a deliberate abbreviation of "if and only if" but I think in this context it doesn't make a difference.

* @public
*/
export type IsListener<TListener> = TListener extends (...args: any[]) => void ? true : false;
Expand Down Expand Up @@ -44,7 +44,8 @@ export type Listeners<T extends object> = {
* ```
* {@link createEmitter} can help implement this interface via delegation.
*
* @sealed @public
* @sealed
* @public
*/
export interface Listenable<TListeners extends object> {
/**
Expand Down
11 changes: 11 additions & 0 deletions packages/common/core-interfaces/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,14 @@ export type { FluidObjectProviderKeys, FluidObject, FluidObjectKeys } from "./pr
export type { ConfigTypes, IConfigProviderBase } from "./config.js";
export type { ISignalEnvelope } from "./messages.js";
export type { ErasedType } from "./erasedType.js";
export type {
IEmitter,
NoListenersCallback,
HasListeners,
Listeners,
Listenable,
Off,
IsListener,
MapGetSet,
NestedMap,
} from "./events/index.js";
3 changes: 3 additions & 0 deletions packages/common/core-utils/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,9 @@
],
"temp-directory": "nyc/.nyc_output"
},
"dependencies": {
"@fluidframework/core-interfaces": "workspace:~"
},
"devDependencies": {
"@arethetypeswrong/cli": "^0.16.4",
"@biomejs/biome": "~1.9.3",
Expand Down
Loading
Loading