@excofy/utils 2.1.1 → 2.2.0

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/dist/index.cjs CHANGED
@@ -96,6 +96,30 @@ function isValidCNPJ(cnpj) {
96
96
  return digit1 === digits[1];
97
97
  }
98
98
 
99
+ // src/helpers/file.ts
100
+ var fileUtils = {
101
+ base64ToFile: (base64, filename) => {
102
+ if (!base64 || base64 === "") {
103
+ return null;
104
+ }
105
+ const arr = base64.split(",");
106
+ if (arr.length !== 2) {
107
+ return null;
108
+ }
109
+ const mime = arr[0].match(/:(.*?);/)?.[1];
110
+ if (!mime) {
111
+ return null;
112
+ }
113
+ const bstr = atob(arr[1]);
114
+ let n = bstr.length;
115
+ const u8arr = new Uint8Array(n);
116
+ while (n--) {
117
+ u8arr[n] = bstr.charCodeAt(n);
118
+ }
119
+ return new File([u8arr], filename, { type: mime });
120
+ }
121
+ };
122
+
99
123
  // src/helpers/sanitize.ts
100
124
  var import_xss = require("xss");
101
125
  var allTags = [
@@ -234,6 +258,8 @@ function createValidator() {
234
258
  isValidType = typeof value === "object" && value !== null && !Array.isArray(value);
235
259
  } else if (type === "file") {
236
260
  isValidType = value instanceof File;
261
+ } else if (type === "base64") {
262
+ isValidType = typeof value === "string" && !!value.match(/^data:([A-Za-z-+\/]+);base64,([A-Za-z0-9+/=]+)$/);
237
263
  } else if (type === "date") {
238
264
  isValidType = typeof value === "string" && !Number.isNaN(new Date(value).getTime());
239
265
  } else {
@@ -592,6 +618,22 @@ function createValidator() {
592
618
  current.inputs[current.field] = arr;
593
619
  }
594
620
  return validator;
621
+ },
622
+ asFileFromBase64(message, filename) {
623
+ if (shouldSkipValidation()) return validator;
624
+ const value = current.value;
625
+ if (typeof value !== "string") {
626
+ current.pushError(message);
627
+ return validator;
628
+ }
629
+ if (!value.match(/^data:([A-Za-z-+\/]+);base64,([A-Za-z0-9+/=]+)$/)) {
630
+ current.pushError(message);
631
+ return validator;
632
+ }
633
+ const file = fileUtils.base64ToFile(value, filename);
634
+ current.value = file;
635
+ current.inputs[current.field] = file;
636
+ return validator;
595
637
  }
596
638
  };
