Security News
38% of CISOs Fear They’re Not Moving Fast Enough on AI
CISOs are racing to adopt AI for cybersecurity, but hurdles in budgets and governance may leave some falling behind in the fight against cyber threats.
Jpex is an Inversion of Control framework. Register dependencies on a container, then resolve them anywhere in your application. The real magic of jpex is its ability to infer dependencies using the magic of babel and typescript...
npm install jpex
Jpex uses babel to infer type interfaces at build time. You can do this with one of several methods: @jpex-js/babel-plugin @jpex-js/rollup-plugin @jpex-js/webpack-plugin
Jpex comes bundled with the @jpex-js/babel-plugin
so you can easily get started with a .babelrc
like this:
// .bablerc
{
presets: [ '@babel/preset-typescript' ],
plugins: [ 'jpex/babel-plugin' ]
}
import jpex from 'jpex';
import { IFoo, IBah } from './types';
jpex.factory<IFoo>((bah: IBah) => bah.baz);
const foo = jpex.resolve<IFoo>();
Services and factories are small modules or functions that provide a reusable or common piece of functionality.
type MyFactory = {};
jpex.factory<MyFactory>(() => {
return {};
});
type MyService = { method: () => any };
jpex.service<MyService>(function(){
this.method = function(){
...
};
});
type MyConstant = string;
jpex.constant<MyConstant>('foo');
You can then resolve a dependency anywhere in your app:
const value = jpex.resolve<MyFactory>();
You can also request a dependency from within another factory:
jpex.constant<MyConstant>('foo');
jpex.factory<MyFactory>((myConstant: MyConstant) => {
return `my constant is ${myConstant}`;
});
Or you can encase a function so that dependencies are injected into it on-the-fly:
const myFn = jpex.encase((value: MyFactory) => (arg1, arg2) => {
return value + arg1 + arg2;
});
<T>(obj: T): void
Registers a constant value.
<T>(fn: (...deps: any[] => T), opts?: object): void
Registers a factory function against the given type. Jpex works out the types of deps
and injects them at resolution time, then returns the resulting value T
.
type GetStuff = () => Promise<string>;
jpex.factory<GetStuff>((window: Window) => () => window.fetch('/stuff));
The following options can be provided for both factories and services:
'application' | 'class' | 'instance' | 'none'
Determines how long the factory is cached for once resolved.
application
is resolved forever across all containersclass
is resolved for the current jpex container, if you .extend()
the new container will resolve it againinstance
if you request the same dependency multiple times in the same resolve
call, this will use the same value, but the next time you call resolve
it will start againnone
never caches anythingThe default lifecycle is class
'active' | 'passive'
Determines the behavior when the same factory is registered multiple times.
active
overwrites the existing factorypassive
prefers the existing factoryDefaults to active
boolean
Specifically for services, automatically binds all of the dependencies to the service instance.
<T>(class: ClassWithConstructor, opts?: object): void
Registers a service. A service is like a factory but instantiates a class instead.
class Foo {
constructor(window: Window) {
// ...
}
}
jpex.service<Foo>(Foo);
<T>(alias: string): void
Creates an alias to another factory
<T>(opts?: object): T
Locates and resolves the desired factory.
const foo = jpex.resolve<Foo>();
The following options can be provided for both resolve
and resolveWith
:
boolean
When true
if the dependency cannot be found or resolved, it will just return undefined
rather than throwing an error.
object
Lets you pass in static values to use when resolving dependencies. This should be used as an escape hatch, as resolveWith
was created specifically for this purpose.
<T, ...Rest[]>(values: Rest, opts?: object): T
Resolves a factory while substituting dependencies for the given values
const foo = jpex.resolveWith<Foo, Bah, Baz>([ 'bah', 'baz' ]);
(...deps: any[]): (...args: any[]) => any
Wraps a function and injects values into it, it then returns the inner function for use.
const getStuff = jpex.encase((http: Http) => (thing: string) => {
return http(`api/app/${thing}`);
});
await getStuff('my-thing');
To help with testing, the returned function also has an encased
property containng the outer function
getStuff.encased(fakeHttp)('my-thing');
(config?: object): Jpex
creates a new container, using the current one as a base.
This is useful for creating isolated contexts or not poluting the global container.
The default behavior is to pass down all config options and factories to the new container.
boolean
Whether or not to inherit config and factories from its parent
'application' | 'class' | 'instance' | 'none'
The default lifecycle for factories. class
by default
'active' | 'passive'
The default precedence for factories. active
by default
boolean
Whether factories should be optional by default
boolean
When trying to resolve a tdependency, should it attempt to import the dependency from node modules?
boolean
When trying to resolve a dependency, should it check for it on the global object?
<T>() => (...deps: any[]) => T
Returns the raw factory function, useful for testing.
() => void
<T>() => void
Clears the cache of resolved factories. If you provide a type, that specific factory will be cleared, otherwise it will clear all factories.
<T>() => string
Under the hood jpex converts types into strings for runtime resolution. If you want to get that calculated string for whatever reason, you can use jpex.infer
There are a few caveats to be aware of:
jpex.factory<{}>()
interface Bah extends Foo {}
you can't then try to resolve Foo
and expect to be given Bah
, they are treated as 2 separate thingsconst jpex2 = jpex; jpex2.constant<Foo>(foo);
without explicitly adding jpex2
to the plugin configconst { factory } = jpex
Jpex is a really good fit with React as it offers a good way to inject impure effects into pure components. There is a react-jpex
library that exposes a few hooks.
import React from 'react';
import { useResolve } from 'react-jpex';
import { SaveData } from '../types';
const MyComponent = (props) => {
const saveData = useResolve<SaveData>();
const onSubmit = () => saveData(props.values);
return (
<div>
<MyForm/>
<button onClick={onSubmit}>Submit</button>
</div>
);
};
And this pattern also makes it really easy to isolate a component from its side effects when writing tests:
import { Provider, useJpex } from 'jpex';
// create a stub for the SaveData dependency
const saveData = stub();
render(
// the Provider component will create a new jpex instance
<Provider>
{() => {
// grab jpex - it will be isolated to this context only
const jpex = useJpex();
// register our stub dependency
jpex.constant<SaveData>(saveData);
// when we render MyComponent, it will be given our stubbed dependency
return (<MyComponent/>);
}}
</Provider>
);
// trigger the compnent's onClick
doOnClick();
expect(saveData.called).to.be.true;
Perhaps you hate typescript, or babel, or both. Or perhaps you don't have the luxury of a build pipeline in your application. That's fine because jpex supports vanilla js as well, you just have to explicitly state your dependencies up front:
jpex.constant('foo', 'foo');
jpex.factory('bah', [ 'foo' ], (foo) => foo + 'bah');
const value = jpex.resolve('bah');
Jpex uses language features supported by the latest browsers, but if you need to support IE11 et al. you can import from 'jpex/dist/es5` (or create an alias in your build process)
4.0.0
Window
and Document
are now automatically resolved (unless you register your own dependency of the same name)nodeModules
and globals
optional
flagTS
inference, or explicitly pass an array of deps.factory
.service
and .resolve
opts
parameter when registering a factory i.e. .factory<A>(fn, { lifecycle: 'none' })
opts
parameter when resolving i.e. .resolve<A>({ optional: true })
resolveWith
now has a nicer syntax for ts inference: .resolveWith<Foo, Dep1, Dep2>([ 'val1', 'val2' ])
. The original syntax i.e. .resolveWith({ dep1: 'val1' })
is still valid.$options
. You can no longer do .resolve({ foo: 'someValue' })
$resolve
precedence
option lets you determine if a factory should overwrite an existing factory or notjpex/dist/es5.js
jpex.alias<From, To>()
Window
without registering it first, rather than throw an error, you will now get the global variablejpex.factory('foo', (depA, depB) => { ... })
as we no longer parse the function and extract the dependencies..factory<A>(fn).lifecycle.application()
you must now do .factory<A>(fn, { lifecycle: 'application' })
clearCache('a', 'b', 'c')
whereas previous it took an array.factory<A>([ 'b' ], fn)
Lifecycle
is now a type rather than an enum__
will no longer make it optional, you must explicitly pass the optional flag$options
and $resolve
functionality have been removedjpex/dist/es5.js
or create an alias for itFAQs
Javascript Prototype Extension
The npm package jpex receives a total of 690 weekly downloads. As such, jpex popularity was classified as not popular.
We found that jpex demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 0 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
CISOs are racing to adopt AI for cybersecurity, but hurdles in budgets and governance may leave some falling behind in the fight against cyber threats.
Research
Security News
Socket researchers uncovered a backdoored typosquat of BoltDB in the Go ecosystem, exploiting Go Module Proxy caching to persist undetected for years.
Security News
Company News
Socket is joining TC54 to help develop standards for software supply chain security, contributing to the evolution of SBOMs, CycloneDX, and Package URL specifications.