xtaskjs 1.0.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.
Files changed (88) hide show
  1. package/.github/workflows/publish.yml +44 -0
  2. package/app.ts +7 -0
  3. package/babel.config.js +4 -0
  4. package/dist/packages/common/index.js +17 -0
  5. package/dist/packages/common/src/decorators/core/server/applicationrunner.js +11 -0
  6. package/dist/packages/common/src/decorators/core/server/commandlinerunner.js +11 -0
  7. package/dist/packages/common/src/decorators/core/server/constants.js +5 -0
  8. package/dist/packages/common/src/decorators/core/server/index.js +20 -0
  9. package/dist/packages/common/src/decorators/core/server/onevent.js +11 -0
  10. package/dist/packages/common/src/index.js +24 -0
  11. package/dist/packages/common/src/logger/index.js +17 -0
  12. package/dist/packages/common/src/logger/logger.js +40 -0
  13. package/dist/packages/common/src/types/eventhandlermeta.js +2 -0
  14. package/dist/packages/common/src/types/index.js +19 -0
  15. package/dist/packages/common/src/types/lifecycle-events.js +2 -0
  16. package/dist/packages/common/src/types/runnermeta.js +2 -0
  17. package/dist/packages/common/test/logger/logger.test.js +12 -0
  18. package/dist/packages/core/index.js +17 -0
  19. package/dist/packages/core/src/bootstrap.js +13 -0
  20. package/dist/packages/core/src/di/autowired.js +41 -0
  21. package/dist/packages/core/src/di/component.js +19 -0
  22. package/dist/packages/core/src/di/container.js +225 -0
  23. package/dist/packages/core/src/di/index.js +27 -0
  24. package/dist/packages/core/src/di/lifecycle.js +27 -0
  25. package/dist/packages/core/src/di/managedinstance.js +2 -0
  26. package/dist/packages/core/src/di/qualifier.js +26 -0
  27. package/dist/packages/core/src/di/stereotypes.js +10 -0
  28. package/dist/packages/core/src/index.js +21 -0
  29. package/dist/packages/core/src/kernel/index.js +18 -0
  30. package/dist/packages/core/src/kernel/kernel.js +26 -0
  31. package/dist/packages/core/src/kernel/kernellisteners.js +72 -0
  32. package/dist/packages/core/src/server/application-lifecycle.js +103 -0
  33. package/dist/packages/core/src/server/index.js +19 -0
  34. package/dist/packages/core/src/server/registereventhandlers.js +15 -0
  35. package/dist/packages/core/test/di/autowired.test.js +92 -0
  36. package/dist/packages/core/test/di/container.test.js +43 -0
  37. package/dist/packages/core/test/di/qualifier.test.js +71 -0
  38. package/dist/packages/core/test/logger/logger.test.js +12 -0
  39. package/dist/packages/core/test/server/applicationlifecycle.test.js +28 -0
  40. package/jest.config.js +9 -0
  41. package/package.json +36 -0
  42. package/packages/common/README.md +21 -0
  43. package/packages/common/babel.config.js +3 -0
  44. package/packages/common/index.ts +1 -0
  45. package/packages/common/jest.config.js +11 -0
  46. package/packages/common/package.json +50 -0
  47. package/packages/common/src/decorators/core/server/applicationrunner.ts +10 -0
  48. package/packages/common/src/decorators/core/server/commandlinerunner.ts +10 -0
  49. package/packages/common/src/decorators/core/server/constants.ts +2 -0
  50. package/packages/common/src/decorators/core/server/index.ts +4 -0
  51. package/packages/common/src/decorators/core/server/onevent.ts +10 -0
  52. package/packages/common/src/index.ts +5 -0
  53. package/packages/common/src/logger/index.ts +1 -0
  54. package/packages/common/src/logger/logger.ts +26 -0
  55. package/packages/common/src/types/eventhandlermeta.ts +7 -0
  56. package/packages/common/src/types/index.ts +3 -0
  57. package/packages/common/src/types/lifecycle-events.ts +10 -0
  58. package/packages/common/src/types/runnermeta.ts +5 -0
  59. package/packages/common/test/logger/logger.test.ts +11 -0
  60. package/packages/common/tsconfig.json +13 -0
  61. package/packages/core/README.md +21 -0
  62. package/packages/core/babel.config.js +3 -0
  63. package/packages/core/index.ts +1 -0
  64. package/packages/core/jest.config.js +15 -0
  65. package/packages/core/package.json +51 -0
  66. package/packages/core/src/bootstrap.ts +11 -0
  67. package/packages/core/src/di/autowired.ts +56 -0
  68. package/packages/core/src/di/component.ts +34 -0
  69. package/packages/core/src/di/container.ts +216 -0
  70. package/packages/core/src/di/index.ts +8 -0
  71. package/packages/core/src/di/lifecycle.ts +25 -0
  72. package/packages/core/src/di/managedinstance.ts +4 -0
  73. package/packages/core/src/di/qualifier.ts +25 -0
  74. package/packages/core/src/di/stereotypes.ts +10 -0
  75. package/packages/core/src/index.ts +5 -0
  76. package/packages/core/src/kernel/index.ts +2 -0
  77. package/packages/core/src/kernel/kernel.ts +31 -0
  78. package/packages/core/src/kernel/kernellisteners.ts +36 -0
  79. package/packages/core/src/server/application-lifecycle.ts +85 -0
  80. package/packages/core/src/server/index.ts +3 -0
  81. package/packages/core/src/server/registereventhandlers.ts +18 -0
  82. package/packages/core/test/di/autowired.test.ts +83 -0
  83. package/packages/core/test/di/container.test.ts +49 -0
  84. package/packages/core/test/di/qualifier.test.ts +60 -0
  85. package/packages/core/test/logger/logger.test.ts +11 -0
  86. package/packages/core/test/server/applicationlifecycle.test.ts +13 -0
  87. package/packages/core/tsconfig.json +19 -0
  88. package/tsconfig.json +18 -0
