Socket
Socket
Sign inDemoInstall

@feathersjs/hooks

Package Overview
Dependencies
Maintainers
4
Versions
22
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@feathersjs/hooks - npm Package Compare versions

Comparing version 0.4.0-alpha.0 to 0.5.0-alpha.0

deno/utils.ts

8

CHANGELOG.md

@@ -6,2 +6,10 @@ # Change Log

# [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)

@@ -8,0 +16,0 @@

250

deno/base.ts
import { Middleware } from './compose.ts';
import { copyToSelf } from './utils';
export const HOOKS: string = Symbol('@feathersjs/hooks') as any;
export const CONTEXT: string = Symbol('@feathersjs/hooks/context') as any;
export function getMiddleware<T> (target: any): Middleware<T>[] {
return (target && target[HOOKS]) || [];
}
export type HookContextData = { [key: string]: any };
export type MiddlewareSetter = (currentMiddleware: Middleware[]) => Middleware[];
/**
* @param target The target object or function
* @param middleware or function
*/
export function setMiddleware<T> (target: T, middleware: Middleware[] | MiddlewareSetter) {
(target as any)[HOOKS] = typeof middleware === 'function' ? middleware(getMiddleware(target)) : middleware;
return target;
}
/**
* @param target The target object
* @param middleware or a function that takes current middleware as first argument
*/
export function registerMiddleware<T> (target: T, middleware: Middleware[]) {
return setMiddleware(target, (current: Middleware[]) => current.concat(middleware));
}
export function getContextUpdater<T> (target: any): ContextUpdater<T>[] {
return (target && target[CONTEXT]) || [];
}
/**
* @param target The target object or function
* @param updaters
*/
export function registerContextUpdater<T> (target: T, updaters: ContextUpdater[]) {
const current = getContextUpdater(target);
(target as any)[CONTEXT] = current.concat(updaters);
return target;
}
/**
* The base hook context.

@@ -56,3 +18,3 @@ */

