raiton 1.0.0-alpha.1 → 1.0.0-alpha.3

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 (72) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/README.md +87 -11
  3. package/build/bin/index.mjs +3831 -3649
  4. package/build/raiton-1.0.0-alpha.3.tgz +0 -0
  5. package/deno.json +9 -0
  6. package/package.json +4 -1
  7. package/source/bin/cli-tools.ts +34 -9
  8. package/source/bin/constants.ts +5 -0
  9. package/source/bin/index.ts +1 -1
  10. package/source/commands/develop.command.ts +3 -3
  11. package/source/commands/start.command.ts +1 -1
  12. package/source/core/application.ts +66 -18
  13. package/source/core/builder.ts +27 -21
  14. package/source/core/controller/builder.ts +8 -5
  15. package/source/core/controller/metadata.ts +1 -1
  16. package/source/core/directories.ts +1 -1
  17. package/source/core/index.ts +0 -1
  18. package/source/core/injection/injection.ts +67 -5
  19. package/source/core/middleware/compose.ts +1 -1
  20. package/source/core/raiton.ts +2 -3
  21. package/source/core/router/handler.ts +100 -44
  22. package/source/core/thread.ts +18 -8
  23. package/source/sdk/artifacts.ts +65 -41
  24. package/source/sdk/constants/decorators.constant.ts +1 -0
  25. package/source/sdk/data-transfer-object.ts +11 -3
  26. package/source/sdk/decorators/parametrable.ts +19 -15
  27. package/source/sdk/decorators/routable.decorator.ts +0 -3
  28. package/source/sdk/encryption.ts +5 -6
  29. package/source/sdk/enums/encrypted.enum.ts +10 -0
  30. package/source/sdk/enums/http-status.enum.ts +73 -0
  31. package/source/sdk/enums/index.ts +2 -1
  32. package/source/sdk/exceptions/http-exception.ts +28 -0
  33. package/source/sdk/exceptions/index.ts +2 -0
  34. package/source/sdk/index.ts +7 -0
  35. package/source/sdk/parameter-bag.ts +55 -0
  36. package/source/sdk/repositories.ts +1 -1
  37. package/source/sdk/responses/error.ts +52 -0
  38. package/source/sdk/responses/helpers.ts +28 -0
  39. package/source/sdk/responses/http-throwable.ts +28 -0
  40. package/source/sdk/responses/http.ts +48 -0
  41. package/source/sdk/responses/index.ts +4 -0
  42. package/source/sdk/runtime/bun/server.ts +2 -1
  43. package/source/sdk/runtime/deno/server.ts +2 -2
  44. package/source/sdk/runtime/node/server.ts +2 -2
  45. package/source/sdk/utilities/artifact.util.ts +18 -0
  46. package/source/sdk/utilities/index.ts +1 -3
  47. package/source/types/application.ts +3 -3
  48. package/source/types/artifact.ts +36 -32
  49. package/source/types/builder.ts +0 -4
  50. package/source/types/config.ts +2 -2
  51. package/source/types/controller.ts +1 -2
  52. package/source/types/index.ts +2 -3
  53. package/source/types/lifecycle.ts +11 -0
  54. package/source/types/responses.ts +17 -7
  55. package/source/types/runtime.ts +1 -1
  56. package/build/raiton-1.0.0-alpha.1.tgz +0 -0
  57. package/source/core/artifacts/artifact.ts +0 -109
  58. package/source/core/artifacts/artifacts.ts +0 -10
  59. package/source/core/artifacts/index.ts +0 -1
  60. package/source/core/artifacts/runner.ts +0 -3
  61. package/source/core/hmr.ts +0 -106
  62. package/source/sdk/decorators/payload.decorator.ts +0 -77
  63. package/source/sdk/json.ts +0 -55
  64. package/source/sdk/request.ts +0 -3
  65. package/source/sdk/responses.ts +0 -45
  66. package/source/sdk/schemes.ts +0 -178
  67. package/source/sdk/utilities/artifacts.util.ts +0 -62
  68. package/source/sdk/utilities/controller.util.ts +0 -8
  69. package/source/types/data-transfer-object.ts +0 -4
  70. package/source/types/hmr.ts +0 -39
  71. /package/source/sdk/enums/{http.enum.ts → http-method.enum.ts} +0 -0
  72. /package/source/sdk/{throwable.ts → exceptions/throwable.ts} +0 -0