597
639
  return {
@@ -659,8 +701,8 @@ function createValidator() {
659
701
 
660
702
  // src/helpers/generateCode.ts
661
703
  var generateCode = (length = 6, type = "alphanumeric") => {
662
- const digits = "0123456789";
663
- const letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
704
+ const digits = "123456789";
705
+ const letters = "ABCDEFGHIJKLMNPQRSTUVWXYZ";
664
706
  let charset;
665
707
  switch (type) {
666
708
  case "numeric":
package/dist/index.d.cts CHANGED
@@ -13,7 +13,7 @@ type TMessage = {
13
13
  };
14
14
  type TValue = string | number | boolean | File | object | null | undefined;
15
15
  type TInputValue = TValue;
16
- type TTypes = 'string' | 'number' | 'boolean' | 'object' | 'array' | 'file' | 'date';
16
+ type TTypes = 'string' | 'number' | 'boolean' | 'object' | 'array' | 'file' | 'base64' | 'date';
17
17
  interface IInputErrors {
18
18
  [key: string]: TMessage[] | IInputErrors[];
19
19
  }
@@ -48,6 +48,7 @@ interface ValidatorField {
48
48
  asNumber(message: string): ValidatorField;
49
49
  asBoolean(message: string): ValidatorField;
50
50
  asStringArray(message: string): ValidatorField;
51
+ asFileFromBase64(message: string, filename: string): ValidatorField;
51
52
  }
52
53
  /**
53
54
  * Cria um validador de inputs com suporte a validações encadeadas, transformação e sanitização.
package/dist/index.d.ts CHANGED
@@ -13,7 +13,7 @@ type TMessage = {
13
13
  };
14
14
  type TValue = string | number | boolean | File | object | null | undefined;
15
15
  type TInputValue = TValue;
16
- type TTypes = 'string' | 'number' | 'boolean' | 'object' | 'array' | 'file' | 'date';
16
+ type TTypes = 'string' | 'number' | 'boolean' | 'object' | 'array' | 'file' | 'base64' | 'date';
17
17
  interface IInputErrors {
18
18
  [key: string]: TMessage[] | IInputErrors[];
19
19
  }
@@ -48,6 +48,7 @@ interface ValidatorField {
48
48
  asNumber(message: string): ValidatorField;
49
49
  asBoolean(message: string): ValidatorField;
50
50
  asStringArray(message: string): ValidatorField;
51
+ asFileFromBase64(message: string, filename: string): ValidatorField;
51
52
  }
52
53
  /**
53
54
  * Cria um validador de inputs com suporte a validações encadeadas, transformação e sanitização.
package/dist/index.js CHANGED
@@ -58,6 +58,30 @@ function isValidCNPJ(cnpj) {
58
58
  return digit1 === digits[1];
59
59
  }
60
60
 
61
+ // src/helpers/file.ts
62
+ var fileUtils = {
63
+ base64ToFile: (base64, filename) => {
64
+ if (!base64 || base64 === "") {
65
+ return null;
66
+ }
67
+ const arr = base64.split(",");
68
+ if (arr.length !== 2) {
69
+ return null;
70
+ }
71
+ const mime = arr[0].match(/:(.*?);/)?.[1];
72
+ if (!mime) {
73
+ return null;
74
+ }
75
+ const bstr = atob(arr[1]);
76
+ let n = bstr.length;
77
+ const u8arr = new Uint8Array(n);
78
+ while (n--) {
79
+ u8arr[n] = bstr.charCodeAt(n);
80
+ }
81
+ return new File([u8arr], filename, { type: mime });
82
+ }
83
+ };
84
+
61
85
  // src/helpers/sanitize.ts
62
86
  import { FilterXSS } from "xss";
63
87
  var allTags = [
@@ -196,6 +220,8 @@ function createValidator() {
196
220
  isValidType = typeof value === "object" && value !== null && !Array.isArray(value);
197
221
  } else if (type === "file") {
198
222
  isValidType = value instanceof File;
223
+ } else if (type === "base64") {
224
+ isValidType = typeof value === "string" && !!value.match(/^data:([A-Za-z-+\/]+);base64,([A-Za-z0-9+/=]+)$/);
199
225
  } else if (type === "date") {
200
226
  isValidType = typeof value === "string" && !Number.isNaN(new Date(value).getTime());
201
227
  } else {
@@ -554,6 +580,22 @@ function createValidator() {
554
580
  current.inputs[current.field] = arr;
555
581
  }
556
582
  return validator;
583
+ },
584
+ asFileFromBase64(message, filename) {
585
+ if (shouldSkipValidation()) return validator;
586
+ const value = current.value;
587
+ if (typeof value !== "string") {
588
+ current.pushError(message);
589
+ return validator;
590
+ }
591
+ if (!value.match(/^data:([A-Za-z-+\/]+);base64,([A-Za-z0-9+/=]+)$/)) {
592
+ current.pushError(message);
593
+ return validator;
594
+ }
595
+ const file = fileUtils.base64ToFile(value, filename);
596
+ current.value = file;
597
+ current.inputs[current.field] = file;
598
+ return validator;
557
599
  }
558
600
  };
559
601
  return {
@@ -621,8 +663,8 @@ function createValidator() {
621
663
 
622
664
  // src/helpers/generateCode.ts
623
665
  var generateCode = (length = 6, type = "alphanumeric") => {
624
- const digits = "0123456789";
625
- const letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
666
+ const digits = "123456789";
667
+ const letters = "ABCDEFGHIJKLMNPQRSTUVWXYZ";
626
668
  let charset;
627
669
  switch (type) {
628
670
  case "numeric":
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@excofy/utils",
3
- "version": "2.1.1",
3
+ "version": "2.2.0",
4
4
  "description": "Biblioteca de utilitários para o Excofy",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",
@@ -16,7 +16,7 @@
16
16
  "typecheck": "tsc --noEmit",
17
17
  "prepublishOnly": "npm run build",
18
18
  "deploy": "act",
19
- "test": "tsx --test 'tests/**/*.spec.ts' --testTimeout=10000"
19
+ "test": "tsx --test 'tests/helpers/crypto.spec.ts' --testTimeout=10000"
20
20
  },
21
21
  "repository": {
22
22
  "type": "git",
@@ -153,7 +153,7 @@ export async function verifyDeterministicHash(
153
153
  export const cryptoUtils: ICrypto = {
154
154
  uuidV4: (): string => crypto.randomUUID(),
155
155
 
156
- hash: async (password: string, salt: number = 10): Promise<string> => {
156
+ hash: async (password: string, salt = 10): Promise<string> => {
157
157
  const importedKey = await crypto.subtle.importKey(
158
158
  'raw',
159
159
  encoder.encode(password),
@@ -0,0 +1,28 @@
1
+ export const fileUtils = {
2
+ base64ToFile: (base64: string | null, filename: string): File | null => {
3
+ if (!base64 || base64 === '') {
4
+ return null;
5
+ }
6
+
7
+ const arr = base64.split(',');
8
+ if (arr.length !== 2) {
9
+ return null;
10
+ }
11
+
12
+ const mime = arr[0].match(/:(.*?);/)?.[1];
13
+
14
+ if (!mime) {
15
+ return null;
16
+ }
17
+
18
+ const bstr = atob(arr[1]);
19
+ let n = bstr.length;
20
+ const u8arr = new Uint8Array(n);
21
+
22
+ while (n--) {
23
+ u8arr[n] = bstr.charCodeAt(n);
24
+ }
25
+
26
+ return new File([u8arr], filename, { type: mime });
27
+ },
28
+ };
@@ -18,8 +18,8 @@ export const generateCode = (
18
18
  length = 6,
19
19
  type: 'alphanumeric' | 'numeric' | 'letters' = 'alphanumeric'
20
20
  ): string => {
21
- const digits = '0123456789';
22
- const letters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
21
+ const digits = '123456789';
22
+ const letters = 'ABCDEFGHIJKLMNPQRSTUVWXYZ';
23
23
  let charset: string;
24
24
 
25
25
  switch (type) {
@@ -1,4 +1,5 @@
1
1
  import { isValidCNPJ, isValidCPF } from './document';
2
+ import { fileUtils } from './file';
2
3
  import { sanitizeValue, type AllowedTag } from './sanitize';
3
4
  import { video } from './video';
4
5
 
@@ -12,6 +13,7 @@ type TTypes =
12
13
  | 'object'
13
14
  | 'array'
14
15
  | 'file'
16
+ | 'base64'
15
17
  | 'date';
16
18
 
17
19
  interface IInputErrors {
@@ -56,6 +58,7 @@ interface ValidatorField {
56
58
  asNumber(message: string): ValidatorField;
57
59
  asBoolean(message: string): ValidatorField;
58
60
  asStringArray(message: string): ValidatorField;
61
+ asFileFromBase64(message: string, filename: string): ValidatorField;
59
62
  }
60
63
 
61
64
  /**
@@ -139,6 +142,10 @@ export function createValidator<
139
142
  typeof value === 'object' && value !== null && !Array.isArray(value);
140
143
  } else if (type === 'file') {
141
144
  isValidType = value instanceof File;
145
+ } else if (type === 'base64') {
146
+ isValidType =
147
+ typeof value === 'string' &&
148
+ !!value.match(/^data:([A-Za-z-+\/]+);base64,([A-Za-z0-9+/=]+)$/);
142
149
  } else if (type === 'date') {
143
150
  isValidType =
144
151
  typeof value === 'string' && !Number.isNaN(new Date(value).getTime());
@@ -629,6 +636,28 @@ export function createValidator<
629
636
 
630
637
  return validator;
631
638
  },
639
+
640
+ asFileFromBase64(message: string, filename: string) {
641
+ if (shouldSkipValidation()) return validator;
642
+
643
+ const value = current.value;
644
+
645
+ if (typeof value !== 'string') {
646
+ current.pushError(message);
647
+ return validator;
648
+ }
649
+
650
+ if (!value.match(/^data:([A-Za-z-+\/]+);base64,([A-Za-z0-9+/=]+)$/)) {
651
+ current.pushError(message);
652
+ return validator;
653
+ }
654
+
655
+ const file = fileUtils.base64ToFile(value, filename);
656
+ current.value = file;
657
+ current.inputs[current.field] = file as TRaw[keyof TRaw];
658
+
659
+ return validator;
660
+ },
632
661
  };
633
662
 
634
663
  return {
@@ -0,0 +1,22 @@
1
+ import { describe, it } from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ import { cryptoUtils } from '../../src/helpers/crypto';
4
+
5
+ const AUTH_PAYLOAD_SECRET = '98c026b8-80d7-43ac-80d0-5118cb9c0102';
6
+ const code = 'N3HDL5';
7
+
8
+ describe('cryptoUtils', () => {
9
+ it('uuidV4 deve gerar um UUID válido', async () => {
10
+ const encrypted = await cryptoUtils.encryptPayload({
11
+ AUTH_PAYLOAD_SECRET,
12
+ payload: code,
13
+ });
14
+
15
+ const hash = await cryptoUtils.generateDeterministicHash(
16
+ code,
17
+ AUTH_PAYLOAD_SECRET
18
+ );
19
+
20
+ console.log({ encrypted, hash });
21
+ });
22
+ });