@kanjijs/core 0.2.0-beta.2 → 0.2.0-beta.20

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
@@ -14,8 +14,10 @@ It provides the decorators used to define APIs and the internal storage mechanis
14
14
  - **`@Use(...middlewares)`**: Registers Hono-compatible middleware for a route or controller.
15
15
  - **`@Module({ ... })`**: Defines a module with `imports`, `controllers`, and `providers`.
16
16
  - **`@Inject(token)`**: Token-based dependency injection.
17
+ - **`@Body()`, `@Query()`, `@Param()`, `@Headers()`**: Parameter decorators for fine-grained metadata (used by platform adapters).
17
18
 
18
19
  ### Request Context (AsyncLocalStorage)
20
+
19
21
  Kanjijs Core includes a built-in `kanjijsContext` (based on Node.js AsyncLocalStorage) to track request state globally without prop drilling. It enables access to `requestId` anywhere in the stack.
20
22
 
21
23
  ```typescript
@@ -24,8 +26,11 @@ const reqId = getRequestId();
24
26
  ```
25
27
 
26
28
  ### Dependency Injection
29
+
27
30
  **KanjijsIoC**: A lightweight, platform-agnostic IoC container embedded in Core. It handles singleton resolution and dependency injection for your modules and controllers.
28
31
 
32
+ **Strict Mode Compatible**: The DI container now supports strict Token typing (`Token<T>`) and robustly handles `Function` vs `Constructor` type narrowing, ensuring compatibility with strict TypeScript configurations (`strict: true`).
33
+
29
34
  ### Metadata Storage
30
35
 
31
36
  We use a `WeakMap`-based `MetadataStorage` singleton to keep the memory footprint minimal and ensure metadata is garbage collected if the controller class is no longer referenced.
@@ -39,8 +44,8 @@ const routes = MetadataStorage.getRoutes(ControllerClass.prototype);
39
44
 
40
45
  Kanjijs Core defines the standard exceptions used across the framework.
41
46
 
42
- - **`HttpException`**: Base class for HTTP errors.
43
- - **`ExceptionFilter`**: Interface for implementing custom exception filters.
47
+ - **`HttpException`**: Base class for HTTP errors.
48
+ - **`ExceptionFilter`**: Interface for implementing custom exception filters.
44
49
 
