Skip to main content
version 1.0.0

Helpers

Introduction​

Athenna includes a variety of "helpers" classes inside the @athenna/common package that comes by default installed in your application. Many of these classes are used by the framework itself; however, you are free to use them in your own applications if you find them convenient.

Available helpers​

File​

Use the File class to create an instance of a file, it's existing or not.

import { File } from '@athenna/common'

// With the File helper you can manipulate an existing file, or create a new one

const existentFile = new File('path/to/existent/file.txt')
const nonExistentFile = new File('path/to/nonExistent/file.txt', Buffer.from('File content'))

// Now existentFile and nonExistentFile instances are created, but not loaded/created

// using load here because the file already exists, if using create, would generate an exception
existentFile.loadSync({ withContent: true })
// property withContent if true, will save the file content in the instance, Be careful with big files
nonExistentFile.createSync().loadSync({ withContent: true })

// now the files will have this properties
console.log(existentFile.createdAt)
console.log(existentFile.accessedAt)
console.log(existentFile.modifiedAt)
console.log(existentFile.fileSize)
console.log(existentFile.content)

// you can delete the file using remove method
existentFile.removeSync() // void

// you can get the content of the file with getContent method
console.log(existentFile.getContentSync()) // Some Buffer instance

// you can use toJSON method to get the instance informations in JSON
console.log(existentFile.toJSON()) // { ...infos }

// you can make a copy from existentFile using copy
console.log(existentFile.copySync('path/to/copy.txt'))

// you can move existentFile to other path using move
console.log(existentFile.moveSync('path/to/move.txt'))

// you can add content to the end of the file with append
console.log(existentFile.appendSync(Buffer.from('Content\n')))

// you can add content to the top of the file with prepend
console.log(existentFile.prependSync(Buffer.from('Content\n')))

// File uses readable streams in async methods to not block the event loop when handling huge files content
await existentFile.load()
await existentFile.copy()
await existentFile.move()
await existentFile.remove()
await existentFile.create()
await existentFile.append()
await existentFile.prepend()
await existentFile.getContent()

// You can use safeRemove method to delete the file without any exception if it does no exists
await File.safeRemove(existentFile.path)

// You can use isFileSync to verify if path is a file or directory
await File.isFileSync('package.json')

// You can use existsSync to verify if file exists
await File.existsSync('package.json')

// You can use createFileOfSize to create a fake file with determined size
// 100MB
await File.createFileOfSize('fake.js', 1024 * 1024 * 100)

Folder​

Use the Folder class to create an instance of a Folder, it's existing or not.

import { Folder } from '@athenna/common'

// With the Folder helper you can manipulate an existing folder, or create a new one

const existentFolder = new Folder('path/to/existent/folder')
const nonExistentFolder = new Folder('path/to/nonExistent/folder')

// Now existentFolder and nonExistentFolder instances are created, but not loaded/created

// using load here because the file already exists, if using create, would generate an exception
existentFolder.loadSync({ withSub: true, withFileContent: false })

// property withSub if true, will load files and subFolders from the folder
// property withFileContent if true, will get the content of all files in the folder, Be careful with big files
nonExistentFolder.createSync().loadSync({ withSub: true, withFileContent: true })

// now the folders will have this properties
console.log(existentFolder.createdAt)
console.log(existentFolder.accessedAt)
console.log(existentFolder.modifiedAt)
console.log(existentFolder.folderSize)

// you can delete the folder using remove method
existentFolder.removeSync() // void

// you can use toJSON method to get the instance informations in JSON
console.log(existentFolder.toJSON()) // { ...infos }

// you can make a copy from existentFolder using copy
console.log(existentFolder.copySync('path/to/copy'))

// you can move existentFolder to other path using move
console.log(existentFolder.moveSync('path/to/move'))

// you can use getFilesByPattern method to get all files in the folder that match some pattern
// if recursive is true, will go inside subFolders too
const recursive = true
console.log(existentFolder.getFilesByPattern('**/*.ts', recursive)) // [...files instance]

// you can use getFoldersByPattern method to get all folders in the folder that match some pattern
console.log(existentFolder.getFoldersByPattern('**', recursive)) // [...folders instance]

