From c9996901a9b4a7fa92f04f0628292f92f3d17a1c Mon Sep 17 00:00:00 2001 From: Akshay Nair Date: Sat, 17 Oct 2020 20:51:31 +0530 Subject: [PATCH 01/11] cname validation + some refactoring --- tests/domain-utils.test.js | 2 +- utils/domain.js | 22 +++++++++++++--------- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/tests/domain-utils.test.js b/tests/domain-utils.test.js index ad231b281..daf298efd 100644 --- a/tests/domain-utils.test.js +++ b/tests/domain-utils.test.js @@ -50,7 +50,7 @@ describe('validateDomainData', () => { ...defaultDomain, description: getstroflen(99), }, - { ...defaultDomain, record: { CNAME: 'sd', URL: '121,3213' } }, + { ...defaultDomain, record: { CNAME: 'aa.sd', URL: '121,3213' } }, ]; it('should return false for invalid data', () => { diff --git a/utils/domain.js b/utils/domain.js index 452aab658..583fbfd56 100644 --- a/utils/domain.js +++ b/utils/domain.js @@ -25,13 +25,17 @@ const validate = pattern => data => R.compose( R.toPairs, )(pattern); -const validateCnameRecord = key => R.allPass([ +const or = R.anyPass; +const and = R.allPass; + +const validateCnameRecord = key => and([ R.propSatisfies(R.is(String), key), R.compose(R.equals(1), R.length, R.reject(R.equals('URL')), R.keys), + R.propSatisfies(R.compose(R.gte(R.__, 3), R.length), key), R.propSatisfies(R.complement(testRegex(/^https?:\/\//ig)), key), ]); -const validateARecord = key => R.allPass([ +const validateARecord = key => and([ R.compose(R.equals(1), R.length, R.keys), R.propSatisfies(R.compose(R.gte(R.__, 1), R.length), key), ]); @@ -39,9 +43,9 @@ const validateARecord = key => R.allPass([ const validateDomainData = validate({ name: { reason: 'The name of the file is invalid', - fn: R.anyPass([ + fn: or([ R.equals('@'), - R.allPass([ + and([ R.compose(between(2, 100), R.length), testRegex(/^[a-z0-9\-]+$/g), ]) @@ -51,7 +55,7 @@ const validateDomainData = validate({ repo: { reason: '', fn: R.T, }, owner: { reason: '`owner` needs username and email properties', - fn: R.allPass([ + fn: and([ R.is(Object), R.complement(R.isEmpty), R.where({ @@ -62,13 +66,13 @@ const validateDomainData = validate({ }, record: { reason: 'Invalid record. CNAME records have to be a host name and A records has to be a list of ips', - fn: R.allPass([ + fn: and([ R.is(Object), R.compose(R.isEmpty, R.flip(R.difference)(VALID_RECORD_TYPES), R.keys), R.cond([ - [R.prop('CNAME'), validateCnameRecord('CNAME')], - [R.prop('A'), validateARecord('A')], - [R.prop('URL'), R.propSatisfies(R.is(String), 'URL')], + [R.has('CNAME'), validateCnameRecord('CNAME')], + [R.has('A'), validateARecord('A')], + [R.has('URL'), R.propSatisfies(R.is(String), 'URL')], [R.T, R.T], ]), ]), From 08c7e5c65adae6f1da9ce461c5f5ab0e5cccaff6 Mon Sep 17 00:00:00 2001 From: Akshay Nair Date: Sat, 17 Oct 2020 21:21:44 +0530 Subject: [PATCH 02/11] Refactors validations --- tests/domain-utils.test.js | 66 +---------------------------------- tests/domains.test.js | 3 +- tests/validations.test.js | 66 +++++++++++++++++++++++++++++++++++ utils/domain.js | 71 ++------------------------------------ utils/helpers.js | 20 +++++++++++ utils/validations.js | 56 ++++++++++++++++++++++++++++++ 6 files changed, 148 insertions(+), 134 deletions(-) create mode 100644 tests/validations.test.js create mode 100644 utils/helpers.js create mode 100644 utils/validations.js diff --git a/tests/domain-utils.test.js b/tests/domain-utils.test.js index daf298efd..2d6868b8d 100644 --- a/tests/domain-utils.test.js +++ b/tests/domain-utils.test.js @@ -1,4 +1,4 @@ -const { getDomains, validateDomainData } = require('../utils/domain'); +const { getDomains } = require('../utils/domain'); describe('getDomains', () => { it('should resolve with the list of domains', async () => { @@ -7,67 +7,3 @@ describe('getDomains', () => { }); }); -const defaultDomain = { - name: 'aaa', - record: { - A: ['121.121.121.121'] - }, - owner: { - username: 'betsy', - email: 'betsyfuckyoassup@foobar.com', - }, -}; - -const getstroflen = len => Array(len).fill('a').join(''); - -describe('validateDomainData', () => { - const invalidCases = [ - {}, - { name: 'helo' }, - { name: 'wwow', record: { A: ['12312'] } }, - ...['', ' ', undefined, 'hlo wld', 'g32++13', 'ajsdD_123yq', 'khsda%', '122*dsd', getstroflen(101)].map(name => ({ - ...defaultDomain, - name, - })), - { ...defaultDomain, record: { CNAME: 'sd', A: ['121,3213'] } }, - { ...defaultDomain, record: { A: ['121', '12'], FOOBAR: ['sd'] } }, - { ...defaultDomain, record: { A: [] } }, - { ...defaultDomain, record: { A: ['11122'], URL: 'foobar' } }, - { ...defaultDomain, owner: {}, }, - { ...defaultDomain, owner: { username: 'hwelo', }, }, - { ...defaultDomain, owner: { email: 'hwelo' }, }, - { ...defaultDomain, record: { CNAME: 'http://foobar.com' } }, - { ...defaultDomain, record: { CNAME: 'https://foobar.com' } }, - ]; - - const validCases = [ - defaultDomain, - ...['hello', 'hello-world', '11111111111', '--wow--', 'wow--', '--wow'].map(name => ({ - ...defaultDomain, - name, - })), - { - ...defaultDomain, - description: getstroflen(99), - }, - { ...defaultDomain, record: { CNAME: 'aa.sd', URL: '121,3213' } }, - ]; - - it('should return false for invalid data', () => { - invalidCases.forEach(data => { - const { valid, errors } = validateDomainData(data); - expect(valid).toBe(false); - expect(errors.length).toBeGreaterThan(0); - }); - }); - - it('should return true if the name is valid', () => { - validCases.forEach(data => { - const { valid, errors } = validateDomainData(data); - if (!valid) console.log(errors); - expect(valid).toBe(true); - expect(errors).toEqual([]); - }); - }); -}); - diff --git a/tests/domains.test.js b/tests/domains.test.js index 7f219fec4..fd80c3f43 100644 --- a/tests/domains.test.js +++ b/tests/domains.test.js @@ -1,6 +1,7 @@ const R = require('ramda'); const fs = require('fs'); -const { getDomains, validateDomainData } = require('../utils/domain'); +const { getDomains } = require('../utils/domain'); +const { validateDomainData } = require('../utils/validations'); const { DOMAINS_PATH } = require('../utils/constants'); describe('Domains', () => { diff --git a/tests/validations.test.js b/tests/validations.test.js new file mode 100644 index 000000000..057ffc3c8 --- /dev/null +++ b/tests/validations.test.js @@ -0,0 +1,66 @@ +const { validateDomainData } = require('../utils/validations'); + +const defaultDomain = { + name: 'aaa', + record: { + A: ['121.121.121.121'] + }, + owner: { + username: 'betsy', + email: 'betsyfuckyoassup@foobar.com', + }, +}; + +const getstroflen = len => Array(len).fill('a').join(''); + +describe('validateDomainData', () => { + const invalidCases = [ + {}, + { name: 'helo' }, + { name: 'wwow', record: { A: ['12312'] } }, + ...['', ' ', undefined, 'hlo wld', 'g32++13', 'ajsdD_123yq', 'khsda%', '122*dsd', getstroflen(101)].map(name => ({ + ...defaultDomain, + name, + })), + { ...defaultDomain, record: { CNAME: 'sd', A: ['121,3213'] } }, + { ...defaultDomain, record: { A: ['121', '12'], FOOBAR: ['sd'] } }, + { ...defaultDomain, record: { A: [] } }, + { ...defaultDomain, record: { A: ['11122'], URL: 'foobar' } }, + { ...defaultDomain, owner: {}, }, + { ...defaultDomain, owner: { username: 'hwelo', }, }, + { ...defaultDomain, owner: { email: 'hwelo' }, }, + { ...defaultDomain, record: { CNAME: 'http://foobar.com' } }, + { ...defaultDomain, record: { CNAME: 'https://foobar.com' } }, + ]; + + const validCases = [ + defaultDomain, + ...['hello', 'hello-world', '11111111111', '--wow--', 'wow--', '--wow'].map(name => ({ + ...defaultDomain, + name, + })), + { + ...defaultDomain, + description: getstroflen(99), + }, + { ...defaultDomain, record: { CNAME: 'aa.sd', URL: '121,3213' } }, + ]; + + it('should return false for invalid data', () => { + invalidCases.forEach(data => { + const { valid, errors } = validateDomainData(data); + expect(valid).toBe(false); + expect(errors.length).toBeGreaterThan(0); + }); + }); + + it('should return true if the name is valid', () => { + validCases.forEach(data => { + const { valid, errors } = validateDomainData(data); + if (!valid) console.log(errors); + expect(valid).toBe(true); + expect(errors).toEqual([]); + }); + }); +}); + diff --git a/utils/domain.js b/utils/domain.js index 583fbfd56..e932aafff 100644 --- a/utils/domain.js +++ b/utils/domain.js @@ -1,13 +1,11 @@ const fs = require('fs'); const path = require('path'); const R = require('ramda'); -const { VALID_RECORD_TYPES, DOMAINS_PATH } = require('./constants'); - -const log = m => x => console.log(m, x) || x; +const { DOMAINS_PATH } = require('./constants'); const toDomain = str => path.join(DOMAINS_PATH, str); -const toDomainData = R.compose(require, toDomain); +const toDomainData = R.compose(JSON.parse, R.toString, fs.readFileSync, toDomain); const getDomains = () => fs.promises.readdir(DOMAINS_PATH, {}) @@ -16,67 +14,4 @@ const getDomains = () => name: name.replace(/\.json$/, ''), }))); -const between = (min, max) => num => num >= min && num <= max; -const testRegex = regex => str => !!(str && str.match(regex)); - -const validate = pattern => data => R.compose( - invalidPairs => invalidPairs.length ? { errors: invalidPairs, valid: false } : { errors: [], valid: true }, - R.filter(([key, { fn }]) => fn ? !fn(data[key]) : false), - R.toPairs, -)(pattern); - -const or = R.anyPass; -const and = R.allPass; - -const validateCnameRecord = key => and([ - R.propSatisfies(R.is(String), key), - R.compose(R.equals(1), R.length, R.reject(R.equals('URL')), R.keys), - R.propSatisfies(R.compose(R.gte(R.__, 3), R.length), key), - R.propSatisfies(R.complement(testRegex(/^https?:\/\//ig)), key), -]); - -const validateARecord = key => and([ - R.compose(R.equals(1), R.length, R.keys), - R.propSatisfies(R.compose(R.gte(R.__, 1), R.length), key), -]); - -const validateDomainData = validate({ - name: { - reason: 'The name of the file is invalid', - fn: or([ - R.equals('@'), - and([ - R.compose(between(2, 100), R.length), - testRegex(/^[a-z0-9\-]+$/g), - ]) - ]), - }, - description: { reason: '', fn: R.T, }, - repo: { reason: '', fn: R.T, }, - owner: { - reason: '`owner` needs username and email properties', - fn: and([ - R.is(Object), - R.complement(R.isEmpty), - R.where({ - username: R.is(String), - email: R.is(String), - }), - ]), - }, - record: { - reason: 'Invalid record. CNAME records have to be a host name and A records has to be a list of ips', - fn: and([ - R.is(Object), - R.compose(R.isEmpty, R.flip(R.difference)(VALID_RECORD_TYPES), R.keys), - R.cond([ - [R.has('CNAME'), validateCnameRecord('CNAME')], - [R.has('A'), validateARecord('A')], - [R.has('URL'), R.propSatisfies(R.is(String), 'URL')], - [R.T, R.T], - ]), - ]), - }, -}); - -module.exports = { getDomains, validateDomainData }; +module.exports = { getDomains }; diff --git a/utils/helpers.js b/utils/helpers.js new file mode 100644 index 000000000..713a06797 --- /dev/null +++ b/utils/helpers.js @@ -0,0 +1,20 @@ +const R = require('ramda'); + +const log = m => x => console.log(m, x) || x; + +const between = (min, max) => num => num >= min && num <= max; +const testRegex = regex => str => !!(str && str.match(regex)); + +const validate = pattern => data => R.compose( + invalidPairs => invalidPairs.length ? { errors: invalidPairs, valid: false } : { errors: [], valid: true }, + R.filter(([key, { fn }]) => fn ? !fn(data[key]) : false), + R.toPairs, +)(pattern); + +const or = R.anyPass; +const and = R.allPass; + +const then = fn => p => p.then(fn); + +module.exports = { or, and, validate, between, testRegex, log, then }; + diff --git a/utils/validations.js b/utils/validations.js new file mode 100644 index 000000000..cd5acdb8e --- /dev/null +++ b/utils/validations.js @@ -0,0 +1,56 @@ +const R = require('ramda'); +const { VALID_RECORD_TYPES } = require('./constants'); +const { or, and, validate, between, testRegex } = require('./helpers'); + +const validateCnameRecord = key => and([ + R.propSatisfies(R.is(String), key), + R.compose(R.equals(1), R.length, R.reject(R.equals('URL')), R.keys), + R.propSatisfies(R.compose(R.gte(R.__, 3), R.length), key), + R.propSatisfies(R.complement(testRegex(/^https?:\/\//ig)), key), +]); + +const validateARecord = key => and([ + R.compose(R.equals(1), R.length, R.keys), + R.propSatisfies(R.compose(R.gte(R.__, 1), R.length), key), +]); + +const validateDomainData = validate({ + name: { + reason: 'The name of the file is invalid', + fn: or([ + R.equals('@'), + and([ + R.compose(between(2, 100), R.length), + testRegex(/^[a-z0-9\-]+$/g), + ]) + ]), + }, + description: { reason: '', fn: R.T, }, + repo: { reason: '', fn: R.T, }, + owner: { + reason: '`owner` needs username and email properties', + fn: and([ + R.is(Object), + R.complement(R.isEmpty), + R.where({ + username: R.is(String), + email: R.is(String), + }), + ]), + }, + record: { + reason: 'Invalid record. CNAME records have to be a host name and A records has to be a list of ips', + fn: and([ + R.is(Object), + R.compose(R.isEmpty, R.flip(R.difference)(VALID_RECORD_TYPES), R.keys), + R.cond([ + [R.has('CNAME'), validateCnameRecord('CNAME')], + [R.has('A'), validateARecord('A')], + [R.has('URL'), R.propSatisfies(R.is(String), 'URL')], + [R.T, R.T], + ]), + ]), + }, +}); + +module.exports = { validateDomainData }; From 7155beb00c1e82a61b9c85cd1ee70b41b4262ab7 Mon Sep 17 00:00:00 2001 From: Akshay Nair Date: Sat, 17 Oct 2020 21:21:59 +0530 Subject: [PATCH 03/11] Refactors out some of the helpers --- scripts/register-domains.js | 2 +- tests/domain-utils.test.js | 2 +- tests/domains.test.js | 2 +- utils/domain-service.js | 33 +++++------------------------- utils/{domain.js => get-domain.js} | 0 utils/helpers.js | 18 ++++++++++++++-- 6 files changed, 24 insertions(+), 33 deletions(-) rename utils/{domain.js => get-domain.js} (100%) diff --git a/scripts/register-domains.js b/scripts/register-domains.js index 00626092f..69435063c 100644 --- a/scripts/register-domains.js +++ b/scripts/register-domains.js @@ -1,7 +1,7 @@ const R = require('ramda'); const { VALID_RECORD_TYPES, TTL, ENV } = require('../utils/constants'); const { domainService: dc } = require('../utils/domain-service'); -const { getDomains: gd } = require('../utils/domain'); +const { getDomains: gd } = require('../utils/get-domain'); const getRecords = R.compose(R.toPairs, R.pick(VALID_RECORD_TYPES)); diff --git a/tests/domain-utils.test.js b/tests/domain-utils.test.js index 2d6868b8d..077896726 100644 --- a/tests/domain-utils.test.js +++ b/tests/domain-utils.test.js @@ -1,4 +1,4 @@ -const { getDomains } = require('../utils/domain'); +const { getDomains } = require('../utils/get-domain'); describe('getDomains', () => { it('should resolve with the list of domains', async () => { diff --git a/tests/domains.test.js b/tests/domains.test.js index fd80c3f43..6c58d7037 100644 --- a/tests/domains.test.js +++ b/tests/domains.test.js @@ -1,6 +1,6 @@ const R = require('ramda'); const fs = require('fs'); -const { getDomains } = require('../utils/domain'); +const { getDomains } = require('../utils/get-domain'); const { validateDomainData } = require('../utils/validations'); const { DOMAINS_PATH } = require('../utils/constants'); diff --git a/utils/domain-service.js b/utils/domain-service.js index 560e77853..76d511a0f 100644 --- a/utils/domain-service.js +++ b/utils/domain-service.js @@ -1,8 +1,9 @@ const R = require('ramda'); const { cpanel } = require('./lib/cpanel'); const { DOMAIN_DOMAIN, IS_TEST } = require('./constants'); +const { log, print, lazyTask, batchLazyTasks } = require('./helpers'); -const log = IS_TEST ? () => {} : console.log; +const BATCH_SIZE = 1; const recordToRedirection = ({ name, address }) => ({ domain: `${name}.${DOMAIN_DOMAIN}`, @@ -46,20 +47,6 @@ const diffRecords = (oldRecords, newRecords) => { return { add, remove }; }; -const print = fn => x => log(fn(x)) || x; - -const lazyTask = fn => data => () => fn(data); - -const batchLazyTasks = count => tasks => tasks.reduce((batches, task) => { - if (batches.length === 0) return [[task]]; - - const full = R.init(batches); - const last = R.last(batches); - - if (last.length >= count) return [...batches, [task]]; - return [...full, [...last, task]]; -}, []); - const executeBatch = (batches) => batches.reduce((promise, batch, index) => { return promise.then(async () => { log('>>> Running batch number:', index + 1, `(size: ${batch.length})`); @@ -77,8 +64,6 @@ const executeBatch = (batches) => batches.reduce((promise, batch, index) => { }, Promise.resolve()); const getDomainService = ({ cpanel }) => { - let hostList = []; - const fetchZoneRecords = () => cpanel.zone.fetch().then(R.map(zoneToRecord)); const fetchRedirections = () => cpanel.redirection.fetch().then(R.map(redirectionToRecord)); @@ -105,19 +90,11 @@ const getDomainService = ({ cpanel }) => { print(({ name }) => `Deleting redirection for ${name}`), )); - const getHosts = async () => { - if (hostList.length) return hostList; - - const list = await Promise.all([fetchZoneRecords(), fetchRedirections()]).then(R.flatten); - - hostList = list; - return list; - }; - - const BATCH_SIZE = 1; + const getHosts = () => + Promise.all([fetchZoneRecords(), fetchRedirections()]).then(R.flatten); const addRecords = R.compose(batchLazyTasks(BATCH_SIZE), R.filter(Boolean), R.map(R.cond([ - [ R.propEq('name', 'www'), R.always(null) ], + [ R.propEq('name', 'www'), R.always(null) ], // Ignore www [ R.propEq('type', 'URL'), addRedirection ], [ R.T, addZoneRecord ], ]))); diff --git a/utils/domain.js b/utils/get-domain.js similarity index 100% rename from utils/domain.js rename to utils/get-domain.js diff --git a/utils/helpers.js b/utils/helpers.js index 713a06797..2c1f61351 100644 --- a/utils/helpers.js +++ b/utils/helpers.js @@ -1,6 +1,8 @@ const R = require('ramda'); +const { IS_TEST } = require('./constants'); -const log = m => x => console.log(m, x) || x; +const log = IS_TEST ? () => {} : console.log; +const print = fn => x => log(fn(x)) || x; const between = (min, max) => num => num >= min && num <= max; const testRegex = regex => str => !!(str && str.match(regex)); @@ -16,5 +18,17 @@ const and = R.allPass; const then = fn => p => p.then(fn); -module.exports = { or, and, validate, between, testRegex, log, then }; +const lazyTask = fn => data => () => fn(data); + +const batchLazyTasks = count => tasks => tasks.reduce((batches, task) => { + if (batches.length === 0) return [[task]]; + + const full = R.init(batches); + const last = R.last(batches); + + if (last.length >= count) return [...batches, [task]]; + return [...full, [...last, task]]; +}, []); + +module.exports = { or, and, validate, between, testRegex, log, print, then, lazyTask, batchLazyTasks }; From b2ba3c7c73f1f68fa9cead417435b986a64c5262 Mon Sep 17 00:00:00 2001 From: Akshay Nair Date: Sat, 17 Oct 2020 21:24:25 +0530 Subject: [PATCH 04/11] Some more refactoring --- utils/domain-service.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/utils/domain-service.js b/utils/domain-service.js index 76d511a0f..41aecef1a 100644 --- a/utils/domain-service.js +++ b/utils/domain-service.js @@ -1,7 +1,7 @@ const R = require('ramda'); const { cpanel } = require('./lib/cpanel'); -const { DOMAIN_DOMAIN, IS_TEST } = require('./constants'); -const { log, print, lazyTask, batchLazyTasks } = require('./helpers'); +const { DOMAIN_DOMAIN } = require('./constants'); +const { then, log, print, lazyTask, batchLazyTasks } = require('./helpers'); const BATCH_SIZE = 1; @@ -64,8 +64,8 @@ const executeBatch = (batches) => batches.reduce((promise, batch, index) => { }, Promise.resolve()); const getDomainService = ({ cpanel }) => { - const fetchZoneRecords = () => cpanel.zone.fetch().then(R.map(zoneToRecord)); - const fetchRedirections = () => cpanel.redirection.fetch().then(R.map(redirectionToRecord)); + const fetchZoneRecords = R.compose(then(R.map(zoneToRecord)), cpanel.zone.fetch); + const fetchRedirections = R.compose(then(R.map(redirectionToRecord)), cpanel.redirection.fetch); const addZoneRecord = lazyTask(R.compose( cpanel.zone.add, From 92e02ef996952c4931089dcd98e13b7f52b3662d Mon Sep 17 00:00:00 2001 From: Akshay Nair Date: Sat, 17 Oct 2020 21:33:08 +0530 Subject: [PATCH 05/11] Adds linter for scripts --- .eslintrc.json | 21 ++ package.json | 9 + scripts/reply.js | 6 +- utils/domain-service.js | 2 +- utils/validations.js | 2 +- yarn.lock | 418 ++++++++++++++++++++++++++++++++++++++-- 6 files changed, 442 insertions(+), 16 deletions(-) create mode 100644 .eslintrc.json diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100644 index 000000000..61b4916de --- /dev/null +++ b/.eslintrc.json @@ -0,0 +1,21 @@ +{ + "extends": [ + "eslint:recommended", + "plugin:node/recommended" + ], + "parserOptions": { + "ecmaVersion": 2020 + }, + "rules": { + "node/exports-style": ["error", "module.exports"], + "node/file-extension-in-import": ["error", "always"], + "node/prefer-global/buffer": ["error", "always"], + "node/prefer-global/console": ["error", "always"], + "node/prefer-global/process": ["error", "always"], + "node/prefer-global/url-search-params": ["error", "always"], + "node/prefer-global/url": ["error", "always"], + "node/prefer-promises/dns": "error", + "node/prefer-promises/fs": "error", + "no-process-exit": [0] + } +} diff --git a/package.json b/package.json index 8a92a77a1..9155ff64a 100644 --- a/package.json +++ b/package.json @@ -2,8 +2,12 @@ "name": "is-a-dev-core", "version": "1.0.0", "description": "Register *.is-a.dev domains for free", + "engines": { + "node": ">=11.14.0" + }, "scripts": { "test": "ENV=test jest", + "lint": "eslint utils scripts", "publish-records": "node ./scripts/register-domains.js" }, "repository": { @@ -19,6 +23,11 @@ "dotenv": "^8.2.0", "jest": "^26.4.2", "node-fetch": "^2.6.1", + "qs": "^6.9.4", "ramda": "^0.27.1" + }, + "devDependencies": { + "eslint": "^7.11.0", + "eslint-plugin-node": "^11.1.0" } } diff --git a/scripts/reply.js b/scripts/reply.js index 9c5fc11b2..b7d10841e 100644 --- a/scripts/reply.js +++ b/scripts/reply.js @@ -2,7 +2,7 @@ const getInstructions = () => ` The changes have been published!! It should reflect in less than 24 hours. -## Here\'s what you need to do next +## Here's what you need to do next If your domain points to a server you own, add \`domain-name.is-a.dev\` to your server config. For https, you will have to configure ssl certificate to allow the new subdomain. @@ -16,13 +16,13 @@ If your domain points to a server you own, add \`domain-name.is-a.dev\` to your ## Need support with your domain? -If you are having trouble setting up your domain, [create an issue](https://github.com/is-a-dev/register/issues/new/choose). I\'ll try my best to get back to you asap! +If you're having trouble setting up your domain, [create an issue](https://github.com/is-a-dev/register/issues/new/choose). I'll try my best to get back to you asap! ## Love/Hate the service? **Love it?** Leave it a **star**! Also consider **[donating](https://github.com/is-a-dev/register#donations)** so that I can keep this service running forever. -**Hate it?** Please leave your feedback by [creating an issue](https://github.com/is-a-dev/register/issues/new/choose). I\'d really like to keep improving this service for developers. +**Hate it?** Please leave your feedback by [creating an issue](https://github.com/is-a-dev/register/issues/new/choose). I'd really like to keep improving this service for developers. `; module.exports = { diff --git a/utils/domain-service.js b/utils/domain-service.js index 41aecef1a..984d274cb 100644 --- a/utils/domain-service.js +++ b/utils/domain-service.js @@ -20,7 +20,7 @@ const recordToZone = ({ name, type, address, id }) => ({ ...(type === 'CNAME' ? { cname: address } : {}), }); -const cleanName = name => `${name}`.replace(new RegExp(`\.${DOMAIN_DOMAIN}\.?$`), '').toLowerCase(); +const cleanName = name => `${name}`.replace(new RegExp(`\\.${DOMAIN_DOMAIN}\\.?$`), '').toLowerCase(); const zoneToRecord = ({ name, type, cname, address, record, line: id }) => ({ id, diff --git a/utils/validations.js b/utils/validations.js index cd5acdb8e..57767769a 100644 --- a/utils/validations.js +++ b/utils/validations.js @@ -21,7 +21,7 @@ const validateDomainData = validate({ R.equals('@'), and([ R.compose(between(2, 100), R.length), - testRegex(/^[a-z0-9\-]+$/g), + testRegex(/^[a-z0-9-]+$/g), ]) ]), }, diff --git a/yarn.lock b/yarn.lock index fb8f6f11e..a14509ba4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -271,6 +271,22 @@ exec-sh "^0.3.2" minimist "^1.2.0" +"@eslint/eslintrc@^0.1.3": + version "0.1.3" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-0.1.3.tgz#7d1a2b2358552cc04834c0979bd4275362e37085" + integrity sha512-4YVwPkANLeNtRjMekzux1ci8hIaH5eGKktGqR0d3LWsKNn5B2X/1Z6Trxy7jQXl9EBGE6Yj02O+t09FMeRllaA== + dependencies: + ajv "^6.12.4" + debug "^4.1.1" + espree "^7.3.0" + globals "^12.1.0" + ignore "^4.0.6" + import-fresh "^3.2.1" + js-yaml "^3.13.1" + lodash "^4.17.19" + minimatch "^3.0.4" + strip-json-comments "^3.1.1" + "@istanbuljs/load-nyc-config@^1.0.0": version "1.1.0" resolved "https://registry.yarnpkg.com/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz#fd3db1d59ecf7cf121e80650bb86712f9b55eced" @@ -581,6 +597,11 @@ acorn-globals@^6.0.0: acorn "^7.1.1" acorn-walk "^7.1.1" +acorn-jsx@^5.2.0: + version "5.3.1" + resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.1.tgz#fc8661e11b7ac1539c47dbfea2e72b3af34d267b" + integrity sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng== + acorn-walk@^7.1.1: version "7.2.0" resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-7.2.0.tgz#0de889a601203909b0fbe07b8938dc21d2e967bc" @@ -591,6 +612,21 @@ acorn@^7.1.1: resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.0.tgz#e1ad486e6c54501634c6c397c5c121daa383607c" integrity sha512-+G7P8jJmCHr+S+cLfQxygbWhXy+8YTVGzAkpEbcLo2mLoL7tij/VG41QSHACSf5QgYRhMZYHuNc6drJaO0Da+w== +acorn@^7.4.0: + version "7.4.1" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" + integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== + +ajv@^6.10.0, ajv@^6.10.2, ajv@^6.12.4: + version "6.12.6" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" + integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== + dependencies: + fast-deep-equal "^3.1.1" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.2" + ajv@^6.12.3: version "6.12.5" resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.5.tgz#19b0e8bae8f476e5ba666300387775fb1a00a4da" @@ -601,6 +637,11 @@ ajv@^6.12.3: json-schema-traverse "^0.4.1" uri-js "^4.2.2" +ansi-colors@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348" + integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA== + ansi-escapes@^4.2.1: version "4.3.1" resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.1.tgz#a5c47cc43181f1f38ffd7076837700d395522a61" @@ -608,12 +649,17 @@ ansi-escapes@^4.2.1: dependencies: type-fest "^0.11.0" +ansi-regex@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997" + integrity sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg== + ansi-regex@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.0.tgz#388539f55179bf39339c81af30a654d69f87cb75" integrity sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg== -ansi-styles@^3.2.1: +ansi-styles@^3.2.0, ansi-styles@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== @@ -688,6 +734,11 @@ assign-symbols@^1.0.0: resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" integrity sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c= +astral-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-1.0.0.tgz#6c8c3fb827dd43ee3918f27b82782ab7658a6fd9" + integrity sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg== + asynckit@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" @@ -1016,7 +1067,7 @@ cross-spawn@^6.0.0: shebang-command "^1.2.0" which "^1.2.9" -cross-spawn@^7.0.0: +cross-spawn@^7.0.0, cross-spawn@^7.0.2: version "7.0.3" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== @@ -1065,7 +1116,7 @@ debug@^2.2.0, debug@^2.3.3: dependencies: ms "2.0.0" -debug@^4.1.0, debug@^4.1.1: +debug@^4.0.1, debug@^4.1.0, debug@^4.1.1: version "4.2.0" resolved "https://registry.yarnpkg.com/debug/-/debug-4.2.0.tgz#7f150f93920e94c58f5574c2fd01a3110effe7f1" integrity sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg== @@ -1087,7 +1138,7 @@ decode-uri-component@^0.2.0: resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545" integrity sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU= -deep-is@~0.1.3: +deep-is@^0.1.3, deep-is@~0.1.3: version "0.1.3" resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ= @@ -1134,6 +1185,13 @@ diff-sequences@^26.3.0: resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-26.3.0.tgz#62a59b1b29ab7fd27cef2a33ae52abe73042d0a2" integrity sha512-5j5vdRcw3CNctePNYN0Wy2e/JbWT6cAYnXv5OuqPhDpyCGc0uLu2TK0zOCJWNB9kOIfYMSpIulRaDgIi4HJ6Ig== +doctrine@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961" + integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w== + dependencies: + esutils "^2.0.2" + domexception@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/domexception/-/domexception-2.0.1.tgz#fb44aefba793e1574b0af6aed2801d057529f304" @@ -1159,6 +1217,11 @@ emittery@^0.7.1: resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.7.1.tgz#c02375a927a40948c0345cc903072597f5270451" integrity sha512-d34LN4L6h18Bzz9xpoku2nPwKxCPlPMr3EEKTkoEBi+1/+b0lcRkRJ1UVyyZaKNeqGR3swcGl6s390DNO4YVgQ== +emoji-regex@^7.0.1: + version "7.0.3" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156" + integrity sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA== + emoji-regex@^8.0.0: version "8.0.0" resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" @@ -1171,6 +1234,13 @@ end-of-stream@^1.1.0: dependencies: once "^1.4.0" +enquirer@^2.3.5: + version "2.3.6" + resolved "https://registry.yarnpkg.com/enquirer/-/enquirer-2.3.6.tgz#2a7fe5dd634a1e4125a975ec994ff5456dc3734d" + integrity sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg== + dependencies: + ansi-colors "^4.1.1" + error-ex@^1.3.1: version "1.3.2" resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" @@ -1200,16 +1270,132 @@ escodegen@^1.14.1: optionalDependencies: source-map "~0.6.1" +eslint-plugin-es@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-es/-/eslint-plugin-es-3.0.1.tgz#75a7cdfdccddc0589934aeeb384175f221c57893" + integrity sha512-GUmAsJaN4Fc7Gbtl8uOBlayo2DqhwWvEzykMHSCZHU3XdJ+NSzzZcVhXh3VxX5icqQ+oQdIEawXX8xkR3mIFmQ== + dependencies: + eslint-utils "^2.0.0" + regexpp "^3.0.0" + +eslint-plugin-node@^11.1.0: + version "11.1.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-node/-/eslint-plugin-node-11.1.0.tgz#c95544416ee4ada26740a30474eefc5402dc671d" + integrity sha512-oUwtPJ1W0SKD0Tr+wqu92c5xuCeQqB3hSCHasn/ZgjFdA9iDGNkNf2Zi9ztY7X+hNuMib23LNGRm6+uN+KLE3g== + dependencies: + eslint-plugin-es "^3.0.0" + eslint-utils "^2.0.0" + ignore "^5.1.1" + minimatch "^3.0.4" + resolve "^1.10.1" + semver "^6.1.0" + +eslint-scope@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" + integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw== + dependencies: + esrecurse "^4.3.0" + estraverse "^4.1.1" + +eslint-utils@^2.0.0, eslint-utils@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-2.1.0.tgz#d2de5e03424e707dc10c74068ddedae708741b27" + integrity sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg== + dependencies: + eslint-visitor-keys "^1.1.0" + +eslint-visitor-keys@^1.1.0, eslint-visitor-keys@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz#30ebd1ef7c2fdff01c3a4f151044af25fab0523e" + integrity sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ== + +eslint-visitor-keys@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.0.0.tgz#21fdc8fbcd9c795cc0321f0563702095751511a8" + integrity sha512-QudtT6av5WXels9WjIM7qz1XD1cWGvX4gGXvp/zBn9nXG02D0utdU3Em2m/QjTnrsk6bBjmCygl3rmj118msQQ== + +eslint@^7.11.0: + version "7.11.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.11.0.tgz#aaf2d23a0b5f1d652a08edacea0c19f7fadc0b3b" + integrity sha512-G9+qtYVCHaDi1ZuWzBsOWo2wSwd70TXnU6UHA3cTYHp7gCTXZcpggWFoUVAMRarg68qtPoNfFbzPh+VdOgmwmw== + dependencies: + "@babel/code-frame" "^7.0.0" + "@eslint/eslintrc" "^0.1.3" + ajv "^6.10.0" + chalk "^4.0.0" + cross-spawn "^7.0.2" + debug "^4.0.1" + doctrine "^3.0.0" + enquirer "^2.3.5" + eslint-scope "^5.1.1" + eslint-utils "^2.1.0" + eslint-visitor-keys "^2.0.0" + espree "^7.3.0" + esquery "^1.2.0" + esutils "^2.0.2" + file-entry-cache "^5.0.1" + functional-red-black-tree "^1.0.1" + glob-parent "^5.0.0" + globals "^12.1.0" + ignore "^4.0.6" + import-fresh "^3.0.0" + imurmurhash "^0.1.4" + is-glob "^4.0.0" + js-yaml "^3.13.1" + json-stable-stringify-without-jsonify "^1.0.1" + levn "^0.4.1" + lodash "^4.17.19" + minimatch "^3.0.4" + natural-compare "^1.4.0" + optionator "^0.9.1" + progress "^2.0.0" + regexpp "^3.1.0" + semver "^7.2.1" + strip-ansi "^6.0.0" + strip-json-comments "^3.1.0" + table "^5.2.3" + text-table "^0.2.0" + v8-compile-cache "^2.0.3" + +espree@^7.3.0: + version "7.3.0" + resolved "https://registry.yarnpkg.com/espree/-/espree-7.3.0.tgz#dc30437cf67947cf576121ebd780f15eeac72348" + integrity sha512-dksIWsvKCixn1yrEXO8UosNSxaDoSYpq9reEjZSbHLpT5hpaCAKTLBwq0RHtLrIr+c0ByiYzWT8KTMRzoRCNlw== + dependencies: + acorn "^7.4.0" + acorn-jsx "^5.2.0" + eslint-visitor-keys "^1.3.0" + esprima@^4.0.0, esprima@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== -estraverse@^4.2.0: +esquery@^1.2.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.3.1.tgz#b78b5828aa8e214e29fb74c4d5b752e1c033da57" + integrity sha512-olpvt9QG0vniUBZspVRN6lwB7hOZoTRtT+jzR+tS4ffYx2mzbw+z0XCOk44aaLYKApNX5nMm+E+P6o25ip/DHQ== + dependencies: + estraverse "^5.1.0" + +esrecurse@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" + integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== + dependencies: + estraverse "^5.2.0" + +estraverse@^4.1.1, estraverse@^4.2.0: version "4.3.0" resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== +estraverse@^5.1.0, estraverse@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.2.0.tgz#307df42547e6cc7324d3cf03c155d5cdb8c53880" + integrity sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ== + esutils@^2.0.2: version "2.0.3" resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" @@ -1332,7 +1518,7 @@ fast-json-stable-stringify@^2.0.0: resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== -fast-levenshtein@~2.0.6: +fast-levenshtein@^2.0.6, fast-levenshtein@~2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= @@ -1344,6 +1530,13 @@ fb-watchman@^2.0.0: dependencies: bser "2.1.1" +file-entry-cache@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-5.0.1.tgz#ca0f6efa6dd3d561333fb14515065c2fafdf439c" + integrity sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g== + dependencies: + flat-cache "^2.0.1" + fill-range@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7" @@ -1369,6 +1562,20 @@ find-up@^4.0.0, find-up@^4.1.0: locate-path "^5.0.0" path-exists "^4.0.0" +flat-cache@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-2.0.1.tgz#5d296d6f04bda44a4630a301413bdbc2ec085ec0" + integrity sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA== + dependencies: + flatted "^2.0.0" + rimraf "2.6.3" + write "1.0.3" + +flatted@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-2.0.2.tgz#4575b21e2bcee7434aa9be662f4b7b5f9c2b5138" + integrity sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA== + for-in@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" @@ -1405,6 +1612,11 @@ fsevents@^2.1.2: resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.1.3.tgz#fb738703ae8d2f9fe900c33836ddebee8b97f23e" integrity sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ== +functional-red-black-tree@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" + integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc= + gensync@^1.0.0-beta.1: version "1.0.0-beta.1" resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.1.tgz#58f4361ff987e5ff6e1e7a210827aa371eaac269" @@ -1446,6 +1658,13 @@ getpass@^0.1.1: dependencies: assert-plus "^1.0.0" +glob-parent@^5.0.0: + version "5.1.1" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.1.tgz#b6c1ef417c4e5663ea498f1c45afac6916bbc229" + integrity sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ== + dependencies: + is-glob "^4.0.1" + glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4: version "7.1.6" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" @@ -1463,6 +1682,13 @@ globals@^11.1.0: resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== +globals@^12.1.0: + version "12.4.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-12.4.0.tgz#a18813576a41b00a24a97e7f815918c2e19925f8" + integrity sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg== + dependencies: + type-fest "^0.8.1" + graceful-fs@^4.2.4: version "4.2.4" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.4.tgz#2256bde14d3632958c465ebc96dc467ca07a29fb" @@ -1565,6 +1791,24 @@ iconv-lite@0.4.24: dependencies: safer-buffer ">= 2.1.2 < 3" +ignore@^4.0.6: + version "4.0.6" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" + integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg== + +ignore@^5.1.1: + version "5.1.8" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.1.8.tgz#f150a8b50a34289b33e22f5889abd4d8016f0e57" + integrity sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw== + +import-fresh@^3.0.0, import-fresh@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.2.1.tgz#633ff618506e793af5ac91bf48b72677e15cbe66" + integrity sha512-6e1q1cnWP2RXD9/keSkxHScg508CdXqXWgWBaETNhyuBFz+kUZlKboh+ISK+bU++DmbHimVBrOz/zzPe0sZ3sQ== + dependencies: + parent-module "^1.0.0" + resolve-from "^4.0.0" + import-local@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/import-local/-/import-local-3.0.2.tgz#a8cfd0431d1de4a2199703d003e3e62364fa6db6" @@ -1676,6 +1920,16 @@ is-extendable@^1.0.1: dependencies: is-plain-object "^2.0.4" +is-extglob@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= + +is-fullwidth-code-point@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" + integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8= + is-fullwidth-code-point@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" @@ -1686,6 +1940,13 @@ is-generator-fn@^2.0.0: resolved "https://registry.yarnpkg.com/is-generator-fn/-/is-generator-fn-2.1.0.tgz#7d140adc389aaf3011a8f2a2a4cfa6faadffb118" integrity sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ== +is-glob@^4.0.0, is-glob@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc" + integrity sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg== + dependencies: + is-extglob "^2.1.1" + is-number@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195" @@ -2245,6 +2506,11 @@ json-schema@0.2.3: resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" integrity sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM= +json-stable-stringify-without-jsonify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" + integrity sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE= + json-stringify-safe@~5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" @@ -2301,6 +2567,14 @@ leven@^3.1.0: resolved "https://registry.yarnpkg.com/leven/-/leven-3.1.0.tgz#77891de834064cccba82ae7842bb6b14a13ed7f2" integrity sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A== +levn@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade" + integrity sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ== + dependencies: + prelude-ls "^1.2.1" + type-check "~0.4.0" + levn@~0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" @@ -2326,7 +2600,7 @@ lodash.sortby@^4.7.0: resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438" integrity sha1-7dFMgk4sycHgsKG0K7UhBRakJDg= -lodash@^4.17.19: +lodash@^4.17.14, lodash@^4.17.19: version "4.17.20" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52" integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA== @@ -2426,6 +2700,13 @@ mixin-deep@^1.2.0: for-in "^1.0.2" is-extendable "^1.0.1" +mkdirp@^0.5.1: + version "0.5.5" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" + integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ== + dependencies: + minimist "^1.2.5" + ms@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" @@ -2585,6 +2866,18 @@ optionator@^0.8.1: type-check "~0.3.2" word-wrap "~1.2.3" +optionator@^0.9.1: + version "0.9.1" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.1.tgz#4f236a6373dae0566a6d43e1326674f50c291499" + integrity sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw== + dependencies: + deep-is "^0.1.3" + fast-levenshtein "^2.0.6" + levn "^0.4.1" + prelude-ls "^1.2.1" + type-check "^0.4.0" + word-wrap "^1.2.3" + p-each-series@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/p-each-series/-/p-each-series-2.1.0.tgz#961c8dd3f195ea96c747e636b262b800a6b1af48" @@ -2614,6 +2907,13 @@ p-try@^2.0.0: resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== +parent-module@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" + integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== + dependencies: + callsites "^3.0.0" + parse-json@^5.0.0: version "5.1.0" resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.1.0.tgz#f96088cdf24a8faa9aea9a009f2d9d942c999646" @@ -2688,6 +2988,11 @@ posix-character-classes@^0.1.0: resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab" integrity sha1-AerA/jta9xoqbAL+q7jB/vfgDqs= +prelude-ls@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" + integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== + prelude-ls@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" @@ -2703,6 +3008,11 @@ pretty-format@^26.4.2: ansi-styles "^4.0.0" react-is "^16.12.0" +progress@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" + integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== + prompts@^2.0.1: version "2.3.2" resolved "https://registry.yarnpkg.com/prompts/-/prompts-2.3.2.tgz#480572d89ecf39566d2bd3fe2c9fccb7c4c0b068" @@ -2729,6 +3039,11 @@ punycode@^2.1.0, punycode@^2.1.1: resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== +qs@^6.9.4: + version "6.9.4" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.9.4.tgz#9090b290d1f91728d3c22e54843ca44aea5ab687" + integrity sha512-A1kFqHekCTM7cz0udomYUoYNWjBebHm/5wzU/XqrBRBNWectVH0QIiN+NEcZ0Dte5hvzHwbr8+XQmguPhJ6WdQ== + qs@~6.5.2: version "6.5.2" resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" @@ -2771,6 +3086,11 @@ regex-not@^1.0.0, regex-not@^1.0.2: extend-shallow "^3.0.2" safe-regex "^1.1.0" +regexpp@^3.0.0, regexpp@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.1.0.tgz#206d0ad0a5648cffbdb8ae46438f3dc51c9f78e2" + integrity sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q== + remove-trailing-separator@^1.0.1: version "1.1.0" resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef" @@ -2845,6 +3165,11 @@ resolve-cwd@^3.0.0: dependencies: resolve-from "^5.0.0" +resolve-from@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" + integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== + resolve-from@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" @@ -2855,7 +3180,7 @@ resolve-url@^0.2.1: resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo= -resolve@^1.10.0, resolve@^1.17.0, resolve@^1.3.2: +resolve@^1.10.0, resolve@^1.10.1, resolve@^1.17.0, resolve@^1.3.2: version "1.17.0" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.17.0.tgz#b25941b54968231cc2d1bb76a79cb7f2c0bf8444" integrity sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w== @@ -2867,6 +3192,13 @@ ret@~0.1.10: resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" integrity sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg== +rimraf@2.6.3: + version "2.6.3" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab" + integrity sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA== + dependencies: + glob "^7.1.3" + rimraf@^3.0.0: version "3.0.2" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" @@ -2928,12 +3260,12 @@ saxes@^5.0.0: resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== -semver@^6.0.0, semver@^6.3.0: +semver@^6.0.0, semver@^6.1.0, semver@^6.3.0: version "6.3.0" resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== -semver@^7.3.2: +semver@^7.2.1, semver@^7.3.2: version "7.3.2" resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.2.tgz#604962b052b81ed0786aae84389ffba70ffd3938" integrity sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ== @@ -2997,6 +3329,15 @@ slash@^3.0.0: resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== +slice-ansi@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-2.1.0.tgz#cacd7693461a637a5788d92a7dd4fba068e81636" + integrity sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ== + dependencies: + ansi-styles "^3.2.0" + astral-regex "^1.0.0" + is-fullwidth-code-point "^2.0.0" + snapdragon-node@^2.0.1: version "2.1.1" resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b" @@ -3147,6 +3488,15 @@ string-length@^4.0.1: char-regex "^1.0.2" strip-ansi "^6.0.0" +string-width@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-3.1.0.tgz#22767be21b62af1081574306f69ac51b62203961" + integrity sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w== + dependencies: + emoji-regex "^7.0.1" + is-fullwidth-code-point "^2.0.0" + strip-ansi "^5.1.0" + string-width@^4.1.0, string-width@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.0.tgz#952182c46cc7b2c313d1596e623992bd163b72b5" @@ -3156,6 +3506,13 @@ string-width@^4.1.0, string-width@^4.2.0: is-fullwidth-code-point "^3.0.0" strip-ansi "^6.0.0" +strip-ansi@^5.1.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae" + integrity sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA== + dependencies: + ansi-regex "^4.1.0" + strip-ansi@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.0.tgz#0b1571dd7669ccd4f3e06e14ef1eed26225ae532" @@ -3178,6 +3535,11 @@ strip-final-newline@^2.0.0: resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== +strip-json-comments@^3.1.0, strip-json-comments@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" + integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== + supports-color@^5.3.0: version "5.5.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" @@ -3205,6 +3567,16 @@ symbol-tree@^3.2.4: resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2" integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw== +table@^5.2.3: + version "5.4.6" + resolved "https://registry.yarnpkg.com/table/-/table-5.4.6.tgz#1292d19500ce3f86053b05f0e8e7e4a3bb21079e" + integrity sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug== + dependencies: + ajv "^6.10.2" + lodash "^4.17.14" + slice-ansi "^2.1.0" + string-width "^3.0.0" + terminal-link@^2.0.0: version "2.1.1" resolved "https://registry.yarnpkg.com/terminal-link/-/terminal-link-2.1.1.tgz#14a64a27ab3c0df933ea546fba55f2d078edc994" @@ -3222,6 +3594,11 @@ test-exclude@^6.0.0: glob "^7.1.4" minimatch "^3.0.4" +text-table@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" + integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ= + throat@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/throat/-/throat-5.0.0.tgz#c5199235803aad18754a667d659b5e72ce16764b" @@ -3305,6 +3682,13 @@ tweetnacl@^0.14.3, tweetnacl@~0.14.0: resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q= +type-check@^0.4.0, type-check@~0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" + integrity sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew== + dependencies: + prelude-ls "^1.2.1" + type-check@~0.3.2: version "0.3.2" resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" @@ -3384,6 +3768,11 @@ uuid@^8.3.0: resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.0.tgz#ab738085ca22dc9a8c92725e459b1d507df5d6ea" integrity sha512-fX6Z5o4m6XsXBdli9g7DtWgAx+osMsRRZFKma1mIUsLCz6vRvv+pz5VNbyu9UEDzpMWulZfvpgb/cmDXVulYFQ== +v8-compile-cache@^2.0.3: + version "2.1.1" + resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.1.1.tgz#54bc3cdd43317bca91e35dcaf305b1a7237de745" + integrity sha512-8OQ9CL+VWyt3JStj7HX7/ciTL2V3Rl1Wf5OL+SNTm0yK1KvtReVulksyeRnCANHHuUxHlQig+JJDlUhBt1NQDQ== + v8-to-istanbul@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-5.0.1.tgz#0608f5b49a481458625edb058488607f25498ba5" @@ -3481,7 +3870,7 @@ which@^2.0.1, which@^2.0.2: dependencies: isexe "^2.0.0" -word-wrap@~1.2.3: +word-wrap@^1.2.3, word-wrap@~1.2.3: version "1.2.3" resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== @@ -3510,6 +3899,13 @@ write-file-atomic@^3.0.0: signal-exit "^3.0.2" typedarray-to-buffer "^3.1.5" +write@1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/write/-/write-1.0.3.tgz#0800e14523b923a387e415123c865616aae0f5c3" + integrity sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig== + dependencies: + mkdirp "^0.5.1" + ws@^7.2.3: version "7.3.1" resolved "https://registry.yarnpkg.com/ws/-/ws-7.3.1.tgz#d0547bf67f7ce4f12a72dfe31262c68d7dc551c8" From 39ca2e530cd108368f49bf3ee1e5f1e64e4fcf1c Mon Sep 17 00:00:00 2001 From: Akshay Nair Date: Sat, 17 Oct 2020 21:38:48 +0530 Subject: [PATCH 06/11] Adds json linting and adds lint to ci --- .eslintrc.json | 4 +++- .github/workflows/checks.yml | 4 ++++ domains/@.json | 2 +- package.json | 3 ++- yarn.lock | 44 ++++++++++++++++++++++++++++++++++++ 5 files changed, 54 insertions(+), 3 deletions(-) diff --git a/.eslintrc.json b/.eslintrc.json index 61b4916de..33dc25fe3 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -1,12 +1,14 @@ { "extends": [ "eslint:recommended", - "plugin:node/recommended" + "plugin:node/recommended", + "plugin:json/recommended" ], "parserOptions": { "ecmaVersion": 2020 }, "rules": { + "indent": ["error", 2], "node/exports-style": ["error", "module.exports"], "node/file-extension-in-import": ["error", "always"], "node/prefer-global/buffer": ["error", "always"], diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index 6ff9fe1cd..2e43a9291 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -15,6 +15,10 @@ jobs: uses: borales/actions-yarn@v2.0.0 with: cmd: install + - name: Linting + uses: borales/actions-yarn@v2.0.0 + with: + cmd: lint - name: Running tests uses: borales/actions-yarn@v2.0.0 with: diff --git a/domains/@.json b/domains/@.json index 1a088239e..60b51ee6c 100644 --- a/domains/@.json +++ b/domains/@.json @@ -6,4 +6,4 @@ "email": "phenax5@gmail.com" }, "record": {} -} \ No newline at end of file +} diff --git a/package.json b/package.json index 9155ff64a..fcdd4252f 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,7 @@ }, "scripts": { "test": "ENV=test jest", - "lint": "eslint utils scripts", + "lint": "eslint utils scripts domains --ext .json,.js", "publish-records": "node ./scripts/register-domains.js" }, "repository": { @@ -28,6 +28,7 @@ }, "devDependencies": { "eslint": "^7.11.0", + "eslint-plugin-json": "^2.1.2", "eslint-plugin-node": "^11.1.0" } } diff --git a/yarn.lock b/yarn.lock index a14509ba4..52437eede 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1278,6 +1278,14 @@ eslint-plugin-es@^3.0.0: eslint-utils "^2.0.0" regexpp "^3.0.0" +eslint-plugin-json@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/eslint-plugin-json/-/eslint-plugin-json-2.1.2.tgz#5bc1c221984583c0c5ff21c488386e8263a6bbb7" + integrity sha512-isM/fsUxS4wN1+nLsWoV5T4gLgBQnsql3nMTr8u+cEls1bL8rRQO5CP5GtxJxaOfbcKqnz401styw+H/P+e78Q== + dependencies: + lodash "^4.17.19" + vscode-json-languageservice "^3.7.0" + eslint-plugin-node@^11.1.0: version "11.1.0" resolved "https://registry.yarnpkg.com/eslint-plugin-node/-/eslint-plugin-node-11.1.0.tgz#c95544416ee4ada26740a30474eefc5402dc671d" @@ -2523,6 +2531,11 @@ json5@^2.1.2: dependencies: minimist "^1.2.5" +jsonc-parser@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-2.3.1.tgz#59549150b133f2efacca48fe9ce1ec0659af2342" + integrity sha512-H8jvkz1O50L3dMZCsLqiuB2tA7muqbSg1AtGEkN0leAqGjsUzDJir3Zwr02BhqdcITPg3ei3mZ+HjMocAknhhg== + jsprim@^1.2.2: version "1.4.1" resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2" @@ -3799,6 +3812,37 @@ verror@1.10.0: core-util-is "1.0.2" extsprintf "^1.2.0" +vscode-json-languageservice@^3.7.0: + version "3.9.1" + resolved "https://registry.yarnpkg.com/vscode-json-languageservice/-/vscode-json-languageservice-3.9.1.tgz#f72b581f8cd2bd9b47445ccf8b0ddcde6aba7483" + integrity sha512-oJkknkdCVitQ5XPSRa0weHjUxt8eSCptaL+MBQQlRsa6Nb8XnEY0S5wYnLUFHzEvKzwt01/LKk8LdOixWEXkNA== + dependencies: + jsonc-parser "^2.3.1" + vscode-languageserver-textdocument "^1.0.1" + vscode-languageserver-types "3.16.0-next.2" + vscode-nls "^5.0.0" + vscode-uri "^2.1.2" + +vscode-languageserver-textdocument@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.1.tgz#178168e87efad6171b372add1dea34f53e5d330f" + integrity sha512-UIcJDjX7IFkck7cSkNNyzIz5FyvpQfY7sdzVy+wkKN/BLaD4DQ0ppXQrKePomCxTS7RrolK1I0pey0bG9eh8dA== + +vscode-languageserver-types@3.16.0-next.2: + version "3.16.0-next.2" + resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.16.0-next.2.tgz#940bd15c992295a65eae8ab6b8568a1e8daa3083" + integrity sha512-QjXB7CKIfFzKbiCJC4OWC8xUncLsxo19FzGVp/ADFvvi87PlmBSCAtZI5xwGjF5qE0xkLf0jjKUn3DzmpDP52Q== + +vscode-nls@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-5.0.0.tgz#99f0da0bd9ea7cda44e565a74c54b1f2bc257840" + integrity sha512-u0Lw+IYlgbEJFF6/qAqG2d1jQmJl0eyAGJHoAJqr2HT4M2BNuQYSEiSE75f52pXHSJm8AlTjnLLbBFPrdz2hpA== + +vscode-uri@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/vscode-uri/-/vscode-uri-2.1.2.tgz#c8d40de93eb57af31f3c715dd650e2ca2c096f1c" + integrity sha512-8TEXQxlldWAuIODdukIb+TR5s+9Ds40eSJrw+1iDDA9IFORPjMELarNQE3myz5XIkWWpdprmJjm1/SxMlWOC8A== + w3c-hr-time@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz#0a89cdf5cc15822df9c360543676963e0cc308cd" From b8ba0eab7cea585b2f6530235dca11e43b22058a Mon Sep 17 00:00:00 2001 From: Akshay Nair Date: Sat, 17 Oct 2020 21:43:56 +0530 Subject: [PATCH 07/11] Adds test to publish record workflow --- .github/workflows/checks.yml | 2 -- .github/workflows/publish-records.yml | 20 ++++++++++++-------- package.json | 4 ++-- 3 files changed, 14 insertions(+), 12 deletions(-) diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index 2e43a9291..f35be82a3 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -1,5 +1,3 @@ -# https://github.com/actions/labeler - name: Checks on: [pull_request_target] diff --git a/.github/workflows/publish-records.yml b/.github/workflows/publish-records.yml index e4b02e011..41f919882 100644 --- a/.github/workflows/publish-records.yml +++ b/.github/workflows/publish-records.yml @@ -7,20 +7,24 @@ on: jobs: publish: runs-on: ubuntu-latest - env: - CI: 1 - ENV: production - DOMAIN_USER: ${{ secrets.DOMAIN_USER }} - DOMAIN_API_KEY: ${{ secrets.DOMAIN_API_KEY }} - DOMAIN_API_HOST: ${{ secrets.DOMAIN_API_HOST }} - DOMAIN_API_PORT: ${{ secrets.DOMAIN_API_PORT }} - DOMAIN_DOMAIN: ${{ secrets.DOMAIN_DOMAIN }} steps: - uses: actions/checkout@v2 - uses: borales/actions-yarn@v2.0.0 with: cmd: install + - name: Running tests + uses: borales/actions-yarn@v2.0.0 + with: + cmd: test - name: Publishing records + env: + CI: 1 + ENV: production + DOMAIN_USER: ${{ secrets.DOMAIN_USER }} + DOMAIN_API_KEY: ${{ secrets.DOMAIN_API_KEY }} + DOMAIN_API_HOST: ${{ secrets.DOMAIN_API_HOST }} + DOMAIN_API_PORT: ${{ secrets.DOMAIN_API_PORT }} + DOMAIN_DOMAIN: ${{ secrets.DOMAIN_DOMAIN }} uses: borales/actions-yarn@v2.0.0 with: cmd: publish-records diff --git a/package.json b/package.json index fcdd4252f..fef02d225 100644 --- a/package.json +++ b/package.json @@ -12,10 +12,10 @@ }, "repository": { "type": "git", - "url": "https://github.com/is-a.dev/register-domain" + "url": "https://github.com/is-a-dev/register" }, "keywords": [ - "__" + "subdomain" ], "author": "Akshay Nair ", "license": "GPL-3.0", From 757013690877ec49b31e7dcb6a6869f9ee3b50bb Mon Sep 17 00:00:00 2001 From: Akshay Nair Date: Sat, 17 Oct 2020 21:57:22 +0530 Subject: [PATCH 08/11] Changes validations --- utils/validations.js | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/utils/validations.js b/utils/validations.js index 57767769a..ad32d88e1 100644 --- a/utils/validations.js +++ b/utils/validations.js @@ -1,17 +1,19 @@ const R = require('ramda'); const { VALID_RECORD_TYPES } = require('./constants'); const { or, and, validate, between, testRegex } = require('./helpers'); +const withLengthGte = n => R.compose(R.gte(R.__, n), R.length); +const withLengthEq = n => R.compose(R.equals(n), R.length); const validateCnameRecord = key => and([ R.propSatisfies(R.is(String), key), - R.compose(R.equals(1), R.length, R.reject(R.equals('URL')), R.keys), - R.propSatisfies(R.compose(R.gte(R.__, 3), R.length), key), + R.compose(withLengthEq(1), R.reject(R.equals('URL')), R.keys), + R.propSatisfies(withLengthGte(3), key), R.propSatisfies(R.complement(testRegex(/^https?:\/\//ig)), key), ]); const validateARecord = key => and([ - R.compose(R.equals(1), R.length, R.keys), - R.propSatisfies(R.compose(R.gte(R.__, 1), R.length), key), + R.compose(withLengthEq(1), R.keys), + R.propSatisfies(withLengthGte(1), key), ]); const validateDomainData = validate({ @@ -33,7 +35,7 @@ const validateDomainData = validate({ R.is(Object), R.complement(R.isEmpty), R.where({ - username: R.is(String), + username: and([ R.is(String), withLengthGte(1) ]), email: R.is(String), }), ]), From 732f76a557e12892d2b3871fe78de9bd964f20d8 Mon Sep 17 00:00:00 2001 From: Akshay Nair Date: Sat, 17 Oct 2020 22:03:12 +0530 Subject: [PATCH 09/11] Cleanup --- .eslintrc.json | 6 ++++++ utils/constants.js | 8 +++++++- utils/helpers.js | 22 ++++++++++++++++++++-- utils/validations.js | 4 +--- 4 files changed, 34 insertions(+), 6 deletions(-) diff --git a/.eslintrc.json b/.eslintrc.json index 33dc25fe3..89af53aa3 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -9,6 +9,12 @@ }, "rules": { "indent": ["error", 2], + "max-len": ["error", { + "code": 100, + "ignoreStrings": true, + "ignoreTemplateLiterals": true, + "ignoreTrailingComments": true + }], "node/exports-style": ["error", "module.exports"], "node/file-extension-in-import": ["error", "always"], "node/prefer-global/buffer": ["error", "always"], diff --git a/utils/constants.js b/utils/constants.js index 086bb0f29..f3ef2b6ac 100644 --- a/utils/constants.js +++ b/utils/constants.js @@ -6,7 +6,13 @@ if (!CI) { require('dotenv').config({ path: path.resolve(`.env.${ENV}`) }); } -const { DOMAIN_USER, DOMAIN_API_KEY, DOMAIN_DOMAIN, DOMAIN_API_HOST, DOMAIN_API_PORT } = process.env; +const { + DOMAIN_USER, + DOMAIN_API_KEY, + DOMAIN_DOMAIN, + DOMAIN_API_HOST, + DOMAIN_API_PORT, +} = process.env; const IS_TEST = ENV === 'test'; diff --git a/utils/helpers.js b/utils/helpers.js index 2c1f61351..171dce8cc 100644 --- a/utils/helpers.js +++ b/utils/helpers.js @@ -8,7 +8,9 @@ const between = (min, max) => num => num >= min && num <= max; const testRegex = regex => str => !!(str && str.match(regex)); const validate = pattern => data => R.compose( - invalidPairs => invalidPairs.length ? { errors: invalidPairs, valid: false } : { errors: [], valid: true }, + invalidPairs => invalidPairs.length + ? { errors: invalidPairs, valid: false } + : { errors: [], valid: true }, R.filter(([key, { fn }]) => fn ? !fn(data[key]) : false), R.toPairs, )(pattern); @@ -30,5 +32,21 @@ const batchLazyTasks = count => tasks => tasks.reduce((batches, task) => { return [...full, [...last, task]]; }, []); -module.exports = { or, and, validate, between, testRegex, log, print, then, lazyTask, batchLazyTasks }; +const withLengthGte = n => R.compose(R.gte(R.__, n), R.length); +const withLengthEq = n => R.compose(R.equals(n), R.length); + +module.exports = { + or, + and, + validate, + between, + testRegex, + log, + print, + then, + lazyTask, + batchLazyTasks, + withLengthEq, + withLengthGte, +}; diff --git a/utils/validations.js b/utils/validations.js index ad32d88e1..cf0c872ec 100644 --- a/utils/validations.js +++ b/utils/validations.js @@ -1,8 +1,6 @@ const R = require('ramda'); const { VALID_RECORD_TYPES } = require('./constants'); -const { or, and, validate, between, testRegex } = require('./helpers'); -const withLengthGte = n => R.compose(R.gte(R.__, n), R.length); -const withLengthEq = n => R.compose(R.equals(n), R.length); +const { or, and, validate, between, testRegex, withLengthEq, withLengthGte } = require('./helpers'); const validateCnameRecord = key => and([ R.propSatisfies(R.is(String), key), From 194e4700c6b515c20bb92ab4a382c08c62fd95fc Mon Sep 17 00:00:00 2001 From: Akshay Nair Date: Sat, 17 Oct 2020 22:12:24 +0530 Subject: [PATCH 10/11] refactors validation --- utils/validations.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/validations.js b/utils/validations.js index cf0c872ec..cb8943692 100644 --- a/utils/validations.js +++ b/utils/validations.js @@ -42,7 +42,7 @@ const validateDomainData = validate({ reason: 'Invalid record. CNAME records have to be a host name and A records has to be a list of ips', fn: and([ R.is(Object), - R.compose(R.isEmpty, R.flip(R.difference)(VALID_RECORD_TYPES), R.keys), + R.compose(R.isEmpty, R.difference(R.__, VALID_RECORD_TYPES), R.keys), R.cond([ [R.has('CNAME'), validateCnameRecord('CNAME')], [R.has('A'), validateARecord('A')], From 9fdc1fac73708d289b54eada86e636c2b99c9c91 Mon Sep 17 00:00:00 2001 From: Akshay Nair Date: Sat, 17 Oct 2020 22:16:02 +0530 Subject: [PATCH 11/11] Adds better validation messages --- utils/validations.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/utils/validations.js b/utils/validations.js index cb8943692..4d0444816 100644 --- a/utils/validations.js +++ b/utils/validations.js @@ -16,7 +16,7 @@ const validateARecord = key => and([ const validateDomainData = validate({ name: { - reason: 'The name of the file is invalid', + reason: 'The name of the file is invalid. It must be lowercased, alphanumeric and more than 2 characters long', fn: or([ R.equals('@'), and([ @@ -28,7 +28,7 @@ const validateDomainData = validate({ description: { reason: '', fn: R.T, }, repo: { reason: '', fn: R.T, }, owner: { - reason: '`owner` needs username and email properties', + reason: '`owner` needs valid username and email properties', fn: and([ R.is(Object), R.complement(R.isEmpty),