From 349f04e1b2e44d7de142918b4d4d1e217cceed8b Mon Sep 17 00:00:00 2001 From: Dexter Miguel <5452298+divmgl@users.noreply.github.com> Date: Sat, 28 Oct 2023 02:07:59 -0400 Subject: [PATCH] Server tests working --- packages/nwire/dist/Container.chatgpt.d.ts | 0 packages/nwire/dist/Container.d.ts | 13 +- packages/nwire/dist/CountingSet.d.ts | 12 ++ packages/nwire/dist/cjs/index.js | 154 +++++++++++---------- packages/nwire/dist/cjs/index.js.map | 6 +- packages/nwire/dist/esm/index.js | 154 +++++++++++---------- packages/nwire/dist/esm/index.js.map | 6 +- packages/nwire/src/Container.chatgpt.ts | 88 ++++++++++++ packages/nwire/src/Container.ts | 104 ++++++-------- packages/nwire/src/CountingSet.ts | 53 +++++++ 10 files changed, 370 insertions(+), 220 deletions(-) create mode 100644 packages/nwire/dist/Container.chatgpt.d.ts create mode 100644 packages/nwire/dist/CountingSet.d.ts create mode 100644 packages/nwire/src/Container.chatgpt.ts create mode 100644 packages/nwire/src/CountingSet.ts diff --git a/packages/nwire/dist/Container.chatgpt.d.ts b/packages/nwire/dist/Container.chatgpt.d.ts new file mode 100644 index 0000000..e69de29 diff --git a/packages/nwire/dist/Container.d.ts b/packages/nwire/dist/Container.d.ts index 293583e..f3a8b4a 100644 --- a/packages/nwire/dist/Container.d.ts +++ b/packages/nwire/dist/Container.d.ts @@ -1,3 +1,4 @@ +import { CountingSet } from "./CountingSet"; export type Context = { [key: string]: unknown; }; @@ -16,20 +17,20 @@ type RegistrationOptions = { export declare class Container { private _parentContainer?; private _registry; - private _map; + private _resolvers; private _transient; constructor(_parentContainer?: Container | undefined); static build(): Container; copy(rootContext?: Context): TContext; - context(rootContext?: Context, resolving?: Set): TWriteContext; + context(rootContext?: Context, resolved?: CountingSet): TWriteContext; group(key: TNewKey, decorator: (container: Container) => Container): Container>; static group(key: TNewKey, decorator: (container: Container) => Container): Container>; - instance(key: TNewKey, ValueClass: Instance, ...args: any[]): Container>; - static instance(key: TNewKey, ValueClass: Instance, ...args: any[]): Container>; - register(key: TNewKey, value: (context: TContext) => TValue, { transient }?: RegistrationOptions): Container>; + instance(key: TNewKey, ClassConstructor: Instance, ...args: any[]): Container>; + static instance(key: TNewKey, ClassConstructor: Instance, ...args: any[]): Container>; + register(key: TNewKey, resolver: (context: TContext, resolvedSet: CountingSet) => TValue, { transient }?: RegistrationOptions): Container>; static register(key: TNewKey, value: (context: Context) => TValue, options?: RegistrationOptions): Container>; unregister(key: TNewKey): Container>; static unregister(key: TNewKey): Container>; - resolve(key: keyof TContext): T; + resolve(key: keyof TContext, resolved?: CountingSet): T; } export {}; diff --git a/packages/nwire/dist/CountingSet.d.ts b/packages/nwire/dist/CountingSet.d.ts new file mode 100644 index 0000000..1c93b20 --- /dev/null +++ b/packages/nwire/dist/CountingSet.d.ts @@ -0,0 +1,12 @@ +export declare class CountingSet { + private readonly _map; + private readonly _set; + add(value: T): this; + delete(value: T): boolean; + has(value: T): boolean; + count(value: T): number; + clear(): void; + get size(): number; + [Symbol.iterator](): Iterator; + forEach(callbackfn: (value: T, value2: T, set: Set) => void, thisArg?: any): void; +} diff --git a/packages/nwire/dist/cjs/index.js b/packages/nwire/dist/cjs/index.js index 9adbb75..8b140a6 100644 --- a/packages/nwire/dist/cjs/index.js +++ b/packages/nwire/dist/cjs/index.js @@ -25,67 +25,94 @@ __export(src_exports, { }); module.exports = __toCommonJS(src_exports); +// src/CountingSet.ts +var CountingSet = class { + _map = /* @__PURE__ */ new Map(); + _set = /* @__PURE__ */ new Set(); + add(value) { + const count = this._map.get(value) || 0; + this._map.set(value, count + 1); + this._set.add(value); + return this; + } + delete(value) { + if (this._map.has(value)) { + const count = this._map.get(value); + if (count > 1) { + this._map.set(value, count - 1); + } else { + this._map.delete(value); + this._set.delete(value); + } + return true; + } + return false; + } + has(value) { + return this._set.has(value); + } + count(value) { + return this._map.get(value) || 0; + } + clear() { + this._map.clear(); + this._set.clear(); + } + get size() { + return this._set.size; + } + [Symbol.iterator]() { + return this._set[Symbol.iterator](); + } + forEach(callbackfn, thisArg) { + this._set.forEach(callbackfn, thisArg); + } +}; + // src/Container.ts var Container = class _Container { constructor(_parentContainer) { this._parentContainer = _parentContainer; - this._transient = /* @__PURE__ */ new Set(); - this._registry = /* @__PURE__ */ new Map(); - this._map = /* @__PURE__ */ new Map(); } - _registry; - _map; - _transient; + _registry = /* @__PURE__ */ new Map(); + _resolvers = /* @__PURE__ */ new Map(); + _transient = /* @__PURE__ */ new Set(); static build() { return new _Container(); } copy(rootContext = {}) { - const keys = Array.from(this._map.keys()); + const keys = Array.from(this._resolvers.keys()); const context = keys.reduce((acc, key) => { if (rootContext.hasOwnProperty(key)) { acc[key] = rootContext[key]; } else if (this._registry.has(key)) { acc[key] = this._registry.get(key); } else { - acc[key] = this._map.get(key)(this.context()); + acc[key] = this.resolve(key); } return acc; }, {}); return { - ...rootContext, - ...handleCircularReferences( - context - ) + ...context, + ...rootContext }; } - context(rootContext = {}, resolving = /* @__PURE__ */ new Set()) { + context(rootContext = {}, resolved = new CountingSet()) { const cache = {}; const handler = { get: (target, key) => { - const generator = this._map.get(key); - if (resolving.has(generator)) - return void 0; if (cache.hasOwnProperty(key)) return cache[key]; - if (rootContext.hasOwnProperty(key)) - return rootContext[key]; - if (!this._map.has(key)) + if (target.hasOwnProperty(key)) return target[key]; - if (this._registry.has(key)) - return this._registry.get(key); - resolving.add(generator); - const instance = generator(this.context(rootContext, resolving)); - resolving.delete(generator); - if (!this._transient.has(key)) - this._registry.set(key, instance); - return instance; + return this.resolve(key, resolved); }, - set: (target, key, value) => { + set: (_target, key, value) => { cache[key] = value; return true; } }; - const proxy = new Proxy({}, handler); + const proxy = new Proxy(rootContext, handler); return proxy; } // Add a subcontext to a property of this context @@ -98,32 +125,24 @@ var Container = class _Container { static group(key, decorator) { return _Container.build().group(key, decorator); } - instance(key, ValueClass, ...args) { - this._map.set( - key, - () => new ValueClass((this._parentContainer ?? this).context(), ...args) - ); + instance(key, ClassConstructor, ...args) { + this.register(key, (context) => new ClassConstructor(context, ...args)); return this; } - static instance(key, ValueClass, ...args) { - return _Container.build().instance(key, ValueClass, ...args); + static instance(key, ClassConstructor, ...args) { + return _Container.build().instance(key, ClassConstructor, ...args); } - register(key, value, { transient } = { transient: false }) { - this._map.set( - key, - () => value( - (this._parentContainer ?? this).context() - ) - ); + register(key, resolver, { transient } = { transient: false }) { if (transient) this._transient.add(key); + this._resolvers.set(key, resolver); return this; } static register(key, value, options) { return _Container.build().register(key, value, options); } unregister(key) { - this._map.delete(key); + this._resolvers.delete(key); this._registry.delete(key); this._transient.delete(key); return this; @@ -131,39 +150,28 @@ var Container = class _Container { static unregister(key) { return _Container.build().unregister(key); } - resolve(key) { + resolve(key, resolved = new CountingSet()) { var _a; - return (_a = this._map.get(key)) == null ? void 0 : _a(this.context()); - } -}; -function handleCircularReferences(obj, path = []) { - if (obj === null) - return null; - if (typeof obj !== "object") - return obj; - const occurrence = path.filter((p) => p === obj).length; - if (occurrence > 1) { - return void 0; - } - path.push(obj); - let result; - if (Array.isArray(obj)) { - result = obj.map( - (item) => handleCircularReferences(item, path.slice()) + if (this._registry.has(key)) { + return this._registry.get(key); + } + const resolver = this._resolvers.get(key); + if (!resolver) + return void 0; + if (resolved.count(resolver) > 1) + return void 0; + const context = this.context(void 0, resolved.add(resolver)); + const value = resolver( + ((_a = this._parentContainer) == null ? void 0 : _a.context(void 0, resolved)) ?? context, + resolved.add(resolver) ); - } else { - result = {}; - for (let key in obj) { - ; - result[key] = handleCircularReferences( - obj[key], - path.slice() - ); + resolved.delete(resolver); + if (!this._transient.has(key)) { + this._registry.set(key, value); } + return value; } - path.pop(); - return result; -} +}; // src/Injected.ts var Injected = class { diff --git a/packages/nwire/dist/cjs/index.js.map b/packages/nwire/dist/cjs/index.js.map index 83e9b48..e60d1d2 100644 --- a/packages/nwire/dist/cjs/index.js.map +++ b/packages/nwire/dist/cjs/index.js.map @@ -1,7 +1,7 @@ { "version": 3, - "sources": ["../../src/index.ts", "../../src/Container.ts", "../../src/Injected.ts"], - "sourcesContent": ["export { Container, Instance } from \"./Container\"\nexport { Injected } from \"./Injected\"\n", "export type Context = {\n [key: string]: unknown\n}\nexport type Instance = {\n new (context: any, ...args: any[]): TValue\n}\n\ntype Flatten = {} & { [P in keyof T]: T[P] }\n\ntype MergeContext = Flatten<\n TExisting & {\n [P in TKey]: TValue\n }\n>\n\ntype RegistrationOptions = {\n transient?: boolean\n}\n\nexport class Container {\n private _registry: Map\n private _map: Map unknown>\n private _transient: Set\n\n constructor(private _parentContainer?: Container) {\n this._transient = new Set()\n this._registry = new Map()\n this._map = new Map unknown>()\n }\n\n static build(): Container {\n return new Container()\n }\n\n copy(rootContext: Context = {}): TContext {\n // Get all of the keys in the map\n const keys = Array.from(this._map.keys())\n\n // Create an object that either has the value from the root context, the value from the registry\n // or gets the value from the map's generator.\n const context = keys.reduce((acc: Record, key) => {\n if (rootContext.hasOwnProperty(key)) {\n acc[key] = rootContext[key]\n } else if (this._registry.has(key)) {\n acc[key] = this._registry.get(key)\n } else {\n acc[key] = this._map.get(key)!(this.context())\n }\n\n return acc\n }, {}) as TContext\n\n return {\n ...rootContext,\n ...(handleCircularReferences(\n context as unknown as Serializable\n ) as TContext),\n }\n }\n\n context(\n rootContext: Context = {},\n resolving: Set = new Set()\n ): TWriteContext {\n const cache: Record = {}\n\n const handler = {\n get: (target: Context, key: string) => {\n const generator = this._map.get(key)!\n if (resolving.has(generator)) return undefined\n if (cache.hasOwnProperty(key)) return cache[key]\n if (rootContext.hasOwnProperty(key)) return rootContext[key]\n if (!this._map.has(key)) return target[key]\n if (this._registry.has(key)) return this._registry.get(key)\n\n resolving.add(generator)\n const instance = generator(this.context(rootContext, resolving))\n resolving.delete(generator)\n if (!this._transient.has(key)) this._registry.set(key, instance)\n\n return instance\n },\n set: (target: Context, key: string, value: unknown) => {\n cache[key] = value\n return true\n },\n }\n\n const proxy = new Proxy({} as TContext, handler)\n\n return proxy as unknown as TWriteContext\n }\n\n // Add a subcontext to a property of this context\n group(\n key: TNewKey,\n decorator: (container: Container) => Container\n ): Container> {\n const nestedContainer = new Container(this._parentContainer ?? this)\n const value = decorator(nestedContainer).context()\n this.register(key, () => value)\n return this as any\n }\n\n static group(\n key: TNewKey,\n decorator: (container: Container) => Container\n ): Container> {\n return Container.build().group(key, decorator) as any\n }\n\n instance(\n key: TNewKey,\n ValueClass: Instance,\n ...args: any[]\n ): Container> {\n this._map.set(\n key,\n () => new ValueClass((this._parentContainer ?? this).context(), ...args)\n )\n return this as any\n }\n\n static instance(\n key: TNewKey,\n ValueClass: Instance,\n ...args: any[]\n ): Container> {\n return Container.build().instance(key, ValueClass, ...args) as any\n }\n\n register(\n key: TNewKey,\n value: (context: TContext) => TValue,\n { transient }: RegistrationOptions = { transient: false }\n ): Container> {\n this._map.set(key, () =>\n value(\n (this._parentContainer ?? this).context() as MergeContext<\n TContext,\n TNewKey,\n TValue\n >\n )\n )\n\n if (transient) this._transient.add(key)\n\n return this as any\n }\n\n static register(\n key: TNewKey,\n value: (context: Context) => TValue,\n options?: RegistrationOptions\n ): Container> {\n return Container.build().register(key, value, options) as any\n }\n\n unregister(\n key: TNewKey\n ): Container> {\n this._map.delete(key)\n this._registry.delete(key)\n this._transient.delete(key)\n\n return this as any\n }\n\n static unregister(\n key: TNewKey\n ): Container> {\n return Container.build().unregister(key) as any\n }\n\n resolve(key: keyof TContext): T {\n return this._map.get(key as string)?.(this.context()) as unknown as T\n }\n}\n\ntype Serializable =\n | null\n | string\n | number\n | boolean\n | undefined\n | { [key: string]: Serializable }\n | Serializable[]\n\nfunction handleCircularReferences(\n obj: Serializable,\n path: Serializable[] = []\n): Serializable {\n if (obj === null) return null\n if (typeof obj !== \"object\") return obj\n\n const occurrence = path.filter((p) => p === obj).length\n\n // If this object appears more than once in the current path, it's a circular reference.\n if (occurrence > 1) {\n return undefined\n }\n\n path.push(obj)\n\n let result: Serializable\n if (Array.isArray(obj)) {\n result = obj.map((item) =>\n handleCircularReferences(item, path.slice())\n ) as Serializable[]\n } else {\n result = {}\n for (let key in obj) {\n ;(result as any)[key] = handleCircularReferences(\n (obj as any)[key],\n path.slice()\n )\n }\n }\n\n path.pop()\n\n return result\n}\n", "import { Context } from \"./Container\"\n\nexport class Injected {\n constructor(protected _context: TContext) {}\n}\n"], - "mappings": "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", + "sources": ["../../src/index.ts", "../../src/CountingSet.ts", "../../src/Container.ts", "../../src/Injected.ts"], + "sourcesContent": ["export { Container, Instance } from \"./Container\"\nexport { Injected } from \"./Injected\"\n", "export class CountingSet {\n private readonly _map = new Map()\n private readonly _set = new Set()\n\n add(value: T): this {\n const count = this._map.get(value) || 0\n this._map.set(value, count + 1)\n this._set.add(value)\n return this\n }\n\n delete(value: T): boolean {\n if (this._map.has(value)) {\n const count = this._map.get(value)!\n if (count > 1) {\n this._map.set(value, count - 1)\n } else {\n this._map.delete(value)\n this._set.delete(value)\n }\n return true\n }\n return false\n }\n\n has(value: T): boolean {\n return this._set.has(value)\n }\n\n count(value: T): number {\n return this._map.get(value) || 0\n }\n\n clear(): void {\n this._map.clear()\n this._set.clear()\n }\n\n get size(): number {\n return this._set.size\n }\n\n [Symbol.iterator](): Iterator {\n return this._set[Symbol.iterator]()\n }\n\n forEach(\n callbackfn: (value: T, value2: T, set: Set) => void,\n thisArg?: any\n ): void {\n this._set.forEach(callbackfn, thisArg)\n }\n}\n", "import { CountingSet } from \"./CountingSet\"\n\nexport type Context = {\n [key: string]: unknown\n}\n\nexport type Instance = {\n new (context: any, ...args: any[]): TValue\n}\n\ntype Flatten = {} & { [P in keyof T]: T[P] }\n\ntype MergeContext = Flatten<\n TExisting & {\n [P in TKey]: TValue\n }\n>\n\ntype RegistrationOptions = {\n transient?: boolean\n}\n\nexport class Container {\n private _registry: Map = new Map()\n private _resolvers: Map<\n string,\n (context: TContext, resolved: CountingSet) => unknown\n > = new Map unknown>()\n private _transient: Set = new Set()\n\n constructor(private _parentContainer?: Container) {}\n\n static build(): Container {\n return new Container()\n }\n\n copy(rootContext: Context = {}): TContext {\n // Get all of the keys in the map\n const keys = Array.from(this._resolvers.keys())\n\n // Create an object that either has the value from the root context, the value from the registry\n // or gets the value from the map's generator.\n const context = keys.reduce((acc: Record, key) => {\n if (rootContext.hasOwnProperty(key)) {\n acc[key] = rootContext[key]\n } else if (this._registry.has(key)) {\n acc[key] = this._registry.get(key)\n } else {\n acc[key] = this.resolve(key)\n }\n\n return acc\n }, {}) as TContext\n\n return {\n ...context,\n ...rootContext,\n } as TContext\n }\n\n context(\n rootContext: Context = {},\n resolved: CountingSet = new CountingSet()\n ): TWriteContext {\n const cache: Record = {}\n\n const handler = {\n get: (target: TContext, key: string) => {\n if (cache.hasOwnProperty(key)) return cache[key]\n if (target.hasOwnProperty(key)) return target[key]\n return this.resolve(key, resolved)\n },\n set: (_target: Context, key: string, value: unknown) => {\n cache[key] = value\n return true\n },\n }\n\n const proxy = new Proxy(rootContext as TContext, handler)\n\n return proxy as unknown as TWriteContext\n }\n\n // Add a subcontext to a property of this context\n group(\n key: TNewKey,\n decorator: (container: Container) => Container\n ): Container> {\n const nestedContainer = new Container(this._parentContainer ?? this)\n const value = decorator(nestedContainer).context()\n this.register(key, () => value)\n return this as any\n }\n\n static group(\n key: TNewKey,\n decorator: (container: Container) => Container\n ): Container> {\n return Container.build().group(key, decorator) as any\n }\n\n instance(\n key: TNewKey,\n ClassConstructor: Instance,\n ...args: any[]\n ): Container> {\n this.register(key, (context) => new ClassConstructor(context, ...args))\n return this as any\n }\n\n static instance(\n key: TNewKey,\n ClassConstructor: Instance,\n ...args: any[]\n ): Container> {\n return Container.build().instance(key, ClassConstructor, ...args) as any\n }\n\n register(\n key: TNewKey,\n resolver: (context: TContext, resolvedSet: CountingSet) => TValue,\n { transient }: RegistrationOptions = { transient: false }\n ): Container> {\n if (transient) this._transient.add(key)\n this._resolvers.set(key, resolver)\n return this as any\n }\n\n static register(\n key: TNewKey,\n value: (context: Context) => TValue,\n options?: RegistrationOptions\n ): Container> {\n return Container.build().register(key, value, options) as any\n }\n\n unregister(\n key: TNewKey\n ): Container> {\n this._resolvers.delete(key)\n this._registry.delete(key)\n this._transient.delete(key)\n\n return this as any\n }\n\n static unregister(\n key: TNewKey\n ): Container> {\n return Container.build().unregister(key) as any\n }\n\n resolve(\n key: keyof TContext,\n resolved: CountingSet = new CountingSet()\n ): T {\n if (this._registry.has(key as string)) {\n return this._registry.get(key as string) as unknown as T\n }\n\n const resolver = this._resolvers.get(key as string)!\n if (!resolver) return undefined as unknown as T\n if (resolved.count(resolver) > 1) return undefined as unknown as T\n\n const context = this.context(undefined, resolved.add(resolver))\n\n const value = resolver(\n this._parentContainer?.context(undefined, resolved) ?? context,\n resolved.add(resolver)\n ) as unknown as T\n\n resolved.delete(resolver)\n\n if (!this._transient.has(key as string)) {\n this._registry.set(key as string, value)\n }\n\n return value\n }\n}\n\ntype Serializable =\n | null\n | string\n | number\n | boolean\n | undefined\n | { [key: string]: Serializable }\n | Serializable[]\n\nfunction handleCircularReferences(\n obj: Serializable,\n path: Serializable[] = []\n): Serializable {\n if (obj === null) return null\n if (typeof obj !== \"object\") return obj\n\n const occurrence = path.filter((p) => p === obj).length\n\n // If this object appears more than once in the current path, it's a circular reference.\n if (occurrence > 1) {\n return undefined\n }\n\n // path.push(obj)\n\n let result: Serializable\n if (Array.isArray(obj)) {\n result = obj.map((item) =>\n handleCircularReferences(item, path.slice())\n ) as Serializable[]\n } else {\n result = {}\n for (let key in obj) {\n ;(result as any)[key] = handleCircularReferences(\n (obj as any)[key],\n path.slice()\n )\n }\n }\n\n path.pop()\n\n return result\n}\n", "import { Context } from \"./Container\"\n\nexport class Injected {\n constructor(protected _context: TContext) {}\n}\n"], + "mappings": "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", "names": [] } diff --git a/packages/nwire/dist/esm/index.js b/packages/nwire/dist/esm/index.js index 9121339..f92f924 100644 --- a/packages/nwire/dist/esm/index.js +++ b/packages/nwire/dist/esm/index.js @@ -1,64 +1,91 @@ +// src/CountingSet.ts +var CountingSet = class { + _map = /* @__PURE__ */ new Map(); + _set = /* @__PURE__ */ new Set(); + add(value) { + const count = this._map.get(value) || 0; + this._map.set(value, count + 1); + this._set.add(value); + return this; + } + delete(value) { + if (this._map.has(value)) { + const count = this._map.get(value); + if (count > 1) { + this._map.set(value, count - 1); + } else { + this._map.delete(value); + this._set.delete(value); + } + return true; + } + return false; + } + has(value) { + return this._set.has(value); + } + count(value) { + return this._map.get(value) || 0; + } + clear() { + this._map.clear(); + this._set.clear(); + } + get size() { + return this._set.size; + } + [Symbol.iterator]() { + return this._set[Symbol.iterator](); + } + forEach(callbackfn, thisArg) { + this._set.forEach(callbackfn, thisArg); + } +}; + // src/Container.ts var Container = class _Container { constructor(_parentContainer) { this._parentContainer = _parentContainer; - this._transient = /* @__PURE__ */ new Set(); - this._registry = /* @__PURE__ */ new Map(); - this._map = /* @__PURE__ */ new Map(); } - _registry; - _map; - _transient; + _registry = /* @__PURE__ */ new Map(); + _resolvers = /* @__PURE__ */ new Map(); + _transient = /* @__PURE__ */ new Set(); static build() { return new _Container(); } copy(rootContext = {}) { - const keys = Array.from(this._map.keys()); + const keys = Array.from(this._resolvers.keys()); const context = keys.reduce((acc, key) => { if (rootContext.hasOwnProperty(key)) { acc[key] = rootContext[key]; } else if (this._registry.has(key)) { acc[key] = this._registry.get(key); } else { - acc[key] = this._map.get(key)(this.context()); + acc[key] = this.resolve(key); } return acc; }, {}); return { - ...rootContext, - ...handleCircularReferences( - context - ) + ...context, + ...rootContext }; } - context(rootContext = {}, resolving = /* @__PURE__ */ new Set()) { + context(rootContext = {}, resolved = new CountingSet()) { const cache = {}; const handler = { get: (target, key) => { - const generator = this._map.get(key); - if (resolving.has(generator)) - return void 0; if (cache.hasOwnProperty(key)) return cache[key]; - if (rootContext.hasOwnProperty(key)) - return rootContext[key]; - if (!this._map.has(key)) + if (target.hasOwnProperty(key)) return target[key]; - if (this._registry.has(key)) - return this._registry.get(key); - resolving.add(generator); - const instance = generator(this.context(rootContext, resolving)); - resolving.delete(generator); - if (!this._transient.has(key)) - this._registry.set(key, instance); - return instance; + return this.resolve(key, resolved); }, - set: (target, key, value) => { + set: (_target, key, value) => { cache[key] = value; return true; } }; - const proxy = new Proxy({}, handler); + const proxy = new Proxy(rootContext, handler); return proxy; } // Add a subcontext to a property of this context @@ -71,32 +98,24 @@ var Container = class _Container { static group(key, decorator) { return _Container.build().group(key, decorator); } - instance(key, ValueClass, ...args) { - this._map.set( - key, - () => new ValueClass((this._parentContainer ?? this).context(), ...args) - ); + instance(key, ClassConstructor, ...args) { + this.register(key, (context) => new ClassConstructor(context, ...args)); return this; } - static instance(key, ValueClass, ...args) { - return _Container.build().instance(key, ValueClass, ...args); + static instance(key, ClassConstructor, ...args) { + return _Container.build().instance(key, ClassConstructor, ...args); } - register(key, value, { transient } = { transient: false }) { - this._map.set( - key, - () => value( - (this._parentContainer ?? this).context() - ) - ); + register(key, resolver, { transient } = { transient: false }) { if (transient) this._transient.add(key); + this._resolvers.set(key, resolver); return this; } static register(key, value, options) { return _Container.build().register(key, value, options); } unregister(key) { - this._map.delete(key); + this._resolvers.delete(key); this._registry.delete(key); this._transient.delete(key); return this; @@ -104,39 +123,28 @@ var Container = class _Container { static unregister(key) { return _Container.build().unregister(key); } - resolve(key) { + resolve(key, resolved = new CountingSet()) { var _a; - return (_a = this._map.get(key)) == null ? void 0 : _a(this.context()); - } -}; -function handleCircularReferences(obj, path = []) { - if (obj === null) - return null; - if (typeof obj !== "object") - return obj; - const occurrence = path.filter((p) => p === obj).length; - if (occurrence > 1) { - return void 0; - } - path.push(obj); - let result; - if (Array.isArray(obj)) { - result = obj.map( - (item) => handleCircularReferences(item, path.slice()) + if (this._registry.has(key)) { + return this._registry.get(key); + } + const resolver = this._resolvers.get(key); + if (!resolver) + return void 0; + if (resolved.count(resolver) > 1) + return void 0; + const context = this.context(void 0, resolved.add(resolver)); + const value = resolver( + ((_a = this._parentContainer) == null ? void 0 : _a.context(void 0, resolved)) ?? context, + resolved.add(resolver) ); - } else { - result = {}; - for (let key in obj) { - ; - result[key] = handleCircularReferences( - obj[key], - path.slice() - ); + resolved.delete(resolver); + if (!this._transient.has(key)) { + this._registry.set(key, value); } + return value; } - path.pop(); - return result; -} +}; // src/Injected.ts var Injected = class { diff --git a/packages/nwire/dist/esm/index.js.map b/packages/nwire/dist/esm/index.js.map index e18d793..efbbe43 100644 --- a/packages/nwire/dist/esm/index.js.map +++ b/packages/nwire/dist/esm/index.js.map @@ -1,7 +1,7 @@ { "version": 3, - "sources": ["../../src/Container.ts", "../../src/Injected.ts"], - "sourcesContent": ["export type Context = {\n [key: string]: unknown\n}\nexport type Instance = {\n new (context: any, ...args: any[]): TValue\n}\n\ntype Flatten = {} & { [P in keyof T]: T[P] }\n\ntype MergeContext = Flatten<\n TExisting & {\n [P in TKey]: TValue\n }\n>\n\ntype RegistrationOptions = {\n transient?: boolean\n}\n\nexport class Container {\n private _registry: Map\n private _map: Map unknown>\n private _transient: Set\n\n constructor(private _parentContainer?: Container) {\n this._transient = new Set()\n this._registry = new Map()\n this._map = new Map unknown>()\n }\n\n static build(): Container {\n return new Container()\n }\n\n copy(rootContext: Context = {}): TContext {\n // Get all of the keys in the map\n const keys = Array.from(this._map.keys())\n\n // Create an object that either has the value from the root context, the value from the registry\n // or gets the value from the map's generator.\n const context = keys.reduce((acc: Record, key) => {\n if (rootContext.hasOwnProperty(key)) {\n acc[key] = rootContext[key]\n } else if (this._registry.has(key)) {\n acc[key] = this._registry.get(key)\n } else {\n acc[key] = this._map.get(key)!(this.context())\n }\n\n return acc\n }, {}) as TContext\n\n return {\n ...rootContext,\n ...(handleCircularReferences(\n context as unknown as Serializable\n ) as TContext),\n }\n }\n\n context(\n rootContext: Context = {},\n resolving: Set = new Set()\n ): TWriteContext {\n const cache: Record = {}\n\n const handler = {\n get: (target: Context, key: string) => {\n const generator = this._map.get(key)!\n if (resolving.has(generator)) return undefined\n if (cache.hasOwnProperty(key)) return cache[key]\n if (rootContext.hasOwnProperty(key)) return rootContext[key]\n if (!this._map.has(key)) return target[key]\n if (this._registry.has(key)) return this._registry.get(key)\n\n resolving.add(generator)\n const instance = generator(this.context(rootContext, resolving))\n resolving.delete(generator)\n if (!this._transient.has(key)) this._registry.set(key, instance)\n\n return instance\n },\n set: (target: Context, key: string, value: unknown) => {\n cache[key] = value\n return true\n },\n }\n\n const proxy = new Proxy({} as TContext, handler)\n\n return proxy as unknown as TWriteContext\n }\n\n // Add a subcontext to a property of this context\n group(\n key: TNewKey,\n decorator: (container: Container) => Container\n ): Container> {\n const nestedContainer = new Container(this._parentContainer ?? this)\n const value = decorator(nestedContainer).context()\n this.register(key, () => value)\n return this as any\n }\n\n static group(\n key: TNewKey,\n decorator: (container: Container) => Container\n ): Container> {\n return Container.build().group(key, decorator) as any\n }\n\n instance(\n key: TNewKey,\n ValueClass: Instance,\n ...args: any[]\n ): Container> {\n this._map.set(\n key,\n () => new ValueClass((this._parentContainer ?? this).context(), ...args)\n )\n return this as any\n }\n\n static instance(\n key: TNewKey,\n ValueClass: Instance,\n ...args: any[]\n ): Container> {\n return Container.build().instance(key, ValueClass, ...args) as any\n }\n\n register(\n key: TNewKey,\n value: (context: TContext) => TValue,\n { transient }: RegistrationOptions = { transient: false }\n ): Container> {\n this._map.set(key, () =>\n value(\n (this._parentContainer ?? this).context() as MergeContext<\n TContext,\n TNewKey,\n TValue\n >\n )\n )\n\n if (transient) this._transient.add(key)\n\n return this as any\n }\n\n static register(\n key: TNewKey,\n value: (context: Context) => TValue,\n options?: RegistrationOptions\n ): Container> {\n return Container.build().register(key, value, options) as any\n }\n\n unregister(\n key: TNewKey\n ): Container> {\n this._map.delete(key)\n this._registry.delete(key)\n this._transient.delete(key)\n\n return this as any\n }\n\n static unregister(\n key: TNewKey\n ): Container> {\n return Container.build().unregister(key) as any\n }\n\n resolve(key: keyof TContext): T {\n return this._map.get(key as string)?.(this.context()) as unknown as T\n }\n}\n\ntype Serializable =\n | null\n | string\n | number\n | boolean\n | undefined\n | { [key: string]: Serializable }\n | Serializable[]\n\nfunction handleCircularReferences(\n obj: Serializable,\n path: Serializable[] = []\n): Serializable {\n if (obj === null) return null\n if (typeof obj !== \"object\") return obj\n\n const occurrence = path.filter((p) => p === obj).length\n\n // If this object appears more than once in the current path, it's a circular reference.\n if (occurrence > 1) {\n return undefined\n }\n\n path.push(obj)\n\n let result: Serializable\n if (Array.isArray(obj)) {\n result = obj.map((item) =>\n handleCircularReferences(item, path.slice())\n ) as Serializable[]\n } else {\n result = {}\n for (let key in obj) {\n ;(result as any)[key] = handleCircularReferences(\n (obj as any)[key],\n path.slice()\n )\n }\n }\n\n path.pop()\n\n return result\n}\n", "import { Context } from \"./Container\"\n\nexport class Injected {\n constructor(protected _context: TContext) {}\n}\n"], - "mappings": ";AAmBO,IAAM,YAAN,MAAM,WAAyC;AAAA,EAKpD,YAAoB,kBAAwC;AAAxC;AAClB,SAAK,aAAa,oBAAI,IAAY;AAClC,SAAK,YAAY,oBAAI,IAAqB;AAC1C,SAAK,OAAO,oBAAI,IAA4C;AAAA,EAC9D;AAAA,EARQ;AAAA,EACA;AAAA,EACA;AAAA,EAQR,OAAO,QAA8C;AACnD,WAAO,IAAI,WAAa;AAAA,EAC1B;AAAA,EAEA,KAAe,cAAuB,CAAC,GAAa;AAElD,UAAM,OAAO,MAAM,KAAK,KAAK,KAAK,KAAK,CAAC;AAIxC,UAAM,UAAU,KAAK,OAAO,CAAC,KAA8B,QAAQ;AACjE,UAAI,YAAY,eAAe,GAAG,GAAG;AACnC,YAAI,GAAG,IAAI,YAAY,GAAG;AAAA,MAC5B,WAAW,KAAK,UAAU,IAAI,GAAG,GAAG;AAClC,YAAI,GAAG,IAAI,KAAK,UAAU,IAAI,GAAG;AAAA,MACnC,OAAO;AACL,YAAI,GAAG,IAAI,KAAK,KAAK,IAAI,GAAG,EAAG,KAAK,QAAQ,CAAC;AAAA,MAC/C;AAEA,aAAO;AAAA,IACT,GAAG,CAAC,CAAC;AAEL,WAAO;AAAA,MACL,GAAG;AAAA,MACH,GAAI;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EAEA,QACE,cAAuB,CAAC,GACxB,YAA0B,oBAAI,IAAa,GAC5B;AACf,UAAM,QAAiC,CAAC;AAExC,UAAM,UAAU;AAAA,MACd,KAAK,CAAC,QAAiB,QAAgB;AACrC,cAAM,YAAY,KAAK,KAAK,IAAI,GAAG;AACnC,YAAI,UAAU,IAAI,SAAS;AAAG,iBAAO;AACrC,YAAI,MAAM,eAAe,GAAG;AAAG,iBAAO,MAAM,GAAG;AAC/C,YAAI,YAAY,eAAe,GAAG;AAAG,iBAAO,YAAY,GAAG;AAC3D,YAAI,CAAC,KAAK,KAAK,IAAI,GAAG;AAAG,iBAAO,OAAO,GAAG;AAC1C,YAAI,KAAK,UAAU,IAAI,GAAG;AAAG,iBAAO,KAAK,UAAU,IAAI,GAAG;AAE1D,kBAAU,IAAI,SAAS;AACvB,cAAM,WAAW,UAAU,KAAK,QAAQ,aAAa,SAAS,CAAC;AAC/D,kBAAU,OAAO,SAAS;AAC1B,YAAI,CAAC,KAAK,WAAW,IAAI,GAAG;AAAG,eAAK,UAAU,IAAI,KAAK,QAAQ;AAE/D,eAAO;AAAA,MACT;AAAA,MACA,KAAK,CAAC,QAAiB,KAAa,UAAmB;AACrD,cAAM,GAAG,IAAI;AACb,eAAO;AAAA,MACT;AAAA,IACF;AAEA,UAAM,QAAQ,IAAI,MAAgB,CAAC,GAAe,OAAO;AAEzD,WAAO;AAAA,EACT;AAAA;AAAA,EAGA,MACE,KACA,WACyD;AACzD,UAAM,kBAAkB,IAAI,WAAU,KAAK,oBAAoB,IAAI;AACnE,UAAM,QAAQ,UAAU,eAAe,EAAE,QAAQ;AACjD,SAAK,SAAS,KAAK,MAAM,KAAK;AAC9B,WAAO;AAAA,EACT;AAAA,EAEA,OAAO,MACL,KACA,WACwD;AACxD,WAAO,WAAU,MAAM,EAAE,MAAM,KAAK,SAAS;AAAA,EAC/C;AAAA,EAEA,SACE,KACA,eACG,MACiD;AACpD,SAAK,KAAK;AAAA,MACR;AAAA,MACA,MAAM,IAAI,YAAY,KAAK,oBAAoB,MAAM,QAAQ,GAAG,GAAG,IAAI;AAAA,IACzE;AACA,WAAO;AAAA,EACT;AAAA,EAEA,OAAO,SACL,KACA,eACG,MACgD;AACnD,WAAO,WAAU,MAAM,EAAE,SAAS,KAAK,YAAY,GAAG,IAAI;AAAA,EAC5D;AAAA,EAEA,SACE,KACA,OACA,EAAE,UAAU,IAAyB,EAAE,WAAW,MAAM,GACJ;AACpD,SAAK,KAAK;AAAA,MAAI;AAAA,MAAK,MACjB;AAAA,SACG,KAAK,oBAAoB,MAAM,QAAQ;AAAA,MAK1C;AAAA,IACF;AAEA,QAAI;AAAW,WAAK,WAAW,IAAI,GAAG;AAEtC,WAAO;AAAA,EACT;AAAA,EAEA,OAAO,SACL,KACA,OACA,SACmD;AACnD,WAAO,WAAU,MAAM,EAAE,SAAS,KAAK,OAAO,OAAO;AAAA,EACvD;AAAA,EAEA,WACE,KACoC;AACpC,SAAK,KAAK,OAAO,GAAG;AACpB,SAAK,UAAU,OAAO,GAAG;AACzB,SAAK,WAAW,OAAO,GAAG;AAE1B,WAAO;AAAA,EACT;AAAA,EAEA,OAAO,WACL,KACmC;AACnC,WAAO,WAAU,MAAM,EAAE,WAAW,GAAG;AAAA,EACzC;AAAA,EAEA,QAAW,KAAwB;AA/KrC;AAgLI,YAAO,UAAK,KAAK,IAAI,GAAa,MAA3B,mBAA+B,KAAK,QAAQ;AAAA,EACrD;AACF;AAWA,SAAS,yBACP,KACA,OAAuB,CAAC,GACV;AACd,MAAI,QAAQ;AAAM,WAAO;AACzB,MAAI,OAAO,QAAQ;AAAU,WAAO;AAEpC,QAAM,aAAa,KAAK,OAAO,CAAC,MAAM,MAAM,GAAG,EAAE;AAGjD,MAAI,aAAa,GAAG;AAClB,WAAO;AAAA,EACT;AAEA,OAAK,KAAK,GAAG;AAEb,MAAI;AACJ,MAAI,MAAM,QAAQ,GAAG,GAAG;AACtB,aAAS,IAAI;AAAA,MAAI,CAAC,SAChB,yBAAyB,MAAM,KAAK,MAAM,CAAC;AAAA,IAC7C;AAAA,EACF,OAAO;AACL,aAAS,CAAC;AACV,aAAS,OAAO,KAAK;AACnB;AAAC,MAAC,OAAe,GAAG,IAAI;AAAA,QACrB,IAAY,GAAG;AAAA,QAChB,KAAK,MAAM;AAAA,MACb;AAAA,IACF;AAAA,EACF;AAEA,OAAK,IAAI;AAET,SAAO;AACT;;;AC7NO,IAAM,WAAN,MAAyC;AAAA,EAC9C,YAAsB,UAAoB;AAApB;AAAA,EAAqB;AAC7C;", + "sources": ["../../src/CountingSet.ts", "../../src/Container.ts", "../../src/Injected.ts"], + "sourcesContent": ["export class CountingSet {\n private readonly _map = new Map()\n private readonly _set = new Set()\n\n add(value: T): this {\n const count = this._map.get(value) || 0\n this._map.set(value, count + 1)\n this._set.add(value)\n return this\n }\n\n delete(value: T): boolean {\n if (this._map.has(value)) {\n const count = this._map.get(value)!\n if (count > 1) {\n this._map.set(value, count - 1)\n } else {\n this._map.delete(value)\n this._set.delete(value)\n }\n return true\n }\n return false\n }\n\n has(value: T): boolean {\n return this._set.has(value)\n }\n\n count(value: T): number {\n return this._map.get(value) || 0\n }\n\n clear(): void {\n this._map.clear()\n this._set.clear()\n }\n\n get size(): number {\n return this._set.size\n }\n\n [Symbol.iterator](): Iterator {\n return this._set[Symbol.iterator]()\n }\n\n forEach(\n callbackfn: (value: T, value2: T, set: Set) => void,\n thisArg?: any\n ): void {\n this._set.forEach(callbackfn, thisArg)\n }\n}\n", "import { CountingSet } from \"./CountingSet\"\n\nexport type Context = {\n [key: string]: unknown\n}\n\nexport type Instance = {\n new (context: any, ...args: any[]): TValue\n}\n\ntype Flatten = {} & { [P in keyof T]: T[P] }\n\ntype MergeContext = Flatten<\n TExisting & {\n [P in TKey]: TValue\n }\n>\n\ntype RegistrationOptions = {\n transient?: boolean\n}\n\nexport class Container {\n private _registry: Map = new Map()\n private _resolvers: Map<\n string,\n (context: TContext, resolved: CountingSet) => unknown\n > = new Map unknown>()\n private _transient: Set = new Set()\n\n constructor(private _parentContainer?: Container) {}\n\n static build(): Container {\n return new Container()\n }\n\n copy(rootContext: Context = {}): TContext {\n // Get all of the keys in the map\n const keys = Array.from(this._resolvers.keys())\n\n // Create an object that either has the value from the root context, the value from the registry\n // or gets the value from the map's generator.\n const context = keys.reduce((acc: Record, key) => {\n if (rootContext.hasOwnProperty(key)) {\n acc[key] = rootContext[key]\n } else if (this._registry.has(key)) {\n acc[key] = this._registry.get(key)\n } else {\n acc[key] = this.resolve(key)\n }\n\n return acc\n }, {}) as TContext\n\n return {\n ...context,\n ...rootContext,\n } as TContext\n }\n\n context(\n rootContext: Context = {},\n resolved: CountingSet = new CountingSet()\n ): TWriteContext {\n const cache: Record = {}\n\n const handler = {\n get: (target: TContext, key: string) => {\n if (cache.hasOwnProperty(key)) return cache[key]\n if (target.hasOwnProperty(key)) return target[key]\n return this.resolve(key, resolved)\n },\n set: (_target: Context, key: string, value: unknown) => {\n cache[key] = value\n return true\n },\n }\n\n const proxy = new Proxy(rootContext as TContext, handler)\n\n return proxy as unknown as TWriteContext\n }\n\n // Add a subcontext to a property of this context\n group(\n key: TNewKey,\n decorator: (container: Container) => Container\n ): Container> {\n const nestedContainer = new Container(this._parentContainer ?? this)\n const value = decorator(nestedContainer).context()\n this.register(key, () => value)\n return this as any\n }\n\n static group(\n key: TNewKey,\n decorator: (container: Container) => Container\n ): Container> {\n return Container.build().group(key, decorator) as any\n }\n\n instance(\n key: TNewKey,\n ClassConstructor: Instance,\n ...args: any[]\n ): Container> {\n this.register(key, (context) => new ClassConstructor(context, ...args))\n return this as any\n }\n\n static instance(\n key: TNewKey,\n ClassConstructor: Instance,\n ...args: any[]\n ): Container> {\n return Container.build().instance(key, ClassConstructor, ...args) as any\n }\n\n register(\n key: TNewKey,\n resolver: (context: TContext, resolvedSet: CountingSet) => TValue,\n { transient }: RegistrationOptions = { transient: false }\n ): Container> {\n if (transient) this._transient.add(key)\n this._resolvers.set(key, resolver)\n return this as any\n }\n\n static register(\n key: TNewKey,\n value: (context: Context) => TValue,\n options?: RegistrationOptions\n ): Container> {\n return Container.build().register(key, value, options) as any\n }\n\n unregister(\n key: TNewKey\n ): Container> {\n this._resolvers.delete(key)\n this._registry.delete(key)\n this._transient.delete(key)\n\n return this as any\n }\n\n static unregister(\n key: TNewKey\n ): Container> {\n return Container.build().unregister(key) as any\n }\n\n resolve(\n key: keyof TContext,\n resolved: CountingSet = new CountingSet()\n ): T {\n if (this._registry.has(key as string)) {\n return this._registry.get(key as string) as unknown as T\n }\n\n const resolver = this._resolvers.get(key as string)!\n if (!resolver) return undefined as unknown as T\n if (resolved.count(resolver) > 1) return undefined as unknown as T\n\n const context = this.context(undefined, resolved.add(resolver))\n\n const value = resolver(\n this._parentContainer?.context(undefined, resolved) ?? context,\n resolved.add(resolver)\n ) as unknown as T\n\n resolved.delete(resolver)\n\n if (!this._transient.has(key as string)) {\n this._registry.set(key as string, value)\n }\n\n return value\n }\n}\n\ntype Serializable =\n | null\n | string\n | number\n | boolean\n | undefined\n | { [key: string]: Serializable }\n | Serializable[]\n\nfunction handleCircularReferences(\n obj: Serializable,\n path: Serializable[] = []\n): Serializable {\n if (obj === null) return null\n if (typeof obj !== \"object\") return obj\n\n const occurrence = path.filter((p) => p === obj).length\n\n // If this object appears more than once in the current path, it's a circular reference.\n if (occurrence > 1) {\n return undefined\n }\n\n // path.push(obj)\n\n let result: Serializable\n if (Array.isArray(obj)) {\n result = obj.map((item) =>\n handleCircularReferences(item, path.slice())\n ) as Serializable[]\n } else {\n result = {}\n for (let key in obj) {\n ;(result as any)[key] = handleCircularReferences(\n (obj as any)[key],\n path.slice()\n )\n }\n }\n\n path.pop()\n\n return result\n}\n", "import { Context } from \"./Container\"\n\nexport class Injected {\n constructor(protected _context: TContext) {}\n}\n"], + "mappings": "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", "names": [] } diff --git a/packages/nwire/src/Container.chatgpt.ts b/packages/nwire/src/Container.chatgpt.ts new file mode 100644 index 0000000..424b7e5 --- /dev/null +++ b/packages/nwire/src/Container.chatgpt.ts @@ -0,0 +1,88 @@ +// export class Container> { +// private registry: { [key: string]: Function } = {} +// private singletonCache: { [key: string]: any } = {} + +// constructor(private parentContext?: Container) {} + +// static build() { +// return new Container() +// } + +// register(key: string, factory: Function) { +// this.registry[key] = factory +// return this +// } + +// static register(key: string, factory: Function) { +// const container = new Container() +// container.register(key, factory) +// return container +// } + +// unregister(key: string) { +// delete this.registry[key] +// return this +// } + +// instance( +// key: K, +// ClassConstructor: new (...args: any[]) => TContext[K], +// ...args: any[] +// ) { +// this.register(key, (context: TContext) => { +// if (!this.singletonCache[key]) { +// this.singletonCache[key] = new ClassConstructor(context, ...args) +// } +// return this.singletonCache[key] +// }) +// return this +// } + +// static instance( +// key: K, +// ClassConstructor: new (...args: any[]) => T[K], +// ...args: any[] +// ) { +// const container = new Container() +// container.instance(key, ClassConstructor, ...args) +// return container +// } + +// group( +// key: K, +// groupFunc: (groupContainer: Container) => void +// ) { +// const groupContainer = new Container(this) +// groupFunc(groupContainer) +// this.register(key, () => groupContainer.context()) +// return this +// } + +// context(): TContext { +// const resolvingStack = new Set() +// return new Proxy({} as TContext, { +// get: (target, prop: keyof TContext) => { +// if (resolvingStack.has(prop)) { +// return undefined // Circular reference detected +// } +// resolvingStack.add(prop) +// let value: any +// if (this.registry[prop]) { +// value = this.registry[prop](this.context()) +// } else if (this.parentContext) { +// value = this.parentContext.context()[prop] +// } +// resolvingStack.delete(prop) +// return value +// }, +// }) +// } + +// resolve(key: K): TContext[K] { +// const service = this.context()[key] +// if (!service) { +// throw new Error(`Service not found: ${key}`) +// } +// return service +// } +// } diff --git a/packages/nwire/src/Container.ts b/packages/nwire/src/Container.ts index 175a8f5..e496e1e 100644 --- a/packages/nwire/src/Container.ts +++ b/packages/nwire/src/Container.ts @@ -1,6 +1,9 @@ +import { CountingSet } from "./CountingSet" + export type Context = { [key: string]: unknown } + export type Instance = { new (context: any, ...args: any[]): TValue } @@ -21,11 +24,9 @@ export class Container { private _registry: Map = new Map() private _resolvers: Map< string, - (context: TContext, resolved: Set) => unknown + (context: TContext, resolved: CountingSet) => unknown > = new Map unknown>() private _transient: Set = new Set() - private _registeredDecorators: Set = new Set() - private _decoratorMap: Map = new Map() constructor(private _parentContainer?: Container) {} @@ -45,38 +46,29 @@ export class Container { } else if (this._registry.has(key)) { acc[key] = this._registry.get(key) } else { - acc[key] = this._resolvers.get(key)!(this.context(), new Set()) + acc[key] = this.resolve(key) } return acc }, {}) as TContext return { + ...context, ...rootContext, - ...(handleCircularReferences( - context as unknown as Serializable - ) as TContext), - } + } as TContext } context( rootContext: Context = {}, - resolved: Set = new Set() + resolved: CountingSet = new CountingSet() ): TWriteContext { const cache: Record = {} - // console.log({ start: resolving }) const handler = { get: (target: TContext, key: string) => { if (cache.hasOwnProperty(key)) return cache[key] if (target.hasOwnProperty(key)) return target[key] - if (this._registry.has(key)) return this._registry.get(key) - if (!this._resolvers.has(key)) return target[key] - - const resolver = this._resolvers.get(key)! - const instance = resolver(target, resolved.add(resolver)) - - return instance + return this.resolve(key, resolved) }, set: (_target: Context, key: string, value: unknown) => { cache[key] = value @@ -109,51 +101,28 @@ export class Container { instance( key: TNewKey, - ValueClass: Instance, + ClassConstructor: Instance, ...args: any[] ): Container> { - this.register(key, (context) => new ValueClass(context, ...args)) + this.register(key, (context) => new ClassConstructor(context, ...args)) return this as any } static instance( key: TNewKey, - ValueClass: Instance, + ClassConstructor: Instance, ...args: any[] ): Container> { - return Container.build().instance(key, ValueClass, ...args) as any + return Container.build().instance(key, ClassConstructor, ...args) as any } register( key: TNewKey, - resolver: (context: TContext, resolvedSet: Set) => TValue, + resolver: (context: TContext, resolvedSet: CountingSet) => TValue, { transient }: RegistrationOptions = { transient: false } ): Container> { if (transient) this._transient.add(key) - - this._resolvers.set(key, (_context, resolvedSet = new Set()) => { - // Check if the key is already in the registry. If it is, return it. - if (this._registry.has(key)) return this._registry.get(key) - - if (resolvedSet.has(resolver)) return - - const effectiveContainer = this._parentContainer ?? this - const value = resolver( - effectiveContainer.context( - undefined, - resolvedSet.add(resolver) - ) as MergeContext, - // new Set() - resolvedSet.add(resolver) - ) - - resolvedSet.delete(resolver) - - if (!this._transient.has(key)) this._registry.set(key, value) - - return value - }) - + this._resolvers.set(key, resolver) return this as any } @@ -169,15 +138,7 @@ export class Container { key: TNewKey ): Container> { this._resolvers.delete(key) - - const value = this._registry.get(key) - if (value) { - const ValueClass = this._decoratorMap.get(value) - - this._registeredDecorators.delete(ValueClass) - this._registry.delete(key) - } - + this._registry.delete(key) this._transient.delete(key) return this as any @@ -189,11 +150,32 @@ export class Container { return Container.build().unregister(key) as any } - resolve(key: keyof TContext): T { - return this._resolvers.get(key as string)?.( - this.context(), - new Set() + resolve( + key: keyof TContext, + resolved: CountingSet = new CountingSet() + ): T { + if (this._registry.has(key as string)) { + return this._registry.get(key as string) as unknown as T + } + + const resolver = this._resolvers.get(key as string)! + if (!resolver) return undefined as unknown as T + if (resolved.count(resolver) > 1) return undefined as unknown as T + + const context = this.context(undefined, resolved.add(resolver)) + + const value = resolver( + this._parentContainer?.context(undefined, resolved) ?? context, + resolved.add(resolver) ) as unknown as T + + resolved.delete(resolver) + + if (!this._transient.has(key as string)) { + this._registry.set(key as string, value) + } + + return value } } @@ -220,9 +202,7 @@ function handleCircularReferences( return undefined } - path.push(obj) - - console.log(path) + // path.push(obj) let result: Serializable if (Array.isArray(obj)) { diff --git a/packages/nwire/src/CountingSet.ts b/packages/nwire/src/CountingSet.ts new file mode 100644 index 0000000..ceaaa88 --- /dev/null +++ b/packages/nwire/src/CountingSet.ts @@ -0,0 +1,53 @@ +export class CountingSet { + private readonly _map = new Map() + private readonly _set = new Set() + + add(value: T): this { + const count = this._map.get(value) || 0 + this._map.set(value, count + 1) + this._set.add(value) + return this + } + + delete(value: T): boolean { + if (this._map.has(value)) { + const count = this._map.get(value)! + if (count > 1) { + this._map.set(value, count - 1) + } else { + this._map.delete(value) + this._set.delete(value) + } + return true + } + return false + } + + has(value: T): boolean { + return this._set.has(value) + } + + count(value: T): number { + return this._map.get(value) || 0 + } + + clear(): void { + this._map.clear() + this._set.clear() + } + + get size(): number { + return this._set.size + } + + [Symbol.iterator](): Iterator { + return this._set[Symbol.iterator]() + } + + forEach( + callbackfn: (value: T, value2: T, set: Set) => void, + thisArg?: any + ): void { + this._set.forEach(callbackfn, thisArg) + } +}