constructor (data: { [key: string]: any } = {}) {
constructor (data: HookContextData = {}) {
Object.assign(this, data);

@@ -62,122 +24,134 @@ }

/**
* A function that updates the hook context with the `this` reference and
* arguments of the function call.
*/
export type ContextUpdater<T = any> = (self: any, fn: any, args: any[], context: HookContext<T>) => HookContext<T>;
/**
* A function that for a given function, calling context and arguments returns the list of hooks
*/
export type MiddlewareCollector<T = any> = (self: any, fn: any, args: any[]) => Middleware<T>[];
export type HookContextConstructor = new (data?: { [key: string]: any }) => HookContext;
/**
* Available options when initializing hooks with more than just an array of middleware
*/
export interface FunctionHookOptions<T = any> {
middleware: Middleware<T>[];
context: ContextUpdater<T>[];
collect: MiddlewareCollector<T>;
}
export class HookManager {
_parent?: this|null = null;
_params: string[] = [];
_middleware: Middleware[] = [];
_props: HookContextData = {};
_defaults: HookContextData|(() => HookContextData) = {};
export type HookSettings<T = any> = Middleware<T>[]|Partial<Omit<FunctionHookOptions, 'context'> & {
context: ContextUpdater<T>|ContextUpdater<T>[];
}>;
parent (parent: this) {
this._parent = parent;
export function defaultCollectMiddleware<T = any> (self: any, fn: any, args: any[]): Middleware[] {
return [
...getMiddleware<T>(self),
...(fn && typeof fn.collect === 'function' ? fn.collect(fn, fn.original, args) : getMiddleware(fn))
];
}
return this;
}
export function normalizeOptions<T = any> (opts: any): FunctionHookOptions<T> {
const options: Partial<FunctionHookOptions> = Array.isArray(opts) ? { middleware: opts } : opts;
const {
middleware = [],
context = withParams(),
collect = defaultCollectMiddleware
} = options;
middleware (middleware: Middleware[]) {
this._middleware = middleware;
const contextUpdaters = Array.isArray(context) ? context : [context];
return this;
}
return { middleware, context: contextUpdaters, collect };
}
getMiddleware (): Middleware[] {
const previous = this._parent ? this._parent.getMiddleware() : [];
export function collectContextUpdaters<T = any> (self: any, fn: any, args: any[]): ContextUpdater[] {
return [
...getContextUpdater<T>(self),
...(fn.original ? collectContextUpdaters(fn, fn.original, args) : getContextUpdater(fn))
];
}
return previous.concat(this._middleware);
}
/**
* Returns a ContextUpdater function that turns function arguments like
* `function (data, name)` into named properties (`context.data`, `context.name`)
* on the hook context
*
* @param params The list of parameter names
*/
export function withParams<T = any> (...params: (string | [string, any])[]) {
return (self: any, _fn: any, args: any[], context: HookContext<T>) => {
params.forEach((param: string | [string, any], index: number) => {
if (typeof param === 'string') {
context[param] = args[index];
return;
}
const [name, defaultValue] = param;
context[name] = args[index] === undefined ? defaultValue : args[index];
});
collectMiddleware (self: any, _args: any[]): Middleware[] {
const otherMiddleware = getMiddleware(self);
if (params.length > 0) {
Object.defineProperty(context, 'arguments', {
enumerable: true,
get (this: HookContext<T>) {
const result: any = [];
return otherMiddleware.concat(this.getMiddleware().reverse());
}
params.forEach((param, index) => {
const name = typeof param === 'string' ? param : param[0];
props (props: HookContextData) {
Object.assign(this._props, props);
Object.defineProperty(result, index, {
enumerable: true,
configurable: true,
get: () => this[name],
set: (value) => {
this[name] = value;
if (result[index] !== this[name]) {
result[index] = value;
}
}
});
return this;
}
this[name] = result[index];
});
getProps (): HookContextData {
const previous = this._parent ? this._parent.getProps() : {};
return result;
return Object.assign({}, previous, this._props);
}
params (...params: string[]) {
this._params = params;
return this;
}
getParams (): string[] {
const previous = this._parent ? this._parent.getParams() : [];
return previous.concat(this._params);
}
defaults (defaults: HookContextData|(() => HookContextData)) {
this._defaults = defaults;
return this;
}
getContextClass (Base: HookContextConstructor = HookContext): HookContextConstructor {
const ContextClass = class ContextClass extends Base {
constructor (data: any) {
super(data);
copyToSelf(this);
}
};
const params = this.getParams();
const props = this.getProps();
params.forEach((name, index) => {
Object.defineProperty(ContextClass.prototype, name, {
enumerable: true,
get () {
return this.arguments[index];
},
set (value: any) {
this.arguments[index] = value;
}
});
} else if (!context.arguments) {
context.arguments = args;
}
});
Object.seal(context.arguments);
Object.assign(ContextClass.prototype, props);
return ContextClass;
}
initializeContext (self: any, args: any[], context: HookContext): HookContext {
const ctx = this._parent ? this._parent.initializeContext(self, args, context) : context;
if (self) {
context.self = self;
ctx.self = self;
}
return context;
};
ctx.arguments = args;
return ctx;
}
}
/**
* Returns a ContextUpdater function that adds props on the hook context
*
* @param props The props object to assign
*/
export function withProps<T = any> (props: any) {
return (_self: any, _fn: any, _args: any[], context: HookContext<T>) => {
Object.assign(context, props);
export type HookOptions = HookManager|Middleware[];
return context;
};
export function convertOptions (options: HookOptions = []) {
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[] {
const manager = getManager(target);
return manager ? manager.getMiddleware() : [];
}
export function setMiddleware<T> (target: T, middleware: Middleware[]) {
const manager = new HookManager().middleware(middleware);
return setManager(target, manager);
}
import { functionHooks } from './function.ts';
import {
HookContext,
registerMiddleware,
normalizeOptions,
HookSettings, withParams
} from './base.ts';
import { setManager, HookOptions, convertOptions } from './base.ts';
export const hookDecorator = <T> (hooks: HookSettings<T> = []) => {
export const hookDecorator = (managerOrMiddleware?: HookOptions) => {
const wrapper: any = (_target: any, method: string, descriptor: TypedPropertyDescriptor<any>): TypedPropertyDescriptor<any> => {
const { context, ...options } = normalizeOptions(hooks);
const manager = convertOptions(managerOrMiddleware);
if (!descriptor) {
registerMiddleware(_target.prototype, options.middleware);
setManager(_target.prototype, manager);

@@ -25,24 +20,8 @@ return _target;

context.push((_self: any, _fn: any, _args: any[], ctx: HookContext) => {
ctx.method = method;
return ctx;
});
descriptor.value = functionHooks(fn, manager.props({ method}));
descriptor.value = functionHooks(fn, {
...options,
context
});
return descriptor;
};
function params (...args: (string | [string, any])[]): typeof wrapper {
const { context, ...options } = normalizeOptions(hooks);
return {
...options,
context: [...context, withParams(...args)]
};
}
return Object.assign(wrapper, { params });
return wrapper;
};
import { compose, Middleware } from './compose.ts';
import {
HookContext,
registerMiddleware,
registerContextUpdater,
normalizeOptions,
collectContextUpdaters,
HookSettings,
withParams
HookContext, setManager, HookContextData, HookOptions, convertOptions
} from './base.ts';
import { getOriginal, copyProperties } from './utils';
function getOriginal (fn: any): any {
return typeof fn.original === 'function' ? getOriginal(fn.original) : fn;
}
/**

@@ -26,22 +17,16 @@ * Returns a new function that is wrapped in the given hooks.

*/
export const functionHooks = <F, T = any>(original: F, opts: HookSettings<T>) => {
if (typeof original !== 'function') {
export function functionHooks <F> (fn: F, managerOrMiddleware: HookOptions) {
if (typeof fn !== 'function') {
throw new Error('Can not apply hooks to non-function');
}
const { context: updateContext, collect, middleware } = normalizeOptions(opts);
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 HookContext;
// Initialize the context. Either the default context or the one that was passed
let context: HookContext = returnContext ? args.pop() : new HookContext();
const contextUpdaters = collectContextUpdaters(this, wrapper, args);
// Initialize the context with the self reference and arguments
for (const contextUpdater of contextUpdaters) {
context = contextUpdater(this, wrapper, args, context);
}
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

@@ -52,7 +37,7 @@ const hookChain: Middleware[] = [

// Create the hook chain by calling the `collectMiddleware function
...collect(this, wrapper, args),
...manager.collectMiddleware(this, args),
// Runs the actual original method if `ctx.result` is not already set
(ctx, next) => {
if (ctx.result === undefined) {
return Promise.resolve(getOriginal(original).apply(this, ctx.arguments)).then(result => {
return Promise.resolve(original.apply(this, ctx.arguments)).then(result => {
ctx.result = result;

@@ -71,21 +56,12 @@

registerContextUpdater(wrapper, updateContext);
registerMiddleware(wrapper, middleware);
copyProperties(wrapper, fn);
setManager(wrapper, manager);
const originalProps = (Object.keys(original) as any)
.concat(Object.getOwnPropertySymbols(original));
for (const prop of originalProps) {
const propDescriptor = Object.getOwnPropertyDescriptor(original, prop);
if (!wrapper.hasOwnProperty(prop)) {
Object.defineProperty(wrapper, prop, propDescriptor);
return Object.assign(wrapper, {
original: getOriginal(fn),
Context: manager.getContextClass(),
createContext: (data: HookContextData = {}) => {
return new wrapper.Context(data);
}
}
function params (...args: (string | [string, any])[]): typeof wrapper {
return registerContextUpdater(wrapper, [withParams(...args)]);
}
return Object.assign(wrapper, { original, collect, params });
});
};
import { functionHooks } from './function.ts';
import { HookSettings } from './base.ts';
import { Middleware } from './compose.ts';
import { objectHooks, HookMap } from './object.ts';
import { hookDecorator } from './decorator.ts';
import { HookManager, HookContextData, HookContext, HookContextConstructor, HookOptions } from './base.ts';

@@ -10,16 +11,25 @@ export * from './function.ts';

export interface WrapperAddon<F, T = any> {
export interface WrapperAddon<F> {
original: F;
params: (...params: (string | [string, any])[]) => F&((...rest: any[]) => Promise<T>);
Context: HookContextConstructor;
createContext: (data?: HookContextData) => HookContext;
}
// hooks(fn, hookSettings)
export type WrappedFunction<F, T> = F&((...rest: any[]) => Promise<T>|Promise<HookContext>)&WrapperAddon<F>;
export function middleware (mw: Middleware[]) {
const manager = new HookManager();
return manager.middleware(mw);
}
// hooks(fn, hookOptions)
export function hooks<F, T = any> (
fn: F, hooks: HookSettings
): F&((...rest: any[]) => Promise<T>)&WrapperAddon<F, T>;
fn: F, manager: HookManager
): WrappedFunction<F, T>;
// hooks(object, hookMap)
export function hooks<O> (obj: O, hookMap: HookMap): O;
// @hooks(hookSettings)
export function hooks<O> (obj: O, hookMap: HookMap|Middleware[]): O;
// @hooks(hookOptions)
export function hooks<T = any> (
hooks?: HookSettings
_manager?: HookOptions
): any;

@@ -30,3 +40,3 @@ // Fallthrough to actual implementation

if (typeof target === 'function' && Array.isArray(_hooks.middleware || _hooks)) {
if (typeof target === 'function' && (_hooks instanceof HookManager || Array.isArray(_hooks))) {
return functionHooks(target, _hooks);

@@ -33,0 +43,0 @@ }

import { Middleware } from './compose.ts';
import { functionHooks } from './function.ts';
import { HookContext, registerMiddleware, normalizeOptions, HookSettings } from './base.ts';
import { setMiddleware, convertOptions, HookOptions } from './base.ts';
export interface HookMap {
[key: string]: HookSettings;
[key: string]: HookOptions;
}
export const objectHooks = (_obj: any, hooks: HookMap|Middleware[]) => {
export function objectHooks (_obj: any, hooks: HookMap|Middleware[]) {
const obj = typeof _obj === 'function' ? _obj.prototype : _obj;
if (Array.isArray(hooks)) {
return registerMiddleware(obj, hooks);
return setMiddleware(obj, hooks);
}
return Object.keys(hooks).reduce((result, method) => {
const value = obj[method];
const { context, ...options } = normalizeOptions(hooks[method]);
const fn = obj[method];
if (typeof value !== 'function') {
if (typeof fn !== 'function') {
throw new Error(`Can not apply hooks. '${method}' is not a function`);
}
context.push((_self: any, _fn: any, _args: any[], ctx: HookContext) => {
ctx.method = method;
return ctx;
});
const manager = convertOptions(hooks[method]);
result[method] = functionHooks(value, {
...options,
context
});
result[method] = functionHooks(fn, manager.props({ method }));

@@ -34,0 +27,0 @@ return result;

@@ -1,1 +0,1 @@

!function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.hooks=e():t.hooks=e()}(this,(function(){return function(t){var e={};function o(n){if(e[n])return e[n].exports;var r=e[n]={i:n,l:!1,exports:{}};return t[n].call(r.exports,r,r.exports,o),r.l=!0,r.exports}return o.m=t,o.c=e,o.d=function(t,e,n){o.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:n})},o.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},o.t=function(t,e){if(1&e&&(t=o(t)),8&e)return t;if(4&e&&"object"==typeof t&&t&&t.__esModule)return t;var n=Object.create(null);if(o.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var r in t)o.d(n,r,function(e){return t[e]}.bind(null,r));return n},o.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return o.d(e,"a",e),e},o.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},o.p="",o(o.s=3)}([function(t,e,o){"use strict";Object.defineProperty(e,"__esModule",{value:!0});const n=o(2),r=o(1);e.functionHooks=(t,e)=>{if("function"!=typeof t)throw new Error("Can not apply hooks to non-function");const{context:o,collect:i,middleware:c}=r.normalizeOptions(e),s=function(...e){const o=e[e.length-1]instanceof r.HookContext;let c=o?e.pop():new r.HookContext;const u=r.collectContextUpdaters(this,s,e);for(const t of u)c=t(this,s,e,c);const a=[(t,e)=>e().then(()=>o?t:t.result),...i(this,s,e),(e,o)=>void 0===e.result?Promise.resolve(function t(e){return"function"==typeof e.original?t(e.original):e}(t).apply(this,e.arguments)).then(t=>(e.result=t,o())):o()];return n.compose(a).call(this,c)};r.registerContextUpdater(s,o),r.registerMiddleware(s,c);const u=Object.keys(t).concat(Object.getOwnPropertySymbols(t));for(const e of u){const o=Object.getOwnPropertyDescriptor(t,e);s.hasOwnProperty(e)||Object.defineProperty(s,e,o)}return Object.assign(s,{original:t,collect:i,params:function(...t){return r.registerContextUpdater(s,[r.withParams(...t)])}})}},function(t,e,o){"use strict";function n(t){return t&&t[e.HOOKS]||[]}function r(t,o){return t[e.HOOKS]="function"==typeof o?o(n(t)):o,t}function i(t){return t&&t[e.CONTEXT]||[]}Object.defineProperty(e,"__esModule",{value:!0}),e.HOOKS=Symbol("@feathersjs/hooks"),e.CONTEXT=Symbol("@feathersjs/hooks/context"),e.getMiddleware=n,e.setMiddleware=r,e.registerMiddleware=function(t,e){return r(t,t=>t.concat(e))},e.getContextUpdater=i,e.registerContextUpdater=function(t,o){const n=i(t);return t[e.CONTEXT]=n.concat(o),t};function c(t,e,o){return[...n(t),...e&&"function"==typeof e.collect?e.collect(e,e.original,o):n(e)]}function s(...t){return(e,o,n,r)=>(t.forEach((t,e)=>{if("string"==typeof t)return void(r[t]=n[e]);const[o,i]=t;r[o]=void 0===n[e]?i:n[e]}),t.length>0?Object.defineProperty(r,"arguments",{enumerable:!0,get(){const e=[];return t.forEach((t,o)=>{const n="string"==typeof t?t:t[0];Object.defineProperty(e,o,{enumerable:!0,configurable:!0,get:()=>this[n],set:t=>{this[n]=t,e[o]!==this[n]&&(e[o]=t)}}),this[n]=e[o]}),e}}):r.arguments||(r.arguments=n),Object.seal(r.arguments),e&&(r.self=e),r)}e.HookContext=class{constructor(t={}){Object.assign(this,t)}},e.defaultCollectMiddleware=c,e.normalizeOptions=function(t){const e=Array.isArray(t)?{middleware:t}:t,{middleware:o=[],context:n=s(),collect:r=c}=e;return{middleware:o,context:Array.isArray(n)?n:[n],collect:r}},e.collectContextUpdaters=function t(e,o,n){return[...i(e),...o.original?t(o,o.original,n):i(o)]},e.withParams=s,e.withProps=function(t){return(e,o,n,r)=>(Object.assign(r,t),r)}},function(t,e,o){"use strict";Object.defineProperty(e,"__esModule",{value:!0}),e.compose=function(t){if(!Array.isArray(t))throw new TypeError("Middleware stack must be an array!");for(const e of t)if("function"!=typeof e)throw new TypeError("Middleware must be composed of functions!");return function(e,o){let n=-1;return function r(i){if(i<=n)return Promise.reject(new Error("next() called multiple times"));n=i;let c=t[i];i===t.length&&(c=o);if(!c)return Promise.resolve();try{return Promise.resolve(c.call(this,e,r.bind(this,i+1)))}catch(t){return Promise.reject(t)}}.call(this,0)}}},function(t,e,o){"use strict";function n(t){for(var o in t)e.hasOwnProperty(o)||(e[o]=t[o])}Object.defineProperty(e,"__esModule",{value:!0});const r=o(0),i=o(4),c=o(5);n(o(0)),n(o(2)),n(o(1)),e.hooks=function(...t){const[e,o]=t;return"function"==typeof e&&Array.isArray(o.middleware||o)?r.functionHooks(e,o):2===t.length?i.objectHooks(e,o):c.hookDecorator(e)}},function(t,e,o){"use strict";Object.defineProperty(e,"__esModule",{value:!0});const n=o(0),r=o(1);e.objectHooks=(t,e)=>{const o="function"==typeof t?t.prototype:t;return Array.isArray(e)?r.registerMiddleware(o,e):Object.keys(e).reduce((t,i)=>{const c=o[i],{context:s,...u}=r.normalizeOptions(e[i]);if("function"!=typeof c)throw new Error(`Can not apply hooks. '${i}' is not a function`);return s.push((t,e,o,n)=>(n.method=i,n)),t[i]=n.functionHooks(c,{...u,context:s}),t},o)}},function(t,e,o){"use strict";Object.defineProperty(e,"__esModule",{value:!0});const n=o(0),r=o(1);e.hookDecorator=(t=[])=>Object.assign((e,o,i)=>{const{context:c,...s}=r.normalizeOptions(t);if(!i)return r.registerMiddleware(e.prototype,s.middleware),e;const u=i.value;if("function"!=typeof u)throw new Error(`Can not apply hooks. '${o}' is not a function`);return c.push((t,e,n,r)=>(r.method=o,r)),i.value=n.functionHooks(u,{...s,context:c}),i},{params:function(...e){const{context:o,...n}=r.normalizeOptions(t);return{...n,context:[...o,r.withParams(...e)]}}})}])}));
!function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.hooks=e():t.hooks=e()}(this,(function(){return function(t){var e={};function r(n){if(e[n])return e[n].exports;var o=e[n]={i:n,l:!1,exports:{}};return t[n].call(o.exports,o,o.exports,r),o.l=!0,o.exports}return r.m=t,r.c=e,r.d=function(t,e,n){r.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:n})},r.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},r.t=function(t,e){if(1&e&&(t=r(t)),8&e)return t;if(4&e&&"object"==typeof t&&t&&t.__esModule)return t;var n=Object.create(null);if(r.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var o in t)r.d(n,o,function(e){return t[e]}.bind(null,o));return n},r.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return r.d(e,"a",e),e},r.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},r.p="",r(r.s=4)}([function(t,e,r){"use strict";Object.defineProperty(e,"__esModule",{value:!0});const n=r(3);e.HOOKS=Symbol("@feathersjs/hooks");class o{constructor(t={}){Object.assign(this,t)}}e.HookContext=o;class i{constructor(){this._parent=null,this._params=[],this._middleware=[],this._props={},this._defaults={}}parent(t){return this._parent=t,this}middleware(t){return this._middleware=t,this}getMiddleware(){return(this._parent?this._parent.getMiddleware():[]).concat(this._middleware)}collectMiddleware(t,e){return u(t).concat(this.getMiddleware().reverse())}props(t){return Object.assign(this._props,t),this}getProps(){const t=this._parent?this._parent.getProps():{};return Object.assign({},t,this._props)}params(...t){return this._params=t,this}getParams(){return(this._parent?this._parent.getParams():[]).concat(this._params)}defaults(t){return this._defaults=t,this}getContextClass(t=o){const e=class extends t{constructor(t){super(t),n.copyToSelf(this)}},r=this.getParams(),i=this.getProps();return r.forEach((t,r)=>{Object.defineProperty(e.prototype,t,{enumerable:!0,get(){return this.arguments[r]},set(t){this.arguments[r]=t}})}),Object.assign(e.prototype,i),e}initializeContext(t,e,r){const n=this._parent?this._parent.initializeContext(t,e,r):r;return t&&(n.self=t),n.arguments=e,n}}function s(t){return t&&t[e.HOOKS]||null}function c(t,r){const n=s(t);return t[e.HOOKS]=r.parent(n),t}function u(t){const e=s(t);return e?e.getMiddleware():[]}e.HookManager=i,e.convertOptions=function(t=[]){return Array.isArray(t)?(new i).middleware(t):t},e.getManager=s,e.setManager=c,e.getMiddleware=u,e.setMiddleware=function(t,e){return c(t,(new i).middleware(e))}},function(t,e,r){"use strict";Object.defineProperty(e,"__esModule",{value:!0});const n=r(2),o=r(0),i=r(3);e.functionHooks=function(t,e){if("function"!=typeof t)throw new Error("Can not apply hooks to non-function");const r=o.convertOptions(e),s=function(...t){const{Context:e,original:o}=s,i=t[t.length-1]instanceof e,c=i?t.pop():new e,u=r.initializeContext(this,t,c),a=[(t,e)=>e().then(()=>i?t:t.result),...r.collectMiddleware(this,t),(t,e)=>void 0===t.result?Promise.resolve(o.apply(this,t.arguments)).then(r=>(t.result=r,e())):e()];return n.compose(a).call(this,u)};return i.copyProperties(s,t),o.setManager(s,r),Object.assign(s,{original:i.getOriginal(t),Context:r.getContextClass(),createContext:(t={})=>new s.Context(t)})}},function(t,e,r){"use strict";Object.defineProperty(e,"__esModule",{value:!0}),e.compose=function(t){if(!Array.isArray(t))throw new TypeError("Middleware stack must be an array!");for(const e of t)if("function"!=typeof e)throw new TypeError("Middleware must be composed of functions!");return function(e,r){let n=-1;return function o(i){if(i<=n)return Promise.reject(new Error("next() called multiple times"));n=i;let s=t[i];i===t.length&&(s=r);if(!s)return Promise.resolve();try{return Promise.resolve(s.call(this,e,o.bind(this,i+1)))}catch(t){return Promise.reject(t)}}.call(this,0)}}},function(t,e,r){"use strict";Object.defineProperty(e,"__esModule",{value:!0}),e.getOriginal=function t(e){return"function"==typeof e.original?t(e.original):e},e.copyProperties=function(t,e){const r=Object.keys(e).concat(Object.getOwnPropertySymbols(e));for(const n of r){const r=Object.getOwnPropertyDescriptor(e,n);t.hasOwnProperty(n)||Object.defineProperty(t,n,r)}return t};const n=Object.prototype,o="function"==typeof n.__lookupGetter__&&"function"==typeof n.__defineGetter__&&"function"==typeof n.__defineSetter__;e.copyToSelf=function(t){for(const e in t)if(!t.hasOwnProperty(e)){const r=o?t.constructor.prototype.__lookupGetter__(e):Object.getOwnPropertyDescriptor(t,e);r&&o?(t.__defineGetter__(e,r),t.__defineSetter__(e,t.constructor.prototype.__lookupSetter__(e))):r?Object.defineProperty(t,e,r):t[e]=t[e]}}},function(t,e,r){"use strict";function n(t){for(var r in t)e.hasOwnProperty(r)||(e[r]=t[r])}Object.defineProperty(e,"__esModule",{value:!0});const o=r(1),i=r(5),s=r(6),c=r(0);n(r(1)),n(r(2)),n(r(0)),e.middleware=function(t){return(new c.HookManager).middleware(t)},e.hooks=function(...t){const[e,r]=t;return"function"==typeof e&&(r instanceof c.HookManager||Array.isArray(r))?o.functionHooks(e,r):2===t.length?i.objectHooks(e,r):s.hookDecorator(e)}},function(t,e,r){"use strict";Object.defineProperty(e,"__esModule",{value:!0});const n=r(1),o=r(0);e.objectHooks=function(t,e){const r="function"==typeof t?t.prototype:t;return Array.isArray(e)?o.setMiddleware(r,e):Object.keys(e).reduce((t,i)=>{const s=r[i];if("function"!=typeof s)throw new Error(`Can not apply hooks. '${i}' is not a function`);const c=o.convertOptions(e[i]);return t[i]=n.functionHooks(s,c.props({method:i})),t},r)}},function(t,e,r){"use strict";Object.defineProperty(e,"__esModule",{value:!0});const n=r(1),o=r(0);e.hookDecorator=t=>(e,r,i)=>{const s=o.convertOptions(t);if(!i)return o.setManager(e.prototype,s),e;const c=i.value;if("function"!=typeof c)throw new Error(`Can not apply hooks. '${r}' is not a function`);return i.value=n.functionHooks(c,s.props({method:r})),i}}])}));
import { Middleware } from './compose';
export declare const HOOKS: string;
export declare const CONTEXT: string;
export declare function getMiddleware<T>(target: any): Middleware<T>[];
export declare type MiddlewareSetter = (currentMiddleware: Middleware[]) => Middleware[];
export declare type HookContextData = {
[key: string]: any;
};
/**
* @param target The target object or function
* @param middleware or function
*/
export declare function setMiddleware<T>(target: T, middleware: Middleware[] | MiddlewareSetter): T;
/**
* @param target The target object
* @param middleware or a function that takes current middleware as first argument
*/
export declare function registerMiddleware<T>(target: T, middleware: Middleware[]): T;
export declare function getContextUpdater<T>(target: any): ContextUpdater<T>[];
/**
* @param target The target object or function
* @param updaters
*/
export declare function registerContextUpdater<T>(target: T, updaters: ContextUpdater[]): T;
/**
* The base hook context.

@@ -31,42 +15,30 @@ */

[key: string]: any;
constructor(data?: {
[key: string]: any;
});
constructor(data?: HookContextData);
}
/**
* A function that updates the hook context with the `this` reference and
* arguments of the function call.
*/
export declare type ContextUpdater<T = any> = (self: any, fn: any, args: any[], context: HookContext<T>) => HookContext<T>;
/**
* A function that for a given function, calling context and arguments returns the list of hooks
*/
export declare type MiddlewareCollector<T = any> = (self: any, fn: any, args: any[]) => Middleware<T>[];
/**
* Available options when initializing hooks with more than just an array of middleware
*/
export interface FunctionHookOptions<T = any> {
middleware: Middleware<T>[];
context: ContextUpdater<T>[];
collect: MiddlewareCollector<T>;
export declare type HookContextConstructor = new (data?: {
[key: string]: any;
}) => HookContext;
export declare class HookManager {
_parent?: this | null;
_params: string[];
_middleware: Middleware[];
_props: HookContextData;
_defaults: HookContextData | (() => HookContextData);
parent(parent: this): this;
middleware(middleware: Middleware[]): this;
getMiddleware(): Middleware[];
collectMiddleware(self: any, _args: any[]): Middleware[];
props(props: HookContextData): this;
getProps(): HookContextData;
params(...params: string[]): this;
getParams(): string[];
defaults(defaults: HookContextData | (() => HookContextData)): this;
getContextClass(Base?: HookContextConstructor): HookContextConstructor;
initializeContext(self: any, args: any[], context: HookContext): HookContext;
}
export declare type HookSettings<T = any> = Middleware<T>[] | Partial<Omit<FunctionHookOptions, 'context'> & {
context: ContextUpdater<T> | ContextUpdater<T>[];
}>;
export declare function defaultCollectMiddleware<T = any>(self: any, fn: any, args: any[]): Middleware[];
export declare function normalizeOptions<T = any>(opts: any): FunctionHookOptions<T>;
export declare function collectContextUpdaters<T = any>(self: any, fn: any, args: any[]): ContextUpdater[];
/**
* Returns a ContextUpdater function that turns function arguments like
* `function (data, name)` into named properties (`context.data`, `context.name`)
* on the hook context
*
* @param params The list of parameter names
*/
export declare function withParams<T = any>(...params: (string | [string, any])[]): (self: any, _fn: any, args: any[], context: HookContext<T, any>) => HookContext<T, any>;
/**
* Returns a ContextUpdater function that adds props on the hook context
*
* @param props The props object to assign
*/
export declare function withProps<T = any>(props: any): (_self: any, _fn: any, _args: any[], context: HookContext<T, any>) => HookContext<T, any>;
export declare type HookOptions = HookManager | Middleware[];
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[];
export declare function setMiddleware<T>(target: T, middleware: Middleware[]): T;
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const utils_1 = require("./utils");
exports.HOOKS = Symbol('@feathersjs/hooks');
exports.CONTEXT = Symbol('@feathersjs/hooks/context');
function getMiddleware(target) {
return (target && target[exports.HOOKS]) || [];
}
exports.getMiddleware = getMiddleware;
/**
* @param target The target object or function
* @param middleware or function
*/
function setMiddleware(target, middleware) {
target[exports.HOOKS] = typeof middleware === 'function' ? middleware(getMiddleware(target)) : middleware;
return target;
}
exports.setMiddleware = setMiddleware;
/**
* @param target The target object
* @param middleware or a function that takes current middleware as first argument
*/
function registerMiddleware(target, middleware) {
return setMiddleware(target, (current) => current.concat(middleware));
}
exports.registerMiddleware = registerMiddleware;
function getContextUpdater(target) {
return (target && target[exports.CONTEXT]) || [];
}
exports.getContextUpdater = getContextUpdater;
/**
* @param target The target object or function
* @param updaters
*/
function registerContextUpdater(target, updaters) {
const current = getContextUpdater(target);
target[exports.CONTEXT] = current.concat(updaters);
return target;
}
exports.registerContextUpdater = registerContextUpdater;
/**
* The base hook context.

@@ -49,87 +14,103 @@ */

exports.HookContext = HookContext;
function defaultCollectMiddleware(self, fn, args) {
return [
...getMiddleware(self),
...(fn && typeof fn.collect === 'function' ? fn.collect(fn, fn.original, args) : getMiddleware(fn))
];
}
exports.defaultCollectMiddleware = defaultCollectMiddleware;
function normalizeOptions(opts) {
const options = Array.isArray(opts) ? { middleware: opts } : opts;
const { middleware = [], context = withParams(), collect = defaultCollectMiddleware } = options;
const contextUpdaters = Array.isArray(context) ? context : [context];
return { middleware, context: contextUpdaters, collect };
}
exports.normalizeOptions = normalizeOptions;
function collectContextUpdaters(self, fn, args) {
return [
...getContextUpdater(self),
...(fn.original ? collectContextUpdaters(fn, fn.original, args) : getContextUpdater(fn))
];
}
exports.collectContextUpdaters = collectContextUpdaters;
/**
* Returns a ContextUpdater function that turns function arguments like
* `function (data, name)` into named properties (`context.data`, `context.name`)
* on the hook context
*
* @param params The list of parameter names
*/
function withParams(...params) {
return (self, _fn, args, context) => {
params.forEach((param, index) => {
if (typeof param === 'string') {
context[param] = args[index];
return;
class HookManager {
constructor() {
this._parent = null;
this._params = [];
this._middleware = [];
this._props = {};
this._defaults = {};
}
parent(parent) {
this._parent = parent;
return this;
}
middleware(middleware) {
this._middleware = middleware;
return this;
}
getMiddleware() {
const previous = this._parent ? this._parent.getMiddleware() : [];
return previous.concat(this._middleware);
}
collectMiddleware(self, _args) {
const otherMiddleware = getMiddleware(self);
return otherMiddleware.concat(this.getMiddleware().reverse());
}
props(props) {
Object.assign(this._props, props);
return this;
}
getProps() {
const previous = this._parent ? this._parent.getProps() : {};
return Object.assign({}, previous, this._props);
}
params(...params) {
this._params = params;
return this;
}
getParams() {
const previous = this._parent ? this._parent.getParams() : [];
return previous.concat(this._params);
}
defaults(defaults) {
this._defaults = defaults;
return this;
}
getContextClass(Base = HookContext) {
const ContextClass = class ContextClass extends Base {
constructor(data) {
super(data);
utils_1.copyToSelf(this);
}
const [name, defaultValue] = param;
context[name] = args[index] === undefined ? defaultValue : args[index];
});
if (params.length > 0) {
Object.defineProperty(context, 'arguments', {
};
const params = this.getParams();
const props = this.getProps();
params.forEach((name, index) => {
Object.defineProperty(ContextClass.prototype, name, {
enumerable: true,
get() {
const result = [];
params.forEach((param, index) => {
const name = typeof param === 'string' ? param : param[0];
Object.defineProperty(result, index, {
enumerable: true,
configurable: true,
get: () => this[name],
set: (value) => {
this[name] = value;
if (result[index] !== this[name]) {
result[index] = value;
}
}
});
this[name] = result[index];
});
return result;
return this.arguments[index];
},
set(value) {
this.arguments[index] = value;
}
});
}
else if (!context.arguments) {
context.arguments = args;
}
Object.seal(context.arguments);
});
Object.assign(ContextClass.prototype, props);
return ContextClass;
}
initializeContext(self, args, context) {
const ctx = this._parent ? this._parent.initializeContext(self, args, context) : context;
if (self) {
context.self = self;
ctx.self = self;
}
return context;
};
ctx.arguments = args;
return ctx;
}
}
exports.withParams = withParams;
/**
* Returns a ContextUpdater function that adds props on the hook context
*
* @param props The props object to assign
*/
function withProps(props) {
return (_self, _fn, _args, context) => {
Object.assign(context, props);
return context;
};
exports.HookManager = HookManager;
function convertOptions(options = []) {
return Array.isArray(options) ? new HookManager().middleware(options) : options;
}
exports.withProps = withProps;
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() : [];
}
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,2 +0,2 @@

import { HookSettings } from './base';
export declare const hookDecorator: <T>(hooks?: HookSettings<T>) => any;
import { HookOptions } from './base';
export declare const hookDecorator: (managerOrMiddleware?: HookOptions) => any;

@@ -5,7 +5,7 @@ "use strict";

const base_1 = require("./base");
exports.hookDecorator = (hooks = []) => {
exports.hookDecorator = (managerOrMiddleware) => {
const wrapper = (_target, method, descriptor) => {
const { context, ...options } = base_1.normalizeOptions(hooks);
const manager = base_1.convertOptions(managerOrMiddleware);
if (!descriptor) {
base_1.registerMiddleware(_target.prototype, options.middleware);
base_1.setManager(_target.prototype, manager);
return _target;

@@ -17,21 +17,7 @@ }

}
context.push((_self, _fn, _args, ctx) => {
ctx.method = method;
return ctx;
});
descriptor.value = function_1.functionHooks(fn, {
...options,
context
});
descriptor.value = function_1.functionHooks(fn, manager.props({ method }));
return descriptor;
};
function params(...args) {
const { context, ...options } = base_1.normalizeOptions(hooks);
return {
...options,
context: [...context, base_1.withParams(...args)]
};
}
return Object.assign(wrapper, { params });
return wrapper;
};
//# sourceMappingURL=decorator.js.map

@@ -1,2 +0,2 @@

import { HookSettings } from './base';
import { HookOptions } from './base';
/**

@@ -12,2 +12,2 @@ * Returns a new function that is wrapped in the given hooks.

*/
export declare const functionHooks: <F, T = any>(original: F, opts: HookSettings<T>) => any;
export declare function functionHooks<F>(fn: F, managerOrMiddleware: HookOptions): any;

@@ -5,5 +5,3 @@ "use strict";

const base_1 = require("./base");
function getOriginal(fn) {
return typeof fn.original === 'function' ? getOriginal(fn.original) : fn;
}
const utils_1 = require("./utils");
/**

@@ -19,17 +17,15 @@ * Returns a new function that is wrapped in the given hooks.

*/
exports.functionHooks = (original, opts) => {
if (typeof original !== 'function') {
function functionHooks(fn, managerOrMiddleware) {
if (typeof fn !== 'function') {
throw new Error('Can not apply hooks to non-function');
}
const { context: updateContext, collect, middleware } = base_1.normalizeOptions(opts);
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 base_1.HookContext;
// Initialize the context. Either the default context or the one that was passed
let context = returnContext ? args.pop() : new base_1.HookContext();
const contextUpdaters = base_1.collectContextUpdaters(this, wrapper, args);
// Initialize the context with the self reference and arguments
for (const contextUpdater of contextUpdaters) {
context = contextUpdater(this, wrapper, args, context);
}
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

@@ -40,7 +36,7 @@ const hookChain = [

// Create the hook chain by calling the `collectMiddleware function
...collect(this, wrapper, args),
...manager.collectMiddleware(this, args),
// Runs the actual original method if `ctx.result` is not already set
(ctx, next) => {
if (ctx.result === undefined) {
return Promise.resolve(getOriginal(original).apply(this, ctx.arguments)).then(result => {
return Promise.resolve(original.apply(this, ctx.arguments)).then(result => {
ctx.result = result;

@@ -55,17 +51,14 @@ return next();

};
base_1.registerContextUpdater(wrapper, updateContext);
base_1.registerMiddleware(wrapper, middleware);
const originalProps = Object.keys(original)
.concat(Object.getOwnPropertySymbols(original));
for (const prop of originalProps) {
const propDescriptor = Object.getOwnPropertyDescriptor(original, prop);
if (!wrapper.hasOwnProperty(prop)) {
Object.defineProperty(wrapper, prop, propDescriptor);
utils_1.copyProperties(wrapper, fn);
base_1.setManager(wrapper, manager);
return Object.assign(wrapper, {
original: utils_1.getOriginal(fn),
Context: manager.getContextClass(),
createContext: (data = {}) => {
return new wrapper.Context(data);
}
}
function params(...args) {
return base_1.registerContextUpdater(wrapper, [base_1.withParams(...args)]);
}
return Object.assign(wrapper, { original, collect, params });
};
});
}
exports.functionHooks = functionHooks;
;
//# sourceMappingURL=function.js.map

@@ -1,12 +0,16 @@

import { HookSettings } from './base';
import { Middleware } from './compose';
import { HookMap } from './object';
import { HookManager, HookContextData, HookContext, HookContextConstructor, HookOptions } from './base';
export * from './function';
export * from './compose';
export * from './base';
export interface WrapperAddon<F, T = any> {
export interface WrapperAddon<F> {
original: F;
params: (...params: (string | [string, any])[]) => F & ((...rest: any[]) => Promise<T>);
Context: HookContextConstructor;
createContext: (data?: HookContextData) => HookContext;
}
export declare function hooks<F, T = any>(fn: F, hooks: HookSettings): F & ((...rest: any[]) => Promise<T>) & WrapperAddon<F, T>;
export declare function hooks<O>(obj: O, hookMap: HookMap): O;
export declare function hooks<T = any>(hooks?: HookSettings): any;
export declare type WrappedFunction<F, T> = F & ((...rest: any[]) => Promise<T> | Promise<HookContext>) & WrapperAddon<F>;
export declare function middleware(mw: Middleware[]): HookManager;
export declare function hooks<F, T = any>(fn: F, manager: HookManager): WrappedFunction<F, T>;
export declare function hooks<O>(obj: O, hookMap: HookMap | Middleware[]): O;
export declare function hooks<T = any>(_manager?: HookOptions): any;

@@ -9,9 +9,15 @@ "use strict";

const decorator_1 = require("./decorator");
const base_1 = require("./base");
__export(require("./function"));
__export(require("./compose"));
__export(require("./base"));
function middleware(mw) {
const manager = new base_1.HookManager();
return manager.middleware(mw);
}
exports.middleware = middleware;
// Fallthrough to actual implementation
function hooks(...args) {
const [target, _hooks] = args;
if (typeof target === 'function' && Array.isArray(_hooks.middleware || _hooks)) {
if (typeof target === 'function' && (_hooks instanceof base_1.HookManager || Array.isArray(_hooks))) {
return function_1.functionHooks(target, _hooks);

@@ -18,0 +24,0 @@ }

import { Middleware } from './compose';
import { HookSettings } from './base';
import { HookOptions } from './base';
export interface HookMap {
[key: string]: HookSettings;
[key: string]: HookOptions;
}
export declare const objectHooks: (_obj: any, hooks: HookMap | Middleware<any>[]) => any;
export declare function objectHooks(_obj: any, hooks: HookMap | Middleware[]): any;

@@ -5,24 +5,19 @@ "use strict";

const base_1 = require("./base");
exports.objectHooks = (_obj, hooks) => {
function objectHooks(_obj, hooks) {
const obj = typeof _obj === 'function' ? _obj.prototype : _obj;
if (Array.isArray(hooks)) {
return base_1.registerMiddleware(obj, hooks);
return base_1.setMiddleware(obj, hooks);
}
return Object.keys(hooks).reduce((result, method) => {
const value = obj[method];
const { context, ...options } = base_1.normalizeOptions(hooks[method]);
if (typeof value !== 'function') {
const fn = obj[method];
if (typeof fn !== 'function') {
throw new Error(`Can not apply hooks. '${method}' is not a function`);
}
context.push((_self, _fn, _args, ctx) => {
ctx.method = method;
return ctx;
});
result[method] = function_1.functionHooks(value, {
...options,
context
});
const manager = base_1.convertOptions(hooks[method]);
result[method] = function_1.functionHooks(fn, manager.props({ method }));
return result;
}, obj);
};
}
exports.objectHooks = objectHooks;
;
//# sourceMappingURL=object.js.map
{
"name": "@feathersjs/hooks",
"version": "0.4.0-alpha.0",
"version": "0.5.0-alpha.0",
"description": "Async middleware for JavaScript and TypeScript",

@@ -38,3 +38,3 @@ "homepage": "https://feathersjs.com",

"prepublish": "npm run build",
"test": "mocha --opts ../../mocha.opts --recursive test/**.test.ts test/**/*.test.ts"
"test": "mocha --config ../../.mocharc.json --recursive test/**.test.ts test/**/*.test.ts"
},

@@ -59,3 +59,3 @@ "directories": {

},
"gitHead": "8762f76abf27fe1209f75bb27fa8aa846b65c16d"
"gitHead": "b31bf55f1cd59620dcb4f3a41b4141557841c551"
}
import { Middleware } from './compose';
import { copyToSelf } from './utils';
export const HOOKS: string = Symbol('@feathersjs/hooks') as any;
export const CONTEXT: string = Symbol('@feathersjs/hooks/context') as any;
export function getMiddleware<T> (target: any): Middleware<T>[] {
return (target && target[HOOKS]) || [];
}
export type HookContextData = { [key: string]: any };
export type MiddlewareSetter = (currentMiddleware: Middleware[]) => Middleware[];
/**
* @param target The target object or function
* @param middleware or function
*/
export function setMiddleware<T> (target: T, middleware: Middleware[] | MiddlewareSetter) {
(target as any)[HOOKS] = typeof middleware === 'function' ? middleware(getMiddleware(target)) : middleware;
return target;
}
/**
* @param target The target object
* @param middleware or a function that takes current middleware as first argument
*/
export function registerMiddleware<T> (target: T, middleware: Middleware[]) {
return setMiddleware(target, (current: Middleware[]) => current.concat(middleware));
}
export function getContextUpdater<T> (target: any): ContextUpdater<T>[] {
return (target && target[CONTEXT]) || [];
}
/**
* @param target The target object or function
* @param updaters
*/
export function registerContextUpdater<T> (target: T, updaters: ContextUpdater[]) {
const current = getContextUpdater(target);
(target as any)[CONTEXT] = current.concat(updaters);
return target;
}
/**
* The base hook context.

@@ -56,3 +18,3 @@ */

constructor (data: { [key: string]: any } = {}) {
constructor (data: HookContextData = {}) {
Object.assign(this, data);

@@ -62,122 +24,134 @@ }

/**
* A function that updates the hook context with the `this` reference and
* arguments of the function call.
*/
export type ContextUpdater<T = any> = (self: any, fn: any, args: any[], context: HookContext<T>) => HookContext<T>;
/**
* A function that for a given function, calling context and arguments returns the list of hooks
*/
export type MiddlewareCollector<T = any> = (self: any, fn: any, args: any[]) => Middleware<T>[];
export type HookContextConstructor = new (data?: { [key: string]: any }) => HookContext;
/**
* Available options when initializing hooks with more than just an array of middleware
*/
export interface FunctionHookOptions<T = any> {
middleware: Middleware<T>[];
context: ContextUpdater<T>[];
collect: MiddlewareCollector<T>;
}
export class HookManager {
_parent?: this|null = null;
_params: string[] = [];
_middleware: Middleware[] = [];
_props: HookContextData = {};
_defaults: HookContextData|(() => HookContextData) = {};
export type HookSettings<T = any> = Middleware<T>[]|Partial<Omit<FunctionHookOptions, 'context'> & {
context: ContextUpdater<T>|ContextUpdater<T>[];
}>;
parent (parent: this) {
this._parent = parent;
export function defaultCollectMiddleware<T = any> (self: any, fn: any, args: any[]): Middleware[] {
return [
...getMiddleware<T>(self),
...(fn && typeof fn.collect === 'function' ? fn.collect(fn, fn.original, args) : getMiddleware(fn))
];
}
return this;
}
export function normalizeOptions<T = any> (opts: any): FunctionHookOptions<T> {
const options: Partial<FunctionHookOptions> = Array.isArray(opts) ? { middleware: opts } : opts;
const {
middleware = [],
context = withParams(),
collect = defaultCollectMiddleware
} = options;
middleware (middleware: Middleware[]) {
this._middleware = middleware;
const contextUpdaters = Array.isArray(context) ? context : [context];
return this;
}
return { middleware, context: contextUpdaters, collect };
}
getMiddleware (): Middleware[] {
const previous = this._parent ? this._parent.getMiddleware() : [];
export function collectContextUpdaters<T = any> (self: any, fn: any, args: any[]): ContextUpdater[] {
return [
...getContextUpdater<T>(self),
...(fn.original ? collectContextUpdaters(fn, fn.original, args) : getContextUpdater(fn))
];
}
return previous.concat(this._middleware);
}
/**
* Returns a ContextUpdater function that turns function arguments like
* `function (data, name)` into named properties (`context.data`, `context.name`)
* on the hook context
*
* @param params The list of parameter names
*/
export function withParams<T = any> (...params: (string | [string, any])[]) {
return (self: any, _fn: any, args: any[], context: HookContext<T>) => {
params.forEach((param: string | [string, any], index: number) => {
if (typeof param === 'string') {
context[param] = args[index];
return;
}
const [name, defaultValue] = param;
context[name] = args[index] === undefined ? defaultValue : args[index];
});
collectMiddleware (self: any, _args: any[]): Middleware[] {
const otherMiddleware = getMiddleware(self);
if (params.length > 0) {
Object.defineProperty(context, 'arguments', {
enumerable: true,
get (this: HookContext<T>) {
const result: any = [];
return otherMiddleware.concat(this.getMiddleware().reverse());
}
params.forEach((param, index) => {
const name = typeof param === 'string' ? param : param[0];
props (props: HookContextData) {
Object.assign(this._props, props);
Object.defineProperty(result, index, {
enumerable: true,
configurable: true,
get: () => this[name],
set: (value) => {
this[name] = value;
if (result[index] !== this[name]) {
result[index] = value;
}
}
});
return this;
}
this[name] = result[index];
});
getProps (): HookContextData {
const previous = this._parent ? this._parent.getProps() : {};
return result;
return Object.assign({}, previous, this._props);
}
params (...params: string[]) {
this._params = params;
return this;
}
getParams (): string[] {
const previous = this._parent ? this._parent.getParams() : [];
return previous.concat(this._params);
}
defaults (defaults: HookContextData|(() => HookContextData)) {
this._defaults = defaults;
return this;
}
getContextClass (Base: HookContextConstructor = HookContext): HookContextConstructor {
const ContextClass = class ContextClass extends Base {
constructor (data: any) {
super(data);
copyToSelf(this);
}
};
const params = this.getParams();
const props = this.getProps();
params.forEach((name, index) => {
Object.defineProperty(ContextClass.prototype, name, {
enumerable: true,
get () {
return this.arguments[index];
},
set (value: any) {
this.arguments[index] = value;
}
});
} else if (!context.arguments) {
context.arguments = args;
}
});
Object.seal(context.arguments);
Object.assign(ContextClass.prototype, props);
return ContextClass;
}
initializeContext (self: any, args: any[], context: HookContext): HookContext {
const ctx = this._parent ? this._parent.initializeContext(self, args, context) : context;
if (self) {
context.self = self;
ctx.self = self;
}
return context;
};
ctx.arguments = args;
return ctx;
}
}
/**
* Returns a ContextUpdater function that adds props on the hook context
*
* @param props The props object to assign
*/
export function withProps<T = any> (props: any) {
return (_self: any, _fn: any, _args: any[], context: HookContext<T>) => {
Object.assign(context, props);
export type HookOptions = HookManager|Middleware[];
return context;
};
export function convertOptions (options: HookOptions = []) {
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[] {
const manager = getManager(target);
return manager ? manager.getMiddleware() : [];
}
export function setMiddleware<T> (target: T, middleware: Middleware[]) {
const manager = new HookManager().middleware(middleware);
return setManager(target, manager);
}
import { functionHooks } from './function';
import {
HookContext,
registerMiddleware,
normalizeOptions,
HookSettings, withParams
} from './base';
import { setManager, HookOptions, convertOptions } from './base';
export const hookDecorator = <T> (hooks: HookSettings<T> = []) => {
export const hookDecorator = (managerOrMiddleware?: HookOptions) => {
const wrapper: any = (_target: any, method: string, descriptor: TypedPropertyDescriptor<any>): TypedPropertyDescriptor<any> => {
const { context, ...options } = normalizeOptions(hooks);
const manager = convertOptions(managerOrMiddleware);
if (!descriptor) {
registerMiddleware(_target.prototype, options.middleware);
setManager(_target.prototype, manager);

@@ -25,24 +20,8 @@ return _target;

context.push((_self: any, _fn: any, _args: any[], ctx: HookContext) => {
ctx.method = method;
return ctx;
});
descriptor.value = functionHooks(fn, manager.props({ method}));
descriptor.value = functionHooks(fn, {
...options,
context
});
return descriptor;
};
function params (...args: (string | [string, any])[]): typeof wrapper {
const { context, ...options } = normalizeOptions(hooks);
return {
...options,
context: [...context, withParams(...args)]
};
}
return Object.assign(wrapper, { params });
return wrapper;
};
import { compose, Middleware } from './compose';
import {
HookContext,
registerMiddleware,
registerContextUpdater,
normalizeOptions,
collectContextUpdaters,
HookSettings,
withParams
HookContext, setManager, HookContextData, HookOptions, convertOptions
} from './base';
import { getOriginal, copyProperties } from './utils';
function getOriginal (fn: any): any {
return typeof fn.original === 'function' ? getOriginal(fn.original) : fn;
}
/**

@@ -26,22 +17,16 @@ * Returns a new function that is wrapped in the given hooks.

*/
export const functionHooks = <F, T = any>(original: F, opts: HookSettings<T>) => {
if (typeof original !== 'function') {
export function functionHooks <F> (fn: F, managerOrMiddleware: HookOptions) {
if (typeof fn !== 'function') {
throw new Error('Can not apply hooks to non-function');
}
const { context: updateContext, collect, middleware } = normalizeOptions(opts);
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 HookContext;
// Initialize the context. Either the default context or the one that was passed
let context: HookContext = returnContext ? args.pop() : new HookContext();
const contextUpdaters = collectContextUpdaters(this, wrapper, args);
// Initialize the context with the self reference and arguments
for (const contextUpdater of contextUpdaters) {
context = contextUpdater(this, wrapper, args, context);
}
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

@@ -52,7 +37,7 @@ const hookChain: Middleware[] = [

// Create the hook chain by calling the `collectMiddleware function
...collect(this, wrapper, args),
...manager.collectMiddleware(this, args),
// Runs the actual original method if `ctx.result` is not already set
(ctx, next) => {
if (ctx.result === undefined) {
return Promise.resolve(getOriginal(original).apply(this, ctx.arguments)).then(result => {
return Promise.resolve(original.apply(this, ctx.arguments)).then(result => {
ctx.result = result;

@@ -71,21 +56,12 @@

registerContextUpdater(wrapper, updateContext);
registerMiddleware(wrapper, middleware);
copyProperties(wrapper, fn);
setManager(wrapper, manager);
const originalProps = (Object.keys(original) as any)
.concat(Object.getOwnPropertySymbols(original));
for (const prop of originalProps) {
const propDescriptor = Object.getOwnPropertyDescriptor(original, prop);
if (!wrapper.hasOwnProperty(prop)) {
Object.defineProperty(wrapper, prop, propDescriptor);
return Object.assign(wrapper, {
original: getOriginal(fn),
Context: manager.getContextClass(),
createContext: (data: HookContextData = {}) => {
return new wrapper.Context(data);
}
}
function params (...args: (string | [string, any])[]): typeof wrapper {
return registerContextUpdater(wrapper, [withParams(...args)]);
}
return Object.assign(wrapper, { original, collect, params });
});
};
import { functionHooks } from './function';
import { HookSettings } from './base';
import { Middleware } from './compose';
import { objectHooks, HookMap } from './object';
import { hookDecorator } from './decorator';
import { HookManager, HookContextData, HookContext, HookContextConstructor, HookOptions } from './base';

@@ -10,16 +11,25 @@ export * from './function';

export interface WrapperAddon<F, T = any> {
export interface WrapperAddon<F> {
original: F;
params: (...params: (string | [string, any])[]) => F&((...rest: any[]) => Promise<T>);
Context: HookContextConstructor;
createContext: (data?: HookContextData) => HookContext;
}
// hooks(fn, hookSettings)
export type WrappedFunction<F, T> = F&((...rest: any[]) => Promise<T>|Promise<HookContext>)&WrapperAddon<F>;
export function middleware (mw: Middleware[]) {
const manager = new HookManager();
return manager.middleware(mw);
}
// hooks(fn, hookOptions)
export function hooks<F, T = any> (
fn: F, hooks: HookSettings
): F&((...rest: any[]) => Promise<T>)&WrapperAddon<F, T>;
fn: F, manager: HookManager
): WrappedFunction<F, T>;
// hooks(object, hookMap)
export function hooks<O> (obj: O, hookMap: HookMap): O;
// @hooks(hookSettings)
export function hooks<O> (obj: O, hookMap: HookMap|Middleware[]): O;
// @hooks(hookOptions)
export function hooks<T = any> (
hooks?: HookSettings
_manager?: HookOptions
): any;

@@ -30,3 +40,3 @@ // Fallthrough to actual implementation

if (typeof target === 'function' && Array.isArray(_hooks.middleware || _hooks)) {
if (typeof target === 'function' && (_hooks instanceof HookManager || Array.isArray(_hooks))) {
return functionHooks(target, _hooks);

@@ -33,0 +43,0 @@ }

import { Middleware } from './compose';
import { functionHooks } from './function';
import { HookContext, registerMiddleware, normalizeOptions, HookSettings } from './base';
import { setMiddleware, convertOptions, HookOptions } from './base';
export interface HookMap {
[key: string]: HookSettings;
[key: string]: HookOptions;
}
export const objectHooks = (_obj: any, hooks: HookMap|Middleware[]) => {
export function objectHooks (_obj: any, hooks: HookMap|Middleware[]) {
const obj = typeof _obj === 'function' ? _obj.prototype : _obj;
if (Array.isArray(hooks)) {
return registerMiddleware(obj, hooks);
return setMiddleware(obj, hooks);
}
return Object.keys(hooks).reduce((result, method) => {
const value = obj[method];
const { context, ...options } = normalizeOptions(hooks[method]);
const fn = obj[method];
if (typeof value !== 'function') {
if (typeof fn !== 'function') {
throw new Error(`Can not apply hooks. '${method}' is not a function`);
}
context.push((_self: any, _fn: any, _args: any[], ctx: HookContext) => {
ctx.method = method;
return ctx;
});
const manager = convertOptions(hooks[method]);
result[method] = functionHooks(value, {
...options,
context
});
result[method] = functionHooks(fn, manager.props({ method }));

@@ -34,0 +27,0 @@ return result;

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc