Skip to content
This repository has been archived by the owner on Jun 8, 2023. It is now read-only.

noel-archive/Lilith

Repository files navigation

🧡 Lilith

Application framework for TypeScript to build robust, and simple services

Lilith is Noelware's framework to build JavaScript-based microservices with TypeScript! It is used to build robust applications with TypeScript with the tools you need to build it! Lilith comes with pre-built libraries that makes it easy to work with:

  • @noelware/lilith-logging ~ Package to handle logging scenarios, combined with the @noelware/lilith-config package also!
  • @noelware/lilith-config ~ Package to handle different configuration sources, that are easily injectable with the @Variable decorator.

Usage

Warning β€” Lilith v6 and above is not compatible with older versions of Lilith, so you will need to refractor your whole application that was built upon Lilith. You can read up on the migration section.

$ npm install @noelware/lilith
$ yarn add @noelware/lilith
$ pnpm install @noelware/lilith
import { createLilith, singleton, service, inject } from '@noelware/lilith';

const container = createLilith({
  singletons: [
    // Defines a path to load singletons from
    { path: './path/to/singletons' },
    singleton<Logger>({
      provide() { return Logger.create(); }
      onLoad(logger /*: Logger */) { /* ran once singleton is loaded into the container */ },
      onDestroy(logger /*: Logger */) { /* destroy singleton */ }
    })
  ],

  services: [
    // Defines a path to load services from
    { path: './path/to/services' },
    service({
      name: 'myservice',
      children: [/* list of children that this service has control over */],
      onLoad() { /* called for loading the service (i.e, start http server/load config) */ },
      onDestroy() { /* called for destroying the service (i.e, stopping http service) */ },
      onChildLoad(child /*: any */) { /* called for loading children into the service scope */ },
      onChildDestroy(child /*: any */) { /* called for destroying children into the service scope */ },
    })
  ]
});

const logger = inject('logger');
// => Logger

const service = inject('myservice');
// => Service

Packages

@noelware/lilith

@noelware/lilith is the main library that ties together with the new packages like @noelware/lilith-logging. You use @noelware/lilith to manage the lifecycle of the managed IoC container to do dependency injection out of the box.

@noelware/lilith doesn't need any peer dependencies, but you will need to have reflect-metadata loaded before using @noelware/lilith because it depends on it!

$ npm install @noelware/lilith
$ yarn add @noelware/lilith
$ pnpm install @noelware/lilith
import { Service, Inject, createLilith } from '@noelware/lilith';

@Service({ name: 'a name' })
class MyService {
  @Inject
  private readonly other!: OtherService;

  onLoad() {
    console.log('I have loaded!');
  }
}

@Service({ name: 'another name', priority: 10 })
class OtherService {
  private readonly lucky: number = 42;

  get luckyNumber() {
    return this.lucky;
  }
}

const container = createLilith({
  services: [MyService, OtherService]
});

container.start();
  • Lilith will construct the container as a export of Container,
  • When you call start, the IoC hooks will be attached to the services in this minimal example,
  • Since services can be a higher priority, the services that have a high priority will be initialized first
    • You can't inject components that are even a higher priority in a high priority service since services are not lazily constructed (i.e, needed when it needs to be used). They're loaded automatically, only singletons are lazily loaded.
    • You can't have service names with the same name, you will get a TypeError thrown.
| ~~~~~~~~~~~~~~ |  /----> | another name |
| ioc container  | -             / \
| ~~~~~~~~~~~~~~ |  \----> |    a name    |

@noelware/lilith-logging

@noelware/lilith-logging is a service package that lets you inject a LoggerFactoryService into your services and creates a Logger for using logging. This package requires a peer dependency on winston:

$ npm install @noelware/lilith @noelware/lilith-logging @noelware/lilith-logging-winston winston
$ yarn add @noelware/lilith @noelware/lilith-logging @noelware/lilith-logging-winston winston
$ pnpm install @noelware/lilith @noelware/lilith-logging @noelware/lilith-logging-winston winston
import { Service, Inject, createLilith } from '@noelware/lilith';
import { LogService, type Logger } from '@noelware/lilith-logging';
import { WinstonBackend } from '@noelware/lilith-logging-winston';
import winston from 'winston';

@Service({ name: 'my service name' })
class MyService {
  @Inject
  private readonly logging!: LogService<WinstonBackend>;
  private readonly logger!: Logger;

  onLoad() {
    this.logger = this.logging.loggerFactory.get('my', 'service', 'info');
    this.logger.info('I am loading stuff!');
  }
}

const container = createLilith({
  services: [
    new LogService({
      defaultLevel: 'debug',
      backend: new WinstonBackend({
        transports: [new winston.transports.Console()]
      })
    }),
    MyService
  ]
});

container.start();

@noelware/lilith-config

@noelware/lilith-config is a service that gives you a way to simplify configuration files with a Zod schema. It has loaders for:

  • YAML (requires js-yaml)
  • JSON (no extra dependencies)
  • TOML (requires @ltd/j-toml)

@noelware/lilith-config has a peer dependency on zod if you wish to have schema validation. It is not required to have zod installed, it'll just ignore the configuration schema if provided.

$ npm install @noelware/lilith @noelware/lilith-config zod
$ yarn add @noelware/lilith @noelware/lilith-config zod
$ pnpm install @noelware/lilith @noelware/lilith-config zod
import { Service, Inject, createLilith } from '@noelware/lilith';
import { ConfigService, YamlLoader } from '@noelware/lilith-config';
import z from 'zod';

export interface Config {
  lol: boolean;
}

@Service({ name: 'my service' })
class MyService {
  @Inject
  private readonly config!: ConfigService<Config>;

  onLoad() {
    const shouldLol = this.config.get('lol');
    if (shouldLol) {
      console.log('lol!!!!');
    }
  }
}

const container = createLilith({
  services: [
    new ConfigService<Config>({
      schema: z
        .object({
          lol: z.boolean()
        })
        .strict(),

      loader: new YamlLoader()
    }),
    MyService
  ]
});

container.start();
const service = container.inject(MyService);

Contributing

Thanks for considering contributing to Lilith! Before you boop your heart out on your keyboard ✧ ─=≑Σ((( ぀‒̀ω‒́)぀, we recommend you to do the following:

If you read both if you're a new time contributor, now you can do the following:

  • Fork me! **β™‘( βŽα΅•α΄—α΅•βŽ οΌ‰
  • Clone your fork on your machine: git clone https://github.com/your-username/Lilith
  • Create a new branch: git checkout -b some-branch-name
  • BOOP THAT KEYBOARD!!!! ♑┉ˏ͛ (❛ γ€° ❛)ΛŠΛŽβ”‰β™‘
  • Commit your changes onto your branch: git commit -am "add features (q>β€Ώβ€Ώ<q οΌ‰"
  • Push it to the fork you created: git push -u origin some-branch-name
  • Submit a Pull Request and then cry! qο½₯゚゚ο½₯(ΰ°₯ Π” ΰ°₯。)ο½₯゚゚ο½₯q

License

Lilith is released under the MIT License with love by Noelware. :3