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

ioc-service-container

Package Overview
Dependencies
Maintainers
1
Versions
15
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

ioc-service-container - npm Package Compare versions

Comparing version 1.5.2 to 1.6.0

12

dist/decorators.js

@@ -18,12 +18,12 @@ "use strict";

function redefineObject(target, propertyKey, serviceId) {
const getter = () => {
function get() {
return ServiceContainer_1.default.get((serviceId === null || serviceId === void 0 ? void 0 : serviceId.toLowerCase()) || propertyKey.toLowerCase());
};
const setter = () => {
}
function set() {
throw new Error(`Injected property [${propertyKey}] can't be reset`);
};
}
Object.defineProperty(target, propertyKey, {
get: getter,
set: setter
get,
set
});
}

@@ -10,6 +10,7 @@ declare const _default: {

get<T>(id: string): T;
override(id: string, factory: () => any): void;
override(id: string, factoryOrClassReference: Factory | Function, buildInstantly?: boolean): void;
isSet(id: string): boolean;
reset(): void;
};
export default _default;
declare type Factory = () => any;
type Factory = () => any;

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

}
let factory = isConstructable(factoryOrClassReference)
? () => new factoryOrClassReference()
: factoryOrClassReference;
let factory = getFactory(factoryOrClassReference);
services.push({

@@ -36,13 +34,21 @@ id: lowerId,

},
override(id, factory) {
override(id, factoryOrClassReference, buildInstantly = false) {
const lowerId = id.toLowerCase();
const index = services.findIndex(s => s.id === lowerId);
let factory = getFactory(factoryOrClassReference);
const service = {
id: lowerId,
factory,
instance: buildInstantly ? factory() : undefined,
};
if (index === -1) {
throw new Error(`No service is registered for [${id}]`);
services.push(service);
}
services[index] = {
id: lowerId,
factory
};
else {
services[index] = service;
}
},
isSet(id) {
return services.some(s => s.id === id.toLowerCase());
},
reset() {

@@ -55,1 +61,6 @@ services = [];

}
function getFactory(factoryOrClassReference) {
return isConstructable(factoryOrClassReference)
? () => new factoryOrClassReference()
: factoryOrClassReference;
}
{
"name": "ioc-service-container",
"version": "1.5.2",
"version": "1.6.0",
"description": "Lightweight ioc service container",

@@ -16,8 +16,8 @@ "main": "dist/index.js",

"devDependencies": {
"@types/jest": "^27.0.2",
"jest": "^27.3.1",
"@types/jest": "^29.2.4",
"jest": "^29.3.1",
"sonarqube-scanner": "^2.8.1",
"ts-jest": "^27.0.7",
"ts-jest": "^29.0.3",
"ts-node": "^10.4.0",
"typescript": "^4.4.4"
"typescript": "^4.9.3"
},

@@ -24,0 +24,0 @@ "files": [

@@ -14,4 +14,2 @@ # ioc-service-container

This is a lightweight library for a service container written in TypeScript.
<a href="https://www.buymeacoffee.com/Mrcwbr" target="_blank">

@@ -23,22 +21,59 @@ <img src="https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png"

> This is a lightweight **zero-dependency** library for a service container written in TypeScript.
## Features
* **Fully typed**
* **100% TypeScript written**
* **100% test coverage**
* **0 dependencies**
* **< 2 KB package size**
* **Typescript Decorator support**
* **Simple API**
* **Works beautiful with [jest-mock-extended](https://www.npmjs.com/package/jest-mock-extended)**
## Demo
In this [StackBlitz-Demo](https://stackblitz.com/edit/react-ts-qya4xy?file=App.tsx) you can see a demonstration of
the `ioc-service-container`. In the `App.tsx` you can verify that the `UserService` is fully typed without importing the
class.
![TypeScriptSupport](https://i.ibb.co/stpBrkk/type.jpg)
## Get started
Install the dependency with `npm install ioc-service-container
`
Install the dependency with `npm install ioc-service-container`
## Usage
First set up an Enum for preventing typos or redefinition of service ids in a file called `ServiceIds.ts`:
### 1. Define the Types
If you use the `ioc-service-container` in a TypeScript project, define the types of your services in a `ioc.d.ts` file
otherwise you can skip this step.
```typescript
export enum ServiceId {
TestApi = 'TestApi',
TestService = 'TestService',
FooApi = 'FooApi',
// Import your services
import { TestApi } from '../your-path/to/TestApi'
import { FooApi } from '../your-path/to/FooApi'
import { TestService } from '../your-path/to/TestService'
// Create the mapping between ServiceId and Service
type IoCTypes = {
TestApi: TestApi,
FooApi: FooApi,
TestService: TestService,
// ...
};
// Redeclare the scg function to get full Typscript support
declare module 'ioc-service-container' {
export function scg<T extends keyof IoCTypes, U extends IoCTypes[T]>(id: T): U;
}
```
According to this you have to pass a factory of your required services to the ioc container. So at the initial script of
your application you call a function named e.g. `setupService`:
### 2. Setup your services
According to this you have to pass a factory or a class reference of your required services to the ioc container. So
at the initial script of your application you call a function named e.g. `setupService`:
```typescript

@@ -48,61 +83,45 @@ import { ServiceContainer } from 'ioc-service-container';

function setupService() {
ServiceContainer.set(ServiceId.TestApi, CustomTestApi); // setup by class reference
ServiceContainer.set(ServiceId.FooApi, () => new CustomFooApi()); // setup by custom factory
ServiceContainer.set(ServiceId.Xyz, () => 'xyz');
ServiceContainer.set('TestApi', CustomTestApi); // setup by class reference
ServiceContainer.set('FooApi', () => new CustomFooApi()); // setup by custom factory
ServiceContainer.set('TestService', TestService, true); // instantieate immediately
}
```
Now you have two options to inject the requested service. The first one is without the usage of TypeScript annotations.
This can be used anywhere in your code:
The factory is only instantiated at need. You can pass the `buildInstantly` attribute if the service should be
initialized immediately e.g. for setting up [Sentry](https://sentry.io/welcome/) in a `LoggingService`.
### Assign service to a var
### 3. Inject services
```typescript
import { scg, ServiceContainer } from 'ioc-service-container';
Now you have 2 options to inject the requested service.
const testService = ServiceContainer.get<TestService>(ServiceId.TestService);
const testService1 = scg<TestService>(ServiceId.TestService); // scg is a shortcut for ServiceContainer.get()
```
#### 3.1 `scg()` Function
#### Full TypeScript Support without generics
The first is the most common one: `const testApi = scg('TestApi);`. (Shortcut for `ServiceContainer.get()`. Because of
the type declaration you have full TypeScript support at this point and no dependency on the file/class `TestApi`. (See
the [Demo](https://stackblitz.com/edit/react-ts-qya4xy?file=App.tsx))
As you can see in the example above it's very unsexy to assign a service to a constant. You have to write 3
times `testService` (constant's name, generic & ServiceId). You are able to improve the typings by adding following
content in your `ServiceIds.ts` file :
#### 3.2 `@inject` Decorator
```typescript
export enum ServiceId {
TestApi = 'TestApi',
// ...
}
> This requires `"experimentalDecorators": true` to be enabled in your `tsconfig.json`
> (See [Typescript Docs](https://www.typescriptlang.org/tsconfig#experimentalDecorators))
declare module 'ioc-service-container' {
export function scg<T extends keyof ServiceIdMap, U extends ServiceIdMap[T]>(id: T): U;
type ServiceIdMap = {
[ServiceId.TestApi]: TestApi,
}
}
```
If you now use `const a = scg(ServiceId.TestApi)`, `a` is correctly typed.
### Inject service via typescript decorator
The second option is to use the `@inject` decorator inside a class:
```typescript
export class CustomTestService implements TestService {
@inject
private readonly customApi!: Api; // Important is the naming of the property, its mapped to the serice id
private readonly customApi!: Api; // Important is the naming of the property, it's mapped to the service id
@inject(ServiceId.FooApi) // If you don't want to name your property like the service id, use this decorator
@inject('FooApi') // If you don't want to name your property like the service id, pass the id as parameter
private readonly nameThisHowYouWant!: Api;
private readonly barApi = ServiceContainer.get<Api>(ServiceId.BarApi) // Use this syntax if you don't want to use decorators
private readonly fooApi = ServiceContainer.get<Api>('FooApi') // Use this syntax if you don't want to use decorators
private readonly barApi = scg('BarApi') // Shortcut for ServiceContainer.get()
}
```
```
Your can see a demo in the `./example` folder. To run this type in `npm run example`.
### 4. Other Use-Cases
For Testing or similar use cases you have the option to
use `ServiceContainer.isSet('anId')`, `ServiceContainer.override('anId', () => 123)` or `ServiceContainer.reset()`.
## Background

@@ -123,33 +142,3 @@

## Goal
The goal of DI is to encapsulate the dependencies of a class. The CustomService should work without knowing which api it
is using. Following structure should be created:
```
+----------+ +-------------------+
| | | |
| Consumer +--->+ interface Service |
| | | |
+----------+ +---------+---------+
^
|
|
+---------+-----------+ +----------------+
| | | |
| class CustomService +---->+ interface Api |
| implements Service | | |
| | +--------+-------+
+---------------------+ ^
|
|
+--------+--------+
| |
| class CustomApi |
| implements Api |
| |
+-----------------+
```
(Btw [asciiflow.com](http://asciiflow.com/) is a great tool for creating small charts for e.g. Readme.md)
is using. The following structure should be created.
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