@adonisjs/events 8.1.4-0 → 8.2.0-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/README.md CHANGED
@@ -5,7 +5,7 @@
5
5
  [![gh-workflow-image]][gh-workflow-url] [![npm-image]][npm-url] ![][typescript-image] [![license-image]][license-url] [![synk-image]][synk-url]
6
6
 
7
7
  ## Introduction
8
- AdonisJS events is an implementation of the event emitter built on top of [emittery](https://github.com/sindresorhus/emittery). Alongside defining event listeners as inline callbacks, you can also bind modules to events and register event subscribers.
8
+ AdonisJS events is an implementation of the event emitter built on top of [emittery](https://github.com/sindresorhus/emittery). Alongside defining event listeners as inline callbacks, you can also bind modules to events and define events as classes.
9
9
 
10
10
  ## Official Documentation
11
11
  The documentation is available on the [AdonisJS website](https://docs.adonisjs.com/guides/events)
@@ -21,7 +21,7 @@ In order to ensure that the AdonisJS community is welcoming to all, please revie
21
21
  ## License
22
22
  AdonisJS events is open-sourced software licensed under the [MIT license](LICENSE.md).
23
23
 
24
- [gh-workflow-image]: https://img.shields.io/github/workflow/status/adonisjs/events/test?style=for-the-badge
24
+ [gh-workflow-image]: https://img.shields.io/github/actions/workflow/status/adonisjs/events/test.yml?style=for-the-badge
25
25
  [gh-workflow-url]: https://github.com/adonisjs/events/actions/workflows/test.yml "Github action"
26
26
 
27
27
  [typescript-image]: https://img.shields.io/badge/Typescript-294E80.svg?style=for-the-badge&logo=typescript
@@ -1,21 +1,26 @@
1
- import { type Application } from '@adonisjs/application';
2
- import type { EventsListItem } from './types.js';
1
+ import type { Application } from '@adonisjs/application';
2
+ import { type UnsubscribeFunction } from 'emittery';
3
3
  import { EventsBuffer } from './events_buffer.js';
4
+ import type { Listener, Constructor, ListenerMethod, AllowedEventTypes } from './types.js';
4
5
  export declare class Emitter<EventsList extends Record<string | symbol | number, any>> {
5
6
  #private;
7
+ get eventsListeners(): Map<AllowedEventTypes, Map<Listener<any, Constructor<any>>, ListenerMethod<any>>>;
6
8
  constructor(app: Application<any, any>);
7
- onError(callback: (event: keyof EventsList, error: any, data: any) => void): this;
8
- on<Name extends keyof EventsList>(event: Name, handler: string | ((data: EventsList[Name]) => any | Promise<any>)): this;
9
- once<Name extends keyof EventsList>(event: Name, handler: string | ((data: EventsList[Name]) => any | Promise<any>)): this;
10
- onAny(handler: string | ((event: keyof EventsList, data: EventsListItem<EventsList>) => any | Promise<any>)): this;
9
+ onError(callback: (event: keyof EventsList | Constructor<any>, error: any, data: any) => void): this;
10
+ on<Event extends Constructor<any>, ListenerClass extends Constructor<any>>(event: Event, listener: Listener<InstanceType<Event>, ListenerClass>): UnsubscribeFunction;
11
+ on<Name extends keyof EventsList, ListenerClass extends Constructor<any>>(event: Name, listener: Listener<EventsList[Name], ListenerClass>): UnsubscribeFunction;
12
+ once<Event extends Constructor<any>, ListenerClass extends Constructor<any>>(event: Event, listener: Listener<InstanceType<Event>, ListenerClass>): void;
13
+ once<Name extends keyof EventsList, ListenerClass extends Constructor<any>>(event: Name, listener: Listener<EventsList[Name], ListenerClass>): void;
14
+ onAny(listener: (event: AllowedEventTypes, data: any) => any | Promise<any>): UnsubscribeFunction;
15
+ emit<Event extends Constructor<any>>(event: Event, data: InstanceType<Event>): Promise<void>;
11
16
  emit<Name extends keyof EventsList>(event: Name, data: EventsList[Name]): Promise<void>;
12
- off<Name extends keyof EventsList>(event: Name, handler: string | ((data: EventsList[Name]) => any | Promise<any>)): this;
13
- offAny(handler: string | ((event: keyof EventsList, data: EventsListItem<EventsList>) => any | Promise<any>)): this;
14
- clearListener<Name extends keyof EventsList>(event: Name, handler: string | ((data: EventsList[Name]) => any | Promise<any>)): this;
15
- clearListeners(event: keyof EventsList): void;
17
+ off(event: keyof EventsList | Constructor<any>, listener: Listener<any, Constructor<any>>): void;
18
+ offAny(listener: (event: keyof EventsList | Constructor<any>, data: any) => any | Promise<any>): this;
19
+ clearListener(event: keyof EventsList | Constructor<any>, listener: Listener<any, Constructor<any>>): void;
20
+ clearListeners(event: keyof EventsList | Constructor<any>): void;
16
21
  clearAllListeners(): void;
17
- listenerCount<Name extends keyof EventsList>(event?: Name): number;
18
- hasListeners<Name extends keyof EventsList>(event?: Name): boolean;
19
- fake(events?: (keyof EventsList)[]): EventsBuffer<EventsList>;
22
+ listenerCount(event?: keyof EventsList | Constructor<any>): number;
23
+ hasListeners(event?: keyof EventsList | Constructor<any>): boolean;
24
+ fake(events?: (keyof EventsList | Constructor<any>)[]): EventsBuffer<EventsList>;
20
25
  restore(): void;
21
26
  }
@@ -1,68 +1,98 @@
1
+ import is from '@adonisjs/application/helpers/is';
1
2
  import Emittery from 'emittery';
2
- import { moduleExpression } from '@adonisjs/fold';
3
+ import { moduleExpression, moduleCaller, moduleImporter } from '@adonisjs/fold';
3
4
  import debug from './debug.js';
4
5
  import { EventsBuffer } from './events_buffer.js';
5
6
  export class Emitter {
7
+ #eventsClassSymbols = new Map();
8
+ #eventsListeners = new Map();
6
9
  #transport = new Emittery();
7
10
  #eventsBuffer;
8
11
  #eventsToFake = new Set();
9
12
  #errorHandler;
10
- #moduleListeners = new Map();
11
13
  #app;
14
+ get eventsListeners() {
15
+ return this.#eventsListeners;
16
+ }
12
17
  constructor(app) {
13
18
  this.#app = app;
14
19
  }
15
- #createModuleListener(importExpression) {
16
- return moduleExpression(importExpression, this.#app.appRoot).toCallable(this.#app.container);
20
+ #getEventClassSymbol(event) {
21
+ if (!this.#eventsClassSymbols.has(event)) {
22
+ this.#eventsClassSymbols.set(event, Symbol(event.name));
23
+ }
24
+ return this.#eventsClassSymbols.get(event);
25
+ }
26
+ #resolveEvent(event) {
27
+ if (is.class_(event)) {
28
+ return this.#getEventClassSymbol(event);
29
+ }
30
+ return event;
17
31
  }
18
- #getSetModuleListener(importExpression) {
19
- if (!this.#moduleListeners.has(importExpression)) {
20
- this.#moduleListeners.set(importExpression, this.#createModuleListener(importExpression));
32
+ #getEventListeners(event) {
33
+ if (!this.#eventsListeners.has(event)) {
34
+ this.#eventsListeners.set(event, new Map());
21
35
  }
22
- return this.#moduleListeners.get(importExpression);
36
+ return this.#eventsListeners.get(event);
37
+ }
38
+ #normalizeEventListener(listener) {
39
+ if (typeof listener === 'string') {
40
+ return moduleExpression(listener, this.#app.appRoot).toCallable(this.#app.container);
41
+ }
42
+ if (Array.isArray(listener)) {
43
+ const listenerModule = listener[0];
44
+ const method = listener[1] || 'handle';
45
+ if (is.class_(listenerModule)) {
46
+ return moduleCaller(listenerModule, method).toCallable(this.#app.container);
47
+ }
48
+ return moduleImporter(listenerModule, method).toCallable(this.#app.container);
49
+ }
50
+ return listener;
51
+ }
52
+ #resolveEventListener(event, listener) {
53
+ const eventListeners = this.#getEventListeners(event);
54
+ if (!eventListeners.has(listener)) {
55
+ eventListeners.set(listener, this.#normalizeEventListener(listener));
56
+ }
57
+ return eventListeners.get(listener);
23
58
  }
24
59
  onError(callback) {
25
60
  this.#errorHandler = callback;
26
61
  return this;
27
62
  }
28
- on(event, handler) {
29
- if (typeof handler === 'string') {
30
- this.#transport.on(event, this.#getSetModuleListener(handler));
31
- return this;
63
+ on(event, listener) {
64
+ if (debug.enabled) {
65
+ debug('registering event listener, event: %O, listener: %O', event, listener);
32
66
  }
33
- this.#transport.on(event, handler);
34
- return this;
35
- }
36
- once(event, handler) {
37
- if (typeof handler === 'string') {
38
- const off = this.#transport.on(event, async (data) => {
39
- off();
40
- await this.#createModuleListener(handler)(data);
41
- });
42
- return this;
67
+ const normalizedEvent = this.#resolveEvent(event);
68
+ const normalizedListener = this.#resolveEventListener(event, listener);
69
+ this.#transport.on(normalizedEvent, normalizedListener);
70
+ return () => this.off(event, listener);
71
+ }
72
+ once(event, listener) {
73
+ if (debug.enabled) {
74
+ debug('registering one time event listener, event: %O, listener: %O', event, listener);
43
75
  }
44
- const off = this.#transport.on(event, async (data) => {
76
+ const normalizedEvent = this.#resolveEvent(event);
77
+ const normalizedListener = this.#normalizeEventListener(listener);
78
+ const off = this.#transport.on(normalizedEvent, async (data) => {
45
79
  off();
46
- await handler(data);
80
+ debug('removing one time event listener, event: %O', event);
81
+ await normalizedListener(data);
47
82
  });
48
- return this;
49
83
  }
50
- onAny(handler) {
51
- if (typeof handler === 'string') {
52
- this.#transport.onAny(this.#getSetModuleListener(handler));
53
- return this;
54
- }
55
- this.#transport.onAny(handler);
56
- return this;
84
+ onAny(listener) {
85
+ return this.#transport.onAny(listener);
57
86
  }
58
87
  async emit(event, data) {
59
88
  if (this.#eventsToFake.has(event) || this.#eventsToFake.has('*')) {
60
- debug('faking emit. event: "%s", data: %O', event, data);
61
- this.#eventsBuffer.events.push({ name: event, data });
89
+ debug('faking emit. event: %O, data: %O', event, data);
90
+ this.#eventsBuffer.add(event, data);
62
91
  return;
63
92
  }
64
93
  try {
65
- await this.#transport.emit(event, data);
94
+ const normalizedEvent = this.#resolveEvent(event);
95
+ await this.#transport.emit(normalizedEvent, data);
66
96
  }
67
97
  catch (error) {
68
98
  if (this.#errorHandler) {
@@ -73,37 +103,38 @@ export class Emitter {
73
103
  }
74
104
  }
75
105
  }
76
- off(event, handler) {
77
- if (typeof handler === 'string') {
78
- if (this.#moduleListeners.has(handler)) {
79
- this.#transport.off(event, this.#moduleListeners.get(handler));
80
- }
81
- return this;
106
+ off(event, listener) {
107
+ if (debug.enabled) {
108
+ debug('removing listener, event: %O, listener: %O', event, listener);
82
109
  }
83
- this.#transport.off(event, handler);
84
- return this;
85
- }
86
- offAny(handler) {
87
- if (typeof handler === 'string') {
88
- if (this.#moduleListeners.has(handler)) {
89
- this.#transport.offAny(this.#moduleListeners.get(handler));
90
- }
91
- return this;
110
+ const normalizedEvent = this.#resolveEvent(event);
111
+ const listeners = this.#getEventListeners(event);
112
+ const normalizedListener = listeners.get(listener);
113
+ if (!normalizedListener) {
114
+ return;
92
115
  }
93
- this.#transport.offAny(handler);
116
+ listeners.delete(listener);
117
+ this.#transport.off(normalizedEvent, normalizedListener);
118
+ }
119
+ offAny(listener) {
120
+ this.#transport.offAny(listener);
94
121
  return this;
95
122
  }
96
- clearListener(event, handler) {
97
- return this.off(event, handler);
123
+ clearListener(event, listener) {
124
+ return this.off(event, listener);
98
125
  }
99
126
  clearListeners(event) {
100
- this.#transport.clearListeners(event);
127
+ debug('clearing all listeners for event %O', event);
128
+ this.#transport.clearListeners(this.#resolveEvent(event));
129
+ this.#eventsListeners.delete(event);
101
130
  }
102
131
  clearAllListeners() {
132
+ debug('clearing all event listeners');
103
133
  this.#transport.clearListeners();
134
+ this.#eventsListeners.clear();
104
135
  }
105
136
  listenerCount(event) {
106
- return this.#transport.listenerCount(event);
137
+ return this.#transport.listenerCount(event ? this.#resolveEvent(event) : undefined);
107
138
  }
108
139
  hasListeners(event) {
109
140
  return this.listenerCount(event) > 0;
@@ -1,10 +1,11 @@
1
- import type { BufferedEventsListItem } from './types.js';
1
+ import type { AllowedEventTypes, BufferedEvent, BufferedEventsList, Constructor } from './types.js';
2
2
  export declare class EventsBuffer<EventsList extends Record<string | symbol | number, any>> {
3
- events: BufferedEventsListItem<EventsList>[];
4
- all(): BufferedEventsListItem<EventsList>[];
3
+ #private;
4
+ add<Name extends AllowedEventTypes>(event: Name, data: any): void;
5
+ all(): BufferedEventsList<EventsList>[];
5
6
  size(): number;
6
- exists(finder: keyof EventsList | ((event: BufferedEventsListItem<EventsList>) => boolean)): boolean;
7
- filter(finder: keyof EventsList | ((event: BufferedEventsListItem<EventsList>) => boolean)): BufferedEventsListItem<EventsList>[];
8
- find(finder: keyof EventsList | ((event: BufferedEventsListItem<EventsList>) => boolean)): BufferedEventsListItem<EventsList> | null;
7
+ exists<Event extends keyof EventsList | Constructor<any>>(finder: Event | ((event: BufferedEventsList<EventsList>) => boolean)): boolean;
8
+ filter(finder: keyof EventsList | Constructor<any> | ((event: BufferedEventsList<EventsList>) => boolean)): BufferedEventsList<EventsList>[];
9
+ find<Event extends keyof EventsList | Constructor<any>>(finder: Event | ((event: BufferedEventsList<EventsList>) => boolean)): (Event extends keyof EventsList ? BufferedEvent<Event, EventsList[Event]> : Event extends Constructor<infer A> ? BufferedEvent<Event, A> : BufferedEventsList<EventsList>) | null;
9
10
  flush(): void;
10
11
  }
@@ -1,27 +1,31 @@
1
+ import is from '@adonisjs/application/helpers/is';
1
2
  export class EventsBuffer {
2
- events = [];
3
+ #events = [];
4
+ add(event, data) {
5
+ this.#events.push({ event: event, data });
6
+ }
3
7
  all() {
4
- return this.events;
8
+ return this.#events;
5
9
  }
6
10
  size() {
7
- return this.events.length;
11
+ return this.#events.length;
8
12
  }
9
13
  exists(finder) {
10
14
  return !!this.find(finder);
11
15
  }
12
16
  filter(finder) {
13
- if (typeof finder === 'function') {
14
- return this.events.filter(finder);
17
+ if (typeof finder === 'function' && !is.class_(finder)) {
18
+ return this.#events.filter(finder);
15
19
  }
16
- return this.events.filter((event) => event.name === finder);
20
+ return this.#events.filter((event) => event.event === finder);
17
21
  }
18
22
  find(finder) {
19
- if (typeof finder === 'function') {
20
- return this.events.find(finder) || null;
23
+ if (typeof finder === 'function' && !is.class_(finder)) {
24
+ return (this.#events.find(finder) || null);
21
25
  }
22
- return this.events.find((event) => event.name === finder) || null;
26
+ return (this.#events.find((event) => event.event === finder) || null);
23
27
  }
24
28
  flush() {
25
- this.events = [];
29
+ this.#events = [];
26
30
  }
27
31
  }
@@ -1,9 +1,18 @@
1
- export type BufferedEventsListItem<EventsList> = {
2
- [Name in keyof EventsList]: {
3
- name: Name;
4
- data: EventsList[Name];
5
- };
6
- }[keyof EventsList];
7
- export type EventsListItem<EventsList> = {
8
- [Name in keyof EventsList]: EventsList[Name];
9
- }[keyof EventsList];
1
+ export type AllowedEventTypes = string | symbol | number | Constructor<any>;
2
+ export type Constructor<T> = new (...args: any[]) => T;
3
+ export type LazyImport<DefaultExport> = () => Promise<{
4
+ default: DefaultExport;
5
+ }>;
6
+ export type BufferedEvent<Event, Data> = {
7
+ event: Event;
8
+ data: Data;
9
+ };
10
+ export type BufferedEventsList<EventsList> = {
11
+ [Name in keyof EventsList]: BufferedEvent<Name, EventsList[Name]>;
12
+ }[keyof EventsList] | BufferedEvent<Constructor<any>, any>;
13
+ export type ListenerMethod<Data> = (data: Data, ...args: any[]) => any | Promise<any>;
14
+ export type ListenerFn<Data> = (data: Data) => any | Promise<any>;
15
+ export type GetListenersMethods<Listener extends Constructor<any>, Data> = {
16
+ [K in keyof InstanceType<Listener>]: InstanceType<Listener>[K] extends ListenerMethod<Data> ? K : never;
17
+ }[keyof InstanceType<Listener>];
18
+ export type Listener<Data, ListenerClass extends Constructor<any>> = ListenerFn<Data> | string | [LazyImport<ListenerClass> | ListenerClass, GetListenersMethods<ListenerClass, Data>?];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adonisjs/events",
3
- "version": "8.1.4-0",
3
+ "version": "8.2.0-0",
4
4
  "description": "An implementation of the event emitter built on top of emittery",
5
5
  "main": "build/index.js",
6
6
  "type": "module",
@@ -37,33 +37,33 @@
37
37
  "author": "virk,adonisjs",
38
38
  "license": "MIT",
39
39
  "devDependencies": {
40
- "@adonisjs/application": "^6.4.0-0",
41
- "@adonisjs/config": "^4.1.2-0",
42
- "@adonisjs/env": "^4.1.1-0",
43
- "@adonisjs/fold": "^9.8.0-0",
44
- "@adonisjs/logger": "^5.1.0-0",
45
- "@commitlint/cli": "^17.3.0",
46
- "@commitlint/config-conventional": "^17.3.0",
40
+ "@adonisjs/application": "^6.5.1-0",
41
+ "@adonisjs/config": "^4.1.3-0",
42
+ "@adonisjs/env": "^4.1.2-0",
43
+ "@adonisjs/fold": "^9.9.0-0",
44
+ "@adonisjs/logger": "^5.1.1-0",
45
+ "@commitlint/cli": "^17.4.0",
46
+ "@commitlint/config-conventional": "^17.4.0",
47
47
  "@japa/assert": "^1.3.6",
48
48
  "@japa/expect-type": "^1.0.2",
49
49
  "@japa/run-failed-tests": "^1.1.0",
50
50
  "@japa/runner": "^2.2.2",
51
51
  "@japa/spec-reporter": "^1.3.2",
52
52
  "@poppinss/dev-utils": "^2.0.1",
53
- "@swc/core": "^1.3.22",
53
+ "@swc/core": "^1.3.24",
54
54
  "@types/fs-extra": "^9.0.13",
55
- "@types/node": "^18.11.15",
55
+ "@types/node": "^18.11.18",
56
56
  "c8": "^7.12.0",
57
57
  "cross-env": "^7.0.3",
58
58
  "del-cli": "^5.0.0",
59
- "eslint": "^8.29.0",
60
- "eslint-config-prettier": "^8.3.0",
59
+ "eslint": "^8.31.0",
60
+ "eslint-config-prettier": "^8.6.0",
61
61
  "eslint-plugin-adonis": "^3.0.3",
62
62
  "eslint-plugin-prettier": "^4.2.1",
63
63
  "fs-extra": "^11.1.0",
64
64
  "github-label-sync": "^2.0.1",
65
- "husky": "^8.0.2",
66
- "np": "^7.6.2",
65
+ "husky": "^8.0.3",
66
+ "np": "^7.6.3",
67
67
  "prettier": "^2.8.1",
68
68
  "ts-node": "^10.9.1",
69
69
  "typescript": "^4.9.4"
@@ -72,8 +72,8 @@
72
72
  "emittery": "^1.0.1"
73
73
  },
74
74
  "peerDependencies": {
75
- "@adonisjs/application": "^6.4.0-0",
76
- "@adonisjs/fold": "^9.8.0-0"
75
+ "@adonisjs/application": "^6.5.1-0",
76
+ "@adonisjs/fold": "^9.9.0-0"
77
77
  },
78
78
  "repository": {
79
79
  "type": "git",