adorn-api 1.1.11 → 1.1.13
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 +18 -0
- package/dist/adapter/express/types.d.ts +3 -46
- package/dist/adapter/fastify/coercion.d.ts +12 -0
- package/dist/adapter/fastify/coercion.js +289 -0
- package/dist/adapter/fastify/controllers.d.ts +7 -0
- package/dist/adapter/fastify/controllers.js +201 -0
- package/dist/adapter/fastify/index.d.ts +14 -0
- package/dist/adapter/fastify/index.js +67 -0
- package/dist/adapter/fastify/multipart.d.ts +26 -0
- package/dist/adapter/fastify/multipart.js +75 -0
- package/dist/adapter/fastify/openapi.d.ts +10 -0
- package/dist/adapter/fastify/openapi.js +76 -0
- package/dist/adapter/fastify/response-serializer.d.ts +2 -0
- package/dist/adapter/fastify/response-serializer.js +162 -0
- package/dist/adapter/fastify/types.d.ts +100 -0
- package/dist/adapter/fastify/types.js +2 -0
- package/dist/adapter/metal-orm/index.d.ts +1 -1
- package/dist/adapter/metal-orm/types.d.ts +23 -0
- package/dist/adapter/native/coercion.d.ts +12 -0
- package/dist/adapter/native/coercion.js +289 -0
- package/dist/adapter/native/controllers.d.ts +17 -0
- package/dist/adapter/native/controllers.js +215 -0
- package/dist/adapter/native/index.d.ts +14 -0
- package/dist/adapter/native/index.js +127 -0
- package/dist/adapter/native/openapi.d.ts +7 -0
- package/dist/adapter/native/openapi.js +82 -0
- package/dist/adapter/native/response-serializer.d.ts +5 -0
- package/dist/adapter/native/response-serializer.js +160 -0
- package/dist/adapter/native/router.d.ts +25 -0
- package/dist/adapter/native/router.js +68 -0
- package/dist/adapter/native/types.d.ts +77 -0
- package/dist/adapter/native/types.js +2 -0
- package/dist/core/auth.d.ts +11 -12
- package/dist/core/auth.js +2 -2
- package/dist/core/logger.d.ts +3 -4
- package/dist/core/logger.js +2 -2
- package/dist/core/streaming.d.ts +10 -10
- package/dist/core/streaming.js +31 -19
- package/dist/core/types.d.ts +102 -0
- package/dist/index.d.ts +6 -1
- package/dist/index.js +16 -1
- package/examples/fastify/app.ts +16 -0
- package/examples/fastify/index.ts +21 -0
- package/package.json +24 -18
- package/src/adapter/express/controllers.ts +249 -249
- package/src/adapter/express/types.ts +121 -160
- package/src/adapter/fastify/coercion.ts +369 -0
- package/src/adapter/fastify/controllers.ts +255 -0
- package/src/adapter/fastify/index.ts +53 -0
- package/src/adapter/fastify/multipart.ts +94 -0
- package/src/adapter/fastify/openapi.ts +93 -0
- package/src/adapter/fastify/response-serializer.ts +179 -0
- package/src/adapter/fastify/types.ts +119 -0
- package/src/adapter/metal-orm/index.ts +3 -0
- package/src/adapter/metal-orm/types.ts +25 -0
- package/src/adapter/native/coercion.ts +369 -0
- package/src/adapter/native/controllers.ts +271 -0
- package/src/adapter/native/index.ts +116 -0
- package/src/adapter/native/openapi.ts +109 -0
- package/src/adapter/native/response-serializer.ts +177 -0
- package/src/adapter/native/router.ts +90 -0
- package/src/adapter/native/types.ts +96 -0
- package/src/core/auth.ts +314 -315
- package/src/core/health.ts +234 -235
- package/src/core/logger.ts +245 -247
- package/src/core/streaming.ts +342 -330
- package/src/core/types.ts +115 -0
- package/src/index.ts +46 -16
- package/tests/e2e/fastify.e2e.test.ts +174 -0
- package/tests/native.test.ts +191 -0
- package/tests/typecheck/query-params.typecheck.ts +42 -0
- package/tests/unit/openapi-parameters.test.ts +97 -97
- package/tsconfig.json +14 -13
- package/tsconfig.typecheck.json +8 -0
- package/vitest.config.ts +47 -7
package/src/core/health.ts
CHANGED
|
@@ -1,235 +1,234 @@
|
|
|
1
|
-
import { Dto, Field, Controller, Get, Returns, Doc } from "./decorators";
|
|
2
|
-
import { t } from "./schema";
|
|
3
|
-
import type { Constructor } from "./types";
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
*
|
|
104
|
-
* @
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
const
|
|
109
|
-
const
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
@
|
|
115
|
-
@
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
const
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
@
|
|
147
|
-
@
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
@
|
|
154
|
-
@
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
*
|
|
166
|
-
* @param
|
|
167
|
-
* @
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
*
|
|
192
|
-
* @
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
const
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
const
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
*
|
|
229
|
-
* @param
|
|
230
|
-
* @
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
}
|
|
1
|
+
import { Dto, Field, Controller, Get, Returns, Doc } from "./decorators";
|
|
2
|
+
import { t } from "./schema";
|
|
3
|
+
import type { Constructor, RequestContext } from "./types";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Health status values.
|
|
7
|
+
*/
|
|
8
|
+
export type HealthStatus = "healthy" | "degraded" | "unhealthy";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Result of a single health indicator check.
|
|
12
|
+
*/
|
|
13
|
+
export interface HealthIndicatorResult {
|
|
14
|
+
/** Status of this indicator */
|
|
15
|
+
status: HealthStatus;
|
|
16
|
+
/** Optional message providing details */
|
|
17
|
+
message?: string;
|
|
18
|
+
/** Optional additional data */
|
|
19
|
+
data?: Record<string, unknown>;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Function that performs a health check.
|
|
24
|
+
*/
|
|
25
|
+
export type HealthIndicator = () => HealthIndicatorResult | Promise<HealthIndicatorResult>;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Named health indicator with a key and check function.
|
|
29
|
+
*/
|
|
30
|
+
export interface NamedHealthIndicator {
|
|
31
|
+
/** Unique key identifying this indicator */
|
|
32
|
+
key: string;
|
|
33
|
+
/** The health check function */
|
|
34
|
+
check: HealthIndicator;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Options for creating a health controller.
|
|
39
|
+
*/
|
|
40
|
+
export interface HealthControllerOptions {
|
|
41
|
+
/** Base path for health endpoints (default: "/health") */
|
|
42
|
+
path?: string;
|
|
43
|
+
/** Array of health indicators to check */
|
|
44
|
+
indicators?: NamedHealthIndicator[];
|
|
45
|
+
/** Tags for OpenAPI documentation */
|
|
46
|
+
tags?: string[];
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* DTO for individual component health status.
|
|
51
|
+
*/
|
|
52
|
+
@Dto({ description: "Health status of a single component" })
|
|
53
|
+
export class HealthComponentDto {
|
|
54
|
+
@Field(t.enum(["healthy", "degraded", "unhealthy"], { description: "Component status" }))
|
|
55
|
+
status!: HealthStatus;
|
|
56
|
+
|
|
57
|
+
@Field(t.optional(t.string({ description: "Additional status message" })))
|
|
58
|
+
message?: string;
|
|
59
|
+
|
|
60
|
+
@Field(t.optional(t.record(t.any(), { description: "Additional data" })))
|
|
61
|
+
data?: Record<string, unknown>;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* DTO for overall health check response.
|
|
66
|
+
*/
|
|
67
|
+
@Dto({ description: "Overall health check response" })
|
|
68
|
+
export class HealthResponseDto {
|
|
69
|
+
@Field(t.enum(["healthy", "degraded", "unhealthy"], { description: "Overall status" }))
|
|
70
|
+
status!: HealthStatus;
|
|
71
|
+
|
|
72
|
+
@Field(t.optional(t.record(t.ref(HealthComponentDto), { description: "Component statuses" })))
|
|
73
|
+
components?: Record<string, HealthComponentDto>;
|
|
74
|
+
|
|
75
|
+
@Field(t.string({ description: "Timestamp of the health check" }))
|
|
76
|
+
timestamp!: string;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* DTO for simple liveness probe response.
|
|
81
|
+
*/
|
|
82
|
+
@Dto({ description: "Simple liveness probe response" })
|
|
83
|
+
export class LivenessDto {
|
|
84
|
+
@Field(t.literal("ok", { description: "Liveness status" }))
|
|
85
|
+
status!: "ok";
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Determines the overall status from component statuses.
|
|
90
|
+
*/
|
|
91
|
+
function aggregateStatus(statuses: HealthStatus[]): HealthStatus {
|
|
92
|
+
if (statuses.some((s) => s === "unhealthy")) {
|
|
93
|
+
return "unhealthy";
|
|
94
|
+
}
|
|
95
|
+
if (statuses.some((s) => s === "degraded")) {
|
|
96
|
+
return "degraded";
|
|
97
|
+
}
|
|
98
|
+
return "healthy";
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Creates a health check controller with the specified options.
|
|
103
|
+
* @param options - Health controller configuration
|
|
104
|
+
* @returns Controller class for health endpoints
|
|
105
|
+
*/
|
|
106
|
+
export function createHealthController(options: HealthControllerOptions = {}): Constructor {
|
|
107
|
+
const basePath = options.path ?? "/health";
|
|
108
|
+
const indicators = options.indicators ?? [];
|
|
109
|
+
const tags = options.tags ?? ["Health"];
|
|
110
|
+
|
|
111
|
+
@Controller({ path: basePath, tags })
|
|
112
|
+
class HealthController {
|
|
113
|
+
@Get("/")
|
|
114
|
+
@Doc({ summary: "Health check", description: "Returns the health status of the application and its components" })
|
|
115
|
+
@Returns(HealthResponseDto)
|
|
116
|
+
async check(_ctx: RequestContext): Promise<HealthResponseDto> {
|
|
117
|
+
const components: Record<string, HealthComponentDto> = {};
|
|
118
|
+
const statuses: HealthStatus[] = [];
|
|
119
|
+
|
|
120
|
+
for (const indicator of indicators) {
|
|
121
|
+
try {
|
|
122
|
+
const result = await indicator.check();
|
|
123
|
+
components[indicator.key] = {
|
|
124
|
+
status: result.status,
|
|
125
|
+
message: result.message,
|
|
126
|
+
data: result.data
|
|
127
|
+
};
|
|
128
|
+
statuses.push(result.status);
|
|
129
|
+
} catch (error) {
|
|
130
|
+
components[indicator.key] = {
|
|
131
|
+
status: "unhealthy",
|
|
132
|
+
message: error instanceof Error ? error.message : "Unknown error"
|
|
133
|
+
};
|
|
134
|
+
statuses.push("unhealthy");
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return {
|
|
139
|
+
status: statuses.length > 0 ? aggregateStatus(statuses) : "healthy",
|
|
140
|
+
components: Object.keys(components).length > 0 ? components : undefined,
|
|
141
|
+
timestamp: new Date().toISOString()
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
@Get("/live")
|
|
146
|
+
@Doc({ summary: "Liveness probe", description: "Simple liveness check for container orchestration" })
|
|
147
|
+
@Returns(LivenessDto)
|
|
148
|
+
live(_ctx: RequestContext): LivenessDto {
|
|
149
|
+
return { status: "ok" };
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
@Get("/ready")
|
|
153
|
+
@Doc({ summary: "Readiness probe", description: "Checks if application is ready to receive traffic" })
|
|
154
|
+
@Returns(HealthResponseDto)
|
|
155
|
+
async ready(_ctx: RequestContext): Promise<HealthResponseDto> {
|
|
156
|
+
return this.check(_ctx);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
return HealthController;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Creates a database health indicator.
|
|
165
|
+
* @param name - Name of the database
|
|
166
|
+
* @param pingFn - Function to ping the database (should throw on failure)
|
|
167
|
+
* @returns Named health indicator
|
|
168
|
+
*/
|
|
169
|
+
export function databaseIndicator(
|
|
170
|
+
name: string,
|
|
171
|
+
pingFn: () => Promise<void> | void
|
|
172
|
+
): NamedHealthIndicator {
|
|
173
|
+
return {
|
|
174
|
+
key: name,
|
|
175
|
+
check: async () => {
|
|
176
|
+
try {
|
|
177
|
+
await pingFn();
|
|
178
|
+
return { status: "healthy" };
|
|
179
|
+
} catch (error) {
|
|
180
|
+
return {
|
|
181
|
+
status: "unhealthy",
|
|
182
|
+
message: error instanceof Error ? error.message : "Database unreachable"
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Creates a memory usage health indicator.
|
|
191
|
+
* @param thresholds - Memory thresholds in MB
|
|
192
|
+
* @returns Named health indicator
|
|
193
|
+
*/
|
|
194
|
+
export function memoryIndicator(thresholds?: {
|
|
195
|
+
degradedMB?: number;
|
|
196
|
+
unhealthyMB?: number;
|
|
197
|
+
}): NamedHealthIndicator {
|
|
198
|
+
const degradedMB = thresholds?.degradedMB ?? 512;
|
|
199
|
+
const unhealthyMB = thresholds?.unhealthyMB ?? 1024;
|
|
200
|
+
|
|
201
|
+
return {
|
|
202
|
+
key: "memory",
|
|
203
|
+
check: () => {
|
|
204
|
+
const usage = process.memoryUsage();
|
|
205
|
+
const heapUsedMB = Math.round(usage.heapUsed / 1024 / 1024);
|
|
206
|
+
|
|
207
|
+
let status: HealthStatus = "healthy";
|
|
208
|
+
if (heapUsedMB >= unhealthyMB) {
|
|
209
|
+
status = "unhealthy";
|
|
210
|
+
} else if (heapUsedMB >= degradedMB) {
|
|
211
|
+
status = "degraded";
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
return {
|
|
215
|
+
status,
|
|
216
|
+
data: {
|
|
217
|
+
heapUsedMB,
|
|
218
|
+
heapTotalMB: Math.round(usage.heapTotal / 1024 / 1024),
|
|
219
|
+
rssMB: Math.round(usage.rss / 1024 / 1024)
|
|
220
|
+
}
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Creates a custom health indicator.
|
|
228
|
+
* @param key - Unique identifier for this indicator
|
|
229
|
+
* @param check - Health check function
|
|
230
|
+
* @returns Named health indicator
|
|
231
|
+
*/
|
|
232
|
+
export function customIndicator(key: string, check: HealthIndicator): NamedHealthIndicator {
|
|
233
|
+
return { key, check };
|
|
234
|
+
}
|