diff --git a/.gitignore b/.gitignore index beddd2e..3665cfa 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,4 @@ yarn-error.log* .env.development.local .env.test.local .env.production.local +playground diff --git a/CHANGELOG.md b/CHANGELOG.md index 5b7888d..46eeff7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,154 @@ # deverything +## 0.28.1 + +### Patch Changes + +- fix random pw + +## 0.28.0 + +### Minor Changes + +- company and phone number + +## 0.27.1 + +### Patch Changes + +- guard bool + +## 0.27.0 + +### Minor Changes + +- add math + +## 0.26.0 + +### Minor Changes + +- serializeObject + +## 0.25.2 + +### Patch Changes + +- addresses + +## 0.25.1 + +### Patch Changes + +- clamping + +## 0.25.0 + +### Minor Changes + +- normalize + +## 0.24.0 + +### Minor Changes + +- shuffle + +## 0.23.1 + +### Patch Changes + +- add direct val + +## 0.23.0 + +### Minor Changes + +- checks + +## 0.22.3 + +### Patch Changes + +- more nouns + +## 0.22.2 + +### Patch Changes + +- percentage and dates + +## 0.22.1 + +### Patch Changes + +- random date fix + +## 0.22.0 + +### Minor Changes + +- enums + +## 0.21.2 + +### Patch Changes + +- missing exports + +## 0.21.1 + +### Patch Changes + +- fix pretty + +## 0.21.0 + +### Minor Changes + +- Added moveToIndex helper function + +## 0.20.0 + +### Minor Changes + +- bank accounts + +## 0.19.2 + +### Patch Changes + +- clean spaces pro + +## 0.19.1 + +### Patch Changes + +- consts + +## 0.19.0 + +### Minor Changes + +- fixes and object firsts + +## 0.18.0 + +### Minor Changes + +- add diffs + +## 0.17.1 + +### Patch Changes + +- capitalize + ## 0.17.0 ### Minor Changes -- Updated randomParagraph to have the maxCharacters and words variables +- added promiseWithTimeout() function with test and readme entry ## 0.16.0 diff --git a/README.md b/README.md index adeb269..4f47e36 100644 --- a/README.md +++ b/README.md @@ -28,9 +28,9 @@ Contributions always welcome! - `isJsDate()` if it's a **valid** javascript's Date - `isFutureDate()` - `isPastDate()` - - `isTimestamp()` - `isStringDate()` also checks if the string passed is a **valid** date - `isKey()` is a real key of an object +- `isLastIndex()` is the index is the last item of array - `isNumber()` if the arg is number, and also usable (no infinity) - `isInt()` - `isEven()` @@ -43,14 +43,24 @@ Contributions always welcome! - `isPWA()` - `isReactElement()` - `isRegExp()` +- ⭐ `isSame()` Compare if dates, functions, arrays, objects or anything else are the same - `isServer()` if you are on the server - `isString()` - `isURL()` - `isUUID()` +### Math + +- `average()` +- `max()` +- `min()` +- `sum()` + ### Helpers - `array()` create an arbitrary array based on a function + - `arrayDiff()` + - `arrayIntersection()` - `capitalize()` word => Word - `cleanSpaces()` trims and turns double spaces into single space - `clamp()` clamp number in a range @@ -59,12 +69,18 @@ Contributions always welcome! - `last()` get the last element of an array - ⭐ `merge()` deep merge objects - `moveToFirst()` move array element to first +- `moveToIndex()` move array element to desired index - `moveToLast()` move array element to last +- `normalizeNumber()` normalizes between 0 and 1 +- `objectDiff()` - ⭐ `parseDate()` pass anything Date-Like, and get a JS Date back -- `pretty()` -- `sleep()` -- `toggleArray()` +- `pretty()` stringify anything, without breaking on circular dependencies +- `promiseWithTimeout()` takes a promise, a timeoutMs, and an option error as arguments. Returns a new Promise that either resolves with the value of the input promise or rejects with the provided error or a default error message if the input promise does not resolve or reject within the specified timeoutMs. +- `sleep()` promise-based sleep +- `shuffle()` shuffles elements in an array +- `toggleArrayValue()` remove/add value in array - `truncate()` truncate text, does not break emojis +- `uniqueValues()` gets unique values in an array ### Random data generators @@ -73,7 +89,9 @@ These functions are optimized for low entropy random data generation useful for - `randomAddress()` - `randomAlphaNumericCode()` - ⭐ `randomArrayItem()` +- `randomBankAccount()` - `randomBool()` +- `randomCompany()` - ⭐ `randomCoords()` - `randomLat()` - `randomLng()` @@ -107,23 +125,31 @@ These functions are optimized for low entropy random data generation useful for - `randomNumericId()` autoincremental process-unique id - `randomParagraph()` - `randomPassword()` +- `randomPhoneNumber()` - `randomUUID()` lightweight uuid generation, passing UUID validation - `randomWord()` +### Checks + +Checks are functions that throw an error, if the validation fails + +- ⭐ `checkEnvVars()` Make sure env vars are set per-environment + ### TypeScript Helpers - `Coords` - `DateLike` - `Dimensions` -- `Maybe` - - `MaybePromise` - - `MaybePromiseOrValue` - - `MaybePromiseOrValueArray` +- `Maybe<>` + - `MaybePromise<>` + - `MaybePromiseOrValue<>` + - `MaybePromiseOrValueArray<>` - `NonUndefined` -- `ObjectValues` +- `ObjectKey<>` +- `ObjectValue<>` - `PlainObject` - `Point` -- `PrismaSelect` +- `PrismaSelect<>` ## Development diff --git a/package.json b/package.json index 5645d19..4e62efc 100644 --- a/package.json +++ b/package.json @@ -1,8 +1,8 @@ { "name": "deverything", - "version": "0.17.0", + "version": "0.28.1", "description": "Everything you need for Dev", - "main": "dist/index.js", + "main": "./dist/index.js", "module": "./dist/index.mjs", "types": "./dist/index.d.ts", "sideEffects": false, @@ -24,18 +24,19 @@ "url": "git+https://github.com/codeledge/deverything.git" }, "keywords": [ - "random", - "generator", + "checks", + "dates", "fake", - "testing", - "validators", + "generator", "helpers", - "toolkit", - "words", "numbers", - "dates", + "random", + "testing", + "toolkit", "types", - "utils" + "utils", + "validators", + "words" ], "author": "ogroppo", "license": "MIT", diff --git a/src/_internals/getProp.ts b/src/_internals/getProp.ts new file mode 100644 index 0000000..15cb81f --- /dev/null +++ b/src/_internals/getProp.ts @@ -0,0 +1,15 @@ +import { PlainObject } from "../types"; +import { isFunction } from "../validators"; + +export type PropertyAccessor = keyof T | ((item: T) => any); + +export const getProp = ( + obj: T, + propertyAccessor: PropertyAccessor +) => { + if (isFunction(propertyAccessor)) { + return propertyAccessor(obj); + } + + return obj[propertyAccessor]; +}; diff --git a/src/_internals/loopAllChars.ts b/src/_internals/loopAllChars.ts new file mode 100644 index 0000000..d09a1c5 --- /dev/null +++ b/src/_internals/loopAllChars.ts @@ -0,0 +1,11 @@ +export const loopAllChars = ( + callback: (char: string, charcode: number) => void +) => { + let charcode = 1; + let char; + while (char !== "\x00") { + char = String.fromCharCode(charcode); + callback(char, charcode); + charcode += 1; + } +}; diff --git a/src/_internals/objectSerializer.ts b/src/_internals/objectSerializer.ts new file mode 100644 index 0000000..a77fcfe --- /dev/null +++ b/src/_internals/objectSerializer.ts @@ -0,0 +1,28 @@ +type Replacer = (this: any, key: string, value: any) => any; + +/** + * Stringifies objects without breaking on circular dependencies + * @source https://github.com/moll/json-stringify-safe/blob/master/stringify.js + */ +export const objectSerializer = () => { + const stack: any[] = []; + const keys: string[] = []; + + const cycleReplacer: Replacer = function (_key, value) { + if (stack[0] === value) return "[Circular ~]"; + return "[Circular ~." + keys.slice(0, stack.indexOf(value)).join(".") + "]"; + }; + + return function (this: any, key: string, value: any) { + if (stack.length > 0) { + const thisPos = stack.indexOf(this); + ~thisPos ? stack.splice(thisPos + 1) : stack.push(this); + ~thisPos ? keys.splice(thisPos, Infinity, key) : keys.push(key); + if (~stack.indexOf(value)) value = cycleReplacer.call(this, key, value); + + // TODO: also sort keys! so it can be used for deep serialization + } else stack.push(value); + + return value; + }; +}; diff --git a/src/checks/checkEnvVars.test.ts b/src/checks/checkEnvVars.test.ts new file mode 100644 index 0000000..e82d94b --- /dev/null +++ b/src/checks/checkEnvVars.test.ts @@ -0,0 +1,99 @@ +import { expect, it, describe } from "@jest/globals"; +import { checkEnvVars } from "./checkEnvVars"; + +describe("checkEnvVars", function () { + describe("SimpleValidation", function () { + it("undefined value", function () { + expect(() => + checkEnvVars({ + MISSING: true, + }) + ).toThrow(); + expect(() => + checkEnvVars({ + MISSING: "always", + }) + ).toThrow(); + + expect(() => + checkEnvVars({ + MISSING: false, + }) + ).not.toThrow(); + expect(() => + checkEnvVars({ + MISSING: "never", + }) + ).not.toThrow(); + }); + + it("string value", function () { + process.env.IS_THERE = "true"; + expect(() => + checkEnvVars({ + IS_THERE: true, + }) + ).not.toThrow(); + + expect(() => + checkEnvVars({ + IS_THERE: false, + }) + ).toThrow(); + }); + + it("empty value", function () { + process.env.IS_THERE_EMPTY = ""; + + // DOES throw because empty string could be result of `VAR=` in .env file + expect(() => + checkEnvVars({ + IS_THERE_EMPTY: true, + }) + ).toThrow(); + + expect(() => + checkEnvVars({ + IS_THERE_EMPTY: false, // TODO, send warning for empty string + }) + ).not.toThrow(); + }); + }); + + describe("AdvancedValidation", function () { + process.env.TEST_VAL = "TEST_VAL"; + + it("simple", function () { + expect(() => + checkEnvVars({ + TEST_VAL: { + TEST: true, + DEV: false, + }, + }) + ).not.toThrow(); + }); + it("direct oneOf", function () { + expect(() => + checkEnvVars({ + TEST_VAL: { oneOf: ["TEST_VAL", "TEST_VALDO"] }, + }) + ).not.toThrow(); + }); + + it("env oneOf", function () { + expect(() => + checkEnvVars({ + TEST_VAL: { + TEST: { + oneOf: ["TEST_VAL", "TEST_VALDO"], + }, + STAGE: { + oneOf: ["doesnt", "matter"], + }, + }, + }) + ).not.toThrow(); + }); + }); +}); diff --git a/src/checks/checkEnvVars.ts b/src/checks/checkEnvVars.ts new file mode 100644 index 0000000..e139904 --- /dev/null +++ b/src/checks/checkEnvVars.ts @@ -0,0 +1,186 @@ +import { isArray } from "../validators/isArray"; +import { isObject } from "../validators/isObject"; + +type Keyword = "should" | "shouldNot" | "always" | "never"; +type SimpleValidationRule = Keyword | boolean; +type EnvValidation = string[]; +type AdvancedValidation = { + oneOf?: string[]; + endsWith?: string; + startsWith?: string; +}; +const advancedValidationKeys: (keyof AdvancedValidation)[] = [ + "oneOf", + "endsWith", + "startsWith", +]; +type WithEnvValidation = + | AdvancedValidation + | { + [env: string]: AdvancedValidation | SimpleValidationRule; + }; +type Config = { + processEnvKey?: string; +}; + +/** + * @param envVarsMap + * @param config + * @example + * checkEnvVars({ + * NEW_API_KEY: true, + * OLD_API: false, + * ONLY_NON_PROD: ["test", "dev"], + * ONLY_PROD: { + * prod: true, + * }, + * APP_ENV: { + * oneOf: ["test", "dev", "prod"], + * }, + * STRIPE_KEY: { + * prod: { + * startsWith: "live_key_", + * endsWith: "_end", + * }, + * }, + * }, { + * processEnvKey: "APP_ENV" // default is "NODE_ENV" + * }) + */ +export const checkEnvVars = ( + envVarsMap: Record< + string, + SimpleValidationRule | EnvValidation | WithEnvValidation + >, + config?: Config +) => { + const processEnvKey = config?.processEnvKey || "NODE_ENV"; + const errors: string[] = []; + const warnings: string[] = []; + + const validateAdvanced = ({ + envVarKey, + envVarValue, + validation, + }: { + envVarKey: string; + envVarValue: string | undefined; + validation: AdvancedValidation; + }) => { + if (validation.oneOf) { + if (envVarValue) { + if (!validation.oneOf.includes(envVarValue)) + errors.push( + `${envVarKey}=${envVarValue} is not allowed, use one of: ${validation.oneOf.join( + ", " + )}` + ); + } else { + errors.push(`${envVarKey} is missing`); + } + } + + if (validation.endsWith) { + if (envVarValue) { + if (!envVarValue?.endsWith(validation.endsWith)) { + errors.push( + `${envVarKey}=${envVarValue} is not allowed, must end in: ${validation.endsWith}` + ); + } + } else { + errors.push(`${envVarKey} is missing`); + } + } + + if (validation.startsWith) { + if (envVarValue) { + if (!envVarValue?.startsWith(validation.startsWith)) { + errors.push( + `${envVarKey}=${envVarValue} is not allowed, must start with: ${validation.startsWith}` + ); + } + } else { + errors.push(`${envVarKey} is missing`); + } + } + }; + + const validateSimple = ({ + envVarKey, + envVarValue, + rule, + }: { + envVarKey: string; + envVarValue: string | undefined; + rule: SimpleValidationRule; + }) => { + switch (rule) { + case "should": + if (!envVarValue) warnings.push(`${envVarKey} should be set`); + break; + case "shouldNot": + if (envVarValue) warnings.push(`${envVarKey} should not be set`); + break; + case "never": + case false: + if (envVarValue) errors.push(`${envVarKey} is not allowed`); + break; + case "always": + case true: + default: // safety net if ts gets ignored + if (!envVarValue) errors.push(`${envVarKey} is missing`); + break; + } + }; + + Object.entries(envVarsMap).forEach(([envVarKey, rule]) => { + const envVarValue = process.env[envVarKey]; + if (isObject(rule)) { + // Direct + Object.entries(rule).forEach(([advancedValidationKey, rule]) => { + if (advancedValidationKeys.includes(advancedValidationKey as any)) + validateAdvanced({ + envVarValue, + validation: { [advancedValidationKey]: rule }, + envVarKey, + }); + }); + + //With Env + Object.entries(rule).forEach(([envName, rule]) => { + if (process.env[processEnvKey] === envName) { + if (isObject(rule)) + validateAdvanced({ + envVarValue, + validation: rule, + envVarKey, + }); + else + validateSimple({ + envVarValue, + rule: rule as SimpleValidationRule, + envVarKey, + }); + } + }); + } else if (isArray(rule)) { + const envNames = rule; + envNames.forEach((envName) => { + if (process.env[processEnvKey] === envName && !envVarValue) + errors.push(`${envVarKey} is missing`); + }); + } else { + validateSimple({ + envVarValue, + rule, + envVarKey, + }); + } + }); + + if (warnings.length) console.warn(`[WARNING] ` + warnings.join(", ")); + + if (errors.length) { + throw new Error(`[ERROR] ` + errors.join(", ")); + } +}; diff --git a/src/checks/index.ts b/src/checks/index.ts new file mode 100644 index 0000000..1cd3d99 --- /dev/null +++ b/src/checks/index.ts @@ -0,0 +1 @@ +export * from "./checkEnvVars"; diff --git a/src/constants/addresses.ts b/src/constants/addresses.ts index d76e1aa..06cba24 100644 --- a/src/constants/addresses.ts +++ b/src/constants/addresses.ts @@ -1,41 +1,61 @@ -export const UK_ADDRESSES = [ +export type Address = { + city: string; + country: string; + countryCode: string; + state?: string; + street: string; + line2?: string; + number: string; + zip: string; +}; + +export const UK_ADDRESSES: Address[] = [ { city: "London", country: "United Kingdom", - street: "Baker Street", + countryCode: "GB", + line2: "Marylebone", number: "221B", + street: "Baker Street", zip: "NW1 6XE", }, { city: "Birmingham", country: "United Kingdom", - street: "Bordesley Street", + countryCode: "GB", number: "B1 1AA", + street: "Bordesley Street", zip: "B1 1AA", }, ]; -export const US_ADDRESSES = [ +export const US_ADDRESSES: Address[] = [ { city: "New York", country: "United States", + countryCode: "US", + state: "NY", street: "Broadway", + line2: "Manhattan", number: "42", zip: "10036", }, { city: "Los Angeles", country: "United States", + countryCode: "US", + state: "CA", street: "Hollywood Boulevard", number: "6801", zip: "90028", }, ]; -export const EUROPE_ADDRESSES = [ +export const EUROPE_ADDRESSES: Address[] = [ { city: "Paris", country: "France", + countryCode: "FR", street: "Rue de Rivoli", number: "75001", zip: "75001", @@ -43,13 +63,16 @@ export const EUROPE_ADDRESSES = [ { city: "Berlin", country: "Germany", + countryCode: "DE", street: "Unter den Linden", + line2: "Mitte", number: "10117", zip: "10117", }, { city: "Rome", country: "Italy", + countryCode: "IT", street: "Via del Corso", number: "00186", zip: "00186", @@ -57,16 +80,19 @@ export const EUROPE_ADDRESSES = [ { city: "Madrid", country: "Spain", + countryCode: "ES", street: "Gran Vía", + line2: "Sol", number: "28013", zip: "28013", }, ]; -export const MIXED_ADDRESSES = [ +export const MIXED_ADDRESSES: Address[] = [ { city: "Moscow", country: "Russia", + countryCode: "RU", street: "Tverskaya", number: "101000", zip: "101000", @@ -74,13 +100,16 @@ export const MIXED_ADDRESSES = [ { city: "Tokyo", country: "Japan", + countryCode: "JP", street: "Shinjuku", + line2: "Shinjuku City", number: "160-0022", zip: "160-0022", }, { city: "Beijing", country: "China", + countryCode: "CN", street: "Changan", number: "100005", zip: "100005", @@ -88,14 +117,15 @@ export const MIXED_ADDRESSES = [ { city: "Cairo", country: "Egypt", + countryCode: "EG", street: "Al-Muizz", number: "11511", - zip: "11511", }, { city: "Buenos Aires", country: "Argentina", + countryCode: "AR", street: "Avenida de Mayo", number: "1002", zip: "C1006AAQ", @@ -103,6 +133,7 @@ export const MIXED_ADDRESSES = [ { city: "Cape Town", country: "South Africa", + countryCode: "ZA", street: "Adderley", number: "7700", zip: "7700", @@ -110,13 +141,16 @@ export const MIXED_ADDRESSES = [ { city: "Sydney", country: "Australia", + countryCode: "AU", street: "George", + line2: "Haymarket", number: "2000", zip: "2000", }, { city: "Rio de Janeiro", country: "Brazil", + countryCode: "BR", street: "Av. Presidente Vargas", number: "20021-000", zip: "20021-000", @@ -124,59 +158,16 @@ export const MIXED_ADDRESSES = [ { city: "Mexico City", country: "Mexico", + countryCode: "MX", street: "Paseo de la Reforma", number: "06500", zip: "06500", }, ]; -export const ADDRESSES = [ +export const ADDRESSES: Address[] = [ ...UK_ADDRESSES, ...US_ADDRESSES, ...EUROPE_ADDRESSES, ...MIXED_ADDRESSES, ]; - -export const CITIES = [ - "London", - "Birmingham", - "New York", - "Los Angeles", - "Paris", - "Berlin", - "Rome", - "Madrid", - "Moscow", - "Tokyo", - "Beijing", - "Cairo", - "Buenos Aires", - "Cape Town", - "Sydney", - "Rio de Janeiro", - "Mexico City", -]; - -export const US_STATE_CODE_SAMPLES = [ - "AL", - "AK", - "AZ", - "AR", - "CA", - "CO", - "CT", - "DE", - "FL", - "GA", - "HI", -]; - -export const ZIP_SAMPLES = [ - "10036", - "90028", - "75001", - "10117", - "00186", - "28013", - "101000", -]; diff --git a/src/constants/banking.ts b/src/constants/banking.ts index 0a4043d..fdc040f 100644 --- a/src/constants/banking.ts +++ b/src/constants/banking.ts @@ -3,16 +3,6 @@ export const IBAN_SAMPLES = [ "BA391290079401028494", "BE68539007547034", "BG80BNBG96611020345678", - "BH67BMAG00001299123456", - "BY13NBRB3600900000002Z00AB00", - "CH9300762011623852957", - "CR05015202001026284066", - "CY17002001280000001200527600", - "CZ6508000000192000145399", - "DE89370400440532013000", - "DO28BAGR00000001212453611324", - "EE382200221020145685", - "ES9121000418450200051332", "FI2112345600000785", "FO6264600001631634", "FR1420041010050500013M02606", @@ -31,16 +21,13 @@ export const BIC_SWIFT_SAMPLES = [ ]; export const CARD_NUMBER_SAMPLES = [ - "4539148803436467", - "4929722653797141", - "4556364607930798", - "4539148803436467", - "4929722653797141", - "4556364607930798", - "4539148803436467", - "4929722653797141", - "4556364607930798", - "4539148803436467", + "371449635398431", //american express + "30569309025904", //diners club + "6011111111111117", //discover + "3530111333300000", //jcb + "6304000000000000", //laser + "5555555555554444", //mastercard + "4111111111111111", //visa ]; //An ABA routing number is a nine-digit code that identifies banks in the U.S. @@ -66,30 +53,133 @@ export const BANK_ACCOUNT_NUMBER_SAMPLES = [ "72796383", ]; -export const VAT_REGISTRATION_NUMBER_SAMPLES = [ - "IE1234567T", - "GB123456789", - "XI123456789", -]; - export const SORT_CODE_SAMPLES = ["100000", "902127", "800551"]; export const SSN_SAMPLES = ["235-55-7216", "372-37-3976", "414-99-6488"]; export const BANK_NAME_SAMPLES = [ - "Bank of England", - "Bank of Ireland", - "Bank of Scotland", - "Bank of America", + "JPMorgan Chase & Co.", + "Bank of America Corporation", + "Wells Fargo & Company", + "Citigroup Inc.", + "Goldman Sachs Group Inc.", +]; + +export type BankAccount = { + abaNumber?: string; + accountHolderName: string; + accountHolderType: "company" | "individual" | "other"; + accountNumber: string; + accountType?: "checking" | "savings"; + bankName?: string; + bsbNumber?: string; + bankAddress?: string; + bicSwift?: string; + branchCode?: string; + iban?: string; + routingNumber?: string; + institutionNumber?: string; + branchTransitNumber?: string; + sortCode?: string; +}; + +export const UK_BANK_ACCOUNT_SAMPLES: BankAccount[] = [ + { + accountHolderName: "John Peters", + accountNumber: "12345678", + bankAddress: "1 Churchill Place, Canary Wharf, London, E14 5HP, UK", + bankName: "Barclays plc", + bicSwift: "BARCGB22", + iban: "GB51BARC20039534871253", + sortCode: "12-34-56", + accountHolderType: "individual", + }, + { + accountHolderName: "Jane Evans", + accountNumber: "87654321", + bankAddress: "8 Canada Square, London, E14 5HQ, UK", + bankName: "HSBC Holdings plc", + bicSwift: "HSBCGB2L", + iban: "GB82BARC20031847813531", + sortCode: "65-43-21", + accountHolderType: "company", + }, +]; + +export const US_BANK_ACCOUNT_SAMPLES: BankAccount[] = [ + { + accountHolderName: "Jack I. Taylor", + accountNumber: "123456789012", + accountType: "checking", + bankAddress: + "Bank of America Corporate Center, 100 North Tryon Street, Charlotte, NC 28255, USA", + bankName: "Bank of America Corporation", + routingNumber: "111000025", + accountHolderType: "individual", + }, + { + accountHolderName: "Sally T King", + accountNumber: "987654321098", + accountType: "savings", + bankAddress: "383 Madison Avenue, New York, NY 10179, USA", + bankName: "JPMorgan Chase & Co.", + routingNumber: "021000021", + accountHolderType: "company", + }, +]; + +export const AU_BANK_ACCOUNT_SAMPLES: BankAccount[] = [ + { + accountHolderName: "William Kevin White", + accountNumber: "123456789012", + accountType: "savings", + bankAddress: + "Commonwealth Bank Centre, Tower 1, 201 Sussex Street, Sydney, NSW 2000, Australia", + bankName: "Commonwealth Bank of Australia", + bicSwift: "CTBAAU2S", + bsbNumber: "062-000", + accountHolderType: "individual", + }, + { + accountHolderName: "Jennifer Ann Brown", + accountNumber: "987654321098", + accountType: "checking", + bankAddress: "Westpac Place, 275 Kent Street, Sydney, NSW 2000, Australia", + bankName: "Westpac Banking Corporation", + bicSwift: "WPACAU2S", + bsbNumber: "032-001", + accountHolderType: "company", + }, +]; + +export const CA_BANK_ACCOUNT_SAMPLES: BankAccount[] = [ + { + accountHolderName: "Charles Green", + accountNumber: "123456789012", + accountType: "savings", + bankAddress: + "Royal Bank Plaza, 200 Bay Street, North Tower, Toronto, ON M5J 2J5, Canada", + bankName: "Royal Bank of Canada", + branchTransitNumber: "45678", + institutionNumber: "123", + accountHolderType: "individual", + }, + { + accountHolderName: "Olivia Orange", + accountNumber: "987654321098", + accountType: "checking", + bankAddress: + "TD Canada Trust Tower, 161 Bay Street, Toronto, ON M5J 2S8, Canada", + bankName: "Toronto-Dominion Bank", + branchTransitNumber: "65432", + institutionNumber: "987", + accountHolderType: "company", + }, ]; -export const COMPANY_NAME_SAMPLES = [ - "Acme Inc.", - "Acme Corp.", - "Acme Ltd.", - "Acme LLC", - "Umbrella Corp.", - "Umbrella Inc.", - "Umbrella Ltd.", - "Umbrella LLC", +export const BANK_ACCOUNT_SAMPLES = [ + ...UK_BANK_ACCOUNT_SAMPLES, + ...US_BANK_ACCOUNT_SAMPLES, + ...AU_BANK_ACCOUNT_SAMPLES, + ...CA_BANK_ACCOUNT_SAMPLES, ]; diff --git a/src/constants/companies.ts b/src/constants/companies.ts new file mode 100644 index 0000000..c0cdc5f --- /dev/null +++ b/src/constants/companies.ts @@ -0,0 +1,19 @@ +export const VAT_REGISTRATION_NUMBER_SAMPLES = [ + "IE1234567T", + "GB123456789", + "XI123456789", +]; + +export const COMPANY_NAME_SAMPLES = [ + "Acme Inc.", + "Globex Ltd.", + "Aurora LLC", + "Serenity Systems", + "Vulcan Ventures", + "Umbrella Corp.", +]; + +export type Company = { + name: string; + vatRegNumber?: string; +}; diff --git a/src/constants/domains.ts b/src/constants/domains.ts index 7cde016..7536178 100644 --- a/src/constants/domains.ts +++ b/src/constants/domains.ts @@ -6,16 +6,12 @@ export const DOMAIN_NAMES = [ "msn.com", "comcast.net", "live.com", - "sbcglobal.net", - "verizon.net", "att.net", "mac.com", "me.com", - "earthlink.net", "charter.net", "shaw.ca", "yahoo.ca", - "googlemail.com", "mail.com", "qq.com", "web.de", diff --git a/src/constants/index.ts b/src/constants/index.ts deleted file mode 100644 index 139597f..0000000 --- a/src/constants/index.ts +++ /dev/null @@ -1,2 +0,0 @@ - - diff --git a/src/constants/names.ts b/src/constants/names.ts index b611308..774056c 100644 --- a/src/constants/names.ts +++ b/src/constants/names.ts @@ -1,68 +1,76 @@ export const ANIMAL_NAMES = [ - "Aardvark", "Albatross", - "Alligator", - "Alpaca", - "Ant", - "Anteater", - "Antelope", - "Ape", + "Dolphin", + "Elephant", + "Giraffe", + "Koala", + "Lion", + "Penguin", + "Squirrel", + "Tiger", + "Turtle", + "Whale", + "Zebra", ]; export const TOOL_NAMES = [ "Axe", - "Ball Peen Hammer", - "Band Saw", - "Bench Grinder", - "Biscuit Joiner", - "Bolt Cutter", - "Boring Machine", - "Bow Saw", + "Chisel", + "Drill", + "Hammer", + "Mallet", + "Pliers", + "Saw", + "Screwdriver", + "Wrench", + "Blowtorch", + "Crowbar", + "Ladder", ]; -export const LATIN_FIRST_NAMES = [ - "Adam", +export const WESTERN_FIRST_NAMES = [ "Adrian", - "Alan", "Albert", "Alexander", "Alice", "Amanda", - "Emma", - "Amelia", "Amy", - "Andrew", + "Benjamin", + "David", + "Emma", "Esther", "Olivia", "Ruby", + "Sarah", + "Victoria", ]; -export const LATIN_LAST_NAMES = [ - "Smith", - "Johnson", - "Williams", - "Jones", +export const WESTERN_LAST_NAMES = [ + "Anderson", "Brown", "Davis", + "Jackson", + "Johnson", + "Jones", "Miller", - "Wilson", "Moore", + "Smith", "Taylor", - "Anderson", "Thomas", - "Jackson", "White", + "Williams", + "Wilson", ]; export const CYRILLIC_FIRST_NAMES = [ "Абигаил", + "Агнес", "Адам", "Адриан", - "Агнес", "Алан", - "Альберт", "Александр", "Алиса", + "Альберт", "Аманда", "Амелия", "Эми", @@ -70,95 +78,95 @@ export const CYRILLIC_FIRST_NAMES = [ ]; export const CYRILLIC_LAST_NAMES = [ - "Смит", - "Джонсон", - "Уильямс", - "Джонс", + "Андерсон", "Браун", + "Вилсон", + "Джексон", + "Джонс", + "Джонсон", "Дэвис", "Миллер", - "Вилсон", "Мур", + "Смит", "Тейлор", - "Андерсон", "Томас", - "Джексон", "Уайт", + "Уильямс", ]; export const JAPANESE_FIRST_NAMES = [ - "アビゲイル", + "アグネス", "アダム", "アドリアン", - "アグネス", + "アビゲイル", + "アマンダ", + "アミー", + "アメリア", "アラン", + "アリス", "アルバート", "アレクサンダー", - "アリス", - "アマンダ", - "アメリア", - "アミー", "アンドリュー", ]; export const JAPANESE_LAST_NAMES = [ - "スミス", - "ジョンソン", + "アンダーソン", "ウィリアムズ", - "ジョーンズ", - "ブラウン", - "デイビス", - "ミラー", "ウィルソン", - "モア", + "ジャクソン", + "ジョーンズ", + "ジョンソン", + "スミス", "タイラー", - "アンダーソン", + "デイビス", "トーマス", - "ジャクソン", + "ブラウン", "ホワイト", + "ミラー", + "モア", ]; export const ARABIC_FIRST_NAMES = [ - "أبيجيل", - "آدم", "آدريان", - "أغنيس", + "آدم", "آلان", "آلبرت", - "ألكسندر", "آليس", "آماندا", - "آميليا", "آمي", + "آميليا", + "أبيجيل", + "أغنيس", + "ألكسندر", "أندرو", ]; export const ARABIC_LAST_NAMES = [ - "سميث", - "جونسون", - "ويليامز", - "جونز", + "أندرسون", "براون", - "ديفيس", - "ميلر", - "ويلسون", - "مور", "تايلور", - "أندرسون", "توماس", "جاكسون", + "جونز", + "جونسون", + "ديفيس", + "سميث", + "مور", + "ميلر", "وايت", + "ويلسون", + "ويليامز", ]; export const FIRST_NAMES = [ - ...LATIN_FIRST_NAMES, + ...WESTERN_FIRST_NAMES, ...CYRILLIC_FIRST_NAMES, ...JAPANESE_FIRST_NAMES, ...ARABIC_FIRST_NAMES, ]; export const LAST_NAMES = [ - ...LATIN_LAST_NAMES, + ...WESTERN_LAST_NAMES, ...CYRILLIC_LAST_NAMES, ...JAPANESE_LAST_NAMES, ...ARABIC_LAST_NAMES, diff --git a/src/constants/phoneNumbers.ts b/src/constants/phoneNumbers.ts new file mode 100644 index 0000000..cf6d2df --- /dev/null +++ b/src/constants/phoneNumbers.ts @@ -0,0 +1,7 @@ +export const PHONE_NUMBER_SAMPLES = [ + "+44 20 7123 4567", // (United Kingdom) + "+33 1 45 67 89 10", // (France) + "+81 3 1234 5678", //(Japan) + "+61 2 9876 5432", //(Australia) + "+49 30 9876 5432", //(Germany) +]; diff --git a/src/constants/primitives.ts b/src/constants/primitives.ts new file mode 100644 index 0000000..a51d512 --- /dev/null +++ b/src/constants/primitives.ts @@ -0,0 +1,20 @@ +export const PRIMITIVES_OBJECT = { + str: "string", + num: 0, + obj: { foo: "foo" }, + arr: [1, 2, 3], + bool: true, + nil: null, + undef: undefined, + exp: 1e13, + inf: Infinity, + date: new Date("Thu, 28 Apr 2016 22:02:17 GMT"), + map: new Map([["hello", "world"]]), + set: new Set([123, 456]), + void: void 0, + fn: () => { + return void 0; + }, + re: /([^\s]+)/g, + bigInt: BigInt(10), +}; diff --git a/src/constants/words.ts b/src/constants/words.ts index 7752360..e5eb0a6 100644 --- a/src/constants/words.ts +++ b/src/constants/words.ts @@ -25,34 +25,37 @@ export const VERBS = [ ]; export const NOUNS = [ - "abandon", - "ability", - "able", - "abortion", - "about", - "above", - "abroad", - "absence", - "absent", - "absolute", + "courage", + "family", + "food", + "friend", + "fun", + "hope", + "justice", + "life", + "love", + "music", + "smile", + "time", ]; export const ADJECTIVES = [ - "abandoned", - "abdominal", - "ability", - "able", - "abnormal", - "abolish", - "abortion", - "about", - "above", - "abroad", - "absence", - "absent", "absolute", + "compassionate", + "cozy", + "dull", + "enigmatic", + "fascinating", + "interesting", + "playful", + "remarkable", + "sunny", + "unforgettable", + "wonderful", + "predictable", ]; +// TODO: curate export const ADVERBS = [ "abnormally", "aboard", diff --git a/src/helpers/array.test.ts b/src/helpers/array.test.ts new file mode 100644 index 0000000..22bb91e --- /dev/null +++ b/src/helpers/array.test.ts @@ -0,0 +1,8 @@ +import { describe, expect, test } from "@jest/globals"; +import { array } from "./array"; + +describe("array", () => { + test("no arg", async () => { + expect(array(3)).toStrictEqual([undefined, undefined, undefined]); + }); +}); diff --git a/src/helpers/array.ts b/src/helpers/array.ts index 16e7bc4..4daf2de 100644 --- a/src/helpers/array.ts +++ b/src/helpers/array.ts @@ -1,6 +1,6 @@ export const array = any>( length: number, - mapFn: U + mapFn: U = (() => {}) as U ): ReturnType[] => { return Array.from({ length }, mapFn); }; diff --git a/src/helpers/arrayDiff.test.ts b/src/helpers/arrayDiff.test.ts new file mode 100644 index 0000000..4c18f03 --- /dev/null +++ b/src/helpers/arrayDiff.test.ts @@ -0,0 +1,14 @@ +import { describe, expect, test } from "@jest/globals"; +import { arrayDiff } from "./arrayDiff"; + +describe("arrayDiff", () => { + test("no arg", async () => { + expect(arrayDiff([], [])).toStrictEqual([]); + }); + + test("args", async () => { + expect(arrayDiff([1], [2])).toStrictEqual([1, 2]); + expect(arrayDiff([1, 1], [1, 2, 2, 2])).toStrictEqual([2]); + expect(arrayDiff([1, 2, 3], [1, 2])).toStrictEqual([3]); + }); +}); diff --git a/src/helpers/arrayDiff.ts b/src/helpers/arrayDiff.ts new file mode 100644 index 0000000..d23f4a7 --- /dev/null +++ b/src/helpers/arrayDiff.ts @@ -0,0 +1,9 @@ +import { uniqueValues } from "./uniqueValues"; + +export const arrayDiff = (arr1: any[], arr2: any[]) => { + return uniqueValues( + arr1 + .filter((value) => !arr2.includes(value)) + .concat(arr2.filter((value) => !arr1.includes(value))) + ); +}; diff --git a/src/helpers/arrayIntersection.test.ts b/src/helpers/arrayIntersection.test.ts new file mode 100644 index 0000000..04c0ca7 --- /dev/null +++ b/src/helpers/arrayIntersection.test.ts @@ -0,0 +1,14 @@ +import { describe, expect, test } from "@jest/globals"; +import { arrayIntersection } from "./arrayIntersection"; + +describe("arrayIntersection", () => { + test("no arg", async () => { + expect(arrayIntersection([], [])).toStrictEqual([]); + }); + + test("args", async () => { + expect(arrayIntersection([1], [2])).toStrictEqual([]); + expect(arrayIntersection([1, 1], [1, 2])).toStrictEqual([1]); + expect(arrayIntersection([1, 2, 3], [1, 2])).toStrictEqual([1, 2]); + }); +}); diff --git a/src/helpers/arrayIntersection.ts b/src/helpers/arrayIntersection.ts new file mode 100644 index 0000000..f680364 --- /dev/null +++ b/src/helpers/arrayIntersection.ts @@ -0,0 +1,5 @@ +import { uniqueValues } from "./uniqueValues"; + +export const arrayIntersection = (arr1: any[], arr2: any[]) => { + return uniqueValues(arr1.filter((value) => arr2.includes(value))); +}; diff --git a/src/helpers/capitalize.test.ts b/src/helpers/capitalize.test.ts new file mode 100644 index 0000000..e1123f3 --- /dev/null +++ b/src/helpers/capitalize.test.ts @@ -0,0 +1,12 @@ +import {describe, expect, test} from "@jest/globals" +import {capitalize} from "./capitalize"; + +describe("capitalize", () => { + test("args", async () => { + expect(capitalize("test")).toBe("Test"); + expect(capitalize("Test")).toBe("Test"); + expect(capitalize("tEST")).toBe("Test"); + expect(capitalize("TEST")).toBe("Test"); + expect(capitalize("TEST Test")).toBe("Test test"); + }) +}) \ No newline at end of file diff --git a/src/helpers/capitalize.ts b/src/helpers/capitalize.ts index 2b68174..c7b56bf 100644 --- a/src/helpers/capitalize.ts +++ b/src/helpers/capitalize.ts @@ -1,3 +1,3 @@ export const capitalize = (string: string) => { - return string.charAt(0).toUpperCase() + string.slice(1); + return string.charAt(0).toUpperCase() + string.slice(1).toLowerCase(); }; diff --git a/src/helpers/cleanSpaces.test.ts b/src/helpers/cleanSpaces.test.ts index 3be270f..2d5a22e 100644 --- a/src/helpers/cleanSpaces.test.ts +++ b/src/helpers/cleanSpaces.test.ts @@ -1,4 +1,9 @@ import { describe, expect, test } from "@jest/globals"; +import { + ARABIC_FIRST_NAMES, + CYRILLIC_FIRST_NAMES, + JAPANESE_FIRST_NAMES, +} from "../constants/names"; import { cleanSpaces } from "./cleanSpaces"; describe("cleanSpaces", () => { @@ -6,8 +11,32 @@ describe("cleanSpaces", () => { expect(cleanSpaces(" ")).toBe(""); expect(cleanSpaces(" ")).toBe(""); expect(cleanSpaces(" \n ")).toBe(""); + expect(cleanSpaces(" \t ")).toBe(""); + expect(cleanSpaces(" \f ")).toBe(""); + expect(cleanSpaces(" \r ")).toBe(""); + expect(cleanSpaces(" \b ")).toBe(""); + expect(cleanSpaces(" ab\ba")).toBe("ab a"); // TODO \b is not a space technically? + expect(cleanSpaces(" \v ")).toBe(""); + expect(cleanSpaces("\n")).toBe(""); + expect(cleanSpaces("\t")).toBe(""); + expect(cleanSpaces("\f")).toBe(""); + expect(cleanSpaces("\r")).toBe(""); + expect( + cleanSpaces( + String.fromCharCode(8233) + "" + String.fromCharCode(8232) + " a" + ) + ).toBe("a"); + expect(cleanSpaces("t\na±!@£$%^&*()_`")).toBe("t a±!@£$%^&*()_`"); + expect(cleanSpaces("t\r\na")).toBe("t a"); + expect(cleanSpaces("t\t\ta")).toBe("t a"); + expect(cleanSpaces("t\fa")).toBe("t a"); + expect(cleanSpaces("t\f\b\ba")).toBe("t a"); expect(cleanSpaces(" asd asd ")).toBe("asd asd"); - expect(cleanSpaces("gino\npaoli")).toBe("gino\npaoli"); - expect(cleanSpaces("gino\n paoli")).toBe("gino paoli"); // How to preserve the new line? + expect(cleanSpaces("gino\npaoli")).toBe("gino paoli"); + expect(cleanSpaces("gino\n \vpaoli")).toBe("gino paoli"); + expect(cleanSpaces("00000")).toBe("00000"); + expect(cleanSpaces(CYRILLIC_FIRST_NAMES[0])).toBe(CYRILLIC_FIRST_NAMES[0]); + expect(cleanSpaces(JAPANESE_FIRST_NAMES[0])).toBe(JAPANESE_FIRST_NAMES[0]); + expect(cleanSpaces(ARABIC_FIRST_NAMES[0])).toBe(ARABIC_FIRST_NAMES[0]); }); }); diff --git a/src/helpers/cleanSpaces.ts b/src/helpers/cleanSpaces.ts index d7c5037..e1e3b6c 100644 --- a/src/helpers/cleanSpaces.ts +++ b/src/helpers/cleanSpaces.ts @@ -1,3 +1,14 @@ +import { controlCharRegex } from "../regex/controlCharRegex"; +import { invisibleWhitespaceRegex } from "../regex/invisibleWhitespaceRegex"; +import { lineSeparatorRegex } from "../regex/lineSeparatorRegex"; +import { paragraphSeparatorRegex } from "../regex/paragraphSeparatorRegex"; + export const cleanSpaces = (str: string): string => { - return str.trim().replace(/\s{2,}/g, " "); + return str + .replace(controlCharRegex, " ") + .replace(invisibleWhitespaceRegex, " ") + .replace(lineSeparatorRegex, " ") + .replace(paragraphSeparatorRegex, " ") + .trim() // leave trim after replace, so this test can pass `expect(cleanSpaces(" \b ")).toBe("");` + .replace(/\s{2,}/g, " "); // replace multiple spaces with one space }; diff --git a/src/helpers/dir.ts b/src/helpers/dir.ts new file mode 100644 index 0000000..1c6476a --- /dev/null +++ b/src/helpers/dir.ts @@ -0,0 +1,6 @@ +/** + * Print or log helper that does not break on circular references, and expands nested objects. + */ +export const dir = (arg: any, depth = 5): void => { + console.dir(arg, { depth }); +}; diff --git a/src/helpers/enumKeys.ts b/src/helpers/enumKeys.ts new file mode 100644 index 0000000..f22723f --- /dev/null +++ b/src/helpers/enumKeys.ts @@ -0,0 +1,8 @@ +import { ObjectKeys } from "../types/Object"; +import { isNumeric } from "../validators/isNumeric"; + +export const enumKeys = (arg: T): ObjectKeys => { + return Object.keys(arg).filter( + (key) => !isNumeric(key) // enum key cannot be a number + ) as ObjectKeys; // Type 'string' is not assignable to type 'keyof T'. +}; diff --git a/src/helpers/enumValues.ts b/src/helpers/enumValues.ts new file mode 100644 index 0000000..7660d88 --- /dev/null +++ b/src/helpers/enumValues.ts @@ -0,0 +1,16 @@ +import { ObjectValue, ObjectKey, ObjectValues } from "../types/Object"; +import { isKey } from "../validators/isKey"; + +export const enumValues = ( + enumObject: T +): ObjectValues => { + let values: ObjectValues = []; + + Object.values(enumObject).forEach((value) => { + // types are tricky here because the value is used also to check if exists as a key + if (isKey(value, enumObject) && !values.includes(value as ObjectValue)) + values.push(enumObject[value as ObjectKey]); + }); + + return values; +}; diff --git a/src/helpers/firstKey.ts b/src/helpers/firstKey.ts new file mode 100644 index 0000000..fece476 --- /dev/null +++ b/src/helpers/firstKey.ts @@ -0,0 +1,4 @@ +import { PlainObject } from "../types"; +import { getKeys } from "./getKeys"; + +export const firstKey = (arg: PlainObject): string => getKeys(arg)[0]; diff --git a/src/helpers/firstValue.ts b/src/helpers/firstValue.ts new file mode 100644 index 0000000..ffb673e --- /dev/null +++ b/src/helpers/firstValue.ts @@ -0,0 +1,3 @@ +import { PlainObject } from "../types"; + +export const firstValue = (arg: PlainObject): any => Object.values(arg)[0]; diff --git a/src/helpers/getKeys.test.ts b/src/helpers/getKeys.test.ts new file mode 100644 index 0000000..3f62e56 --- /dev/null +++ b/src/helpers/getKeys.test.ts @@ -0,0 +1,21 @@ +import { describe, expect, test } from "@jest/globals"; +import { getKeys } from "./getKeys"; + +describe("getKeys", () => { + test("{}", async () => { + expect(getKeys(new Date())).toStrictEqual([]); + expect(getKeys(Date)).toStrictEqual([]); + expect(getKeys(Function)).toStrictEqual([]); + expect(getKeys(new Function())).toStrictEqual([]); + expect(getKeys(new Object())).toStrictEqual([]); + expect(getKeys(new Array())).toStrictEqual([]); + }); + + test("keys", async () => { + expect(getKeys({ a: 1, b: 2 })).toStrictEqual(["a", "b"]); + expect(getKeys({ [Symbol.for("1")]: 1, b: 2 })).toStrictEqual([ + "b", + Symbol.for("1"), + ]); + }); +}); diff --git a/src/helpers/getUrlSearchParam.test.ts b/src/helpers/getUrlSearchParam.test.ts new file mode 100644 index 0000000..d4cb302 --- /dev/null +++ b/src/helpers/getUrlSearchParam.test.ts @@ -0,0 +1,36 @@ +import { describe, expect, test } from "@jest/globals"; +import { getUrlSearchParam } from "./getUrlSearchParam"; + +describe("getUrlSearchParam", () => { + test("undefined", async () => { + expect(getUrlSearchParam(undefined, "")).toBeUndefined(); + expect(getUrlSearchParam(undefined, "param")).toBeUndefined(); + expect(getUrlSearchParam("", "param")).toBeUndefined(); + expect(getUrlSearchParam(null, "param")).toBeUndefined(); + expect(getUrlSearchParam(null, "")).toBeUndefined(); + expect(getUrlSearchParam("", "")).toBeUndefined(); + expect(getUrlSearchParam("/", "param")).toBeUndefined(); + expect(getUrlSearchParam("/kaboom=1", "param")).toBeUndefined(); + expect(getUrlSearchParam("hey.com/?kaboom=1", "kaboom")).toBeUndefined(); + expect(getUrlSearchParam("www.hey.com?kaboom=1", "kaboom")).toBeUndefined(); + expect(getUrlSearchParam("localhost?param=1", "param")).toBeUndefined(); + expect( + getUrlSearchParam("https://www.ciao.com/?param=1", "param2") + ).toBeUndefined(); + expect(getUrlSearchParam("https://www.ciao.com", "param2")).toBeUndefined(); + }); + + test("found", async () => { + expect(getUrlSearchParam("https://www.ciao.com/?param=", "param")).toBe(""); + expect(getUrlSearchParam("https://www.ciao.com/?param", "param")).toBe(""); + expect(getUrlSearchParam("https://www.ciao.com/?param=1", "param")).toBe( + "1" + ); + expect(getUrlSearchParam("http://pippo.com?param=2¶m=1", "param")).toBe( + "2" + ); + expect( + getUrlSearchParam("http://localhost/?param=1¶m2=2", "param2") + ).toBe("2"); + }); +}); diff --git a/src/helpers/getUrlSearchParam.ts b/src/helpers/getUrlSearchParam.ts new file mode 100644 index 0000000..bafef2b --- /dev/null +++ b/src/helpers/getUrlSearchParam.ts @@ -0,0 +1,12 @@ +import { Maybe } from "../types/Maybe"; + +export const getUrlSearchParam = (urlString: Maybe, param: string) => { + if (!urlString) return undefined; + try { + const url = new URL(urlString); + const value = url.searchParams.get(param); + return value ?? undefined; + } catch (_e) { + return undefined; + } +}; diff --git a/src/helpers/index.ts b/src/helpers/index.ts index 09e44dd..ed1acc2 100644 --- a/src/helpers/index.ts +++ b/src/helpers/index.ts @@ -1,16 +1,27 @@ export * from "./array"; +export * from "./arrayDiff"; +export * from "./arrayIntersection"; export * from "./capitalize"; export * from "./clamp"; export * from "./cleanSpaces"; +export * from "./dir"; export * from "./first"; +export * from "./firstKey"; +export * from "./firstValue"; export * from "./getKeys"; export * from "./last"; export * from "./merge"; export * from "./moveToFirst"; export * from "./moveToLast"; +export * from "./normalizeNumber"; +export * from "./objectDiff"; +export * from "./omit"; export * from "./parseDate"; export * from "./pretty"; export * from "./promiseWithTimeout"; +export * from "./serialize"; +export * from "./shuffle"; export * from "./sleep"; -export * from "./toggleArray"; +export * from "./toggleArrayValue"; export * from "./truncate"; +export * from "./uniqueValues"; diff --git a/src/helpers/merge.ts b/src/helpers/merge.ts index ffee4e2..9398e59 100644 --- a/src/helpers/merge.ts +++ b/src/helpers/merge.ts @@ -2,7 +2,10 @@ import { PlainObject } from "../types"; import { isKey, isObject } from "../validators"; import { getKeys } from "./getKeys"; -// Simple merge function that merges two objects, arrays get overwritten, no options +/** + * @description Simple merge function that merges two objects, arrays get overwritten, no options + * + */ export const merge = (target: PlainObject, source: PlainObject) => { const merger: PlainObject = {}; getKeys(target).forEach((key) => { diff --git a/src/helpers/moveToIndex.test.ts b/src/helpers/moveToIndex.test.ts new file mode 100644 index 0000000..8654cf1 --- /dev/null +++ b/src/helpers/moveToIndex.test.ts @@ -0,0 +1,18 @@ +import { describe, expect, test } from "@jest/globals"; +import { moveToIndex } from "./moveToIndex"; + +describe("moveToIndex", () => { + test("no arg", () => { + expect(moveToIndex([], 0, 0)).toStrictEqual([]); + }); + + test("invalid index", () => { + expect(moveToIndex([1, 2, 3], 4, 0)).toStrictEqual([1, 2, 3]); + }); + + test("args", () => { + expect(moveToIndex([1, 2, 3, 4, 5], 2, 0)).toStrictEqual([3, 1, 2, 4, 5]); + expect(moveToIndex([1, 2], 1, 0)).toStrictEqual([2, 1]); + expect(moveToIndex(Array.from("deveryhingt"), 10, 6).join("")).toBe("deverything"); + }); +}); diff --git a/src/helpers/moveToIndex.ts b/src/helpers/moveToIndex.ts new file mode 100644 index 0000000..ccf2ca4 --- /dev/null +++ b/src/helpers/moveToIndex.ts @@ -0,0 +1,13 @@ +export const moveToIndex = ( + items: T[], + itemToMoveIndex: number, + destinationIndex: number +): T[] => { + if (!items.length || itemToMoveIndex > items.length || destinationIndex > items.length) + return items; + + const newArray = [...items]; + const [removedItem] = newArray.splice(itemToMoveIndex, 1); + newArray.splice(destinationIndex, 0, removedItem); + return newArray; +}; diff --git a/src/helpers/normalizeNumber.test.ts b/src/helpers/normalizeNumber.test.ts new file mode 100644 index 0000000..3ac754a --- /dev/null +++ b/src/helpers/normalizeNumber.test.ts @@ -0,0 +1,16 @@ +import { describe, expect, test } from "@jest/globals"; +import { normalizeNumber } from "./normalizeNumber"; + +describe("normalizeNumber", () => { + test("arg", async () => { + expect(normalizeNumber({ value: 50, min: 0, max: 100 })).toBe(0.5); + expect(normalizeNumber({ value: 0, min: 0, max: 10 })).toBe(0); + expect(normalizeNumber({ value: 10e5, min: 0, max: 10e5 })).toBe(1); + expect(normalizeNumber({ value: 0, min: 0, max: 0 })).toBe(1); + }); + test("edge", async () => { + expect(normalizeNumber({ value: -10, min: 0, max: 10 })).toBe(0); + expect(normalizeNumber({ value: 10e5, min: 0, max: 10 })).toBe(1); + expect(normalizeNumber({ value: 0, min: 0, max: 0 })).toBe(1); + }); +}); diff --git a/src/helpers/normalizeNumber.ts b/src/helpers/normalizeNumber.ts new file mode 100644 index 0000000..72edcc9 --- /dev/null +++ b/src/helpers/normalizeNumber.ts @@ -0,0 +1,14 @@ +export const normalizeNumber = ({ + value, + max, + min, +}: { + value: number; + max: number; + min: number; +}) => { + if (value >= max) return 1; + if (value <= min) return 0; + + return (value - min) / (max - min); +}; diff --git a/src/helpers/objectDiff.test.ts b/src/helpers/objectDiff.test.ts new file mode 100644 index 0000000..6c78672 --- /dev/null +++ b/src/helpers/objectDiff.test.ts @@ -0,0 +1,45 @@ +import { describe, expect, test } from "@jest/globals"; +import { objectDiff } from "./objectDiff"; + +describe("objectDiff", () => { + test("no arg", async () => { + expect(objectDiff({}, {})).toStrictEqual({}); + }); + + test("args", async () => { + const date = new Date(); + expect( + objectDiff({ a: [() => {}, date] }, { a: [() => {}, date] }) + ).toStrictEqual({}); + expect(objectDiff({ a: () => {} }, { a: () => {} })).toStrictEqual({}); + expect( + objectDiff( + { + a: () => { + return 1; + }, + }, + { + a: () => { + return 1; + }, + } + ) + ).toStrictEqual({}); + expect(objectDiff({ a: undefined }, { a: null })).toStrictEqual({ + a: { from: undefined, to: null }, + }); + expect(objectDiff({ a: 1 }, { a: 2 })).toStrictEqual({ + a: { from: 1, to: 2 }, + }); + expect( + objectDiff( + { [Symbol.for("1")]: 1, [Symbol.for("same")]: 1 }, + { a: 2, [Symbol.for("same")]: 1 } + ) + ).toStrictEqual({ + a: { from: undefined, to: 2 }, + [Symbol.for("1")]: { from: 1, to: undefined }, + }); + }); +}); diff --git a/src/helpers/objectDiff.ts b/src/helpers/objectDiff.ts new file mode 100644 index 0000000..2e8db71 --- /dev/null +++ b/src/helpers/objectDiff.ts @@ -0,0 +1,22 @@ +import { PlainObject } from "../types"; +import { isSame } from "../validators/isSame"; +import { getKeys } from "./getKeys"; + +export const objectDiff = ( + leftObject: PlainObject, + rightObject: PlainObject +) => { + var diff: PlainObject = {}; + const allKeys = new Set([...getKeys(leftObject), ...getKeys(rightObject)]); + + for (const key of allKeys) { + if (!isSame(rightObject[key], leftObject[key])) { + diff[key] = { + from: leftObject[key], + to: rightObject[key], + }; + } + } + + return diff; +}; diff --git a/src/helpers/omit.ts b/src/helpers/omit.ts new file mode 100644 index 0000000..a4c4722 --- /dev/null +++ b/src/helpers/omit.ts @@ -0,0 +1,10 @@ +import { PlainObject } from "../types"; + +export const omit = (obj: T, keys: (keyof T)[]) => { + const ret: Partial = {}; + for (const key in obj) { + if (keys.includes(key)) continue; + ret[key] = obj[key]; + } + return ret; +}; diff --git a/src/helpers/parseDate.test.ts b/src/helpers/parseDate.test.ts index da2973d..1dc5bbd 100644 --- a/src/helpers/parseDate.test.ts +++ b/src/helpers/parseDate.test.ts @@ -1,7 +1,8 @@ import { describe, expect, test } from "@jest/globals"; -import { MAX_DATE_MILLISECONDS } from "../constants"; +import { MAX_DATE_MILLISECONDS } from "../constants/time"; import { parseDate } from "./parseDate"; +// TODO: add string with millis 1678792327650170902 describe("parseDate", () => { test("no arg", async () => { expect(parseDate()).toBeUndefined(); diff --git a/src/helpers/pretty.test.ts b/src/helpers/pretty.test.ts new file mode 100644 index 0000000..fc6aaec --- /dev/null +++ b/src/helpers/pretty.test.ts @@ -0,0 +1,19 @@ +import { describe, expect, test } from "@jest/globals"; +import { pretty } from "./pretty"; + +describe("pretty", () => { + test("mixed", async () => { + expect(pretty()).toBeUndefined(); + expect(pretty("")).toBe(`""`); + expect(pretty(true)).toBe("true"); + expect(pretty(0)).toBe("0"); + }); + test("object", async () => { + expect(pretty({})).toBe("{}"); + expect(pretty({ a: 1 })).toBe(`{\n "a": 1\n}`); + const a = { a: 1 }; + // @ts-ignore + a.b = a; + expect(pretty(a)).toBe(`{\n "a": 1,\n "b": "[Circular ~]"\n}`); + }); +}); diff --git a/src/helpers/pretty.ts b/src/helpers/pretty.ts index 290b290..ad7bf19 100644 --- a/src/helpers/pretty.ts +++ b/src/helpers/pretty.ts @@ -1,3 +1,6 @@ -export const pretty = (arg: any) => { - return JSON.stringify(arg, null, 2); +import { objectSerializer } from "../_internals/objectSerializer"; + +// TODO: deprecate and rename to stringify +export const pretty = (arg?: any) => { + return JSON.stringify(arg, objectSerializer(), 2); }; diff --git a/src/helpers/serialize.test.ts b/src/helpers/serialize.test.ts new file mode 100644 index 0000000..00b88cd --- /dev/null +++ b/src/helpers/serialize.test.ts @@ -0,0 +1,25 @@ +import { describe, expect, test } from "@jest/globals"; +import { serialize } from "./serialize"; +import { PRIMITIVES_OBJECT } from "../constants/primitives"; +import { omit } from "./omit"; + +describe("serialize", () => { + test("simple", async () => { + expect(serialize({ a: 1, b: 1 })).toBe('{"a":1,"b":1}'); + expect(serialize({ b: 1, a: 1 })).toBe('{"a":1,"b":1}'); + }); + test("array", async () => { + expect(serialize({ a: [1, 3, 2], b: 1 })).toBe('{"a":[1,3,2],"b":1}'); + }); + test("nested", async () => { + expect(serialize(omit(PRIMITIVES_OBJECT, ["bigInt"]))).toBe( + '{"arr":[1,2,3],"bool":true,"date":"2016-04-28T22:02:17.000Z","exp":10000000000000,"inf":null,"map":{},"nil":null,"num":0,"obj":{"foo":"foo"},"re":{},"set":{},"str":"string"}' + ); + expect(serialize({ a: { h: Infinity, g: "1" }, b: 1 })).toBe( + '{"a":{"g":"1","h":null},"b":1}' + ); + + expect(serialize({ b: null, a: 1 })).toBe('{"a":1,"b":null}'); + expect(serialize({ b: undefined, a: 1 })).toBe('{"a":1}'); + }); +}); diff --git a/src/helpers/serialize.ts b/src/helpers/serialize.ts new file mode 100644 index 0000000..c953eb9 --- /dev/null +++ b/src/helpers/serialize.ts @@ -0,0 +1,14 @@ +import { PlainObject } from "../types"; + +/** + * Serialize plain object to a deterministic string, + * for nested objects use [json-stable-stringify](https://www.npmjs.com/package/json-stable-stringify) + * + * @example + * serialize({ b: 1, a: 2 }) // '{"a":1,"b":2}' + */ +export const serialize = (obj: T) => { + const allKeys = new Set(); + JSON.stringify(obj, (key, value) => (allKeys.add(key), value)); + return JSON.stringify(obj, Array.from(allKeys).sort()); +}; diff --git a/src/helpers/shuffle.ts b/src/helpers/shuffle.ts new file mode 100644 index 0000000..ab3827c --- /dev/null +++ b/src/helpers/shuffle.ts @@ -0,0 +1,8 @@ +export const shuffle = (array: T[]): T[] => { + const newArray = [...array]; // create a new array to avoid modifying the original array + for (let i = newArray.length - 1; i > 0; i--) { + const j = Math.floor(Math.random() * (i + 1)); + [newArray[i], newArray[j]] = [newArray[j], newArray[i]]; // swap elements + } + return newArray; +}; diff --git a/src/helpers/toggleArray.test.ts b/src/helpers/toggleArray.test.ts deleted file mode 100644 index fc53bc2..0000000 --- a/src/helpers/toggleArray.test.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { describe, expect, test } from "@jest/globals"; -import { toggleArray } from "./toggleArray"; - -describe("toggleArray", () => { - test("array", async () => { - expect(toggleArray([0], 0)).toStrictEqual([]); - expect(toggleArray([0], 1)).toStrictEqual([0, 1]); - expect(toggleArray([0, 1, "1"], "1")).toStrictEqual([0, 1]); - expect(toggleArray([0, 1], null)).toStrictEqual([0, 1, null]); - expect(toggleArray([0, 1, null], null)).toStrictEqual([0, 1]); - expect(toggleArray([0, 1, undefined], undefined)).toStrictEqual([0, 1]); - expect(toggleArray(["1", 0, "1"], "1")).toStrictEqual([0]); - }); -}); diff --git a/src/helpers/toggleArray.ts b/src/helpers/toggleArray.ts deleted file mode 100644 index 6a1586c..0000000 --- a/src/helpers/toggleArray.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { isArray } from "../validators"; - -export const toggleArray = (arg: T[], value: T) => { - if (isArray(arg)) { - const copy: T[] = arg.reduce((acc: T[], val) => { - if (val !== value) acc.push(val); - return acc; - }, []); - if (copy.length === arg.length) copy.push(value); - return copy; - } - - return arg; -}; diff --git a/src/helpers/toggleArrayValue.test.ts b/src/helpers/toggleArrayValue.test.ts new file mode 100644 index 0000000..3131441 --- /dev/null +++ b/src/helpers/toggleArrayValue.test.ts @@ -0,0 +1,16 @@ +import { describe, expect, test } from "@jest/globals"; +import { toggleArrayValue } from "./toggleArrayValue"; + +describe("toggleArrayValue", () => { + test("array arg", async () => { + expect(toggleArrayValue([0], 0)).toStrictEqual([]); + expect(toggleArrayValue([0], 1)).toStrictEqual([0, 1]); + expect(toggleArrayValue([0, 1, "1"], "1")).toStrictEqual([0, 1]); + expect(toggleArrayValue([0, 1], null)).toStrictEqual([0, 1, null]); + expect(toggleArrayValue([0, 1, null], null)).toStrictEqual([0, 1]); + expect(toggleArrayValue([0, 1, undefined], undefined)).toStrictEqual([ + 0, 1, + ]); + expect(toggleArrayValue(["1", 0, "1"], "1")).toStrictEqual([0]); + }); +}); diff --git a/src/helpers/toggleArrayValue.ts b/src/helpers/toggleArrayValue.ts new file mode 100644 index 0000000..6f2cc8c --- /dev/null +++ b/src/helpers/toggleArrayValue.ts @@ -0,0 +1,25 @@ +import { isArray } from "../validators"; + +export const toggleArrayValue = (array: T[], value: T) => { + // extra safety + if (!isArray(array)) return array; + + // This will remove all instances of the value + const copy: T[] = array.reduce((acc: T[], val) => { + if (val !== value) acc.push(val); + return acc; + }, []); + + // no value was removed, so add it + if (copy.length === array.length) copy.push(value); + + return copy; +}; + +/** + * @deprecated Use toggleArrayValue instead + * @param {T[]} array + * @param {T} value + * @returns {T[]} + */ +export const toggleArray = toggleArrayValue; diff --git a/src/helpers/uniqueValues.ts b/src/helpers/uniqueValues.ts new file mode 100644 index 0000000..386a206 --- /dev/null +++ b/src/helpers/uniqueValues.ts @@ -0,0 +1,3 @@ +export const uniqueValues = (arr: any[]) => { + return [...new Set(arr)]; +}; diff --git a/src/index.ts b/src/index.ts index 3593f71..23d7b2c 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,4 +1,6 @@ +export * from "./checks"; export * from "./helpers"; +export * from "./math"; export * from "./random"; export * from "./types"; export * from "./validators"; diff --git a/src/math/average.test.ts b/src/math/average.test.ts new file mode 100644 index 0000000..1bb516f --- /dev/null +++ b/src/math/average.test.ts @@ -0,0 +1,13 @@ +import { describe, expect, test } from "@jest/globals"; +import { average } from "./average"; + +describe("average", () => { + test("no arg", async () => { + expect(average([])).toBe(NaN); + }); + + test("args", async () => { + expect(average([1, 2])).toBe(1.5); + expect(average([1, 2, 3, 4])).toBe(2.5); + }); +}); diff --git a/src/math/average.ts b/src/math/average.ts new file mode 100644 index 0000000..bc92b0e --- /dev/null +++ b/src/math/average.ts @@ -0,0 +1,10 @@ +/** + * Calculates the average of a list of numbers. + * @example + * average([1, 2, 3, 4, 5]); // 3 + * average(1, 2, 3, 4, 5); // 3 + */ +export const average = (numbers: number[]): number => { + const sum = numbers.reduce((total, num) => total + num, 0); + return sum / numbers.length; +}; diff --git a/src/math/index.ts b/src/math/index.ts new file mode 100644 index 0000000..25fe110 --- /dev/null +++ b/src/math/index.ts @@ -0,0 +1,4 @@ +export * from "./average"; +export * from "./max"; +export * from "./min"; +export * from "./sum"; diff --git a/src/math/max.ts b/src/math/max.ts new file mode 100644 index 0000000..e16f2ad --- /dev/null +++ b/src/math/max.ts @@ -0,0 +1,4 @@ +export const max = (values: number[]): number => { + const maxValue = Math.max(...values); + return maxValue; +}; diff --git a/src/math/min.ts b/src/math/min.ts new file mode 100644 index 0000000..1a20730 --- /dev/null +++ b/src/math/min.ts @@ -0,0 +1,4 @@ +export const min = (values: number[]): number => { + const minValue = Math.min(...values); + return minValue; +}; diff --git a/src/math/sum.test.ts b/src/math/sum.test.ts new file mode 100644 index 0000000..9b17188 --- /dev/null +++ b/src/math/sum.test.ts @@ -0,0 +1,28 @@ +import { describe, expect, test } from "@jest/globals"; +import { sum, sumBy } from "./sum"; + +describe("sum", () => { + test("simple", async () => { + expect(sum([1, 2])).toBe(3); + }); +}); + +describe("sumBy", () => { + test("simple", async () => { + expect( + sumBy([{ a: 1 }, { a: 2 }], (item) => { + return item.a; + }) + ).toBe(3); + }); + test("string", async () => { + expect(sumBy([{ a: 1 }, { a: 2 }], "a")).toBe(3); + }); + test("complex", async () => { + expect( + sumBy([{ a: 1 }, { b: { a: 2 } }], (item) => { + return item.a || item.b?.a; + }) + ).toBe(3); + }); +}); diff --git a/src/math/sum.ts b/src/math/sum.ts new file mode 100644 index 0000000..83008ac --- /dev/null +++ b/src/math/sum.ts @@ -0,0 +1,13 @@ +import { PropertyAccessor, getProp } from "../_internals/getProp"; +import { PlainObject } from "../types"; + +export const sum = (numbers: number[]) => { + return numbers.reduce((total, num) => total + num, 0); +}; + +export const sumBy = ( + items: T[], + prop: PropertyAccessor +) => { + return items.reduce((total, item) => total + getProp(item, prop), 0); +}; diff --git a/src/random/index.ts b/src/random/index.ts index 0b361c8..f568783 100644 --- a/src/random/index.ts +++ b/src/random/index.ts @@ -1,7 +1,9 @@ export * from "./randomAddress"; export * from "./randomAlphaNumericCode"; export * from "./randomArrayItem"; +export * from "./randomBankAccount"; export * from "./randomBool"; +export * from "./randomCompany"; export * from "./randomCoords"; export * from "./randomDate"; export * from "./randomEmail"; @@ -20,6 +22,7 @@ export * from "./randomIP"; export * from "./randomName"; export * from "./randomNumericCode"; export * from "./randomNumericId"; +export * from "./randomPhoneNumber"; export * from "./randomParagraph"; export * from "./randomPassword"; export * from "./randomUUID"; diff --git a/src/random/randomBankAccount.ts b/src/random/randomBankAccount.ts new file mode 100644 index 0000000..8ae0e13 --- /dev/null +++ b/src/random/randomBankAccount.ts @@ -0,0 +1,6 @@ +import { BANK_ACCOUNT_SAMPLES } from "../constants/banking"; +import { randomArrayItem } from "./randomArrayItem"; + +export const randomBankAccount = () => { + return randomArrayItem(BANK_ACCOUNT_SAMPLES); +}; diff --git a/src/random/randomCompany.ts b/src/random/randomCompany.ts new file mode 100644 index 0000000..99ad632 --- /dev/null +++ b/src/random/randomCompany.ts @@ -0,0 +1,13 @@ +import { + COMPANY_NAME_SAMPLES, + Company, + VAT_REGISTRATION_NUMBER_SAMPLES, +} from "../constants/companies"; +import { randomArrayItem } from "./randomArrayItem"; + +export const randomCompany = (): Company => { + return { + name: randomArrayItem(COMPANY_NAME_SAMPLES), + vatRegNumber: randomArrayItem(VAT_REGISTRATION_NUMBER_SAMPLES), + }; +}; diff --git a/src/random/randomDate.test.ts b/src/random/randomDate.test.ts new file mode 100644 index 0000000..b1fa530 --- /dev/null +++ b/src/random/randomDate.test.ts @@ -0,0 +1,46 @@ +import { describe, it, expect } from "@jest/globals"; +import { randomDate, randomFutureDate, randomPastDate } from "./randomDate"; + +describe(`randomDate`, () => { + it(`default`, () => { + expect(randomDate().getTime()).toBeGreaterThan(0); + }); + it(`args`, () => { + expect(randomDate("2010", "2011").toISOString().substring(0, 3)).toBe( + "201" + ); + }); +}); + +describe(`randomFutureDate`, () => { + it(`default`, () => { + expect(randomFutureDate().getTime()).toBeGreaterThan(new Date().getTime()); + expect( + randomFutureDate({ + startDate: "2031", + }).getTime() + ).toBeGreaterThan(new Date("2031").getTime()); + + const withEndTime = randomFutureDate({ + endDate: "2031", + }).getTime(); + expect(withEndTime).toBeLessThanOrEqual(new Date("2031").getTime()); + expect(withEndTime).toBeGreaterThanOrEqual(new Date().getTime()); + }); +}); + +describe(`randomPastDate`, () => { + it(`default`, () => { + expect(randomPastDate().getTime()).toBeLessThan(new Date().getTime()); + const withStartTime = randomPastDate({ + startDate: "1031", + }).getTime(); + expect(withStartTime).toBeGreaterThan(new Date("1031").getTime()); + expect(withStartTime).toBeLessThanOrEqual(new Date().getTime()); + + const withEndTime = randomPastDate({ + endDate: "1970-01-02", + }).getTime(); + expect(withEndTime).toBeLessThanOrEqual(new Date("1970-01-02").getTime()); + }); +}); diff --git a/src/random/randomDate.ts b/src/random/randomDate.ts index ae34153..e54bf6a 100644 --- a/src/random/randomDate.ts +++ b/src/random/randomDate.ts @@ -3,14 +3,34 @@ import { MAX_DATE_MILLISECONDS, MILLISECONDS_IN_MINUTE, } from "../constants/time"; +import { parseDate } from "../helpers/parseDate"; +import { DateLike, DateRange } from "../types"; +import { isFutureDate, isPastDate } from "../validators"; +import { randomInt } from "./randomInt"; -export const randomDate = (start?: Date, end?: Date) => { - const startDate = start || new Date(-MILLISECONDS_IN_DECADE); - const endDate = end || new Date(MILLISECONDS_IN_DECADE); - return new Date( - startDate.getTime() + - Math.random() * (endDate.getTime() - startDate.getTime()) - ); +const nowPlusMs = (ms: number) => new Date(new Date().getTime() + ms); + +export const randomDate = (startDate?: DateLike, endDate?: DateLike) => { + const parsedStartDate = parseDate(startDate); + const parsedEndDate = parseDate(endDate); + + if (parsedStartDate && parsedEndDate && parsedStartDate > parsedEndDate) { + console.warn(`randomDate: startDate must be before endDate`); + } + + const finalStartDate = + parsedStartDate || + (parsedEndDate + ? new Date(parsedEndDate.getTime() - MILLISECONDS_IN_DECADE) + : nowPlusMs(-MILLISECONDS_IN_DECADE)); + + const finalEndDate = + parsedEndDate || + (parsedStartDate + ? new Date(parsedStartDate.getTime() + MILLISECONDS_IN_DECADE) + : nowPlusMs(MILLISECONDS_IN_DECADE)); + + return new Date(randomInt(finalStartDate.getTime(), finalEndDate.getTime())); }; export const randomMaxDate = (start?: Date, end?: Date) => { @@ -19,14 +39,36 @@ export const randomMaxDate = (start?: Date, end?: Date) => { return randomDate(startDate, endDate); }; -export const randomFutureDate = () => { - // Add a safe margin in the future (i.e. lagging tests). About 5 minutes is enough. - const safeNow = new Date(new Date().getTime() + 5 * MILLISECONDS_IN_MINUTE); - return randomDate(safeNow); +export const randomFutureDate = ({ + startDate, + endDate, +}: Partial = {}) => { + if (startDate && isPastDate(startDate)) { + console.warn(`randomFutureDate: startDate must be in the future`); + } + if (endDate && isPastDate(endDate)) { + console.warn(`randomFutureDate: endDate must be in the future`); + } + + const finalStartDate = + parseDate(startDate) || nowPlusMs(5 * MILLISECONDS_IN_MINUTE); // Add a safe margin in the future (i.e. lagging tests) + + return randomDate(finalStartDate, endDate); }; -export const randomPastDate = () => { - return randomDate(undefined, new Date()); +export const randomPastDate = ({ + startDate, + endDate, +}: Partial = {}) => { + if (startDate && isFutureDate(startDate)) { + console.warn(`randomPastDate: startDate must be in the past`); + } + if (endDate && isFutureDate(endDate)) { + console.warn(`randomPastDate: endDate must be in the past`); + } + + const finalEndDate = parseDate(endDate) || new Date(); + return randomDate(startDate, finalEndDate); }; export const randomDateRange = () => { diff --git a/src/random/randomEnumKey.ts b/src/random/randomEnumKey.ts index d58210e..da4e847 100644 --- a/src/random/randomEnumKey.ts +++ b/src/random/randomEnumKey.ts @@ -1,8 +1,9 @@ -import { isNumeric } from "../validators/isNumeric"; +import { enumKeys } from "../helpers/enumKeys"; +import { ObjectKey } from "../types/Object"; import { randomArrayItem } from "./randomArrayItem"; -export const randomEnumKey = (enumObject: T): keyof T => { - return randomArrayItem( - Object.keys(enumObject).filter((key) => !isNumeric(key)) // key cannot be a number - ) as keyof T; +export const randomEnumKey = (arg: T): ObjectKey => { + const keys = enumKeys(arg); + + return randomArrayItem(keys); }; diff --git a/src/random/randomEnumValue.ts b/src/random/randomEnumValue.ts index c6ff745..5713163 100644 --- a/src/random/randomEnumValue.ts +++ b/src/random/randomEnumValue.ts @@ -1,16 +1,9 @@ -import { isKey } from "../validators/isKey"; +import { enumValues } from "../helpers/enumValues"; +import { ObjectValue } from "../types/Object"; import { randomArrayItem } from "./randomArrayItem"; -export const randomEnumValue = ( - enumObject: T -): T[keyof T] => { - let values: T[keyof T][] = []; - - Object.values(enumObject).forEach((value) => { - // types are tricky here because the value is used also to check if exists as a key - if (isKey(value, enumObject) && !values.includes(value as T[keyof T])) - values.push(enumObject[value as keyof T]); - }); +export const randomEnumValue = (arg: T): ObjectValue => { + const values = enumValues(arg); return randomArrayItem(values); }; diff --git a/src/random/randomHandle.ts b/src/random/randomHandle.ts index db76f4e..817f278 100644 --- a/src/random/randomHandle.ts +++ b/src/random/randomHandle.ts @@ -1,10 +1,10 @@ -import { LATIN_FIRST_NAMES, LATIN_LAST_NAMES } from "../constants/names"; +import { WESTERN_FIRST_NAMES, WESTERN_LAST_NAMES } from "../constants/names"; import { randomArrayItem } from "./randomArrayItem"; import { randomInt } from "./randomInt"; export const randomHandle = () => ( - randomArrayItem(LATIN_FIRST_NAMES) + + randomArrayItem(WESTERN_FIRST_NAMES) + "." + - randomArrayItem(LATIN_LAST_NAMES) + randomArrayItem(WESTERN_LAST_NAMES) ).toLowerCase() + randomInt(11, 99); diff --git a/src/random/randomIP.test.ts b/src/random/randomIP.test.ts new file mode 100644 index 0000000..8110246 --- /dev/null +++ b/src/random/randomIP.test.ts @@ -0,0 +1,8 @@ +import { describe, expect, test } from "@jest/globals"; +import { randomIP } from "./randomIP"; + +describe("randomIP", () => { + test("no args", async () => { + expect(randomIP()).toContain("."); + }); +}); diff --git a/src/random/randomIP.ts b/src/random/randomIP.ts index 8a26191..f618ac6 100644 --- a/src/random/randomIP.ts +++ b/src/random/randomIP.ts @@ -1,11 +1,6 @@ +import { array } from "../helpers/array"; import { randomInt } from "./randomInt"; export const randomIP = () => { - return `${randomInt(0, 255).toString()}.${randomInt( - 0, - 255 - ).toString()}.${randomInt(0, 255).toString()}.${randomInt( - 0, - 255 - ).toString()}`; + return array(4, () => randomInt(0, 255).toString()).join("."); }; diff --git a/src/random/randomInt.ts b/src/random/randomInt.ts index 419c92f..f3717c2 100644 --- a/src/random/randomInt.ts +++ b/src/random/randomInt.ts @@ -1,11 +1,14 @@ -export const randomInt = (start: number = -100, end: number = 100): number => - Math.floor(Math.random() * (end - start + 1) + start); +export const randomInt = (min: number = -100, max: number = 100): number => { + min = Math.ceil(min); // in case is a float + max = Math.floor(max); // in case is a float + return Math.floor(Math.random() * (max - min + 1) + min); +}; -export const randomPositiveInt = (end: number = 100): number => - randomInt(1, end); +export const randomPositiveInt = (max: number = 100): number => + randomInt(1, max); -export const randomNegativeInt = (start: number = -100): number => - randomInt(start, -1); +export const randomNegativeInt = (min: number = -100): number => + randomInt(min, -1); export const randomMaxSafeInt = () => randomInt(-Number.MAX_SAFE_INTEGER, Number.MAX_SAFE_INTEGER); @@ -13,4 +16,20 @@ export const randomMaxSafeInt = () => export const randomMaxInt = () => randomInt(-Number.MAX_VALUE, Number.MAX_VALUE); -export const randomPercentage = () => randomInt(-100, 100); +export const randomPercentage = ({ + min, + max, +}: { + min?: number; + max?: number; +} = {}) => randomInt(min ?? -100, max ?? 100); + +export const randomPositivePercentage = ({ + min, + max, +}: { + min?: number; + max?: number; +} = {}) => randomInt(min ?? 1, max ?? 100); + +export const randomFormattedPercentage = () => randomPercentage() + "%"; diff --git a/src/random/randomName.ts b/src/random/randomName.ts index 916683c..49aaaf8 100644 --- a/src/random/randomName.ts +++ b/src/random/randomName.ts @@ -2,8 +2,8 @@ import { ANIMAL_NAMES, FIRST_NAMES, LAST_NAMES, - LATIN_FIRST_NAMES, - LATIN_LAST_NAMES, + WESTERN_FIRST_NAMES, + WESTERN_LAST_NAMES, TOOL_NAMES, } from "../constants/names"; import { randomArrayItem } from "./randomArrayItem"; @@ -16,4 +16,6 @@ export const randomFirstName = () => randomArrayItem(FIRST_NAMES); export const randomLastName = () => randomArrayItem(LAST_NAMES); export const randomFullName = () => - randomArrayItem(LATIN_FIRST_NAMES) + " " + randomArrayItem(LATIN_LAST_NAMES); + randomArrayItem(WESTERN_FIRST_NAMES) + + " " + + randomArrayItem(WESTERN_LAST_NAMES); diff --git a/src/random/randomParagraph.ts b/src/random/randomParagraph.ts index 8924bf7..6234cb6 100644 --- a/src/random/randomParagraph.ts +++ b/src/random/randomParagraph.ts @@ -1,7 +1,9 @@ import { array } from "../helpers"; import { capitalize } from "../helpers/capitalize"; +import { randomInt } from "./randomInt"; import { randomWord } from "./randomWord"; +// TODO: add a comma in the middle of the sentence /** * Generates a random paragraph of text. * @param maxCharacters The maximum number of characters. The paragraph will be truncated to this length if it exceeds it. Default is 200. @@ -16,7 +18,7 @@ export const randomParagraph = ({ words?: number; } = {}) => { return capitalize( - array(words, () => randomWord()) + array(randomInt(words, 16), () => randomWord()) .join(" ") .slice(0, maxCharacters - 1) + "." ); diff --git a/src/random/randomPassword.test.ts b/src/random/randomPassword.test.ts new file mode 100644 index 0000000..2cf57c5 --- /dev/null +++ b/src/random/randomPassword.test.ts @@ -0,0 +1,9 @@ +import { describe, it, expect } from "@jest/globals"; +import { randomPassword } from "./randomPassword"; + +describe(`randomPassword`, () => { + it(`no args`, () => { + expect(randomPassword().length).toBeGreaterThan(9); + expect(randomPassword({ minChars: 19 }).length).toBeGreaterThan(19); + }); +}); diff --git a/src/random/randomPassword.ts b/src/random/randomPassword.ts index ccf43ef..eaf29b8 100644 --- a/src/random/randomPassword.ts +++ b/src/random/randomPassword.ts @@ -3,7 +3,7 @@ import { randomArrayItem } from "./randomArrayItem"; import { randomHtmlColorName } from "./randomHtmlColorName"; import { randomInt } from "./randomInt"; -export const randomPassword = () => - randomHtmlColorName() + // So it has an upper case +export const randomPassword = ({ minChars = 9 }: { minChars?: number } = {}) => + randomHtmlColorName().padEnd(minChars, "-") + // So it has an upper case, at least 9 charss randomArrayItem(SPECIAL_CHARACTERS) + // So it has a special character randomInt(11, 99); // So it has a number diff --git a/src/random/randomPhoneNumber.ts b/src/random/randomPhoneNumber.ts new file mode 100644 index 0000000..290f3f3 --- /dev/null +++ b/src/random/randomPhoneNumber.ts @@ -0,0 +1,6 @@ +import { PHONE_NUMBER_SAMPLES } from "../constants/phoneNumbers"; +import { randomArrayItem } from "./randomArrayItem"; + +export const randomPhoneNumber = () => { + return randomArrayItem(PHONE_NUMBER_SAMPLES); +}; diff --git a/src/regex/controlCharRegex.test.ts b/src/regex/controlCharRegex.test.ts new file mode 100644 index 0000000..7f6bba8 --- /dev/null +++ b/src/regex/controlCharRegex.test.ts @@ -0,0 +1,23 @@ +import { describe, expect, test } from "@jest/globals"; +import { controlCharRegex } from "./controlCharRegex"; + +describe("controlCharRegex", () => { + test("args", async () => { + expect(controlCharRegex.test("\r")).toBe(true); + controlCharRegex.lastIndex = 0; + expect(controlCharRegex.test("\t")).toBe(true); + controlCharRegex.lastIndex = 0; + expect(controlCharRegex.test("\n")).toBe(true); + controlCharRegex.lastIndex = 0; + expect(controlCharRegex.test("\v")).toBe(true); + controlCharRegex.lastIndex = 0; + expect(controlCharRegex.test("\b")).toBe(true); + controlCharRegex.lastIndex = 0; + expect(controlCharRegex.test("\f")).toBe(true); + controlCharRegex.lastIndex = 0; + expect(controlCharRegex.test("\u0000")).toBe(true); + controlCharRegex.lastIndex = 0; + expect(controlCharRegex.test("\ud80a")).toBe(true); + controlCharRegex.lastIndex = 0; + }); +}); diff --git a/src/regex/controlCharRegex.ts b/src/regex/controlCharRegex.ts new file mode 100644 index 0000000..7ce5286 --- /dev/null +++ b/src/regex/controlCharRegex.ts @@ -0,0 +1,461 @@ +export const controlCharRegex = new RegExp(/\p{C}/, "gu"); + +// Charcodes: +// [ +// 1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 31, 127, 129, 131, 133, +// 135, 137, 139, 141, 143, 145, 147, 149, 151, 153, 155, 157, 159, 173, 888, +// 896, 898, 907, 909, 930, 1328, 1367, 1419, 1424, 1480, 1482, 1484, 1486, 1515, +// 1517, 1525, 1527, 1529, 1531, 1533, 1535, 1537, 1539, 1541, 1564, 1757, 1806, +// 1867, 1970, 1972, 1974, 1976, 1978, 1980, 1982, 2043, 2094, 2111, 2140, 2143, +// 2155, 2157, 2159, 2191, 2193, 2195, 2197, 2199, 2274, 2436, 2445, 2449, 2473, +// 2481, 2483, 2485, 2490, 2501, 2505, 2511, 2513, 2515, 2517, 2520, 2522, 2526, +// 2532, 2559, 2564, 2571, 2573, 2577, 2601, 2609, 2612, 2615, 2618, 2621, 2627, +// 2629, 2633, 2638, 2640, 2642, 2644, 2646, 2648, 2653, 2655, 2657, 2659, 2661, +// 2679, 2681, 2683, 2685, 2687, 2692, 2702, 2706, 2729, 2737, 2740, 2746, 2758, +// 2762, 2766, 2769, 2771, 2773, 2775, 2777, 2779, 2781, 2783, 2788, 2802, 2804, +// 2806, 2808, 2816, 2820, 2829, 2833, 2857, 2865, 2868, 2874, 2885, 2889, 2894, +// 2896, 2898, 2900, 2904, 2906, 2910, 2916, 2936, 2938, 2940, 2942, 2944, 2948, +// 2955, 2957, 2961, 2966, 2968, 2971, 2973, 2976, 2978, 2981, 2983, 2987, 2989, +// 3002, 3004, 3011, 3013, 3017, 3022, 3025, 3027, 3029, 3032, 3034, 3036, 3038, +// 3040, 3042, 3044, 3067, 3069, 3071, 3085, 3089, 3113, 3130, 3141, 3145, 3150, +// 3152, 3154, 3156, 3159, 3163, 3166, 3172, 3184, 3186, 3188, 3190, 3213, 3217, +// 3241, 3252, 3258, 3269, 3273, 3278, 3280, 3282, 3284, 3287, 3289, 3291, 3295, +// 3300, 3312, 3316, 3318, 3320, 3322, 3324, 3326, 3341, 3345, 3397, 3401, 3408, +// 3410, 3428, 3456, 3460, 3479, 3481, 3506, 3516, 3518, 3527, 3529, 3531, 3533, +// 3541, 3543, 3552, 3554, 3556, 3568, 3573, 3575, 3577, 3579, 3581, 3583, 3643, +// 3645, 3676, 3678, 3680, 3682, 3684, 3686, 3688, 3690, 3692, 3694, 3696, 3698, +// 3700, 3702, 3704, 3706, 3708, 3710, 3712, 3715, 3717, 3723, 3748, 3750, 3774, +// 3781, 3783, 3791, 3802, 3808, 3810, 3812, 3814, 3816, 3818, 3820, 3822, 3824, +// 3826, 3828, 3830, 3832, 3834, 3836, 3838, 3912, 3949, 3951, 3992, 4029, 4045, +// 4059, 4061, 4063, 4065, 4067, 4069, 4071, 4073, 4075, 4077, 4079, 4081, 4083, +// 4085, 4087, 4089, 4091, 4093, 4095, 4294, 4296, 4298, 4300, 4302, 4681, 4686, +// 4695, 4697, 4702, 4745, 4750, 4785, 4790, 4799, 4801, 4806, 4823, 4881, 4886, +// 4955, 4989, 4991, 5018, 5020, 5022, 5110, 5118, 5789, 5791, 5881, 5883, 5885, +// 5887, 5910, 5912, 5914, 5916, 5918, 5943, 5945, 5947, 5949, 5951, 5972, 5974, +// 5976, 5978, 5980, 5982, 5997, 6001, 6004, 6006, 6008, 6010, 6012, 6014, 6110, +// 6122, 6124, 6126, 6138, 6140, 6142, 6158, 6170, 6172, 6174, 6265, 6267, 6269, +// 6271, 6315, 6317, 6319, 6390, 6392, 6394, 6396, 6398, 6431, 6444, 6446, 6460, +// 6462, 6465, 6467, 6510, 6517, 6519, 6521, 6523, 6525, 6527, 6572, 6574, 6602, +// 6604, 6606, 6619, 6621, 6684, 6751, 6781, 6794, 6796, 6798, 6810, 6812, 6814, +// 6830, 6863, 6865, 6867, 6869, 6871, 6873, 6875, 6877, 6879, 6881, 6883, 6885, +// 6887, 6889, 6891, 6893, 6895, 6897, 6899, 6901, 6903, 6905, 6907, 6909, 6911, +// 6989, 6991, 7039, 7156, 7158, 7160, 7162, 7224, 7226, 7242, 7244, 7305, 7307, +// 7309, 7311, 7355, 7368, 7370, 7372, 7374, 7419, 7421, 7423, 7958, 7966, 8006, +// 8014, 8024, 8026, 8028, 8030, 8062, 8117, 8133, 8148, 8156, 8176, 8181, 8191, +// 8203, 8205, 8207, 8234, 8236, 8238, 8288, 8290, 8292, 8294, 8296, 8298, 8300, +// 8302, 8306, 8335, 8349, 8351, 8385, 8387, 8389, 8391, 8393, 8395, 8397, 8399, +// 8433, 8435, 8437, 8439, 8441, 8443, 8445, 8447, 8588, 8590, 9255, 9257, 9259, +// 9261, 9263, 9265, 9267, 9269, 9271, 9273, 9275, 9277, 9279, 9291, 9293, 9295, +// 9297, 9299, 9301, 9303, 9305, 9307, 9309, 9311, 11124, 11158, 11508, 11510, +// 11512, 11558, 11560, 11562, 11564, 11566, 11624, 11626, 11628, 11630, 11633, +// 11635, 11637, 11639, 11641, 11643, 11645, 11671, 11673, 11675, 11677, 11679, +// 11687, 11695, 11703, 11711, 11719, 11727, 11735, 11743, 11870, 11872, 11874, +// 11876, 11878, 11880, 11882, 11884, 11886, 11888, 11890, 11892, 11894, 11896, +// 11898, 11900, 11902, 11930, 12020, 12022, 12024, 12026, 12028, 12030, 12246, +// 12248, 12250, 12252, 12254, 12256, 12258, 12260, 12262, 12264, 12266, 12268, +// 12270, 12284, 12286, 12352, 12439, 12544, 12546, 12548, 12592, 12687, 12772, +// 12774, 12776, 12778, 12780, 12782, 12831, 42125, 42127, 42183, 42185, 42187, +// 42189, 42191, 42540, 42542, 42544, 42546, 42548, 42550, 42552, 42554, 42556, +// 42558, 42744, 42746, 42748, 42750, 42955, 42957, 42959, 42962, 42964, 42970, +// 42972, 42974, 42976, 42978, 42980, 42982, 42984, 42986, 42988, 42990, 42992, +// 43053, 43055, 43066, 43068, 43070, 43128, 43130, 43132, 43134, 43206, 43208, +// 43210, 43212, 43226, 43228, 43230, 43348, 43350, 43352, 43354, 43356, 43358, +// 43389, 43391, 43470, 43482, 43484, 43519, 43575, 43577, 43579, 43581, 43583, +// 43598, 43610, 43715, 43717, 43719, 43721, 43723, 43725, 43727, 43729, 43731, +// 43733, 43735, 43737, 43767, 43769, 43771, 43773, 43775, 43783, 43791, 43799, +// 43801, 43803, 43805, 43807, 43815, 43823, 43884, 43886, 44014, 44026, 44028, +// 44030, 55204, 55206, 55208, 55210, 55212, 55214, 55239, 55241, 55292, 55294, +// 55296, 55298, 55300, 55302, 55304, 55306, 55308, 55310, 55312, 55314, 55316, +// 55318, 55320, 55322, 55324, 55326, 55328, 55330, 55332, 55334, 55336, 55338, +// 55340, 55342, 55344, 55346, 55348, 55350, 55352, 55354, 55356, 55358, 55360, +// 55362, 55364, 55366, 55368, 55370, 55372, 55374, 55376, 55378, 55380, 55382, +// 55384, 55386, 55388, 55390, 55392, 55394, 55396, 55398, 55400, 55402, 55404, +// 55406, 55408, 55410, 55412, 55414, 55416, 55418, 55420, 55422, 55424, 55426, +// 55428, 55430, 55432, 55434, 55436, 55438, 55440, 55442, 55444, 55446, 55448, +// 55450, 55452, 55454, 55456, 55458, 55460, 55462, 55464, 55466, 55468, 55470, +// 55472, 55474, 55476, 55478, 55480, 55482, 55484, 55486, 55488, 55490, 55492, +// 55494, 55496, 55498, 55500, 55502, 55504, 55506, 55508, 55510, 55512, 55514, +// 55516, 55518, 55520, 55522, 55524, 55526, 55528, 55530, 55532, 55534, 55536, +// 55538, 55540, 55542, 55544, 55546, 55548, 55550, 55552, 55554, 55556, 55558, +// 55560, 55562, 55564, 55566, 55568, 55570, 55572, 55574, 55576, 55578, 55580, +// 55582, 55584, 55586, 55588, 55590, 55592, 55594, 55596, 55598, 55600, 55602, +// 55604, 55606, 55608, 55610, 55612, 55614, 55616, 55618, 55620, 55622, 55624, +// 55626, 55628, 55630, 55632, 55634, 55636, 55638, 55640, 55642, 55644, 55646, +// 55648, 55650, 55652, 55654, 55656, 55658, 55660, 55662, 55664, 55666, 55668, +// 55670, 55672, 55674, 55676, 55678, 55680, 55682, 55684, 55686, 55688, 55690, +// 55692, 55694, 55696, 55698, 55700, 55702, 55704, 55706, 55708, 55710, 55712, +// 55714, 55716, 55718, 55720, 55722, 55724, 55726, 55728, 55730, 55732, 55734, +// 55736, 55738, 55740, 55742, 55744, 55746, 55748, 55750, 55752, 55754, 55756, +// 55758, 55760, 55762, 55764, 55766, 55768, 55770, 55772, 55774, 55776, 55778, +// 55780, 55782, 55784, 55786, 55788, 55790, 55792, 55794, 55796, 55798, 55800, +// 55802, 55804, 55806, 55808, 55810, 55812, 55814, 55816, 55818, 55820, 55822, +// 55824, 55826, 55828, 55830, 55832, 55834, 55836, 55838, 55840, 55842, 55844, +// 55846, 55848, 55850, 55852, 55854, 55856, 55858, 55860, 55862, 55864, 55866, +// 55868, 55870, 55872, 55874, 55876, 55878, 55880, 55882, 55884, 55886, 55888, +// 55890, 55892, 55894, 55896, 55898, 55900, 55902, 55904, 55906, 55908, 55910, +// 55912, 55914, 55916, 55918, 55920, 55922, 55924, 55926, 55928, 55930, 55932, +// 55934, 55936, 55938, 55940, 55942, 55944, 55946, 55948, 55950, 55952, 55954, +// 55956, 55958, 55960, 55962, 55964, 55966, 55968, 55970, 55972, 55974, 55976, +// 55978, 55980, 55982, 55984, 55986, 55988, 55990, 55992, 55994, 55996, 55998, +// 56000, 56002, 56004, 56006, 56008, 56010, 56012, 56014, 56016, 56018, 56020, +// 56022, 56024, 56026, 56028, 56030, 56032, 56034, 56036, 56038, 56040, 56042, +// 56044, 56046, 56048, 56050, 56052, 56054, 56056, 56058, 56060, 56062, 56064, +// 56066, 56068, 56070, 56072, 56074, 56076, 56078, 56080, 56082, 56084, 56086, +// 56088, 56090, 56092, 56094, 56096, 56098, 56100, 56102, 56104, 56106, 56108, +// 56110, 56112, 56114, 56116, 56118, 56120, 56122, 56124, 56126, 56128, 56130, +// 56132, 56134, 56136, 56138, 56140, 56142, 56144, 56146, 56148, 56150, 56152, +// 56154, 56156, 56158, 56160, 56162, 56164, 56166, 56168, 56170, 56172, 56174, +// 56176, 56178, 56180, 56182, 56184, 56186, 56188, 56190, 56192, 56194, 56196, +// 56198, 56200, 56202, 56204, 56206, 56208, 56210, 56212, 56214, 56216, 56218, +// 56220, 56222, 56224, 56226, 56228, 56230, 56232, 56234, 56236, 56238, 56240, +// 56242, 56244, 56246, 56248, 56250, 56252, 56254, 56256, 56258, 56260, 56262, +// 56264, 56266, 56268, 56270, 56272, 56274, 56276, 56278, 56280, 56282, 56284, +// 56286, 56288, 56290, 56292, 56294, 56296, 56298, 56300, 56302, 56304, 56306, +// 56308, 56310, 56312, 56314, 56316, 56318, 56320, 56322, 56324, 56326, 56328, +// 56330, 56332, 56334, 56336, 56338, 56340, 56342, 56344, 56346, 56348, 56350, +// 56352, 56354, 56356, 56358, 56360, 56362, 56364, 56366, 56368, 56370, 56372, +// 56374, 56376, 56378, 56380, 56382, 56384, 56386, 56388, 56390, 56392, 56394, +// 56396, 56398, 56400, 56402, 56404, 56406, 56408, 56410, 56412, 56414, 56416, +// 56418, 56420, 56422, 56424, 56426, 56428, 56430, 56432, 56434, 56436, 56438, +// 56440, 56442, 56444, 56446, 56448, 56450, 56452, 56454, 56456, 56458, 56460, +// 56462, 56464, 56466, 56468, 56470, 56472, 56474, 56476, 56478, 56480, 56482, +// 56484, 56486, 56488, 56490, 56492, 56494, 56496, 56498, 56500, 56502, 56504, +// 56506, 56508, 56510, 56512, 56514, 56516, 56518, 56520, 56522, 56524, 56526, +// 56528, 56530, 56532, 56534, 56536, 56538, 56540, 56542, 56544, 56546, 56548, +// 56550, 56552, 56554, 56556, 56558, 56560, 56562, 56564, 56566, 56568, 56570, +// 56572, 56574, 56576, 56578, 56580, 56582, 56584, 56586, 56588, 56590, 56592, +// 56594, 56596, 56598, 56600, 56602, 56604, 56606, 56608, 56610, 56612, 56614, +// 56616, 56618, 56620, 56622, 56624, 56626, 56628, 56630, 56632, 56634, 56636, +// 56638, 56640, 56642, 56644, 56646, 56648, 56650, 56652, 56654, 56656, 56658, +// 56660, 56662, 56664, 56666, 56668, 56670, 56672, 56674, 56676, 56678, 56680, +// 56682, 56684, 56686, 56688, 56690, 56692, 56694, 56696, 56698, 56700, 56702, +// 56704, 56706, 56708, 56710, 56712, 56714, 56716, 56718, 56720, 56722, 56724, +// 56726, 56728, 56730, 56732, 56734, 56736, 56738, 56740, 56742, 56744, 56746, +// 56748, 56750, 56752, 56754, 56756, 56758, 56760, 56762, 56764, 56766, 56768, +// 56770, 56772, 56774, 56776, 56778, 56780, 56782, 56784, 56786, 56788, 56790, +// 56792, 56794, 56796, 56798, 56800, 56802, 56804, 56806, 56808, 56810, 56812, +// 56814, 56816, 56818, 56820, 56822, 56824, 56826, 56828, 56830, 56832, 56834, +// 56836, 56838, 56840, 56842, 56844, 56846, 56848, 56850, 56852, 56854, 56856, +// 56858, 56860, 56862, 56864, 56866, 56868, 56870, 56872, 56874, 56876, 56878, +// 56880, 56882, 56884, 56886, 56888, 56890, 56892, 56894, 56896, 56898, 56900, +// 56902, 56904, 56906, 56908, 56910, 56912, 56914, 56916, 56918, 56920, 56922, +// 56924, 56926, 56928, 56930, 56932, 56934, 56936, 56938, 56940, 56942, 56944, +// 56946, 56948, 56950, 56952, 56954, 56956, 56958, 56960, 56962, 56964, 56966, +// 56968, 56970, 56972, 56974, 56976, 56978, 56980, 56982, 56984, 56986, 56988, +// 56990, 56992, 56994, 56996, 56998, 57000, 57002, 57004, 57006, 57008, 57010, +// 57012, 57014, 57016, 57018, 57020, 57022, 57024, 57026, 57028, 57030, 57032, +// 57034, 57036, 57038, 57040, 57042, 57044, 57046, 57048, 57050, 57052, 57054, +// 57056, 57058, 57060, 57062, 57064, 57066, 57068, 57070, 57072, 57074, 57076, +// 57078, 57080, 57082, 57084, 57086, 57088, 57090, 57092, 57094, 57096, 57098, +// 57100, 57102, 57104, 57106, 57108, 57110, 57112, 57114, 57116, 57118, 57120, +// 57122, 57124, 57126, 57128, 57130, 57132, 57134, 57136, 57138, 57140, 57142, +// 57144, 57146, 57148, 57150, 57152, 57154, 57156, 57158, 57160, 57162, 57164, +// 57166, 57168, 57170, 57172, 57174, 57176, 57178, 57180, 57182, 57184, 57186, +// 57188, 57190, 57192, 57194, 57196, 57198, 57200, 57202, 57204, 57206, 57208, +// 57210, 57212, 57214, 57216, 57218, 57220, 57222, 57224, 57226, 57228, 57230, +// 57232, 57234, 57236, 57238, 57240, 57242, 57244, 57246, 57248, 57250, 57252, +// 57254, 57256, 57258, 57260, 57262, 57264, 57266, 57268, 57270, 57272, 57274, +// 57276, 57278, 57280, 57282, 57284, 57286, 57288, 57290, 57292, 57294, 57296, +// 57298, 57300, 57302, 57304, 57306, 57308, 57310, 57312, 57314, 57316, 57318, +// 57320, 57322, 57324, 57326, 57328, 57330, 57332, 57334, 57336, 57338, 57340, +// 57342, 57344, 57346, 57348, 57350, 57352, 57354, 57356, 57358, 57360, 57362, +// 57364, 57366, 57368, 57370, 57372, 57374, 57376, 57378, 57380, 57382, 57384, +// 57386, 57388, 57390, 57392, 57394, 57396, 57398, 57400, 57402, 57404, 57406, +// 57408, 57410, 57412, 57414, 57416, 57418, 57420, 57422, 57424, 57426, 57428, +// 57430, 57432, 57434, 57436, 57438, 57440, 57442, 57444, 57446, 57448, 57450, +// 57452, 57454, 57456, 57458, 57460, 57462, 57464, 57466, 57468, 57470, 57472, +// 57474, 57476, 57478, 57480, 57482, 57484, 57486, 57488, 57490, 57492, 57494, +// 57496, 57498, 57500, 57502, 57504, 57506, 57508, 57510, 57512, 57514, 57516, +// 57518, 57520, 57522, 57524, 57526, 57528, 57530, 57532, 57534, 57536, 57538, +// 57540, 57542, 57544, 57546, 57548, 57550, 57552, 57554, 57556, 57558, 57560, +// 57562, 57564, 57566, 57568, 57570, 57572, 57574, 57576, 57578, 57580, 57582, +// 57584, 57586, 57588, 57590, 57592, 57594, 57596, 57598, 57600, 57602, 57604, +// 57606, 57608, 57610, 57612, 57614, 57616, 57618, 57620, 57622, 57624, 57626, +// 57628, 57630, 57632, 57634, 57636, 57638, 57640, 57642, 57644, 57646, 57648, +// 57650, 57652, 57654, 57656, 57658, 57660, 57662, 57664, 57666, 57668, 57670, +// 57672, 57674, 57676, 57678, 57680, 57682, 57684, 57686, 57688, 57690, 57692, +// 57694, 57696, 57698, 57700, 57702, 57704, 57706, 57708, 57710, 57712, 57714, +// 57716, 57718, 57720, 57722, 57724, 57726, 57728, 57730, 57732, 57734, 57736, +// 57738, 57740, 57742, 57744, 57746, 57748, 57750, 57752, 57754, 57756, 57758, +// 57760, 57762, 57764, 57766, 57768, 57770, 57772, 57774, 57776, 57778, 57780, +// 57782, 57784, 57786, 57788, 57790, 57792, 57794, 57796, 57798, 57800, 57802, +// 57804, 57806, 57808, 57810, 57812, 57814, 57816, 57818, 57820, 57822, 57824, +// 57826, 57828, 57830, 57832, 57834, 57836, 57838, 57840, 57842, 57844, 57846, +// 57848, 57850, 57852, 57854, 57856, 57858, 57860, 57862, 57864, 57866, 57868, +// 57870, 57872, 57874, 57876, 57878, 57880, 57882, 57884, 57886, 57888, 57890, +// 57892, 57894, 57896, 57898, 57900, 57902, 57904, 57906, 57908, 57910, 57912, +// 57914, 57916, 57918, 57920, 57922, 57924, 57926, 57928, 57930, 57932, 57934, +// 57936, 57938, 57940, 57942, 57944, 57946, 57948, 57950, 57952, 57954, 57956, +// 57958, 57960, 57962, 57964, 57966, 57968, 57970, 57972, 57974, 57976, 57978, +// 57980, 57982, 57984, 57986, 57988, 57990, 57992, 57994, 57996, 57998, 58000, +// 58002, 58004, 58006, 58008, 58010, 58012, 58014, 58016, 58018, 58020, 58022, +// 58024, 58026, 58028, 58030, 58032, 58034, 58036, 58038, 58040, 58042, 58044, +// 58046, 58048, 58050, 58052, 58054, 58056, 58058, 58060, 58062, 58064, 58066, +// 58068, 58070, 58072, 58074, 58076, 58078, 58080, 58082, 58084, 58086, 58088, +// 58090, 58092, 58094, 58096, 58098, 58100, 58102, 58104, 58106, 58108, 58110, +// 58112, 58114, 58116, 58118, 58120, 58122, 58124, 58126, 58128, 58130, 58132, +// 58134, 58136, 58138, 58140, 58142, 58144, 58146, 58148, 58150, 58152, 58154, +// 58156, 58158, 58160, 58162, 58164, 58166, 58168, 58170, 58172, 58174, 58176, +// 58178, 58180, 58182, 58184, 58186, 58188, 58190, 58192, 58194, 58196, 58198, +// 58200, 58202, 58204, 58206, 58208, 58210, 58212, 58214, 58216, 58218, 58220, +// 58222, 58224, 58226, 58228, 58230, 58232, 58234, 58236, 58238, 58240, 58242, +// 58244, 58246, 58248, 58250, 58252, 58254, 58256, 58258, 58260, 58262, 58264, +// 58266, 58268, 58270, 58272, 58274, 58276, 58278, 58280, 58282, 58284, 58286, +// 58288, 58290, 58292, 58294, 58296, 58298, 58300, 58302, 58304, 58306, 58308, +// 58310, 58312, 58314, 58316, 58318, 58320, 58322, 58324, 58326, 58328, 58330, +// 58332, 58334, 58336, 58338, 58340, 58342, 58344, 58346, 58348, 58350, 58352, +// 58354, 58356, 58358, 58360, 58362, 58364, 58366, 58368, 58370, 58372, 58374, +// 58376, 58378, 58380, 58382, 58384, 58386, 58388, 58390, 58392, 58394, 58396, +// 58398, 58400, 58402, 58404, 58406, 58408, 58410, 58412, 58414, 58416, 58418, +// 58420, 58422, 58424, 58426, 58428, 58430, 58432, 58434, 58436, 58438, 58440, +// 58442, 58444, 58446, 58448, 58450, 58452, 58454, 58456, 58458, 58460, 58462, +// 58464, 58466, 58468, 58470, 58472, 58474, 58476, 58478, 58480, 58482, 58484, +// 58486, 58488, 58490, 58492, 58494, 58496, 58498, 58500, 58502, 58504, 58506, +// 58508, 58510, 58512, 58514, 58516, 58518, 58520, 58522, 58524, 58526, 58528, +// 58530, 58532, 58534, 58536, 58538, 58540, 58542, 58544, 58546, 58548, 58550, +// 58552, 58554, 58556, 58558, 58560, 58562, 58564, 58566, 58568, 58570, 58572, +// 58574, 58576, 58578, 58580, 58582, 58584, 58586, 58588, 58590, 58592, 58594, +// 58596, 58598, 58600, 58602, 58604, 58606, 58608, 58610, 58612, 58614, 58616, +// 58618, 58620, 58622, 58624, 58626, 58628, 58630, 58632, 58634, 58636, 58638, +// 58640, 58642, 58644, 58646, 58648, 58650, 58652, 58654, 58656, 58658, 58660, +// 58662, 58664, 58666, 58668, 58670, 58672, 58674, 58676, 58678, 58680, 58682, +// 58684, 58686, 58688, 58690, 58692, 58694, 58696, 58698, 58700, 58702, 58704, +// 58706, 58708, 58710, 58712, 58714, 58716, 58718, 58720, 58722, 58724, 58726, +// 58728, 58730, 58732, 58734, 58736, 58738, 58740, 58742, 58744, 58746, 58748, +// 58750, 58752, 58754, 58756, 58758, 58760, 58762, 58764, 58766, 58768, 58770, +// 58772, 58774, 58776, 58778, 58780, 58782, 58784, 58786, 58788, 58790, 58792, +// 58794, 58796, 58798, 58800, 58802, 58804, 58806, 58808, 58810, 58812, 58814, +// 58816, 58818, 58820, 58822, 58824, 58826, 58828, 58830, 58832, 58834, 58836, +// 58838, 58840, 58842, 58844, 58846, 58848, 58850, 58852, 58854, 58856, 58858, +// 58860, 58862, 58864, 58866, 58868, 58870, 58872, 58874, 58876, 58878, 58880, +// 58882, 58884, 58886, 58888, 58890, 58892, 58894, 58896, 58898, 58900, 58902, +// 58904, 58906, 58908, 58910, 58912, 58914, 58916, 58918, 58920, 58922, 58924, +// 58926, 58928, 58930, 58932, 58934, 58936, 58938, 58940, 58942, 58944, 58946, +// 58948, 58950, 58952, 58954, 58956, 58958, 58960, 58962, 58964, 58966, 58968, +// 58970, 58972, 58974, 58976, 58978, 58980, 58982, 58984, 58986, 58988, 58990, +// 58992, 58994, 58996, 58998, 59000, 59002, 59004, 59006, 59008, 59010, 59012, +// 59014, 59016, 59018, 59020, 59022, 59024, 59026, 59028, 59030, 59032, 59034, +// 59036, 59038, 59040, 59042, 59044, 59046, 59048, 59050, 59052, 59054, 59056, +// 59058, 59060, 59062, 59064, 59066, 59068, 59070, 59072, 59074, 59076, 59078, +// 59080, 59082, 59084, 59086, 59088, 59090, 59092, 59094, 59096, 59098, 59100, +// 59102, 59104, 59106, 59108, 59110, 59112, 59114, 59116, 59118, 59120, 59122, +// 59124, 59126, 59128, 59130, 59132, 59134, 59136, 59138, 59140, 59142, 59144, +// 59146, 59148, 59150, 59152, 59154, 59156, 59158, 59160, 59162, 59164, 59166, +// 59168, 59170, 59172, 59174, 59176, 59178, 59180, 59182, 59184, 59186, 59188, +// 59190, 59192, 59194, 59196, 59198, 59200, 59202, 59204, 59206, 59208, 59210, +// 59212, 59214, 59216, 59218, 59220, 59222, 59224, 59226, 59228, 59230, 59232, +// 59234, 59236, 59238, 59240, 59242, 59244, 59246, 59248, 59250, 59252, 59254, +// 59256, 59258, 59260, 59262, 59264, 59266, 59268, 59270, 59272, 59274, 59276, +// 59278, 59280, 59282, 59284, 59286, 59288, 59290, 59292, 59294, 59296, 59298, +// 59300, 59302, 59304, 59306, 59308, 59310, 59312, 59314, 59316, 59318, 59320, +// 59322, 59324, 59326, 59328, 59330, 59332, 59334, 59336, 59338, 59340, 59342, +// 59344, 59346, 59348, 59350, 59352, 59354, 59356, 59358, 59360, 59362, 59364, +// 59366, 59368, 59370, 59372, 59374, 59376, 59378, 59380, 59382, 59384, 59386, +// 59388, 59390, 59392, 59394, 59396, 59398, 59400, 59402, 59404, 59406, 59408, +// 59410, 59412, 59414, 59416, 59418, 59420, 59422, 59424, 59426, 59428, 59430, +// 59432, 59434, 59436, 59438, 59440, 59442, 59444, 59446, 59448, 59450, 59452, +// 59454, 59456, 59458, 59460, 59462, 59464, 59466, 59468, 59470, 59472, 59474, +// 59476, 59478, 59480, 59482, 59484, 59486, 59488, 59490, 59492, 59494, 59496, +// 59498, 59500, 59502, 59504, 59506, 59508, 59510, 59512, 59514, 59516, 59518, +// 59520, 59522, 59524, 59526, 59528, 59530, 59532, 59534, 59536, 59538, 59540, +// 59542, 59544, 59546, 59548, 59550, 59552, 59554, 59556, 59558, 59560, 59562, +// 59564, 59566, 59568, 59570, 59572, 59574, 59576, 59578, 59580, 59582, 59584, +// 59586, 59588, 59590, 59592, 59594, 59596, 59598, 59600, 59602, 59604, 59606, +// 59608, 59610, 59612, 59614, 59616, 59618, 59620, 59622, 59624, 59626, 59628, +// 59630, 59632, 59634, 59636, 59638, 59640, 59642, 59644, 59646, 59648, 59650, +// 59652, 59654, 59656, 59658, 59660, 59662, 59664, 59666, 59668, 59670, 59672, +// 59674, 59676, 59678, 59680, 59682, 59684, 59686, 59688, 59690, 59692, 59694, +// 59696, 59698, 59700, 59702, 59704, 59706, 59708, 59710, 59712, 59714, 59716, +// 59718, 59720, 59722, 59724, 59726, 59728, 59730, 59732, 59734, 59736, 59738, +// 59740, 59742, 59744, 59746, 59748, 59750, 59752, 59754, 59756, 59758, 59760, +// 59762, 59764, 59766, 59768, 59770, 59772, 59774, 59776, 59778, 59780, 59782, +// 59784, 59786, 59788, 59790, 59792, 59794, 59796, 59798, 59800, 59802, 59804, +// 59806, 59808, 59810, 59812, 59814, 59816, 59818, 59820, 59822, 59824, 59826, +// 59828, 59830, 59832, 59834, 59836, 59838, 59840, 59842, 59844, 59846, 59848, +// 59850, 59852, 59854, 59856, 59858, 59860, 59862, 59864, 59866, 59868, 59870, +// 59872, 59874, 59876, 59878, 59880, 59882, 59884, 59886, 59888, 59890, 59892, +// 59894, 59896, 59898, 59900, 59902, 59904, 59906, 59908, 59910, 59912, 59914, +// 59916, 59918, 59920, 59922, 59924, 59926, 59928, 59930, 59932, 59934, 59936, +// 59938, 59940, 59942, 59944, 59946, 59948, 59950, 59952, 59954, 59956, 59958, +// 59960, 59962, 59964, 59966, 59968, 59970, 59972, 59974, 59976, 59978, 59980, +// 59982, 59984, 59986, 59988, 59990, 59992, 59994, 59996, 59998, 60000, 60002, +// 60004, 60006, 60008, 60010, 60012, 60014, 60016, 60018, 60020, 60022, 60024, +// 60026, 60028, 60030, 60032, 60034, 60036, 60038, 60040, 60042, 60044, 60046, +// 60048, 60050, 60052, 60054, 60056, 60058, 60060, 60062, 60064, 60066, 60068, +// 60070, 60072, 60074, 60076, 60078, 60080, 60082, 60084, 60086, 60088, 60090, +// 60092, 60094, 60096, 60098, 60100, 60102, 60104, 60106, 60108, 60110, 60112, +// 60114, 60116, 60118, 60120, 60122, 60124, 60126, 60128, 60130, 60132, 60134, +// 60136, 60138, 60140, 60142, 60144, 60146, 60148, 60150, 60152, 60154, 60156, +// 60158, 60160, 60162, 60164, 60166, 60168, 60170, 60172, 60174, 60176, 60178, +// 60180, 60182, 60184, 60186, 60188, 60190, 60192, 60194, 60196, 60198, 60200, +// 60202, 60204, 60206, 60208, 60210, 60212, 60214, 60216, 60218, 60220, 60222, +// 60224, 60226, 60228, 60230, 60232, 60234, 60236, 60238, 60240, 60242, 60244, +// 60246, 60248, 60250, 60252, 60254, 60256, 60258, 60260, 60262, 60264, 60266, +// 60268, 60270, 60272, 60274, 60276, 60278, 60280, 60282, 60284, 60286, 60288, +// 60290, 60292, 60294, 60296, 60298, 60300, 60302, 60304, 60306, 60308, 60310, +// 60312, 60314, 60316, 60318, 60320, 60322, 60324, 60326, 60328, 60330, 60332, +// 60334, 60336, 60338, 60340, 60342, 60344, 60346, 60348, 60350, 60352, 60354, +// 60356, 60358, 60360, 60362, 60364, 60366, 60368, 60370, 60372, 60374, 60376, +// 60378, 60380, 60382, 60384, 60386, 60388, 60390, 60392, 60394, 60396, 60398, +// 60400, 60402, 60404, 60406, 60408, 60410, 60412, 60414, 60416, 60418, 60420, +// 60422, 60424, 60426, 60428, 60430, 60432, 60434, 60436, 60438, 60440, 60442, +// 60444, 60446, 60448, 60450, 60452, 60454, 60456, 60458, 60460, 60462, 60464, +// 60466, 60468, 60470, 60472, 60474, 60476, 60478, 60480, 60482, 60484, 60486, +// 60488, 60490, 60492, 60494, 60496, 60498, 60500, 60502, 60504, 60506, 60508, +// 60510, 60512, 60514, 60516, 60518, 60520, 60522, 60524, 60526, 60528, 60530, +// 60532, 60534, 60536, 60538, 60540, 60542, 60544, 60546, 60548, 60550, 60552, +// 60554, 60556, 60558, 60560, 60562, 60564, 60566, 60568, 60570, 60572, 60574, +// 60576, 60578, 60580, 60582, 60584, 60586, 60588, 60590, 60592, 60594, 60596, +// 60598, 60600, 60602, 60604, 60606, 60608, 60610, 60612, 60614, 60616, 60618, +// 60620, 60622, 60624, 60626, 60628, 60630, 60632, 60634, 60636, 60638, 60640, +// 60642, 60644, 60646, 60648, 60650, 60652, 60654, 60656, 60658, 60660, 60662, +// 60664, 60666, 60668, 60670, 60672, 60674, 60676, 60678, 60680, 60682, 60684, +// 60686, 60688, 60690, 60692, 60694, 60696, 60698, 60700, 60702, 60704, 60706, +// 60708, 60710, 60712, 60714, 60716, 60718, 60720, 60722, 60724, 60726, 60728, +// 60730, 60732, 60734, 60736, 60738, 60740, 60742, 60744, 60746, 60748, 60750, +// 60752, 60754, 60756, 60758, 60760, 60762, 60764, 60766, 60768, 60770, 60772, +// 60774, 60776, 60778, 60780, 60782, 60784, 60786, 60788, 60790, 60792, 60794, +// 60796, 60798, 60800, 60802, 60804, 60806, 60808, 60810, 60812, 60814, 60816, +// 60818, 60820, 60822, 60824, 60826, 60828, 60830, 60832, 60834, 60836, 60838, +// 60840, 60842, 60844, 60846, 60848, 60850, 60852, 60854, 60856, 60858, 60860, +// 60862, 60864, 60866, 60868, 60870, 60872, 60874, 60876, 60878, 60880, 60882, +// 60884, 60886, 60888, 60890, 60892, 60894, 60896, 60898, 60900, 60902, 60904, +// 60906, 60908, 60910, 60912, 60914, 60916, 60918, 60920, 60922, 60924, 60926, +// 60928, 60930, 60932, 60934, 60936, 60938, 60940, 60942, 60944, 60946, 60948, +// 60950, 60952, 60954, 60956, 60958, 60960, 60962, 60964, 60966, 60968, 60970, +// 60972, 60974, 60976, 60978, 60980, 60982, 60984, 60986, 60988, 60990, 60992, +// 60994, 60996, 60998, 61000, 61002, 61004, 61006, 61008, 61010, 61012, 61014, +// 61016, 61018, 61020, 61022, 61024, 61026, 61028, 61030, 61032, 61034, 61036, +// 61038, 61040, 61042, 61044, 61046, 61048, 61050, 61052, 61054, 61056, 61058, +// 61060, 61062, 61064, 61066, 61068, 61070, 61072, 61074, 61076, 61078, 61080, +// 61082, 61084, 61086, 61088, 61090, 61092, 61094, 61096, 61098, 61100, 61102, +// 61104, 61106, 61108, 61110, 61112, 61114, 61116, 61118, 61120, 61122, 61124, +// 61126, 61128, 61130, 61132, 61134, 61136, 61138, 61140, 61142, 61144, 61146, +// 61148, 61150, 61152, 61154, 61156, 61158, 61160, 61162, 61164, 61166, 61168, +// 61170, 61172, 61174, 61176, 61178, 61180, 61182, 61184, 61186, 61188, 61190, +// 61192, 61194, 61196, 61198, 61200, 61202, 61204, 61206, 61208, 61210, 61212, +// 61214, 61216, 61218, 61220, 61222, 61224, 61226, 61228, 61230, 61232, 61234, +// 61236, 61238, 61240, 61242, 61244, 61246, 61248, 61250, 61252, 61254, 61256, +// 61258, 61260, 61262, 61264, 61266, 61268, 61270, 61272, 61274, 61276, 61278, +// 61280, 61282, 61284, 61286, 61288, 61290, 61292, 61294, 61296, 61298, 61300, +// 61302, 61304, 61306, 61308, 61310, 61312, 61314, 61316, 61318, 61320, 61322, +// 61324, 61326, 61328, 61330, 61332, 61334, 61336, 61338, 61340, 61342, 61344, +// 61346, 61348, 61350, 61352, 61354, 61356, 61358, 61360, 61362, 61364, 61366, +// 61368, 61370, 61372, 61374, 61376, 61378, 61380, 61382, 61384, 61386, 61388, +// 61390, 61392, 61394, 61396, 61398, 61400, 61402, 61404, 61406, 61408, 61410, +// 61412, 61414, 61416, 61418, 61420, 61422, 61424, 61426, 61428, 61430, 61432, +// 61434, 61436, 61438, 61440, 61442, 61444, 61446, 61448, 61450, 61452, 61454, +// 61456, 61458, 61460, 61462, 61464, 61466, 61468, 61470, 61472, 61474, 61476, +// 61478, 61480, 61482, 61484, 61486, 61488, 61490, 61492, 61494, 61496, 61498, +// 61500, 61502, 61504, 61506, 61508, 61510, 61512, 61514, 61516, 61518, 61520, +// 61522, 61524, 61526, 61528, 61530, 61532, 61534, 61536, 61538, 61540, 61542, +// 61544, 61546, 61548, 61550, 61552, 61554, 61556, 61558, 61560, 61562, 61564, +// 61566, 61568, 61570, 61572, 61574, 61576, 61578, 61580, 61582, 61584, 61586, +// 61588, 61590, 61592, 61594, 61596, 61598, 61600, 61602, 61604, 61606, 61608, +// 61610, 61612, 61614, 61616, 61618, 61620, 61622, 61624, 61626, 61628, 61630, +// 61632, 61634, 61636, 61638, 61640, 61642, 61644, 61646, 61648, 61650, 61652, +// 61654, 61656, 61658, 61660, 61662, 61664, 61666, 61668, 61670, 61672, 61674, +// 61676, 61678, 61680, 61682, 61684, 61686, 61688, 61690, 61692, 61694, 61696, +// 61698, 61700, 61702, 61704, 61706, 61708, 61710, 61712, 61714, 61716, 61718, +// 61720, 61722, 61724, 61726, 61728, 61730, 61732, 61734, 61736, 61738, 61740, +// 61742, 61744, 61746, 61748, 61750, 61752, 61754, 61756, 61758, 61760, 61762, +// 61764, 61766, 61768, 61770, 61772, 61774, 61776, 61778, 61780, 61782, 61784, +// 61786, 61788, 61790, 61792, 61794, 61796, 61798, 61800, 61802, 61804, 61806, +// 61808, 61810, 61812, 61814, 61816, 61818, 61820, 61822, 61824, 61826, 61828, +// 61830, 61832, 61834, 61836, 61838, 61840, 61842, 61844, 61846, 61848, 61850, +// 61852, 61854, 61856, 61858, 61860, 61862, 61864, 61866, 61868, 61870, 61872, +// 61874, 61876, 61878, 61880, 61882, 61884, 61886, 61888, 61890, 61892, 61894, +// 61896, 61898, 61900, 61902, 61904, 61906, 61908, 61910, 61912, 61914, 61916, +// 61918, 61920, 61922, 61924, 61926, 61928, 61930, 61932, 61934, 61936, 61938, +// 61940, 61942, 61944, 61946, 61948, 61950, 61952, 61954, 61956, 61958, 61960, +// 61962, 61964, 61966, 61968, 61970, 61972, 61974, 61976, 61978, 61980, 61982, +// 61984, 61986, 61988, 61990, 61992, 61994, 61996, 61998, 62000, 62002, 62004, +// 62006, 62008, 62010, 62012, 62014, 62016, 62018, 62020, 62022, 62024, 62026, +// 62028, 62030, 62032, 62034, 62036, 62038, 62040, 62042, 62044, 62046, 62048, +// 62050, 62052, 62054, 62056, 62058, 62060, 62062, 62064, 62066, 62068, 62070, +// 62072, 62074, 62076, 62078, 62080, 62082, 62084, 62086, 62088, 62090, 62092, +// 62094, 62096, 62098, 62100, 62102, 62104, 62106, 62108, 62110, 62112, 62114, +// 62116, 62118, 62120, 62122, 62124, 62126, 62128, 62130, 62132, 62134, 62136, +// 62138, 62140, 62142, 62144, 62146, 62148, 62150, 62152, 62154, 62156, 62158, +// 62160, 62162, 62164, 62166, 62168, 62170, 62172, 62174, 62176, 62178, 62180, +// 62182, 62184, 62186, 62188, 62190, 62192, 62194, 62196, 62198, 62200, 62202, +// 62204, 62206, 62208, 62210, 62212, 62214, 62216, 62218, 62220, 62222, 62224, +// 62226, 62228, 62230, 62232, 62234, 62236, 62238, 62240, 62242, 62244, 62246, +// 62248, 62250, 62252, 62254, 62256, 62258, 62260, 62262, 62264, 62266, 62268, +// 62270, 62272, 62274, 62276, 62278, 62280, 62282, 62284, 62286, 62288, 62290, +// 62292, 62294, 62296, 62298, 62300, 62302, 62304, 62306, 62308, 62310, 62312, +// 62314, 62316, 62318, 62320, 62322, 62324, 62326, 62328, 62330, 62332, 62334, +// 62336, 62338, 62340, 62342, 62344, 62346, 62348, 62350, 62352, 62354, 62356, +// 62358, 62360, 62362, 62364, 62366, 62368, 62370, 62372, 62374, 62376, 62378, +// 62380, 62382, 62384, 62386, 62388, 62390, 62392, 62394, 62396, 62398, 62400, +// 62402, 62404, 62406, 62408, 62410, 62412, 62414, 62416, 62418, 62420, 62422, +// 62424, 62426, 62428, 62430, 62432, 62434, 62436, 62438, 62440, 62442, 62444, +// 62446, 62448, 62450, 62452, 62454, 62456, 62458, 62460, 62462, 62464, 62466, +// 62468, 62470, 62472, 62474, 62476, 62478, 62480, 62482, 62484, 62486, 62488, +// 62490, 62492, 62494, 62496, 62498, 62500, 62502, 62504, 62506, 62508, 62510, +// 62512, 62514, 62516, 62518, 62520, 62522, 62524, 62526, 62528, 62530, 62532, +// 62534, 62536, 62538, 62540, 62542, 62544, 62546, 62548, 62550, 62552, 62554, +// 62556, 62558, 62560, 62562, 62564, 62566, 62568, 62570, 62572, 62574, 62576, +// 62578, 62580, 62582, 62584, 62586, 62588, 62590, 62592, 62594, 62596, 62598, +// 62600, 62602, 62604, 62606, 62608, 62610, 62612, 62614, 62616, 62618, 62620, +// 62622, 62624, 62626, 62628, 62630, 62632, 62634, 62636, 62638, 62640, 62642, +// 62644, 62646, 62648, 62650, 62652, 62654, 62656, 62658, 62660, 62662, 62664, +// 62666, 62668, 62670, 62672, 62674, 62676, 62678, 62680, 62682, 62684, 62686, +// 62688, 62690, 62692, 62694, 62696, 62698, 62700, 62702, 62704, 62706, 62708, +// 62710, 62712, 62714, 62716, 62718, 62720, 62722, 62724, 62726, 62728, 62730, +// 62732, 62734, 62736, 62738, 62740, 62742, 62744, 62746, 62748, 62750, 62752, +// 62754, 62756, 62758, 62760, 62762, 62764, 62766, 62768, 62770, 62772, 62774, +// 62776, 62778, 62780, 62782, 62784, 62786, 62788, 62790, 62792, 62794, 62796, +// 62798, 62800, 62802, 62804, 62806, 62808, 62810, 62812, 62814, 62816, 62818, +// 62820, 62822, 62824, 62826, 62828, 62830, 62832, 62834, 62836, 62838, 62840, +// 62842, 62844, 62846, 62848, 62850, 62852, 62854, 62856, 62858, 62860, 62862, +// 62864, 62866, 62868, 62870, 62872, 62874, 62876, 62878, 62880, 62882, 62884, +// 62886, 62888, 62890, 62892, 62894, 62896, 62898, 62900, 62902, 62904, 62906, +// 62908, 62910, 62912, 62914, 62916, 62918, 62920, 62922, 62924, 62926, 62928, +// 62930, 62932, 62934, 62936, 62938, 62940, 62942, 62944, 62946, 62948, 62950, +// 62952, 62954, 62956, 62958, 62960, 62962, 62964, 62966, 62968, 62970, 62972, +// 62974, 62976, 62978, 62980, 62982, 62984, 62986, 62988, 62990, 62992, 62994, +// 62996, 62998, 63000, 63002, 63004, 63006, 63008, 63010, 63012, 63014, 63016, +// 63018, 63020, 63022, 63024, 63026, 63028, 63030, 63032, 63034, 63036, 63038, +// 63040, 63042, 63044, 63046, 63048, 63050, 63052, 63054, 63056, 63058, 63060, +// 63062, 63064, 63066, 63068, 63070, 63072, 63074, 63076, 63078, 63080, 63082, +// 63084, 63086, 63088, 63090, 63092, 63094, 63096, 63098, 63100, 63102, 63104, +// 63106, 63108, 63110, 63112, 63114, 63116, 63118, 63120, 63122, 63124, 63126, +// 63128, 63130, 63132, 63134, 63136, 63138, 63140, 63142, 63144, 63146, 63148, +// 63150, 63152, 63154, 63156, 63158, 63160, 63162, 63164, 63166, 63168, 63170, +// 63172, 63174, 63176, 63178, 63180, 63182, 63184, 63186, 63188, 63190, 63192, +// 63194, 63196, 63198, 63200, 63202, 63204, 63206, 63208, 63210, 63212, 63214, +// 63216, 63218, 63220, 63222, 63224, 63226, 63228, 63230, 63232, 63234, 63236, +// 63238, 63240, 63242, 63244, 63246, 63248, 63250, 63252, 63254, 63256, 63258, +// 63260, 63262, 63264, 63266, 63268, 63270, 63272, 63274, 63276, 63278, 63280, +// 63282, 63284, 63286, 63288, 63290, 63292, 63294, 63296, 63298, 63300, 63302, +// 63304, 63306, 63308, 63310, 63312, 63314, 63316, 63318, 63320, 63322, 63324, +// 63326, 63328, 63330, 63332, 63334, 63336, 63338, 63340, 63342, 63344, 63346, +// 63348, 63350, 63352, 63354, 63356, 63358, 63360, 63362, 63364, 63366, 63368, +// 63370, 63372, 63374, 63376, 63378, 63380, 63382, 63384, 63386, 63388, 63390, +// 63392, 63394, 63396, 63398, 63400, 63402, 63404, 63406, 63408, 63410, 63412, +// 63414, 63416, 63418, 63420, 63422, 63424, 63426, 63428, 63430, 63432, 63434, +// 63436, 63438, 63440, 63442, 63444, 63446, 63448, 63450, 63452, 63454, 63456, +// 63458, 63460, 63462, 63464, 63466, 63468, 63470, 63472, 63474, 63476, 63478, +// 63480, 63482, 63484, 63486, 63488, 63490, 63492, 63494, 63496, 63498, 63500, +// 63502, 63504, 63506, 63508, 63510, 63512, 63514, 63516, 63518, 63520, 63522, +// 63524, 63526, 63528, 63530, 63532, 63534, 63536, 63538, 63540, 63542, 63544, +// 63546, 63548, 63550, 63552, 63554, 63556, 63558, 63560, 63562, 63564, 63566, +// 63568, 63570, 63572, 63574, 63576, 63578, 63580, 63582, 63584, 63586, 63588, +// 63590, 63592, 63594, 63596, 63598, 63600, 63602, 63604, 63606, 63608, 63610, +// 63612, 63614, 63616, 63618, 63620, 63622, 63624, 63626, 63628, 63630, 63632, +// 63634, 63636, 63638, 63640, 63642, 63644, 63646, 63648, 63650, 63652, 63654, +// 63656, 63658, 63660, 63662, 63664, 63666, 63668, 63670, 63672, 63674, 63676, +// 63678, 63680, 63682, 63684, 63686, 63688, 63690, 63692, 63694, 63696, 63698, +// 63700, 63702, 63704, 63706, 63708, 63710, 63712, 63714, 63716, 63718, 63720, +// 63722, 63724, 63726, 63728, 63730, 63732, 63734, 63736, 63738, 63740, 63742, +// 64110, 64218, 64220, 64222, 64224, 64226, 64228, 64230, 64232, 64234, 64236, +// 64238, 64240, 64242, 64244, 64246, 64248, 64250, 64252, 64254, 64263, 64265, +// 64267, 64269, 64271, 64273, 64280, 64282, 64284, 64311, 64317, 64319, 64322, +// 64325, 64451, 64453, 64455, 64457, 64459, 64461, 64463, 64465, 64912, 64968, +// 64970, 64972, 64974, 64976, 64978, 64980, 64982, 64984, 64986, 64988, 64990, +// 64992, 64994, 64996, 64998, 65000, 65002, 65004, 65006, 65050, 65052, 65054, +// 65107, 65127, 65132, 65134, 65141, 65277, 65279, 65471, 65473, 65480, 65488, +// 65496, 65501, 65503, 65511, 65519, 65521, 65523, 65525, 65527, 65529, 65531, +// 65534, 65536, +// ]; diff --git a/src/regex/invisibleWhitespaceRegex.test.ts b/src/regex/invisibleWhitespaceRegex.test.ts new file mode 100644 index 0000000..4873838 --- /dev/null +++ b/src/regex/invisibleWhitespaceRegex.test.ts @@ -0,0 +1,13 @@ +import { describe, expect, test } from "@jest/globals"; +import { invisibleWhitespaceRegex } from "./invisibleWhitespaceRegex"; + +describe("invisibleWhitespaceRegex", () => { + test("args", async () => { + [" ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " ", " "].forEach( + (char) => { + expect(invisibleWhitespaceRegex.test(char)).toBe(true); + invisibleWhitespaceRegex.lastIndex = 0; + } + ); + }); +}); diff --git a/src/regex/invisibleWhitespaceRegex.ts b/src/regex/invisibleWhitespaceRegex.ts new file mode 100644 index 0000000..f68482c --- /dev/null +++ b/src/regex/invisibleWhitespaceRegex.ts @@ -0,0 +1,4 @@ +export const invisibleWhitespaceRegex = /\p{Zs}/gu; + +// Charcodes: +// [32, 160, 5760, 8192, 8194, 8196, 8198, 8200, 8202, 8239, 8287, 12288]; diff --git a/src/regex/lineSeparatorRegex.test.ts b/src/regex/lineSeparatorRegex.test.ts new file mode 100644 index 0000000..f73bc8c --- /dev/null +++ b/src/regex/lineSeparatorRegex.test.ts @@ -0,0 +1,8 @@ +import { describe, expect, test } from "@jest/globals"; +import { lineSeparatorRegex } from "./lineSeparatorRegex"; + +describe("lineSeparatorRegex", () => { + test("args", async () => { + expect(lineSeparatorRegex.test(String.fromCharCode(8232))).toBe(true); + }); +}); diff --git a/src/regex/lineSeparatorRegex.ts b/src/regex/lineSeparatorRegex.ts new file mode 100644 index 0000000..62fc5c9 --- /dev/null +++ b/src/regex/lineSeparatorRegex.ts @@ -0,0 +1,4 @@ +export const lineSeparatorRegex = /\p{Zl}/gu; + +// charcode +// [8232] diff --git a/src/regex/paragraphSeparatorRegex.test.ts b/src/regex/paragraphSeparatorRegex.test.ts new file mode 100644 index 0000000..57108f2 --- /dev/null +++ b/src/regex/paragraphSeparatorRegex.test.ts @@ -0,0 +1,8 @@ +import { describe, expect, test } from "@jest/globals"; +import { paragraphSeparatorRegex } from "./paragraphSeparatorRegex"; + +describe("paragraphSeparatorRegex", () => { + test("args", async () => { + expect(paragraphSeparatorRegex.test(String.fromCharCode(8233))).toBe(true); + }); +}); diff --git a/src/regex/paragraphSeparatorRegex.ts b/src/regex/paragraphSeparatorRegex.ts new file mode 100644 index 0000000..39a3b14 --- /dev/null +++ b/src/regex/paragraphSeparatorRegex.ts @@ -0,0 +1,4 @@ +export const paragraphSeparatorRegex = /\p{Zp}/gu; + +// charcode +// [8233] diff --git a/src/types/Date.ts b/src/types/Date.ts new file mode 100644 index 0000000..b2d1fb0 --- /dev/null +++ b/src/types/Date.ts @@ -0,0 +1,7 @@ +export type DateLike = Date | string | number; +export type Datey = Date | string; + +export type DateRange = { + startDate: DateLike; + endDate: DateLike; +}; diff --git a/src/types/DateLike.ts b/src/types/DateLike.ts deleted file mode 100644 index 03ede2f..0000000 --- a/src/types/DateLike.ts +++ /dev/null @@ -1 +0,0 @@ -export type DateLike = Date | string | number; diff --git a/src/types/Matrix.ts b/src/types/Matrix.ts new file mode 100644 index 0000000..02048b7 --- /dev/null +++ b/src/types/Matrix.ts @@ -0,0 +1 @@ +export type Matrix = T[][]; diff --git a/src/types/NonUndefined.ts b/src/types/NonUndefined.ts new file mode 100644 index 0000000..eb46b14 --- /dev/null +++ b/src/types/NonUndefined.ts @@ -0,0 +1 @@ +export type NonUndefined = T extends undefined ? never : T; diff --git a/src/types/Object.ts b/src/types/Object.ts new file mode 100644 index 0000000..3da775a --- /dev/null +++ b/src/types/Object.ts @@ -0,0 +1,4 @@ +export type ObjectValue = T[keyof T]; +export type ObjectValues = ObjectValue[]; +export type ObjectKey = keyof T; +export type ObjectKeys = ObjectKey[]; diff --git a/src/types/ObjectValues.ts b/src/types/ObjectValues.ts deleted file mode 100644 index 80c59c1..0000000 --- a/src/types/ObjectValues.ts +++ /dev/null @@ -1 +0,0 @@ -export type ObjectValues = ObjectType[keyof ObjectType]; diff --git a/src/types/PlainObject.ts b/src/types/PlainObject.ts index 21d9108..cda52c2 100644 --- a/src/types/PlainObject.ts +++ b/src/types/PlainObject.ts @@ -1 +1 @@ -export type PlainObject = Record; +export type PlainObject = Record & { length?: never }; // Excludes arrays diff --git a/src/types/index.ts b/src/types/index.ts index 3da9779..da3a052 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -1,10 +1,10 @@ -export type NonUndefined = T extends undefined ? never : T; - export * from "./Coords"; -export * from "./DateLike"; +export * from "./Date"; export * from "./Dimensions"; +export * from "./Matrix"; export * from "./Maybe"; -export * from "./ObjectValues"; +export * from "./NonUndefined"; +export * from "./Object"; export * from "./PlainObject"; export * from "./Point"; export * from "./PrismaSelect"; diff --git a/src/validators/index.ts b/src/validators/index.ts index bf20196..6753696 100644 --- a/src/validators/index.ts +++ b/src/validators/index.ts @@ -5,20 +5,24 @@ export * from "./isClient"; export * from "./isEmail"; export * from "./isEmpty"; export * from "./isFunction"; +export * from "./isFutureDate"; export * from "./isJsDate"; export * from "./isKey"; +export * from "./isLastIndex"; export * from "./isNumber"; export * from "./isNumeric"; +export * from "./isNumericId"; export * from "./isObject"; +export * from "./isPastDate"; export * from "./isPromise"; export * from "./isPWA"; export * from "./isReactElement"; export * from "./isRegExp"; +export * from "./isSame"; export * from "./isServer"; +export * from "./isSpacedString"; export * from "./isString"; export * from "./isStringDate"; -export * from "./isUndefined"; export * from "./isURL"; export * from "./isUUID"; export * from "./isValue"; -//export * from "./isVariableName"; //TODO needs clarification diff --git a/src/validators/isBoolean.ts b/src/validators/isBoolean.ts index 839a8bf..76a0d48 100644 --- a/src/validators/isBoolean.ts +++ b/src/validators/isBoolean.ts @@ -1,2 +1,2 @@ -export const isBoolean = (arg: any) => +export const isBoolean = (arg: any): arg is boolean => Object.prototype.toString.call(arg) === "[object Boolean]"; diff --git a/src/validators/isEmpty.test.ts b/src/validators/isEmpty.test.ts index 79aab3c..dfc03c4 100644 --- a/src/validators/isEmpty.test.ts +++ b/src/validators/isEmpty.test.ts @@ -1,5 +1,5 @@ import { describe, expect, test } from "@jest/globals"; -import { isEmpty } from "./isEmpty"; +import { isEmpty, isEmptyObject } from "./isEmpty"; describe("isEmpty", () => { test("mixed", async () => { @@ -20,4 +20,8 @@ describe("isEmpty", () => { expect(isEmpty({})).toBeTruthy(); expect(isEmpty({ key: "value" })).toBeFalsy(); }); + + test("isEmptyObject", async () => { + expect(isEmptyObject({})).toBeTruthy(); + }); }); diff --git a/src/validators/isEmpty.ts b/src/validators/isEmpty.ts index 0aa1145..07b5b04 100644 --- a/src/validators/isEmpty.ts +++ b/src/validators/isEmpty.ts @@ -1,14 +1,16 @@ -import { Maybe } from "../types"; +import { Maybe, PlainObject } from "../types"; import { isArray } from "./isArray"; import { isObject } from "./isObject"; import { isString } from "./isString"; export const isEmpty = (arg?: Maybe) => { + if (arg === undefined) return true; + if (arg === null) return true; + if (isEmptyString(arg)) return true; if (isEmptyObject(arg)) return true; if (isEmptyArray(arg)) return true; - if (isEmptyString(arg)) return true; - return arg === undefined || arg === null; + return false; }; export const isEmptyString = (arg: string) => { @@ -19,6 +21,6 @@ export const isEmptyArray = (arg: any[]) => { return isArray(arg) && arg.length === 0; }; -export const isEmptyObject = (arg: Object) => { +export const isEmptyObject = (arg: PlainObject) => { return isObject(arg) && Object.keys(arg).length === 0; }; diff --git a/src/validators/isFunction.ts b/src/validators/isFunction.ts index 90513bb..3221e2b 100644 --- a/src/validators/isFunction.ts +++ b/src/validators/isFunction.ts @@ -1,2 +1,2 @@ -export const isFunction = (arg: any) => +export const isFunction = (arg: any): arg is Function => Object.prototype.toString.call(arg) === "[object Function]"; diff --git a/src/validators/isFutureDate.test.ts b/src/validators/isFutureDate.test.ts index 8da1918..57e2ae9 100644 --- a/src/validators/isFutureDate.test.ts +++ b/src/validators/isFutureDate.test.ts @@ -2,14 +2,21 @@ import { expect, it, describe } from "@jest/globals"; import { isFutureDate } from "./isFutureDate"; describe("isFutureDate", function () { - it("checks correctly", function () { - expect(isFutureDate("")).toBe(false); - expect(isFutureDate(new Date(new Date().getFullYear() - 1 + ""))).toBe( - false + it("true", function () { + expect(isFutureDate(new Date("3012"))).toBe(true); + expect(isFutureDate(new Date("3012-11-11"))).toBe(true); + expect(isFutureDate(new Date().getTime() + 1)).toBe(true); + expect(isFutureDate(new Date(new Date().getFullYear() + 1 + ""))).toBe( + true ); - expect(isFutureDate(new Date())).toBe(false); // too fast + }); - expect(isFutureDate(new Date().getTime() + 1)).toBe(true); - expect(isFutureDate(new Date("3012-11"))).toBe(true); + it("false", function () { + expect(isFutureDate(0)).toBe(false); + expect(isFutureDate(new Date("1012-11-11"))).toBe(false); + expect(isFutureDate(22220)).toBe(false); + expect(isFutureDate(new Date().getTime() - 1)).toBe(false); + expect(isFutureDate(new Date())).toBe(false); // too fast + expect(isFutureDate(new Date().getTime())).toBe(false); }); }); diff --git a/src/validators/isLastIndex.ts b/src/validators/isLastIndex.ts new file mode 100644 index 0000000..6dc2802 --- /dev/null +++ b/src/validators/isLastIndex.ts @@ -0,0 +1,3 @@ +export const isLastIndex = (index: number, array: any[]): boolean => { + return index === array.length - 1; +}; diff --git a/src/validators/isNumber.test.ts b/src/validators/isNumber.test.ts index 395dc2f..2950f01 100644 --- a/src/validators/isNumber.test.ts +++ b/src/validators/isNumber.test.ts @@ -29,6 +29,10 @@ describe("isNumber", function () { expect(isNumber(12.44)).toBe(true); expect(isNumber(-1244)).toBe(true); expect(isNumber(2e12)).toBe(true); + expect(isNumber(0xff)).toBe(true); + expect(isNumber(0b11111111)).toBe(true); + expect(isNumber(0.255e3)).toBe(true); + expect(isNumber(Math.PI)).toBe(true); }); }); diff --git a/src/validators/isNumeric.test.ts b/src/validators/isNumeric.test.ts new file mode 100644 index 0000000..8090020 --- /dev/null +++ b/src/validators/isNumeric.test.ts @@ -0,0 +1,32 @@ +import { expect, it, describe } from "@jest/globals"; +import { isNumeric } from "./isNumeric"; + +describe("isNumeric", function () { + it("checks true", function () { + expect(isNumeric("33")).toBe(true); + expect(isNumeric("-33")).toBe(true); + expect(isNumeric("2e12")).toBe(true); + expect(isNumeric("2.22")).toBe(true); + expect(isNumeric(".22")).toBe(true); + expect(isNumeric("0xff")).toBe(true); + expect(isNumeric("0b11111111")).toBe(true); + expect(isNumeric("0.255e3")).toBe(true); + }); + + it("checks false", function () { + expect(isNumeric("string")).toBe(false); + expect(isNumeric("")).toBe(false); + expect(isNumeric(" ")).toBe(false); + expect(isNumeric(" 1 ")).toBe(false); + expect(isNumeric("1 3")).toBe(false); + expect(isNumeric("..1")).toBe(false); + expect(isNumeric("\t")).toBe(false); + expect(isNumeric("\n")).toBe(false); + expect(isNumeric("\r")).toBe(false); + expect(isNumeric(Infinity)).toBe(false); + expect(isNumeric(-Infinity)).toBe(false); + expect(isNumeric(Math.pow as unknown as number)).toBe(false); + expect(isNumeric(null as unknown as number)).toBe(false); + expect(isNumeric(undefined as unknown as number)).toBe(false); + }); +}); diff --git a/src/validators/isNumeric.ts b/src/validators/isNumeric.ts index 69395e0..b9f2100 100644 --- a/src/validators/isNumeric.ts +++ b/src/validators/isNumeric.ts @@ -1,3 +1,11 @@ -export const isNumeric = (num: number | string): boolean => { - return !isNaN(num as number); // isNaN DOES actually work with string-numbers! +import { isNumber } from "./isNumber"; +import { isSpacedString } from "./isSpacedString"; +import { isString } from "./isString"; + +export const isNumeric = (arg: number | string): boolean => { + if (isNumber(arg)) return true; + + if (!isString(arg) || isSpacedString(arg)) return false; + + return !isNaN(parseFloat(arg)); }; diff --git a/src/validators/isNumericId.test.ts b/src/validators/isNumericId.test.ts new file mode 100644 index 0000000..562d27e --- /dev/null +++ b/src/validators/isNumericId.test.ts @@ -0,0 +1,22 @@ +import { describe, expect, test } from "@jest/globals"; +import { isNumericId } from "./isNumericId"; + +describe("isNumericId", () => { + test("true", async () => { + expect(isNumericId("1")).toBe(true); + expect(isNumericId("99999999")).toBe(true); + }); + + test("false", async () => { + expect(isNumericId("0")).toBe(false); + expect(isNumericId("2e3")).toBe(false); + expect(isNumericId("-0")).toBe(false); + expect(isNumericId("-1")).toBe(false); + expect(isNumericId(" ")).toBe(false); + expect(isNumericId(" 1")).toBe(false); + expect(isNumericId(" 1 ")).toBe(false); + expect(isNumericId(" 1 1")).toBe(false); + expect(isNumericId("1.1")).toBe(false); + expect(isNumericId("8/28/2023")).toBe(false); + }); +}); diff --git a/src/validators/isNumericId.ts b/src/validators/isNumericId.ts new file mode 100644 index 0000000..88b4500 --- /dev/null +++ b/src/validators/isNumericId.ts @@ -0,0 +1,2 @@ +export const isNumericId = (id: string): boolean => + /^\d+$/.test(id) && parseInt(id) > 0; diff --git a/src/validators/isPastDate.test.ts b/src/validators/isPastDate.test.ts index 15cf01f..a40cef3 100644 --- a/src/validators/isPastDate.test.ts +++ b/src/validators/isPastDate.test.ts @@ -2,12 +2,17 @@ import { expect, it, describe } from "@jest/globals"; import { isPastDate } from "./isPastDate"; describe("isPastDate", function () { - it("checks correctly", function () { - expect(isPastDate("")).toBe(false); - expect(isPastDate(new Date(new Date().getFullYear() + 1 + ""))).toBe(false); - expect(isPastDate(new Date())).toBe(false); // too fast - + it("true", function () { expect(isPastDate(new Date().getTime() - 1)).toBe(true); expect(isPastDate(new Date("2012"))).toBe(true); + expect(isPastDate(22220)).toBe(true); + expect(isPastDate(12220.234)).toBe(true); + }); + + it("false", function () { + expect(isPastDate(new Date(new Date().getFullYear() + 1 + ""))).toBe(false); + expect(isPastDate(new Date())).toBe(false); // too fast + expect(isPastDate(new Date().getTime())).toBe(false); + expect(isPastDate(new Date().getTime() + 1)).toBe(false); }); }); diff --git a/src/validators/isSame.test.ts b/src/validators/isSame.test.ts new file mode 100644 index 0000000..b3347b8 --- /dev/null +++ b/src/validators/isSame.test.ts @@ -0,0 +1,49 @@ +import { describe, expect, test } from "@jest/globals"; +import { isSame } from "./isSame"; + +describe("isSame", () => { + test("date", async () => { + const date = new Date(); + expect(isSame(date, date)).toBeTruthy(); + expect(isSame(new Date(), new Date())).toBeFalsy(); + }); + + test("number", async () => { + expect(isSame(1, 1)).toBeTruthy(); + expect(isSame(1, 0)).toBeFalsy(); + }); + + test("nullish", async () => { + expect(isSame(undefined, undefined)).toBeTruthy(); + expect(isSame(undefined, null)).toBeFalsy(); + }); + + test("function", async () => { + expect( + isSame( + () => {}, + () => {} + ) + ).toBeTruthy(); + expect( + isSame( + () => {}, + () => 1 + ) + ).toBeFalsy(); + }); + + test("object", async () => { + expect(isSame({}, {})).toBeTruthy(); + expect(isSame({ a: 1 }, { a: 1 })).toBeTruthy(); + expect(isSame({ a: [] }, { a: [] })).toBeTruthy(); + expect(isSame({}, { a: 1 })).toBeFalsy(); + }); + + test("array", async () => { + expect(isSame([Infinity], [Infinity])).toBeTruthy(); + expect(isSame([1], [1])).toBeTruthy(); + expect(isSame([1, null, () => {}], [1, null, () => {}])).toBeTruthy(); + expect(isSame([1], [1, null, () => {}])).toBeFalsy(); + }); +}); diff --git a/src/validators/isSame.ts b/src/validators/isSame.ts new file mode 100644 index 0000000..8f2763e --- /dev/null +++ b/src/validators/isSame.ts @@ -0,0 +1,30 @@ +import { getKeys } from "../helpers"; +import { isArray } from "./isArray"; +import { isFunction } from "./isFunction"; +import { isObject } from "./isObject"; + +export const isSame = (value1: any, value2: any): boolean => { + if (value1 === value2) { + return true; + } + + if (isArray(value1) && isArray(value2)) { + if (value1.length !== value2.length) return false; + return value1.every((value, index) => isSame(value, value2[index])); + } + + if (isObject(value1) && isObject(value2)) { + const keys = getKeys(value1); + if (keys.length !== getKeys(value2).length) return false; + + return keys.every((key: string) => { + return isSame(value1[key], value2[key]); + }); + } + + if (isFunction(value1) && isFunction(value2)) { + return value1.toString() === value2.toString(); + } + + return false; +}; diff --git a/src/validators/isSpacedString.ts b/src/validators/isSpacedString.ts new file mode 100644 index 0000000..84fee26 --- /dev/null +++ b/src/validators/isSpacedString.ts @@ -0,0 +1,3 @@ +export const isSpacedString = (s: string) => { + return s.indexOf(" ") >= 0; +}; diff --git a/src/validators/isString.ts b/src/validators/isString.ts index e0a1d26..bef08dc 100644 --- a/src/validators/isString.ts +++ b/src/validators/isString.ts @@ -1 +1 @@ -export const isString = (arg: any) => typeof arg === "string"; +export const isString = (arg: any): arg is string => typeof arg === "string"; diff --git a/src/validators/isTimestamp.ts b/src/validators/isTimestamp.ts deleted file mode 100644 index ae7555e..0000000 --- a/src/validators/isTimestamp.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { isJsDate } from "./isJsDate"; - -export const isTimestamp = (arg: number): boolean => { - const date = new Date(arg); - return isJsDate(date); -}; diff --git a/src/validators/isURL.test.ts b/src/validators/isURL.test.ts index f0fb6e3..f056c0a 100644 --- a/src/validators/isURL.test.ts +++ b/src/validators/isURL.test.ts @@ -2,14 +2,17 @@ import { expect, describe, it } from "@jest/globals"; import { isURL } from "./isURL"; describe("isURL", function () { - it("checks correctly", function () { + it("false", function () { expect(isURL("a@a.a")).toBe(false); expect(isURL("")).toBe(false); expect(isURL(" ")).toBe(false); + expect(isURL("//localhost")).toBe(false); + }); + + it("true", function () { expect(isURL("localhost")).toBe(true); expect(isURL("http://localhost")).toBe(true); expect(isURL("https://localhost")).toBe(true); - expect(isURL("//localhost")).toBe(false); expect(isURL("bim.bam")).toBe(true); }); }); diff --git a/src/validators/isUndefined.ts b/src/validators/isUndefined.ts deleted file mode 100644 index 3161bf0..0000000 --- a/src/validators/isUndefined.ts +++ /dev/null @@ -1 +0,0 @@ -export const isUndefined = (arg: any) => typeof arg === "undefined"; diff --git a/src/validators/isValue.ts b/src/validators/isValue.ts index 5fcddcb..ca011e3 100644 --- a/src/validators/isValue.ts +++ b/src/validators/isValue.ts @@ -1,5 +1,5 @@ import { Maybe } from "../types"; -export const isValue = (arg?: Maybe) => { +export const isValue = (arg?: Maybe): boolean => { return arg !== undefined && arg !== null && !Number.isNaN(arg); }; diff --git a/src/validators/isVariableName.test.ts b/src/validators/isVariableName.test.ts deleted file mode 100644 index f0ad711..0000000 --- a/src/validators/isVariableName.test.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { expect, describe, it } from "@jest/globals"; -import { isVariableName } from "./isVariableName"; - -describe("isVariableName", function () { - it("checks correctly", function () { - // no empty strings, symbols, etc. - expect(isVariableName("a@a.a")).toBe(false); - expect(isVariableName(" ")).toBe(false); - // no spaces at the beginning - expect(isVariableName(" a")).toBe(false); - // no numbers at the beginning - expect(isVariableName("0var")).toBe(false); - // no spaces - expect(isVariableName("let let")).toBe(false); - // no keywords - expect(isVariableName("var")).toBe(false); - expect(isVariableName("const")).toBe(false); - expect(isVariableName("let")).toBe(false); - expect(isVariableName("function")).toBe(false); - - expect(isVariableName("myVar")).toBe(true); - expect(isVariableName("let_let")).toBe(true); - expect( - isVariableName( - "askjdnalksjndalksjdnladnlunaleknASDASDASDASDLASPDLAPSLDPALSDPLASPDL2345643245345345345345345345" - ) - ).toBe(true); - }); -}); diff --git a/src/validators/isVariableName.ts b/src/validators/isVariableName.ts deleted file mode 100644 index 466d427..0000000 --- a/src/validators/isVariableName.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { JS_RESERVED_WORDS } from "../constants/words"; - -// I don't recall exactly why this was useful, probably for CSV exports/Object keys wrapped in quotes? -export const isVariableName = (arg: string) => { - return ( - !!arg && - JS_RESERVED_WORDS.indexOf(arg) === -1 && - new RegExp("^[a-zA-Z_$][0-9a-zA-Z_$]*$").test(arg) - ); -};