// Folder uses readable streams in async methods to not block the event loop when handling huge files content
await existentFolder.load()
await existentFolder.copy()
await existentFolder.move()
await existentFolder.remove()
await existentFolder.create()

// You can use safeRemove method to delete the folder without any exception if it does no exists
await Folder.safeRemove(existentFile.path)

// You can use isFolderSync to verify if path directory or file
await Folder.isFolderSync('path/to/folder')

// You can use existsSync to verify if folders exists
await Folder.existsSync('path/to/folder')

Is​

Use the Is class to validate if value is from some type or is empty, is uuid, is cpf, is cep, etc...

import { Is } from '@athenna/common'

// Is class is a validator. It validates if the value matches the name of the function and returns a boolean

Is.Empty('') // true
Is.Empty([]) // true
Is.Empty([1]) // false
Is.Empty({}) // true
Is.Empty({ hello: 'world' }) // false
Is.Empty(' ') // true
Is.Empty('hello') // false

Is.Json('not-valid-json') // false
Is.Ip('not-valid-ip') // false
Is.Uuid('not-valid-uuid') // false
Is.Cep('not-valid-cep') // false
Is.Cpf('not-valid-cpf') // false
Is.Cnpj('not-valid-cnpj') // false
Is.Async(() => {
}) // false
Is.Async(async () => {
}) // true
Is.Async(() => {
new Promise((resolve => resolve()))
}) // true

Is.String('value') // true
Is.Undefined('value') // false
Is.Null('value') // false
Is.Boolean('value') // false
Is.Buffer('value') // false
Is.Number('value') // false
Is.Object('value') // false
Is.Date('value') // false
Is.Array('value') // false
Is.Regexp('value') // false
Is.Error('value') // false
Is.Function('value') // false
Is.Class('value') // false
Is.Integer('value') // false
Is.Float('value') // false

Is.ArrayOfObjects('') // false
Is.ArrayOfObjects([1, 2, 3]) // false
Is.ArrayOfObjects([{ hello: 'world' }]) // true

String​

Use the String class to generate random strings, normalizations and case changes

import { String } from '@athenna/common'

// With the String class you can change the case of strings

const string = 'Hello world'
const capitalize = true

String.toCamelCase(string) // 'helloWorld'
String.toPascalCase(string) // 'HelloWorld'
String.toNoCase(string) // 'hello world'
String.toConstantCase(string) // HELLO_WORLD
String.toDashCase(string) // 'hello-world'
String.toDashCase(string, capitalize) // 'Hello-World'
String.toDotCase(string) // 'hello.world'
String.toDotCase(string, capitalize) // 'Hello.World'
String.toSnakeCase(string) // 'hello_world'
String.toSnakeCase(string, capitalize) // 'Hello_World'
String.toSentenceCase(string) // 'Hello world'
String.toSentenceCase(string, capitalize) // 'Hello World'

// You can generate random strings by size and random hexadecimal colors

String.generateRandom(10) // 'GpXuZScThi'
String.generateRandomColor() // '#d5063b'

// You can put a string in plural or in singular and in ordinal number

String.pluralize(string) // 'Hello worlds'
String.singularize(String.pluralize(string)) // 'Hello world'
String.ordinalize('1') // '1st'
String.ordinalize('2') // '2nd'
String.ordinalize('3') // '3rd'
String.ordinalize('10') // '10th'

// And you can also normalize base64 string

String.normalizeBase64('+++///===') // '___'

Exception​

Use the Exception class to extend the Error object and create custom exceptions

import { Exception } from '@athenna/common'

const content = 'An error has ocurred in your application!'
const status = 500
const code = 'APPLICATION_ERROR'
const help = 'Delete your code and start again'

const exception = new Exception(content, status, code, help)

const withStack = true
console.log(exception.toJSON(withStack))

/**
* {
* code: 'APPLICATION_ERROR',
* status: 500,
* content: 'An error has ocurred in your application!',
* help: 'Delete your code and start again',
* stack: ...,
* }
*/

console.log(await exception.prettify()) // Pretty exception log using Youch API

Extending Exception helper

import { Exception } from '@athenna/common'

export class InternalServerException extends Exception {
public constructor(content = 'An internal server error has ocurred', status = 500) {
super(content, status)
}
}

throw new InternalServerException()

Path​

Use the Path class to get the absolute path from project folders.

import { Path } from '@athenna/common'

const subPath = '/hello'

Path.pwd(subPath, beforePath) // '/home/your/computer/path/your-project-name/hello'

// You can set a default before path for most Path methods
Path.defaultBeforePath = 'build'

Path.pwd(subPath, beforePath) // '/home/your/computer/path/your-project-name/build/hello'

Path.pwd('/src/') // '/home/your/computer/path/your-project-name/build/src'

Json​

Use the Json class to parse json without errors, deep copy, observeChanges inside objects and more.

import { Json } from '@athenna/common'

const textWithJsons = 'string with a Json inside of it {"text":"hello"} and one more Json {"hello":"world"}'

Json.getJson(textWithJsons) // ['{"text":"hello"}', '{"hello":"world"}']

const text = 'a string that is not a valid JSON'

Json.parse(text) // null
const object = {
test: 'hello',
hello: () => 'hy',
}

const objectCopy = Json.copy(object)

objectCopy.test = 'hello from copy'
objectCopy.hello = () => 'hy from copy'

console.log(object.test) // hello
console.log(object.hello()) // hy
console.log(objectCopy.test) // hello from copy
console.log(objectCopy.hello()) // hy from copy
const data = {}

const doSomething = (value, args) => {
console.log(`Name changed to: ${value}`, args)
}

const args = {
value: 'args are the same second parameter of doSomething function'
}

Json.observeChanges(data, 'name', doSomething, args)

data.name = 'João'

// Name changed to: João { value: 'args are the same second parameter of doSomething function' }

const object = {
number1: 'good string',
number2: 'bad string',
}

const readyToSaveOnDatabase = Json.fillable(object, ['number1'])

console.log(readyToSaveOnDatabase) // { number1: 'good string' }
const array = [1, 1, 2, 4, 4]

console.log(Json.removeDuplicated(array)) // [1, 2, 4]
const array = ['a', 'b', 'c'] // Array length = 2 (0, 1, 2)
const raffledValue = Json.raffle(array) // Raffled value from the array, could be a, b or c

console.log(raffledValue) // a, b or c
const object = {
hello: {
world: {
value: {
hello: 'Hello World!',
},
},
},
}

const value = Json.get(object, 'hello.world.value.hello') // 'Hello World!'
const undefinedValue = Json.get(object, 'hello.worlld.value.hello') // undefined
const defaultValue = Json.get(object, 'hello.worlld.value.hello', 'Hi World!') // 'Hi World!'
const fullObject = Json.get(object, '') // Same as object { hello: { world: { value: { hello: 'Hello World!' } } } }
const defaultValueInObjectNull = Json.get(undefined, '', { hello: 'world' }) // { hello: 'world' }

Module​

Use the Module class to resolve modules exports, import modules using hrefs' ensuring compatibility between OS's, creating aliases for your modules exports and creating __filename and __dirname properties.

import { Module } from '@athenna/common'

const module = await Module.get(import('#src/Helpers/Options'))

console.log(module.name) // Options
import { Module } from '@athenna/common'

const modules = await Module.getAll([import('#src/Helpers/Number'), import('#src/Helpers/Options')])

console.log(modules[0].name) // Number
console.log(modules[1].name) // Options
import { Module } from '@athenna/common'

const modules = await Module.getAllWithAlias([
import('#src/Helpers/Number'),
import('#src/Helpers/Options')
], 'App/Helpers')

console.log(modules[0].module.name) // Number
console.log(modules[0].alias) // 'App/Helpers/Number'

console.log(modules[1].module.name) // Options
console.log(modules[1].alias) // 'App/Helpers/Options'
import { Path, Module } from '@athenna/common'

const module = await Module.getFrom(Path.config('app.js'))

console.log(module.name) // Athenna
console.log(module.description) // Athenna application
console.log(module.environment) // production
import { Path, Module } from '@athenna/common'

const modules = await Module.getAllFromWithAlias(Path.config(), 'App/Configs')
const appConfigFile = module[0].module
const appConfigAlias = module[0].alias

