diff --git a/packages/ioc/package.json b/packages/ioc/package.json index c85d64b..9d63bc6 100644 --- a/packages/ioc/package.json +++ b/packages/ioc/package.json @@ -1,8 +1,19 @@ { "name": "@sern/ioc", - "version": "1.0.2", + "version": "1.0.3", "description": "Dependency Injection system", "main": "dist/index.js", + "module": "./dist/index.js", + "exports": { + "." : { + "import": "./dist/index.js", + "require": "./dist/index.js" + }, + "./global": { + "import": "./dist/global.js", + "require": "./dist/global.js" + } + }, "scripts": { "test": "vitest --run", "tdd": "vitest", diff --git a/packages/ioc/src/container.ts b/packages/ioc/src/container.ts deleted file mode 100644 index 08f5a39..0000000 --- a/packages/ioc/src/container.ts +++ /dev/null @@ -1,107 +0,0 @@ -/** - * A semi-generic container that provides error handling, emitter, and module store. - * For the handler to operate correctly, The only user provided dependency needs to be @sern/client - */ -function hasCallableMethod(obj: object, name: PropertyKey) { - //@ts-ignore - return typeof obj[name] == 'function'; -} -/** - * A Depedency injection container capable of adding singletons, firing hooks, and managing IOC within an application - */ -export class Container { - private __singletons = new Map(); - //hooks are Maps of string -> object, where object is a reference to an object in __singletons - private hooks= new Map(); - private finished_init = false; - constructor(options: { autowire: boolean; path?: string }) { - if(options.autowire) { /* noop */ } - } - - addHook(name: string, callback: object) { - if (!this.hooks.has(name)) { - this.hooks.set(name, []); - } - this.hooks.get(name)!.push(callback); - } - private registerHooks(hookname: string, insert: object) { - - if(hasCallableMethod(insert, hookname)) { - //@ts-ignore - this.addHook(hookname, insert) - } - } - - addSingleton(key: string, insert: object) { - if(typeof insert !== 'object') { - throw Error("Inserted object must be an object"); - } - if(!this.__singletons.has(key)) { - this.registerHooks('init', insert) - this.registerHooks('dispose', insert) - this.__singletons.set(key, insert); - return true; - } - return false; - } - - addWiredSingleton(key: string, fn: (c: Record) => object) { - const insert = fn(this.deps()); - return this.addSingleton(key, insert); - } - - async disposeAll() { - await this.executeHooks('dispose'); - this.hooks.delete('dispose'); - } - - isReady() { return this.finished_init; } - hasKey(key: string) { return this.__singletons.has(key); } - get(key: PropertyKey) : T|undefined { return this.__singletons.get(key); } - - async ready() { - await this.executeHooks('init'); - this.hooks.delete('init'); - this.finished_init = true; - } - - deps>(): T { - return Object.fromEntries(this.__singletons) as T - } - - private async executeHooks(name: string) { - const hookFunctions = this.hooks.get(name) || []; - for (const hookObject of hookFunctions) { - //@ts-ignore .registerHooks verifies the hookObject hasCallableMethod - await hookObject[name](); - } - } - - swap(key: string, swp: object) { - if (typeof swp !== 'object') { - throw Error("Inserted object must be an object"); - } - - const existing = this.__singletons.get(key); - if (!existing) { - return false; - } - // check if there's dispose hook, and call it - if (hasCallableMethod(existing, 'dispose')) { - //this should technically be awaited to ensure synchronicity of swap - // but i dont want to ruin the function signature of swap. - existing.dispose(); - // get the index of the existing singleton, now delete the dispose hook at that index - // .indexOf is safe because we only store singletons, and it should be a reference to - // the original object in this.__singletons - const hookIndex = this.hooks.get('dispose')!.indexOf(existing); - if (hookIndex > -1) { - this.hooks.get('dispose')!.splice(hookIndex, 1); - } - } - - this.__singletons.set(key, swp); - this.registerHooks('init', swp); - return true; - } -} diff --git a/packages/ioc/src/global.ts b/packages/ioc/src/global.ts index 1f3d6d1..c140d84 100644 --- a/packages/ioc/src/global.ts +++ b/packages/ioc/src/global.ts @@ -1,4 +1,4 @@ -import { Container } from './container'; +import { Container } from './index'; //SIDE EFFECT: GLOBAL DI let containerSubject: Container; @@ -14,23 +14,18 @@ export async function __swap_container(c: Container) { containerSubject = c; } -/** - * Don't use this unless you know what you're doing. Destroys old containerSubject if it exists and disposes everything - * then it will swap - */ -export function __add_container(key: string, v: object) { - containerSubject.addSingleton(key, v); -} /** * Initiates the global api. * Once this is finished, the Service api and the other global api is available */ -export function __init_container(options: { +export async function __init_container(options: { autowire: boolean; path?: string | undefined; }) { containerSubject = new Container(options); + await containerSubject.ready() + return containerSubject } /** @@ -49,7 +44,7 @@ export function useContainerRaw() { /** * The Service api, retrieve from the globally init'ed container * Note: this method only works AFTER your container has been initiated - * @since 3.0.0 + * @since 1.0.0 * @example * ```ts * const client = Service('@sern/client'); @@ -65,7 +60,7 @@ export function Service(key: PropertyKey) { return dep; } /** - * @since 3.0.0 + * @since 1.0.0 * The plural version of {@link Service} * @returns array of dependencies, in the same order of keys provided */ diff --git a/packages/ioc/src/index.ts b/packages/ioc/src/index.ts index 920754c..08f5a39 100644 --- a/packages/ioc/src/index.ts +++ b/packages/ioc/src/index.ts @@ -1,2 +1,107 @@ -export { Service, Services, __init_container, __add_container } from './global'; -export { Container } from './container' +/** + * A semi-generic container that provides error handling, emitter, and module store. + * For the handler to operate correctly, The only user provided dependency needs to be @sern/client + */ +function hasCallableMethod(obj: object, name: PropertyKey) { + //@ts-ignore + return typeof obj[name] == 'function'; +} +/** + * A Depedency injection container capable of adding singletons, firing hooks, and managing IOC within an application + */ +export class Container { + private __singletons = new Map(); + //hooks are Maps of string -> object, where object is a reference to an object in __singletons + private hooks= new Map(); + private finished_init = false; + constructor(options: { autowire: boolean; path?: string }) { + if(options.autowire) { /* noop */ } + } + + addHook(name: string, callback: object) { + if (!this.hooks.has(name)) { + this.hooks.set(name, []); + } + this.hooks.get(name)!.push(callback); + } + private registerHooks(hookname: string, insert: object) { + + if(hasCallableMethod(insert, hookname)) { + //@ts-ignore + this.addHook(hookname, insert) + } + } + + addSingleton(key: string, insert: object) { + if(typeof insert !== 'object') { + throw Error("Inserted object must be an object"); + } + if(!this.__singletons.has(key)) { + this.registerHooks('init', insert) + this.registerHooks('dispose', insert) + this.__singletons.set(key, insert); + return true; + } + return false; + } + + addWiredSingleton(key: string, fn: (c: Record) => object) { + const insert = fn(this.deps()); + return this.addSingleton(key, insert); + } + + async disposeAll() { + await this.executeHooks('dispose'); + this.hooks.delete('dispose'); + } + + isReady() { return this.finished_init; } + hasKey(key: string) { return this.__singletons.has(key); } + get(key: PropertyKey) : T|undefined { return this.__singletons.get(key); } + + async ready() { + await this.executeHooks('init'); + this.hooks.delete('init'); + this.finished_init = true; + } + + deps>(): T { + return Object.fromEntries(this.__singletons) as T + } + + private async executeHooks(name: string) { + const hookFunctions = this.hooks.get(name) || []; + for (const hookObject of hookFunctions) { + //@ts-ignore .registerHooks verifies the hookObject hasCallableMethod + await hookObject[name](); + } + } + + swap(key: string, swp: object) { + if (typeof swp !== 'object') { + throw Error("Inserted object must be an object"); + } + + const existing = this.__singletons.get(key); + if (!existing) { + return false; + } + // check if there's dispose hook, and call it + if (hasCallableMethod(existing, 'dispose')) { + //this should technically be awaited to ensure synchronicity of swap + // but i dont want to ruin the function signature of swap. + existing.dispose(); + // get the index of the existing singleton, now delete the dispose hook at that index + // .indexOf is safe because we only store singletons, and it should be a reference to + // the original object in this.__singletons + const hookIndex = this.hooks.get('dispose')!.indexOf(existing); + if (hookIndex > -1) { + this.hooks.get('dispose')!.splice(hookIndex, 1); + } + } + + this.__singletons.set(key, swp); + this.registerHooks('init', swp); + return true; + } +}