45
50
  ```typescript
46
51
  import { HttpException } from "@kanjijs/core";
@@ -0,0 +1,8 @@
1
+ import { AsyncLocalStorage } from "node:async_hooks";
2
+ export interface RequestContext {
3
+ requestId: string;
4
+ [key: string]// biome-ignore lint/suspicious/noExplicitAny: Suppressed
5
+ : any;
6
+ }
7
+ export declare const kanjijsContext: AsyncLocalStorage<RequestContext>;
8
+ export declare function getRequestId(): string | undefined;
@@ -0,0 +1,27 @@
1
+ export type SchemaLike = unknown;
2
+ export interface ContractRequestSpec<T = SchemaLike> {
3
+ params?: T;
4
+ query?: T;
5
+ headers?: T;
6
+ cookies?: T;
7
+ body?: T;
8
+ }
9
+ export interface ContractResponseSpec<T = SchemaLike> {
10
+ [status: number]: T;
11
+ }
12
+ export interface ContractSpec<T = SchemaLike> {
13
+ request?: ContractRequestSpec<T>;
14
+ response?: ContractResponseSpec<T>;
15
+ }
16
+ export interface ValidatorAdapter<S = SchemaLike> {
17
+ parse<O = unknown>(schema: S, data: unknown): Promise<{
18
+ success: true;
19
+ data: O;
20
+ } | {
21
+ success: false;
22
+ issues: unknown[];
23
+ }>;
24
+ }
25
+ export declare class Contract {
26
+ static json<T = SchemaLike>(spec: ContractRequestSpec<T>): ContractSpec<T>;
27
+ }
@@ -0,0 +1,7 @@
1
+ import { AsyncLocalStorage } from "node:async_hooks";
2
+ export interface RequestContext {
3
+ requestId: string;
4
+ [key: string]: any;
5
+ }
6
+ export declare const kanjijsContext: AsyncLocalStorage<RequestContext>;
7
+ export declare function getRequestId(): string | undefined;
@@ -0,0 +1,34 @@
1
+ import type { ContractSpec } from "@kanjijs/contracts";
2
+ import { type ModuleMetadata, type Token } from "./metadata";
3
+ /**
4
+ * @Module({ controllers: [...] })
5
+ */
6
+ export declare function Module(metadata: ModuleMetadata): ClassDecorator;
7
+ /**
8
+ * @Contract({ ... })
9
+ */
10
+ export declare function Contract(spec: ContractSpec): MethodDecorator;
11
+ /**
12
+ * @Controller('/users')
13
+ */
14
+ export declare function Controller(prefix?: string): ClassDecorator;
15
+ export declare function Injectable(): ClassDecorator;
16
+ export declare const Get: (path?: string) => MethodDecorator;
17
+ export declare const Post: (path?: string) => MethodDecorator;
18
+ export declare const Put: (path?: string) => MethodDecorator;
19
+ export declare const Delete: (path?: string) => MethodDecorator;
20
+ export declare const Patch: (path?: string) => MethodDecorator;
21
+ /**
22
+ * @Inject("DATABASE_CLIENT")
23
+ */
24
+ export declare function Inject(token: Token<unknown>): ParameterDecorator;
25
+ /**
26
+ * @Use(middleware1, middleware2)
27
+ * Attaches middlewares to a controller or method.
28
+ */
29
+ export declare function Use(...middlewares: unknown[]): MethodDecorator & ClassDecorator;
30
+ export declare const Body: (data?: string) => ParameterDecorator;
31
+ export declare const Query: (data?: string) => ParameterDecorator;
32
+ export declare const Param: (data?: string) => ParameterDecorator;
33
+ export declare const Headers: (data?: string) => ParameterDecorator;
34
+ export declare const Ctx: (data?: string) => ParameterDecorator;
@@ -0,0 +1,26 @@
1
+ import { type Constructor, type Token } from "../metadata";
2
+ import "reflect-metadata";
3
+ export declare class KanjijsIoC {
4
+ private static providers;
5
+ static register<T>(target: Constructor<T>): void;
6
+ static register<T>(token: Token<T>, provider: {
7
+ useValue?: T;
8
+ useClass?: Constructor<T>;
9
+ }): void;
10
+ static resolve<T>(target: Token<T>): T;
11
+ static clear(): void;
12
+ }
13
+ /**
14
+ * V2 STRICT CONTAINER
15
+ * Instance-based, no auto-registration, explicit visibility.
16
+ */
17
+ export declare class Container {
18
+ private providers;
19
+ register<T>(token: Token<T>, provider: {
20
+ useValue?: T;
21
+ useClass?: Constructor<T>;
22
+ useFactory?: (...args: unknown[]) => T;
23
+ inject?: Array<Token<unknown>>;
24
+ }): void;
25
+ resolve<T>(token: Token<T>): T;
26
+ }
@@ -0,0 +1,12 @@
1
+ import { type Constructor } from "../metadata";
2
+ import { Container } from "./container";
3
+ export declare class ModuleCompiler {
4
+ private nodes;
5
+ private globalExportedTokens;
6
+ compile(rootModule: Constructor): Container;
7
+ private scan;
8
+ private processProviders;
9
+ private validate;
10
+ private checkDependencies;
11
+ private registerProviders;
12
+ }
@@ -0,0 +1,3 @@
1
+ export interface ExceptionFilter<T = any, C = any> {
2
+ catch(exception: T, context: C): void | Promise<void> | any;
3
+ }
@@ -0,0 +1,7 @@
1
+ export declare class HttpException extends Error {
2
+ readonly response: string | object;
3
+ readonly status: number;
4
+ constructor(response: string | object, status: number);
5
+ getResponse(): string | object;
6
+ getStatus(): number;
7
+ }
@@ -0,0 +1,9 @@
1
+ export type { ContractRequestSpec, ContractResponseSpec, ContractSpec, SchemaLike, ValidatorAdapter, } from "@kanjijs/contracts";
2
+ export * from "./context";
3
+ export * from "./decorators";
4
+ export * from "./di/container";
5
+ export * from "./di/module-compiler";
6
+ export * from "./exceptions/exception.filter";
7
+ export * from "./exceptions/http.exception";
8
+ export * from "./metadata";
9
+ export declare const GLOBAL_MIDDLEWARE_TOKEN: unique symbol;
@@ -0,0 +1,64 @@
1
+ import type { ContractSpec } from "@kanjijs/contracts";
2
+ import "reflect-metadata";
3
+ export type Constructor<T = unknown> = new (...args: unknown[]) => T;
4
+ export type HttpMethod = "GET" | "POST" | "PUT" | "DELETE" | "PATCH";
5
+ export type RouteParamType = "BODY" | "QUERY" | "PARAM" | "HEADERS" | "CONTEXT";
6
+ export interface RouteParamMetadata {
7
+ index: number;
8
+ type: RouteParamType;
9
+ data?: string;
10
+ }
11
+ export interface RouteMetadata {
12
+ method: HttpMethod;
13
+ path: string;
14
+ contract?: ContractSpec;
15
+ middlewares?: unknown[];
16
+ params?: RouteParamMetadata[];
17
+ }
18
+ export interface ControllerMetadata {
19
+ prefix: string;
20
+ middlewares?: unknown[];
21
+ }
22
+ export type Token<T = unknown> = string | symbol | Constructor<T>;
23
+ export type Provider<T = unknown> = Constructor<T> | {
24
+ provide: Token<T>;
25
+ useValue: T;
26
+ } | {
27
+ provide: Token<T>;
28
+ useClass: Constructor<T>;
29
+ } | {
30
+ provide: Token<T>;
31
+ useFactory: (...args: unknown[]) => T | Promise<T>;
32
+ inject?: Token[];
33
+ };
34
+ export interface DynamicModule {
35
+ module: Constructor;
36
+ providers?: Provider[];
37
+ imports?: Array<Constructor | DynamicModule>;
38
+ exports?: Token[];
39
+ global?: boolean;
40
+ }
41
+ export interface ModuleMetadata {
42
+ controllers?: Constructor[];
43
+ providers?: Provider[];
44
+ imports?: Array<Constructor | DynamicModule>;
45
+ exports?: Token[];
46
+ global?: boolean;
47
+ }
48
+ export interface IMetadataStorage {
49
+ addRoute(target: object, methodName: string, meta: RouteMetadata): void;
50
+ addContract(target: object, methodName: string, contract: ContractSpec): void;
51
+ addMiddleware(target: object, middleware: unknown, methodName?: string): void;
52
+ getRoutes(target: object): Map<string, RouteMetadata> | undefined;
53
+ setController(target: object, meta: ControllerMetadata): void;
54
+ getController(target: object): ControllerMetadata | undefined;
55
+ defineModule(target: object, meta: ModuleMetadata): void;
56
+ getModule(target: object): ModuleMetadata | undefined;
57
+ addInjection(target: object, index: number, token: Token<unknown>): void;
58
+ getInjections(target: object): Map<number, Token<unknown>> | undefined;
59
+ addRouteParam(target: object, methodName: string, param: RouteParamMetadata): void;
60
+ }
61
+ declare global {
62
+ var KANJI_METADATA_STORAGE: IMetadataStorage | undefined;
63
+ }
64
+ export declare const MetadataStorage: IMetadataStorage;
@@ -0,0 +1,36 @@
1
+ import type { ContractSpec } from "@kanjijs/contracts";
2
+ import { type ModuleMetadata } from "./metadata";
3
+ /**
4
+ * @Module({ controllers: [...] })
5
+ */
6
+ export declare function Module(metadata: ModuleMetadata): ClassDecorator;
7
+ /**
8
+ * @Contract({ ... })
9
+ */
10
+ export declare function Contract(spec: ContractSpec): MethodDecorator;
11
+ /**
12
+ * @Controller('/users')
13
+ */
14
+ export declare function Controller(prefix?: string): ClassDecorator;
15
+ export declare function Injectable(): ClassDecorator;
16
+ export declare const Get: (path?: string) => MethodDecorator;
17
+ export declare const Post: (path?: string) => MethodDecorator;
18
+ export declare const Put: (path?: string) => MethodDecorator;
19
+ export declare const Delete: (path?: string) => MethodDecorator;
20
+ export declare const Patch: (path?: string) => MethodDecorator;
21
+ /**
22
+ * @Inject("DATABASE_CLIENT")
23
+ */
24
+ export declare function Inject(token// biome-ignore lint/suspicious/noExplicitAny: Suppressed
25
+ : any): ParameterDecorator;
26
+ /**
27
+ * @Use(middleware1, middleware2)
28
+ * Attaches middlewares to a controller or method.
29
+ */
30
+ export declare function Use(...middlewares// biome-ignore lint/suspicious/noExplicitAny: Suppressed
31
+ : any[]): MethodDecorator & ClassDecorator;
32
+ export declare const Body: (data?: string) => ParameterDecorator;
33
+ export declare const Query: (data?: string) => ParameterDecorator;
34
+ export declare const Param: (data?: string) => ParameterDecorator;
35
+ export declare const Headers: (data?: string) => ParameterDecorator;
36
+ export declare const Ctx: (data?: string) => ParameterDecorator;
@@ -0,0 +1,31 @@
1
+ import { type Constructor } from "../metadata";
2
+ import "reflect-metadata";
3
+ export declare class KanjijsIoC {
4
+ private static providers;
5
+ static register<T>(target: Constructor<T>): void;
6
+ static register(token// biome-ignore lint/suspicious/noExplicitAny: Suppressed
7
+ : any, provider: {
8
+ useValue?// biome-ignore lint/suspicious/noExplicitAny: Suppressed
9
+ : any;
10
+ useClass?: Constructor;
11
+ }): void;
12
+ static resolve<T>(target: Constructor<T> | any): T;
13
+ static clear(): void;
14
+ }
15
+ /**
16
+ * V2 STRICT CONTAINER
17
+ * Instance-based, no auto-registration, explicit visibility.
18
+ */
19
+ export declare class Container {
20
+ private providers;
21
+ register(token// biome-ignore lint/suspicious/noExplicitAny: Suppressed
22
+ : any, provider: {
23
+ useValue?// biome-ignore lint/suspicious/noExplicitAny: Suppressed
24
+ : any;
25
+ useClass?: Constructor;
26
+ useFactory?: Function;
27
+ inject?// biome-ignore lint/suspicious/noExplicitAny: Suppressed
28
+ : any[];
29
+ }): void;
30
+ resolve<T>(token: Constructor<T> | any): T;
31
+ }
@@ -0,0 +1,11 @@
1
+ import { type Constructor } from "../metadata";
2
+ import { Container } from "./container";
3
+ export declare class ModuleCompiler {
4
+ private nodes;
5
+ private globalExportedTokens;
6
+ compile(rootModule: Constructor): Container;
7
+ private scan;
8
+ private processProviders;
9
+ private validate;
10
+ private checkDependencies;
11
+ }
@@ -0,0 +1,3 @@
1
+ export interface ExceptionFilter<T = any, C = any> {
2
+ catch(exception: T, context: C): void | Promise<void> | any;
3
+ }
@@ -0,0 +1,7 @@
1
+ export declare class HttpException extends Error {
2
+ readonly response: string | object;
3
+ readonly status: number;
4
+ constructor(response: string | object, status: number);
5
+ getResponse(): string | object;
6
+ getStatus(): number;
7
+ }
@@ -0,0 +1,9 @@
1
+ export * from "./decorators";
2
+ export * from "./metadata";
3
+ export * from "./di/container";
4
+ export * from "./context";
5
+ export * from "./exceptions/http.exception";
6
+ export * from "./exceptions/exception.filter";
7
+ export * from "./di/module-compiler";
8
+ export * from "@kanjijs/contracts";
9
+ export declare const GLOBAL_MIDDLEWARE_TOKEN: unique symbol;
package/dist/index.js CHANGED
@@ -1013,75 +1013,95 @@ var require_Reflect = __commonJS(() => {
1013
1013
  })(Reflect2 || (Reflect2 = {}));
1014
1014
  });
1015
1015
 
1016
+ // src/context.ts
1017
+ import { AsyncLocalStorage } from "async_hooks";
1018
+ var kanjijsContext = new AsyncLocalStorage;
1019
+ function getRequestId() {
1020
+ return kanjijsContext.getStore()?.requestId;
1021
+ }
1016
1022
  // src/metadata.ts
1017
1023
  var import_reflect_metadata = __toESM(require_Reflect(), 1);
1018
1024
  var methodMetadataStore = new WeakMap;
1019
1025
  var controllerMetadataStore = new WeakMap;
1020
1026
  var moduleMetadataStore = new WeakMap;
1021
1027
  var injectionMetadataStore = new WeakMap;
1022
- var MetadataStorage = {
1023
- addRoute(target, methodName, meta) {
1024
- let methods = methodMetadataStore.get(target);
1025
- if (!methods) {
1026
- methods = new Map;
1027
- methodMetadataStore.set(target, methods);
1028
- }
1029
- const existing = methods.get(methodName) || {};
1030
- methods.set(methodName, { ...existing, ...meta });
1031
- },
1032
- addContract(target, methodName, contract) {
1033
- let methods = methodMetadataStore.get(target);
1034
- if (!methods) {
1035
- methods = new Map;
1036
- methodMetadataStore.set(target, methods);
1037
- }
1038
- const existing = methods.get(methodName) || {};
1039
- existing.contract = contract;
1040
- methods.set(methodName, existing);
1041
- },
1042
- addMiddleware(target, middleware, methodName) {
1043
- if (methodName) {
1028
+ var globalStore = globalThis;
1029
+ if (!globalStore.KANJI_METADATA_STORAGE) {
1030
+ globalStore.KANJI_METADATA_STORAGE = {
1031
+ addRoute(target, methodName, meta) {
1044
1032
  let methods = methodMetadataStore.get(target);
1045
1033
  if (!methods) {
1046
1034
  methods = new Map;
1047
1035
  methodMetadataStore.set(target, methods);
1048
1036
  }
1049
1037
  const existing = methods.get(methodName) || {};
1050
- existing.middlewares = [...existing.middlewares || [], middleware];
1038
+ methods.set(methodName, { ...existing, ...meta });
1039
+ },
1040
+ addContract(target, methodName, contract) {
1041
+ let methods = methodMetadataStore.get(target);
1042
+ if (!methods) {
1043
+ methods = new Map;
1044
+ methodMetadataStore.set(target, methods);
1045
+ }
1046
+ const existing = methods.get(methodName) || {};
1047
+ existing.contract = contract;
1051
1048
  methods.set(methodName, existing);
1052
- } else {
1053
- const existing = controllerMetadataStore.get(target) || { prefix: "/" };
1054
- existing.middlewares = [...existing.middlewares || [], middleware];
1055
- controllerMetadataStore.set(target, existing);
1056
- }
1057
- },
1058
- getRoutes(target) {
1059
- return methodMetadataStore.get(target);
1060
- },
1061
- setController(target, meta) {
1062
- controllerMetadataStore.set(target, meta);
1063
- },
1064
- getController(target) {
1065
- return controllerMetadataStore.get(target);
1066
- },
1067
- defineModule(target, meta) {
1068
- moduleMetadataStore.set(target, meta);
1069
- },
1070
- getModule(target) {
1071
- return moduleMetadataStore.get(target);
1072
- },
1073
- addInjection(target, index, token) {
1074
- let injections = injectionMetadataStore.get(target);
1075
- if (!injections) {
1076
- injections = new Map;
1077
- injectionMetadataStore.set(target, injections);
1049
+ },
1050
+ addRouteParam(target, methodName, param) {
1051
+ let methods = methodMetadataStore.get(target);
1052
+ if (!methods) {
1053
+ methods = new Map;
1054
+ methodMetadataStore.set(target, methods);
1055
+ }
1056
+ const existing = methods.get(methodName) || {};
1057
+ existing.params = [...existing.params || [], param];
1058
+ methods.set(methodName, existing);
1059
+ },
1060
+ addMiddleware(target, middleware, methodName) {
1061
+ if (methodName) {
1062
+ let methods = methodMetadataStore.get(target);
1063
+ if (!methods) {
1064
+ methods = new Map;
1065
+ methodMetadataStore.set(target, methods);
1066
+ }
1067
+ const existing = methods.get(methodName) || {};
1068
+ existing.middlewares = [...existing.middlewares || [], middleware];
1069
+ methods.set(methodName, existing);
1070
+ } else {
1071
+ const existing = controllerMetadataStore.get(target) || { prefix: "/" };
1072
+ existing.middlewares = [...existing.middlewares || [], middleware];
1073
+ controllerMetadataStore.set(target, existing);
1074
+ }
1075
+ },
1076
+ getRoutes(target) {
1077
+ return methodMetadataStore.get(target);
1078
+ },
1079
+ setController(target, meta) {
1080
+ controllerMetadataStore.set(target, meta);
1081
+ },
1082
+ getController(target) {
1083
+ return controllerMetadataStore.get(target);
1084
+ },
1085
+ defineModule(target, meta) {
1086
+ moduleMetadataStore.set(target, meta);
1087
+ },
1088
+ getModule(target) {
1089
+ return moduleMetadataStore.get(target);
1090
+ },
1091
+ addInjection(target, index, token) {
1092
+ let injections = injectionMetadataStore.get(target);
1093
+ if (!injections) {
1094
+ injections = new Map;
1095
+ injectionMetadataStore.set(target, injections);
1096
+ }
1097
+ injections.set(index, token);
1098
+ },
1099
+ getInjections(target) {
1100
+ return injectionMetadataStore.get(target);
1078
1101
  }
1079
- injections.set(index, token);
1080
- },
1081
- getInjections(target) {
1082
- return injectionMetadataStore.get(target);
1083
- }
1084
- };
1102
+ };
1103
+ }
1104
+ var MetadataStorage = globalStore.KANJI_METADATA_STORAGE;
1085
1105
 
1086
1106
  // src/decorators.ts
1087
1107
  function Module(metadata) {
@@ -1100,7 +1120,7 @@ function Controller(prefix = "") {
1100
1120
  };
1101
1121
  }
1102
1122
  function Injectable() {
1103
- return (target) => {};
1123
+ return (_target) => {};
1104
1124
  }
1105
1125
  function createMethodDecorator(method) {
1106
1126
  return (path = "/") => {
@@ -1118,12 +1138,12 @@ var Put = createMethodDecorator("PUT");
1118
1138
  var Delete = createMethodDecorator("DELETE");
1119
1139
  var Patch = createMethodDecorator("PATCH");
1120
1140
  function Inject(token) {
1121
- return (target, propertyKey, parameterIndex) => {
1141
+ return (target, _propertyKey, parameterIndex) => {
1122
1142
  MetadataStorage.addInjection(target, parameterIndex, token);
1123
1143
  };
1124
1144
  }
1125
1145
  function Use(...middlewares) {
1126
- return (target, propertyKey, descriptor) => {
1146
+ return (target, propertyKey, _descriptor) => {
1127
1147
  if (propertyKey) {
1128
1148
  for (const m of middlewares) {
1129
1149
  MetadataStorage.addMiddleware(target, m, propertyKey);
@@ -1135,6 +1155,24 @@ function Use(...middlewares) {
1135
1155
  }
1136
1156
  };
1137
1157
  }
1158
+ function createParamDecorator(type) {
1159
+ return (data) => {
1160
+ return (target, propertyKey, parameterIndex) => {
1161
+ if (propertyKey) {
1162
+ MetadataStorage.addRouteParam(target, propertyKey, {
1163
+ index: parameterIndex,
1164
+ type,
1165
+ data
1166
+ });
1167
+ }
1168
+ };
1169
+ };
1170
+ }
1171
+ var Body = createParamDecorator("BODY");
1172
+ var Query = createParamDecorator("QUERY");
1173
+ var Param = createParamDecorator("PARAM");
1174
+ var Headers = createParamDecorator("HEADERS");
1175
+ var Ctx = createParamDecorator("CONTEXT");
1138
1176
  // src/di/container.ts
1139
1177
  var import_reflect_metadata2 = __toESM(require_Reflect(), 1);
1140
1178
 
@@ -1143,43 +1181,48 @@ class KanjijsIoC {
1143
1181
  static register(tokenOrTarget, provider) {
1144
1182
  if (provider) {
1145
1183
  if ("useValue" in provider) {
1146
- this.providers.set(tokenOrTarget, { useValue: provider.useValue });
1184
+ KanjijsIoC.providers.set(tokenOrTarget, { useValue: provider.useValue });
1147
1185
  } else if ("useClass" in provider) {
1148
- this.providers.set(tokenOrTarget, { useClass: provider.useClass });
1186
+ KanjijsIoC.providers.set(tokenOrTarget, { useClass: provider.useClass });
1149
1187
  }
1150
1188
  } else {
1151
- this.providers.set(tokenOrTarget, { useClass: tokenOrTarget });
1189
+ KanjijsIoC.providers.set(tokenOrTarget, {
1190
+ useClass: tokenOrTarget
1191
+ });
1152
1192
  }
1153
1193
  }
1154
1194
  static resolve(target) {
1155
- let provider = this.providers.get(target);
1195
+ let provider = KanjijsIoC.providers.get(target);
1156
1196
  if (!provider && typeof target === "function") {
1157
1197
  provider = { useClass: target };
1158
- this.providers.set(target, provider);
1198
+ KanjijsIoC.providers.set(target, provider);
1159
1199
  }
1160
1200
  if (!provider) {
1161
- throw new Error(`Provider not found for token: ${target?.name || target}`);
1201
+ const targetName2 = typeof target === "function" ? target.name ?? "anonymous" : String(target);
1202
+ throw new Error(`Provider not found for token: ${targetName2}`);
1162
1203
  }
1163
1204
  if (provider.instance) {
1164
1205
  return provider.instance;
1165
1206
  }
1166
- console.log(`[DI] Creating NEW instance for ${target?.name || String(target)}`);
1207
+ const targetName = typeof target === "function" ? target.name ?? "anonymous" : String(target);
1208
+ console.log(`[DI] Creating NEW instance for ${targetName}`);
1167
1209
  if (provider.useValue !== undefined) {
1168
1210
  provider.instance = provider.useValue;
1169
1211
  } else if (provider.useClass) {
1170
1212
  const ConcreteClass = provider.useClass;
1171
1213
  const paramTypes = Reflect.getMetadata("design:paramtypes", ConcreteClass) || [];
1172
1214
  const injectionTokens = MetadataStorage.getInjections(ConcreteClass) || new Map;
1173
- const injections = paramTypes.map((token, index) => {
1215
+ const injections = paramTypes.map((paramToken, index) => {
1174
1216
  const overrideToken = injectionTokens.get(index);
1175
- return KanjijsIoC.resolve(overrideToken || token);
1217
+ const resolvedToken = overrideToken || paramToken;
1218
+ return KanjijsIoC.resolve(resolvedToken);
1176
1219
  });
1177
1220
  provider.instance = new ConcreteClass(...injections);
1178
1221
  }
1179
1222
  return provider.instance;
1180
1223
  }
1181
1224
  static clear() {
1182
- this.providers.clear();
1225
+ KanjijsIoC.providers.clear();
1183
1226
  }
1184
1227
  }
1185
1228
 
@@ -1191,7 +1234,8 @@ class Container {
1191
1234
  resolve(token) {
1192
1235
  const provider = this.providers.get(token);
1193
1236
  if (!provider) {
1194
- throw new Error(`[DI] Provider not found for token: ${token?.name || String(token)}`);
1237
+ const tokenName = typeof token === "function" ? token.name ?? "anonymous" : String(token);
1238
+ throw new Error(`[DI] Provider not found for token: ${tokenName}`);
1195
1239
  }
1196
1240
  if (provider.instance) {
1197
1241
  return provider.instance;
@@ -1204,7 +1248,8 @@ class Container {
1204
1248
  const injectionTokens = MetadataStorage.getInjections(ConcreteClass) || new Map;
1205
1249
  const injections = paramTypes.map((paramToken, index) => {
1206
1250
  const overrideToken = injectionTokens.get(index);
1207
- return this.resolve(overrideToken || paramToken);
1251
+ const resolvedToken = overrideToken || paramToken;
1252
+ return this.resolve(resolvedToken);
1208
1253
  });
1209
1254
  provider.instance = new ConcreteClass(...injections);
1210
1255
  } else if (provider.useFactory) {
@@ -1214,29 +1259,6 @@ class Container {
1214
1259
  return provider.instance;
1215
1260
  }
1216
1261
  }
1217
- // src/context.ts
1218
- import { AsyncLocalStorage } from "async_hooks";
1219
- var kanjijsContext = new AsyncLocalStorage;
1220
- function getRequestId() {
1221
- return kanjijsContext.getStore()?.requestId;
1222
- }
1223
- // src/exceptions/http.exception.ts
1224
- class HttpException extends Error {
1225
- response;
1226
- status;
1227
- constructor(response, status) {
1228
- super(typeof response === "string" ? response : JSON.stringify(response));
1229
- this.response = response;
1230
- this.status = status;
1231
- this.name = "HttpException";
1232
- }
1233
- getResponse() {
1234
- return this.response;
1235
- }
1236
- getStatus() {
1237
- return this.status;
1238
- }
1239
- }
1240
1262
  // src/di/module-compiler.ts
1241
1263
  class ModuleCompiler {
1242
1264
  nodes = new Map;
@@ -1245,11 +1267,11 @@ class ModuleCompiler {
1245
1267
  this.scan(rootModule);
1246
1268
  this.validate();
1247
1269
  const container = new Container;
1248
- for (const node of this.nodes.values()) {
1249
- for (const [token, provider] of node.providers) {
1250
- container.register(token, provider);
1251
- }
1270
+ const rootNode = this.nodes.get(rootModule);
1271
+ if (!rootNode) {
1272
+ return container;
1252
1273
  }
1274
+ this.registerProviders(rootNode, container, new Set);
1253
1275
  return container;
1254
1276
  }
1255
1277
  scan(target) {
@@ -1266,20 +1288,20 @@ class ModuleCompiler {
1266
1288
  };
1267
1289
  this.nodes.set(moduleClass, node);
1268
1290
  const meta = MetadataStorage.getModule(moduleClass) || {};
1269
- const dynamicMeta = "module" in target ? target : {};
1270
- const allProviders = [...meta.providers || [], ...dynamicMeta.providers || []];
1291
+ const dynamicMeta = "module" in target ? target : undefined;
1292
+ const allProviders = [...meta.providers || [], ...dynamicMeta?.providers || []];
1271
1293
  this.processProviders(node, allProviders);
1272
- const allExports = [...meta.exports || [], ...dynamicMeta.exports || []];
1294
+ const allExports = [...meta.exports || [], ...dynamicMeta?.exports || []];
1273
1295
  for (const token of allExports) {
1274
1296
  node.exports.add(token);
1275
1297
  }
1276
- if (meta.global || dynamicMeta.global) {
1298
+ if (meta.global || dynamicMeta?.global) {
1277
1299
  node.isGlobal = true;
1278
1300
  for (const token of node.exports) {
1279
1301
  this.globalExportedTokens.add(token);
1280
1302
  }
1281
1303
  }
1282
- const allImports = [...meta.imports || [], ...dynamicMeta.imports || []];
1304
+ const allImports = [...meta.imports || [], ...dynamicMeta?.imports || []];
1283
1305
  for (const imp of allImports) {
1284
1306
  const importedNode = this.scan(imp);
1285
1307
  node.imports.add(importedNode);
@@ -1318,7 +1340,7 @@ class ModuleCompiler {
1318
1340
  for (const globalToken of this.globalExportedTokens) {
1319
1341
  visibleTokens.add(globalToken);
1320
1342
  }
1321
- for (const [token, provider] of node.providers) {
1343
+ for (const [_token, provider] of node.providers) {
1322
1344
  this.checkDependencies(provider, visibleTokens, node.module.name);
1323
1345
  }
1324
1346
  }
@@ -1331,18 +1353,50 @@ class ModuleCompiler {
1331
1353
  targetName = clazz.name;
1332
1354
  const paramTypes = Reflect.getMetadata("design:paramtypes", clazz) || [];
1333
1355
  const injectionTokens = MetadataStorage.getInjections(clazz) || new Map;
1334
- dependencies = paramTypes.map((t, i) => injectionTokens.get(i) || t);
1335
- } else if ("useFactory" in provider && provider.useFactory) {
1336
- targetName = provider.provide?.name || String(provider.provide);
1356
+ dependencies = paramTypes.map((paramType, index) => {
1357
+ const overrideToken = injectionTokens.get(index);
1358
+ return overrideToken || paramType;
1359
+ });
1360
+ } else if ("useFactory" in provider) {
1361
+ targetName = typeof provider.provide === "function" ? provider.provide.name ?? "anonymous" : String(provider.provide);
1337
1362
  dependencies = provider.inject || [];
1338
1363
  }
1339
1364
  for (const dep of dependencies) {
1340
1365
  if (!visibleTokens.has(dep)) {
1341
- const depName = dep?.name || String(dep);
1366
+ const depName = typeof dep === "function" ? dep.name ?? "anonymous" : String(dep);
1342
1367
  throw new Error(`[Kanjijs] strict-di-error: Provider '${targetName}' in Module '${moduleName}' ` + `depends on '${depName}', but it is not visible. ` + `Make sure it is imported and exported by the source module.`);
1343
1368
  }
1344
1369
  }
1345
1370
  }
1371
+ registerProviders(node, container, visited) {
1372
+ if (visited.has(node))
1373
+ return;
1374
+ visited.add(node);
1375
+ for (const imp of node.imports) {
1376
+ this.registerProviders(imp, container, visited);
1377
+ }
1378
+ for (const [token, provider] of node.providers) {
1379
+ const { provide: _provide, ...definition } = provider;
1380
+ container.register(token, definition);
1381
+ }
1382
+ }
1383
+ }
1384
+ // src/exceptions/http.exception.ts
1385
+ class HttpException extends Error {
1386
+ response;
1387
+ status;
1388
+ constructor(response, status) {
1389
+ super(typeof response === "string" ? response : JSON.stringify(response));
1390
+ this.response = response;
1391
+ this.status = status;
1392
+ this.name = "HttpException";
1393
+ }
1394
+ getResponse() {
1395
+ return this.response;
1396
+ }
1397
+ getStatus() {
1398
+ return this.status;
1399
+ }
1346
1400
  }
1347
1401
 
1348
1402
  // src/index.ts
@@ -1351,9 +1405,11 @@ export {
1351
1405
  kanjijsContext,
1352
1406
  getRequestId,
1353
1407
  Use,
1408
+ Query,
1354
1409
  Put,
1355
1410
  Post,
1356
1411
  Patch,
1412
+ Param,
1357
1413
  ModuleCompiler,
1358
1414
  Module,
1359
1415
  MetadataStorage,
@@ -1361,10 +1417,13 @@ export {
1361
1417
  Injectable,
1362
1418
  Inject,
1363
1419
  HttpException,
1420
+ Headers,
1364
1421
  Get,
1365
1422
  GLOBAL_MIDDLEWARE_TOKEN,
1366
1423
  Delete,
1424
+ Ctx,
1367
1425
  Controller,
1368
1426
  Contract,
1369
- Container
1427
+ Container,
1428
+ Body
1370
1429
  };
@@ -0,0 +1,70 @@
1
+ import type { ContractSpec } from "@kanjijs/contracts";
2
+ import "reflect-metadata";
3
+ export type Constructor<T = any> = new (...args// biome-ignore lint/suspicious/noExplicitAny: Suppressed
4
+ : any[]) => T;
5
+ export type HttpMethod = "GET" | "POST" | "PUT" | "DELETE" | "PATCH";
6
+ export type RouteParamType = "BODY" | "QUERY" | "PARAM" | "HEADERS" | "CONTEXT";
7
+ export interface RouteParamMetadata {
8
+ index: number;
9
+ type: RouteParamType;
10
+ data?: string;
11
+ }
12
+ export interface RouteMetadata {
13
+ method: HttpMethod;
14
+ path: string;
15
+ contract?: ContractSpec;
16
+ middlewares?// biome-ignore lint/suspicious/noExplicitAny: Suppressed
17
+ : any[];
18
+ params?: RouteParamMetadata[];
19
+ }
20
+ export interface ControllerMetadata {
21
+ prefix: string;
22
+ middlewares?// biome-ignore lint/suspicious/noExplicitAny: Suppressed
23
+ : any[];
24
+ }
25
+ export type Token<T = any> = string | symbol | Constructor<T>;
26
+ export type Provider<T = any> = Constructor<T> | {
27
+ provide: Token<T>;
28
+ useValue: T;
29
+ } | {
30
+ provide: Token<T>;
31
+ useClass: Constructor<T>;
32
+ } | {
33
+ provide: Token<T>;
34
+ useFactory: (...args// biome-ignore lint/suspicious/noExplicitAny: Suppressed
35
+ : any[]) => T | Promise<T>;
36
+ inject?: Token[];
37
+ };
38
+ export interface DynamicModule {
39
+ module: Constructor;
40
+ providers?: Provider[];
41
+ imports?: Array<Constructor | DynamicModule>;
42
+ exports?: Token[];
43
+ global?: boolean;
44
+ }
45
+ export interface ModuleMetadata {
46
+ controllers?: Constructor[];
47
+ providers?: Provider[];
48
+ imports?: Array<Constructor | DynamicModule>;
49
+ exports?: Token[];
50
+ global?: boolean;
51
+ }
52
+ export interface IMetadataStorage {
53
+ addRoute(target: object, methodName: string, meta: RouteMetadata): void;
54
+ addContract(target: object, methodName: string, contract: ContractSpec): void;
55
+ addMiddleware(target: object, middleware// biome-ignore lint/suspicious/noExplicitAny: Suppressed
56
+ : any, methodName?: string): void;
57
+ getRoutes(target: object): Map<string, RouteMetadata> | undefined;
58
+ setController(target: object, meta: ControllerMetadata): void;
59
+ getController(target: object): ControllerMetadata | undefined;
60
+ defineModule(target: object, meta: ModuleMetadata): void;
61
+ getModule(target: object): ModuleMetadata | undefined;
62
+ addInjection(target: object, index: number, token// biome-ignore lint/suspicious/noExplicitAny: Suppressed
63
+ : any): void;
64
+ getInjections(target: object): Map<number, any> | undefined;
65
+ addRouteParam(target: object, methodName: string, param: RouteParamMetadata): void;
66
+ }
67
+ declare global {
68
+ var KANJI_METADATA_STORAGE: IMetadataStorage | undefined;
69
+ }
70
+ export declare const MetadataStorage: IMetadataStorage;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kanjijs/core",
3
- "version": "0.2.0-beta.2",
3
+ "version": "0.2.0-beta.20",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
@@ -10,12 +10,12 @@
10
10
  "LICENSE"
11
11
  ],
12
12
  "scripts": {
13
- "build": "bun build src/index.ts --outdir dist --target bun",
13
+ "build": "bun build src/index.ts --outdir dist --target bun && bunx tsc --emitDeclarationOnly --declaration --outDir dist",
14
14
  "test": "bun test"
15
15
  },
16
16
  "dependencies": {
17
- "@kanjijs/common": "^0.2.0-beta.2",
18
- "@kanjijs/contracts": "^0.2.0-beta.2",
17
+ "@kanjijs/common": "^0.2.0-beta.20",
18
+ "@kanjijs/contracts": "^0.2.0-beta.20",
19
19
  "reflect-metadata": "^0.2.0"
20
20
  },
21
21
  "devDependencies": {