1.8.0 • Published 11 months ago

surrogate v1.8.0

Weekly downloads
10
License
MIT
Repository
github
Last release
11 months ago

Surrogate

Easily create Pre and Post hooks on JavaScript objects.

Install

npm install surrogate

or

yarn add surrogate

Usage

There are a couple ways to manage Surrogate. The first is to utilize an exposed helper function, wrapSurrogate, to wrap objects and register pre and post methods through it.

import { wrapSurrogate, Surrogate, NextParameters } from 'surrogate';

const guitar: Surrogate<Guitar> = wrapSurrogate(new Guitar(), options);

guitar.getSurrogate().registerPreHook('play', ({ next }: NextParameters<Guitar>) => {
  // do things before running 'play'

  next.next();
});

Check examples for expanded samples.

SurrogateOptions

OptionTypeDefault ValueDescription
useSingleton?booleantrueInforms Surrogate to operate as a Singleton
useContext?anyTThe context in which to call surrogate handlers. Handler specific contexts take precedence.
provide?anynullUser provided content to pass to handlers and conditionals. Handler specific values take precedence
silenceErrors?boolean | SilenceErrorsfalseSpecify if Surrogate error output should be silenced. Accepts boolean or function that will receive the error and return a boolean
maintainContext?boolean | string | string[]falseMaintain context for methods without hooks. Can be a method name or array of method names

SurrogateMethods

After wrapping your instance with Surrogate new methods are available, getSurrogate, which, when called will return an instance of Surrogate Event Manager that allows management of pre and post methods and disposeSurrogate which will restore all methods, deregister hooks and remove the current instance from Surrogate management.

MethodParametersReturnsDescription
getSurrogaten/aSurrogateEventManagerProvides capabilities for managing method hooks
disposeSurrogaten/aTCleans current instance of Surrogate handlers
bypassSurrogaten/aTAllows calling target methods without running handlers

SurrogateEventManager

MethodParametersReturnsDescription
registerPreHook(event: string, handler: SurrogateHandlers, options?: SurrogateHandlerOptions)SurrogateEventManagerRegisters a pre hook
registerPostHook(event: string, handler: SurrogateHandlers, options?: SurrogateHandlerOptions)SurrogateEventManagerRegisters a post hook
deregisterPreHooks(event: string)SurrogateEventManagerDeregisters pre hooks for the given event
deregisterPostHooks(event: string)SurrogateEventManagerDeregisters post hooks for the given event
deregisterHooksn/aSurrogateEventManagerRemoves all hooks for the current instance.

SurrogateHandlers is any function that accepts a NextParameters object which can be used to control flow through pre and post hooks. It may also be the name of a method on the target object.

CommonParameters

Common parameters passed to all handlers and conditional functions. (runConditions, runOnError, runOnBail)

PropertyMember OfTypeDescription
actionProviderParametersstringThe current target method.
correlationIdProviderParametersstringUnique identifier for the current hook pipeline
instanceProviderParametersTThe current unwrapped instance
error?ProviderParametersErrorAn error object, if passed
hookTypeProviderParametersstringProvides the current hook type as a string.
receivedArgumentsProviderParametersany[]Arguments received from the previous handler.
currentArgsProviderParametersany[]Array of potentially modified arguments passed to original method invoked by surrogate
originalArgsProviderParametersany[]Array of arguments passed to the instance invoked method
timeTrackerProviderParametersTimeTrackerProvides access to the current time tracker
resultProviderParametersanyResult of original method invocation if run
provideProviderParametersanyUser provided content to pass to handlers and conditionals. Default value is null

NextParameters

NextParameters is passed to all hook handlers. In addition to CommonParameters you'll receive the current Surrogate wrapped instance and an INext object that provides functionality to skip hooks or continue to the next hook for execution.

PropertyTypeDescription
surrogateSurrogateProvides handler access to surrogate wrapped instance.
nextINextObject that provides flow control capabilities

TimeTracker

MethodMember OfParametersReturn ValueDescription
geStartTimeTimeTrackernonenumberReturns the start time of the current pipeline.
getTotalDurationTimeTrackernonenumberReturns the total duration of the current pipeline.
getHookStartTimeTimeTrackernonenumberReturns the start time of the current hook.
getLastRunDurationTimeTrackernonenumberReturns the duration of the last hook.
getTimeSinceLastRunTimeTrackernonenumberReturns the duration since the last hook completed.

INext

MethodMember OfParametersDefault ValueDescription
skipINext(skipAmount?: number)1Method that will skip the next 'skipAmount' handlers
skipWithINext(skipAmount: number, ...args: any[])[]Same as skip but will accept any number of arguments, passing to the next executed handler
nextINext(nextOptions?: NextOptions)n/aCalling next may advance to the next hook.

Next Options

PropertyTypeDescription
error?ErrorPassing an Error may result in the error being thrown, depending on supplied handler options
using?any[]An array of values to pass to the next handler
bail?booleanIndicates all subsequent handler executions should stop immediately. Target method is not called if bailing in pre hook
replace?anyReplaces the original arguments passed to the target method.
bailWith?anyIf bailing, the supplied value should be returned

SurrogateHandlerOptions

When registering a hook you may provide any of the following options.

PropertyTypeDefault ValueDescription
useNext?booleantruetrue indicates usage of the INext object to control flow, otherwise Surrogate makes a determination when to advance
noArgs?booleanfalseSpecify that NextParameters should NOT be passed to a handler
ignoreErrors?booleanfalseIf true and an Error is passed or caught Surrogate will not throw.
useContext?anyTThe context in which to call surrogate handlers.
wrapper?MethodWrapperssyncTells Surrogate if it is managing synchronous or asynchronous methods.
runConditions?RunCondition | RunCondition[]n/aConditions to determine if a handler should be executed.
runOnError?RunOnError | RunOnError[]n/aFunctions to run in the event of handler error. Runs regardless of ignoreError
runOnBail?RunOnBail | RunOnBail[]n/aFunctions to run in the event of handler bailing.
priority?number0Used to determine the order in which handlers are executed. Larger numbers have higher priority

