Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

true-di

Package Overview
Dependencies
Maintainers
1
Versions
21
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

true-di - npm Package Compare versions

Comparing version 2.0.2 to 2.1.0

esm/utils/memo.d.ts

3

esm/create-instance.d.ts
import { IFactories, VoidFn } from './types';
declare const createInstanceFactory: <C extends object>(factories: IFactories<C>, instances: Map<keyof C, any>, stack?: import("./unique-stack").IUniqueStack<keyof C>, initializers?: VoidFn[]) => <N extends keyof C>(container: C, name: N) => C[N];
declare const $INIT: unique symbol;
declare const createInstanceFactory: <C extends object>(factories: IFactories<C>, instances?: Map<keyof C, any>, stack?: import("./unique-stack").IUniqueStack<typeof $INIT | keyof C>, initializers?: VoidFn[]) => <N extends keyof C>(container: C, name: N) => C[N];
export default createInstanceFactory;
import UniqueStack from './unique-stack';
const call = (fn) => fn();
const createInstanceFactory = (factories, instances, stack = UniqueStack(), initializers = []) => (container, name) => {
const $INIT = Symbol('$INIT');
const createInstanceFactory = (factories, instances = new Map(), stack = UniqueStack(), initializers = []) => (container, name) => {
if (instances.has(name))
return instances.get(name);
if (stack.push(name)[0] != null) {

@@ -21,4 +24,7 @@ throw new Error('Cyclic dependencies couldn\'t be resolved.');

if (stack.size === 0) {
stack.push($INIT);
initializers.forEach(call);
initializers = [];
instances.clear();
stack.pop($INIT);
}

@@ -25,0 +31,0 @@ return instance;

import { IFactories, IPureFactories } from './types';
export declare const isReady: <C extends object>(container: C, name: keyof C) => boolean;
export declare const prepareAll: <C extends object>(container: C) => C;
export declare const releaseAll: <C extends object>(container: C) => void;
export declare const factoriesFrom: <C extends object, N extends keyof C = keyof C>(container: C, names?: N[]) => IPureFactories<Pick<C, N>>;
declare function diContainer<C extends object>(factories: IFactories<C>): C;
export default diContainer;
import createInstanceFactory from './create-instance';
import allNames from './utils/all-names';
import assertExists from './utils/assert-exists';
import mapObject from './utils/map-object';
const $INSTANCES = Symbol('TRUE-DI::INSTANCES');
const itemsOf = (container) => assertExists(container[$INSTANCES], 'Argument is not a container');
export const isReady = (container, name) => itemsOf(container).has(name);
export const prepareAll = (container) => mapObject(container, name => container[name]);
export const releaseAll = (container) => {
itemsOf(container).clear();
};
export const factoriesFrom = (container, names) => mapObject(container, name => () => container[name], names);
function diContainer(factories) {
const instances = new Map();
const createInstance = createInstanceFactory(factories, instances);
const createInstance = createInstanceFactory(factories);
const container = allNames(factories).reduce((containerObj, name) => Object.defineProperty(containerObj, name, {
configurable: false,
enumerable: Object.getOwnPropertyDescriptor(factories, name).enumerable,
get: () => (instances.has(name) ? instances.get(name) : createInstance(container, name)),
set: (value) => {
if (value != null) {
throw new Error('Container does\'t allow to replace items');
}
instances.delete(name);
},
}), Object.create(Object.create(null, {
[$INSTANCES]: {
configurable: false, writable: false, enumerable: false, value: instances,
},
})));
get: () => createInstance(container, name),
}), Object.create(null));
return container;
}
export default diContainer;
export declare type IInstanceInitializer<IContainer, N extends keyof IContainer> = (instance: IContainer[N], container: IContainer) => void;
export declare type VoidFn = () => void;
export declare type IFactory<IContainer, N extends keyof IContainer> = (container: IContainer) => IContainer[N];
export declare type IFactoryTuple<IContainer, N extends keyof IContainer> = [IFactory<IContainer, N>, IInstanceInitializer<IContainer, N>];
export declare type IFactoryTuple<IContainer, N extends keyof IContainer> = [
IFactory<IContainer, N>,
IInstanceInitializer<IContainer, N>
];
export declare type IFactories<IContainer extends object> = {

@@ -6,0 +9,0 @@ [name in keyof IContainer]: IFactory<IContainer, name> | IFactoryTuple<IContainer, name>;

import { IFactories, VoidFn } from './types';
declare const createInstanceFactory: <C extends object>(factories: IFactories<C>, instances: Map<keyof C, any>, stack?: import("./unique-stack").IUniqueStack<keyof C>, initializers?: VoidFn[]) => <N extends keyof C>(container: C, name: N) => C[N];
declare const $INIT: unique symbol;
declare const createInstanceFactory: <C extends object>(factories: IFactories<C>, instances?: Map<keyof C, any>, stack?: import("./unique-stack").IUniqueStack<typeof $INIT | keyof C>, initializers?: VoidFn[]) => <N extends keyof C>(container: C, name: N) => C[N];
export default createInstanceFactory;

@@ -5,6 +5,10 @@ "use strict";

var call = function (fn) { return fn(); };
var $INIT = Symbol('$INIT');
var createInstanceFactory = function (factories, instances, stack, initializers) {
if (instances === void 0) { instances = new Map(); }
if (stack === void 0) { stack = unique_stack_1.default(); }
if (initializers === void 0) { initializers = []; }
return function (container, name) {
if (instances.has(name))
return instances.get(name);
if (stack.push(name)[0] != null) {

@@ -27,4 +31,7 @@ throw new Error('Cyclic dependencies couldn\'t be resolved.');

if (stack.size === 0) {
stack.push($INIT);
initializers.forEach(call);
initializers = [];
instances.clear();
stack.pop($INIT);
}

@@ -31,0 +38,0 @@ return instance;

import { IFactories, IPureFactories } from './types';
export declare const isReady: <C extends object>(container: C, name: keyof C) => boolean;
export declare const prepareAll: <C extends object>(container: C) => C;
export declare const releaseAll: <C extends object>(container: C) => void;
export declare const factoriesFrom: <C extends object, N extends keyof C = keyof C>(container: C, names?: N[]) => IPureFactories<Pick<C, N>>;
declare function diContainer<C extends object>(factories: IFactories<C>): C;
export default diContainer;
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.factoriesFrom = exports.releaseAll = exports.prepareAll = exports.isReady = void 0;
exports.factoriesFrom = exports.prepareAll = void 0;
var create_instance_1 = require("./create-instance");
var all_names_1 = require("./utils/all-names");
var assert_exists_1 = require("./utils/assert-exists");
var map_object_1 = require("./utils/map-object");
var $INSTANCES = Symbol('TRUE-DI::INSTANCES');
var itemsOf = function (container) {
return assert_exists_1.default(container[$INSTANCES], 'Argument is not a container');
};
exports.isReady = function (container, name) {
return itemsOf(container).has(name);
};
exports.prepareAll = function (container) {
return map_object_1.default(container, function (name) { return container[name]; });
};
exports.releaseAll = function (container) {
itemsOf(container).clear();
};
exports.factoriesFrom = function (container, names) {

@@ -25,5 +14,3 @@ return map_object_1.default(container, function (name) { return function () { return container[name]; }; }, names);

function diContainer(factories) {
var _a;
var instances = new Map();
var createInstance = create_instance_1.default(factories, instances);
var createInstance = create_instance_1.default(factories);
var container = all_names_1.default(factories).reduce(function (containerObj, name) {

@@ -33,17 +20,7 @@ return Object.defineProperty(containerObj, name, {

enumerable: Object.getOwnPropertyDescriptor(factories, name).enumerable,
get: function () { return (instances.has(name) ? instances.get(name) : createInstance(container, name)); },
set: function (value) {
if (value != null) {
throw new Error('Container does\'t allow to replace items');
}
instances.delete(name);
},
get: function () { return createInstance(container, name); },
});
}, Object.create(Object.create(null, (_a = {},
_a[$INSTANCES] = {
configurable: false, writable: false, enumerable: false, value: instances,
},
_a))));
}, Object.create(null));
return container;
}
exports.default = diContainer;

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

var __exportStar = (this && this.__exportStar) || function(m, exports) {
for (var p in m) if (p !== "default" && !exports.hasOwnProperty(p)) __createBinding(exports, m, p);
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
};

@@ -13,0 +13,0 @@ Object.defineProperty(exports, "__esModule", { value: true });

export declare type IInstanceInitializer<IContainer, N extends keyof IContainer> = (instance: IContainer[N], container: IContainer) => void;
export declare type VoidFn = () => void;
export declare type IFactory<IContainer, N extends keyof IContainer> = (container: IContainer) => IContainer[N];
export declare type IFactoryTuple<IContainer, N extends keyof IContainer> = [IFactory<IContainer, N>, IInstanceInitializer<IContainer, N>];
export declare type IFactoryTuple<IContainer, N extends keyof IContainer> = [
IFactory<IContainer, N>,
IInstanceInitializer<IContainer, N>
];
export declare type IFactories<IContainer extends object> = {

@@ -6,0 +9,0 @@ [name in keyof IContainer]: IFactory<IContainer, name> | IFactoryTuple<IContainer, name>;

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

var __exportStar = (this && this.__exportStar) || function(m, exports) {
for (var p in m) if (p !== "default" && !exports.hasOwnProperty(p)) __createBinding(exports, m, p);
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
};

@@ -13,0 +13,0 @@ Object.defineProperty(exports, "__esModule", { value: true });

{
"name": "true-di",
"version": "2.0.2",
"version": "2.1.0",
"description": "Simple Dependency Injection solution for TypeScript and Javascript",

@@ -5,0 +5,0 @@ "main": "./lib/index.js",

# true-di
`true-di` is a Simple Dependency Injection Container for TypeScript and JavaScript
Framework Agnostic, Zero Dependency, Isomorphic & Minimalistic **Dependency Injection Container** for TypeScript and JavaScript projects

@@ -10,3 +10,3 @@ [![Build Status](https://travis-ci.org/DScheglov/true-di.svg?branch=master)](https://travis-ci.org/DScheglov/true-di) [![Coverage Status](https://coveralls.io/repos/github/DScheglov/true-di/badge.svg?branch=master)](https://coveralls.io/github/DScheglov/true-di?branch=master) [![npm version](https://img.shields.io/npm/v/true-di.svg?style=flat-square)](https://www.npmjs.com/package/true-di) [![npm downloads](https://img.shields.io/npm/dm/true-di.svg?style=flat-square)](https://www.npmjs.com/package/true-di) [![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/DScheglov/true-di/blob/master/LICENSE)

```bash
npm i --S true-di
npm i --save true-di
```

@@ -24,2 +24,6 @@

- [Live Demo on Sandbox](https://codesandbox.io/s/github/DScheglov/true-di/tree/master/examples/getting-started?fontsize=14&hidenavigation=1&initialpath=%2Forders&module=%2Fsrc%2Fcontainer.ts&theme=dark)
- [Example Source Code](./examples/getting-started)
**./src/container.ts**

@@ -29,3 +33,3 @@

import diContainer from 'true-di';
import { IContainer } from './interfaces';
import { ILogger, IDataSourceService, IECommerceService } from './interfaces';
import Logger from './Logger';

@@ -35,8 +39,13 @@ import DataSourceService from './DataSourceService';

type IServices = {
logger: ILogger,
dataSourceService: IDataSourceService,
ecommerceService: IECommerceService,
}
const container = diContainer<IContainer>({
export default diContainer<IServices>({
logger: () =>
new Logger(),
dataSourceService: ({ logger }) =>
dataSourceService: ({ logger }) =>
new DataSourceService(logger),

@@ -47,4 +56,31 @@

});
```
export default container;
**./src/ECommerceService/index.ts**
```typescript
import {
IECommerceService, IDataSourceService, Order, IInfoLogger
} from '../interfaces';
class ECommerceSerive implements IECommerceService {
constructor(
private readonly _logger: IInfoLogger,
private readonly _dataSourceService: IDataSourceService,
) {
_logger.info('ECommerceService has been created');
}
async getOrders(): Promise<Order[]> {
const { _logger, _dataSourceService } = this;
// do something
}
async getOrderById(id: string): Promise<Order | null> {
const { _logger, _dataSourceService } = this;
// do something
}
}
export default ECommerceSerive;
```

@@ -56,9 +92,26 @@

import Express from 'express';
import { IGetOrderById, IGetOrders } from './interfaces';
import { Injected } from './interfaces/IRequestInjected';
import { sendJson } from './utils/sendJson';
import { expectFound } from './utils/NotFoundError';
export const getOrders = async (req: Express.Request, res: Express.Response) => {
const { ecommerceService } = req.injected;
res.json(
await ecommerceService.getOrders()
);
}
export const getOrders = (
{ injected: { ecommerceService } }: Injected<{ ecommerceService: IGetOrders }>,
res: Express.Response,
next: Express.NextFunction,
) =>
ecommerceService
.getOrders()
.then(sendJson(res), next);
export const getOrderById = (
{ params, injected: { ecommerceService } }:
{ params: { id: string } } & Injected<{ ecommerceService: IGetOrderById }>,
res: Express.Response,
next: Express.NextFunction,
) =>
ecommerceService
.getOrderById(params.id)
.then(expectFound(`Order(${params.id})`))
.then(sendJson(res), next);
```

@@ -71,7 +124,8 @@

import container from './container';
import { getOrders } from './controller';
import { getOrderById, getOrders } from './controller';
import { handleErrors } from './middlewares';
const app = express();
app.use((req, res, next) => {
app.use((req, _, next) => {
req.injected = container;

@@ -82,30 +136,45 @@ next();

app.get('/orders', getOrders);
app.listen(8080);
app.get('/orders/:id', getOrderById);
app.use(handleErrors);
if (module.parent == null) {
app.listen(8080, () => {
console.log('Server is listening on port: 8080');
console.log('Follow: http://localhost:8080/orders');
});
}
export default app;
```
### [Live Demo on Sandbox](https://codesandbox.io/s/github/DScheglov/true-di/tree/master/examples/getting-started?fontsize=14&hidenavigation=1&initialpath=%2Forders&module=%2Fsrc%2Fcontainer.ts&theme=dark)
## Motivation
![Live Demo Preview](./docs/assets/image.png)
`true-di` is designed to be used with [Apollo Server](https://github.com/apollographql/apollo-server) considering to make resolvers as thin as possible, to keep all business logic testable and framework agnostic by strictly following **S**.**O**.**L**.**I**.**D** Principles.
## Why `true-di`?
Despite the origin motivation being related to Apollo Server the library could be used with any other framework or library that supports injection through the context.
1. It's light and idiomatic.
1. It works with **JavaScript** and **TypeScript**.
1. It's well-typed. Items types are always known whenever your code assess them.
1. It is Isomorphic: works on Back end and on Front end.
1. It works with Classes and with Closures.
1. It doesn't require decorators (annotations).
1. It doesn't depend on `reflect-metadata`.
1. It allows referring items without strings or other artificial elements.
1. It uses `getters` under the hood.
1. It's lazy: container doesn't create an item until your code requests
1. It automatically resolves dependencies
1. It immediately falls down on constructor-injection of cyclic dependency
1. It supports property-, setter- cyclic dependencies
1. It doesn't know anything about context but it could be easily placed into the context.
1. Its containers are composable.
1. It's SOLID-compatible.
1. It's more SOLID-compatible then other IOC solutions. Your code doesn't need to depend on the container (nor even on its interface).
1. It's testable.
1. It supports symbolic names.
1. It doesn't have any other dependencies (and it will be kept)
The [Getting Started Example](./examples/getting-started) shows how to use `true-di` with one of the most popular nodejs libraries: [Express](https://expressjs.com/). Almost all code in the example (~95%) is covered with tests that prove your business logic could be easily decoupled and kept independent even from dependency injection mechanism.
Thanking to business logic does not depend on specific injection mechanism you can defer the
choice of framework. Such deferring is suggested by Robert Martin:
> The purpose of a good architecture is to defer decisions, delay decisions. The job of an architect is not to make decisions, the job of an architect is to build a structure that allows decisions to be delayed as long as possible.
[&copy; 2014, Robert C. Martin: Clean Architecture and Design, NDC Conference](https://vimeo.com/68215570),
Summarizing, `true-di` is based on:
- emulattion of a plain object that allows to specify exact type for each item and makes strict static type checking possible
- explicit dependency injection for business logic (see example: [./src/container.ts](./examples/getting-started/src/container.ts))
- transperent injection through the context for framework-integrated components (see example: [./src/index.ts](./examples/getting-started/src/index.ts))
## Some useful facts about `true-di`?
1. No syntatic decorators (annotations) as well as `reflect-metadata` are needed
1. `diContainer` uses `getters` under the hood. Container doesn't create an item until your code requests
1. Constructor-injection of cyclic dependency raises an exception
1. The property-, setter- dependency injects is also supported (what allows to resolve cyclic dependencies)
1. `diContainer`-s could be composed
1. Symbolic names are also supported for items

@@ -32,3 +32,3 @@ import UniqueStack from './unique-stack';

if (typeof initializer === 'function') {
initializers.push(() => initializer(instance, container));
initializers.push(() => initializer(instance, container, name));
}

@@ -35,0 +35,0 @@

import diContainer from './di-container';
import multiple from './multiple';
export * from './types';
export * from './di-container';
export { multiple };
export default diContainer;
export type IInstanceInitializer<IContainer, N extends keyof IContainer> = (
instance: IContainer[N],
container: IContainer
container: IContainer,
name?: N,
) => void;

@@ -5,0 +6,0 @@

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