Initial commit

This commit is contained in:
2025-03-07 19:22:02 +01:00
commit 4a98255d83
55743 changed files with 5280367 additions and 0 deletions
+161
View File
@@ -0,0 +1,161 @@
# Change Log
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [0.6.5](https://github.com/feathersjs/hooks/compare/v0.6.4...v0.6.5) (2021-04-20)
### Bug Fixes
* **typescript:** Tighten up TypeScript settings to be ECMAScript and Deno compatible ([#81](https://github.com/feathersjs/hooks/issues/81)) ([28fe875](https://github.com/feathersjs/hooks/commit/28fe8758b4764981473830db4a0013dd1beca489))
## [0.6.4](https://github.com/feathersjs/hooks/compare/v0.6.3...v0.6.4) (2021-04-11)
### Bug Fixes
* allow to hooks a function without middleware ([#77](https://github.com/feathersjs/hooks/issues/77)) ([38b44c3](https://github.com/feathersjs/hooks/commit/38b44c3ba1bd7753cdb81492b517e4fd3a6af50e))
* conserve fn.length and fn.name ([#79](https://github.com/feathersjs/hooks/issues/79)) ([d9bc9af](https://github.com/feathersjs/hooks/commit/d9bc9af689f15398168ce4493fcfb23af0f3ef05))
## [0.6.3](https://github.com/feathersjs/hooks/compare/v0.6.2...v0.6.3) (2021-03-31)
### Bug Fixes
* **hooks:** Add Deno tests and build, CI and fix build ([#73](https://github.com/feathersjs/hooks/issues/73)) ([44787cd](https://github.com/feathersjs/hooks/commit/44787cd2106c6d1ff4fe8bc5d59362e14427c468))
## [0.6.2](https://github.com/feathersjs/hooks/compare/v0.6.1...v0.6.2) (2021-02-08)
### Bug Fixes
* **hooks:** Allow to set context.result to undefined ([#70](https://github.com/feathersjs/hooks/issues/70)) ([7b5807f](https://github.com/feathersjs/hooks/commit/7b5807f8a31b0e4859eaabdbcc8b49fc3b544548))
## [0.6.1](https://github.com/feathersjs/hooks/compare/v0.6.0...v0.6.1) (2020-12-11)
### Bug Fixes
* **hooks:** fix some errors for feathers integration ([#67](https://github.com/feathersjs/hooks/issues/67)) ([fcfc0ca](https://github.com/feathersjs/hooks/commit/fcfc0ca6423a8062959d41f34e673f81d3c006dd))
* **hooks:** Remove redundant method call ([#65](https://github.com/feathersjs/hooks/issues/65)) ([4ff10a9](https://github.com/feathersjs/hooks/commit/4ff10a9935682276b8ca3ffb699275b627230dfa))
* **hooks:** Stricter condition ([#64](https://github.com/feathersjs/hooks/issues/64)) ([6de77a1](https://github.com/feathersjs/hooks/commit/6de77a1afcbee4867b7e464be0b556a8dc9656e3))
# [0.6.0](https://github.com/feathersjs/hooks/compare/v0.5.0...v0.6.0) (2020-11-12)
### Features
* **hooks:** Revert refactoring into separate hooks (PR [#37](https://github.com/feathersjs/hooks/issues/37)) ([#57](https://github.com/feathersjs/hooks/issues/57)) ([56a44be](https://github.com/feathersjs/hooks/commit/56a44beb3388873f7bef12ac640f115beffceb95))
# [0.5.0](https://github.com/feathersjs/hooks/compare/v0.5.0-alpha.0...v0.5.0) (2020-06-01)
### Features
* **hooks:** Finalize default initializer functionality ([#35](https://github.com/feathersjs/hooks/issues/35)) ([d380d76](https://github.com/feathersjs/hooks/commit/d380d76891b28160c992438bfb3f28493eacddc4))
* **hooks:** Refactor .params, .props and .defaults into hooks ([#37](https://github.com/feathersjs/hooks/issues/37)) ([9b13b7d](https://github.com/feathersjs/hooks/commit/9b13b7de6b708a2152927335aae25dd320b4cfeb))
* **typescript:** Improve type indexes for stricter object and class hooks ([699f2fd](https://github.com/feathersjs/hooks/commit/699f2fd973eb72c0d7c3aefff7b230a7a8a2918a))
# [0.5.0-alpha.0](https://github.com/feathersjs/hooks/compare/v0.4.0-alpha.0...v0.5.0-alpha.0) (2020-04-05)
**Note:** Version bump only for package @feathersjs/hooks
# [0.4.0-alpha.0](https://github.com/feathersjs/hooks/compare/v0.3.1...v0.4.0-alpha.0) (2020-02-19)
### Bug Fixes
* conserve props from original method ([#19](https://github.com/feathersjs/hooks/issues/19)) ([9a77e81](https://github.com/feathersjs/hooks/commit/9a77e81a8b0912a8d3275a2d18e19616d4e4d37e))
* remove walkOriginal ([df1f7ff](https://github.com/feathersjs/hooks/commit/df1f7ffa73ea087d487582efa3c8c7f5be992ab9))
* Update withParams ([#16](https://github.com/feathersjs/hooks/issues/16)) ([b89d044](https://github.com/feathersjs/hooks/commit/b89d0443680d1a30f0875d1b817ddf9ad6220ffe))
* use collector of each .original ([#17](https://github.com/feathersjs/hooks/issues/17)) ([33fd2cb](https://github.com/feathersjs/hooks/commit/33fd2cb3a66301e76be6e83c5a7d6248434c7fd0))
### Features
* add setMiddleware ([#18](https://github.com/feathersjs/hooks/issues/18)) ([7d0113d](https://github.com/feathersjs/hooks/commit/7d0113d4e6c972983e6384ff892cb5ca8c70365c))
* Chainable configuration ([#23](https://github.com/feathersjs/hooks/issues/23)) ([c534827](https://github.com/feathersjs/hooks/commit/c534827d539faab885f84d035e2edb912770759f))
## [0.3.1](https://github.com/feathersjs/hooks/compare/v0.3.0...v0.3.1) (2020-01-29)
### Bug Fixes
* Fix dependency entries ([4a15e74](https://github.com/feathersjs/hooks/commit/4a15e74f83247833edf7de8ea26b908115a5ab7a))
# [0.3.0](https://github.com/feathersjs/hooks/compare/v0.2.0...v0.3.0) (2020-01-29)
### Features
* Allow multiple context initializers ([#12](https://github.com/feathersjs/hooks/issues/12)) ([a556272](https://github.com/feathersjs/hooks/commit/a556272f535c7d2a25bcbc12d8473cdaefaf8c56))
# [0.2.0](https://github.com/feathersjs/hooks/compare/v0.1.0...v0.2.0) (2020-01-14)
### Bug Fixes
* Add tests for fn.original and update documentation ([#5](https://github.com/feathersjs/hooks/issues/5)) ([f4c1955](https://github.com/feathersjs/hooks/commit/f4c195512c2f24d4d9abb68d39275f2287574162))
### Features
* Add browser build ([#8](https://github.com/feathersjs/hooks/issues/8)) ([d6162ca](https://github.com/feathersjs/hooks/commit/d6162caccabe43c468df9360f7f03362ad36c41d))
* Add build script and publish a version for Deno ([#6](https://github.com/feathersjs/hooks/issues/6)) ([f2b5697](https://github.com/feathersjs/hooks/commit/f2b56972fa2ef21799bc6e531644ef9e751bd25b))
* Refactoring to pass an option object to initialize hooks more explicitly ([#7](https://github.com/feathersjs/hooks/issues/7)) ([8f2453f](https://github.com/feathersjs/hooks/commit/8f2453f3e230f6c17989f244cc3dc8126a895eeb))
# 0.1.0 (2020-01-05)
### Features
* Finalize functionality for initial release of @feathersjs/hooks package ([#1](https://github.com/feathersjs/feathers/issues/1)) ([edab7a1](https://github.com/feathersjs/feathers/commit/edab7a1d24b2f25f59af01aad1275ea74dee3879))
+22
View File
@@ -0,0 +1,22 @@
The MIT License (MIT)
Copyright (c) 2020 Feathers
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+37
View File
@@ -0,0 +1,37 @@
# Hooks
[![CI GitHub action](https://github.com/feathersjs/hooks/workflows/Node%20CI/badge.svg)](https://github.com/feathersjs/hooks/actions?query=workflow%3A%22Node+CI%22)
[![Deno CI](https://github.com/feathersjs/hooks/actions/workflows/deno.yml/badge.svg)](https://github.com/feathersjs/hooks/actions/workflows/deno.yml)
[![Download Status](https://img.shields.io/npm/dm/@feathersjs/hooks.svg?style=flat-square)](https://www.npmjs.com/package/@feathersjs/hooks)
`@feathersjs/hooks` brings Koa style middleware to any asynchronous JavaScript and TypeScript function or class.
## Installation
### Node
```
npm install @feathersjs/hooks --save
```
```js
const { hooks } = require('@feathersjs/hooks');
import { hooks } from '@feathersjs/hooks';
```
### Deno
```js
import { hooks } from 'https://deno.land/x/hooks@x.x.x/deno/index.ts';
```
## Documentation
See [feathersjs/hooks](https://github.com/feathersjs/hooks/) for the complete documentation.
## License
Copyright (c) 2020
Licensed under the [MIT license](LICENSE).
+213
View File
@@ -0,0 +1,213 @@
import { Middleware } from './compose.ts';
import { copyToSelf, copyProperties } from './utils.ts';
export const HOOKS: string = Symbol('@feathersjs/hooks') as any;
export type HookContextData = { [key: string]: any };
/**
* The base hook context.
*/
export class BaseHookContext<C = any> {
self?: C;
[key: string]: any;
constructor (data: HookContextData = {}) {
Object.assign(this, data);
}
}
export interface HookContext<T = any, C = any> extends BaseHookContext<C> {
result?: T;
method?: string;
arguments: any[];
}
export type HookContextConstructor = new (data?: { [key: string]: any }) => BaseHookContext;
export type HookDefaultsInitializer = (self?: any, args?: any[], context?: HookContext) => HookContextData;
export class HookManager {
_parent?: this|null = null;
_params: string[]|null = null;
_middleware: Middleware[]|null = null;
_props: HookContextData|null = null;
_defaults?: HookDefaultsInitializer;
parent (parent: this|null) {
this._parent = parent;
return this;
}
middleware (middleware?: Middleware[]) {
this._middleware = middleware?.length ? middleware : null;
return this;
}
getMiddleware (): Middleware[]|null {
const previous = this._parent?.getMiddleware();
if (previous && this._middleware) {
return previous.concat(this._middleware);
}
return previous || this._middleware;
}
collectMiddleware (self: any, _args: any[]): Middleware[] {
const otherMiddleware = getMiddleware(self);
const middleware = this.getMiddleware();
if (otherMiddleware && middleware) {
return otherMiddleware.concat(middleware);
}
return otherMiddleware || middleware || [];
}
props (props: HookContextData) {
if (!this._props) {
this._props = {};
}
copyProperties(this._props, props);
return this;
}
getProps (): HookContextData|null {
const previous = this._parent?.getProps();
if (previous && this._props) {
return copyProperties({}, previous, this._props);
}
return previous || this._props || null;
}
params (...params: string[]) {
this._params = params;
return this;
}
getParams (): string[]|null {
const previous = this._parent?.getParams();
if (previous && this._params) {
return previous.concat(this._params);
}
return previous || this._params;
}
defaults (defaults: HookDefaultsInitializer) {
this._defaults = defaults;
return this;
}
getDefaults (self: any, args: any[], context: HookContext): HookContextData|null {
const defaults = typeof this._defaults === 'function' ? this._defaults(self, args, context) : null;
const previous = this._parent?.getDefaults(self, args, context);
if (previous && defaults) {
return Object.assign({}, previous, defaults);
}
return previous || defaults;
}
getContextClass (Base: HookContextConstructor = BaseHookContext): HookContextConstructor {
const ContextClass = class ContextClass extends Base {
constructor (data: any) {
super(data);
copyToSelf(this);
}
};
const params = this.getParams();
const props = this.getProps();
if (params) {
params.forEach((name, index) => {
if (props?.[name] !== undefined) {
throw new Error(`Hooks can not have a property and param named '${name}'. Use .defaults instead.`);
}
Object.defineProperty(ContextClass.prototype, name, {
enumerable: true,
get () {
return this?.arguments[index];
},
set (value: any) {
this.arguments[index] = value;
}
});
});
}
if (props) {
copyProperties(ContextClass.prototype, props);
}
return ContextClass;
}
initializeContext (self: any, args: any[], context: HookContext): HookContext {
const ctx = this._parent ? this._parent.initializeContext(self, args, context) : context;
const defaults = this.getDefaults(self, args, ctx);
if (self) {
ctx.self = self;
}
ctx.arguments = args;
if (defaults) {
for (const name of Object.keys(defaults)) {
if (ctx[name] === undefined) {
ctx[name] = defaults[name];
}
}
}
return ctx;
}
}
export type HookOptions = HookManager|Middleware[]|null;
export function convertOptions (options: HookOptions = null) {
if (!options) {
return new HookManager()
}
return Array.isArray(options) ? new HookManager().middleware(options) : options;
}
export function getManager (target: any): HookManager|null {
return (target && target[HOOKS]) || null;
}
export function setManager<T> (target: T, manager: HookManager) {
const parent = getManager(target);
(target as any)[HOOKS] = manager.parent(parent);
return target;
}
export function getMiddleware (target: any): Middleware[]|null {
const manager = getManager(target);
return manager ? manager.getMiddleware() : null;
}
export function setMiddleware<T> (target: T, middleware: Middleware[]) {
const manager = new HookManager().middleware(middleware);
return setManager(target, manager);
}
+47
View File
@@ -0,0 +1,47 @@
// TypeScript port of koa-compose (https://github.com/koajs/compose)
export type NextFunction = () => Promise<any>;
export type Middleware<T = any> = (context: T, next: NextFunction) => Promise<any>;
export function compose<T = any> (middleware: Middleware<T>[]) {
if (!Array.isArray(middleware)) {
throw new TypeError('Middleware stack must be an array!');
}
for (const fn of middleware) {
if (typeof fn !== 'function') {
throw new TypeError('Middleware must be composed of functions!');
}
}
return function (this: any, context: T, next?: Middleware<T>) {
// last called middleware #
let index: number = -1;
return dispatch.call(this, 0);
function dispatch (this: any, i: number): Promise<any> {
if (i <= index) {
return Promise.reject(new Error('next() called multiple times'));
}
index = i;
let fn: Middleware|undefined = middleware[i];
if (i === middleware.length) {
fn = next;
}
if (!fn) {
return Promise.resolve();
}
try {
return Promise.resolve(fn.call(this, context, dispatch.bind(this, i + 1)));
} catch (err) {
return Promise.reject(err);
}
}
};
}
+115
View File
@@ -0,0 +1,115 @@
import { compose, Middleware } from './compose.ts';
import {
HookContext, setManager, HookContextData, HookOptions, convertOptions, setMiddleware
} from './base.ts';
import { copyFnProperties, copyProperties } from './utils.ts';
export function getOriginal (fn: any): any {
return typeof fn.original === 'function' ? getOriginal(fn.original) : fn;
}
export function functionHooks <F> (fn: F, managerOrMiddleware: HookOptions) {
if (typeof fn !== 'function') {
throw new Error('Can not apply hooks to non-function');
}
const manager = convertOptions(managerOrMiddleware);
const wrapper: any = function (this: any, ...args: any[]) {
const { Context, original } = wrapper;
// If we got passed an existing HookContext instance, we want to return it as well
const returnContext = args[args.length - 1] instanceof Context;
// Use existing context or default
const base = returnContext ? (args.pop() as HookContext) : new Context();
// Initialize the context
const context = manager.initializeContext(this, args, base);
// Assemble the hook chain
const hookChain: Middleware[] = [
// Return `ctx.result` or the context
(ctx, next) => next().then(() => returnContext ? ctx : ctx.result)
];
// Create the hook chain by calling the `collectMiddleware function
const mw = manager.collectMiddleware(this, args);
if (mw) {
Array.prototype.push.apply(hookChain, mw);
}
// Runs the actual original method if `ctx.result` is not already set
hookChain.push((ctx, next) => {
if (!Object.prototype.hasOwnProperty.call(context, 'result')) {
return Promise.resolve(original.apply(this, ctx.arguments)).then(result => {
ctx.result = result;
return next();
});
}
return next();
});
return compose(hookChain).call(this, context);
};
copyFnProperties(wrapper, fn);
copyProperties(wrapper, fn);
setManager(wrapper, manager);
return Object.assign(wrapper, {
original: getOriginal(fn),
Context: manager.getContextClass(),
createContext: (data: HookContextData = {}) => {
return new wrapper.Context(data);
}
});
};
export type HookMap<O = any> = {
[L in keyof O]?: HookOptions;
}
export function objectHooks (_obj: any, hooks: HookMap|Middleware[]) {
const obj = typeof _obj === 'function' ? _obj.prototype : _obj;
if (Array.isArray(hooks)) {
return setMiddleware(obj, hooks);
}
return Object.keys(hooks).reduce((result, method) => {
const fn = obj[method];
if (typeof fn !== 'function') {
throw new Error(`Can not apply hooks. '${method}' is not a function`);
}
const manager = convertOptions(hooks[method]);
result[method] = functionHooks(fn, manager.props({ method }));
return result;
}, obj);
};
export const hookDecorator = (managerOrMiddleware?: HookOptions) => {
const wrapper: any = (_target: any, method: string, descriptor: TypedPropertyDescriptor<any>): TypedPropertyDescriptor<any> => {
const manager = convertOptions(managerOrMiddleware);
if (!descriptor) {
setManager(_target.prototype, manager);
return _target;
}
const fn = descriptor.value;
if (typeof fn !== 'function') {
throw new Error(`Can not apply hooks. '${method}' is not a function`);
}
descriptor.value = functionHooks(fn, manager.props({ method }));
return descriptor;
};
return wrapper;
};
+92
View File
@@ -0,0 +1,92 @@
import { Middleware } from './compose.ts';
import {
HookManager, HookContextData, HookContext, HookContextConstructor, HookOptions
} from './base.ts';
import { functionHooks, hookDecorator, objectHooks, HookMap } from './hooks.ts';
export * from './hooks.ts';
export * from './compose.ts';
export * from './base.ts';
export interface WrapperAddon<F> {
original: F;
Context: HookContextConstructor;
createContext: (data?: HookContextData) => HookContext;
}
export type WrappedFunction<F, T> = F&((...rest: any[]) => Promise<T>|Promise<HookContext>)&WrapperAddon<F>;
export type MiddlewareOptions = {
params?: any;
defaults?: any;
props?: any;
};
/**
* Initializes a hook settings object with the given middleware.
* @param mw The list of middleware
* @param options Middleware options (params, default, props)
*/
export function middleware (mw?: Middleware[], options?: MiddlewareOptions) {
const manager = new HookManager().middleware(mw);
if (options) {
if (options.params) {
manager.params(...options.params);
}
if (options.defaults) {
manager.defaults(options.defaults);
}
if (options.props) {
manager.props(options.props);
}
}
return manager;
}
/**
* Returns a new function that wraps an existing async function
* with hooks.
*
* @param fn The async function to add hooks to.
* @param manager An array of middleware or hook settings
* (`middleware([]).params()` etc.)
*/
export function hooks<F, T = any> (
fn: F&(() => void),
manager?: HookManager
): WrappedFunction<F, T>;
/**
* Add hooks to one or more methods on an object or class.
* @param obj The object to add hooks to
* @param hookMap A map of middleware settings where the
* key is the method name.
*/
export function hooks<O> (obj: O|(new (...args: any[]) => O), hookMap: HookMap<O>|Middleware[]): O;
/**
* Decorate a class method with hooks.
* @param manager The hooks settings
*/
export function hooks<T = any> (
manager?: HookOptions
): any;
// Fallthrough to actual implementation
export function hooks (...args: any[]) {
const [ target, _hooks ] = args;
if (typeof target === 'function' && (_hooks instanceof HookManager || Array.isArray(_hooks) || args.length === 1)) {
return functionHooks(target, _hooks);
}
if (args.length === 2) {
return objectHooks(target, _hooks);
}
return hookDecorator(target);
}
+63
View File
@@ -0,0 +1,63 @@
const proto = Object.prototype as any;
// These are non-standard but offer a more reliable prototype based
// lookup for properties
const hasProtoDefinitions = typeof proto.__lookupGetter__ === 'function' &&
typeof proto.__defineGetter__ === 'function' &&
typeof proto.__defineSetter__ === 'function';
export function copyToSelf (target: any) {
// tslint:disable-next-line
for (const key in target) {
if (!target.hasOwnProperty(key)) {
const getter = hasProtoDefinitions ? target.constructor.prototype.__lookupGetter__(key)
: Object.getOwnPropertyDescriptor(target, key);
if (hasProtoDefinitions && getter) {
target.__defineGetter__(key, getter);
const setter = target.constructor.prototype.__lookupSetter__(key);
if (setter) {
target.__defineSetter__(key, setter);
}
} else if (getter) {
Object.defineProperty(target, key, getter);
} else {
target[key] = target[key];
}
}
}
}
export function copyProperties <F> (target: F, ...originals: any[]) {
for (const original of originals) {
const originalProps = (Object.keys(original) as any)
.concat(Object.getOwnPropertySymbols(original));
for (const prop of originalProps) {
const propDescriptor = Object.getOwnPropertyDescriptor(original, prop);
if (propDescriptor && !Object.prototype.hasOwnProperty.call(target, prop)) {
Object.defineProperty(target, prop, propDescriptor);
}
}
}
return target;
}
export function copyFnProperties <F> (target: F, original : any) {
const internalProps = ['name', 'length'];
try {
for (const prop of internalProps) {
const value = original[prop];
Object.defineProperty(target, prop, { value });
}
} catch (e) {
// Avoid IE error
}
return target;
}
+1
View File
File diff suppressed because one or more lines are too long
+47
View File
@@ -0,0 +1,47 @@
import { Middleware } from './compose';
export declare const HOOKS: string;
export declare type HookContextData = {
[key: string]: any;
};
/**
* The base hook context.
*/
export declare class BaseHookContext<C = any> {
self?: C;
[key: string]: any;
constructor(data?: HookContextData);
}
export interface HookContext<T = any, C = any> extends BaseHookContext<C> {
result?: T;
method?: string;
arguments: any[];
}
export declare type HookContextConstructor = new (data?: {
[key: string]: any;
}) => BaseHookContext;
export declare type HookDefaultsInitializer = (self?: any, args?: any[], context?: HookContext) => HookContextData;
export declare class HookManager {
_parent?: this | null;
_params: string[] | null;
_middleware: Middleware[] | null;
_props: HookContextData | null;
_defaults?: HookDefaultsInitializer;
parent(parent: this | null): this;
middleware(middleware?: Middleware[]): this;
getMiddleware(): Middleware[] | null;
collectMiddleware(self: any, _args: any[]): Middleware[];
props(props: HookContextData): this;
getProps(): HookContextData | null;
params(...params: string[]): this;
getParams(): string[] | null;
defaults(defaults: HookDefaultsInitializer): this;
getDefaults(self: any, args: any[], context: HookContext): HookContextData | null;
getContextClass(Base?: HookContextConstructor): HookContextConstructor;
initializeContext(self: any, args: any[], context: HookContext): HookContext;
}
export declare type HookOptions = HookManager | Middleware[] | null;
export declare function convertOptions(options?: HookOptions): HookManager;
export declare function getManager(target: any): HookManager | null;
export declare function setManager<T>(target: T, manager: HookManager): T;
export declare function getMiddleware(target: any): Middleware[] | null;
export declare function setMiddleware<T>(target: T, middleware: Middleware[]): T;
+193
View File
@@ -0,0 +1,193 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.setMiddleware = exports.getMiddleware = exports.setManager = exports.getManager = exports.convertOptions = exports.HookManager = exports.BaseHookContext = exports.HOOKS = void 0;
const utils_1 = require("./utils");
exports.HOOKS = Symbol('@feathersjs/hooks');
/**
* The base hook context.
*/
class BaseHookContext {
constructor(data = {}) {
Object.defineProperty(this, "self", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
Object.assign(this, data);
}
}
exports.BaseHookContext = BaseHookContext;
class HookManager {
constructor() {
Object.defineProperty(this, "_parent", {
enumerable: true,
configurable: true,
writable: true,
value: null
});
Object.defineProperty(this, "_params", {
enumerable: true,
configurable: true,
writable: true,
value: null
});
Object.defineProperty(this, "_middleware", {
enumerable: true,
configurable: true,
writable: true,
value: null
});
Object.defineProperty(this, "_props", {
enumerable: true,
configurable: true,
writable: true,
value: null
});
Object.defineProperty(this, "_defaults", {
enumerable: true,
configurable: true,
writable: true,
value: void 0
});
}
parent(parent) {
this._parent = parent;
return this;
}
middleware(middleware) {
this._middleware = (middleware === null || middleware === void 0 ? void 0 : middleware.length) ? middleware : null;
return this;
}
getMiddleware() {
var _a;
const previous = (_a = this._parent) === null || _a === void 0 ? void 0 : _a.getMiddleware();
if (previous && this._middleware) {
return previous.concat(this._middleware);
}
return previous || this._middleware;
}
collectMiddleware(self, _args) {
const otherMiddleware = getMiddleware(self);
const middleware = this.getMiddleware();
if (otherMiddleware && middleware) {
return otherMiddleware.concat(middleware);
}
return otherMiddleware || middleware || [];
}
props(props) {
if (!this._props) {
this._props = {};
}
utils_1.copyProperties(this._props, props);
return this;
}
getProps() {
var _a;
const previous = (_a = this._parent) === null || _a === void 0 ? void 0 : _a.getProps();
if (previous && this._props) {
return utils_1.copyProperties({}, previous, this._props);
}
return previous || this._props || null;
}
params(...params) {
this._params = params;
return this;
}
getParams() {
var _a;
const previous = (_a = this._parent) === null || _a === void 0 ? void 0 : _a.getParams();
if (previous && this._params) {
return previous.concat(this._params);
}
return previous || this._params;
}
defaults(defaults) {
this._defaults = defaults;
return this;
}
getDefaults(self, args, context) {
var _a;
const defaults = typeof this._defaults === 'function' ? this._defaults(self, args, context) : null;
const previous = (_a = this._parent) === null || _a === void 0 ? void 0 : _a.getDefaults(self, args, context);
if (previous && defaults) {
return Object.assign({}, previous, defaults);
}
return previous || defaults;
}
getContextClass(Base = BaseHookContext) {
const ContextClass = class ContextClass extends Base {
constructor(data) {
super(data);
utils_1.copyToSelf(this);
}
};
const params = this.getParams();
const props = this.getProps();
if (params) {
params.forEach((name, index) => {
if ((props === null || props === void 0 ? void 0 : props[name]) !== undefined) {
throw new Error(`Hooks can not have a property and param named '${name}'. Use .defaults instead.`);
}
Object.defineProperty(ContextClass.prototype, name, {
enumerable: true,
get() {
return this === null || this === void 0 ? void 0 : this.arguments[index];
},
set(value) {
this.arguments[index] = value;
}
});
});
}
if (props) {
utils_1.copyProperties(ContextClass.prototype, props);
}
return ContextClass;
}
initializeContext(self, args, context) {
const ctx = this._parent ? this._parent.initializeContext(self, args, context) : context;
const defaults = this.getDefaults(self, args, ctx);
if (self) {
ctx.self = self;
}
ctx.arguments = args;
if (defaults) {
for (const name of Object.keys(defaults)) {
if (ctx[name] === undefined) {
ctx[name] = defaults[name];
}
}
}
return ctx;
}
}
exports.HookManager = HookManager;
function convertOptions(options = null) {
if (!options) {
return new HookManager();
}
return Array.isArray(options) ? new HookManager().middleware(options) : options;
}
exports.convertOptions = convertOptions;
function getManager(target) {
return (target && target[exports.HOOKS]) || null;
}
exports.getManager = getManager;
function setManager(target, manager) {
const parent = getManager(target);
target[exports.HOOKS] = manager.parent(parent);
return target;
}
exports.setManager = setManager;
function getMiddleware(target) {
const manager = getManager(target);
return manager ? manager.getMiddleware() : null;
}
exports.getMiddleware = getMiddleware;
function setMiddleware(target, middleware) {
const manager = new HookManager().middleware(middleware);
return setManager(target, manager);
}
exports.setMiddleware = setMiddleware;
//# sourceMappingURL=base.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"base.js","sourceRoot":"","sources":["../src/base.ts"],"names":[],"mappings":"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"}
+3
View File
@@ -0,0 +1,3 @@
export declare type NextFunction = () => Promise<any>;
export declare type Middleware<T = any> = (context: T, next: NextFunction) => Promise<any>;
export declare function compose<T = any>(middleware: Middleware<T>[]): (this: any, context: T, next?: Middleware<T> | undefined) => Promise<any>;
+39
View File
@@ -0,0 +1,39 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.compose = void 0;
function compose(middleware) {
if (!Array.isArray(middleware)) {
throw new TypeError('Middleware stack must be an array!');
}
for (const fn of middleware) {
if (typeof fn !== 'function') {
throw new TypeError('Middleware must be composed of functions!');
}
}
return function (context, next) {
// last called middleware #
let index = -1;
return dispatch.call(this, 0);
function dispatch(i) {
if (i <= index) {
return Promise.reject(new Error('next() called multiple times'));
}
index = i;
let fn = middleware[i];
if (i === middleware.length) {
fn = next;
}
if (!fn) {
return Promise.resolve();
}
try {
return Promise.resolve(fn.call(this, context, dispatch.bind(this, i + 1)));
}
catch (err) {
return Promise.reject(err);
}
}
};
}
exports.compose = compose;
//# sourceMappingURL=compose.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"compose.js","sourceRoot":"","sources":["../src/compose.ts"],"names":[],"mappings":";;;AAKA,SAAgB,OAAO,CAAW,UAA2B;IAC3D,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE;QAC9B,MAAM,IAAI,SAAS,CAAC,oCAAoC,CAAC,CAAC;KAC3D;IAED,KAAK,MAAM,EAAE,IAAI,UAAU,EAAE;QAC3B,IAAI,OAAO,EAAE,KAAK,UAAU,EAAE;YAC5B,MAAM,IAAI,SAAS,CAAC,2CAA2C,CAAC,CAAC;SAClE;KACF;IAED,OAAO,UAAqB,OAAU,EAAE,IAAoB;QAC1D,2BAA2B;QAC3B,IAAI,KAAK,GAAW,CAAC,CAAC,CAAC;QAEvB,OAAO,QAAQ,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;QAE9B,SAAS,QAAQ,CAAa,CAAS;YACrC,IAAI,CAAC,IAAI,KAAK,EAAE;gBACd,OAAO,OAAO,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,8BAA8B,CAAC,CAAC,CAAC;aAClE;YAED,KAAK,GAAG,CAAC,CAAC;YAEV,IAAI,EAAE,GAAyB,UAAU,CAAC,CAAC,CAAC,CAAC;YAE7C,IAAI,CAAC,KAAK,UAAU,CAAC,MAAM,EAAE;gBAC3B,EAAE,GAAG,IAAI,CAAC;aACX;YAED,IAAI,CAAC,EAAE,EAAE;gBACP,OAAO,OAAO,CAAC,OAAO,EAAE,CAAC;aAC1B;YAED,IAAI;gBACF,OAAO,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,OAAO,EAAE,QAAQ,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;aAC5E;YAAC,OAAO,GAAG,EAAE;gBACZ,OAAO,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;aAC5B;QACH,CAAC;IACH,CAAC,CAAC;AACJ,CAAC;AAzCD,0BAyCC"}
+9
View File
@@ -0,0 +1,9 @@
import { Middleware } from './compose';
import { HookOptions } from './base';
export declare function getOriginal(fn: any): any;
export declare function functionHooks<F>(fn: F, managerOrMiddleware: HookOptions): any;
export declare type HookMap<O = any> = {
[L in keyof O]?: HookOptions;
};
export declare function objectHooks(_obj: any, hooks: HookMap | Middleware[]): any;
export declare const hookDecorator: (managerOrMiddleware?: HookOptions | undefined) => any;
+93
View File
@@ -0,0 +1,93 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.hookDecorator = exports.objectHooks = exports.functionHooks = exports.getOriginal = void 0;
const compose_1 = require("./compose");
const base_1 = require("./base");
const utils_1 = require("./utils");
function getOriginal(fn) {
return typeof fn.original === 'function' ? getOriginal(fn.original) : fn;
}
exports.getOriginal = getOriginal;
function functionHooks(fn, managerOrMiddleware) {
if (typeof fn !== 'function') {
throw new Error('Can not apply hooks to non-function');
}
const manager = base_1.convertOptions(managerOrMiddleware);
const wrapper = function (...args) {
const { Context, original } = wrapper;
// If we got passed an existing HookContext instance, we want to return it as well
const returnContext = args[args.length - 1] instanceof Context;
// Use existing context or default
const base = returnContext ? args.pop() : new Context();
// Initialize the context
const context = manager.initializeContext(this, args, base);
// Assemble the hook chain
const hookChain = [
// Return `ctx.result` or the context
(ctx, next) => next().then(() => returnContext ? ctx : ctx.result)
];
// Create the hook chain by calling the `collectMiddleware function
const mw = manager.collectMiddleware(this, args);
if (mw) {
Array.prototype.push.apply(hookChain, mw);
}
// Runs the actual original method if `ctx.result` is not already set
hookChain.push((ctx, next) => {
if (!Object.prototype.hasOwnProperty.call(context, 'result')) {
return Promise.resolve(original.apply(this, ctx.arguments)).then(result => {
ctx.result = result;
return next();
});
}
return next();
});
return compose_1.compose(hookChain).call(this, context);
};
utils_1.copyFnProperties(wrapper, fn);
utils_1.copyProperties(wrapper, fn);
base_1.setManager(wrapper, manager);
return Object.assign(wrapper, {
original: getOriginal(fn),
Context: manager.getContextClass(),
createContext: (data = {}) => {
return new wrapper.Context(data);
}
});
}
exports.functionHooks = functionHooks;
;
function objectHooks(_obj, hooks) {
const obj = typeof _obj === 'function' ? _obj.prototype : _obj;
if (Array.isArray(hooks)) {
return base_1.setMiddleware(obj, hooks);
}
return Object.keys(hooks).reduce((result, method) => {
const fn = obj[method];
if (typeof fn !== 'function') {
throw new Error(`Can not apply hooks. '${method}' is not a function`);
}
const manager = base_1.convertOptions(hooks[method]);
result[method] = functionHooks(fn, manager.props({ method }));
return result;
}, obj);
}
exports.objectHooks = objectHooks;
;
const hookDecorator = (managerOrMiddleware) => {
const wrapper = (_target, method, descriptor) => {
const manager = base_1.convertOptions(managerOrMiddleware);
if (!descriptor) {
base_1.setManager(_target.prototype, manager);
return _target;
}
const fn = descriptor.value;
if (typeof fn !== 'function') {
throw new Error(`Can not apply hooks. '${method}' is not a function`);
}
descriptor.value = functionHooks(fn, manager.props({ method }));
return descriptor;
};
return wrapper;
};
exports.hookDecorator = hookDecorator;
//# sourceMappingURL=hooks.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"hooks.js","sourceRoot":"","sources":["../src/hooks.ts"],"names":[],"mappings":"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"}
+44
View File
@@ -0,0 +1,44 @@
import { Middleware } from './compose';
import { HookManager, HookContextData, HookContext, HookContextConstructor, HookOptions } from './base';
import { HookMap } from './hooks';
export * from './hooks';
export * from './compose';
export * from './base';
export interface WrapperAddon<F> {
original: F;
Context: HookContextConstructor;
createContext: (data?: HookContextData) => HookContext;
}
export declare type WrappedFunction<F, T> = F & ((...rest: any[]) => Promise<T> | Promise<HookContext>) & WrapperAddon<F>;
export declare type MiddlewareOptions = {
params?: any;
defaults?: any;
props?: any;
};
/**
* Initializes a hook settings object with the given middleware.
* @param mw The list of middleware
* @param options Middleware options (params, default, props)
*/
export declare function middleware(mw?: Middleware[], options?: MiddlewareOptions): HookManager;
/**
* Returns a new function that wraps an existing async function
* with hooks.
*
* @param fn The async function to add hooks to.
* @param manager An array of middleware or hook settings
* (`middleware([]).params()` etc.)
*/
export declare function hooks<F, T = any>(fn: F & (() => void), manager?: HookManager): WrappedFunction<F, T>;
/**
* Add hooks to one or more methods on an object or class.
* @param obj The object to add hooks to
* @param hookMap A map of middleware settings where the
* key is the method name.
*/
export declare function hooks<O>(obj: O | (new (...args: any[]) => O), hookMap: HookMap<O> | Middleware[]): O;
/**
* Decorate a class method with hooks.
* @param manager The hooks settings
*/
export declare function hooks<T = any>(manager?: HookOptions): any;
+52
View File
@@ -0,0 +1,52 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __exportStar = (this && this.__exportStar) || function(m, exports) {
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.hooks = exports.middleware = void 0;
const base_1 = require("./base");
const hooks_1 = require("./hooks");
__exportStar(require("./hooks"), exports);
__exportStar(require("./compose"), exports);
__exportStar(require("./base"), exports);
/**
* Initializes a hook settings object with the given middleware.
* @param mw The list of middleware
* @param options Middleware options (params, default, props)
*/
function middleware(mw, options) {
const manager = new base_1.HookManager().middleware(mw);
if (options) {
if (options.params) {
manager.params(...options.params);
}
if (options.defaults) {
manager.defaults(options.defaults);
}
if (options.props) {
manager.props(options.props);
}
}
return manager;
}
exports.middleware = middleware;
// Fallthrough to actual implementation
function hooks(...args) {
const [target, _hooks] = args;
if (typeof target === 'function' && (_hooks instanceof base_1.HookManager || Array.isArray(_hooks) || args.length === 1)) {
return hooks_1.functionHooks(target, _hooks);
}
if (args.length === 2) {
return hooks_1.objectHooks(target, _hooks);
}
return hooks_1.hookDecorator(target);
}
exports.hooks = hooks;
//# sourceMappingURL=index.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;AACA,iCAEgB;AAChB,mCAA6E;AAE7E,0CAAwB;AACxB,4CAA0B;AAC1B,yCAAuB;AAgBvB;;;;GAIG;AACH,SAAgB,UAAU,CAAE,EAAiB,EAAE,OAA2B;IACxE,MAAM,OAAO,GAAG,IAAI,kBAAW,EAAE,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC;IAEjD,IAAI,OAAO,EAAE;QACX,IAAI,OAAO,CAAC,MAAM,EAAE;YAClB,OAAO,CAAC,MAAM,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;SACnC;QAED,IAAI,OAAO,CAAC,QAAQ,EAAE;YACpB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;SACpC;QAED,IAAI,OAAO,CAAC,KAAK,EAAE;YACjB,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;SAC9B;KACF;IAED,OAAO,OAAO,CAAC;AACjB,CAAC;AAlBD,gCAkBC;AA+BD,uCAAuC;AACvC,SAAgB,KAAK,CAAE,GAAG,IAAW;IACnC,MAAM,CAAE,MAAM,EAAE,MAAM,CAAE,GAAG,IAAI,CAAC;IAEhC,IAAI,OAAO,MAAM,KAAK,UAAU,IAAI,CAAC,MAAM,YAAY,kBAAW,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,CAAC,EAAE;QACjH,OAAO,qBAAa,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;KACtC;IAED,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE;QACrB,OAAO,mBAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;KACpC;IAED,OAAO,qBAAa,CAAC,MAAM,CAAC,CAAC;AAC/B,CAAC;AAZD,sBAYC"}
+3
View File
@@ -0,0 +1,3 @@
export declare function copyToSelf(target: any): void;
export declare function copyProperties<F>(target: F, ...originals: any[]): F;
export declare function copyFnProperties<F>(target: F, original: any): F;
+61
View File
@@ -0,0 +1,61 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.copyFnProperties = exports.copyProperties = exports.copyToSelf = void 0;
const proto = Object.prototype;
// These are non-standard but offer a more reliable prototype based
// lookup for properties
const hasProtoDefinitions = typeof proto.__lookupGetter__ === 'function' &&
typeof proto.__defineGetter__ === 'function' &&
typeof proto.__defineSetter__ === 'function';
function copyToSelf(target) {
// tslint:disable-next-line
for (const key in target) {
if (!target.hasOwnProperty(key)) {
const getter = hasProtoDefinitions ? target.constructor.prototype.__lookupGetter__(key)
: Object.getOwnPropertyDescriptor(target, key);
if (hasProtoDefinitions && getter) {
target.__defineGetter__(key, getter);
const setter = target.constructor.prototype.__lookupSetter__(key);
if (setter) {
target.__defineSetter__(key, setter);
}
}
else if (getter) {
Object.defineProperty(target, key, getter);
}
else {
target[key] = target[key];
}
}
}
}
exports.copyToSelf = copyToSelf;
function copyProperties(target, ...originals) {
for (const original of originals) {
const originalProps = Object.keys(original)
.concat(Object.getOwnPropertySymbols(original));
for (const prop of originalProps) {
const propDescriptor = Object.getOwnPropertyDescriptor(original, prop);
if (propDescriptor && !Object.prototype.hasOwnProperty.call(target, prop)) {
Object.defineProperty(target, prop, propDescriptor);
}
}
}
return target;
}
exports.copyProperties = copyProperties;
function copyFnProperties(target, original) {
const internalProps = ['name', 'length'];
try {
for (const prop of internalProps) {
const value = original[prop];
Object.defineProperty(target, prop, { value });
}
}
catch (e) {
// Avoid IE error
}
return target;
}
exports.copyFnProperties = copyFnProperties;
//# sourceMappingURL=utils.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":";;;AAAA,MAAM,KAAK,GAAG,MAAM,CAAC,SAAgB,CAAC;AACtC,mEAAmE;AACnE,wBAAwB;AACxB,MAAM,mBAAmB,GAAG,OAAO,KAAK,CAAC,gBAAgB,KAAK,UAAU;IACtE,OAAO,KAAK,CAAC,gBAAgB,KAAK,UAAU;IAC5C,OAAO,KAAK,CAAC,gBAAgB,KAAK,UAAU,CAAC;AAE/C,SAAgB,UAAU,CAAE,MAAW;IACrC,2BAA2B;IAC3B,KAAK,MAAM,GAAG,IAAI,MAAM,EAAE;QACxB,IAAI,CAAC,MAAM,CAAC,cAAc,CAAC,GAAG,CAAC,EAAE;YAC/B,MAAM,MAAM,GAAG,mBAAmB,CAAC,CAAC,CAAC,MAAM,CAAC,WAAW,CAAC,SAAS,CAAC,gBAAgB,CAAC,GAAG,CAAC;gBACrF,CAAC,CAAC,MAAM,CAAC,wBAAwB,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;YAEjD,IAAI,mBAAmB,IAAI,MAAM,EAAE;gBACjC,MAAM,CAAC,gBAAgB,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;gBAErC,MAAM,MAAM,GAAG,MAAM,CAAC,WAAW,CAAC,SAAS,CAAC,gBAAgB,CAAC,GAAG,CAAC,CAAC;gBAElE,IAAI,MAAM,EAAE;oBACV,MAAM,CAAC,gBAAgB,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;iBACtC;aACF;iBAAM,IAAI,MAAM,EAAE;gBACjB,MAAM,CAAC,cAAc,CAAC,MAAM,EAAE,GAAG,EAAE,MAAM,CAAC,CAAC;aAC5C;iBAAM;gBACL,MAAM,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;aAC3B;SACF;KACF;AACH,CAAC;AAtBD,gCAsBC;AAED,SAAgB,cAAc,CAAM,MAAS,EAAE,GAAG,SAAgB;IAChE,KAAK,MAAM,QAAQ,IAAI,SAAS,EAAE;QAChC,MAAM,aAAa,GAAI,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAS;aAC/C,MAAM,CAAC,MAAM,CAAC,qBAAqB,CAAC,QAAQ,CAAC,CAAC,CAAC;QAEpD,KAAK,MAAM,IAAI,IAAI,aAAa,EAAE;YAChC,MAAM,cAAc,GAAG,MAAM,CAAC,wBAAwB,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;YAEvE,IAAI,cAAc,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,cAAc,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,EAAE;gBACzE,MAAM,CAAC,cAAc,CAAC,MAAM,EAAE,IAAI,EAAE,cAAc,CAAC,CAAC;aACrD;SACF;KACF;IAED,OAAO,MAAM,CAAC;AAChB,CAAC;AAfD,wCAeC;AAED,SAAgB,gBAAgB,CAAM,MAAS,EAAE,QAAc;IAC7D,MAAM,aAAa,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IAEzC,IAAI;QACF,KAAK,MAAM,IAAI,IAAI,aAAa,EAAE;YAChC,MAAM,KAAK,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC;YAE7B,MAAM,CAAC,cAAc,CAAC,MAAM,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;SAChD;KACF;IAAC,OAAO,CAAC,EAAE;QACV,iBAAiB;KAClB;IAED,OAAO,MAAM,CAAC;AAChB,CAAC;AAdD,4CAcC"}
+70
View File
@@ -0,0 +1,70 @@
{
"name": "@feathersjs/hooks",
"version": "0.6.5",
"description": "Async middleware for JavaScript and TypeScript",
"homepage": "https://feathersjs.com",
"keywords": [
"feathers",
"hooks",
"hook",
"async",
"middleware"
],
"license": "MIT",
"repository": {
"type": "git",
"url": "git://github.com/feathersjs/hooks.git"
},
"author": {
"name": "Feathers contributor",
"email": "hello@feathersjs.com",
"url": "https://feathersjs.com"
},
"contributors": [],
"bugs": {
"url": "https://github.com/feathersjs/hooks/issues"
},
"engines": {
"node": ">= 10"
},
"main": "lib/",
"types": "lib/",
"scripts": {
"build:browser": "webpack --config build/webpack.config.js",
"build": "npm run compile && npm run build:browser",
"compile": "shx rm -rf lib/ && tsc",
"prepublish": "npm run build",
"mocha": "mocha --config ../../.mocharc.json --recursive test/**.test.ts test/**/*.test.ts",
"test": "npm run build && npm run mocha",
"build:deno": "shx rm -rf deno && shx mkdir -p deno && node build/deno",
"test:deno": "npm run build:deno && deno test test/deno_test.ts"
},
"directories": {
"lib": "lib"
},
"files": [
"CHANGELOG.md",
"LICENSE",
"README.md",
"src/**",
"lib/**",
"deno/**",
"dist/**"
],
"publishConfig": {
"access": "public"
},
"devDependencies": {
"@types/chai": "^4.2.16",
"@types/mocha": "^8.2.2",
"@types/node": "^14.14.41",
"mocha": "^8.3.2",
"shx": "^0.3.3",
"ts-loader": "^9.0.1",
"ts-node": "^9.1.1",
"typescript": "^4.2.4",
"webpack": "^5.34.0",
"webpack-cli": "^4.6.0"
},
"gitHead": "61776c45dd3f23a7ca51ec3a58dc23897a2fc292"
}
+213
View File
@@ -0,0 +1,213 @@
import { Middleware } from './compose';
import { copyToSelf, copyProperties } from './utils';
export const HOOKS: string = Symbol('@feathersjs/hooks') as any;
export type HookContextData = { [key: string]: any };
/**
* The base hook context.
*/
export class BaseHookContext<C = any> {
self?: C;
[key: string]: any;
constructor (data: HookContextData = {}) {
Object.assign(this, data);
}
}
export interface HookContext<T = any, C = any> extends BaseHookContext<C> {
result?: T;
method?: string;
arguments: any[];
}
export type HookContextConstructor = new (data?: { [key: string]: any }) => BaseHookContext;
export type HookDefaultsInitializer = (self?: any, args?: any[], context?: HookContext) => HookContextData;
export class HookManager {
_parent?: this|null = null;
_params: string[]|null = null;
_middleware: Middleware[]|null = null;
_props: HookContextData|null = null;
_defaults?: HookDefaultsInitializer;
parent (parent: this|null) {
this._parent = parent;
return this;
}
middleware (middleware?: Middleware[]) {
this._middleware = middleware?.length ? middleware : null;
return this;
}
getMiddleware (): Middleware[]|null {
const previous = this._parent?.getMiddleware();
if (previous && this._middleware) {
return previous.concat(this._middleware);
}
return previous || this._middleware;
}
collectMiddleware (self: any, _args: any[]): Middleware[] {
const otherMiddleware = getMiddleware(self);
const middleware = this.getMiddleware();
if (otherMiddleware && middleware) {
return otherMiddleware.concat(middleware);
}
return otherMiddleware || middleware || [];
}
props (props: HookContextData) {
if (!this._props) {
this._props = {};
}
copyProperties(this._props, props);
return this;
}
getProps (): HookContextData|null {
const previous = this._parent?.getProps();
if (previous && this._props) {
return copyProperties({}, previous, this._props);
}
return previous || this._props || null;
}
params (...params: string[]) {
this._params = params;
return this;
}
getParams (): string[]|null {
const previous = this._parent?.getParams();
if (previous && this._params) {
return previous.concat(this._params);
}
return previous || this._params;
}
defaults (defaults: HookDefaultsInitializer) {
this._defaults = defaults;
return this;
}
getDefaults (self: any, args: any[], context: HookContext): HookContextData|null {
const defaults = typeof this._defaults === 'function' ? this._defaults(self, args, context) : null;
const previous = this._parent?.getDefaults(self, args, context);
if (previous && defaults) {
return Object.assign({}, previous, defaults);
}
return previous || defaults;
}
getContextClass (Base: HookContextConstructor = BaseHookContext): HookContextConstructor {
const ContextClass = class ContextClass extends Base {
constructor (data: any) {
super(data);
copyToSelf(this);
}
};
const params = this.getParams();
const props = this.getProps();
if (params) {
params.forEach((name, index) => {
if (props?.[name] !== undefined) {
throw new Error(`Hooks can not have a property and param named '${name}'. Use .defaults instead.`);
}
Object.defineProperty(ContextClass.prototype, name, {
enumerable: true,
get () {
return this?.arguments[index];
},
set (value: any) {
this.arguments[index] = value;
}
});
});
}
if (props) {
copyProperties(ContextClass.prototype, props);
}
return ContextClass;
}
initializeContext (self: any, args: any[], context: HookContext): HookContext {
const ctx = this._parent ? this._parent.initializeContext(self, args, context) : context;
const defaults = this.getDefaults(self, args, ctx);
if (self) {
ctx.self = self;
}
ctx.arguments = args;
if (defaults) {
for (const name of Object.keys(defaults)) {
if (ctx[name] === undefined) {
ctx[name] = defaults[name];
}
}
}
return ctx;
}
}
export type HookOptions = HookManager|Middleware[]|null;
export function convertOptions (options: HookOptions = null) {
if (!options) {
return new HookManager()
}
return Array.isArray(options) ? new HookManager().middleware(options) : options;
}
export function getManager (target: any): HookManager|null {
return (target && target[HOOKS]) || null;
}
export function setManager<T> (target: T, manager: HookManager) {
const parent = getManager(target);
(target as any)[HOOKS] = manager.parent(parent);
return target;
}
export function getMiddleware (target: any): Middleware[]|null {
const manager = getManager(target);
return manager ? manager.getMiddleware() : null;
}
export function setMiddleware<T> (target: T, middleware: Middleware[]) {
const manager = new HookManager().middleware(middleware);
return setManager(target, manager);
}
+47
View File
@@ -0,0 +1,47 @@
// TypeScript port of koa-compose (https://github.com/koajs/compose)
export type NextFunction = () => Promise<any>;
export type Middleware<T = any> = (context: T, next: NextFunction) => Promise<any>;
export function compose<T = any> (middleware: Middleware<T>[]) {
if (!Array.isArray(middleware)) {
throw new TypeError('Middleware stack must be an array!');
}
for (const fn of middleware) {
if (typeof fn !== 'function') {
throw new TypeError('Middleware must be composed of functions!');
}
}
return function (this: any, context: T, next?: Middleware<T>) {
// last called middleware #
let index: number = -1;
return dispatch.call(this, 0);
function dispatch (this: any, i: number): Promise<any> {
if (i <= index) {
return Promise.reject(new Error('next() called multiple times'));
}
index = i;
let fn: Middleware|undefined = middleware[i];
if (i === middleware.length) {
fn = next;
}
if (!fn) {
return Promise.resolve();
}
try {
return Promise.resolve(fn.call(this, context, dispatch.bind(this, i + 1)));
} catch (err) {
return Promise.reject(err);
}
}
};
}
+115
View File
@@ -0,0 +1,115 @@
import { compose, Middleware } from './compose';
import {
HookContext, setManager, HookContextData, HookOptions, convertOptions, setMiddleware
} from './base';
import { copyFnProperties, copyProperties } from './utils';
export function getOriginal (fn: any): any {
return typeof fn.original === 'function' ? getOriginal(fn.original) : fn;
}
export function functionHooks <F> (fn: F, managerOrMiddleware: HookOptions) {
if (typeof fn !== 'function') {
throw new Error('Can not apply hooks to non-function');
}
const manager = convertOptions(managerOrMiddleware);
const wrapper: any = function (this: any, ...args: any[]) {
const { Context, original } = wrapper;
// If we got passed an existing HookContext instance, we want to return it as well
const returnContext = args[args.length - 1] instanceof Context;
// Use existing context or default
const base = returnContext ? (args.pop() as HookContext) : new Context();
// Initialize the context
const context = manager.initializeContext(this, args, base);
// Assemble the hook chain
const hookChain: Middleware[] = [
// Return `ctx.result` or the context
(ctx, next) => next().then(() => returnContext ? ctx : ctx.result)
];
// Create the hook chain by calling the `collectMiddleware function
const mw = manager.collectMiddleware(this, args);
if (mw) {
Array.prototype.push.apply(hookChain, mw);
}
// Runs the actual original method if `ctx.result` is not already set
hookChain.push((ctx, next) => {
if (!Object.prototype.hasOwnProperty.call(context, 'result')) {
return Promise.resolve(original.apply(this, ctx.arguments)).then(result => {
ctx.result = result;
return next();
});
}
return next();
});
return compose(hookChain).call(this, context);
};
copyFnProperties(wrapper, fn);
copyProperties(wrapper, fn);
setManager(wrapper, manager);
return Object.assign(wrapper, {
original: getOriginal(fn),
Context: manager.getContextClass(),
createContext: (data: HookContextData = {}) => {
return new wrapper.Context(data);
}
});
};
export type HookMap<O = any> = {
[L in keyof O]?: HookOptions;
}
export function objectHooks (_obj: any, hooks: HookMap|Middleware[]) {
const obj = typeof _obj === 'function' ? _obj.prototype : _obj;
if (Array.isArray(hooks)) {
return setMiddleware(obj, hooks);
}
return Object.keys(hooks).reduce((result, method) => {
const fn = obj[method];
if (typeof fn !== 'function') {
throw new Error(`Can not apply hooks. '${method}' is not a function`);
}
const manager = convertOptions(hooks[method]);
result[method] = functionHooks(fn, manager.props({ method }));
return result;
}, obj);
};
export const hookDecorator = (managerOrMiddleware?: HookOptions) => {
const wrapper: any = (_target: any, method: string, descriptor: TypedPropertyDescriptor<any>): TypedPropertyDescriptor<any> => {
const manager = convertOptions(managerOrMiddleware);
if (!descriptor) {
setManager(_target.prototype, manager);
return _target;
}
const fn = descriptor.value;
if (typeof fn !== 'function') {
throw new Error(`Can not apply hooks. '${method}' is not a function`);
}
descriptor.value = functionHooks(fn, manager.props({ method }));
return descriptor;
};
return wrapper;
};
+92
View File
@@ -0,0 +1,92 @@
import { Middleware } from './compose';
import {
HookManager, HookContextData, HookContext, HookContextConstructor, HookOptions
} from './base';
import { functionHooks, hookDecorator, objectHooks, HookMap } from './hooks';
export * from './hooks';
export * from './compose';
export * from './base';
export interface WrapperAddon<F> {
original: F;
Context: HookContextConstructor;
createContext: (data?: HookContextData) => HookContext;
}
export type WrappedFunction<F, T> = F&((...rest: any[]) => Promise<T>|Promise<HookContext>)&WrapperAddon<F>;
export type MiddlewareOptions = {
params?: any;
defaults?: any;
props?: any;
};
/**
* Initializes a hook settings object with the given middleware.
* @param mw The list of middleware
* @param options Middleware options (params, default, props)
*/
export function middleware (mw?: Middleware[], options?: MiddlewareOptions) {
const manager = new HookManager().middleware(mw);
if (options) {
if (options.params) {
manager.params(...options.params);
}
if (options.defaults) {
manager.defaults(options.defaults);
}
if (options.props) {
manager.props(options.props);
}
}
return manager;
}
/**
* Returns a new function that wraps an existing async function
* with hooks.
*
* @param fn The async function to add hooks to.
* @param manager An array of middleware or hook settings
* (`middleware([]).params()` etc.)
*/
export function hooks<F, T = any> (
fn: F&(() => void),
manager?: HookManager
): WrappedFunction<F, T>;
/**
* Add hooks to one or more methods on an object or class.
* @param obj The object to add hooks to
* @param hookMap A map of middleware settings where the
* key is the method name.
*/
export function hooks<O> (obj: O|(new (...args: any[]) => O), hookMap: HookMap<O>|Middleware[]): O;
/**
* Decorate a class method with hooks.
* @param manager The hooks settings
*/
export function hooks<T = any> (
manager?: HookOptions
): any;
// Fallthrough to actual implementation
export function hooks (...args: any[]) {
const [ target, _hooks ] = args;
if (typeof target === 'function' && (_hooks instanceof HookManager || Array.isArray(_hooks) || args.length === 1)) {
return functionHooks(target, _hooks);
}
if (args.length === 2) {
return objectHooks(target, _hooks);
}
return hookDecorator(target);
}
+63
View File
@@ -0,0 +1,63 @@
const proto = Object.prototype as any;
// These are non-standard but offer a more reliable prototype based
// lookup for properties
const hasProtoDefinitions = typeof proto.__lookupGetter__ === 'function' &&
typeof proto.__defineGetter__ === 'function' &&
typeof proto.__defineSetter__ === 'function';
export function copyToSelf (target: any) {
// tslint:disable-next-line
for (const key in target) {
if (!target.hasOwnProperty(key)) {
const getter = hasProtoDefinitions ? target.constructor.prototype.__lookupGetter__(key)
: Object.getOwnPropertyDescriptor(target, key);
if (hasProtoDefinitions && getter) {
target.__defineGetter__(key, getter);
const setter = target.constructor.prototype.__lookupSetter__(key);
if (setter) {
target.__defineSetter__(key, setter);
}
} else if (getter) {
Object.defineProperty(target, key, getter);
} else {
target[key] = target[key];
}
}
}
}
export function copyProperties <F> (target: F, ...originals: any[]) {
for (const original of originals) {
const originalProps = (Object.keys(original) as any)
.concat(Object.getOwnPropertySymbols(original));
for (const prop of originalProps) {
const propDescriptor = Object.getOwnPropertyDescriptor(original, prop);
if (propDescriptor && !Object.prototype.hasOwnProperty.call(target, prop)) {
Object.defineProperty(target, prop, propDescriptor);
}
}
}
return target;
}
export function copyFnProperties <F> (target: F, original : any) {
const internalProps = ['name', 'length'];
try {
for (const prop of internalProps) {
const value = original[prop];
Object.defineProperty(target, prop, { value });
}
} catch (e) {
// Avoid IE error
}
return target;
}