azurajs 1.0.2 → 1.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/LICENSE +673 -673
- package/README.md +501 -501
- package/package.json +1 -1
- package/src/decorators/Route.ts +141 -141
- package/src/decorators/index.ts +24 -24
- package/src/index.ts +27 -27
- package/src/infra/Router.ts +56 -56
- package/src/infra/Server.ts +297 -274
- package/src/infra/utils/GetIp.ts +15 -15
- package/src/infra/utils/GetOpenPort.ts +15 -15
- package/src/infra/utils/HttpError.ts +9 -9
- package/src/infra/utils/RequestHandler.ts +33 -33
- package/src/infra/utils/route/Node.ts +15 -15
- package/src/middleware/LoggingMiddleware.ts +108 -108
- package/src/middleware/index.ts +14 -14
- package/src/shared/config/ConfigModule.ts +115 -113
- package/src/shared/plugins/CORSPlugin.ts +28 -0
- package/src/shared/plugins/RateLimitPlugin.ts +32 -0
- package/src/types/common.type.ts +9 -4
- package/src/types/http/request.type.ts +20 -20
- package/src/types/http/response.type.ts +32 -32
- package/src/types/plugins/cors.type.ts +5 -0
- package/src/types/routes.type.ts +23 -23
- package/src/types/validations.type.ts +1 -1
- package/src/utils/Logger.ts +23 -5
- package/src/utils/Parser.ts +19 -19
- package/src/utils/cookies/ParserCookie.ts +9 -9
- package/src/utils/cookies/SerializeCookie.ts +15 -15
- package/src/utils/validators/DTOValidator.ts +30 -30
- package/src/utils/validators/SchemaValidator.ts +37 -37
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
import { IncomingMessage, type IncomingHttpHeaders } from "node:http";
|
|
2
|
-
|
|
3
|
-
export interface RequestServer extends IncomingMessage {
|
|
4
|
-
path: string;
|
|
5
|
-
originalUrl: string;
|
|
6
|
-
method: string;
|
|
7
|
-
protocol: "http" | "https";
|
|
8
|
-
secure: boolean;
|
|
9
|
-
hostname: string;
|
|
10
|
-
subdomains: string[];
|
|
11
|
-
ip: string;
|
|
12
|
-
ips?: string[];
|
|
13
|
-
params: Record<string, string>;
|
|
14
|
-
query: Record<string, string>;
|
|
15
|
-
body: unknown;
|
|
16
|
-
cookies: Record<string, string>;
|
|
17
|
-
headers: IncomingHttpHeaders;
|
|
18
|
-
|
|
19
|
-
get(name: string): string | undefined;
|
|
20
|
-
header(name: string): string | undefined;
|
|
1
|
+
import { IncomingMessage, type IncomingHttpHeaders } from "node:http";
|
|
2
|
+
|
|
3
|
+
export interface RequestServer extends IncomingMessage {
|
|
4
|
+
path: string;
|
|
5
|
+
originalUrl: string;
|
|
6
|
+
method: string;
|
|
7
|
+
protocol: "http" | "https";
|
|
8
|
+
secure: boolean;
|
|
9
|
+
hostname: string;
|
|
10
|
+
subdomains: string[];
|
|
11
|
+
ip: string;
|
|
12
|
+
ips?: string[];
|
|
13
|
+
params: Record<string, string>;
|
|
14
|
+
query: Record<string, string>;
|
|
15
|
+
body: unknown;
|
|
16
|
+
cookies: Record<string, string>;
|
|
17
|
+
headers: IncomingHttpHeaders;
|
|
18
|
+
|
|
19
|
+
get(name: string): string | undefined;
|
|
20
|
+
header(name: string): string | undefined;
|
|
21
21
|
}
|
|
@@ -1,32 +1,32 @@
|
|
|
1
|
-
import { ServerResponse } from "node:http";
|
|
2
|
-
|
|
3
|
-
export interface CookieOptions {
|
|
4
|
-
domain?: string;
|
|
5
|
-
encode?: (value: string) => string;
|
|
6
|
-
expires?: Date;
|
|
7
|
-
httpOnly?: boolean;
|
|
8
|
-
maxAge?: number;
|
|
9
|
-
path?: string;
|
|
10
|
-
sameSite?: "strict" | "lax" | "none";
|
|
11
|
-
secure?: boolean;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export interface ResponseServer extends ServerResponse {
|
|
15
|
-
end(cb?: () => void): this;
|
|
16
|
-
end(chunk: any, cb?: () => void): this;
|
|
17
|
-
end(chunk: any, encoding: string, cb?: () => void): this;
|
|
18
|
-
headersSent: boolean;
|
|
19
|
-
status(code: number): this;
|
|
20
|
-
set(field: string, value: string | number | string[]): this;
|
|
21
|
-
header(field: string, value: string | number | string[]): this;
|
|
22
|
-
get(field: string): string | undefined;
|
|
23
|
-
type(type: string): this;
|
|
24
|
-
contentType(type: string): this;
|
|
25
|
-
redirect(url: string): this;
|
|
26
|
-
redirect(status: number, url: string): this;
|
|
27
|
-
location(url: string): this;
|
|
28
|
-
cookie(name: string, value: string, options?: CookieOptions): this;
|
|
29
|
-
clearCookie(name: string, options?: CookieOptions): this;
|
|
30
|
-
send(body: any): this;
|
|
31
|
-
json(body: any): this;
|
|
32
|
-
}
|
|
1
|
+
import { ServerResponse } from "node:http";
|
|
2
|
+
|
|
3
|
+
export interface CookieOptions {
|
|
4
|
+
domain?: string;
|
|
5
|
+
encode?: (value: string) => string;
|
|
6
|
+
expires?: Date;
|
|
7
|
+
httpOnly?: boolean;
|
|
8
|
+
maxAge?: number;
|
|
9
|
+
path?: string;
|
|
10
|
+
sameSite?: "strict" | "lax" | "none";
|
|
11
|
+
secure?: boolean;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface ResponseServer extends ServerResponse {
|
|
15
|
+
end(cb?: () => void): this;
|
|
16
|
+
end(chunk: any, cb?: () => void): this;
|
|
17
|
+
end(chunk: any, encoding: string, cb?: () => void): this;
|
|
18
|
+
headersSent: boolean;
|
|
19
|
+
status(code: number): this;
|
|
20
|
+
set(field: string, value: string | number | string[]): this;
|
|
21
|
+
header(field: string, value: string | number | string[]): this;
|
|
22
|
+
get(field: string): string | undefined;
|
|
23
|
+
type(type: string): this;
|
|
24
|
+
contentType(type: string): this;
|
|
25
|
+
redirect(url: string): this;
|
|
26
|
+
redirect(status: number, url: string): this;
|
|
27
|
+
location(url: string): this;
|
|
28
|
+
cookie(name: string, value: string, options?: CookieOptions): this;
|
|
29
|
+
clearCookie(name: string, options?: CookieOptions): this;
|
|
30
|
+
send(body: any): this;
|
|
31
|
+
json(body: any): this;
|
|
32
|
+
}
|
package/src/types/routes.type.ts
CHANGED
|
@@ -1,23 +1,23 @@
|
|
|
1
|
-
export type ParamSource =
|
|
2
|
-
| "param"
|
|
3
|
-
| "query"
|
|
4
|
-
| "body"
|
|
5
|
-
| "headers"
|
|
6
|
-
| "req"
|
|
7
|
-
| "res"
|
|
8
|
-
| "next"
|
|
9
|
-
| "ip"
|
|
10
|
-
| "useragent";
|
|
11
|
-
|
|
12
|
-
export interface ParamDefinition {
|
|
13
|
-
index: number;
|
|
14
|
-
type: ParamSource;
|
|
15
|
-
name?: string;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export interface RouteDefinition {
|
|
19
|
-
method: string;
|
|
20
|
-
path: string;
|
|
21
|
-
propertyKey: string;
|
|
22
|
-
params: ParamDefinition[];
|
|
23
|
-
}
|
|
1
|
+
export type ParamSource =
|
|
2
|
+
| "param"
|
|
3
|
+
| "query"
|
|
4
|
+
| "body"
|
|
5
|
+
| "headers"
|
|
6
|
+
| "req"
|
|
7
|
+
| "res"
|
|
8
|
+
| "next"
|
|
9
|
+
| "ip"
|
|
10
|
+
| "useragent";
|
|
11
|
+
|
|
12
|
+
export interface ParamDefinition {
|
|
13
|
+
index: number;
|
|
14
|
+
type: ParamSource;
|
|
15
|
+
name?: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface RouteDefinition {
|
|
19
|
+
method: string;
|
|
20
|
+
path: string;
|
|
21
|
+
propertyKey: string;
|
|
22
|
+
params: ParamDefinition[];
|
|
23
|
+
}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export type PrimitiveType = "string" | "number" | "boolean";
|
|
1
|
+
export type PrimitiveType = "string" | "number" | "boolean";
|
|
2
2
|
export type Schema = PrimitiveType | Schema[] | { [key: string]: Schema };
|
package/src/utils/Logger.ts
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
const RESET = "\x1b[0m";
|
|
2
|
+
const COLORS = {
|
|
3
|
+
info: "\x1b[36m",
|
|
4
|
+
warn: "\x1b[33m",
|
|
5
|
+
error: "\x1b[31m",
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
const LEVEL_LABELS = {
|
|
9
|
+
info: "INFO",
|
|
10
|
+
warn: "WARN",
|
|
11
|
+
error: "ERROR",
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export function logger(level: keyof typeof COLORS, msg: string) {
|
|
15
|
+
const color = COLORS[level];
|
|
16
|
+
const levelLabel = LEVEL_LABELS[level];
|
|
17
|
+
|
|
18
|
+
const prefix = `${color}[Azura:${levelLabel}]${RESET}`;
|
|
19
|
+
|
|
20
|
+
(level === "error" ? console.error : level === "warn" ? console.warn : console.log)(
|
|
21
|
+
`${prefix} ${msg}`
|
|
22
|
+
);
|
|
23
|
+
}
|
package/src/utils/Parser.ts
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
|
-
export function parseQS(qs: string): Record<string, string | string[]> {
|
|
2
|
-
const out: Record<string, string | string[]> = {};
|
|
3
|
-
if (!qs) return out;
|
|
4
|
-
const parts = qs.replace(/^\?/, "").split("&");
|
|
5
|
-
for (const p of parts) {
|
|
6
|
-
if (!p) continue;
|
|
7
|
-
const idx = p.indexOf("=");
|
|
8
|
-
const k = idx === -1 ? decodeURIComponent(p) : decodeURIComponent(p.slice(0, idx));
|
|
9
|
-
const v = idx === -1 ? "" : decodeURIComponent(p.slice(idx + 1));
|
|
10
|
-
if (Object.prototype.hasOwnProperty.call(out, k)) {
|
|
11
|
-
const cur = out[k];
|
|
12
|
-
if (Array.isArray(cur)) cur.push(v);
|
|
13
|
-
else out[k] = [cur as string, v];
|
|
14
|
-
} else {
|
|
15
|
-
out[k] = v;
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
return out;
|
|
19
|
-
}
|
|
1
|
+
export function parseQS(qs: string): Record<string, string | string[]> {
|
|
2
|
+
const out: Record<string, string | string[]> = {};
|
|
3
|
+
if (!qs) return out;
|
|
4
|
+
const parts = qs.replace(/^\?/, "").split("&");
|
|
5
|
+
for (const p of parts) {
|
|
6
|
+
if (!p) continue;
|
|
7
|
+
const idx = p.indexOf("=");
|
|
8
|
+
const k = idx === -1 ? decodeURIComponent(p) : decodeURIComponent(p.slice(0, idx));
|
|
9
|
+
const v = idx === -1 ? "" : decodeURIComponent(p.slice(idx + 1));
|
|
10
|
+
if (Object.prototype.hasOwnProperty.call(out, k)) {
|
|
11
|
+
const cur = out[k];
|
|
12
|
+
if (Array.isArray(cur)) cur.push(v);
|
|
13
|
+
else out[k] = [cur as string, v];
|
|
14
|
+
} else {
|
|
15
|
+
out[k] = v;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
return out;
|
|
19
|
+
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
export function parseCookiesHeader(header: string | undefined): Record<string, string> {
|
|
2
|
-
if (!header) return {};
|
|
3
|
-
return header.split(";").reduce<Record<string, string>>((acc, pair) => {
|
|
4
|
-
const [k, ...vals] = pair.trim().split("=");
|
|
5
|
-
if (!k) return acc;
|
|
6
|
-
acc[k] = decodeURIComponent(vals.join("="));
|
|
7
|
-
return acc;
|
|
8
|
-
}, {});
|
|
9
|
-
}
|
|
1
|
+
export function parseCookiesHeader(header: string | undefined): Record<string, string> {
|
|
2
|
+
if (!header) return {};
|
|
3
|
+
return header.split(";").reduce<Record<string, string>>((acc, pair) => {
|
|
4
|
+
const [k, ...vals] = pair.trim().split("=");
|
|
5
|
+
if (!k) return acc;
|
|
6
|
+
acc[k] = decodeURIComponent(vals.join("="));
|
|
7
|
+
return acc;
|
|
8
|
+
}, {});
|
|
9
|
+
}
|
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
import type { CookieOptions } from "../../types/http/response.type";
|
|
2
|
-
|
|
3
|
-
export function serializeCookie(name: string, val: string, opts: CookieOptions = {}): string {
|
|
4
|
-
const encode = opts.encode ?? encodeURIComponent;
|
|
5
|
-
let str = `${name}=${encode(val)}`;
|
|
6
|
-
if (opts.maxAge != null && !Number.isNaN(Number(opts.maxAge)))
|
|
7
|
-
str += `; Max-Age=${Math.floor(Number(opts.maxAge))}`;
|
|
8
|
-
if (opts.domain) str += `; Domain=${opts.domain}`;
|
|
9
|
-
if (opts.path) str += `; Path=${opts.path}`;
|
|
10
|
-
if (opts.expires) str += `; Expires=${opts.expires.toUTCString()}`;
|
|
11
|
-
if (opts.httpOnly) str += `; HttpOnly`;
|
|
12
|
-
if (opts.secure) str += `; Secure`;
|
|
13
|
-
if (opts.sameSite) str += `; SameSite=${opts.sameSite}`;
|
|
14
|
-
return str;
|
|
15
|
-
}
|
|
1
|
+
import type { CookieOptions } from "../../types/http/response.type";
|
|
2
|
+
|
|
3
|
+
export function serializeCookie(name: string, val: string, opts: CookieOptions = {}): string {
|
|
4
|
+
const encode = opts.encode ?? encodeURIComponent;
|
|
5
|
+
let str = `${name}=${encode(val)}`;
|
|
6
|
+
if (opts.maxAge != null && !Number.isNaN(Number(opts.maxAge)))
|
|
7
|
+
str += `; Max-Age=${Math.floor(Number(opts.maxAge))}`;
|
|
8
|
+
if (opts.domain) str += `; Domain=${opts.domain}`;
|
|
9
|
+
if (opts.path) str += `; Path=${opts.path}`;
|
|
10
|
+
if (opts.expires) str += `; Expires=${opts.expires.toUTCString()}`;
|
|
11
|
+
if (opts.httpOnly) str += `; HttpOnly`;
|
|
12
|
+
if (opts.secure) str += `; Secure`;
|
|
13
|
+
if (opts.sameSite) str += `; SameSite=${opts.sameSite}`;
|
|
14
|
+
return str;
|
|
15
|
+
}
|
|
@@ -1,30 +1,30 @@
|
|
|
1
|
-
const DTOS = new WeakMap<Function, Map<string, Array<{ index: number; dto: unknown }>>>();
|
|
2
|
-
|
|
3
|
-
export function validateDto(dto: unknown): ParameterDecorator {
|
|
4
|
-
return (target, propertyKey, parameterIndex) => {
|
|
5
|
-
const ctor = typeof target === "function" ? (target as Function) : (target as any).constructor;
|
|
6
|
-
|
|
7
|
-
let map = DTOS.get(ctor);
|
|
8
|
-
if (!map) {
|
|
9
|
-
map = new Map();
|
|
10
|
-
DTOS.set(ctor, map);
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
const key = String(propertyKey);
|
|
14
|
-
const list = map.get(key) ?? [];
|
|
15
|
-
|
|
16
|
-
list.push({
|
|
17
|
-
index: parameterIndex,
|
|
18
|
-
dto,
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
map.set(key, list);
|
|
22
|
-
};
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export function getDtoValidators(
|
|
26
|
-
ctor: Function,
|
|
27
|
-
propertyKey: string
|
|
28
|
-
): Array<{ index: number; dto: unknown }> {
|
|
29
|
-
return DTOS.get(ctor)?.get(propertyKey) ?? [];
|
|
30
|
-
}
|
|
1
|
+
const DTOS = new WeakMap<Function, Map<string, Array<{ index: number; dto: unknown }>>>();
|
|
2
|
+
|
|
3
|
+
export function validateDto(dto: unknown): ParameterDecorator {
|
|
4
|
+
return (target, propertyKey, parameterIndex) => {
|
|
5
|
+
const ctor = typeof target === "function" ? (target as Function) : (target as any).constructor;
|
|
6
|
+
|
|
7
|
+
let map = DTOS.get(ctor);
|
|
8
|
+
if (!map) {
|
|
9
|
+
map = new Map();
|
|
10
|
+
DTOS.set(ctor, map);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const key = String(propertyKey);
|
|
14
|
+
const list = map.get(key) ?? [];
|
|
15
|
+
|
|
16
|
+
list.push({
|
|
17
|
+
index: parameterIndex,
|
|
18
|
+
dto,
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
map.set(key, list);
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function getDtoValidators(
|
|
26
|
+
ctor: Function,
|
|
27
|
+
propertyKey: string
|
|
28
|
+
): Array<{ index: number; dto: unknown }> {
|
|
29
|
+
return DTOS.get(ctor)?.get(propertyKey) ?? [];
|
|
30
|
+
}
|
|
@@ -1,37 +1,37 @@
|
|
|
1
|
-
import { HttpError } from "../../infra/utils/HttpError";
|
|
2
|
-
import type { Schema } from "../../types/validations.type";
|
|
3
|
-
|
|
4
|
-
function isSchemaObject(schema: Schema): schema is Record<string, Schema> {
|
|
5
|
-
return typeof schema === "object" && !Array.isArray(schema);
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
export function validateSchema(schema: Schema, data: unknown): void {
|
|
9
|
-
if (typeof data !== "object" || data === null) {
|
|
10
|
-
throw new HttpError(400, "Payload inválido");
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
if (!isSchemaObject(schema)) {
|
|
14
|
-
throw new Error("Schema inválido: deve ser um objeto no topo");
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
const obj = data as Record<string, unknown>;
|
|
18
|
-
|
|
19
|
-
for (const key of Object.keys(schema)) {
|
|
20
|
-
const rule = schema[key];
|
|
21
|
-
const val = obj[key];
|
|
22
|
-
|
|
23
|
-
if (!rule) continue;
|
|
24
|
-
|
|
25
|
-
if (typeof rule === "string") {
|
|
26
|
-
if (typeof val !== rule) {
|
|
27
|
-
throw new HttpError(400, `${key} deve ser ${rule}`);
|
|
28
|
-
}
|
|
29
|
-
} else if (Array.isArray(rule)) {
|
|
30
|
-
if (!Array.isArray(val)) {
|
|
31
|
-
throw new HttpError(400, `${key} deve ser array`);
|
|
32
|
-
}
|
|
33
|
-
} else if (isSchemaObject(rule)) {
|
|
34
|
-
validateSchema(rule, val);
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
}
|
|
1
|
+
import { HttpError } from "../../infra/utils/HttpError";
|
|
2
|
+
import type { Schema } from "../../types/validations.type";
|
|
3
|
+
|
|
4
|
+
function isSchemaObject(schema: Schema): schema is Record<string, Schema> {
|
|
5
|
+
return typeof schema === "object" && !Array.isArray(schema);
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export function validateSchema(schema: Schema, data: unknown): void {
|
|
9
|
+
if (typeof data !== "object" || data === null) {
|
|
10
|
+
throw new HttpError(400, "Payload inválido");
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
if (!isSchemaObject(schema)) {
|
|
14
|
+
throw new Error("Schema inválido: deve ser um objeto no topo");
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const obj = data as Record<string, unknown>;
|
|
18
|
+
|
|
19
|
+
for (const key of Object.keys(schema)) {
|
|
20
|
+
const rule = schema[key];
|
|
21
|
+
const val = obj[key];
|
|
22
|
+
|
|
23
|
+
if (!rule) continue;
|
|
24
|
+
|
|
25
|
+
if (typeof rule === "string") {
|
|
26
|
+
if (typeof val !== rule) {
|
|
27
|
+
throw new HttpError(400, `${key} deve ser ${rule}`);
|
|
28
|
+
}
|
|
29
|
+
} else if (Array.isArray(rule)) {
|
|
30
|
+
if (!Array.isArray(val)) {
|
|
31
|
+
throw new HttpError(400, `${key} deve ser array`);
|
|
32
|
+
}
|
|
33
|
+
} else if (isSchemaObject(rule)) {
|
|
34
|
+
validateSchema(rule, val);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|