@@ -0,0 +1,50 @@
1
+ {
2
+ "name": "@xtaskjs/common",
3
+ "version": "1.0.0",
4
+ "description": "xtaskjs - modern, fast, node.js web framework (@common)",
5
+ "author": "Javier Rodríguez Soler",
6
+ "homepage": "https://xtaskjs.com",
7
+ "main": "dist/index.js",
8
+ "types": "dist/index.d.ts",
9
+ "scripts": {
10
+ "build": "tsc",
11
+ "test":" jest --passWithNoTests"
12
+ },
13
+ "funding": {
14
+ "type": "opencollective",
15
+ "url": "https://opencollective.com/xtaskjs"
16
+ },
17
+ "repository": {
18
+ "type": "git",
19
+ "url": "https://github.com/xtaskjs/xtaskjs.git",
20
+ "directory": "packages/common"
21
+ },
22
+ "publishConfig": {
23
+ "access": "public"
24
+ },
25
+ "license": "MIT",
26
+ "dependencies": {
27
+ },
28
+ "peerDependencies": {
29
+ "reflect-metadata": "^0.1.12 || ^0.2.0"
30
+ },
31
+ "peerDependenciesMeta": {
32
+ "class-validator": {
33
+ "optional": true
34
+ },
35
+ "class-transformer": {
36
+ "optional": true
37
+ }
38
+ },
39
+ "devDependencies": {
40
+ "@jest/globals": "^30.1.2",
41
+ "@types/express": "^5.0.3",
42
+ "@types/jest": "^30.0.0",
43
+ "jest": "^30.1.3",
44
+ "nodemon": "^3.1.10",
45
+ "ts-jest": "^29.4.4",
46
+ "ts-node": "^10.9.2",
47
+ "tsconfig-paths": "^4.2.0",
48
+ "typescript": "^5.9.2"
49
+ }
50
+ }
@@ -0,0 +1,10 @@
1
+ import { RunnerMeta } from '@xtaskjs/core';
2
+ import { RUNNERS_KEY } from './constants';
3
+
4
+ export function ApplicationRunner(priority = 0): MethodDecorator {
5
+ return (target, propertyKey) => {
6
+ const runners: RunnerMeta[] = Reflect.getMetadata(RUNNERS_KEY, target.constructor) || [];
7
+ runners.push({ type: "ApplicationRunner", method: propertyKey, priority });
8
+ Reflect.defineMetadata(RUNNERS_KEY, runners, target.constructor);
9
+ };
10
+ }
@@ -0,0 +1,10 @@
1
+ import { RunnerMeta } from '@xtaskjs/core';
2
+ import { RUNNERS_KEY } from './constants';
3
+
4
+ export function CommandLineRunner(priority = 0): MethodDecorator {
5
+ return (target, propertyKey) => {
6
+ const runners: RunnerMeta[] = Reflect.getMetadata(RUNNERS_KEY, target.constructor) || [];
7
+ runners.push({ type: "CommandLineRunner", method: propertyKey, priority });
8
+ Reflect.defineMetadata(RUNNERS_KEY, runners, target.constructor);
9
+ };
10
+ }
@@ -0,0 +1,2 @@
1
+ export const HANDLERS_KEY = Symbol("xtaskjs:eventHandlers");
2
+ export const RUNNERS_KEY = Symbol("xtaskjs:runners");
@@ -0,0 +1,4 @@
1
+ export * from './onevent';
2
+ export * from './applicationrunner';
3
+ export * from './commandlinerunner';
4
+ export * from './constants';
@@ -0,0 +1,10 @@
1
+ import { HANDLERS_KEY } from "./constants";
2
+ import { EventHandlerMeta , LifeCyclePhase } from "../../../types";
3
+
4
+ export function OnEvent(phase: LifeCyclePhase, priority = 0): MethodDecorator {
5
+ return (target, propertyKey) => {
6
+ const handlers: EventHandlerMeta[] = Reflect.getMetadata(HANDLERS_KEY, target.constructor) || [];
7
+ handlers.push({ phase, method: propertyKey, priority });
8
+ Reflect.defineMetadata(HANDLERS_KEY, handlers, target.constructor);
9
+ };
10
+ }
@@ -0,0 +1,5 @@
1
+ import 'reflect-metadata';
2
+ export * from "./logger";
3
+ export * from "./decorators/core/server";
4
+ export * from "./types";
5
+ export { HANDLERS_KEY , RUNNERS_KEY} from "./decorators/core/server/constants";
@@ -0,0 +1 @@
1
+ export * from "./logger"
@@ -0,0 +1,26 @@
1
+ import { Service } from '../../../core/src/di/stereotypes';
2
+ import { PostConstruct } from '../../../core/src/di/lifecycle';
3
+
4
+ @Service({ scope: "singleton" })
5
+ export class Logger {
6
+
7
+ constructor() {}
8
+
9
+ @PostConstruct()
10
+ init() {
11
+ console.log("Logger Initialized");
12
+ }
13
+
14
+
15
+ info(message: string): void {
16
+ console.log(`INFO: ${message}`);
17
+ }
18
+
19
+ warn(message: string): void {
20
+ console.warn(`WARN: ${message}`);
21
+ }
22
+
23
+ error(message: string): void {
24
+ console.error(`ERROR: ${message}`);
25
+ }
26
+ }
@@ -0,0 +1,7 @@
1
+ import { LifeCyclePhase } from "./lifecycle-events";
2
+
3
+ export interface EventHandlerMeta {
4
+ phase: LifeCyclePhase;
5
+ method: string | symbol;
6
+ priority: number;
7
+ }
@@ -0,0 +1,3 @@
1
+ export * from "./lifecycle-events";
2
+ export * from "./runnermeta";
3
+ export * from "./eventhandlermeta";
@@ -0,0 +1,10 @@
1
+ export type LifeCyclePhase =
2
+ | "starting"
3
+ | "environmentPrepared"
4
+ | "contextPrepared"
5
+ | "serverStarting"
6
+ | "serverStarted"
7
+ | "ready"
8
+ | "error"
9
+ | "memoryReport"
10
+ | "cpuReport" ;
@@ -0,0 +1,5 @@
1
+ export interface RunnerMeta {
2
+ type: "ApplicationRunner" | "CommandLineRunner";
3
+ method: string | symbol;
4
+ priority: number;
5
+ }
@@ -0,0 +1,11 @@
1
+ import { Logger } from "../../src/logger/logger";
2
+
3
+ describe("Logger", () => {
4
+ it("Should print logs", () => {
5
+ const logger = new Logger();
6
+ const spy = jest.spyOn(console, "log").mockImplementation();
7
+ logger.info("hello");
8
+ expect(spy).toHaveBeenCalledWith(expect.stringContaining("hello"));
9
+ spy.mockRestore();
10
+ });
11
+ });
@@ -0,0 +1,13 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2021",
4
+ "module": "commonjs",
5
+ "rootDir": "./src",
6
+ "outDir": "dist",
7
+ "esModuleInterop": true,
8
+ "experimentalDecorators": true,
9
+ "emitDecoratorMetadata": true,
10
+ },
11
+ "include": ["src/**/*"],
12
+ "exclude": ["node_modules", "dist"]
13
+ }
@@ -0,0 +1,21 @@
1
+ # @xtaskjs/core
2
+
3
+ Core package for xtaskjs, a modern, fast Node.js web framework.
4
+
5
+ ## Installation
6
+ ```bash
7
+ npm install @xtaskjs/core
8
+ ```
9
+
10
+ ## Usage
11
+ ```typescript
12
+ import { ... } from '@xtaskjs/core';
13
+ ```
14
+
15
+ ## Features
16
+ - Dependency Injection
17
+ - Application Lifecycle
18
+ - Kernel and Server utilities
19
+
20
+ ## Documentation
21
+ See [xtaskjs.com](https://xtaskjs.com) for full documentation.
@@ -0,0 +1,3 @@
1
+ module.exports = {
2
+ presets: ['next/babel'],
3
+ }
@@ -0,0 +1 @@
1
+ export * from "./src";
@@ -0,0 +1,15 @@
1
+ const { createDefaultPreset } = require("ts-jest");
2
+
3
+ const tsJestTransformCfg = createDefaultPreset().transform;
4
+
5
+ /** @type {import("jest").Config} **/
6
+ module.exports = {
7
+ testEnvironment: "node",
8
+ transform: {
9
+ ...tsJestTransformCfg,
10
+ },
11
+ moduleNameMapper: {
12
+ "^@xtaskjs/common$": "<rootDir>/../common/dist/index.js",
13
+ "^@xtaskjs/common/(.*)$": "<rootDir>/../common/dist/$1"
14
+ },
15
+ };
@@ -0,0 +1,51 @@
1
+ {
2
+ "name": "@xtaskjs/core",
3
+ "version": "1.0.0",
4
+ "description": "xtaskjs - modern, fast, node.js web framework (@core)",
5
+ "author": "Javier Rodríguez Soler",
6
+ "homepage": "https://xtaskjs.com",
7
+ "main": "dist/index.js",
8
+ "types": "dist/index.d.ts",
9
+ "scripts": {
10
+ "build": "tsc",
11
+ "test":" jest --passWithNoTests"
12
+ },
13
+ "funding": {
14
+ "type": "opencollective",
15
+ "url": "https://opencollective.com/xtaskjs"
16
+ },
17
+ "repository": {
18
+ "type": "git",
19
+ "url": "https://github.com/xtaskjs/xtaskjs.git",
20
+ "directory": "packages/core"
21
+ },
22
+ "publishConfig": {
23
+ "access": "public"
24
+ },
25
+ "license": "MIT",
26
+ "dependencies": {
27
+ "@xtaskjs/common": "^1.0.0"
28
+ },
29
+ "peerDependencies": {
30
+ "reflect-metadata": "^0.1.12 || ^0.2.0"
31
+ },
32
+ "peerDependenciesMeta": {
33
+ "class-validator": {
34
+ "optional": true
35
+ },
36
+ "class-transformer": {
37
+ "optional": true
38
+ }
39
+ },
40
+ "devDependencies": {
41
+ "@jest/globals": "^30.1.2",
42
+ "@types/express": "^5.0.3",
43
+ "@types/jest": "^30.0.0",
44
+ "jest": "^30.1.3",
45
+ "nodemon": "^3.1.10",
46
+ "ts-jest": "^29.4.4",
47
+ "ts-node": "^10.9.2",
48
+ "tsconfig-paths": "^4.2.0",
49
+ "typescript": "^5.9.2"
50
+ }
51
+ }
@@ -0,0 +1,11 @@
1
+ import "reflect-metadata";
2
+ import { ApplicationLifeCycle, Kernel, KernelListeners, registerEventHandlers } from "@xtaskjs/core";
3
+
4
+ export async function Bootstrap(): Promise<Kernel> {
5
+ const lifecycle = new ApplicationLifeCycle();
6
+ const kernel = new Kernel();
7
+ const listeners = new KernelListeners();
8
+ registerEventHandlers(listeners, lifecycle);
9
+ await lifecycle.boot(() => kernel.boot());
10
+ return kernel;
11
+ }
@@ -0,0 +1,56 @@
1
+ import "reflect-metadata";
2
+
3
+ const AUTOWIRED_KEY = Symbol("xtaskjs:autowired");
4
+ const AUTOWIRED_PROPS_KEY = Symbol("xtaskjs:autowired:props");
5
+
6
+ export interface AutoWiredMetaData {
7
+ type: any;
8
+ required: boolean;
9
+ qualifier?: string;
10
+ }
11
+
12
+ export function AutoWired(options: { required?: boolean; qualifier?: string } = {}) {
13
+ return function (target: any, propertyKey: string | symbol) {
14
+ const type = Reflect.getMetadata("design:type", target, propertyKey);
15
+ const metaData: AutoWiredMetaData = {
16
+ type,
17
+ required: options.required !== false,
18
+ qualifier: options.qualifier,
19
+ };
20
+
21
+ // Store metadata for the property
22
+ Reflect.defineMetadata(AUTOWIRED_KEY, metaData, target, propertyKey);
23
+
24
+ // Store list of autowired properties on the class prototype
25
+ const existingProps=
26
+ Reflect.getMetadata(AUTOWIRED_PROPS_KEY, target.constructor) || [];
27
+ if (!existingProps.includes(propertyKey)) {
28
+ existingProps.push(propertyKey);
29
+ Reflect.defineMetadata(AUTOWIRED_PROPS_KEY, existingProps, target.constructor);
30
+ }
31
+ };
32
+ }
33
+
34
+ export function getAutoWiredProperties(target: any): Map<string | symbol, AutoWiredMetaData>{
35
+
36
+ const properties = new Map<string | symbol, AutoWiredMetaData>();
37
+ let currentClass = target.contructor;
38
+
39
+ while (currentClass && currentClass !== Object) {
40
+ const autowiredProps = Reflect.getMetadata(AUTOWIRED_PROPS_KEY, currentClass) || [];
41
+ const prototype = currentClass.prototype;
42
+
43
+ autowiredProps.forEach((prop: string | symbol) => {
44
+ const metaData = Reflect.getMetadata(AUTOWIRED_KEY, prototype, prop);
45
+ if (metaData && !properties.has(prop)) {
46
+ properties.set(prop, metaData);
47
+ }
48
+ });
49
+
50
+ currentClass = Object.getPrototypeOf(currentClass);
51
+
52
+ }
53
+
54
+ return properties;
55
+
56
+ }
@@ -0,0 +1,34 @@
1
+ import "reflect-metadata";
2
+
3
+ export type ComponentType = "service" | "controller" | "repository";
4
+ export type ScoreType = "singleton" | "transient";
5
+
6
+ const COMPONENT_KEY = Symbol("xtaskjs:component");
7
+
8
+ export interface ComponentMetadata {
9
+ scope ?: "singleton" | "transient";
10
+ condition?:() => boolean;
11
+ name?: string;
12
+ primary?: boolean;
13
+ }
14
+
15
+ export interface ComponentOptions {
16
+ type?: ComponentType;
17
+ scope?: ScoreType;
18
+ condition?:() => boolean;
19
+ name?: string;
20
+ primary?: boolean;
21
+ }
22
+
23
+ export function Component (options: ComponentOptions = {}) {
24
+ return function (target: any) {
25
+ Reflect.defineMetadata(COMPONENT_KEY, options, target);
26
+ };
27
+ }
28
+
29
+ export function getComponentMetadata(target: any): ComponentOptions | undefined {
30
+ return Reflect.getMetadata(COMPONENT_KEY, target);
31
+ }
32
+
33
+ export const Service = (meta: ComponentMetadata = {}) => Component(meta);
34
+ export const Repository = (meta: ComponentMetadata = {}) => Component(meta);
@@ -0,0 +1,216 @@
1
+ import "reflect-metadata"
2
+ import { ComponentMetadata, getComponentMetadata } from "./component";
3
+ import { getPostConstructMethod, getPreDestroyMethod } from "./lifecycle";
4
+ import { readdirSync , statSync } from "fs";
5
+ import { join } from "path";
6
+ import { ManagedInstance } from "./managedinstance";
7
+ import { Project } from "ts-morph";
8
+ import { getAutoWiredProperties } from "./autowired";
9
+ import { getConstructorQualifiers } from "./qualifier";
10
+ import { registerEventHandlers} from "../server";
11
+ import { HANDLERS_KEY, RUNNERS_KEY } from "@xtaskjs/common";
12
+
13
+
14
+ export class Container{
15
+ private providers = new Map<any, () => any>();
16
+ private singletons = new Map<any, any>();
17
+ private nameToType = new Map<string, any>();
18
+ private typeToNames = new Map<any, string[]>();
19
+ private primaryBeans = new Map<any, any>();
20
+ private resolving = new Set<any>();
21
+ public managedInstances : ManagedInstance[] = [];
22
+
23
+ // SCAN FOLDER BASE DIR FOR @Service() AND @Component()
24
+
25
+ async autoload(baseDir = "packages"){
26
+ const files = await this.scanDir(join(process.cwd(), baseDir));
27
+
28
+ for (const file of files) {
29
+ const name = file.toString();
30
+ if (name.includes("test")) continue; //skip test files
31
+ if (name.endsWith("d.ts")) continue; // skip TypeScript declarations Files.
32
+ if (name.includes("js")) continue; //skip configuration files
33
+ if (name.includes("json")) continue; //skip configuration files
34
+ if (name.includes("index.ts")) continue; //skip export files
35
+ const module = await import(file);
36
+ const project = new Project();
37
+ const sourceFile = project.addSourceFileAtPath(file);
38
+ const classes = sourceFile.getClasses();
39
+
40
+ for (const cls of classes) {
41
+ if (cls.getDecorator("Service") || cls.getDecorator("Component") || cls.getDecorator("Controller") || cls.getDecorator("Repository")) {
42
+ const className = cls.getName();
43
+ if (className && module[className]){
44
+ const classConstructor = module[className];
45
+ const metaData = getComponentMetadata(classConstructor) || { scope: "singleton"};
46
+ const beanName = metaData.name || className;
47
+ this.registerWithName(classConstructor, metaData, beanName);
48
+ }
49
+ }
50
+ }
51
+ }
52
+ }
53
+
54
+
55
+ public registerWithName(target: any, meta: ComponentMetadata, name?: string){
56
+ if (name){
57
+ this.nameToType.set(name, target);
58
+ const existingNames = this.typeToNames.get(target) || [];
59
+ existingNames.push(name);
60
+ this.typeToNames.set(target, existingNames);
61
+ }
62
+
63
+ if (meta.primary){
64
+ this.primaryBeans.set(target,target);
65
+ }
66
+
67
+ this.register(target, meta);
68
+
69
+ }
70
+
71
+ // Register aa class with the container
72
+
73
+ public register(target: any, meta: ComponentMetadata){
74
+ const paramTypes: any[] =
75
+ Reflect.getMetadata("design:paramtypes", target) || [];
76
+
77
+ console.log(`Registering component: ${target.name} with dependencies:`, paramTypes.map(t => t?.name || 'unknown'));
78
+
79
+ const provider = () => {
80
+ if (this.resolving.has(target)) {
81
+ const resolvingNames = Array.from(this.resolving).map(t => t.name || 'unknown').join(" -> ");
82
+ throw new Error(`Circular dependency detected: ${resolvingNames} -> ${target.name}`);
83
+ }
84
+
85
+ this.resolving.add(target);
86
+
87
+
88
+ try{
89
+ const qualifiers = getConstructorQualifiers(target);
90
+ const dependencies = paramTypes.map((dep, index) => {
91
+ const qualifier = qualifiers?.[index];
92
+ return this.getWithQualifier(dep, qualifier);
93
+ });
94
+
95
+ const instance = new target(...dependencies);
96
+
97
+ this.injectAutoWiredFields(instance);
98
+
99
+ //PostConstruct
100
+ const postMethod = getPostConstructMethod(instance);
101
+ if (postMethod && typeof instance[postMethod] === "function") {
102
+ instance[postMethod]();
103
+ }
104
+
105
+ //PreDestroy
106
+ const preMethod = getPreDestroyMethod(instance);
107
+ if (preMethod && typeof instance[preMethod] === "function") {
108
+ this.managedInstances.push({
109
+ instance,
110
+ preDestroy: () => instance[preMethod](),
111
+ });
112
+ }
113
+
114
+ return instance;
115
+ } finally {
116
+ this.resolving.delete(target);
117
+ }
118
+ }
119
+
120
+ if (meta.scope === "transient") {
121
+ this.providers.set(target, provider);
122
+ } else { //singleton by default
123
+ this.providers.set(target,()=> {
124
+ if (!this.singletons.has(target)) {
125
+ const instance = provider();
126
+ this.singletons.set(target, instance);
127
+ }
128
+ return this.singletons.get(target);
129
+ })
130
+ }
131
+ }
132
+
133
+ public getByName<T>(name: string): T {
134
+ const type = this.nameToType.get(name);
135
+ if (!type) {
136
+ throw new Error(`No component found with name: ${name}`);
137
+ }
138
+ return this.get(type);
139
+ }
140
+
141
+ private getWithQualifier<T>(type: new (...args: any[]) => T, qualifier?: string): T {
142
+ if(qualifier){
143
+ return this.getByName<T>(qualifier);
144
+ }
145
+ return this.get(type);
146
+ }
147
+
148
+ private injectAutoWiredFields(instance: any) {
149
+ const autoWiredProperties = getAutoWiredProperties(instance);
150
+ autoWiredProperties.forEach((metaData, propertyKey) => {
151
+ try {
152
+ let value;
153
+ if (metaData.qualifier) {
154
+ value = this.getByName(metaData.qualifier);
155
+ } else {
156
+ value = this.get(metaData.type);
157
+ }
158
+ instance[propertyKey] = value;
159
+ }catch (error) {
160
+ if (metaData.required) {
161
+ throw new Error(`Failed to inject required dependency for property "${String(propertyKey)}": ${error.message}`);
162
+ }
163
+ }
164
+ });
165
+ }
166
+
167
+ // Get instance of class
168
+ get<T>(target: new (...args: any[]) => T): T {
169
+ const provider = this.providers.get(target);
170
+ if (!provider) {
171
+ throw new Error(`No provider found for ${target.name}`);
172
+ }
173
+ return provider();
174
+ }
175
+
176
+ // Execute all @PreDestroy in reverse order
177
+
178
+ destroy() {
179
+ this.managedInstances.reverse().forEach((m) => m.preDestroy?.());
180
+ this.managedInstances = [];
181
+ this.singletons.clear();
182
+ this.providers.clear();
183
+ }
184
+
185
+ // Scan folder recursively for .ts or .js files
186
+ public async scanDir(dir: string): Promise<string[]> {
187
+ let results: string[] = [];
188
+ for (const file of readdirSync(dir)) {
189
+ const full = join (dir, file);
190
+ const stat = statSync(full);
191
+ if (stat && stat.isDirectory()) {
192
+ const res = await this.scanDir(full);
193
+ results = results.concat(res);
194
+ } else if (/\.(ts|js)$/.test(file)) {
195
+ results.push(full);
196
+ }
197
+ }
198
+ return results;
199
+ }
200
+
201
+
202
+ public registerLifeCycleListeners(app: any)
203
+ {
204
+
205
+ for (const [type] of this.providers.entries()){
206
+ // Check if class has lifecycle decorators
207
+ const handlers = Reflect.getMetadata(HANDLERS_KEY, type) || [];
208
+ const runners = Reflect.getMetadata(RUNNERS_KEY, type) || [];
209
+
210
+ if(handlers.length > 0 || runners.length > 0){
211
+ const instance = this.get(type);
212
+ registerEventHandlers(instance, app);
213
+ }
214
+ }
215
+ }
216
+ }
@@ -0,0 +1,8 @@
1
+ export {Component, ComponentOptions} from "./component"
2
+ export {Container} from "./container"
3
+ export * from "./lifecycle"
4
+ export {ManagedInstance} from "./managedinstance"
5
+ export {getComponentMetadata} from "./component"
6
+ export * from "./stereotypes"
7
+ export * from "./autowired"
8
+ export * from "./qualifier"
@@ -0,0 +1,25 @@
1
+ import "reflect-metadata";
2
+
3
+ export const POST_CONSTRUCT_KEY = Symbol("post_construct");
4
+ export const PRE_DESTROY_KEY = Symbol("pre_destroy");
5
+
6
+ export function PostConstruct() {
7
+ return function (target: any, propertyKey: string) {
8
+ Reflect.defineMetadata(POST_CONSTRUCT_KEY, propertyKey, target);
9
+ };
10
+ };
11
+
12
+
13
+ export function PreDestroy() {
14
+ return function (target: any, propertyKey: string) {
15
+ Reflect.defineMetadata(PRE_DESTROY_KEY, propertyKey, target);
16
+ };
17
+ }
18
+
19
+ export function getPostConstructMethod(target: any): string | undefined {
20
+ return Reflect.getMetadata(POST_CONSTRUCT_KEY, target);
21
+ }
22
+
23
+ export function getPreDestroyMethod(target: any): string | undefined {
24
+ return Reflect.getMetadata(PRE_DESTROY_KEY, target);
25
+ }
@@ -0,0 +1,4 @@
1
+ export interface ManagedInstance<T = any> {
2
+ instance: T;
3
+ preDestroy?: ()=> void;
4
+ }