ts-ioc-container 55.6.0 → 55.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -53,6 +53,7 @@ provider pipelines, aliases, and custom injector strategies.
53
53
  - [Module](#module)
54
54
  - [Hook](#hook) `@hook`
55
55
  - [OnConstruct](#onconstruct) `@onConstruct`
56
+ - [OnConstructAsync](#onconstructasync) `@onConstructAsync`
56
57
  - [OnContainerDisposed](#oncontainerdisposed) `@onContainerDisposed`
57
58
  - [Inject Property](#inject-property)
58
59
  - [Inject Method](#inject-method)
@@ -2584,6 +2585,94 @@ describe('onConstruct', function () {
2584
2585
 
2585
2586
  ```
2586
2587
 
2588
+ ### OnConstructAsync
2589
+
2590
+ `@onConstructAsync` runs promise-returning initialization. Resolution stays
2591
+ synchronous: `AddOnConstructAsyncHookModule` starts the hooks when the instance
2592
+ is created and they settle afterwards, so `resolve` returns before they finish.
2593
+
2594
+ ```typescript
2595
+ import 'reflect-metadata';
2596
+ import {
2597
+ AddOnConstructAsyncHookModule,
2598
+ Container,
2599
+ type ExecutionContext,
2600
+ type HookFn,
2601
+ inject,
2602
+ onConstructAsync,
2603
+ Registration as R,
2604
+ } from 'ts-ioc-container';
2605
+
2606
+ const execute: HookFn = async (ctx) => {
2607
+ await ctx.invokeMethod({ args: ctx.resolveArgs() });
2608
+ };
2609
+
2610
+ describe('onConstructAsync', function () {
2611
+ it('should run an async initialization method after the instance is created', async function () {
2612
+ class DatabaseConnection {
2613
+ isConnected = false;
2614
+ connectionString = '';
2615
+ readonly ready: Promise<void>;
2616
+
2617
+ private markReady!: () => void;
2618
+
2619
+ constructor() {
2620
+ this.ready = new Promise((resolve) => {
2621
+ this.markReady = resolve;
2622
+ });
2623
+ }
2624
+
2625
+ @onConstructAsync(execute)
2626
+ async connect(@inject('ConnectionString') connectionString: string) {
2627
+ await Promise.resolve();
2628
+ this.connectionString = connectionString;
2629
+ this.isConnected = true;
2630
+ this.markReady();
2631
+ }
2632
+ }
2633
+
2634
+ const container = new Container()
2635
+ .useModule(new AddOnConstructAsyncHookModule())
2636
+ .addRegistration(R.fromValue('postgres://localhost:5432').bindTo('ConnectionString'));
2637
+
2638
+ const db = container.resolve(DatabaseConnection);
2639
+
2640
+ // resolution does not wait for async hooks
2641
+ expect(db.isConnected).toBe(false);
2642
+
2643
+ await db.ready;
2644
+
2645
+ expect(db.isConnected).toBe(true);
2646
+ expect(db.connectionString).toBe('postgres://localhost:5432');
2647
+ });
2648
+
2649
+ it('should forward rejected hooks to the onException handler with the execution context', async function () {
2650
+ const failure = new Error('boom');
2651
+
2652
+ class BrokenService {
2653
+ @onConstructAsync(() => Promise.reject(failure))
2654
+ init() {}
2655
+ }
2656
+
2657
+ let captured: { ex: unknown; context: ExecutionContext } | undefined;
2658
+ const container = new Container().useModule(
2659
+ new AddOnConstructAsyncHookModule((ex, context) => {
2660
+ captured = { ex, context };
2661
+ }),
2662
+ );
2663
+
2664
+ const child = container.createScope();
2665
+ child.resolve(BrokenService);
2666
+
2667
+ await vi.waitFor(() => expect(captured).toBeDefined());
2668
+
2669
+ expect(captured?.ex).toBe(failure);
2670
+ expect(captured?.context.scope).toBe(child);
2671
+ });
2672
+ });
2673
+
2674
+ ```
2675
+
2587
2676
  ### OnContainerDisposed
2588
2677
 
2589
2678
  ```typescript
@@ -0,0 +1,28 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.AddOnConstructAsyncHookModule = exports.onConstructAsync = exports.onConstructAsyncHooksRunner = void 0;
4
+ const hook_1 = require("./hook");
5
+ const HooksRunner_1 = require("./HooksRunner");
6
+ exports.onConstructAsyncHooksRunner = new HooksRunner_1.HooksRunner('onConstructAsync');
7
+ const onConstructAsync = (...fns) => (0, hook_1.hook)('onConstructAsync', ...fns);
8
+ exports.onConstructAsync = onConstructAsync;
9
+ class AddOnConstructAsyncHookModule {
10
+ onException;
11
+ constructor(onException) {
12
+ this.onException = onException;
13
+ }
14
+ applyTo(container) {
15
+ container.addOnConstructHook((instance, scope) => {
16
+ if (!exports.onConstructAsyncHooksRunner.hasHooks(instance)) {
17
+ return;
18
+ }
19
+ exports.onConstructAsyncHooksRunner.executeAsync(instance, { scope }).catch((ex) => {
20
+ if (!this.onException) {
21
+ throw ex;
22
+ }
23
+ this.onException(ex, { scope });
24
+ });
25
+ });
26
+ }
27
+ }
28
+ exports.AddOnConstructAsyncHookModule = AddOnConstructAsyncHookModule;
package/cjm/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.ClassToken = exports.toSingleAlias = exports.SingleAliasToken = exports.toGroupAlias = exports.GroupAliasToken = exports.InjectionToken = exports.HooksRunner = exports.AddOnDisposeHookModule = exports.onContainerDisposed = exports.onContainerDisposedHooksRunner = exports.AddOnConstructHookModule = exports.onConstruct = exports.onConstructHooksRunner = exports.injectProp = exports.createHookContext = exports.createHookContextFactory = exports.HookContext = exports.hasHooks = exports.hook = exports.getHooks = exports.CannonSingletonApplyTwiceError = exports.UnexpectedHookResultError = exports.ProviderDisposedError = exports.ContainerDisposedError = exports.MethodNotImplementedError = exports.DependencyMissingKeyError = exports.ContainerNotFoundError = exports.DependencyNotFoundError = exports.Registration = exports.appendArgsFn = exports.appendArgs = exports.decorate = exports.singleton = exports.lazy = exports.scopeAccess = exports.scope = exports.bindTo = exports.register = exports.Provider = exports.ProxyInjector = exports.SimpleInjector = exports.resolveArgs = exports.argsFn = exports.args = exports.inject = exports.MetadataInjector = exports.Injector = exports.EmptyContainer = exports.Container = exports.isDependencyKey = void 0;
4
- exports.resolveConstructor = exports.Is = exports.pipe = exports.select = exports.once = exports.shallowCache = exports.debounce = exports.throttle = exports.handleAsyncError = exports.handleError = exports.getMethodTags = exports.addMethodTag = exports.getMethodLabels = exports.addMethodLabel = exports.getMethodMeta = exports.addMethodMeta = exports.getParamTags = exports.addParamTag = exports.getParamLabels = exports.addParamLabel = exports.getParamMeta = exports.addParamMeta = exports.getClassTags = exports.addClassTag = exports.getClassLabels = exports.addClassLabel = exports.getClassMeta = exports.addClassMeta = exports.GroupInstanceToken = exports.ConstantToken = exports.FunctionToken = exports.SingleToken = void 0;
3
+ exports.toGroupAlias = exports.GroupAliasToken = exports.InjectionToken = exports.HooksRunner = exports.AddOnDisposeHookModule = exports.onContainerDisposed = exports.onContainerDisposedHooksRunner = exports.AddOnConstructAsyncHookModule = exports.onConstructAsync = exports.onConstructAsyncHooksRunner = exports.AddOnConstructHookModule = exports.onConstruct = exports.onConstructHooksRunner = exports.injectProp = exports.createHookContext = exports.createHookContextFactory = exports.HookContext = exports.hasHooks = exports.hook = exports.getHooks = exports.CannonSingletonApplyTwiceError = exports.UnexpectedHookResultError = exports.ProviderDisposedError = exports.ContainerDisposedError = exports.MethodNotImplementedError = exports.DependencyMissingKeyError = exports.ContainerNotFoundError = exports.DependencyNotFoundError = exports.Registration = exports.appendArgsFn = exports.appendArgs = exports.decorate = exports.singleton = exports.lazy = exports.scopeAccess = exports.scope = exports.bindTo = exports.register = exports.Provider = exports.ProxyInjector = exports.SimpleInjector = exports.resolveArgs = exports.argsFn = exports.args = exports.inject = exports.MetadataInjector = exports.Injector = exports.EmptyContainer = exports.Container = exports.isDependencyKey = void 0;
4
+ exports.resolveConstructor = exports.Is = exports.pipe = exports.select = exports.once = exports.shallowCache = exports.debounce = exports.throttle = exports.handleAsyncError = exports.handleError = exports.getMethodTags = exports.addMethodTag = exports.getMethodLabels = exports.addMethodLabel = exports.getMethodMeta = exports.addMethodMeta = exports.getParamTags = exports.addParamTag = exports.getParamLabels = exports.addParamLabel = exports.getParamMeta = exports.addParamMeta = exports.getClassTags = exports.addClassTag = exports.getClassLabels = exports.addClassLabel = exports.getClassMeta = exports.addClassMeta = exports.GroupInstanceToken = exports.ConstantToken = exports.FunctionToken = exports.SingleToken = exports.ClassToken = exports.toSingleAlias = exports.SingleAliasToken = void 0;
5
5
  var IContainer_1 = require("./container/IContainer");
6
6
  Object.defineProperty(exports, "isDependencyKey", { enumerable: true, get: function () { return IContainer_1.isDependencyKey; } });
7
7
  var Container_1 = require("./container/Container");
@@ -64,6 +64,10 @@ var onConstruct_1 = require("./hooks/onConstruct");
64
64
  Object.defineProperty(exports, "onConstructHooksRunner", { enumerable: true, get: function () { return onConstruct_1.onConstructHooksRunner; } });
65
65
  Object.defineProperty(exports, "onConstruct", { enumerable: true, get: function () { return onConstruct_1.onConstruct; } });
66
66
  Object.defineProperty(exports, "AddOnConstructHookModule", { enumerable: true, get: function () { return onConstruct_1.AddOnConstructHookModule; } });
67
+ var onConstructAsync_1 = require("./hooks/onConstructAsync");
68
+ Object.defineProperty(exports, "onConstructAsyncHooksRunner", { enumerable: true, get: function () { return onConstructAsync_1.onConstructAsyncHooksRunner; } });
69
+ Object.defineProperty(exports, "onConstructAsync", { enumerable: true, get: function () { return onConstructAsync_1.onConstructAsync; } });
70
+ Object.defineProperty(exports, "AddOnConstructAsyncHookModule", { enumerable: true, get: function () { return onConstructAsync_1.AddOnConstructAsyncHookModule; } });
67
71
  var onContainerDisposed_1 = require("./hooks/onContainerDisposed");
68
72
  Object.defineProperty(exports, "onContainerDisposedHooksRunner", { enumerable: true, get: function () { return onContainerDisposed_1.onContainerDisposedHooksRunner; } });
69
73
  Object.defineProperty(exports, "onContainerDisposed", { enumerable: true, get: function () { return onContainerDisposed_1.onContainerDisposed; } });
@@ -0,0 +1,23 @@
1
+ import { hook } from './hook.js';
2
+ import { HooksRunner } from './HooksRunner.js';
3
+ export const onConstructAsyncHooksRunner = new HooksRunner('onConstructAsync');
4
+ export const onConstructAsync = (...fns) => hook('onConstructAsync', ...fns);
5
+ export class AddOnConstructAsyncHookModule {
6
+ onException;
7
+ constructor(onException) {
8
+ this.onException = onException;
9
+ }
10
+ applyTo(container) {
11
+ container.addOnConstructHook((instance, scope) => {
12
+ if (!onConstructAsyncHooksRunner.hasHooks(instance)) {
13
+ return;
14
+ }
15
+ onConstructAsyncHooksRunner.executeAsync(instance, { scope }).catch((ex) => {
16
+ if (!this.onException) {
17
+ throw ex;
18
+ }
19
+ this.onException(ex, { scope });
20
+ });
21
+ });
22
+ }
23
+ }
package/esm/index.js CHANGED
@@ -20,6 +20,7 @@ export { getHooks, hook, hasHooks } from './hooks/hook.js';
20
20
  export { HookContext, createHookContextFactory, createHookContext } from './hooks/HookContext.js';
21
21
  export { injectProp } from './hooks/injectProp.js';
22
22
  export { onConstructHooksRunner, onConstruct, AddOnConstructHookModule, } from './hooks/onConstruct.js';
23
+ export { onConstructAsyncHooksRunner, onConstructAsync, AddOnConstructAsyncHookModule } from './hooks/onConstructAsync.js';
23
24
  export { onContainerDisposedHooksRunner, onContainerDisposed, AddOnDisposeHookModule, } from './hooks/onContainerDisposed.js';
24
25
  export { HooksRunner } from './hooks/HooksRunner.js';
25
26
  export { InjectionToken } from './token/InjectionToken.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ts-ioc-container",
3
- "version": "55.6.0",
3
+ "version": "55.7.0",
4
4
  "description": "Fast, lightweight TypeScript dependency injection container with a clean API, scoped lifecycles, decorators, tokens, hooks, lazy injection, customizable providers, and no global container objects.",
5
5
  "publishConfig": {
6
6
  "access": "public",
@@ -0,0 +1,12 @@
1
+ import { HookClass, HookFn } from './hook.js';
2
+ import type { IContainer, IContainerModule } from '../container/IContainer.js';
3
+ import { constructor } from '../utils/basic.js';
4
+ import { HooksRunner } from './HooksRunner.js';
5
+ import type { OnExceptionHandler } from './onConstruct.js';
6
+ export declare const onConstructAsyncHooksRunner: HooksRunner;
7
+ export declare const onConstructAsync: (...fns: (HookFn | constructor<HookClass>)[]) => (target: object, propertyKey: string | symbol) => void;
8
+ export declare class AddOnConstructAsyncHookModule implements IContainerModule {
9
+ private readonly onException?;
10
+ constructor(onException?: OnExceptionHandler | undefined);
11
+ applyTo(container: IContainer): void;
12
+ }
@@ -21,6 +21,7 @@ export { getHooks, hook, hasHooks, type HookFn, type HookClass, type InjectFn, t
21
21
  export { HookContext, createHookContextFactory, createHookContext, type IHookContext } from './hooks/HookContext.js';
22
22
  export { injectProp } from './hooks/injectProp.js';
23
23
  export { onConstructHooksRunner, onConstruct, AddOnConstructHookModule, type OnExceptionHandler, } from './hooks/onConstruct.js';
24
+ export { onConstructAsyncHooksRunner, onConstructAsync, AddOnConstructAsyncHookModule } from './hooks/onConstructAsync.js';
24
25
  export { onContainerDisposedHooksRunner, onContainerDisposed, AddOnDisposeHookModule, } from './hooks/onContainerDisposed.js';
25
26
  export { HooksRunner, type HooksRunnerContext, type MapHookContext } from './hooks/HooksRunner.js';
26
27
  export { InjectionToken } from './token/InjectionToken.js';