console.log(appConfigAlias) // App/Configs/App
console.log(appConfigFile.name) // Athenna
console.log(appConfigFile.description) // Athenna application
console.log(appConfigFile.environment) // production
import { Module } from '@athenna/common'

const setInGlobalTrue = true
const setInGlobalFalse = false

const dirname = Module.createDirname(import.meta.url, setInGlobalFalse)
const filename = Module.createFilename(import.meta.url, setInGlobalTrue)

console.log(__dirname) // Error! __dirname is not defined in global
console.log(__filename) // '/Users/...'

Route​

Use the Route class to manipulate paths, getParams, getQueryParams, create route matcher RegExp etc.

import { Route } from '@athenna/common'

const absolutePath = '/tests/:id/users/:user_id'
const path = '/tests/1/users/2?page=1&limit=10'

Route.getQueryString(path) // ?page=1&limit=10
Route.removeQueryParams(path) // /tests/1/users/2
Route.getQueryParamsValue(path) // { page: '1', limit: '10' }
Route.getQueryParamsName(path) // ['path', 'limit']
Route.getParamsValue(absolutePath, path) // { id: '1', user_id: '10' }
Route.getParamsName(absolutePath) // ['id', 'user_id']

const regExpMatcher = Route.createMatcher(absolutePath) // /^(?:\/tests\b)(?:\/[\w-]+)(?:\/users\b)(?:\/[\w-]+)$/

regExpMatcher.test(path) // false - because of queryParams
regExpMatcher.test(Route.removeQueryParams(path)) // true

Number​

Use the Number class to manipulate numbers the best way.

import { Number } from '@athenna/common'

const arrayOfNumbers = [2, 4]
const stringNumber = "Hello my name is João, I'm 20 year old!"

// Get the lower/higher number from the array
console.log(Number.getLower(arrayOfNumbers)) // 2
console.log(Number.getHigher(arrayOfNumbers)) // 4

// Extract numbers from strings
console.log(Number.extractNumber(stringNumber)) // '20'
console.log(Number.extractNumbers(stringNumber)) // ['20']

// Return the average from infinite parameters or array of numbers
console.log(Number.argsAverage(2, 4)) // 3
console.log(Number.arrayAverage(arrayOfNumbers)) // 3

// Generate random integers values between interval
console.log(Number.randomIntFromInterval(1, 1)) // 1
console.log(Number.randomIntFromInterval(1, 2)) // 1
console.log(Number.randomIntFromInterval(1, 2)) // 2
console.log(Number.randomIntFromInterval(1, 10)) // 8

Token​

Use the Token class to generate UUID tokens using a prefix, and validate it to using uuidv4 library.

import { Token } from '@athenna/common'

// Do not use the char "-", it would break token.verify() method
const uuidGeneratedToken = Token.generate('yourServicePrefix')
console.log(uuidGeneratedToken) // yourServicePrefix-c546b11c-2c2b-11eb-adc1-0242ac120002

const isUuid = Token.verify(uuidGeneratedToken)
console.log(isUuid) // true

Parser​

Use the Parser class to parse all type of data of you application.

import { Parser } from '@athenna/common'

// Convert a string to array using a separator

const string1 = '1,2,3'
const separator = ','
const parsed1 = Parser.stringToArray(string1, separator)

console.log(parsed1) // ['1', '2', '3']
// Convert an array to string using separators

Parser.arrayToString(['1', '2', '3', '4']) // '1, 2, 3 and 4'
Parser.arrayToString(['1', '2', '3', '4'], // '1|2|3-4'
{ separator: '|', lastSeparator: '-' }
)

// Pair separator is only for two indexes arrays
Parser.arrayToString(['1', '2'], { // '1_2'
pairSeparator: '_',
})
const string2 = 'aaaasadzczaaa21313'
const parsed2 = Parser.stringToNumber(string2)

console.log(parsed2) // 21313
const object = {
joao: 'joao',
email: 'lenonsec7@gmail.com',
}
const parsed3 = Parser.jsonToFormData(object)

console.log(parsed3) // &joao=joao&email=lenonSec7%40gmail.com
const parsed4 = Parser.formDataToJson('?joao=joao&email=lenonSec7%40gmail.com')