Binary file
package/deno.json ADDED
@@ -0,0 +1,9 @@
1
+ {
2
+ "imports": {
3
+ "@": "./source",
4
+ "@/": "./source/"
5
+ },
6
+ "tasks": {
7
+ "start": "deno run --allow-all source/bin/bootstrapper.ts"
8
+ }
9
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "raiton",
3
- "version": "1.0.0-alpha.1",
3
+ "version": "1.0.0-alpha.3",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "description": "Protorians Raiton Development Kit",
@@ -16,7 +16,9 @@
16
16
  "development",
17
17
  "kit",
18
18
  "typescript",
19
+ "node",
19
20
  "bun",
21
+ "deno",
20
22
  "backend"
21
23
  ],
22
24
  "author": "Y. Yannick GOBOU",
@@ -52,6 +54,7 @@
52
54
  "@types/node": "^25.0.3",
53
55
  "argon2": "^0.44.0",
54
56
  "bcrypt": "^6.0.0",
57
+ "class-validator": "^0.14.3",
55
58
  "commander": "^14.0.2",
56
59
  "dotenv": "^17.2.3",
57
60
  "reflect-metadata": "^0.2.2"
@@ -1,22 +1,30 @@
1
1
  import {spawn} from 'node:child_process';
2
+ import {isBunUsed, isDenoUsed} from "@/bin/constants";
2
3
 
3
- const isBunUsed = typeof Bun !== "undefined";
4
4
 
5
5
  export class CliTools {
6
6
  static get cwd() {
7
- return `${process.cwd()}`;
7
+ return `${isDenoUsed ? (globalThis as any).Deno.cwd() : process.cwd()}`;
8
8
  }
9
9
 
10
10
  static set cwd(value: string) {
11
- process.chdir(value)
11
+ if (isDenoUsed) {
12
+ (globalThis as any).Deno.chdir(value);
13
+ } else {
14
+ process.chdir(value);
15
+ }
12
16
  }
13
17
 
14
18
  static get argv() {
15
- return isBunUsed ? Bun.argv : process.argv;
19
+ if (isBunUsed) return (globalThis as any).Bun.argv;
20
+ if (isDenoUsed) return (globalThis as any).Deno.args;
21
+ return process.argv;
16
22
  }
17
23
 
18
24
  static get process() {
19
- return isBunUsed ? Bun : process;
25
+ if (isBunUsed) return (globalThis as any).Bun;
26
+ if (isDenoUsed) return (globalThis as any).Deno;
27
+ return process;
20
28
  }
21
29
 
22
30
  static spawn(command: string | string[], args: string[] = [], options?: Record<string, any>) {
@@ -25,19 +33,36 @@ export class CliTools {
25
33
  ...(typeof command == 'string' ? [command] : (Array.isArray(command) ? command : [])),
26
34
  ...args
27
35
  ];
28
- // If the command is a .ts file, prepend bun
29
36
  if (typeof command === 'string' && command.endsWith('.ts')) {
30
37
  cmdArray.unshift('bun');
31
38
  }
32
39
  return Bun.spawn(cmdArray, options);
33
40
  }
34
41
 
42
+ if (isDenoUsed) {
43
+ const cmd = typeof command == 'string' ? command : command[0];
44
+ const cmdArgs = typeof command == 'string' ? args : [...command.slice(1), ...args];
45
+
46
+ if (cmd.endsWith('.ts')) {
47
+ return new Deno.Command('deno', {
48
+ args: ['run', '-A', cmd, ...cmdArgs],
49
+ ...options
50
+ }).spawn();
51
+ }
52
+
53
+ return new Deno.Command(cmd, {
54
+ args: cmdArgs,
55
+ ...options
56
+ }).spawn();
57
+ }
58
+
35
59
  const cmd = typeof command == 'string' ? command : command[0];
36
- // If the command is a .ts file, prepend bun (or npx tsx/ts-node if we wanted to be Node generic, but here we favor Bun)
60
+ const cmdArgs = typeof command == 'string' ? args : [...command.slice(1), ...args];
61
+
37
62
  if (cmd.endsWith('.ts')) {
38
- return spawn('bun', [cmd, ...args], options);
63
+ return spawn('node', ['--loader', 'ts-node/register', cmd, ...cmdArgs], options);
39
64
  }
40
65
 
41
- return spawn(cmd, args, options);
66
+ return spawn(cmd, cmdArgs, options);
42
67
  }
