azurajs 3.0.2 → 3.0.4
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 +32 -0
- package/dist/IpResolver-BVgnGnpf.d.mts +5 -0
- package/dist/IpResolver-BVgnGnpf.d.ts +5 -0
- package/dist/SwaggerPlugin-C0UZTjaZ.d.ts +6 -0
- package/dist/SwaggerPlugin-wr9S4SRG.d.mts +6 -0
- package/dist/cookies/index.d.mts +7 -0
- package/dist/cookies/index.d.ts +7 -0
- package/dist/cookies/index.js +38 -0
- package/dist/cookies/index.js.map +1 -0
- package/dist/cookies/index.mjs +35 -0
- package/dist/cookies/index.mjs.map +1 -0
- package/dist/core/index.d.mts +18 -27
- package/dist/core/index.d.ts +18 -27
- package/dist/core/index.js +214 -14
- package/dist/core/index.js.map +1 -1
- package/dist/core/index.mjs +214 -14
- package/dist/core/index.mjs.map +1 -1
- package/dist/cors/index.d.mts +7 -0
- package/dist/cors/index.d.ts +7 -0
- package/dist/cors/index.js +52 -0
- package/dist/cors/index.js.map +1 -0
- package/dist/cors/index.mjs +50 -0
- package/dist/cors/index.mjs.map +1 -0
- package/dist/decorators/index.d.mts +2 -0
- package/dist/decorators/index.d.ts +2 -0
- package/dist/decorators/index.js +25 -0
- package/dist/decorators/index.js.map +1 -1
- package/dist/decorators/index.mjs +24 -1
- package/dist/decorators/index.mjs.map +1 -1
- package/dist/decorators-B6l3CbxC.d.ts +13 -0
- package/dist/decorators-D5nY109r.d.mts +13 -0
- package/dist/http-error/index.d.mts +18 -0
- package/dist/http-error/index.d.ts +18 -0
- package/dist/http-error/index.js +81 -0
- package/dist/http-error/index.js.map +1 -0
- package/dist/http-error/index.mjs +79 -0
- package/dist/http-error/index.mjs.map +1 -0
- package/dist/index-j6QGMhZU.d.mts +30 -0
- package/dist/index-tpPZS_UK.d.ts +30 -0
- package/dist/index.d.mts +16 -5
- package/dist/index.d.ts +16 -5
- package/dist/index.js +1178 -14
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1176 -15
- package/dist/index.mjs.map +1 -1
- package/dist/infra/index.d.mts +6 -0
- package/dist/infra/index.d.ts +6 -0
- package/dist/infra/index.js +162 -0
- package/dist/infra/index.js.map +1 -0
- package/dist/infra/index.mjs +159 -0
- package/dist/infra/index.mjs.map +1 -0
- package/dist/{Logger-iEQNVVSc.d.mts → logger/index.d.mts} +2 -1
- package/dist/{Logger-iEQNVVSc.d.ts → logger/index.d.ts} +2 -1
- package/dist/logger/index.js +123 -0
- package/dist/logger/index.js.map +1 -0
- package/dist/logger/index.mjs +120 -0
- package/dist/logger/index.mjs.map +1 -0
- package/dist/plugins/index.d.mts +8 -6
- package/dist/plugins/index.d.ts +8 -6
- package/dist/plugins/index.js +1101 -0
- package/dist/plugins/index.js.map +1 -1
- package/dist/plugins/index.mjs +1101 -1
- package/dist/plugins/index.mjs.map +1 -1
- package/dist/rate-limit/index.d.mts +7 -0
- package/dist/rate-limit/index.d.ts +7 -0
- package/dist/rate-limit/index.js +81 -0
- package/dist/rate-limit/index.js.map +1 -0
- package/dist/rate-limit/index.mjs +79 -0
- package/dist/rate-limit/index.mjs.map +1 -0
- package/dist/router/index.d.mts +4 -0
- package/dist/router/index.d.ts +4 -0
- package/dist/router/index.js +218 -0
- package/dist/router/index.js.map +1 -0
- package/dist/router/index.mjs +216 -0
- package/dist/router/index.mjs.map +1 -0
- package/dist/routes.type-DZO5VBW2.d.mts +58 -0
- package/dist/routes.type-DzHNkCag.d.ts +58 -0
- package/dist/swagger/index.d.mts +30 -0
- package/dist/swagger/index.d.ts +30 -0
- package/dist/swagger/index.js +1136 -0
- package/dist/swagger/index.js.map +1 -0
- package/dist/swagger/index.mjs +1126 -0
- package/dist/swagger/index.mjs.map +1 -0
- package/dist/swagger/swagger-ui-modern.html +894 -0
- package/dist/swagger.type-Bfn5nGR8.d.mts +42 -0
- package/dist/swagger.type-CfDbFCZC.d.ts +42 -0
- package/dist/types/index.d.mts +5 -58
- package/dist/types/index.d.ts +5 -58
- package/dist/utils/index.d.mts +7 -72
- package/dist/utils/index.d.ts +7 -72
- package/dist/validators/index.d.mts +48 -0
- package/dist/validators/index.d.ts +48 -0
- package/dist/validators/index.js +144 -0
- package/dist/validators/index.js.map +1 -0
- package/dist/validators/index.mjs +141 -0
- package/dist/validators/index.mjs.map +1 -0
- package/package.json +86 -2
- package/src/cookies/index.ts +1 -0
- package/src/core/index.ts +1 -0
- package/src/core/router.ts +26 -15
- package/src/core/server.ts +64 -14
- package/src/cors/index.ts +2 -0
- package/src/decorators/index.ts +2 -0
- package/src/http-error/index.ts +1 -0
- package/src/infra/index.ts +3 -0
- package/src/logger/index.ts +1 -0
- package/src/plugins/SwaggerPlugin.ts +45 -0
- package/src/plugins/index.ts +1 -0
- package/src/rate-limit/index.ts +2 -0
- package/src/router/index.ts +1 -0
- package/src/swagger/constants.ts +8 -0
- package/src/swagger/decorators.ts +35 -0
- package/src/swagger/index.ts +10 -0
- package/src/swagger/openapi-builder.ts +199 -0
- package/src/swagger/swagger-ui-html.ts +24 -0
- package/src/swagger/swagger-ui-modern.html +894 -0
- package/src/swagger/swagger-ui-template.ts +5 -0
- package/src/types/index.ts +7 -0
- package/src/types/swagger.type.ts +36 -0
- package/src/validators/index.ts +4 -0
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
// src/utils/validators/DTOValidator.ts
|
|
2
|
+
var DTOValidator = class {
|
|
3
|
+
static validate(data, schema) {
|
|
4
|
+
const errors = [];
|
|
5
|
+
if (data == null || typeof data !== "object") {
|
|
6
|
+
return {
|
|
7
|
+
valid: false,
|
|
8
|
+
errors: [{ field: "_root", message: "Request body must be an object" }]
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
for (const [field, rule] of Object.entries(schema)) {
|
|
12
|
+
const value = data[field];
|
|
13
|
+
if (rule.required && (value === void 0 || value === null || value === "")) {
|
|
14
|
+
errors.push({
|
|
15
|
+
field,
|
|
16
|
+
message: rule.message ?? `${field} is required`,
|
|
17
|
+
value
|
|
18
|
+
});
|
|
19
|
+
continue;
|
|
20
|
+
}
|
|
21
|
+
if (value === void 0 || value === null) continue;
|
|
22
|
+
if (rule.type) {
|
|
23
|
+
const actualType = Array.isArray(value) ? "array" : typeof value;
|
|
24
|
+
if (actualType !== rule.type) {
|
|
25
|
+
errors.push({
|
|
26
|
+
field,
|
|
27
|
+
message: rule.message ?? `${field} must be of type ${rule.type}`,
|
|
28
|
+
value
|
|
29
|
+
});
|
|
30
|
+
continue;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
if (rule.type === "string" || typeof value === "string") {
|
|
34
|
+
if (rule.min !== void 0 && value.length < rule.min) {
|
|
35
|
+
errors.push({
|
|
36
|
+
field,
|
|
37
|
+
message: rule.message ?? `${field} must be at least ${rule.min} characters`,
|
|
38
|
+
value
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
if (rule.max !== void 0 && value.length > rule.max) {
|
|
42
|
+
errors.push({
|
|
43
|
+
field,
|
|
44
|
+
message: rule.message ?? `${field} must be at most ${rule.max} characters`,
|
|
45
|
+
value
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
if (rule.pattern && !rule.pattern.test(value)) {
|
|
49
|
+
errors.push({
|
|
50
|
+
field,
|
|
51
|
+
message: rule.message ?? `${field} has invalid format`,
|
|
52
|
+
value
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
if (rule.type === "number" || typeof value === "number") {
|
|
57
|
+
if (rule.min !== void 0 && value < rule.min) {
|
|
58
|
+
errors.push({
|
|
59
|
+
field,
|
|
60
|
+
message: rule.message ?? `${field} must be at least ${rule.min}`,
|
|
61
|
+
value
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
if (rule.max !== void 0 && value > rule.max) {
|
|
65
|
+
errors.push({
|
|
66
|
+
field,
|
|
67
|
+
message: rule.message ?? `${field} must be at most ${rule.max}`,
|
|
68
|
+
value
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
if (rule.type === "array" && Array.isArray(value)) {
|
|
73
|
+
if (rule.min !== void 0 && value.length < rule.min) {
|
|
74
|
+
errors.push({
|
|
75
|
+
field,
|
|
76
|
+
message: rule.message ?? `${field} must have at least ${rule.min} items`,
|
|
77
|
+
value
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
if (rule.max !== void 0 && value.length > rule.max) {
|
|
81
|
+
errors.push({
|
|
82
|
+
field,
|
|
83
|
+
message: rule.message ?? `${field} must have at most ${rule.max} items`,
|
|
84
|
+
value
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
if (rule.enum && !rule.enum.includes(value)) {
|
|
89
|
+
errors.push({
|
|
90
|
+
field,
|
|
91
|
+
message: rule.message ?? `${field} must be one of: ${rule.enum.join(", ")}`,
|
|
92
|
+
value
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
if (rule.custom) {
|
|
96
|
+
const customError = rule.custom(value);
|
|
97
|
+
if (customError) {
|
|
98
|
+
errors.push({ field, message: customError, value });
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return {
|
|
103
|
+
valid: errors.length === 0,
|
|
104
|
+
errors,
|
|
105
|
+
data: errors.length === 0 ? data : void 0
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
// src/utils/validators/SchemaValidator.ts
|
|
111
|
+
var SchemaValidator = class {
|
|
112
|
+
static validate(data, schema) {
|
|
113
|
+
if (typeof schema.safeParse === "function") {
|
|
114
|
+
const result = schema.safeParse(data);
|
|
115
|
+
if (result.success) {
|
|
116
|
+
return { valid: true, data: result.data };
|
|
117
|
+
}
|
|
118
|
+
return { valid: false, errors: result.error };
|
|
119
|
+
}
|
|
120
|
+
if (typeof schema.parse === "function") {
|
|
121
|
+
try {
|
|
122
|
+
const parsed = schema.parse(data);
|
|
123
|
+
return { valid: true, data: parsed };
|
|
124
|
+
} catch (err) {
|
|
125
|
+
return { valid: false, errors: err };
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
if (typeof schema.validate === "function") {
|
|
129
|
+
const result = schema.validate(data);
|
|
130
|
+
if (result.error) {
|
|
131
|
+
return { valid: false, errors: result.error };
|
|
132
|
+
}
|
|
133
|
+
return { valid: true, data: result.value };
|
|
134
|
+
}
|
|
135
|
+
return { valid: true, data };
|
|
136
|
+
}
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
export { DTOValidator, SchemaValidator };
|
|
140
|
+
//# sourceMappingURL=index.mjs.map
|
|
141
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/utils/validators/DTOValidator.ts","../../src/utils/validators/SchemaValidator.ts"],"names":[],"mappings":";AA2BO,IAAM,eAAN,MAAmB;AAAA,EACxB,OAAO,QAAA,CAAS,IAAA,EAAW,MAAA,EAAkC;AAC3D,IAAA,MAAM,SAA4B,EAAC;AAEnC,IAAA,IAAI,IAAA,IAAQ,IAAA,IAAQ,OAAO,IAAA,KAAS,QAAA,EAAU;AAC5C,MAAA,OAAO;AAAA,QACL,KAAA,EAAO,KAAA;AAAA,QACP,QAAQ,CAAC,EAAE,OAAO,OAAA,EAAS,OAAA,EAAS,kCAAkC;AAAA,OACxE;AAAA,IACF;AAEA,IAAA,KAAA,MAAW,CAAC,KAAA,EAAO,IAAI,KAAK,MAAA,CAAO,OAAA,CAAQ,MAAM,CAAA,EAAG;AAClD,MAAA,MAAM,KAAA,GAAQ,KAAK,KAAK,CAAA;AAExB,MAAA,IAAI,KAAK,QAAA,KAAa,KAAA,KAAU,UAAa,KAAA,KAAU,IAAA,IAAQ,UAAU,EAAA,CAAA,EAAK;AAC5E,QAAA,MAAA,CAAO,IAAA,CAAK;AAAA,UACV,KAAA;AAAA,UACA,OAAA,EAAS,IAAA,CAAK,OAAA,IAAW,CAAA,EAAG,KAAK,CAAA,YAAA,CAAA;AAAA,UACjC;AAAA,SACD,CAAA;AACD,QAAA;AAAA,MACF;AAEA,MAAA,IAAI,KAAA,KAAU,MAAA,IAAa,KAAA,KAAU,IAAA,EAAM;AAE3C,MAAA,IAAI,KAAK,IAAA,EAAM;AACb,QAAA,MAAM,aAAa,KAAA,CAAM,OAAA,CAAQ,KAAK,CAAA,GAAI,UAAU,OAAO,KAAA;AAC3D,QAAA,IAAI,UAAA,KAAe,KAAK,IAAA,EAAM;AAC5B,UAAA,MAAA,CAAO,IAAA,CAAK;AAAA,YACV,KAAA;AAAA,YACA,SAAS,IAAA,CAAK,OAAA,IAAW,GAAG,KAAK,CAAA,iBAAA,EAAoB,KAAK,IAAI,CAAA,CAAA;AAAA,YAC9D;AAAA,WACD,CAAA;AACD,UAAA;AAAA,QACF;AAAA,MACF;AAEA,MAAA,IAAI,IAAA,CAAK,IAAA,KAAS,QAAA,IAAY,OAAO,UAAU,QAAA,EAAU;AACvD,QAAA,IAAI,KAAK,GAAA,KAAQ,MAAA,IAAa,KAAA,CAAM,MAAA,GAAS,KAAK,GAAA,EAAK;AACrD,UAAA,MAAA,CAAO,IAAA,CAAK;AAAA,YACV,KAAA;AAAA,YACA,SAAS,IAAA,CAAK,OAAA,IAAW,GAAG,KAAK,CAAA,kBAAA,EAAqB,KAAK,GAAG,CAAA,WAAA,CAAA;AAAA,YAC9D;AAAA,WACD,CAAA;AAAA,QACH;AACA,QAAA,IAAI,KAAK,GAAA,KAAQ,MAAA,IAAa,KAAA,CAAM,MAAA,GAAS,KAAK,GAAA,EAAK;AACrD,UAAA,MAAA,CAAO,IAAA,CAAK;AAAA,YACV,KAAA;AAAA,YACA,SAAS,IAAA,CAAK,OAAA,IAAW,GAAG,KAAK,CAAA,iBAAA,EAAoB,KAAK,GAAG,CAAA,WAAA,CAAA;AAAA,YAC7D;AAAA,WACD,CAAA;AAAA,QACH;AACA,QAAA,IAAI,KAAK,OAAA,IAAW,CAAC,KAAK,OAAA,CAAQ,IAAA,CAAK,KAAK,CAAA,EAAG;AAC7C,UAAA,MAAA,CAAO,IAAA,CAAK;AAAA,YACV,KAAA;AAAA,YACA,OAAA,EAAS,IAAA,CAAK,OAAA,IAAW,CAAA,EAAG,KAAK,CAAA,mBAAA,CAAA;AAAA,YACjC;AAAA,WACD,CAAA;AAAA,QACH;AAAA,MACF;AAEA,MAAA,IAAI,IAAA,CAAK,IAAA,KAAS,QAAA,IAAY,OAAO,UAAU,QAAA,EAAU;AACvD,QAAA,IAAI,IAAA,CAAK,GAAA,KAAQ,MAAA,IAAa,KAAA,GAAQ,KAAK,GAAA,EAAK;AAC9C,UAAA,MAAA,CAAO,IAAA,CAAK;AAAA,YACV,KAAA;AAAA,YACA,SAAS,IAAA,CAAK,OAAA,IAAW,GAAG,KAAK,CAAA,kBAAA,EAAqB,KAAK,GAAG,CAAA,CAAA;AAAA,YAC9D;AAAA,WACD,CAAA;AAAA,QACH;AACA,QAAA,IAAI,IAAA,CAAK,GAAA,KAAQ,MAAA,IAAa,KAAA,GAAQ,KAAK,GAAA,EAAK;AAC9C,UAAA,MAAA,CAAO,IAAA,CAAK;AAAA,YACV,KAAA;AAAA,YACA,SAAS,IAAA,CAAK,OAAA,IAAW,GAAG,KAAK,CAAA,iBAAA,EAAoB,KAAK,GAAG,CAAA,CAAA;AAAA,YAC7D;AAAA,WACD,CAAA;AAAA,QACH;AAAA,MACF;AAEA,MAAA,IAAI,KAAK,IAAA,KAAS,OAAA,IAAW,KAAA,CAAM,OAAA,CAAQ,KAAK,CAAA,EAAG;AACjD,QAAA,IAAI,KAAK,GAAA,KAAQ,MAAA,IAAa,KAAA,CAAM,MAAA,GAAS,KAAK,GAAA,EAAK;AACrD,UAAA,MAAA,CAAO,IAAA,CAAK;AAAA,YACV,KAAA;AAAA,YACA,SAAS,IAAA,CAAK,OAAA,IAAW,GAAG,KAAK,CAAA,oBAAA,EAAuB,KAAK,GAAG,CAAA,MAAA,CAAA;AAAA,YAChE;AAAA,WACD,CAAA;AAAA,QACH;AACA,QAAA,IAAI,KAAK,GAAA,KAAQ,MAAA,IAAa,KAAA,CAAM,MAAA,GAAS,KAAK,GAAA,EAAK;AACrD,UAAA,MAAA,CAAO,IAAA,CAAK;AAAA,YACV,KAAA;AAAA,YACA,SAAS,IAAA,CAAK,OAAA,IAAW,GAAG,KAAK,CAAA,mBAAA,EAAsB,KAAK,GAAG,CAAA,MAAA,CAAA;AAAA,YAC/D;AAAA,WACD,CAAA;AAAA,QACH;AAAA,MACF;AAEA,MAAA,IAAI,KAAK,IAAA,IAAQ,CAAC,KAAK,IAAA,CAAK,QAAA,CAAS,KAAK,CAAA,EAAG;AAC3C,QAAA,MAAA,CAAO,IAAA,CAAK;AAAA,UACV,KAAA;AAAA,UACA,OAAA,EAAS,IAAA,CAAK,OAAA,IAAW,CAAA,EAAG,KAAK,oBAAoB,IAAA,CAAK,IAAA,CAAK,IAAA,CAAK,IAAI,CAAC,CAAA,CAAA;AAAA,UACzE;AAAA,SACD,CAAA;AAAA,MACH;AAEA,MAAA,IAAI,KAAK,MAAA,EAAQ;AACf,QAAA,MAAM,WAAA,GAAc,IAAA,CAAK,MAAA,CAAO,KAAK,CAAA;AACrC,QAAA,IAAI,WAAA,EAAa;AACf,UAAA,MAAA,CAAO,KAAK,EAAE,KAAA,EAAO,OAAA,EAAS,WAAA,EAAa,OAAO,CAAA;AAAA,QACpD;AAAA,MACF;AAAA,IACF;AAEA,IAAA,OAAO;AAAA,MACL,KAAA,EAAO,OAAO,MAAA,KAAW,CAAA;AAAA,MACzB,MAAA;AAAA,MACA,IAAA,EAAM,MAAA,CAAO,MAAA,KAAW,CAAA,GAAI,IAAA,GAAO;AAAA,KACrC;AAAA,EACF;AACF;;;ACpIO,IAAM,kBAAN,MAAsB;AAAA,EAC3B,OAAO,QAAA,CAAS,IAAA,EAAW,MAAA,EAAgD;AAEzE,IAAA,IAAI,OAAO,MAAA,CAAO,SAAA,KAAc,UAAA,EAAY;AAC1C,MAAA,MAAM,MAAA,GAAS,MAAA,CAAO,SAAA,CAAU,IAAI,CAAA;AACpC,MAAA,IAAI,OAAO,OAAA,EAAS;AAClB,QAAA,OAAO,EAAE,KAAA,EAAO,IAAA,EAAM,IAAA,EAAM,OAAO,IAAA,EAAK;AAAA,MAC1C;AACA,MAAA,OAAO,EAAE,KAAA,EAAO,KAAA,EAAO,MAAA,EAAQ,OAAO,KAAA,EAAM;AAAA,IAC9C;AAGA,IAAA,IAAI,OAAO,MAAA,CAAO,KAAA,KAAU,UAAA,EAAY;AACtC,MAAA,IAAI;AACF,QAAA,MAAM,MAAA,GAAS,MAAA,CAAO,KAAA,CAAM,IAAI,CAAA;AAChC,QAAA,OAAO,EAAE,KAAA,EAAO,IAAA,EAAM,IAAA,EAAM,MAAA,EAAO;AAAA,MACrC,SAAS,GAAA,EAAK;AACZ,QAAA,OAAO,EAAE,KAAA,EAAO,KAAA,EAAO,MAAA,EAAQ,GAAA,EAAI;AAAA,MACrC;AAAA,IACF;AAGA,IAAA,IAAI,OAAO,MAAA,CAAO,QAAA,KAAa,UAAA,EAAY;AACzC,MAAA,MAAM,MAAA,GAAS,MAAA,CAAO,QAAA,CAAS,IAAI,CAAA;AACnC,MAAA,IAAI,OAAO,KAAA,EAAO;AAChB,QAAA,OAAO,EAAE,KAAA,EAAO,KAAA,EAAO,MAAA,EAAQ,OAAO,KAAA,EAAM;AAAA,MAC9C;AACA,MAAA,OAAO,EAAE,KAAA,EAAO,IAAA,EAAM,IAAA,EAAM,OAAO,KAAA,EAAM;AAAA,IAC3C;AAEA,IAAA,OAAO,EAAE,KAAA,EAAO,IAAA,EAAM,IAAA,EAAK;AAAA,EAC7B;AACF","file":"index.mjs","sourcesContent":["export interface ValidationError {\r\n field: string;\r\n message: string;\r\n value?: any;\r\n}\r\n\r\nexport interface ValidationResult {\r\n valid: boolean;\r\n errors: ValidationError[];\r\n data?: any;\r\n}\r\n\r\ntype RuleType = \"string\" | \"number\" | \"boolean\" | \"object\" | \"array\";\r\n\r\ninterface FieldRule {\r\n type?: RuleType;\r\n required?: boolean;\r\n min?: number;\r\n max?: number;\r\n pattern?: RegExp;\r\n enum?: any[];\r\n custom?: (value: any) => string | null;\r\n message?: string;\r\n}\r\n\r\nexport type Schema = Record<string, FieldRule>;\r\n\r\nexport class DTOValidator {\r\n static validate(data: any, schema: Schema): ValidationResult {\r\n const errors: ValidationError[] = [];\r\n\r\n if (data == null || typeof data !== \"object\") {\r\n return {\r\n valid: false,\r\n errors: [{ field: \"_root\", message: \"Request body must be an object\" }],\r\n };\r\n }\r\n\r\n for (const [field, rule] of Object.entries(schema)) {\r\n const value = data[field];\r\n\r\n if (rule.required && (value === undefined || value === null || value === \"\")) {\r\n errors.push({\r\n field,\r\n message: rule.message ?? `${field} is required`,\r\n value,\r\n });\r\n continue;\r\n }\r\n\r\n if (value === undefined || value === null) continue;\r\n\r\n if (rule.type) {\r\n const actualType = Array.isArray(value) ? \"array\" : typeof value;\r\n if (actualType !== rule.type) {\r\n errors.push({\r\n field,\r\n message: rule.message ?? `${field} must be of type ${rule.type}`,\r\n value,\r\n });\r\n continue;\r\n }\r\n }\r\n\r\n if (rule.type === \"string\" || typeof value === \"string\") {\r\n if (rule.min !== undefined && value.length < rule.min) {\r\n errors.push({\r\n field,\r\n message: rule.message ?? `${field} must be at least ${rule.min} characters`,\r\n value,\r\n });\r\n }\r\n if (rule.max !== undefined && value.length > rule.max) {\r\n errors.push({\r\n field,\r\n message: rule.message ?? `${field} must be at most ${rule.max} characters`,\r\n value,\r\n });\r\n }\r\n if (rule.pattern && !rule.pattern.test(value)) {\r\n errors.push({\r\n field,\r\n message: rule.message ?? `${field} has invalid format`,\r\n value,\r\n });\r\n }\r\n }\r\n\r\n if (rule.type === \"number\" || typeof value === \"number\") {\r\n if (rule.min !== undefined && value < rule.min) {\r\n errors.push({\r\n field,\r\n message: rule.message ?? `${field} must be at least ${rule.min}`,\r\n value,\r\n });\r\n }\r\n if (rule.max !== undefined && value > rule.max) {\r\n errors.push({\r\n field,\r\n message: rule.message ?? `${field} must be at most ${rule.max}`,\r\n value,\r\n });\r\n }\r\n }\r\n\r\n if (rule.type === \"array\" && Array.isArray(value)) {\r\n if (rule.min !== undefined && value.length < rule.min) {\r\n errors.push({\r\n field,\r\n message: rule.message ?? `${field} must have at least ${rule.min} items`,\r\n value,\r\n });\r\n }\r\n if (rule.max !== undefined && value.length > rule.max) {\r\n errors.push({\r\n field,\r\n message: rule.message ?? `${field} must have at most ${rule.max} items`,\r\n value,\r\n });\r\n }\r\n }\r\n\r\n if (rule.enum && !rule.enum.includes(value)) {\r\n errors.push({\r\n field,\r\n message: rule.message ?? `${field} must be one of: ${rule.enum.join(\", \")}`,\r\n value,\r\n });\r\n }\r\n\r\n if (rule.custom) {\r\n const customError = rule.custom(value);\r\n if (customError) {\r\n errors.push({ field, message: customError, value });\r\n }\r\n }\r\n }\r\n\r\n return {\r\n valid: errors.length === 0,\r\n errors,\r\n data: errors.length === 0 ? data : undefined,\r\n };\r\n }\r\n}\r\n","export interface ExternalSchema {\r\n parse?: (data: any) => any;\r\n safeParse?: (data: any) => { success: boolean; data?: any; error?: any };\r\n validate?: (data: any) => { value?: any; error?: any };\r\n}\r\n\r\nexport interface SchemaValidationResult {\r\n valid: boolean;\r\n data?: any;\r\n errors?: any;\r\n}\r\n\r\nexport class SchemaValidator {\r\n static validate(data: any, schema: ExternalSchema): SchemaValidationResult {\r\n // Zod-style schema\r\n if (typeof schema.safeParse === \"function\") {\r\n const result = schema.safeParse(data);\r\n if (result.success) {\r\n return { valid: true, data: result.data };\r\n }\r\n return { valid: false, errors: result.error };\r\n }\r\n\r\n // Zod parse (throws on error)\r\n if (typeof schema.parse === \"function\") {\r\n try {\r\n const parsed = schema.parse(data);\r\n return { valid: true, data: parsed };\r\n } catch (err) {\r\n return { valid: false, errors: err };\r\n }\r\n }\r\n\r\n // Joi-style schema\r\n if (typeof schema.validate === \"function\") {\r\n const result = schema.validate(data);\r\n if (result.error) {\r\n return { valid: false, errors: result.error };\r\n }\r\n return { valid: true, data: result.value };\r\n }\r\n\r\n return { valid: true, data };\r\n }\r\n}\r\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "azurajs",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.4",
|
|
4
4
|
"description": "Ultra-fast TypeScript-first web framework for Node.js and Bun with decorator-based routing, zero dependencies, and built-in plugin system",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.mjs",
|
|
@@ -70,6 +70,79 @@
|
|
|
70
70
|
"require": "./dist/middleware/index.js",
|
|
71
71
|
"default": "./dist/middleware/index.mjs"
|
|
72
72
|
},
|
|
73
|
+
"./logger": {
|
|
74
|
+
"types": "./dist/logger/index.d.ts",
|
|
75
|
+
"bun": "./dist/logger/index.mjs",
|
|
76
|
+
"import": "./dist/logger/index.mjs",
|
|
77
|
+
"module": "./dist/logger/index.mjs",
|
|
78
|
+
"require": "./dist/logger/index.js",
|
|
79
|
+
"default": "./dist/logger/index.mjs"
|
|
80
|
+
},
|
|
81
|
+
"./validators": {
|
|
82
|
+
"types": "./dist/validators/index.d.ts",
|
|
83
|
+
"bun": "./dist/validators/index.mjs",
|
|
84
|
+
"import": "./dist/validators/index.mjs",
|
|
85
|
+
"module": "./dist/validators/index.mjs",
|
|
86
|
+
"require": "./dist/validators/index.js",
|
|
87
|
+
"default": "./dist/validators/index.mjs"
|
|
88
|
+
},
|
|
89
|
+
"./router": {
|
|
90
|
+
"types": "./dist/router/index.d.ts",
|
|
91
|
+
"bun": "./dist/router/index.mjs",
|
|
92
|
+
"import": "./dist/router/index.mjs",
|
|
93
|
+
"module": "./dist/router/index.mjs",
|
|
94
|
+
"require": "./dist/router/index.js",
|
|
95
|
+
"default": "./dist/router/index.mjs"
|
|
96
|
+
},
|
|
97
|
+
"./cookies": {
|
|
98
|
+
"types": "./dist/cookies/index.d.ts",
|
|
99
|
+
"bun": "./dist/cookies/index.mjs",
|
|
100
|
+
"import": "./dist/cookies/index.mjs",
|
|
101
|
+
"module": "./dist/cookies/index.mjs",
|
|
102
|
+
"require": "./dist/cookies/index.js",
|
|
103
|
+
"default": "./dist/cookies/index.mjs"
|
|
104
|
+
},
|
|
105
|
+
"./http-error": {
|
|
106
|
+
"types": "./dist/http-error/index.d.ts",
|
|
107
|
+
"bun": "./dist/http-error/index.mjs",
|
|
108
|
+
"import": "./dist/http-error/index.mjs",
|
|
109
|
+
"module": "./dist/http-error/index.mjs",
|
|
110
|
+
"require": "./dist/http-error/index.js",
|
|
111
|
+
"default": "./dist/http-error/index.mjs"
|
|
112
|
+
},
|
|
113
|
+
"./cors": {
|
|
114
|
+
"types": "./dist/cors/index.d.ts",
|
|
115
|
+
"bun": "./dist/cors/index.mjs",
|
|
116
|
+
"import": "./dist/cors/index.mjs",
|
|
117
|
+
"module": "./dist/cors/index.mjs",
|
|
118
|
+
"require": "./dist/cors/index.js",
|
|
119
|
+
"default": "./dist/cors/index.mjs"
|
|
120
|
+
},
|
|
121
|
+
"./rate-limit": {
|
|
122
|
+
"types": "./dist/rate-limit/index.d.ts",
|
|
123
|
+
"bun": "./dist/rate-limit/index.mjs",
|
|
124
|
+
"import": "./dist/rate-limit/index.mjs",
|
|
125
|
+
"module": "./dist/rate-limit/index.mjs",
|
|
126
|
+
"require": "./dist/rate-limit/index.js",
|
|
127
|
+
"default": "./dist/rate-limit/index.mjs"
|
|
128
|
+
},
|
|
129
|
+
"./infra": {
|
|
130
|
+
"types": "./dist/infra/index.d.ts",
|
|
131
|
+
"bun": "./dist/infra/index.mjs",
|
|
132
|
+
"import": "./dist/infra/index.mjs",
|
|
133
|
+
"module": "./dist/infra/index.mjs",
|
|
134
|
+
"require": "./dist/infra/index.js",
|
|
135
|
+
"default": "./dist/infra/index.mjs"
|
|
136
|
+
},
|
|
137
|
+
"./swagger": {
|
|
138
|
+
"types": "./dist/swagger/index.d.ts",
|
|
139
|
+
"bun": "./dist/swagger/index.mjs",
|
|
140
|
+
"import": "./dist/swagger/index.mjs",
|
|
141
|
+
"module": "./dist/swagger/index.mjs",
|
|
142
|
+
"require": "./dist/swagger/index.js",
|
|
143
|
+
"default": "./dist/swagger/index.mjs"
|
|
144
|
+
},
|
|
145
|
+
"./swagger-ui": "./dist/swagger/swagger-ui-modern.html",
|
|
73
146
|
"./package.json": "./package.json"
|
|
74
147
|
},
|
|
75
148
|
"typesVersions": {
|
|
@@ -80,7 +153,16 @@
|
|
|
80
153
|
"types": ["./dist/types/index.d.ts"],
|
|
81
154
|
"utils": ["./dist/utils/index.d.ts"],
|
|
82
155
|
"config": ["./dist/config/index.d.ts"],
|
|
83
|
-
"middleware": ["./dist/middleware/index.d.ts"]
|
|
156
|
+
"middleware": ["./dist/middleware/index.d.ts"],
|
|
157
|
+
"logger": ["./dist/logger/index.d.ts"],
|
|
158
|
+
"validators": ["./dist/validators/index.d.ts"],
|
|
159
|
+
"router": ["./dist/router/index.d.ts"],
|
|
160
|
+
"cookies": ["./dist/cookies/index.d.ts"],
|
|
161
|
+
"http-error": ["./dist/http-error/index.d.ts"],
|
|
162
|
+
"cors": ["./dist/cors/index.d.ts"],
|
|
163
|
+
"rate-limit": ["./dist/rate-limit/index.d.ts"],
|
|
164
|
+
"infra": ["./dist/infra/index.d.ts"],
|
|
165
|
+
"swagger": ["./dist/swagger/index.d.ts"]
|
|
84
166
|
}
|
|
85
167
|
},
|
|
86
168
|
"files": [
|
|
@@ -90,6 +172,8 @@
|
|
|
90
172
|
"LICENSE"
|
|
91
173
|
],
|
|
92
174
|
"scripts": {
|
|
175
|
+
"generate:swagger-ui": "node scripts/embed-swagger-ui.mjs",
|
|
176
|
+
"prebuild": "node scripts/embed-swagger-ui.mjs",
|
|
93
177
|
"build": "tsup",
|
|
94
178
|
"build:check": "tsc --noEmit",
|
|
95
179
|
"dev": "tsup --watch",
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { serializeCookie, clearCookieHeader } from "../utils/cookies/CookieManager.js";
|
package/src/core/index.ts
CHANGED
package/src/core/router.ts
CHANGED
|
@@ -1,4 +1,12 @@
|
|
|
1
1
|
import type { HttpMethod, RouteHandler, MiddlewareHandler, RouteMatch } from "../types/common.type.js";
|
|
2
|
+
import type { RouteMeta } from "../types/routes.type.js";
|
|
3
|
+
|
|
4
|
+
/** Rota registada com metadados opcionais (OpenAPI / documentação). */
|
|
5
|
+
export interface RouteDocument {
|
|
6
|
+
method: HttpMethod;
|
|
7
|
+
path: string;
|
|
8
|
+
meta?: RouteMeta;
|
|
9
|
+
}
|
|
2
10
|
|
|
3
11
|
const PARAM_PREFIX = 58; // ':'
|
|
4
12
|
|
|
@@ -243,32 +251,35 @@ export class Router {
|
|
|
243
251
|
}
|
|
244
252
|
|
|
245
253
|
getAllRoutes(): Array<{ method: HttpMethod; path: string }> {
|
|
246
|
-
|
|
254
|
+
return this.getRouteDocuments().map(({ method, path }) => ({ method, path }));
|
|
255
|
+
}
|
|
247
256
|
|
|
248
|
-
|
|
257
|
+
/**
|
|
258
|
+
* Todas as rotas com metadados (ex.: OpenAPI) associados ao handler.
|
|
259
|
+
*/
|
|
260
|
+
getRouteDocuments(): RouteDocument[] {
|
|
261
|
+
const routes: RouteDocument[] = [];
|
|
262
|
+
|
|
263
|
+
for (const [key, stored] of this.staticRoutes) {
|
|
249
264
|
const [method, path] = key.split(":", 2) as [HttpMethod, string];
|
|
250
|
-
routes.push({ method, path });
|
|
265
|
+
routes.push({ method, path, meta: stored.meta });
|
|
251
266
|
}
|
|
252
267
|
|
|
253
|
-
this.
|
|
268
|
+
this.collectRoutesWithMeta(this.root, "", routes);
|
|
254
269
|
return routes;
|
|
255
270
|
}
|
|
256
271
|
|
|
257
|
-
private
|
|
258
|
-
node
|
|
259
|
-
|
|
260
|
-
routes: Array<{ method: HttpMethod; path: string }>,
|
|
261
|
-
): void {
|
|
262
|
-
for (const [method] of node.handlers) {
|
|
263
|
-
routes.push({ method, path: prefix || "/" });
|
|
272
|
+
private collectRoutesWithMeta(node: RadixNode, prefix: string, routes: RouteDocument[]): void {
|
|
273
|
+
for (const [method, stored] of node.handlers) {
|
|
274
|
+
routes.push({ method, path: prefix || "/", meta: stored.meta });
|
|
264
275
|
}
|
|
265
276
|
|
|
266
277
|
for (const [seg, child] of node.children) {
|
|
267
|
-
this.
|
|
278
|
+
this.collectRoutesWithMeta(child, `${prefix}/${seg}`, routes);
|
|
268
279
|
}
|
|
269
280
|
|
|
270
281
|
if (node.paramChild) {
|
|
271
|
-
this.
|
|
282
|
+
this.collectRoutesWithMeta(
|
|
272
283
|
node.paramChild,
|
|
273
284
|
`${prefix}/:${node.paramChild.paramName}`,
|
|
274
285
|
routes,
|
|
@@ -276,8 +287,8 @@ export class Router {
|
|
|
276
287
|
}
|
|
277
288
|
|
|
278
289
|
if (node.wildcardHandler) {
|
|
279
|
-
for (const [method] of node.wildcardHandler) {
|
|
280
|
-
routes.push({ method, path: `${prefix}
|
|
290
|
+
for (const [method, stored] of node.wildcardHandler) {
|
|
291
|
+
routes.push({ method, path: `${prefix}/*`, meta: stored.meta });
|
|
281
292
|
}
|
|
282
293
|
}
|
|
283
294
|
}
|
package/src/core/server.ts
CHANGED
|
@@ -18,7 +18,26 @@ import type {
|
|
|
18
18
|
} from "../types/common.type.js";
|
|
19
19
|
import type { AzuraConfig } from "../types/config.type.js";
|
|
20
20
|
import type { PluginHandler } from "../types/plugins/plugin.type.js";
|
|
21
|
-
import type { ControllerMetadata, RouteMetadata } from "../types/routes.type.js";
|
|
21
|
+
import type { ControllerMetadata, RouteMeta, RouteMetadata } from "../types/routes.type.js";
|
|
22
|
+
import type { BuildOpenApiOptions } from "../types/swagger.type.js";
|
|
23
|
+
import { API_OPERATION_KEY, API_TAGS_KEY, METHOD_TAGS_KEY } from "../swagger/constants.js";
|
|
24
|
+
import { buildOpenApiDocument } from "../swagger/openapi-builder.js";
|
|
25
|
+
|
|
26
|
+
function isRouteDocumentMeta(o: unknown): o is RouteMeta {
|
|
27
|
+
if (!o || typeof o !== "object" || Array.isArray(o)) return false;
|
|
28
|
+
const x = o as Record<string, unknown>;
|
|
29
|
+
return (
|
|
30
|
+
"summary" in x ||
|
|
31
|
+
"description" in x ||
|
|
32
|
+
"tags" in x ||
|
|
33
|
+
"deprecated" in x ||
|
|
34
|
+
"parameters" in x ||
|
|
35
|
+
"responses" in x ||
|
|
36
|
+
"security" in x ||
|
|
37
|
+
"produces" in x ||
|
|
38
|
+
"consumes" in x
|
|
39
|
+
);
|
|
40
|
+
}
|
|
22
41
|
|
|
23
42
|
const CONTROLLER_META_KEY = "azura:controller";
|
|
24
43
|
const ROUTES_META_KEY = "azura:routes";
|
|
@@ -86,35 +105,35 @@ export class AzuraServer {
|
|
|
86
105
|
return this;
|
|
87
106
|
}
|
|
88
107
|
|
|
89
|
-
get(path: string, ...handlers: (MiddlewareHandler | Function)[]): this {
|
|
108
|
+
get(path: string, ...handlers: (MiddlewareHandler | Function | RouteMeta)[]): this {
|
|
90
109
|
return this.route("GET", path, handlers);
|
|
91
110
|
}
|
|
92
111
|
|
|
93
|
-
post(path: string, ...handlers: (MiddlewareHandler | Function)[]): this {
|
|
112
|
+
post(path: string, ...handlers: (MiddlewareHandler | Function | RouteMeta)[]): this {
|
|
94
113
|
return this.route("POST", path, handlers);
|
|
95
114
|
}
|
|
96
115
|
|
|
97
|
-
put(path: string, ...handlers: (MiddlewareHandler | Function)[]): this {
|
|
116
|
+
put(path: string, ...handlers: (MiddlewareHandler | Function | RouteMeta)[]): this {
|
|
98
117
|
return this.route("PUT", path, handlers);
|
|
99
118
|
}
|
|
100
119
|
|
|
101
|
-
delete(path: string, ...handlers: (MiddlewareHandler | Function)[]): this {
|
|
120
|
+
delete(path: string, ...handlers: (MiddlewareHandler | Function | RouteMeta)[]): this {
|
|
102
121
|
return this.route("DELETE", path, handlers);
|
|
103
122
|
}
|
|
104
123
|
|
|
105
|
-
patch(path: string, ...handlers: (MiddlewareHandler | Function)[]): this {
|
|
124
|
+
patch(path: string, ...handlers: (MiddlewareHandler | Function | RouteMeta)[]): this {
|
|
106
125
|
return this.route("PATCH", path, handlers);
|
|
107
126
|
}
|
|
108
127
|
|
|
109
|
-
head(path: string, ...handlers: (MiddlewareHandler | Function)[]): this {
|
|
128
|
+
head(path: string, ...handlers: (MiddlewareHandler | Function | RouteMeta)[]): this {
|
|
110
129
|
return this.route("HEAD", path, handlers);
|
|
111
130
|
}
|
|
112
131
|
|
|
113
|
-
options(path: string, ...handlers: (MiddlewareHandler | Function)[]): this {
|
|
132
|
+
options(path: string, ...handlers: (MiddlewareHandler | Function | RouteMeta)[]): this {
|
|
114
133
|
return this.route("OPTIONS", path, handlers);
|
|
115
134
|
}
|
|
116
135
|
|
|
117
|
-
all(path: string, ...handlers: (MiddlewareHandler | Function)[]): this {
|
|
136
|
+
all(path: string, ...handlers: (MiddlewareHandler | Function | RouteMeta)[]): this {
|
|
118
137
|
const methods: HttpMethod[] = ["GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS"];
|
|
119
138
|
for (const method of methods) {
|
|
120
139
|
this.route(method, path, handlers);
|
|
@@ -122,10 +141,20 @@ export class AzuraServer {
|
|
|
122
141
|
return this;
|
|
123
142
|
}
|
|
124
143
|
|
|
125
|
-
private route(method: HttpMethod, path: string, handlers: (MiddlewareHandler | Function)[]): this {
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
144
|
+
private route(method: HttpMethod, path: string, handlers: (MiddlewareHandler | Function | RouteMeta)[]): this {
|
|
145
|
+
let meta: RouteMeta | undefined;
|
|
146
|
+
let list = handlers as unknown[];
|
|
147
|
+
const last = list[list.length - 1];
|
|
148
|
+
if (list.length >= 2 && isRouteDocumentMeta(last)) {
|
|
149
|
+
meta = last;
|
|
150
|
+
list = list.slice(0, -1);
|
|
151
|
+
}
|
|
152
|
+
if (list.length === 0) {
|
|
153
|
+
throw new Error(`Route ${method} ${path} requires a handler function`);
|
|
154
|
+
}
|
|
155
|
+
const routeHandler = list[list.length - 1] as any;
|
|
156
|
+
const middlewares = list.slice(0, -1) as MiddlewareHandler[];
|
|
157
|
+
this.router.add(method, path, routeHandler, middlewares, meta);
|
|
129
158
|
return this;
|
|
130
159
|
}
|
|
131
160
|
|
|
@@ -141,6 +170,7 @@ export class AzuraServer {
|
|
|
141
170
|
const prefix: string = Reflect.getMetadata?.(CONTROLLER_META_KEY, Controller) ?? "";
|
|
142
171
|
const controllerMiddlewares: MiddlewareHandler[] =
|
|
143
172
|
Reflect.getMetadata?.("azura:middlewares", Controller) ?? [];
|
|
173
|
+
const controllerTags: string[] = Reflect.getMetadata?.(API_TAGS_KEY, Controller) ?? [];
|
|
144
174
|
|
|
145
175
|
const prototype = Controller.prototype;
|
|
146
176
|
const propertyNames = Object.getOwnPropertyNames(prototype).filter(
|
|
@@ -163,6 +193,16 @@ export class AzuraServer {
|
|
|
163
193
|
|
|
164
194
|
const allMiddlewares = [...controllerMiddlewares, ...routeMiddlewares];
|
|
165
195
|
|
|
196
|
+
const methodTags: string[] = Reflect.getMetadata?.(METHOD_TAGS_KEY, prototype, propertyKey) ?? [];
|
|
197
|
+
const opExtra = (Reflect.getMetadata?.(API_OPERATION_KEY, prototype, propertyKey) ??
|
|
198
|
+
{}) as Partial<RouteMeta>;
|
|
199
|
+
const tagList = [...controllerTags, ...methodTags, ...(routeMeta.meta?.tags ?? [])];
|
|
200
|
+
const mergedMeta: RouteMeta | undefined = (() => {
|
|
201
|
+
const m = { ...routeMeta.meta, ...opExtra } as RouteMeta;
|
|
202
|
+
if (tagList.length) m.tags = tagList;
|
|
203
|
+
return Object.keys(m).length ? m : undefined;
|
|
204
|
+
})();
|
|
205
|
+
|
|
166
206
|
const handler = async (req: AzuraRequest, res: AzuraResponse) => {
|
|
167
207
|
const args = this.resolveParams(paramsMeta, req, res);
|
|
168
208
|
const result = await instance[propertyKey](...args);
|
|
@@ -175,7 +215,7 @@ export class AzuraServer {
|
|
|
175
215
|
}
|
|
176
216
|
};
|
|
177
217
|
|
|
178
|
-
this.router.add(routeMeta.method, fullPath, handler, allMiddlewares,
|
|
218
|
+
this.router.add(routeMeta.method, fullPath, handler, allMiddlewares, mergedMeta);
|
|
179
219
|
}
|
|
180
220
|
}
|
|
181
221
|
|
|
@@ -587,4 +627,14 @@ export class AzuraServer {
|
|
|
587
627
|
getRoutes(): Array<{ method: HttpMethod; path: string }> {
|
|
588
628
|
return this.router.getAllRoutes();
|
|
589
629
|
}
|
|
630
|
+
|
|
631
|
+
/** Rotas com metadados OpenAPI (para Swagger ou export manual). */
|
|
632
|
+
getRouteDocuments() {
|
|
633
|
+
return this.router.getRouteDocuments();
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
/** Gera documento OpenAPI 3.0 a partir das rotas registadas. */
|
|
637
|
+
getOpenApiDocument(options: BuildOpenApiOptions = {}) {
|
|
638
|
+
return buildOpenApiDocument(this.getRouteDocuments(), options);
|
|
639
|
+
}
|
|
590
640
|
}
|
package/src/decorators/index.ts
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { HttpError } from "../utils/HttpError.js";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { Logger, logger } from "../utils/Logger.js";
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { PluginHandler } from "../types/plugins/plugin.type.js";
|
|
2
|
+
import type { SwaggerPluginOptions } from "../types/swagger.type.js";
|
|
3
|
+
import { buildOpenApiDocument } from "../swagger/openapi-builder.js";
|
|
4
|
+
import { renderSwaggerUiPage } from "../swagger/swagger-ui-html.js";
|
|
5
|
+
|
|
6
|
+
export function SwaggerPlugin(options: SwaggerPluginOptions): PluginHandler {
|
|
7
|
+
const {
|
|
8
|
+
getDocuments,
|
|
9
|
+
specPath = "/openapi.json",
|
|
10
|
+
docsPath = "/docs",
|
|
11
|
+
deepLinking = true,
|
|
12
|
+
servers,
|
|
13
|
+
pathPrefix,
|
|
14
|
+
info,
|
|
15
|
+
} = options;
|
|
16
|
+
|
|
17
|
+
const title = info?.title ?? "API";
|
|
18
|
+
|
|
19
|
+
return async (ctx, next) => {
|
|
20
|
+
const { req, res } = ctx;
|
|
21
|
+
const path = req.pathname ?? "";
|
|
22
|
+
|
|
23
|
+
if (path === specPath || path === `${specPath}/`) {
|
|
24
|
+
const doc = buildOpenApiDocument(getDocuments(), {
|
|
25
|
+
info: info ?? { title, version: "1.0.0" },
|
|
26
|
+
servers,
|
|
27
|
+
pathPrefix,
|
|
28
|
+
});
|
|
29
|
+
res.json(doc);
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (path === docsPath || path === `${docsPath}/`) {
|
|
34
|
+
const html = renderSwaggerUiPage({
|
|
35
|
+
title,
|
|
36
|
+
specUrl: specPath,
|
|
37
|
+
deepLinking,
|
|
38
|
+
});
|
|
39
|
+
res.html(html);
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
next();
|
|
44
|
+
};
|
|
45
|
+
}
|
package/src/plugins/index.ts
CHANGED
|
@@ -16,3 +16,4 @@ export type { SSEClient } from "./SSEPlugin.js";
|
|
|
16
16
|
export { ProxyPlugin } from "./ProxyPlugin.js";
|
|
17
17
|
export { MultipartPlugin } from "./MultipartPlugin.js";
|
|
18
18
|
export type { UploadedFile } from "./MultipartPlugin.js";
|
|
19
|
+
export { SwaggerPlugin } from "./SwaggerPlugin.js";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { Router } from "../core/router.js";
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/** Tags OpenAPI a nível de controller (todas as rotas do class) */
|
|
2
|
+
export const API_TAGS_KEY = "azura:api-tags";
|
|
3
|
+
|
|
4
|
+
/** Tags por método (independente da ordem em relação a @Get/@Post) */
|
|
5
|
+
export const METHOD_TAGS_KEY = "azura:swagger-method-tags";
|
|
6
|
+
|
|
7
|
+
/** Metadados OpenAPI por método (summary, responses, etc.) */
|
|
8
|
+
export const API_OPERATION_KEY = "azura:swagger-operation";
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { RouteMeta } from "../types/routes.type.js";
|
|
2
|
+
import { API_OPERATION_KEY, API_TAGS_KEY, METHOD_TAGS_KEY } from "./constants.js";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Tags OpenAPI no controller (todas as rotas) ou num método específico.
|
|
6
|
+
* Compatível com `experimentalDecorators` (assinatura legacy de 3 argumentos em métodos).
|
|
7
|
+
*/
|
|
8
|
+
export function ApiTags(...tags: string[]): ClassDecorator & MethodDecorator {
|
|
9
|
+
return function (
|
|
10
|
+
target: object,
|
|
11
|
+
propertyKey?: string | symbol,
|
|
12
|
+
_descriptor?: PropertyDescriptor,
|
|
13
|
+
): void {
|
|
14
|
+
if (propertyKey === undefined) {
|
|
15
|
+
Reflect.defineMetadata(API_TAGS_KEY, tags, target);
|
|
16
|
+
} else {
|
|
17
|
+
Reflect.defineMetadata(METHOD_TAGS_KEY, tags, target, String(propertyKey));
|
|
18
|
+
}
|
|
19
|
+
} as ClassDecorator & MethodDecorator;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Metadados OpenAPI (summary, description, responses, parameters, etc.).
|
|
24
|
+
*/
|
|
25
|
+
export function ApiOperation(meta: Partial<RouteMeta>): MethodDecorator {
|
|
26
|
+
return function (
|
|
27
|
+
target: object,
|
|
28
|
+
propertyKey: string | symbol,
|
|
29
|
+
_descriptor: PropertyDescriptor,
|
|
30
|
+
): void {
|
|
31
|
+
const key = String(propertyKey);
|
|
32
|
+
const cur = (Reflect.getMetadata?.(API_OPERATION_KEY, target, key) ?? {}) as Partial<RouteMeta>;
|
|
33
|
+
Reflect.defineMetadata(API_OPERATION_KEY, { ...cur, ...meta }, target, key);
|
|
34
|
+
};
|
|
35
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/** OpenAPI 3 + UI Swagger (tema escuro minimalista, alinhado ao site AzuraJS). */
|
|
2
|
+
export { buildOpenApiDocument, toOpenApiPath } from "./openapi-builder.js";
|
|
3
|
+
export { SwaggerPlugin } from "../plugins/SwaggerPlugin.js";
|
|
4
|
+
export { renderSwaggerUiPage } from "./swagger-ui-html.js";
|
|
5
|
+
export { ApiTags, ApiOperation } from "./decorators.js";
|
|
6
|
+
export {
|
|
7
|
+
API_TAGS_KEY,
|
|
8
|
+
METHOD_TAGS_KEY,
|
|
9
|
+
API_OPERATION_KEY,
|
|
10
|
+
} from "./constants.js";
|