console.log(parsed4) // { joao: 'joao', email: 'lenonsec7@gmail.com' }
const message = 'Link: https://google.com'

// Convert url to and HTML href

console.log(Parser.linkToHref(message)) // Link: <a href="https://google.com">https://google.com</a>
// Convert number size to bytes

Parser.sizeToByte(1024) // '1KB'
Parser.sizeToByte(1048576) // '1MB'
Parser.sizeToByte(1073741824) // '1GB'
Parser.sizeToByte(1099511627776) // '1TB'
Parser.sizeToByte(1125899906842624) // '1PB'

// Convert bytes to number size

Parser.byteToSize('1KB') // 1024
Parser.byteToSize('1MB') // 1048576
Parser.byteToSize('1GB') // 1073741824
Parser.byteToSize('1TB') // 1099511627776
Parser.byteToSize('1PB') // 1125899906842624
// Convert time string to ms

Parser.timeToMs('2 days') // 172800000
Parser.timeToMs('1d') // 86400000
Parser.timeToMs('10h') // 36000000
Parser.timeToMs('-10h') // -36000000
Parser.timeToMs('1 year') // 31557600000
Parser.timeToMs('-1 year') // -31557600000

// Convert ms to time string

const long = true

Parser.msToTime(172800000, long) // '2 days'
Parser.msToTime(86400000) // 1d
Parser.msToTime(36000000) // 10h
Parser.msToTime(-36000000) // -10h
Parser.msToTime(31557600000, long) // 1 year
Parser.msToTime(-31557600000, long) // -1 year
// Convert status code to reason

Parser.statusCodeToReason(200) // OK
Parser.statusCodeToReason('201') // CREATED
Parser.statusCodeToReason(404) // NOT_FOUND
Parser.statusCodeToReason('500') // INTERNAL_SERVER_ERROR

// Convert reason to status code

Parser.reasonToStatusCode('OK') // 200
Parser.reasonToStatusCode('created') // 201
Parser.reasonToStatusCode('NOT_found') // 404
Parser.reasonToStatusCode('internal server error') // 500
const url =
'postgresql://postgres:root@127.0.0.1:5432/postgres?paramOne=1&paramTwo=2&paramThree=3'

// Convert database connection url to connection object
const connectionObject = Parser.dbUrlToConnectionObj(url)

/** connectionObject result
* {
* protocol: 'postgresql',
* user: 'postgres',
* password: 'root',
* host: '127.0.0.1',
* port: 5432,
* database: 'postgres',
* options: {
* paramOne: '1',
* paramTwo: '2',
* paramThree: '3',
* }
* }
*/

// Convert connection object to database connection url
const connectionUrl = Parser.connectionObjToDbUrl(connectionObject)

/** connectionUrl result
* postgresql://postgres:root@127.0.0.1:5432/postgres?paramOne=1&paramTwo=2&paramThree=3
*/

Clean​

Use the Clean class to clean arrays and objects.

import { Clean } from '@athenna/common'

const array = [null, undefined, 1, "number"]

console.log(Clean.cleanArray(array)) // [1, "number"]

const object = {
number1: "number",
number2: null,
number3: undefined,
number4: 1,
}

const object2 = {
number1: null,
number2: [object],
}

console.log(Clean.cleanObject(object)) // { number1: "number", number4: 1 }
console.log(Clean.cleanArraysInObject(object2)) // { number2: [{ number1: "number", number4: 1 }]}

Debug​

Use the Debug class to generate debug logs in Athenna format.

import { Debug } from '@athenna/common'

const context = 'API'
const namespace = 'api:main'

const debug = new Debug(context, namespace)

// You can still change the context/namespace of the instance in runtime
debug
.buildContext(context)
.buildNamespace(namespace)
.log('Hello World!') // api:main [Athenna Debugger] - PID: 85580 - 02/15/2022, 11:47:56 AM [API] Hello World! +0ms

// You can log objects too, it will be converted to string in the formatter
debug
.buildContext('Object')
.buildNamespace('api:object')
.log({ hello: 'world' }) // api:object [Athenna Debugger] - PID: 85770 - 02/15/2022, 11:53:48 AM [Object] {"hello":"world"} +0ms