@carno.js/core 1.1.1 → 1.1.2

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 (119) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +188 -188
  3. package/dist/Carno.js +46 -26
  4. package/dist/Carno.mjs +46 -26
  5. package/dist/bun/index.js +4 -4
  6. package/dist/bun/index.js.map +29 -29
  7. package/package.json +2 -2
  8. package/src/Carno.ts +718 -673
  9. package/src/DefaultRoutes.ts +34 -34
  10. package/src/cache/CacheDriver.ts +50 -50
  11. package/src/cache/CacheService.ts +139 -139
  12. package/src/cache/MemoryDriver.ts +104 -104
  13. package/src/cache/RedisDriver.ts +116 -116
  14. package/src/compiler/JITCompiler.ts +167 -167
  15. package/src/container/Container.ts +168 -168
  16. package/src/context/Context.ts +130 -130
  17. package/src/cors/CorsHandler.ts +145 -145
  18. package/src/decorators/Controller.ts +63 -63
  19. package/src/decorators/Inject.ts +16 -16
  20. package/src/decorators/Middleware.ts +22 -22
  21. package/src/decorators/Service.ts +18 -18
  22. package/src/decorators/methods.ts +58 -58
  23. package/src/decorators/params.ts +47 -47
  24. package/src/events/Lifecycle.ts +97 -97
  25. package/src/exceptions/HttpException.ts +99 -99
  26. package/src/index.ts +95 -95
  27. package/src/metadata.ts +46 -46
  28. package/src/middleware/CarnoMiddleware.ts +14 -14
  29. package/src/router/RadixRouter.ts +225 -225
  30. package/src/testing/TestHarness.ts +185 -185
  31. package/src/utils/Metadata.ts +43 -43
  32. package/src/utils/parseQuery.ts +161 -161
  33. package/src/validation/ValibotAdapter.ts +95 -95
  34. package/src/validation/ValidatorAdapter.ts +69 -69
  35. package/src/validation/ZodAdapter.ts +102 -102
  36. package/dist/Carno.d.js +0 -14
  37. package/dist/Carno.d.mjs +0 -1
  38. package/dist/DefaultRoutes.d.js +0 -13
  39. package/dist/DefaultRoutes.d.mjs +0 -0
  40. package/dist/cache/CacheDriver.d.js +0 -13
  41. package/dist/cache/CacheDriver.d.mjs +0 -0
  42. package/dist/cache/CacheService.d.js +0 -13
  43. package/dist/cache/CacheService.d.mjs +0 -0
  44. package/dist/cache/MemoryDriver.d.js +0 -13
  45. package/dist/cache/MemoryDriver.d.mjs +0 -0
  46. package/dist/cache/RedisDriver.d.js +0 -13
  47. package/dist/cache/RedisDriver.d.mjs +0 -0
  48. package/dist/compiler/JITCompiler.d.js +0 -13
  49. package/dist/compiler/JITCompiler.d.mjs +0 -0
  50. package/dist/container/Container.d.js +0 -13
  51. package/dist/container/Container.d.mjs +0 -0
  52. package/dist/context/Context.d.js +0 -13
  53. package/dist/context/Context.d.mjs +0 -0
  54. package/dist/cors/CorsHandler.d.js +0 -13
  55. package/dist/cors/CorsHandler.d.mjs +0 -0
  56. package/dist/decorators/Controller.d.js +0 -13
  57. package/dist/decorators/Controller.d.mjs +0 -0
  58. package/dist/decorators/Inject.d.js +0 -13
  59. package/dist/decorators/Inject.d.mjs +0 -0
  60. package/dist/decorators/Middleware.d.js +0 -13
  61. package/dist/decorators/Middleware.d.mjs +0 -0
  62. package/dist/decorators/Service.d.js +0 -13
  63. package/dist/decorators/Service.d.mjs +0 -0
  64. package/dist/decorators/methods.d.js +0 -13
  65. package/dist/decorators/methods.d.mjs +0 -0
  66. package/dist/decorators/params.d.js +0 -13
  67. package/dist/decorators/params.d.mjs +0 -0
  68. package/dist/events/Lifecycle.d.js +0 -13
  69. package/dist/events/Lifecycle.d.mjs +0 -0
  70. package/dist/exceptions/HttpException.d.js +0 -13
  71. package/dist/exceptions/HttpException.d.mjs +0 -0
  72. package/dist/index.d.js +0 -130
  73. package/dist/index.d.mjs +0 -78
  74. package/dist/metadata.d.js +0 -13
  75. package/dist/metadata.d.mjs +0 -0
  76. package/dist/middleware/CarnoMiddleware.d.js +0 -13
  77. package/dist/middleware/CarnoMiddleware.d.mjs +0 -0
  78. package/dist/router/RadixRouter.d.js +0 -13
  79. package/dist/router/RadixRouter.d.mjs +0 -0
  80. package/dist/testing/TestHarness.d.js +0 -13
  81. package/dist/testing/TestHarness.d.mjs +0 -0
  82. package/dist/utils/Metadata.d.js +0 -13
  83. package/dist/utils/Metadata.d.mjs +0 -0
  84. package/dist/utils/parseQuery.d.js +0 -13
  85. package/dist/utils/parseQuery.d.mjs +0 -0
  86. package/dist/validation/ValibotAdapter.d.js +0 -13
  87. package/dist/validation/ValibotAdapter.d.mjs +0 -0
  88. package/dist/validation/ValidatorAdapter.d.js +0 -13
  89. package/dist/validation/ValidatorAdapter.d.mjs +0 -0
  90. package/dist/validation/ZodAdapter.d.js +0 -13
  91. package/dist/validation/ZodAdapter.d.mjs +0 -0
  92. package/src/Carno.d.ts +0 -135
  93. package/src/DefaultRoutes.d.ts +0 -19
  94. package/src/cache/CacheDriver.d.ts +0 -43
  95. package/src/cache/CacheService.d.ts +0 -89
  96. package/src/cache/MemoryDriver.d.ts +0 -32
  97. package/src/cache/RedisDriver.d.ts +0 -34
  98. package/src/compiler/JITCompiler.d.ts +0 -36
  99. package/src/container/Container.d.ts +0 -38
  100. package/src/context/Context.d.ts +0 -36
  101. package/src/cors/CorsHandler.d.ts +0 -47
  102. package/src/decorators/Controller.d.ts +0 -13
  103. package/src/decorators/Inject.d.ts +0 -6
  104. package/src/decorators/Middleware.d.ts +0 -5
  105. package/src/decorators/Service.d.ts +0 -9
  106. package/src/decorators/methods.d.ts +0 -7
  107. package/src/decorators/params.d.ts +0 -13
  108. package/src/events/Lifecycle.d.ts +0 -54
  109. package/src/exceptions/HttpException.d.ts +0 -43
  110. package/src/index.d.ts +0 -42
  111. package/src/metadata.d.ts +0 -41
  112. package/src/middleware/CarnoMiddleware.d.ts +0 -12
  113. package/src/router/RadixRouter.d.ts +0 -19
  114. package/src/testing/TestHarness.d.ts +0 -71
  115. package/src/utils/Metadata.d.ts +0 -20
  116. package/src/utils/parseQuery.d.ts +0 -23
  117. package/src/validation/ValibotAdapter.d.ts +0 -30
  118. package/src/validation/ValidatorAdapter.d.ts +0 -54
  119. package/src/validation/ZodAdapter.d.ts +0 -35
