@arkyn/server 3.0.1-beta.155 → 3.0.1-beta.156

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 (91) hide show
  1. package/dist/index.js +1301 -37
  2. package/dist/modules/http/api/_logRequest.js +69 -0
  3. package/dist/modules/http/api/_makeRequest.js +69 -0
  4. package/dist/modules/http/api/deleteRequest.js +13 -0
  5. package/dist/modules/http/api/getRequest.js +12 -0
  6. package/dist/modules/http/api/patchRequest.js +13 -0
  7. package/dist/modules/http/api/postRequest.js +13 -0
  8. package/dist/modules/http/api/putRequest.js +13 -0
  9. package/dist/modules/http/badResponses/_badResponse.js +59 -0
  10. package/dist/modules/http/badResponses/badGateway.js +30 -0
  11. package/dist/modules/http/badResponses/badRequest.js +30 -0
  12. package/dist/modules/http/badResponses/conflict.js +30 -0
  13. package/dist/modules/http/badResponses/forbidden.js +30 -0
  14. package/dist/modules/http/badResponses/notFound.js +30 -0
  15. package/dist/modules/http/badResponses/notImplemented.js +30 -0
  16. package/dist/modules/http/badResponses/serverError.js +30 -0
  17. package/dist/modules/http/badResponses/unauthorized.js +30 -0
  18. package/dist/modules/http/badResponses/unprocessableEntity.js +36 -0
  19. package/dist/modules/http/successResponses/_successResponse.js +69 -0
  20. package/dist/modules/http/successResponses/created.js +30 -0
  21. package/dist/modules/http/successResponses/found.js +30 -0
  22. package/dist/modules/http/successResponses/noContent.js +21 -0
  23. package/dist/modules/http/successResponses/success.js +30 -0
  24. package/dist/modules/http/successResponses/updated.js +30 -0
  25. package/dist/modules/index.js +68 -0
  26. package/dist/modules/services/apiService.js +129 -0
  27. package/dist/modules/services/debugService.js +66 -0
  28. package/dist/modules/services/logMapperService.js +52 -0
  29. package/dist/modules/services/logService.js +31 -0
  30. package/dist/modules/utilities/decodeRequestBody.js +22 -0
  31. package/dist/modules/utilities/decodeRequestErrorMessage.js +7 -0
  32. package/dist/modules/utilities/errorHandler.js +54 -0
  33. package/dist/modules/utilities/flushDebugLogs.js +20 -0
  34. package/dist/modules/utilities/formAsyncParse.js +19 -0
  35. package/dist/modules/utilities/formParse.js +19 -0
  36. package/dist/modules/utilities/getScopedParams.js +11 -0
  37. package/dist/modules/utilities/schemaValidator.js +106 -0
  38. package/dist/modules/validations/validateCep.js +9 -0
  39. package/dist/modules/validations/validateCnpj.js +31 -0
  40. package/dist/modules/validations/validateCpf.js +28 -0
  41. package/dist/modules/validations/validateDate.js +27 -0
  42. package/dist/modules/validations/validateEmail.js +53 -0
  43. package/dist/modules/validations/validatePassword.js +16 -0
  44. package/dist/modules/validations/validatePhone.js +10 -0
  45. package/dist/modules/validations/validateRg.js +8 -0
  46. package/package.json +121 -87
  47. package/dist/bundle.js +0 -3014
  48. package/dist/bundle.umd.cjs +0 -9
  49. package/dist/http/api/_logRequest.js +0 -109
  50. package/dist/http/api/_makeRequest.js +0 -116
  51. package/dist/http/api/deleteRequest.js +0 -18
  52. package/dist/http/api/getRequest.js +0 -17
  53. package/dist/http/api/patchRequest.js +0 -18
  54. package/dist/http/api/postRequest.js +0 -18
  55. package/dist/http/api/putRequest.js +0 -18
  56. package/dist/http/badResponses/_badResponse.js +0 -62
  57. package/dist/http/badResponses/badGateway.js +0 -43
  58. package/dist/http/badResponses/badRequest.js +0 -41
  59. package/dist/http/badResponses/conflict.js +0 -42
  60. package/dist/http/badResponses/forbidden.js +0 -41
  61. package/dist/http/badResponses/notFound.js +0 -41
  62. package/dist/http/badResponses/notImplemented.js +0 -41
  63. package/dist/http/badResponses/serverError.js +0 -41
  64. package/dist/http/badResponses/unauthorized.js +0 -42
  65. package/dist/http/badResponses/unprocessableEntity.js +0 -53
  66. package/dist/http/successResponses/_successResponse.js +0 -75
  67. package/dist/http/successResponses/created.js +0 -41
  68. package/dist/http/successResponses/found.js +0 -41
  69. package/dist/http/successResponses/noContent.js +0 -32
  70. package/dist/http/successResponses/success.js +0 -41
  71. package/dist/http/successResponses/updated.js +0 -42
  72. package/dist/services/apiService.js +0 -174
  73. package/dist/services/debugService.js +0 -82
  74. package/dist/services/logMapperService.js +0 -68
  75. package/dist/services/logService.js +0 -35
  76. package/dist/utilities/decodeRequestBody.js +0 -40
  77. package/dist/utilities/decodeRequestErrorMessage.js +0 -36
  78. package/dist/utilities/errorHandler.js +0 -74
  79. package/dist/utilities/flushDebugLogs.js +0 -39
  80. package/dist/utilities/formAsyncParse.js +0 -37
  81. package/dist/utilities/formParse.js +0 -37
  82. package/dist/utilities/getScopedParams.js +0 -26
  83. package/dist/utilities/schemaValidator.js +0 -119
  84. package/dist/validations/validateCep.js +0 -27
  85. package/dist/validations/validateCnpj.js +0 -59
  86. package/dist/validations/validateCpf.js +0 -54
  87. package/dist/validations/validateDate.js +0 -51
  88. package/dist/validations/validateEmail.js +0 -111
  89. package/dist/validations/validatePassword.js +0 -34
  90. package/dist/validations/validatePhone.js +0 -28
  91. package/dist/validations/validateRg.js +0 -31