useContext option defaults to the current instance. Specifying context should be surrogate will allow hooks in the pipeline to trigger additional hooks. This can be extraordinarily useful but has the potential to cause recursive loops.

RunCondition

A RunCondition is a function that receives RunConditionParameters, which includes CommonParameters and returns a boolean indicating if the current handler should be executed(true) or skipped(false). All run conditions are executed synchronously and all conditions must be true for the handler to execute.

PropertyMember OfTypeDescription
didErrorRunConditionParametersbooleanIndicates if the previous handler passed an Error.
valueFromConditionRunConditionParametersanyValue passed forward from previous run condition
didReceiveFromLastConditionRunConditionParametersbooleanIndicates if the previous condition passed a value.
passToNextCondition()RunConditionParametersanyFunction to pass a value to next condition

RunOnError

RunOnError is a function that receives RunOnErrorParameters, which includes CommonParameters and the ability to recover from an error.

PropertyMember OfTypeDescription
errorRunOnErrorParametersErrorAn error object received or caught from a handler
recoverFromError()RunOnErrorParametersbooleanFunction to recover from an error

RunOnBail

RunOnBail is a function that receives RunOnBailParameters, which includes CommonParameters and the ability to recover from a bailing handler.

PropertyMember OfTypeDescription
bailWith()RunOnBailParametersanyFunction that accepts a value to bail with
recoverFromBail()RunOnBailParametersbooleanFunction to recover from a bailing handler

Decorators

Perhaps a more convenient way to register hooks is with decorators.

import { SurrogateDelegate } from 'surrogate';

@SurrogateDelegate()
class Guitar {}

SurrogateDelegate registers your class and will automatically wrap instances of that class with Surrogate.
It supports all options from SurrogateOptions as well as option locateWith which may be provided to assist Surrogate in locating method decorators for a particular class. Should only be necessary if multiple class decorators are utilized.

import { SurrogateDelegate } from 'surrogate';

@SurrogateDelegate({
  locateWith: Guitar,
})
@MyOtherClassDecorator()
class Guitar {}

If you wish to use Surrogate methods inside this instance of your class you must extend SurrogateMethods interface.

import { SurrogateDelegate, SurrogateMethods } from 'surrogate';

export interface Guitar extends SurrogateMethods<Guitar> {}

@SurrogateDelegate()
export class Guitar {}

Registering hooks:

import { NextParameters, SurrogatePre, SurrogatePost, SurrogateDelegate } from 'surrogate';

@SurrogateDelegate()
class Guitar {
  @SurrogatePre(({ next }: NextParameters<Guitar>) => {
    console.log(`Tuning guitar`);

    next.next();
  })
  @SurrogatePost<Guitar>({
    handler: () => {
      console.log(`Put guitar away`);
    },
    options: {
      useNext: false,
    },
  })
  play() {
    console.log(`playing guitar`);
  }
}

SurrogateDecoratorOptions

All Surrogate[Async](Pre|Post) decorators accept SurrogateDecoratorOptions or an array of options and must decorate the intended hooked method.

OptionTypeDefault ValueDescription
handlerSurrogateHandlersn/aThis function or array of functions will run before or after the decorated method
options?SurrogateHandlerOptions{}Options defining Surrogate handler behavior

NextDecoratorOptions

All Next[Async](Pre|Post) decorators accept NextDecoratorOptions or an array of options. Any method decorated with Next* will be registered as a Surrogate handler. Therefore, you must supply the target method the Next* method will run with.

OptionTypeDefault ValueDescription
actionkeyof T | string | (keyof T | string )[]n/aName of the target decorated method, accepts regexp string for matching
options?SurrogateHandlerOptions{}Options defining Surrogate handler behavior
@SurrogateDelegate({
  locateWith: Account,
})
@Table({
  timestamps: true,
})
class Account extends Model<Account> {
  @NextAsyncPreAndPost<Account>({
    action: ['update', 'save'],
    options: {
      useNext: false,
    },
  })
  protected async logActions({
    instance: model,
    originalArgs,
    timeTracker,
    hookType,
    action,
  }: NextParameters<Account>) {
    const keys = (model.changed() || []) as (keyof Account)[];
    const changes = keys.reduce(
      (changed, key) => ({
        ...changed,
        [key]: {
          current: model.getDataValue(key),
          previous: model.previous(key),
        },
      }),
      {},
    );

    telemetry.trackEvent({
      name: `${hookType}|${action}|Logging`,
      properties: {
        action,
        changes,
        hookType,
        model: model.toJSON(),
        arguments: originalArgs,
      },
      measurements: {
        lastRunDuration: timeTracker.getDurationSinceLastRun(),
      },
    });
  }
}

Acknowledgements

Many thanks to Dale for transferring the npm package name.

1.8.0

11 months ago

1.7.1

12 months ago

1.7.0

1 year ago

1.6.0

2 years ago

1.5.1

2 years ago

1.5.0

3 years ago

1.4.0

3 years ago

1.3.5

3 years ago

1.3.4

3 years ago

1.3.3

3 years ago

1.3.2

3 years ago

1.3.1

3 years ago

1.3.0

3 years ago

1.2.0

3 years ago

1.1.1

3 years ago

1.1.0

3 years ago

1.0.0

3 years ago

0.0.0

10 years ago

0.2.2

10 years ago

0.2.1

10 years ago

0.2.0

10 years ago

0.0.1

10 years ago