43
68
  }
@@ -0,0 +1,5 @@
1
+ export const isBunUsed = typeof (globalThis as any).Bun !== "undefined";
2
+ export const isDenoUsed = typeof (globalThis as any).Deno !== "undefined";
3
+
4
+ declare const Bun: any;
5
+ declare const Deno: any;
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env bun
2
2
  import "reflect-metadata"
3
- import bootstrapper from "./bootstrapper";
4
3
  import CLI from "./cli";
4
+ import bootstrapper from "./bootstrapper";
5
5
 
6
6
  bootstrapper(CLI)
@@ -1,5 +1,5 @@
1
1
  import {Raiton, RaitonCommand} from "@/core";
2
- import {ChildProcess} from 'node:child_process';
2
+ import {ChildProcess, ChildProcessWithoutNullStreams} from 'node:child_process';
3
3
  import {Logger} from "@protorians/logger";
4
4
  import {EventMessageEnum} from "@/sdk";
5
5
  import {CliTools} from "@/bin/cli-tools";
@@ -9,7 +9,7 @@ export default class DevelopCommand extends RaitonCommand {
9
9
  public readonly name: string = 'develop';
10
10
  public readonly description: string = 'Run the application in development mode';
11
11
 
12
- private child: Bun.Subprocess<"ignore", "pipe", "inherit"> | ChildProcess | null = null;
12
+ private child: Bun.Subprocess<"ignore", "pipe", "inherit"> | ChildProcess | Deno.ChildProcess | ChildProcessWithoutNullStreams | null = null;
13
13
 
14
14
  public register(): void {
15
15
  this.cli
@@ -42,6 +42,6 @@ export default class DevelopCommand extends RaitonCommand {
42
42
  if (msg === EventMessageEnum.RESTART) this.restart()
43
43
  });
44
44
 
45
- Logger.log('PID', this.child.pid)
45
+ Logger.log('PID', this.child?.pid)
46
46
  }
47
47
  }