@@ -1,36 +0,0 @@
1
- /**
2
- * Extracts a human-readable error message from an API response body or a `Response` object.
3
- * Checks `data.message`, `data.operator_erro_message`, `data.error`, `data.error.message`,
4
- * and `response.statusText` in that order. Falls back to `"Missing error message"`.
5
- *
6
- * @param data - Parsed response body that may contain error info.
7
- * @param response - The raw `Response` object, used as a fallback for `statusText`.
8
- * @returns The first non-empty string error message found.
9
- *
10
- * @example
11
- * ```typescript
12
- * const res = await fetch("/api/orders");
13
- * const data = await res.json().catch(() => null);
14
- * const message = decodeRequestErrorMessage(data, res);
15
- * ```
16
- */
17
- function decodeRequestErrorMessage(data, response) {
18
- if (data?.message && typeof data?.message === "string") {
19
- return data?.message;
20
- }
21
- if (data?.operator_erro_message &&
22
- typeof data?.operator_erro_message === "string") {
23
- return data?.operator_erro_message;
24
- }
25
- if (data?.error && typeof data?.error === "string") {
26
- return data?.error;
27
- }
28
- if (data?.error?.message && typeof data?.error?.message === "string") {
29
- return data?.error?.message;
30
- }
31
- if (response?.statusText && typeof response?.statusText === "string") {
32
- return response?.statusText;
33
- }
34
- return "Missing error message";
35
- }
36
- export { decodeRequestErrorMessage };
@@ -1,74 +0,0 @@
1
- import { BadGateway } from "../http/badResponses/badGateway";
2
- import { BadRequest } from "../http/badResponses/badRequest";
3
- import { Conflict } from "../http/badResponses/conflict";
4
- import { Forbidden } from "../http/badResponses/forbidden";
5
- import { NotFound } from "../http/badResponses/notFound";
6
- import { NotImplemented } from "../http/badResponses/notImplemented";
7
- import { ServerError } from "../http/badResponses/serverError";
8
- import { Unauthorized } from "../http/badResponses/unauthorized";
9
- import { UnprocessableEntity } from "../http/badResponses/unprocessableEntity";
10
- import { Created } from "../http/successResponses/created";
11
- import { Found } from "../http/successResponses/found";
12
- import { NoContent } from "../http/successResponses/noContent";
13
- import { Success } from "../http/successResponses/success";
14
- import { Updated } from "../http/successResponses/updated";
15
- /**
16
- * Converts any thrown value into a `Response`. Recognizes all `@arkyn/server` success and error
17
- * response classes, native `Response` objects, and falls back to a 500 `ServerError` for anything else.
18
- *
19
- * Intended to be used as the catch handler of a route action or loader:
20
- *
21
- * @param error - The thrown value to convert.
22
- * @returns A `Response` with the appropriate HTTP status, headers, and JSON body.
23
- *
24
- * @example
25
- * ```typescript
26
- * export async function action({ request }: ActionFunctionArgs) {
27
- * try {
28
- * const user = await findUser(id);
29
- * if (!user) throw new NotFound("User not found");
30
- * return new Success("User retrieved", { user }).toJson();
31
- * } catch (error) {
32
- * return errorHandler(error);
33
- * }
34
- * }
35
- * ```
36
- */
37
- function errorHandler(error) {
38
- switch (true) {
39
- case error instanceof Response:
40
- return error;
41
- case error instanceof Found:
42
- return error.toResponse();
43
- case error instanceof Created:
44
- return error.toResponse();
45
- case error instanceof Updated:
46
- return error.toResponse();
47
- case error instanceof Success:
48
- return error.toResponse();
49
- case error instanceof NoContent:
50
- return error.toResponse();
51
- }
52
- switch (true) {
53
- case error instanceof BadGateway:
54
- return error.toResponse();
55
- case error instanceof BadRequest:
56
- return error.toResponse();
57
- case error instanceof Conflict:
58
- return error.toResponse();
59
- case error instanceof Forbidden:
60
- return error.toResponse();
61
- case error instanceof NotFound:
62
- return error.toResponse();
63
- case error instanceof NotImplemented:
64
- return error.toResponse();
65
- case error instanceof ServerError:
66
- return error.toResponse();
67
- case error instanceof Unauthorized:
68
- return error.toResponse();
69
- case error instanceof UnprocessableEntity:
70
- return error.toResponse();
71
- }
72
- return new ServerError("Server error", error).toResponse();
73
- }
74
- export { errorHandler };
@@ -1,39 +0,0 @@
1
- /**
2
- * Writes colored `[name] message` lines to the console, but only when
3
- * `NODE_ENV === "development"` or `DEBUG_MODE === "true"`. No-op in production.
4
- *
5
- * @param props.name - Label shown before each line (e.g. `"API"`, `"Auth"`).
6
- * @param props.scheme - Color of the label tag: `"cyan"` info, `"green"` success, `"yellow"` warning, `"red"` error.
7
- * @param props.debugs - Lines of text to print, one per console entry.
8
- *
9
- * @example
10
- * ```typescript
11
- * flushDebugLogs({
12
- * name: "API",
13
- * scheme: "cyan",
14
- * debugs: ["POST /api/users", "Status: 201"],
15
- * });
16
- * ```
17
- */
18
- function flushDebugLogs(props) {
19
- const isDebugMode = process.env.NODE_ENV === "development" ||
20
- process.env?.DEBUG_MODE === "true";
21
- if (isDebugMode) {
22
- const reset = "\x1b[0m";
23
- const colors = {
24
- yellow: "\x1b[33m",
25
- cyan: "\x1b[36m",
26
- red: "\x1b[31m",
27
- green: "\x1b[32m",
28
- };
29
- const debugName = `${colors[props.scheme]}[${props.name}]${reset}`;
30
- let consoleData = `\n`;
31
- props.debugs.forEach((debug, index) => {
32
- consoleData += `${debugName} ${debug.trim()}`;
33
- if (index < props.debugs.length - 1)
34
- consoleData += `\n`;
35
- });
36
- console.log(consoleData);
37
- }
38
- }
39
- export { flushDebugLogs };
@@ -1,37 +0,0 @@
1
- /**
2
- * Async variant of `formParse` — uses `safeParseAsync` to support Zod schemas with async refinements.
3
- * Returns `{ success: true, data }` on success or `{ success: false, fieldErrors, fields }` on failure.
4
- *
5
- * @param formData - The raw form data object to validate.
6
- * @param schema - The Zod schema to validate against.
7
- *
8
- * @example
9
- * ```typescript
10
- * const schema = z.object({ email: z.string().email() });
11
- * const result = await formAsyncParse([{ email: "bad" }, schema]);
12
- *
13
- * if (!result.success) {
14
- * console.log(result.fieldErrors); // { email: "Invalid email" }
15
- * }
16
- * ```
17
- */
18
- async function formAsyncParse([formData, schema,]) {
19
- const zodResponse = await schema.safeParseAsync(formData);
20
- if (zodResponse.success === false) {
21
- const errorsObject = Object.fromEntries(zodResponse.error.issues.map((item) => {
22
- return [item.path.join("."), item.message];
23
- }));
24
- return {
25
- success: zodResponse.success,
26
- fieldErrors: errorsObject,
27
- fields: formData,
28
- };
29
- }
30
- else {
31
- return {
32
- success: zodResponse.success,
33
- data: zodResponse.data,
34
- };
35
- }
36
- }
37
- export { formAsyncParse };
@@ -1,37 +0,0 @@
1
- /**
2
- * Validates form data against a Zod schema synchronously.
3
- * Returns `{ success: true, data }` on success or `{ success: false, fieldErrors, fields }` on failure.
4
- *
5
- * @param formData - The raw form data object to validate.
6
- * @param schema - The Zod schema to validate against.
7
- *
8
- * @example
9
- * ```typescript
10
- * const schema = z.object({ name: z.string().min(1, "Required"), age: z.number().min(18) });
11
- * const result = formParse([{ name: "", age: 15 }, schema]);
12
- *
13
- * if (!result.success) {
14
- * console.log(result.fieldErrors); // { name: "Required", age: "..." }
15
- * }
16
- * ```
17
- */
18
- function formParse([formData, schema,]) {
19
- const zodResponse = schema.safeParse(formData);
20
- if (zodResponse.success === false) {
21
- const errorsObject = Object.fromEntries(zodResponse.error.issues.map((item) => {
22
- return [item.path.join("."), item.message];
23
- }));
24
- return {
25
- success: zodResponse.success,
26
- fieldErrors: errorsObject,
27
- fields: formData,
28
- };
29
- }
30
- else {
31
- return {
32
- success: zodResponse.success,
33
- data: zodResponse.data,
34
- };
35
- }
36
- }
37
- export { formParse };
@@ -1,26 +0,0 @@
1
- /**
2
- * Extracts URL search parameters from a request, optionally filtered by a namespace prefix
3
- * (e.g. `scope:key` → `key`). Without a scope, returns all search params as-is.
4
- *
5
- * @param request - The incoming request whose URL will be parsed.
6
- * @param scope - Namespace prefix to filter by (e.g. `"table"` matches `table:page`, `table:sort`).
7
- * @returns A `URLSearchParams` object with the matching params, stripped of the scope prefix.
8
- *
9
- * @example
10
- * ```typescript
11
- * // URL: /products?table:page=2&table:sort=asc&other=1
12
- * const params = getScopedParams(request, "table");
13
- * params.get("page"); // "2"
14
- * params.get("sort"); // "asc"
15
- * ```
16
- */
17
- function getScopedParams(request, scope = "") {
18
- const url = new URL(request.url);
19
- if (scope === "")
20
- return url.searchParams;
21
- const scopedSearchParams = Array.from(url.searchParams.entries())
22
- .filter(([key]) => key.startsWith(`${scope}:`))
23
- .map(([key, value]) => [key.replace(`${scope}:`, ""), value]);
24
- return new URLSearchParams(scopedSearchParams);
25
- }
26
- export { getScopedParams };
@@ -1,119 +0,0 @@
1
- import { ServerError } from "../http/badResponses/serverError";
2
- import { UnprocessableEntity } from "../http/badResponses/unprocessableEntity";
3
- import { formAsyncParse } from "./formAsyncParse";
4
- import { formParse } from "./formParse";
5
- function formatErrorMessage(error) {
6
- const title = "Error validating:";
7
- const lines = error.issues.map(({ path, message }) => `-> ${path.join(".")}: ${message}`);
8
- return [title, ...lines].join("\n");
9
- }
10
- /**
11
- * Wraps a Zod schema with convenience validation methods suited for server-side use:
12
- * - `isValid` — boolean check, no throws
13
- * - `safeValidate` — raw Zod result, no throws
14
- * - `validate` — throws `ServerError` on failure (for trusted/internal data)
15
- * - `formValidate` / `formAsyncValidate` — throws `UnprocessableEntity` on failure (for user-submitted forms)
16
- *
17
- * @example
18
- * ```typescript
19
- * const validator = new SchemaValidator(z.object({ email: z.string().email() }));
20
- *
21
- * // Inside a Remix action:
22
- * const body = validator.formValidate(await decodeRequestBody(request));
23
- * ```
24
- */
25
- class SchemaValidator {
26
- schema;
27
- /**
28
- * @param schema - The Zod schema used for all validation methods on this instance.
29
- */
30
- constructor(schema) {
31
- this.schema = schema;
32
- }
33
- /**
34
- * Returns `true` if the data satisfies the schema, `false` otherwise. Never throws.
35
- *
36
- * @param data - The value to check.
37
- */
38
- isValid(data) {
39
- return this.schema.safeParse(data).success;
40
- }
41
- /**
42
- * Validates data and returns the raw Zod `safeParseResult` without throwing.
43
- * Useful when you need access to the full error details.
44
- *
45
- * @param data - The value to validate.
46
- */
47
- safeValidate(data) {
48
- return this.schema.safeParse(data);
49
- }
50
- /**
51
- * Validates data and returns the typed result, throwing `ServerError` on failure.
52
- * Use for validating internal/trusted data (e.g. env vars, config objects).
53
- *
54
- * @param data - The value to validate.
55
- * @throws `ServerError` with a formatted field-by-field error message.
56
- */
57
- validate(data) {
58
- try {
59
- return this.schema.parse(data);
60
- }
61
- catch (error) {
62
- throw new ServerError(formatErrorMessage(error));
63
- }
64
- }
65
- /**
66
- * Validates form data and returns the typed result, throwing `UnprocessableEntity` on failure.
67
- * The error includes `fieldErrors`, `fields`, and `data.scrollTo` (first failing field name).
68
- *
69
- * @param data - The raw form data to validate.
70
- * @param message - Optional human-readable error message for the 422 response.
71
- * @throws `UnprocessableEntity` with structured field errors for client-side form handling.
72
- *
73
- * @example
74
- * ```typescript
75
- * const validator = new SchemaValidator(registerSchema);
76
- * const body = validator.formValidate(await decodeRequestBody(request));
77
- * ```
78
- */
79
- formValidate(data, message) {
80
- const formParsed = formParse([data, this.schema]);
81
- if (!formParsed.success) {
82
- const firstErrorKey = Object.keys(formParsed.fieldErrors)[0];
83
- throw new UnprocessableEntity({
84
- fields: formParsed.fields,
85
- fieldErrors: formParsed.fieldErrors,
86
- data: { scrollTo: firstErrorKey },
87
- message,
88
- });
89
- }
90
- return formParsed.data;
91
- }
92
- /**
93
- * Async version of `formValidate` for schemas with async refinements (e.g. uniqueness checks).
94
- *
95
- * @param data - The raw form data to validate.
96
- * @param message - Optional human-readable error message for the 422 response.
97
- * @throws `UnprocessableEntity` with structured field errors for client-side form handling.
98
- *
99
- * @example
100
- * ```typescript
101
- * const validator = new SchemaValidator(registerSchema);
102
- * const body = await validator.formAsyncValidate(await decodeRequestBody(request));
103
- * ```
104
- */
105
- async formAsyncValidate(data, message) {
106
- const formParsed = await formAsyncParse([data, this.schema]);
107
- if (!formParsed.success) {
108
- const firstErrorKey = Object.keys(formParsed.fieldErrors)[0];
109
- throw new UnprocessableEntity({
110
- fields: formParsed.fields,
111
- fieldErrors: formParsed.fieldErrors,
112
- data: { scrollTo: firstErrorKey },
113
- message,
114
- });
115
- }
116
- return formParsed.data;
117
- }
118
- }
119
- export { SchemaValidator };
@@ -1,27 +0,0 @@
1
- import { removeNonNumeric } from "@arkyn/shared";
2
- /**
3
- * Validates a Brazilian CEP (postal code).
4
- *
5
- * A valid CEP must contain exactly 8 numeric digits,
6
- * optionally formatted as "12345-678".
7
- *
8
- * @param rawCep - CEP value, with or without formatting.
9
- * @returns `true` if the CEP is valid, otherwise `false`.
10
- *
11
- * @example
12
- * ```typescript
13
- * validateCep("12345-678"); // true
14
- * validateCep("12345678"); // true
15
- * validateCep("ABCDE-123"); // false
16
- * ```
17
- */
18
- function validateCep(rawCep) {
19
- const validFormat = /^\d{5}-\d{3}$/.test(rawCep) || /^\d{8}$/.test(rawCep);
20
- if (!validFormat)
21
- return false;
22
- const cep = removeNonNumeric(rawCep);
23
- const CEP_LENGTH = 8;
24
- const isOnlyDigits = /^\d{8}$/.test(cep);
25
- return cep.length === CEP_LENGTH && isOnlyDigits;
26
- }
27
- export { validateCep };
@@ -1,59 +0,0 @@
1
- import { removeNonNumeric } from "@arkyn/shared";
2
- function isInvalidLength(cnpj) {
3
- const CNPJ_LENGTH = 14;
4
- return cnpj.length !== CNPJ_LENGTH;
5
- }
6
- function hasAllDigitsEqual(cnpj) {
7
- const [firstDigit] = cnpj;
8
- return [...cnpj].every((digit) => digit === firstDigit);
9
- }
10
- function calculateDigit(cnpj, multipliers) {
11
- let total = 0;
12
- for (let i = 0; i < multipliers.length; i++) {
13
- total += parseInt(cnpj[i]) * multipliers[i];
14
- }
15
- const rest = total % 11;
16
- return rest < 2 ? 0 : 11 - rest;
17
- }
18
- function extractDigit(cnpj) {
19
- return cnpj.slice(12);
20
- }
21
- /**
22
- * Validates a Brazilian CNPJ (Cadastro Nacional da Pessoa Jurídica) number.
23
- *
24
- * This function performs:
25
- * - Sanitization (removes non-digit characters).
26
- * - Length check (must be 14 digits).
27
- * - Repeating digits check (invalid if all digits are the same).
28
- * - Verifies the two check digits with the proper weights.
29
- *
30
- * @param rawCnpj - CNPJ string, possibly formatted.
31
- * @returns `true` if valid, otherwise `false`.
32
- *
33
- * @example
34
- * ```typescript
35
- * validateCnpj("12.345.678/0001-95"); // false
36
- * validateCnpj("11.444.777/0001-61"); // true
37
- * ```
38
- */
39
- function validateCnpj(rawCnpj) {
40
- if (!rawCnpj)
41
- return false;
42
- if (rawCnpj.length > 18)
43
- return false;
44
- if (rawCnpj.length < 14)
45
- return false;
46
- const hasSpaces = /\s/.test(rawCnpj);
47
- if (hasSpaces)
48
- return false;
49
- const cnpj = removeNonNumeric(rawCnpj);
50
- if (isInvalidLength(cnpj))
51
- return false;
52
- if (hasAllDigitsEqual(cnpj))
53
- return false;
54
- const base = cnpj.slice(0, 12);
55
- const digit1 = calculateDigit(base, [5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2]);
56
- const digit2 = calculateDigit(base + digit1, [6, 5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2]);
57
- return extractDigit(cnpj) === `${digit1}${digit2}`;
58
- }
59
- export { validateCnpj };
@@ -1,54 +0,0 @@
1
- import { removeNonNumeric } from "@arkyn/shared";
2
- function isInvalidLength(cpf) {
3
- const CPF_LENGTH = 11;
4
- return cpf.length !== CPF_LENGTH;
5
- }
6
- function hasAllDigitsEqual(cpf) {
7
- const [firstCpfDigit] = cpf;
8
- return [...cpf].every((digit) => digit === firstCpfDigit);
9
- }
10
- function calculateDigit(cpf, factor) {
11
- let total = 0;
12
- for (const digit of cpf) {
13
- if (factor > 1)
14
- total += parseInt(digit) * factor--;
15
- }
16
- const rest = total % 11;
17
- return rest < 2 ? 0 : 11 - rest;
18
- }
19
- function extractDigit(cpf) {
20
- return cpf.slice(9);
21
- }
22
- /**
23
- * Validates a Brazilian CPF number. Strips formatting, checks length, rejects
24
- * repeated-digit sequences, and verifies both check digits with the CPF algorithm.
25
- *
26
- * @param rawCpf - CPF string, with or without formatting (dots and dashes).
27
- * @returns `true` if the CPF is valid, otherwise `false`.
28
- *
29
- * @example
30
- * ```typescript
31
- * validateCpf("123.456.789-09"); // false
32
- * validateCpf("111.444.777-35"); // true
33
- * ```
34
- */
35
- function validateCpf(rawCpf) {
36
- if (!rawCpf)
37
- return false;
38
- if (rawCpf.length > 14)
39
- return false;
40
- if (rawCpf.length < 11)
41
- return false;
42
- const hasSpaces = /\s/.test(rawCpf);
43
- if (hasSpaces)
44
- return false;
45
- const cpf = removeNonNumeric(rawCpf);
46
- if (isInvalidLength(cpf))
47
- return false;
48
- if (hasAllDigitsEqual(cpf))
49
- return false;
50
- const digit1 = calculateDigit(cpf, 10);
51
- const digit2 = calculateDigit(cpf, 11);
52
- return extractDigit(cpf) === `${digit1}${digit2}`;
53
- }
54
- export { validateCpf };
@@ -1,51 +0,0 @@
1
- import { ValidateDateService } from "@arkyn/shared";
2
- /**
3
- * Validates a date string against a format and optional year bounds.
4
- *
5
- * @param date - The date string to validate.
6
- * @param config.inputFormat - Parsing format: `"brazilianDate"` (DD/MM/YYYY, default), `"isoDate"` (MM-DD-YYYY), or `"timestamp"` (YYYY-MM-DD).
7
- * @param config.minYear - Minimum allowed year. Defaults to 1900.
8
- * @param config.maxYear - Maximum allowed year. Defaults to 3000.
9
- * @returns `true` if the date is valid according to the format and bounds, otherwise `false`.
10
- *
11
- * @example
12
- * ```typescript
13
- * validateDate("31/12/2023"); // true
14
- * validateDate("2023-12-31", { inputFormat: "timestamp", minYear: 2000, maxYear: 2100 }); // true
15
- * validateDate("29/02/2023"); // false (not a leap year)
16
- * ```
17
- */
18
- function validateDate(date, config) {
19
- const inputFormat = config?.inputFormat || "brazilianDate";
20
- const minYear = config?.minYear || 1900;
21
- const maxYear = config?.maxYear || 3000;
22
- const validateDateService = new ValidateDateService();
23
- validateDateService.validateInputFormat(inputFormat);
24
- let day, month, year;
25
- const dateParts = date.split(/[-/]/).map(Number);
26
- if (dateParts.length !== 3)
27
- return false;
28
- try {
29
- switch (inputFormat) {
30
- case "brazilianDate":
31
- [day, month, year] = dateParts;
32
- validateDateService.validateDateParts(year, month, day);
33
- break;
34
- case "isoDate":
35
- [month, day, year] = dateParts;
36
- validateDateService.validateDateParts(year, month, day);
37
- break;
38
- case "timestamp":
39
- [year, month, day] = dateParts;
40
- validateDateService.validateDateParts(year, month, day);
41
- break;
42
- }
43
- if (year < minYear || year > maxYear)
44
- return false;
45
- return true;
46
- }
47
- catch {
48
- return false;
49
- }
50
- }
51
- export { validateDate };
@@ -1,111 +0,0 @@
1
- import dns from "node:dns";
2
- function isValidBasicFormat(email) {
3
- const emailRegex = /^[a-zA-Z0-9.!$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/;
4
- return emailRegex.test(email);
5
- }
6
- function isValidLocalPart(localPart) {
7
- if (localPart.length === 0 || localPart.length > 64)
8
- return false;
9
- if (localPart.startsWith(".") || localPart.endsWith("."))
10
- return false;
11
- if (localPart.includes(".."))
12
- return false;
13
- const validLocalRegex = /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+$/;
14
- if (!validLocalRegex.test(localPart))
15
- return false;
16
- return true;
17
- }
18
- function isValidDomainLabel(label) {
19
- if (label.length === 0 || label.length > 63)
20
- return false;
21
- if (label.startsWith("-") || label.endsWith("-"))
22
- return false;
23
- if (!/^[a-zA-Z0-9-]+$/.test(label))
24
- return false;
25
- return true;
26
- }
27
- function isValidDomainPart(domainPart) {
28
- if (domainPart.length === 0 || domainPart.length > 253)
29
- return false;
30
- if (domainPart.startsWith(".") ||
31
- domainPart.endsWith(".") ||
32
- domainPart.startsWith("-") ||
33
- domainPart.endsWith("-")) {
34
- return false;
35
- }
36
- const labels = domainPart.split(".");
37
- if (labels.length < 2)
38
- return false;
39
- for (const label of labels)
40
- if (!isValidDomainLabel(label))
41
- return false;
42
- const tld = labels[labels.length - 1];
43
- if (tld.length < 2 || !/^[a-zA-Z]+$/.test(tld))
44
- return false;
45
- return true;
46
- }
47
- function isValidAdvancedSyntax(email) {
48
- const parts = email.split("@");
49
- if (parts.length !== 2)
50
- return false;
51
- const [localPart, domainPart] = parts;
52
- if (!isValidLocalPart(localPart))
53
- return false;
54
- if (!isValidDomainPart(domainPart))
55
- return false;
56
- return true;
57
- }
58
- function extractDomain(email) {
59
- const parts = email.split("@");
60
- return parts.length === 2 ? parts[1].toLowerCase() : null;
61
- }
62
- const DNS_RECORD_TYPES = ["MX", "A", "AAAA"];
63
- async function tryResolveDnsRecord(domain, recordType) {
64
- try {
65
- await dns?.promises?.resolve(domain, recordType);
66
- return true;
67
- }
68
- catch {
69
- return false;
70
- }
71
- }
72
- async function isValidDns(domain) {
73
- for (const recordType of DNS_RECORD_TYPES) {
74
- const hasRecord = await tryResolveDnsRecord(domain, recordType);
75
- if (hasRecord)
76
- return true;
77
- }
78
- return false;
79
- }
80
- /**
81
- * Validates if an email address is valid in all possible ways, including DNS validation.
82
- *
83
- * This function performs comprehensive email validation by:
84
- * - Checking basic email format and syntax
85
- * - Validating advanced RFC 5322 compliance rules
86
- * - Verifying that the domain has valid MX or A records in DNS
87
- *
88
- * @param rawEmail - The email address to validate.
89
- * @returns `true` if the email passes format checks and its domain resolves in DNS, otherwise `false`.
90
- *
91
- * @example
92
- * ```typescript
93
- * await validateEmail("user@gmail.com"); // true
94
- * await validateEmail("user@gmil.com"); // false (invalid domain)
95
- * await validateEmail("invalid-email"); // false (invalid format)
96
- * ```
97
- */
98
- async function validateEmail(rawEmail) {
99
- if (!rawEmail || typeof rawEmail !== "string")
100
- return false;
101
- const email = rawEmail.trim();
102
- if (!isValidBasicFormat(email))
103
- return false;
104
- if (!isValidAdvancedSyntax(email))
105
- return false;
106
- const domain = extractDomain(email);
107
- if (!domain)
108
- return false;
109
- return await isValidDns(domain);
110
- }
111
- export { validateEmail };