@poppinss/hooks
Advanced tools
Comparing version 7.0.0-0 to 7.1.0-0
import { Runner } from './runner.js'; | ||
import { HookHandler, HookProvider } from './types.js'; | ||
import { HookHandler, HookHandlerProvider } from './types.js'; | ||
export declare class Hooks<Events extends Record<string, [any[], any[]]>> { | ||
#private; | ||
all(): { [Event in keyof Events]: Map<Event, Set<HookHandler<Events[Event][0], Events[Event][1]>>>; }[keyof Events]; | ||
providers(): Set<HookProvider>; | ||
has<Event extends keyof Events>(event: Event, handler: HookHandler<Events[Event][0], Events[Event][1]>): boolean; | ||
add<Event extends keyof Events>(event: Event, handler: HookHandler<Events[Event][0], Events[Event][1]>): this; | ||
remove<Event extends keyof Events>(event: Event, handler: HookHandler<Events[Event][0], Events[Event][1]>): boolean; | ||
all(): { [Event in keyof Events]: Map<Event, Set<HookHandler<Events[Event][0], Events[Event][1]> | HookHandlerProvider<Events[Event][0], Events[Event][1]>>>; }[keyof Events]; | ||
has<Event extends keyof Events>(event: Event, handler: HookHandler<Events[Event][0], Events[Event][1]> | HookHandlerProvider<Events[Event][0], Events[Event][1]>): boolean; | ||
add<Event extends keyof Events>(event: Event, handler: HookHandler<Events[Event][0], Events[Event][1]> | HookHandlerProvider<Events[Event][0], Events[Event][1]>): this; | ||
remove<Event extends keyof Events>(event: Event, handler: HookHandler<Events[Event][0], Events[Event][1]> | HookHandlerProvider<Events[Event][0], Events[Event][1]>): boolean; | ||
clear(event?: keyof Events): void; | ||
provider(provider: HookProvider): this; | ||
hasProvider(provider: HookProvider): boolean; | ||
removeProvider(provider: HookProvider): boolean; | ||
merge(hooks: Hooks<Events>): void; | ||
runner<Event extends Extract<keyof Events, string>>(action: Event): Runner<Events[Event][0], Events[Event][1]>; | ||
} |
import { Runner } from './runner.js'; | ||
export class Hooks { | ||
#hooks = new Map(); | ||
#hookProviders = new Set(); | ||
all() { | ||
return this.#hooks; | ||
} | ||
providers() { | ||
return this.#hookProviders; | ||
} | ||
has(event, handler) { | ||
@@ -40,12 +36,2 @@ const handlers = this.#hooks.get(event); | ||
} | ||
provider(provider) { | ||
this.#hookProviders.add(provider); | ||
return this; | ||
} | ||
hasProvider(provider) { | ||
return this.#hookProviders.has(provider); | ||
} | ||
removeProvider(provider) { | ||
return this.#hookProviders.delete(provider); | ||
} | ||
merge(hooks) { | ||
@@ -57,9 +43,6 @@ hooks.all().forEach((actionHooks, action) => { | ||
}); | ||
hooks.providers().forEach((provider) => { | ||
this.provider(provider); | ||
}); | ||
} | ||
runner(action) { | ||
return new Runner(action, this.#hookProviders, this.#hooks.get(action)); | ||
return new Runner(action, this.#hooks.get(action)); | ||
} | ||
} |
@@ -1,2 +0,2 @@ | ||
import { HookHandler, HookProvider, HandlerExecutor, ProviderExecutor } from './types.js'; | ||
import { HookHandler, HookHandlerProvider } from './types.js'; | ||
export declare class Runner<HookArgs extends any[], CleanUpArgs extends any[]> { | ||
@@ -6,8 +6,6 @@ #private; | ||
get isCleanupPending(): boolean; | ||
constructor(action: string, hookProviders: Set<HookProvider>, hookHandlers?: Set<HookHandler<HookArgs, CleanUpArgs>>); | ||
constructor(action: string, hookHandlers?: Set<HookHandler<HookArgs, CleanUpArgs> | HookHandlerProvider<HookArgs, CleanUpArgs>>); | ||
without(handlersToIgnore?: string[]): this; | ||
executor(callback: HandlerExecutor): this; | ||
providerExecutor(callback: ProviderExecutor): this; | ||
run(...data: HookArgs): Promise<void>; | ||
cleanup(...data: CleanUpArgs): Promise<void>; | ||
} |
@@ -1,12 +0,4 @@ | ||
const defaultHandlerExecutor = (handler, _, ...data) => handler(...data); | ||
const defaultProviderExecutor = (provider, event, ...data) => { | ||
const providerInstance = new provider(); | ||
if (typeof providerInstance[event] === 'function') { | ||
return providerInstance[event](...data); | ||
} | ||
}; | ||
export class Runner { | ||
action; | ||
#hookHandlers; | ||
#hookProviders; | ||
#cleanupHandlers = []; | ||
@@ -16,10 +8,7 @@ #state = 'idle'; | ||
#skipAllHooks = false; | ||
#handlerExecutor = defaultHandlerExecutor; | ||
#providerExecutor = defaultProviderExecutor; | ||
get isCleanupPending() { | ||
return this.#state === 'cleanup_pending'; | ||
} | ||
constructor(action, hookProviders, hookHandlers) { | ||
constructor(action, hookHandlers) { | ||
this.action = action; | ||
this.#hookProviders = hookProviders; | ||
this.#hookHandlers = hookHandlers || new Set(); | ||
@@ -40,10 +29,2 @@ } | ||
} | ||
executor(callback) { | ||
this.#handlerExecutor = callback; | ||
return this; | ||
} | ||
providerExecutor(callback) { | ||
this.#providerExecutor = callback; | ||
return this; | ||
} | ||
async run(...data) { | ||
@@ -59,3 +40,5 @@ if (this.#state !== 'idle') { | ||
if (this.#filter(handler.name)) { | ||
const result = await this.#handlerExecutor(handler, false, ...data); | ||
const result = await (typeof handler === 'function' | ||
? handler(...data) | ||
: handler.handle(this.action, ...data)); | ||
if (typeof result === 'function') { | ||
@@ -66,10 +49,2 @@ this.#cleanupHandlers.push(result); | ||
} | ||
for (let provider of this.#hookProviders) { | ||
if (this.#filter(`${provider.name}.${this.action}`)) { | ||
const result = await this.#providerExecutor(provider, this.action, ...data); | ||
if (typeof result === 'function') { | ||
this.#cleanupHandlers.push(result); | ||
} | ||
} | ||
} | ||
} | ||
@@ -83,3 +58,3 @@ async cleanup(...data) { | ||
while (startIndex--) { | ||
await this.#handlerExecutor(this.#cleanupHandlers[startIndex], true, ...data); | ||
await this.#cleanupHandlers[startIndex](...data); | ||
} | ||
@@ -86,0 +61,0 @@ this.#state = 'cleanup_completed'; |
export declare type CleanupHandler<Args extends any[]> = (...args: Args) => void | Promise<void>; | ||
export declare type HookHandler<Args extends any[], CleanUpArgs extends any[]> = (...args: Args) => void | CleanupHandler<CleanUpArgs> | Promise<void> | Promise<CleanupHandler<CleanUpArgs>>; | ||
export declare type HandlerExecutor = (handler: (...args: any[]) => any, isCleanupHandler: boolean, ...args: any[]) => void | Promise<void>; | ||
export declare type HookProvider = new (...args: any[]) => any; | ||
export declare type ProviderExecutor = (Provider: HookProvider, event: string, ...args: any[]) => void | Promise<void>; | ||
export declare type HookHandlerProvider<Args extends any[], CleanUpArgs extends any[]> = { | ||
name: string; | ||
handle(event: string, ...args: Args): void | CleanupHandler<CleanUpArgs> | Promise<void> | Promise<CleanupHandler<CleanUpArgs>>; | ||
}; |
{ | ||
"name": "@poppinss/hooks", | ||
"version": "7.0.0-0", | ||
"version": "7.1.0-0", | ||
"description": "A no brainer hooks module for execute before/after lifecycle hooks", | ||
@@ -5,0 +5,0 @@ "main": "build/index.js", |
102
README.md
@@ -10,3 +10,2 @@ # @poppinss/hooks | ||
- Hooks can return cleanup functions that are executed to perform the cleanup. | ||
- Alongside "hooks as functions", you can also register hook providers, which encapsulate event handlers inside a class. | ||
- Super lightweight | ||
@@ -50,2 +49,15 @@ | ||
You can also define hook as an object with the `name` and the `handle` method property. This is usually helpful when you want to specify a custom name for the hook, or re-use the same handle method multiple times. | ||
```ts | ||
const hooks = new Hooks() | ||
function handleSave() {} | ||
hooks.add('saving', { name: 'beforeSave', handle: handleSave }) | ||
hooks.add('creating', { name: 'beforeCreate', handle: handleSave }) | ||
``` | ||
The `handle` method receives the first argument as the event name, followed by the rest of the arguments supplied during runtime. | ||
## Running hooks | ||
@@ -118,35 +130,2 @@ You can execute hooks using the Hooks Runner. You can create a new runner instance by calling the `hooks.runner` method and passing the event name for which you want to execute hooks. | ||
## Hook Providers | ||
Hook providers are classes with the event lifecycle methods on them. Providers are great when you want to listen to multiple events to create a single cohesive feature. Again, taking the example of models, you can make a hook provider listen for all the hooks and manage a changelog of table columns. | ||
```ts | ||
class ChangeSetProvider { | ||
created() { | ||
// listens for created event | ||
} | ||
updated() { | ||
// listens for updated event | ||
} | ||
deleted() { | ||
// listens for deleted event | ||
} | ||
} | ||
``` | ||
Next, register the provider as follows. | ||
```ts | ||
hooks.provider(ChangeSetProvider) | ||
``` | ||
Run hooks | ||
```ts | ||
await hooks.runner('created').run() | ||
await hooks.runner('updated').run() | ||
await hooks.runner('deleted').run() | ||
``` | ||
## Run without hook handlers | ||
@@ -167,57 +146,2 @@ You can exclude certain hook handlers from executing using the `without` method. | ||
You can specify the provider class and the method name with hook providers. | ||
```ts | ||
class ChangeSetProvider { | ||
created() {} | ||
} | ||
hooks.provider(ChangeSetProvider) | ||
await hooks | ||
.runner('created') | ||
.without(['ChangeSetProvider.created']) | ||
.run() | ||
``` | ||
## Custom executors | ||
The hooks runner allows you to define custom executors for calling the hook callback functions or the provider lifecycle methods. They are helpful when you want to tweak how a method should run. | ||
For example, AdonisJS uses the IoC container to call the provider lifecycle methods. | ||
In the following example, the custom executor is responsible for calling the hook callback functions. | ||
```ts | ||
hooks.add('saving', function hashPassword () {}) | ||
hooks.add('saving', function generateDefaultAvatar () {}) | ||
hooks | ||
.runner('saving') | ||
.executor((handler, isCleanupFunction, ...data) => { | ||
console.log(handler.name) | ||
return handler(...data) | ||
}) | ||
.run(model) | ||
``` | ||
Similarly, you can also define a custom executor for the provider classes. | ||
```ts | ||
class ChangeSetProvider { | ||
created() {} | ||
} | ||
hooks.provider(ChangeSetProvider) | ||
await hooks | ||
.runner('created') | ||
.providerExecutor((Provider, event, ...data) => { | ||
const provider = new Provider() | ||
if (typeof provider[event] === 'function') { | ||
return provider[event](...data) | ||
} | ||
}) | ||
.run(model) | ||
``` | ||
## Event types | ||
@@ -224,0 +148,0 @@ You can also specify the types of supported events and their arguments well in advance as follows. |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
0
14491
136
181