@ -0,0 +1,16 @@ | |||||
{ | |||||
"name": "@autoplex/microservice", | |||||
"version": "0.0.0", | |||||
"main": "dist/lib/index.js", | |||||
"types": "dist/typings/index.d.ts", | |||||
"license": "MIT", | |||||
"scripts": { | |||||
"build": "yarn run clean && tsc", | |||||
"clean": "rimraf ./dist" | |||||
}, | |||||
"devDependencies": { | |||||
"@types/node": "^15.0.1", | |||||
"rimraf": "^3.0.2", | |||||
"typescript": "^4.2.4" | |||||
} | |||||
} |
@ -0,0 +1,60 @@ | |||||
import { Microservice } from "./Microservice"; | |||||
/** | |||||
* A generic service | |||||
*/ | |||||
export abstract class InternalService<T extends Microservice = Microservice> | |||||
{ | |||||
/** | |||||
* The application instance | |||||
*/ | |||||
protected readonly app: T; | |||||
/** | |||||
* Enable/disable logging for this service | |||||
*/ | |||||
public logging: boolean = true; | |||||
/** | |||||
* Create a new service | |||||
*/ | |||||
public constructor(app: T) { | |||||
this.app = app; | |||||
} | |||||
// Required Service Implementation ------------------------------------------------------------- | |||||
/** | |||||
* The service name | |||||
*/ | |||||
public abstract get name(): string; | |||||
/** | |||||
* Boot the service | |||||
*/ | |||||
public abstract boot(): Promise<void>; | |||||
/** | |||||
* Shut the application down | |||||
*/ | |||||
public abstract shutdown(): Promise<void>; | |||||
// Miscellaneous ------------------------------------------------------------------------------ | |||||
/** | |||||
* Indicate the application is ready | |||||
*/ | |||||
public start() { | |||||
// no-op | |||||
}; | |||||
/** | |||||
* Service-specific logging | |||||
*/ | |||||
public log(...args: any[]) { | |||||
if (!this.logging) { | |||||
return; | |||||
} | |||||
console.log(`[${this.name}]:`, ...args); | |||||
} | |||||
} |
@ -0,0 +1,122 @@ | |||||
import { InternalService } from "./InternalService"; | |||||
import assert from "assert"; | |||||
/** | |||||
* Application InternalService map | |||||
*/ | |||||
interface InternalServiceMap { | |||||
[name: string]: InternalService | |||||
} | |||||
/** | |||||
* The InternalService constructor type | |||||
*/ | |||||
type InternalServiceConstructor<T extends Microservice = Microservice> = new (app: T) => InternalService<T>; | |||||
/** | |||||
* The main application class | |||||
*/ | |||||
export class Microservice | |||||
{ | |||||
/** | |||||
* Maintain a static reference to the application instance | |||||
*/ | |||||
private static __instance: Microservice; | |||||
/** | |||||
* All available InternalServices | |||||
*/ | |||||
protected services: InternalServiceMap = {}; | |||||
/** | |||||
* Return the current application instance | |||||
*/ | |||||
public static instance() { | |||||
return this.__instance; | |||||
} | |||||
/** | |||||
* Create a new application instance | |||||
*/ | |||||
public constructor() { | |||||
Microservice.__instance = this; | |||||
} | |||||
// Overridable -------------------------------------------------------------------------------- | |||||
/** | |||||
* Invoked when the application has finished booting | |||||
*/ | |||||
protected onStart(): void|Promise<void> {} | |||||
// Application Management ---------------------------------------------------------------------- | |||||
/** | |||||
* Boot the application and all of the InternalServices | |||||
*/ | |||||
protected async boot() { | |||||
let InternalServices = Object.values(this.services); | |||||
return Promise.all(InternalServices.map(InternalService => InternalService.boot())); | |||||
} | |||||
/** | |||||
* Shutdown the application | |||||
*/ | |||||
protected shutdown() { | |||||
let InternalServices = Object.values(this.services); | |||||
return Promise.all(InternalServices.map(InternalService => InternalService.shutdown())); | |||||
} | |||||
/** | |||||
* Start the application | |||||
*/ | |||||
public async exec() { | |||||
await this.boot(); | |||||
await this.onStart(); | |||||
for (let InternalService of Object.values(this.services)) { | |||||
InternalService.start(); | |||||
} | |||||
} | |||||
/** | |||||
* Quit the application | |||||
*/ | |||||
public async quit(code: number = 0) { | |||||
await this.shutdown(); | |||||
process.exit(code); | |||||
} | |||||
// InternalService Management -------------------------------------------------------------------------- | |||||
/** | |||||
* Install InternalServices into the application | |||||
*/ | |||||
protected installServices<T extends Microservice>(this: T, InternalServices: InternalServiceConstructor<T>[]) { | |||||
for (let InternalServiceClass of InternalServices) { | |||||
this.installService(InternalServiceClass); | |||||
} | |||||
} | |||||
/** | |||||
* Install a InternalService into the application | |||||
*/ | |||||
protected installService<T extends Microservice>(this: T, InternalServiceClass: InternalServiceConstructor<T>) { | |||||
let InternalService = new InternalServiceClass(this); | |||||
this.services[InternalService.name] = InternalService; | |||||
} | |||||
/** | |||||
* Get all available InternalServices | |||||
*/ | |||||
public serviceList() { | |||||
return Object.keys(this.services); | |||||
} | |||||
/** | |||||
* Get an application InternalService instance | |||||
*/ | |||||
public service<T extends InternalService<Microservice>>(InternalServiceName: string) { | |||||
assert(InternalServiceName in this.services); | |||||
return <T>this.services[InternalServiceName]; | |||||
} | |||||
} |
@ -0,0 +1,2 @@ | |||||
export * from "./Microservice"; | |||||
export * from "./InternalService"; |
@ -0,0 +1,7 @@ | |||||
{ | |||||
"extends": "../../tsconfig.package.json", | |||||
"compilerOptions": { | |||||
"outDir": "dist/lib", | |||||
"declarationDir": "dist/typings" | |||||
} | |||||
} |
@ -0,0 +1,92 @@ | |||||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. | |||||
# yarn lockfile v1 | |||||
"@types/node@^15.0.1": | |||||
version "15.0.1" | |||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-15.0.1.tgz#ef34dea0881028d11398be5bf4e856743e3dc35a" | |||||
integrity sha512-TMkXt0Ck1y0KKsGr9gJtWGjttxlZnnvDtphxUOSd0bfaR6Q1jle+sPvrzNR1urqYTWMinoKvjKfXUGsumaO1PA== | |||||
balanced-match@^1.0.0: | |||||
version "1.0.2" | |||||
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" | |||||
integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== | |||||
brace-expansion@^1.1.7: | |||||
version "1.1.11" | |||||
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" | |||||
integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== | |||||
dependencies: | |||||
balanced-match "^1.0.0" | |||||
concat-map "0.0.1" | |||||
concat-map@0.0.1: | |||||
version "0.0.1" | |||||
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" | |||||
integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= | |||||
fs.realpath@^1.0.0: | |||||
version "1.0.0" | |||||
resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" | |||||
integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= | |||||
glob@^7.1.3: | |||||
version "7.1.6" | |||||
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" | |||||
integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== | |||||
dependencies: | |||||
fs.realpath "^1.0.0" | |||||
inflight "^1.0.4" | |||||
inherits "2" | |||||
minimatch "^3.0.4" | |||||
once "^1.3.0" | |||||
path-is-absolute "^1.0.0" | |||||
inflight@^1.0.4: | |||||
version "1.0.6" | |||||
resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" | |||||
integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= | |||||
dependencies: | |||||
once "^1.3.0" | |||||
wrappy "1" | |||||
inherits@2: | |||||
version "2.0.4" | |||||
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" | |||||
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== | |||||
minimatch@^3.0.4: | |||||
version "3.0.4" | |||||
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" | |||||
integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== | |||||
dependencies: | |||||
brace-expansion "^1.1.7" | |||||
once@^1.3.0: | |||||
version "1.4.0" | |||||
resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" | |||||
integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= | |||||
dependencies: | |||||
wrappy "1" | |||||
path-is-absolute@^1.0.0: | |||||
version "1.0.1" | |||||
resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" | |||||
integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= | |||||
rimraf@^3.0.2: | |||||
version "3.0.2" | |||||
resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" | |||||
integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== | |||||
dependencies: | |||||
glob "^7.1.3" | |||||
typescript@^4.2.4: | |||||
version "4.2.4" | |||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.2.4.tgz#8610b59747de028fda898a8aef0e103f156d0961" | |||||
integrity sha512-V+evlYHZnQkaz8TRBuxTA92yZBPotr5H+WhQ7bD3hZUndx5tGOa1fuCgeSjxAzM1RiN5IzvadIXTVefuuwZCRg== | |||||
wrappy@1: | |||||
version "1.0.2" | |||||
resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" | |||||
integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= |