@thirstie/thirstievalidators 0.0.6 → 0.1.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thirstie/thirstievalidators",
3
- "version": "0.0.6",
3
+ "version": "0.1.0",
4
4
  "description": "A collection of data validators used by Thirstiejs modules",
5
5
  "author": "Thirstie, Inc. <technology@thirstie.com>",
6
6
  "license": "MIT",
@@ -22,6 +22,9 @@
22
22
  "test": "concurrently \"npm run lint\" \"npm run test:coverage\"",
23
23
  "test:watch": "node --experimental-vm-modules ../../node_modules/.bin/jest unit --watch"
24
24
  },
25
+ "dependencies": {
26
+ "zod": "^3.23.8"
27
+ },
25
28
  "jest": {
26
29
  "moduleFileExtensions": [
27
30
  "js",
@@ -30,5 +33,5 @@
30
33
  "testEnvironment": "jsdom",
31
34
  "transform": {}
32
35
  },
33
- "gitHead": "b7400621954f917a80a9a530ea89cc2d41aa6373"
36
+ "gitHead": "3e6f54e763d04240e244a4c085488c2b4a61ff4b"
34
37
  }
package/src/agegate.js ADDED
@@ -0,0 +1,31 @@
1
+ import { z } from 'zod';
2
+
3
+ export const ageGate = (ageGateType, ageGateValue, legalAge = null) => {
4
+ const ageGateTypeValidation = z.enum([ 'yesno', 'dob' ]);
5
+ const ageDateValidation = z.coerce.date();
6
+ legalAge = legalAge || 21;
7
+
8
+ let _type = null;
9
+ try {
10
+ _type = ageGateTypeValidation.parse(ageGateType.toLowerCase());
11
+ } catch {
12
+ return {};
13
+ }
14
+
15
+ let _passed = false;
16
+ let { success, data: userDOB } = ageDateValidation.safeParse(ageGateValue);
17
+ switch (_type) {
18
+ case 'yesno':
19
+ _passed = !!ageGateValue;
20
+ userDOB = null;
21
+ break;
22
+ case 'dob':
23
+ if (success) {
24
+ const today = new Date();
25
+ const legalAsOf = new Date(userDOB.getFullYear() + legalAge, userDOB.getMonth(), userDOB.getDate());
26
+ _passed = !legalAsOf ? false : legalAsOf <= today;
27
+ }
28
+ break;
29
+ }
30
+ return { ageGateType: _type, ageGatePassed: _passed, ageGateDOB: userDOB || null };
31
+ };
@@ -0,0 +1,58 @@
1
+ export const emailTypoList = [
2
+ 'gnail.co',
3
+ 'gmil.com',
4
+ 'gmsil.com',
5
+ 'gamil.com',
6
+ 'gmai.com',
7
+ 'gnail.com',
8
+ 'gmail.co',
9
+ 'gmail.cm',
10
+ 'gmail.co',
11
+ 'gmal.com',
12
+ 'gmaill.com',
13
+ 'gmail.ca',
14
+ 'gmal.com',
15
+ 'gmai.com',
16
+ 'gmaiil.com',
17
+ 'gmail.con',
18
+ 'gmail.comm',
19
+ 'gmaol.com',
20
+ 'gmail.om',
21
+ 'gmaol.co',
22
+ 'gmall.com',
23
+ 'gmal.co',
24
+ 'ail.com'
25
+ ];
26
+
27
+ export const trustedEmailDomains = [
28
+ 'gmail.com',
29
+ 'yahoo.com',
30
+ 'hotmail.com',
31
+ 'aol.com',
32
+ 'comcast.net',
33
+ 'icloud.com',
34
+ 'me.com',
35
+ 'msn.com',
36
+ 'outlook.com',
37
+ 'sbcglobal.net',
38
+ 'live.com',
39
+ 'att.net',
40
+ 'verizon.net',
41
+ 'mac.com',
42
+ 'ymail.com',
43
+ 'optonline.net',
44
+ 'cox.net',
45
+ 'bellsouth.net',
46
+ 'charter.net',
47
+ 'anheuser-busch.com',
48
+ 'mail.com',
49
+ 'rocketmail.com',
50
+ 'earthlink.net',
51
+ 'aim.com',
52
+ 'cbrands.com',
53
+ 'kdrp.com',
54
+ 'protonmail.com',
55
+ 'roadrunner.com',
56
+ 'pacbell.net',
57
+ 'yopmail.com'
58
+ ];
package/src/index.js CHANGED
@@ -1,12 +1,28 @@
1
1
  import passwordcheck from './passwordcheck';
