@poppinss/hooks
Advanced tools
Comparing version 7.1.1-6 to 7.2.0
@@ -1,1 +0,48 @@ | ||
export { Hooks as default } from './src/hooks.js'; | ||
import { HookHandler, HookHandlerProvider, Runner } from './src/types.js'; | ||
/** | ||
* Quite simple implementation register lifecycle hooks around specific events. | ||
* | ||
* ```ts | ||
* const hooks = new Hooks() | ||
* | ||
* hooks.add('saving', function hashPassword(entity) { | ||
* }) | ||
* ``` | ||
*/ | ||
declare class Hooks<Events extends Record<string, [any[], any[]]>> { | ||
#private; | ||
/** | ||
* Get access to all the registered hooks. The return value is | ||
* a map of the event name and a set of handlers. | ||
*/ | ||
all(): { [Event_1 in keyof Events]: Map<Event_1, Set<HookHandler<Events[Event_1][0], Events[Event_1][1]> | HookHandlerProvider<Events[Event_1][0], Events[Event_1][1]>>>; }[keyof Events]; | ||
/** | ||
* Find if a handler for a given event exists. | ||
*/ | ||
has<Event extends keyof Events>(event: Event, handler: HookHandler<Events[Event][0], Events[Event][1]> | HookHandlerProvider<Events[Event][0], Events[Event][1]>): boolean; | ||
/** | ||
* Add a hook handler for a given event. Adding the same handler twice will | ||
* result in a noop. | ||
*/ | ||
add<Event extends keyof Events>(event: Event, handler: HookHandler<Events[Event][0], Events[Event][1]> | HookHandlerProvider<Events[Event][0], Events[Event][1]>): this; | ||
/** | ||
* Remove hook handler for a given event. | ||
*/ | ||
remove<Event extends keyof Events>(event: Event, handler: HookHandler<Events[Event][0], Events[Event][1]> | HookHandlerProvider<Events[Event][0], Events[Event][1]>): boolean; | ||
/** | ||
* Clear all the hooks for a specific event or all the | ||
* events. | ||
*/ | ||
clear(event?: keyof Events): void; | ||
/** | ||
* Merge hooks from an existing hooks instance. | ||
*/ | ||
merge(hooks: Hooks<Events>): void; | ||
/** | ||
* Returns an instance of the runner to run hooks | ||
*/ | ||
runner<Event extends Extract<keyof Events, string>>(action: Event): Runner<Events[Event][0], Events[Event][1]>; | ||
} | ||
export { Hooks as default }; |
@@ -1,9 +0,180 @@ | ||
/* | ||
* @poppinss/hooks | ||
* | ||
* (c) Poppinss | ||
* | ||
* For the full copyright and license information, please view the LICENSE | ||
* file that was distributed with this source code. | ||
*/ | ||
export { Hooks as default } from './src/hooks.js'; | ||
// src/runner.ts | ||
var Runner = class { | ||
constructor(action, hookHandlers) { | ||
this.action = action; | ||
this.#hookHandlers = hookHandlers || /* @__PURE__ */ new Set(); | ||
} | ||
/** | ||
* A collection of registered hook handlers | ||
*/ | ||
#hookHandlers; | ||
/** | ||
* Cleanup handlers should always be an array of functions. Using a set will | ||
* discard duplicates and it is very much possible for two hooks to return | ||
* a shared cleanup handler. | ||
*/ | ||
#cleanupHandlers = []; | ||
/** | ||
* State to perform the cleanup | ||
*/ | ||
#state = "idle"; | ||
/** | ||
* A collection of handlers to ignore when executed them | ||
*/ | ||
#handlersToIgnore = []; | ||
/** | ||
* Whether or not to skip all the hooks | ||
*/ | ||
#skipAllHooks = false; | ||
/** | ||
* Find if cleanup is pending or not | ||
*/ | ||
get isCleanupPending() { | ||
return this.#state === "cleanup_pending"; | ||
} | ||
/** | ||
* Filter to check if we should run the handler | ||
*/ | ||
#filter(handlerName) { | ||
return !this.#handlersToIgnore.includes(handlerName); | ||
} | ||
/** | ||
* Ignore specific or all hook handlers. Calling this | ||
* method multiple times will result in overwriting | ||
* the existing state. | ||
*/ | ||
without(handlersToIgnore) { | ||
if (!handlersToIgnore) { | ||
this.#skipAllHooks = true; | ||
} else { | ||
this.#skipAllHooks = false; | ||
this.#handlersToIgnore = handlersToIgnore; | ||
} | ||
return this; | ||
} | ||
/** | ||
* Executing hooks | ||
*/ | ||
async #exec(reverse, data) { | ||
if (this.#state !== "idle") { | ||
return; | ||
} | ||
this.#state = "cleanup_pending"; | ||
if (this.#skipAllHooks) { | ||
return; | ||
} | ||
const handlers = reverse ? Array.from(this.#hookHandlers).reverse() : this.#hookHandlers; | ||
for (let handler of handlers) { | ||
if (this.#filter(handler.name)) { | ||
const result = await (typeof handler === "function" ? handler(...data) : handler.handle(this.action, ...data)); | ||
if (typeof result === "function") { | ||
this.#cleanupHandlers.push(result); | ||
} | ||
} | ||
} | ||
} | ||
/** | ||
* Execute handlers | ||
*/ | ||
async run(...data) { | ||
return this.#exec(false, data); | ||
} | ||
/** | ||
* Execute handlers in reverse order | ||
*/ | ||
async runReverse(...data) { | ||
return this.#exec(true, data); | ||
} | ||
/** | ||
* Execute cleanup actions | ||
*/ | ||
async cleanup(...data) { | ||
if (!this.isCleanupPending) { | ||
return; | ||
} | ||
this.#state = "cleanup_initiated"; | ||
let startIndex = this.#cleanupHandlers.length; | ||
while (startIndex--) { | ||
await this.#cleanupHandlers[startIndex](...data); | ||
} | ||
this.#state = "cleanup_completed"; | ||
this.#cleanupHandlers = []; | ||
} | ||
}; | ||
// src/hooks.ts | ||
var Hooks = class { | ||
/** | ||
* A collection of registered hooks | ||
*/ | ||
#hooks = /* @__PURE__ */ new Map(); | ||
/** | ||
* Get access to all the registered hooks. The return value is | ||
* a map of the event name and a set of handlers. | ||
*/ | ||
all() { | ||
return this.#hooks; | ||
} | ||
/** | ||
* Find if a handler for a given event exists. | ||
*/ | ||
has(event, handler) { | ||
const handlers = this.#hooks.get(event); | ||
if (!handlers) { | ||
return false; | ||
} | ||
return handlers.has(handler); | ||
} | ||
/** | ||
* Add a hook handler for a given event. Adding the same handler twice will | ||
* result in a noop. | ||
*/ | ||
add(event, handler) { | ||
const handlers = this.#hooks.get(event); | ||
if (!handlers) { | ||
this.#hooks.set(event, /* @__PURE__ */ new Set()); | ||
} | ||
this.#hooks.get(event).add(handler); | ||
return this; | ||
} | ||
/** | ||
* Remove hook handler for a given event. | ||
*/ | ||
remove(event, handler) { | ||
const handlers = this.#hooks.get(event); | ||
if (!handlers) { | ||
return false; | ||
} | ||
return handlers.delete(handler); | ||
} | ||
/** | ||
* Clear all the hooks for a specific event or all the | ||
* events. | ||
*/ | ||
clear(event) { | ||
if (!event) { | ||
this.#hooks.clear(); | ||
return; | ||
} | ||
this.#hooks.delete(event); | ||
} | ||
/** | ||
* Merge hooks from an existing hooks instance. | ||
*/ | ||
merge(hooks) { | ||
hooks.all().forEach((actionHooks, action) => { | ||
actionHooks.forEach((handler) => { | ||
this.add(action, handler); | ||
}); | ||
}); | ||
} | ||
/** | ||
* Returns an instance of the runner to run hooks | ||
*/ | ||
runner(action) { | ||
return new Runner(action, this.#hooks.get(action)); | ||
} | ||
}; | ||
export { | ||
Hooks as default | ||
}; |
@@ -1,24 +0,59 @@ | ||
import type { Runner } from './runner.js'; | ||
/** | ||
* Exporting hooks runner as a type | ||
*/ | ||
export { Runner }; | ||
/** | ||
* Shape of the cleanup handler | ||
*/ | ||
export type CleanupHandler<Args extends any[]> = (...args: Args) => void | Promise<void>; | ||
type CleanupHandler<Args extends any[]> = (...args: Args) => void | Promise<void>; | ||
/** | ||
* Shape of the hook handler | ||
*/ | ||
export type HookHandler<Args extends any[], CleanUpArgs extends any[]> = (...args: Args) => void | CleanupHandler<CleanUpArgs> | Promise<void> | Promise<CleanupHandler<CleanUpArgs>>; | ||
type HookHandler<Args extends any[], CleanUpArgs extends any[]> = (...args: Args) => void | CleanupHandler<CleanUpArgs> | Promise<void> | Promise<CleanupHandler<CleanUpArgs>>; | ||
/** | ||
* Extracts args from a hook handler type | ||
*/ | ||
export type ExtractHookHandlerArgs<Handler> = Handler extends HookHandler<infer A, infer B> ? [A, B] : never; | ||
type ExtractHookHandlerArgs<Handler> = Handler extends HookHandler<infer A, infer B> ? [A, B] : never; | ||
/** | ||
* Hook represented as an object with handle method | ||
*/ | ||
export type HookHandlerProvider<Args extends any[], CleanUpArgs extends any[]> = { | ||
type HookHandlerProvider<Args extends any[], CleanUpArgs extends any[]> = { | ||
name: string; | ||
handle(event: string, ...args: Args): void | CleanupHandler<CleanUpArgs> | Promise<void> | Promise<CleanupHandler<CleanUpArgs>>; | ||
}; | ||
/** | ||
* Runner allows running a set of specific hook handlers for a given | ||
* event. You can grab the instance of the runner using the "hook.runner" method. | ||
* | ||
* ```ts | ||
* const hooks = new Hooks() | ||
* | ||
* await hooks.runner('saving').run() | ||
* ``` | ||
*/ | ||
declare class Runner<HookArgs extends any[], CleanUpArgs extends any[]> { | ||
#private; | ||
action: string; | ||
/** | ||
* Find if cleanup is pending or not | ||
*/ | ||
get isCleanupPending(): boolean; | ||
constructor(action: string, hookHandlers?: Set<HookHandler<HookArgs, CleanUpArgs> | HookHandlerProvider<HookArgs, CleanUpArgs>>); | ||
/** | ||
* Ignore specific or all hook handlers. Calling this | ||
* method multiple times will result in overwriting | ||
* the existing state. | ||
*/ | ||
without(handlersToIgnore?: string[]): this; | ||
/** | ||
* Execute handlers | ||
*/ | ||
run(...data: HookArgs): Promise<void>; | ||
/** | ||
* Execute handlers in reverse order | ||
*/ | ||
runReverse(...data: HookArgs): Promise<void>; | ||
/** | ||
* Execute cleanup actions | ||
*/ | ||
cleanup(...data: CleanUpArgs): Promise<void>; | ||
} | ||
export { CleanupHandler, ExtractHookHandlerArgs, HookHandler, HookHandlerProvider, Runner }; |
{ | ||
"name": "@poppinss/hooks", | ||
"version": "7.1.1-6", | ||
"version": "7.2.0", | ||
"description": "A no brainer hooks module for execute before/after lifecycle hooks", | ||
@@ -8,5 +8,3 @@ "main": "build/index.js", | ||
"files": [ | ||
"build/src", | ||
"build/index.d.ts", | ||
"build/index.js" | ||
"build" | ||
], | ||
@@ -25,3 +23,3 @@ "engines": { | ||
"typecheck": "tsc --noEmit", | ||
"compile": "npm run lint && npm run clean && tsc", | ||
"compile": "npm run lint && npm run clean && tsup-node", | ||
"build": "npm run compile", | ||
@@ -48,7 +46,7 @@ "release": "np", | ||
"@commitlint/config-conventional": "^17.7.0", | ||
"@japa/assert": "^2.0.0-1", | ||
"@japa/expect-type": "^2.0.0-0", | ||
"@japa/runner": "^3.0.0-2", | ||
"@japa/assert": "^2.0.0", | ||
"@japa/expect-type": "^2.0.0", | ||
"@japa/runner": "^3.0.1", | ||
"@swc/core": "1.3.82", | ||
"@types/node": "^20.8.3", | ||
"@types/node": "^20.8.6", | ||
"c8": "^8.0.1", | ||
@@ -62,2 +60,3 @@ "del-cli": "^5.1.0", | ||
"ts-node": "^10.9.1", | ||
"tsup": "^7.2.0", | ||
"typescript": "^5.2.2" | ||
@@ -80,7 +79,7 @@ }, | ||
"access": "public", | ||
"tag": "next" | ||
"tag": "latest" | ||
}, | ||
"np": { | ||
"message": "chore(release): %s", | ||
"tag": "next", | ||
"tag": "latest", | ||
"branch": "main", | ||
@@ -101,3 +100,14 @@ "anyBranch": false | ||
}, | ||
"prettier": "@adonisjs/prettier-config" | ||
"prettier": "@adonisjs/prettier-config", | ||
"tsup": { | ||
"entry": [ | ||
"./index.ts", | ||
"./src/types.ts" | ||
], | ||
"outDir": "./build", | ||
"clean": true, | ||
"format": "esm", | ||
"dts": true, | ||
"target": "esnext" | ||
} | ||
} |
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
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
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
0
17154
20
7
282
1