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

@tsxper/service-manager

v2.0.0

Published

TypeScript extension to the Service Locator design pattern, that helps with managing application dependencies.

Downloads

14

Readme

Service-Manager

NPM Version License: MIT npm type definitions NPM Downloads

@tsxper/service-manager is a TypeScript extension to the Service Locator design pattern, that helps with managing application dependencies.

Service manager can be helpful for apps and services that do not require big frameworks with complex dependency injection setup.

It's designed to be used in browser and NodeJS apps.

Advantages of using ServiceManager:

  • Easy setup.
  • Comfortable integration testing.
  • Small size (ServiceManager is implemented as one class).
  • CommonJS and ESM modules support.
  • Aimed to be used in all JavaScript projects (NodeJS and browsers).
  • Doesn't use code generators.
  • Doesn't rely on experimental technology, like decorators.

Examples

See other examples in GitHub.

With using registered factories you can easily inject required dependencies in your services.

Imaging you have a service "DownstreamService" which depends on "LoggerService" and "VaultService".

// define your applications services
export class DownstreamService {
  constructor(logger: LoggerService, vault: VaultService) {
    // ...
  }
}


// sm.ts - add Service Manager configuration with factories
export const sm = new ServiceManager({
  'logger': () => new LoggerService(),
  'vault': () => {
    const url = process.env.VAULT_URL;
    if (!url) throw new Error('VAULT_URL is not set');
    return new VaultService(url);
  },
}).add('downstream', async (sm) => {
  // place for some async calls
  return new DownstreamService(sm.get('logger'), sm.get('vault'));
});


// app.ts - use Service Manager in your app
const downstream = await sm.get('downstream'); // we use "await" because associated factory is async function
await downstream.sendData(records);
sm.get('logger').log('Success');


// __tests__/app.test.ts - easily change behavior in tests
sm.replace(
  'vault',
  () => new VaultFake('http://test/')
);

Caching Instances

By default, all created instances are cached (shared instances). To retrieve a private instance of a service, pass "true" as second argument to "get()" method. See example below.

const privateInstance = sm.get('logger', true);

Disable/Enable Caching

Service instances cache is enabled by default.

Calling disableCache()/enableCache() does not remove existing cached instances.

sm.disableCache();
sm.enableCache();

Clear Services Cache

Call "cleanCache()" method to clear service cache.

Call "destroy()" method to clear service cache and registered factories.

sm.cleanCache(); // clean cache
// or 
sm.destroy(); // clean cache and registered factories

Local vs Global Cache

ServiceManager supports 2 types of cache: "global" (for runtime) and "local" (for particular app instance). Good use case for a "local" cache is tests isolation. To control cache type, ServiceManager constructor has a "useGlobalCache" as 2nd parameter (default is "true").

const useGlobalCache = false;
new ServiceManager({...}, useGlobalCache);

See builder example.

Types Inference

Types inference for registered services is supported.

// Checks for retrieving only registered services
const vault = sm.get('unregistered');
// TypeScript Error: Argument of type '"unregistered"' is not assignable to parameter of type '"logger" | "vault"'.

const vault = sm.get('vault'); 
// vault: VaultService

Known Limitations

  1. Using strings instead of literals. Example: TypeScript does not derive literal from a class name.
// this code will work
const serviceName = 'LoggerService';
const sm = new ServiceManager({[serviceName]: () => new LoggerService()});
const loggerService = sm.get(serviceName); // serviceName is string literal

// this code will NOT work in TypeScript
const sm = new ServiceManager({ [Fruit.name]: () => new Fruit(1), 'Article': () => new Article('title') });
sm.get(Fruit.name).weight; // Fruit.name is a "string" and can't be associated with a concrete service
// Property 'weight' does not exist on type 'Fruit | Article'.

To solve this issue, define a literal property on your class, for example:

class Fruit {
  static service: 'Fruit' = 'Fruit';
}
const sm = new ServiceManager({[Fruit.service]: () => new Fruit()});
  1. Retrieve services in JavaScript.

In plain JavaScript, sm.get('logger') will not check that 'logger' was set or that 'logger' is associated with Logger service factory. What you can do is to add type guards.

Note. In VS Code you can enable types check for JavaScript files with adding a line // @ts-check ;

// @ts-check 
const sm = new ServiceManager({'logger': () => new Logger()});
const logger = sm.get('logger');
if (!(logger instanceof Logger)) throw new Error('"logger" is not Logger');
logger.log();

License

MIT license.