2
+ import { getAdministrativeArea, COUNTRY_CODES } from './iso3166codes';
3
+ import { emailTypoList, trustedEmailDomains } from './emailLists';
4
+ import { ageGate } from './agegate';
2
5
 
3
6
  // see: https://github.com/ThirstieAdmin/archived-repos/tree/master/thirstie-templates/react-cookbook/src/utils
4
- // TODO: research using https://www.npmjs.com/package/validator
5
- const thirstieValidators = {
7
+ // Validator must return true or false
8
+ const Validators = {
6
9
  email: (value, options = null) => {
10
+ const checkTypeoList = (options && options.checkTypeos) ? options.checkTypeos : false;
7
11
  const emailRegex = /^(?![.!#$%&'*+/=?^_`{|}~-])(?!.*\.{2})[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]{1,64}@[a-zA-Z0-9]{1,255}\.[a-zA-Z]{2,}$/;
8
- // TODO: add domain checks
9
- return emailRegex.test(value);
12
+ let isValid = emailRegex.test(value);
13
+ if (isValid && checkTypeoList) {
14
+ const emailDomain = value.split('@')[1].toLowerCase();
15
+ isValid = !emailTypoList.includes(emailDomain);
16
+ }
17
+ return isValid;
18
+ },
19
+ emailHasTrustedDomain: (value, options = null) => {
20
+ const emailParts = value && value.toLowerCase().split('@');
21
+ if (emailParts && emailParts.length === 2) {
22
+ const emailDomain = emailParts[1];
23
+ return trustedEmailDomains.includes(emailDomain);
24
+ }
25
+ return false;
10
26
  },
11
27
  phoneNumber: (value, options = null) => {
12
28
  const phoneRegex = /^((\+1|1)?( |-)?)?(\([2-9][0-9]{2}\)|[2-9][0-9]{2})( |-)?([2-9][0-9]{2}( |-)?[0-9]{4})$/;
@@ -17,7 +33,7 @@ const thirstieValidators = {
17
33
  return false;
18
34
  },
19
35
  postalCode: (value, options = null) => {
20
- const country = options.countryCode ? options.countryCode.toLowerCase() : 'en-us';
36
+ const country = (options && options.countryCode) ? options.countryCode.toLowerCase() : 'en-us';
21
37
  const regex = {
22
38
  'en-us': /^\d{5}$/,
23
39
  'en-ca': /^[a-zA-Z]\d[a-zA-Z]\s?\d[a-zA-Z]\d$/
@@ -27,9 +43,44 @@ const thirstieValidators = {
27
43
  }
28
44
  return false;
29
45
  },
30
- stateProvinceCode: (value, options = null) => { throw new Error('Not implemented'); },
31
- currencyValue: (value, options = null) => { throw new Error('Not implemented'); },
32
- passwordStrength: (value, options = null) => { return passwordcheck(value); }
46
+ stateProvinceCode: (value, options = null) => {
47
+ const countryCode = (options && options.countryCode) ? options.countryCode.toLowerCase() : 'en-us';
48
+ const check = getAdministrativeArea(value.toUpperCase(), countryCode);
49
+ if (!check) {
50
+ return false;
51
+ } else {
52
+ return true;
53
+ }
54
+ },
55
+ currencyValue: (value, options = null) => {
56
+ const countryCode = (options && options.countryCode) ? options.countryCode.toLowerCase() : 'en-us';
57
+ const countryValues = COUNTRY_CODES[countryCode];
58
+ const currencySymbols = countryValues.currency;
59
+
60
+ const checkValue = currencySymbols.reduce((acc, val) => acc.replace(val, ''), value).replace(' ', '');
61
+
62
+ const regx0 = /[^.^\d]+/;
63
+ const regx = /\d+\.+\d+/;
64
+ if (!regx0.test(checkValue)) {
65
+ return regx.test(checkValue);
66
+ } else {
67
+ return false;
68
+ }
69
+ }
70
+ };
71
+
72
+ const Checkers = {
73
+ passwordStrength: (value, options = null) => { return passwordcheck(value); },
74
+ emailCheck: (value, options = null) => {
75
+ const isValid = Validators.email(value, { checkTypeos: true });
76
+ const isTrusted = Validators.emailHasTrustedDomain(value);
77
+ return { isValid, isTrusted };
78
+ },
79
+ ageGate: (value, options = null) => {
80
+ const ageGateType = options.ageGateType || 'dob';
81
+ const legalAge = options.legalAge;
82
+ return ageGate(ageGateType, value, legalAge);
83
+ }
33
84
  };
34
85
 
35
- export default thirstieValidators;
86
+ export { Validators, Checkers };
@@ -0,0 +1,82 @@
1
+ export const US_STATES = {
2
+ AL: 'Alabama',
3
+ AK: 'Alaska',
4
+ AZ: 'Arizona',
5
+ AR: 'Arkansas',
6
+ CA: 'California',
7
+ CO: 'Colorado',
8
+ CT: 'Connecticut',
9
+ DE: 'Delaware',
10
+ DC: 'District of Columbia',
11
+ FL: 'Florida',
12
+ GA: 'Georgia',
13
+ HI: 'Hawaii',
14
+ ID: 'Idaho',
15
+ IL: 'Illinois',
16
+ IN: 'Indiana',
17
+ IA: 'Iowa',
18
+ KS: 'Kansas',
19
+ KY: 'Kentucky',
20
+ LA: 'Louisiana',
21
+ ME: 'Maine',
22
+ MD: 'Maryland',
23
+ MA: 'Massachusetts',
24
+ MI: 'Michigan',
25
+ MN: 'Minnesota',
26
+ MS: 'Mississippi',
27
+ MO: 'Missouri',
28
+ MT: 'Montana',
29
+ NE: 'Nebraska',
30
+ NV: 'Nevada',
31
+ NH: 'New Hampshire',
32
+ NJ: 'New Jersey',
33
+ NM: 'New Mexico',
34
+ NY: 'New York',
35
+ NC: 'North Carolina',
36
+ ND: 'North Dakota',
37
+ OH: 'Ohio',
38
+ OK: 'Oklahoma',
39
+ OR: 'Oregon',
40
+ PA: 'Pennsylvania',
41
+ RI: 'Rhode Island',
42
+ SC: 'South Carolina',
43
+ SD: 'South Dakota',
44
+ TN: 'Tennessee',
45
+ TX: 'Texas',
46
+ UT: 'Utah',
47
+ VT: 'Vermont',
48
+ VA: 'Virginia',
49
+ WA: 'Washington',
50
+ WV: 'West Virginia',
51
+ WI: 'Wisconsin',
52
+ WY: 'Wyoming'
53
+ };
54
+
55
+ export const CA_PROVINCES = {
56
+ AB: 'Alberta',
57
+ BC: 'British Columbia',
58
+ MB: 'Manitoba',
59
+ NB: 'New Brunswick',
60
+ NL: 'Newfoundland and Labrador',
61
+ NS: 'Nova Scotia',
62
+ ON: 'Ontario',
63
+ PE: 'Prince Edward Island',
64
+ QC: 'Quebec',
65
+ SK: 'Saskatchewan'
66
+ };
67
+
68
+ // ISO-3166 codes
69
+ export const COUNTRY_CODES = {
70
+ 'en-us': { name: 'United States', alpha3: 'USA', numeric: '840', administrativeAreas: US_STATES, alpha2: 'US', currency: [ '$', 'USD' ] },
71
+ 'en-ca': { name: 'Canada', alpha3: 'CAN', numeric: '124', administrativeAreas: CA_PROVINCES, alpha1: 'CA', currency: [ '$', 'CAD' ] }
72
+ };
73
+
74
+ export const getAdministrativeArea = (value, countryCode = null) => {
75
+ const country = countryCode ? countryCode.toLowerCase() : 'en-us';
76
+ const countryValues = COUNTRY_CODES[country];
77
+ if (countryValues) {
78
+ return countryValues.administrativeAreas[value];
79
+ } else {
80
+ return false;
81
+ }
82
+ };
@@ -18,7 +18,6 @@ const blindEntropy = (pw) => {
18
18
 
19
19
  const permutations = numberPermutations + lowercasePermutations + uppercasePermutations + punctuationPermutations;
20
20
  const pwLength = pw.length;
21
-
22
21
  return Math.log2(permutations ** pwLength);
23
22
  };
24
23
 
@@ -0,0 +1,40 @@
1
+ import { Checkers } from '../../src/index.js';
2
+
3
+ describe('Checker.ageGate', () => {
4
+ it('should validate a list of items', () => {
5
+ const fixtures = [
6
+ {
7
+ ageGateType: 'YesNo',
8
+ ageGateValue: true,
9
+ expectedvalidation: { ageGateType: 'yesno', ageGatePassed: true, ageGateDOB: null }
10
+ },
11
+ {
12
+ ageGateType: 'yesno',
13
+ ageGateValue: null,
14
+ expectedvalidation: { ageGateType: 'yesno', ageGatePassed: false, ageGateDOB: null }
15
+ },
16
+ {
17
+ ageGateType: 'yesno',
18
+ ageGateValue: false,
19
+ expectedvalidation: { ageGateType: 'yesno', ageGatePassed: false, ageGateDOB: null }
20
+ },
21
+ {
22
+ ageGateType: 'DOB',
23
+ ageGateValue: new Date(1964, 8, 28),
24
+ expectedvalidation: { ageGateType: 'dob', ageGatePassed: true, ageGateDOB: new Date(1964, 8, 28) }
25
+ },
26
+ {
27
+ ageGateType: 'DOB',
28
+ ageGateValue: new Date(2014, 8, 28),
29
+ expectedvalidation: { ageGateType: 'dob', ageGatePassed: false, ageGateDOB: new Date(2014, 8, 28) }
30
+ }
31
+ ];
32
+
33
+ fixtures.forEach((item) => {
34
+ const { ageGateType, ageGateValue, expectedvalidation } = item;
35
+ const result = Checkers.ageGate(ageGateValue, { ageGateType });
36
+ expect(result.ageGateType).toBe(expectedvalidation.ageGateType);
37
+ expect(result.ageGatePassed).toBe(expectedvalidation.ageGatePassed);
38
+ });
39
+ });
40
+ });
@@ -1,4 +1,4 @@
1
- import thirstieValidators from '../../src/index.js';
1
+ import { Checkers } from '../../src/index.js';
2
2
 
3
3
  describe('passwordcheck', () => {
4
4
  it('should return password strength', () => {
@@ -15,7 +15,7 @@ describe('passwordcheck', () => {
15
15
  }
16
16
  ];
17
17
  fixtures.forEach((fixture) => {
18
- const pwStrength = thirstieValidators.passwordStrength(fixture.testValue);
18
+ const pwStrength = Checkers.passwordStrength(fixture.testValue);
19
19
  expect(pwStrength.strength).toBe(fixture.strength);
20
20
  expect(pwStrength.label).toBe(fixture.label);
21
21
  });
@@ -0,0 +1,31 @@
1
+ import { ageGate } from '../../src/agegate';
2
+
3
+ describe('agegate validation', () => {
4
+ it('should validate a list of items', () => {
5
+ const fixtures = [
6
+ {
7
+ ageGateType: 'YesNo',
8
+ ageGateValue: true,
9
+ expectedvalidation: { ageGateType: 'yesno', ageGatePassed: true, ageGateDOB: null }
10
+ },
11
+ {
12
+ ageGateType: 'yesno',
13
+ ageGateValue: 'true',
14
+ expectedvalidation: { ageGateType: 'yesno', ageGatePassed: true, ageGateDOB: null }
15
+ },
16
+ {
17
+ ageGateType: 'DOB',
18
+ ageGateValue: new Date(1964, 8, 28),
19
+ expectedvalidation: { ageGateType: 'dob', ageGatePassed: true, ageGateDOB: new Date(1964, 8, 28) }
20
+ }
21
+ ];
22
+
23
+ fixtures.forEach((item) => {
24
+ const { ageGateType, ageGateValue, expectedvalidation } = item;
25
+ const result = ageGate(ageGateType, ageGateValue);
26
+ expect(result.ageGateType).toBe(expectedvalidation.ageGateType);
27
+ expect(result.ageGatePassed).toBe(expectedvalidation.ageGatePassed);
28
+ expect(result.ageGateDOB).toEqual(expectedvalidation.ageGateDOB);
29
+ });
30
+ });
31
+ });
@@ -1,4 +1,4 @@
1
- import thirstieValidators from '../../src/index.js';
1
+ import { Validators } from '../../src/index.js';
2
2
 
3
3
  describe('email validation', () => {
4
4
  /* see:
@@ -13,10 +13,11 @@ describe('email validation', () => {
13
13
  'first.last@example.com'
14
14
  ];
15
15
  fixtures.forEach((fixture) => {
16
- const validationCheck = thirstieValidators.email(fixture);
16
+ const validationCheck = Validators.email(fixture);
17
17
  expect(validationCheck).toBe(true);
18
18
  });
19
19
  });
20
+
20
21
  it('should return false for invalid emails', () => {
21
22
  const fixtures = [
22
23
  'localpart@domainname',
@@ -24,8 +25,33 @@ describe('email validation', () => {
24
25
  'first.last@example.c'
25
26
  ];
26
27
  fixtures.forEach((fixture) => {
27
- const validationCheck = thirstieValidators.email(fixture);
28
+ const validationCheck = Validators.email(fixture);
29
+ expect(validationCheck).toBe(false);
30
+ });
31
+ });
32
+
33
+ it('should return false for invalid domains if option is selected', () => {
34
+ const fixtures = [
35
+ 'localpart@gnail.co',
36
+ 'first.last@ail.com'
37
+ ];
38
+ fixtures.forEach((fixture) => {
39
+ const options = { checkTypeos: true };
40
+ const validationCheck = Validators.email(fixture, options);
28
41
  expect(validationCheck).toBe(false);
29
42
  });
30
43
  });
44
+
45
+ it('should recognize trusted domains', () => {
46
+ const fixtures = [
47
+ 'localpart@gmail.com',
48
+ 'first.last@yahoo.com',
49
+ 'bob.lablaw@aol.com'
50
+ ];
51
+ fixtures.forEach((fixture) => {
52
+ const options = { checkTypeos: true };
53
+ const validationCheck = Validators.emailHasTrustedDomain(fixture, options);
54
+ expect(validationCheck).toBe(true);
55
+ });
56
+ });
31
57
  });
@@ -0,0 +1,40 @@
1
+ import { Validators } from '../../src/index.js';
2
+
3
+ describe('currency validation', () => {
4
+ it('should validate a currency input', () => {
5
+ const fixtures = [
6
+ [ '$45.00', true ],
7
+ [ '$45.00x', false ]
8
+ ];
9
+ fixtures.forEach((fixture) => {
10
+ const validationCheck = Validators.currencyValue(fixture[0]);
11
+ expect(validationCheck).toBe(fixture[1]);
12
+ });
13
+ });
14
+ });
15
+
16
+ describe('state code validation', () => {
17
+ it('should validate a US state code', () => {
18
+ const fixtures = [
19
+ [ 'ma', true ],
20
+ [ 'MA', true ],
21
+ [ 'XX', false ]
22
+ ];
23
+ fixtures.forEach((fixture) => {
24
+ const validationCheck = Validators.stateProvinceCode(fixture[0]);
25
+ expect(validationCheck).toBe(fixture[1]);
26
+ });
27
+ });
28
+
29
+ it('should validate a Canadian state code', () => {
30
+ const fixtures = [
31
+ [ 'on', true ],
32
+ [ 'ON', true ],
33
+ [ 'XX', false ]
34
+ ];
35
+ fixtures.forEach((fixture) => {
36
+ const validationCheck = Validators.stateProvinceCode(fixture[0], { countryCode: 'en-ca' });
37
+ expect(validationCheck).toBe(fixture[1]);
38
+ });
39
+ });
40
+ });