Skip to content

Commit

Permalink
rm redundant globalapis
Browse files Browse the repository at this point in the history
  • Loading branch information
jacoobes committed Jun 13, 2024
1 parent bc11d99 commit 7b69eeb
Show file tree
Hide file tree
Showing 4 changed files with 125 additions and 121 deletions.
13 changes: 12 additions & 1 deletion packages/ioc/package.json
Original file line number Diff line number Diff line change
@@ -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",
Expand Down
107 changes: 0 additions & 107 deletions packages/ioc/src/container.ts

This file was deleted.

17 changes: 6 additions & 11 deletions packages/ioc/src/global.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Container } from './container';
import { Container } from './index';

//SIDE EFFECT: GLOBAL DI
let containerSubject: Container;
Expand All @@ -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
}

/**
Expand All @@ -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');
Expand All @@ -65,7 +60,7 @@ export function Service<const T>(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
*/
Expand Down
109 changes: 107 additions & 2 deletions packages/ioc/src/index.ts
Original file line number Diff line number Diff line change
@@ -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<PropertyKey, any>();
//hooks are Maps of string -> object, where object is a reference to an object in __singletons
private hooks= new Map<string, object[]>();
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<string,unknown>) => 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<T>(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 extends Record<string,any>>(): 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;
}
}

0 comments on commit 7b69eeb

Please sign in to comment.