npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

surrogate

v1.8.0

Published

Object method hooks made easy

Downloads

37

Readme

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

| Option | Type | Default Value | Description | | ---------------- | ----------------------------- | :-----------: | ---------------------------------------------------------------------------------------------------------------------------------- | | useSingleton? | boolean | true | Informs Surrogate to operate as a Singleton | | useContext? | any | T | The context in which to call surrogate handlers. Handler specific contexts take precedence. | | provide? | any | null | User provided content to pass to handlers and conditionals. Handler specific values take precedence | | silenceErrors? | boolean | SilenceErrors | false | Specify if Surrogate error output should be silenced. Accepts boolean or function that will receive the error and return a boolean | | maintainContext? | boolean | string | string[] | false | Maintain 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.

| Method | Parameters | Returns | Description | | ---------------- | :--------: | --------------------- | ------------------------------------------------------ | | getSurrogate | n/a | SurrogateEventManager | Provides capabilities for managing method hooks | | disposeSurrogate | n/a | T | Cleans current instance of Surrogate handlers | | bypassSurrogate | n/a | T | Allows calling target methods without running handlers |

SurrogateEventManager

| Method | Parameters | Returns | Description | | ------------------- | ------------------------------------------------------------------------------ | --------------------- | -------------------------------------------- | | registerPreHook | (event: string, handler: SurrogateHandlers, options?: SurrogateHandlerOptions) | SurrogateEventManager | Registers a pre hook | | registerPostHook | (event: string, handler: SurrogateHandlers, options?: SurrogateHandlerOptions) | SurrogateEventManager | Registers a post hook | | deregisterPreHooks | (event: string) | SurrogateEventManager | Deregisters pre hooks for the given event | | deregisterPostHooks | (event: string) | SurrogateEventManager | Deregisters post hooks for the given event | | deregisterHooks | n/a | SurrogateEventManager | Removes 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)

| Property | Member Of | Type | Description | | ----------------- | ------------------ | ----------- | -------------------------------------------------------------------------------------- | | action | ProviderParameters | string | The current target method. | | correlationId | ProviderParameters | string | Unique identifier for the current hook pipeline | | instance | ProviderParameters | T | The current unwrapped instance | | error? | ProviderParameters | Error | An error object, if passed | | hookType | ProviderParameters | string | Provides the current hook type as a string. | | receivedArguments | ProviderParameters | any[] | Arguments received from the previous handler. | | currentArgs | ProviderParameters | any[] | Array of potentially modified arguments passed to original method invoked by surrogate | | originalArgs | ProviderParameters | any[] | Array of arguments passed to the instance invoked method | | timeTracker | ProviderParameters | TimeTracker | Provides access to the current time tracker | | result | ProviderParameters | any | Result of original method invocation if run | | provide | ProviderParameters | any | User 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.

| Property | Type | Description | | --------- | --------- | ------------------------------------------------------ | | surrogate | Surrogate | Provides handler access to surrogate wrapped instance. | | next | INext | Object that provides flow control capabilities |

TimeTracker

| Method | Member Of | Parameters | Return Value | Description | | ------------------- | :---------: | :--------: | :----------: | --------------------------------------------------- | | geStartTime | TimeTracker | none | number | Returns the start time of the current pipeline. | | getTotalDuration | TimeTracker | none | number | Returns the total duration of the current pipeline. | | getHookStartTime | TimeTracker | none | number | Returns the start time of the current hook. | | getLastRunDuration | TimeTracker | none | number | Returns the duration of the last hook. | | getTimeSinceLastRun | TimeTracker | none | number | Returns the duration since the last hook completed. |

INext

| Method | Member Of | Parameters | Default Value | Description | | -------- | :-------: | ------------------------------------ | :-----------: | -------------------------------------------------------------------------------------------- | | skip | INext | (skipAmount?: number) | 1 | Method that will skip the next 'skipAmount' handlers | | skipWith | INext | (skipAmount: number, ...args: any[]) | [] | Same as skip but will accept any number of arguments, passing to the next executed handler | | next | INext | (nextOptions?: NextOptions) | n/a | Calling next may advance to the next hook. |

Next Options

| Property | Type | Description | | --------- | ------- | ----------------------------------------------------------------------------------------------------------------------- | | error? | Error | Passing 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? | boolean | Indicates all subsequent handler executions should stop immediately. Target method is not called if bailing in pre hook | | replace? | any | Replaces the original arguments passed to the target method. | | bailWith? | any | If bailing, the supplied value should be returned |

SurrogateHandlerOptions

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

| Property | Type | Default Value | Description | | -------------- | ------------------------------ | :-----------: | ----------------------------------------------------------------------------------------------------------------------- | | useNext? | boolean | true | true indicates usage of the INext object to control flow, otherwise Surrogate makes a determination when to advance | | noArgs? | boolean | false | Specify that NextParameters should NOT be passed to a handler | | ignoreErrors? | boolean | false | If true and an Error is passed or caught Surrogate will not throw. | | useContext? | any | T | The context in which to call surrogate handlers. | | wrapper? | MethodWrappers | sync | Tells Surrogate if it is managing synchronous or asynchronous methods. | | runConditions? | RunCondition | RunCondition[] | n/a | Conditions to determine if a handler should be executed. | | runOnError? | RunOnError | RunOnError[] | n/a | Functions to run in the event of handler error. Runs regardless of ignoreError | | runOnBail? | RunOnBail | RunOnBail[] | n/a | Functions to run in the event of handler bailing. | | priority? | number | 0 | Used 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.

| Property | Member Of | Type | Description | | --------------------------- | ---------------------- | ------- | --------------------------------------------------- | | didError | RunConditionParameters | boolean | Indicates if the previous handler passed an Error. | | valueFromCondition | RunConditionParameters | any | Value passed forward from previous run condition | | didReceiveFromLastCondition | RunConditionParameters | boolean | Indicates if the previous condition passed a value. | | passToNextCondition() | RunConditionParameters | any | Function 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.

| Property | Member Of | Type | Description | | ------------------ | -------------------- | ------- | ------------------------------------------------- | | error | RunOnErrorParameters | Error | An error object received or caught from a handler | | recoverFromError() | RunOnErrorParameters | boolean | Function 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.

| Property | Member Of | Type | Description | | ----------------- | ------------------- | ------- | ------------------------------------------ | | bailWith() | RunOnBailParameters | any | Function that accepts a value to bail with | | recoverFromBail() | RunOnBailParameters | boolean | Function 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.

| Option | Type | Default Value | Description | | :------: | ---------------------------------------------------- | :-----------: | --------------------------------------------------------------------------------- | | handler | SurrogateHandlers | n/a | This 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.

| Option | Type | Default Value | Description | | :------: | ---------------------------------------------------- | :-----------: | ----------------------------------------------------------------------- | | action | keyof T | string | (keyof T | string )[] | n/a | Name 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.