@@ -1,185 +1,185 @@
1
- import type { Server } from 'bun';
2
- import { Carno, type CarnoConfig } from '../Carno';
3
- import { Container, type Token } from '../container/Container';
4
-
5
- /**
6
- * Test configuration options.
7
- */
8
- export interface TestOptions {
9
- config?: CarnoConfig;
10
- listen?: boolean | number;
11
- port?: number;
12
- controllers?: (new (...args: any[]) => any)[];
13
- services?: (Token | any)[];
14
- plugins?: Carno[];
15
- }
16
-
17
- /**
18
- * Test harness - provides utilities for testing Carno applications.
19
- */
20
- export interface TestHarness {
21
- /** The Carno app instance */
22
- app: Carno;
23
-
24
- /** The internal DI container */
25
- container: Container;
26
-
27
- /** The HTTP server (if listening) */
28
- server?: Server<any>;
29
-
30
- /** The port the server is running on */
31
- port?: number;
32
-
33
- /** Resolve a service from the container */
34
- resolve<T>(token: Token<T>): T;
35
-
36
- /** Make an HTTP request to the app */
37
- request(path: string, init?: RequestInit): Promise<Response>;
38
-
39
- /** Make a GET request */
40
- get(path: string, init?: Omit<RequestInit, 'method'>): Promise<Response>;
41
-
42
- /** Make a POST request */
43
- post(path: string, body?: any, init?: Omit<RequestInit, 'method' | 'body'>): Promise<Response>;
44
-
45
- /** Make a PUT request */
46
- put(path: string, body?: any, init?: Omit<RequestInit, 'method' | 'body'>): Promise<Response>;
47
-
48
- /** Make a DELETE request */
49
- delete(path: string, init?: Omit<RequestInit, 'method'>): Promise<Response>;
50
-
51
- /** Close the test harness and cleanup */
52
- close(): Promise<void>;
53
- }
54
-
55
- /**
56
- * Create a test harness for Turbo applications.
57
- *
58
- * @example
59
- * ```typescript
60
- * const harness = await createTestHarness({
61
- * controllers: [UserController],
62
- * services: [UserService],
63
- * listen: true
64
- * });
65
- *
66
- * const response = await harness.get('/users');
67
- * expect(response.status).toBe(200);
68
- *
69
- * await harness.close();
70
- * ```
71
- */
72
- export async function createTestHarness(options: TestOptions = {}): Promise<TestHarness> {
73
- const config: CarnoConfig = {
74
- ...options.config,
75
- disableStartupLog: true
76
- };
77
-
78
- const app = new Carno(config);
79
-
80
- // Register plugins
81
- if (options.plugins) {
82
- for (const plugin of options.plugins) {
83
- app.use(plugin);
84
- }
85
- }
86
-
87
- // Register controllers
88
- if (options.controllers) {
89
- app.controllers(options.controllers);
90
- }
91
-
92
- // Register services
93
- if (options.services) {
94
- app.services(options.services);
95
- }
96
-
97
- const port = resolvePort(options);
98
- let server: Server<any> | undefined;
99
-
100
- if (shouldListen(options.listen)) {
101
- app.listen(port);
102
- server = (app as any).server;
103
- }
104
-
105
- const actualPort = server?.port ?? port;
106
- const container = (app as any).container as Container;
107
-
108
- // Pre-bind methods for performance
109
- const baseUrl = `http://127.0.0.1:${actualPort}`;
110
-
111
- const request = async (path: string, init?: RequestInit): Promise<Response> => {
112
- if (!server) {
113
- throw new Error('Server not running. Set listen: true in options.');
114
- }
115
- const url = path.startsWith('http') ? path : `${baseUrl}${path.startsWith('/') ? path : '/' + path}`;
116
- return fetch(url, init);
117
- };
118
-
119
- return {
120
- app,
121
- container,
122
- server,
123
- port: actualPort,
124
-
125
- resolve: <T>(token: Token<T>): T => container.get(token),
126
-
127
- request,
128
-
129
- get: (path, init) => request(path, { ...init, method: 'GET' }),
130
-
131
- post: (path, body, init) => request(path, {
132
- ...init,
133
- method: 'POST',
134
- body: body ? JSON.stringify(body) : undefined,
135
- headers: { 'Content-Type': 'application/json', ...init?.headers }
136
- }),
137
-
138
- put: (path, body, init) => request(path, {
139
- ...init,
140
- method: 'PUT',
141
- body: body ? JSON.stringify(body) : undefined,
142
- headers: { 'Content-Type': 'application/json', ...init?.headers }
143
- }),
144
-
145
- delete: (path, init) => request(path, { ...init, method: 'DELETE' }),
146
-
147
- close: async () => {
148
- app.stop();
149
- }
150
- };
151
- }
152
-
153
- /**
154
- * Run a test routine with automatic harness cleanup.
155
- *
156
- * @example
157
- * ```typescript
158
- * await withTestApp(async (harness) => {
159
- * const response = await harness.get('/health');
160
- * expect(response.status).toBe(200);
161
- * }, { controllers: [HealthController], listen: true });
162
- * ```
163
- */
164
- export async function withTestApp(
165
- routine: (harness: TestHarness) => Promise<void>,
166
- options: TestOptions = {}
167
- ): Promise<void> {
168
- const harness = await createTestHarness(options);
169
-
170
- try {
171
- await routine(harness);
172
- } finally {
173
- await harness.close();
174
- }
175
- }
176
-
177
- function shouldListen(value: TestOptions['listen']): boolean {
178
- return typeof value === 'number' || Boolean(value);
179
- }
180
-
181
- function resolvePort(options: TestOptions): number {
182
- if (typeof options.listen === 'number') return options.listen;
183
- if (typeof options.port === 'number') return options.port;
184
- return 0; // Random port
185
- }
1
+ import type { Server } from 'bun';
2
+ import { Carno, type CarnoConfig } from '../Carno';
3
+ import { Container, type Token } from '../container/Container';
4
+
5
+ /**
6
+ * Test configuration options.
7
+ */
8
+ export interface TestOptions {
9
+ config?: CarnoConfig;
10
+ listen?: boolean | number;
11
+ port?: number;
12
+ controllers?: (new (...args: any[]) => any)[];
13
+ services?: (Token | any)[];
14
+ plugins?: Carno[];
15
+ }
16
+
17
+ /**
18
+ * Test harness - provides utilities for testing Carno applications.
19
+ */
20
+ export interface TestHarness {
21
+ /** The Carno app instance */
22
+ app: Carno;
23
+
24
+ /** The internal DI container */
25
+ container: Container;
26
+
27
+ /** The HTTP server (if listening) */
28
+ server?: Server<any>;
29
+
30
+ /** The port the server is running on */
31
+ port?: number;
32
+
33
+ /** Resolve a service from the container */
34
+ resolve<T>(token: Token<T>): T;
35
+
36
+ /** Make an HTTP request to the app */
37
+ request(path: string, init?: RequestInit): Promise<Response>;
38
+
39
+ /** Make a GET request */
40
+ get(path: string, init?: Omit<RequestInit, 'method'>): Promise<Response>;
41
+
42
+ /** Make a POST request */
43
+ post(path: string, body?: any, init?: Omit<RequestInit, 'method' | 'body'>): Promise<Response>;
44
+
45
+ /** Make a PUT request */
46
+ put(path: string, body?: any, init?: Omit<RequestInit, 'method' | 'body'>): Promise<Response>;
47
+
48
+ /** Make a DELETE request */
49
+ delete(path: string, init?: Omit<RequestInit, 'method'>): Promise<Response>;
50
+
51
+ /** Close the test harness and cleanup */
52
+ close(): Promise<void>;
53
+ }
54
+
55
+ /**
56
+ * Create a test harness for Turbo applications.
57
+ *
58
+ * @example
59
+ * ```typescript
60
+ * const harness = await createTestHarness({
61
+ * controllers: [UserController],
62
+ * services: [UserService],
63
+ * listen: true
64
+ * });
65
+ *
66
+ * const response = await harness.get('/users');
67
+ * expect(response.status).toBe(200);
68
+ *
69
+ * await harness.close();
70
+ * ```
71
+ */
72
+ export async function createTestHarness(options: TestOptions = {}): Promise<TestHarness> {
73
+ const config: CarnoConfig = {
74
+ ...options.config,
75
+ disableStartupLog: true
76
+ };
77
+
78
+ const app = new Carno(config);
79
+
80
+ // Register plugins
81
+ if (options.plugins) {
82
+ for (const plugin of options.plugins) {
83
+ app.use(plugin);
84
+ }
85
+ }
86
+
87
+ // Register controllers
88
+ if (options.controllers) {
89
+ app.controllers(options.controllers);
90
+ }
91
+
92
+ // Register services
93
+ if (options.services) {
94
+ app.services(options.services);
95
+ }
96
+
97
+ const port = resolvePort(options);
98
+ let server: Server<any> | undefined;
99
+
100
+ if (shouldListen(options.listen)) {
101
+ app.listen(port);
102
+ server = (app as any).server;
103
+ }
104
+
105
+ const actualPort = server?.port ?? port;
106
+ const container = (app as any).container as Container;
107
+
108
+ // Pre-bind methods for performance
109
+ const baseUrl = `http://127.0.0.1:${actualPort}`;
110
+
111
+ const request = async (path: string, init?: RequestInit): Promise<Response> => {
112
+ if (!server) {
113
+ throw new Error('Server not running. Set listen: true in options.');
114
+ }
115
+ const url = path.startsWith('http') ? path : `${baseUrl}${path.startsWith('/') ? path : '/' + path}`;
116
+ return fetch(url, init);
117
+ };
118
+
119
+ return {
120
+ app,
121
+ container,
122
+ server,
123
+ port: actualPort,
124
+
125
+ resolve: <T>(token: Token<T>): T => container.get(token),
126
+
127
+ request,
128
+
129
+ get: (path, init) => request(path, { ...init, method: 'GET' }),
130
+
131
+ post: (path, body, init) => request(path, {
132
+ ...init,
133
+ method: 'POST',
134
+ body: body ? JSON.stringify(body) : undefined,
135
+ headers: { 'Content-Type': 'application/json', ...init?.headers }
136
+ }),
137
+
138
+ put: (path, body, init) => request(path, {
139
+ ...init,
140
+ method: 'PUT',
141
+ body: body ? JSON.stringify(body) : undefined,
142
+ headers: { 'Content-Type': 'application/json', ...init?.headers }
143
+ }),
144
+
145
+ delete: (path, init) => request(path, { ...init, method: 'DELETE' }),
146
+
147
+ close: async () => {
148
+ app.stop();
149
+ }
150
+ };
151
+ }
152
+
153
+ /**
154
+ * Run a test routine with automatic harness cleanup.
155
+ *
156
+ * @example
157
+ * ```typescript
158
+ * await withTestApp(async (harness) => {
159
+ * const response = await harness.get('/health');
160
+ * expect(response.status).toBe(200);
161
+ * }, { controllers: [HealthController], listen: true });
162
+ * ```
163
+ */
164
+ export async function withTestApp(
165
+ routine: (harness: TestHarness) => Promise<void>,
166
+ options: TestOptions = {}
167
+ ): Promise<void> {
168
+ const harness = await createTestHarness(options);
169
+
170
+ try {
171
+ await routine(harness);
172
+ } finally {
173
+ await harness.close();
174
+ }
175
+ }
176
+
177
+ function shouldListen(value: TestOptions['listen']): boolean {
178
+ return typeof value === 'number' || Boolean(value);
179
+ }
180
+
181
+ function resolvePort(options: TestOptions): number {
182
+ if (typeof options.listen === 'number') return options.listen;
183
+ if (typeof options.port === 'number') return options.port;
184
+ return 0; // Random port
185
+ }
@@ -1,43 +1,43 @@
1
- /**
2
- * Utility class for handling metadata operations.
3
- * Wraps Reflect.getMetadata and Reflect.defineMetadata.
4
- */
5
- export class Metadata {
6
- static get<T = any>(key: string | symbol, target: any): T | undefined {
7
- return Reflect.getMetadata(key, target);
8
- }
9
-
10
- static set(key: string | symbol, value: any, target: any): void {
11
- Reflect.defineMetadata(key, value, target);
12
- }
13
-
14
- static has(key: string | symbol, target: any): boolean {
15
- return Reflect.hasMetadata(key, target);
16
- }
17
-
18
- static delete(key: string | symbol, target: any): boolean {
19
- return Reflect.deleteMetadata(key, target);
20
- }
21
-
22
- static keys(target: any): (string | symbol)[] {
23
- return Reflect.getMetadataKeys(target);
24
- }
25
-
26
- static getType(target: any, propertyKey: string | symbol): any {
27
- return Reflect.getMetadata('design:type', target, propertyKey);
28
- }
29
- }
30
-
31
- /**
32
- * Type guard for checking if value is an object.
33
- */
34
- export function isObject(value: unknown): value is Record<string, any> {
35
- return typeof value === 'object' && value !== null && !Array.isArray(value);
36
- }
37
-
38
- /**
39
- * Type guard for checking if value is a string.
40
- */
41
- export function isString(value: unknown): value is string {
42
- return typeof value === 'string';
43
- }
1
+ /**
2
+ * Utility class for handling metadata operations.
3
+ * Wraps Reflect.getMetadata and Reflect.defineMetadata.
4
+ */
5
+ export class Metadata {
6
+ static get<T = any>(key: string | symbol, target: any): T | undefined {
7
+ return Reflect.getMetadata(key, target);
8
+ }
9
+
10
+ static set(key: string | symbol, value: any, target: any): void {
11
+ Reflect.defineMetadata(key, value, target);
12
+ }
13
+
14
+ static has(key: string | symbol, target: any): boolean {
15
+ return Reflect.hasMetadata(key, target);
16
+ }
17
+
18
+ static delete(key: string | symbol, target: any): boolean {
19
+ return Reflect.deleteMetadata(key, target);
20
+ }
21
+
22
+ static keys(target: any): (string | symbol)[] {
23
+ return Reflect.getMetadataKeys(target);
24
+ }
25
+
26
+ static getType(target: any, propertyKey: string | symbol): any {
27
+ return Reflect.getMetadata('design:type', target, propertyKey);
28
+ }
29
+ }
30
+
31
+ /**
32
+ * Type guard for checking if value is an object.
33
+ */
34
+ export function isObject(value: unknown): value is Record<string, any> {
35
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
36
+ }
37
+
38
+ /**
39
+ * Type guard for checking if value is a string.
40
+ */
41
+ export function isString(value: unknown): value is string {
42
+ return typeof value === 'string';
43
+ }