@@ -8,7 +8,7 @@ export default class StartCommand extends RaitonCommand {
8
8
  public readonly name: string = 'start';
9
9
  public readonly description: string = 'Run the application in production mode';
10
10
 
11
- private child: Bun.Subprocess<"ignore", "pipe", "inherit"> | ChildProcess | null = null;
11
+ private child: Bun.Subprocess<"ignore", "pipe", "inherit"> | ChildProcess | Deno.ChildProcess | null = null;
12
12
 
13
13
  public register(): void {
14
14
  this.cli
@@ -4,6 +4,8 @@ import {ApplicationConfig, ApplicationInterface} from "@/types/application";
4
4
  import {HttpMethod} from "@/sdk";
5
5
  import {RouteHandler} from "@/types";
6
6
  import {Logger} from "@protorians/logger";
7
+ import {RaitonConfig} from "@/core/config";
8
+ import {Artifacts} from "@/sdk/artifacts";
7
9
 
8
10
  export class Application implements ApplicationInterface {
9
11
  private root: PluginScope
@@ -12,6 +14,18 @@ export class Application implements ApplicationInterface {
12
14
  readonly config: ApplicationConfig
13
15
  ) {
14
16
  this.root = new PluginScope()
17
+ if (this.config.workdir) {
18
+ process.chdir(this.config.workdir)
19
+ }
20
+ this.initialize()
21
+ }
22
+
23
+ protected initialize(): this {
24
+ const artifacts = RaitonConfig.get('artifacts')
25
+ const artifactTypes = [...artifacts?.types || [], ...Artifacts.defaultTypes]
26
+
27
+ Artifacts.registerMany(...artifactTypes)
28
+ return this;
15
29
  }
16
30
 
17
31
  public get hostname(): string {
@@ -26,15 +40,15 @@ export class Application implements ApplicationInterface {
26
40
  }`
27
41
  }
28
42
 
29
- // public setOption<K extends keyof ApplicationConfig>(key: K, value: ApplicationConfig[K]): this {
30
- // this.config[key] = value;
31
- // return this;
32
- // }
33
- //
34
- // public setOptions(options: ApplicationConfig): this {
35
- // Object.assign(this.config, options);
36
- // return this;
37
- // }
43
+ public setOption<K extends keyof ApplicationConfig>(key: K, value: ApplicationConfig[K]): this {
44
+ this.config[key] = value;
45
+ return this;
46
+ }
47
+
48
+ public setOptions(options: ApplicationConfig): this {
49
+ Object.assign(this.config, options);
50
+ return this;
51
+ }
38
52
 
39
53
  register(plugin: any): this {
40
54
  this.root.register(plugin)
@@ -47,7 +61,9 @@ export class Application implements ApplicationInterface {
47
61
  }
48
62
 
49
63
  route(method: HttpMethod, path: string, handler: RouteHandler, version?: string): this {
50
- this.root.route(method, path, handler, version)
64
+ const prefix = this.config.prefix ?? ''
65
+ const fullPath = `${prefix}${path}`.replace(/\/+/g, '/') || '/'
66
+ this.root.route(method, fullPath, handler, version)
51
67
  return this
52
68
  }
53
69
 
@@ -86,27 +102,59 @@ export class Application implements ApplicationInterface {
86
102
  async handle(req: any, reply: any): Promise<any> {
87
103
  const ctx = new RequestContext(req, reply)
88
104
 
105
+ if (this.config.verbose) {
106
+ Logger.info(
107
+ `Incoming request: ${req.method} ${req.url}`
108
+ )
109
+ }
110
+
89
111
  await this.root.hooks.run('onRequest', ctx)
90
112
 
113
+ const url = new URL(req.url, this.hostname)
114
+ let pathname = url.pathname
115
+
116
+ if (this.config.pathname && this.config.pathname !== '/') {
117
+ const appPathname = this.config.pathname.endsWith('/') ? this.config.pathname : `${this.config.pathname}/`
118
+ if (pathname.startsWith(appPathname)) {
119
+ pathname = pathname.substring(appPathname.length - 1) || '/'
120
+ } else if (pathname === this.config.pathname) {
121
+ pathname = '/'
122
+ } else {
123
+ // Requête hors du pathname de l'application
124
+ if (this.config.verbose) {
125
+ Logger.warn(`Request out of application pathname: ${pathname} (expected prefix: ${this.config.pathname})`)
126
+ }
127
+ reply.status(404)
128
+ return reply.send({error: false, statusCode: 404})
129
+ }
130
+ }
131
+
91
132
  const route = this.root.router.match(
92
133
  req.method,
93
- new URL(req.url, this.hostname).pathname
134
+ pathname
94
135
  )
95
136
 
96
137
  if (!route) {
138
+ if (this.config.verbose) {
139
+ Logger.warn(`Route not found: ${req.method} ${pathname}`)
140
+ }
97
141
  reply.status(404)
98
142
  return reply.send({error: false, statusCode: 404})
99
143
  }
100
144
 
101
145
  const pipeline = this.root.middleware.clone()
102
146
  pipeline.use(async ({context}) => {
103
- (context as any).params = route.parameters;
104
-
105
- const responses = await route.handler(context)
106
-
107
- // Logger.debug('Responses', responses)
108
- // request.reply.header('Content-Type', 'application/json')
109
- context.reply.send(responses)
147
+ try {
148
+ (context as any).params = route.parameters;
149
+ let responses = await route.handler(context)
150
+
151
+ context.reply.send(responses)
152
+ } catch (e: any) {
153
+ Logger.error('Failed to handle request', e.message ?? e)
154
+ if (this.config.develop) {
155
+ console.error(e)
156
+ }
157
+ }
110
158
  })
111
159
 
112
160
  await pipeline.run(ctx)
@@ -2,18 +2,16 @@ import {RaitonConfig} from "./config";
2
2
  import path from "node:path";
3
3
  import {RaitonDirectories} from "./directories";
4
4
  import fs, {WatchEventType} from "node:fs";
5
- import type {
6
- BuilderConfig,
7
- BuilderInterface,
8
- HmrInterface,
9
- } from "@/types";
5
+ import type {BuilderConfig, BuilderInterface,} from "@/types";
10
6
  import {RaitonThread} from "./thread";
11
7
  import {Raiton} from "@/core/raiton";
12
- import {isControllerFile} from "@/sdk";
13
- import {Hmr} from "@/core/hmr";
14
- import {Throwable} from "@/sdk/throwable";
8
+ import {isControllerArtifact, isServiceArtifact} from "@/sdk";
15
9
  import {ControllerBuilder} from "@/core/controller";
16
10
  import {watch} from "fs";
11
+ import {LBadge, Logger} from "@protorians/logger";
12
+ import {Throwable} from "@/sdk/exceptions";
13
+ import {Injection} from "@/core/injection";
14
+ import {Artifacts} from "@/sdk/artifacts";
17
15
 
18
16
  export class RaitonBuilder implements BuilderInterface {
19
17
  protected _source: string | null = null;
@@ -21,8 +19,6 @@ export class RaitonBuilder implements BuilderInterface {
21
19
  protected _bootstrapper: string | null = null;
22
20
  protected _bootstrapperFile: string | null = null;
23
21
  protected _compiledVersionNumber: number = 1;
24
-
25
- public readonly hmr: HmrInterface = new Hmr()
26
22
  protected _watcher?: fs.FSWatcher;
27
23
 
28
24
 
@@ -53,7 +49,7 @@ export class RaitonBuilder implements BuilderInterface {
53
49
  return this._watcher;
54
50
  }
55
51
 
56
- protected parse(filename: string, type?: WatchEventType) {
52
+ protected async parse(filename: string, type?: WatchEventType) {
57
53
  if (!fs.existsSync(filename)) return;
58
54
 
59
55
  const payload = {
@@ -63,20 +59,28 @@ export class RaitonBuilder implements BuilderInterface {
63
59
  type
64
60
  }
65
61
 
66
- if (isControllerFile(filename)) {
62
+ Logger.log(LBadge.info('HMR'), 'activated');
63
+
64
+ if (isControllerArtifact(filename)) {
67
65
  Raiton.signals.dispatch('hmr:controller', payload)
68
66
  }
69
67
 
68
+ if (Artifacts.is(filename))
69
+ Artifacts.reload(
70
+ await import(`${filename}?v=${payload.version || 1}&t=${payload.timestamp || Date.now()}`),
71
+ filename
72
+ )
73
+
70
74
  return payload;
71
75
  }
72
76
 
73
- protected parsing() {
77
+ protected async parsing(): Promise<this> {
74
78
  if (typeof this._source != 'string') throw new Throwable('Application source not found');
75
79
 
76
- for (const filename of [...fs.readdirSync(this._source, {recursive: true})]) {
77
- this.parse(path.join(this._source, String(filename)));
78
- }
80
+ for (const filename of [...fs.readdirSync(this._source, {recursive: true})])
81
+ await this.parse(path.join(this._source, String(filename)));
79
82
 
83
+ return this;
80
84
  }
81
85
 
82
86
  protected watching(): this {
@@ -114,11 +118,12 @@ export class RaitonBuilder implements BuilderInterface {
114
118
  await this.initialize()
115
119
  Raiton.signals.listen(
116
120
  'hmr:controller',
117
- async ({filename, version, timestamp}) =>
121
+ async ({filename, version, timestamp}) => {
118
122
  await ControllerBuilder.build({filename, version, timestamp})
123
+ }
119
124
  )
120
- this.parsing()
121
- this.watching()
125
+ // this.parsing();
126
+ this.watching();
122
127
  return this;
123
128
  }
124
129
 
@@ -131,8 +136,9 @@ export class RaitonBuilder implements BuilderInterface {
131
136
  if (!('default' in bootstrapper))
132
137
  throw new Error('Bootstrapper not supported! Please export to "default"')
133
138
 
134
- Raiton.thread = new RaitonThread(this, {})
135
- return bootstrapper.default(Raiton.thread)
139
+ const thread = new RaitonThread(this, {})
140
+ Raiton.thread = thread;
141
+ return await bootstrapper.default(thread);
136
142
  }
137
143
 
138
144
  }
@@ -3,7 +3,8 @@ import {BuilderHMRDeclaration} from "@/types";
3
3
  import {LBadge, Logger} from "@protorians/logger";
4
4
  import {compileController} from "@/core/controller/compiler";
5
5
  import {RaitonThread} from "@/core/thread";
6
- import {isControllerFile} from "@/sdk";
6
+ import {Injection} from "@/core/injection";
7
+ import {isControllerArtifact} from "@/sdk";
7
8
  import path from "node:path";
8
9
 
9
10
  export class ControllerBuilder {
@@ -13,24 +14,26 @@ export class ControllerBuilder {
13
14
  .map(file => file.toString());
14
15
  const output: any[] = []
15
16
 
16
- for (const file of files)
17
+ for (const file of files) {
17
18
  output.push(await this.build<any>({filename: path.join(workdir, file), version: 1, timestamp: Date.now()}))
19
+ }
18
20
 
19
21
  return output.filter(f => typeof f !== 'undefined');
20
22
  }
21
23
 
22
24
  static async build<T>({filename, version, timestamp}: BuilderHMRDeclaration): Promise<T | undefined> {
23
- if (!isControllerFile(filename))
25
+ if (!isControllerArtifact(filename))
24
26
  return undefined;
25
27
 
26
28
  const imported = await import(`${filename}?v=${version || 1}&t=${timestamp || Date.now()}`)
27
29
  const controller = imported.default || imported || undefined;
28
30
 
29
31
  if (!controller) return undefined;
30
- if(!RaitonThread.current.application) return undefined;
32
+ if (!RaitonThread.current?.application) return undefined;
31
33
 
32
34
  const compilated = compileController(controller, RaitonThread.current.application);
33
- Logger.log(LBadge.info('Controller'), controller.name, 'compilated');
35
+ const name = controller.name || (typeof controller === 'function' ? controller.name : undefined);
36
+ if (name) Injection.registerArtifactPath(name, filename);
34
37
 
35
38
  return compilated;
36
39
  }
@@ -5,7 +5,7 @@ import "reflect-metadata";
5
5
  export function getControllerMetadata(target: any): ControllerMetaInterface {
6
6
  let metadata = Reflect.getMetadata(METADATA_KEYS.CONTROLLERS, target);
7
7
  if (!metadata) {
8
- metadata = {routes: [], params: {}, middlewares: {}};
8
+ metadata = {routes: [], middlewares: {}};
9
9
  Reflect.defineMetadata(METADATA_KEYS.CONTROLLERS, metadata, target);
10
10
  }
11
11
 
@@ -25,6 +25,6 @@ export class RaitonDirectories {
25
25
  }
26
26
 
27
27
  public static app(): string {
28
- return path.join(Raiton.thread.builder.workdir, RaitonConfig.get('rootDir') || './');
28
+ return path.join(Raiton.thread?.builder.workdir || '', RaitonConfig.get('rootDir') || './');
29
29
  }
30
30
  }
@@ -1,7 +1,6 @@
1
1
  export * from "./config"
2
2
  export * from "./process.util"
3
3
  export * from "./builder"
4
- export * from "./hmr"
5
4
  export * from "./bytes.util"
6
5
  export * from "./commands"
7
6
  export * from "./command"
@@ -1,16 +1,19 @@
1
1
  import "reflect-metadata";
2
2
  import type {IConstructor, ContainerDefinitionInterface} from "@/types";
3
- import {Throwable} from "@/sdk/throwable";
4
3
  import {LifetimeEnum, TextUtility} from "@protorians/core";
5
4
  import {Logger} from "@protorians/logger";
6
5
  import {METADATA_KEYS} from "@/sdk/constants";
7
- import camelCase = TextUtility.camelCase;
6
+ import {Throwable} from "@/sdk/exceptions";
7
+
8
+ const camelCase = TextUtility.camelCase;
8
9
 
9
10
  export class Injection {
10
11
 
11
12
  protected static _classes: Map<string, ContainerDefinitionInterface> = new Map();
12
13
  protected static _instances: Map<string, Map<any, any>> = new Map();
13
14
  protected static _resolutionStack: string[] = [];
15
+ protected static _dependents: Map<string, Set<string>> = new Map();
16
+ protected static _artifactPaths: Map<string, string> = new Map();
14
17
 
15
18
  static get classes(): Map<string, ContainerDefinitionInterface> {
16
19
  return this._classes;
@@ -29,6 +32,8 @@ export class Injection {
29
32
  static clear(): void {
30
33
  this._classes.clear();
31
34
  this._instances.clear();
35
+ this._dependents.clear();
36
+ this._artifactPaths.clear();
32
37
  }
33
38
 
34
39
  static normalizeName(name: string): string {
@@ -49,6 +54,25 @@ export class Injection {
49
54
  return this;
50
55
  }
51
56
 
57
+ static updateConstruct(name: string, construct: IConstructor): typeof this {
58
+ const name_ = this.normalizeName(name);
59
+ this._classes.set(name_, {...this._classes.get(this.normalizeName(name))!, construct});
60
+ return this;
61
+ }
62
+
63
+ static getDependents(name: string): string[] {
64
+ return Array.from(this._dependents.get(this.normalizeName(name)) || []);
65
+ }
66
+
67
+ static registerArtifactPath(name: string, path: string): typeof this {
68
+ this._artifactPaths.set(this.normalizeName(name), path);
69
+ return this;
70
+ }
71
+
72
+ static getArtifactPath(name: string): string | undefined {
73
+ return this._artifactPaths.get(this.normalizeName(name));
74
+ }
75
+
52
76
  static resolveArguments(definition: ContainerDefinitionInterface, scope?: any): any[] {
53
77
  try {
54
78
  const parameters = Reflect.getMetadata(METADATA_KEYS.INJECT_PARAMETERS, definition.construct) || [];
@@ -65,19 +89,24 @@ export class Injection {
65
89
  if (param && param !== true) {
66
90
  const token = typeof param === 'function' ? (param.name || param) : param;
67
91
  if (typeof token === 'string') {
92
+ this.addDependent(token, definition.name);
68
93
  args.push(this.get(token, effectiveScope));
69
94
  continue;
70
95
  }
71
96
  if (typeof param === 'function') {
72
97
  const metadata: ContainerDefinitionInterface = Reflect.getMetadata(METADATA_KEYS.CONTAINER, param);
73
- args.push(this.get(metadata?.name || param.name, effectiveScope));
98
+ const token = metadata?.name || param.name;
99
+ this.addDependent(token, definition.name);
100
+ args.push(this.get(token, effectiveScope));
74
101
  continue;
75
102
  }
76
103
  }
77
104
 
78
105
  if (designParam && typeof designParam === 'function' && designParam.name) {
79
106
  const metadata: ContainerDefinitionInterface = Reflect.getMetadata(METADATA_KEYS.CONTAINER, designParam);
80
- args.push(this.get(metadata?.name || designParam.name, effectiveScope));
107
+ const token = metadata?.name || designParam.name;
108
+ this.addDependent(token, definition.name);
109
+ args.push(this.get(token, effectiveScope));
81
110
  continue;
82
111
  }
83
112
 
@@ -90,6 +119,14 @@ export class Injection {
90
119
  }
91
120
  }
92
121
 
122
+ protected static addDependent(dependencyName: string, dependentName: string) {
123
+ const dep = this.normalizeName(dependencyName);
124
+ if (!this._dependents.has(dep)) {
125
+ this._dependents.set(dep, new Set());
126
+ }
127
+ this._dependents.get(dep)!.add(this.normalizeName(dependentName));
128
+ }
129
+
93
130
  static get<T>(name: string, scope?: Symbol): T | undefined {
94
131
  const name_ = this.normalizeName(name);
95
132
  const cls = this._classes.get(name_);
@@ -114,6 +151,7 @@ export class Injection {
114
151
  instance = new cls.construct(...this.resolveArguments(cls, effectiveScope));
115
152
  scopeInstances.set(effectiveScope, instance);
116
153
  this.injectProperties(instance, cls, effectiveScope);
154
+ this.triggerLifecycle(instance);
117
155
  }
118
156
  return scopeInstances.get(effectiveScope);
119
157
  }
@@ -121,6 +159,7 @@ export class Injection {
121
159
  if (cls.lifetime === LifetimeEnum.TRANSIENT) {
122
160
  instance = new cls.construct(...this.resolveArguments(cls, effectiveScope));
123
161
  this.injectProperties(instance, cls, effectiveScope);
162
+ this.triggerLifecycle(instance);
124
163
  return instance as any;
125
164
  }
126
165
  } finally {
@@ -137,13 +176,36 @@ export class Injection {
137
176
  for (const [propertyKey, type] of properties) {
138
177
  const token = typeof type === 'function' ? (type.name || type) : type;
139
178
  if (typeof token === 'string') {
179
+ this.addDependent(token, definition.name);
140
180
  instance[propertyKey] = this.get(token, scope);
141
181
  } else if (typeof type === 'function') {
142
182
  const metadata: ContainerDefinitionInterface = Reflect.getMetadata(METADATA_KEYS.CONTAINER, type);
143
- instance[propertyKey] = this.get(metadata?.name || type.name, scope);
183
+ const token_ = metadata?.name || type.name;
184
+ this.addDependent(token_, definition.name);
185
+ instance[propertyKey] = this.get(token_, scope);
186
+ }
187
+ }
188
+ }
189
+ }
190
+
191
+ protected static triggerLifecycle(instance: any): void {
192
+ if (typeof instance.onInit === 'function') {
193
+ instance.onInit();
194
+ }
195
+ if (typeof instance.onMount === 'function') {
196
+ instance.onMount();
197
+ }
198
+ }
199
+
200
+ static async shutdown(): Promise<void> {
201
+ for (const scopeInstances of this._instances.values()) {
202
+ for (const instance of scopeInstances.values()) {
203
+ if (typeof instance.onUnmount === 'function') {
204
+ await instance.onUnmount();
144
205
  }
145
206
  }
146
207
  }
208
+ this.clear();
147
209
  }
148
210
 
149
211
  static resolve<T>(construct: IConstructor<T>): T {
@@ -1,5 +1,5 @@
1
1
  import {MiddlewareType} from '@/types'
2
- import {Throwable} from "@/sdk/throwable";
2
+ import {Throwable} from "@/sdk/exceptions";
3
3
 
4
4
  export function middlewareCompose(middlewares: MiddlewareType[]) {
5
5
  return function (request: any) {
@@ -9,9 +9,8 @@ export class Raiton {
9
9
  static title: string = 'Protorians Raiton';
10
10
  static identifier: string = 'raiton';
11
11
 
12
- static get thread(): ThreadInterface {
13
- if (!this._thread)
14
- throw new Error(`${Raiton.title} Thread instance not initialized`);
12
+ static get thread(): ThreadInterface| undefined {
13
+ // if (!this._thread) throw new Error(`${Raiton.title} Thread instance not initialized`);
15
14
  return this._thread;
16
15
  }
17
16