mentie 0.0.9 ā 0.0.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.js +5 -1
- package/modules/logging.js +10 -3
- package/modules/numbers.js +24 -0
- package/modules/promises.js +93 -0
- package/modules/text.js +33 -0
- package/modules/time.js +64 -1
- package/modules/validations.js +14 -0
- package/package.json +5 -1
- package/.eslintrc.cjs +0 -7
- package/.husky/pre-commit +0 -17
- package/.vscode/settings.json +0 -27
package/index.js
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
1
|
export * from './modules/logging.js'
|
|
2
2
|
export * from './modules/time.js'
|
|
3
|
-
export * from './modules/environment.js'
|
|
3
|
+
export * from './modules/environment.js'
|
|
4
|
+
export * from './modules/validations.js'
|
|
5
|
+
export * from './modules/text.js'
|
|
6
|
+
export * from './modules/numbers.js'
|
|
7
|
+
export * from './modules/promises.js'
|
package/modules/logging.js
CHANGED
|
@@ -7,6 +7,10 @@ import { dev, loglevel } from "./environment.js"
|
|
|
7
7
|
* šÆ Goal: log informational messages about the state of the application.
|
|
8
8
|
* @example log( `User state was updated to: `, user )
|
|
9
9
|
* @param {...any} messages - The messages to be logged.
|
|
10
|
+
* @property {function} info - Logs info trace messages used only for extremely granular debugging.
|
|
11
|
+
* @property {function} warn - Logs warnings of things that should not happen, but do not break functionality.
|
|
12
|
+
* @property {function} error - Logs errors that impact proper functioning of the application.
|
|
13
|
+
* @property {string} loglevel - The log level used in the environment
|
|
10
14
|
*/
|
|
11
15
|
export function log( ...messages ) {
|
|
12
16
|
|
|
@@ -22,11 +26,11 @@ export function log( ...messages ) {
|
|
|
22
26
|
/**
|
|
23
27
|
* Logs the provided info messages to the console.
|
|
24
28
|
* Only logs in development mode OR if ?loglevel= or LOG_LEVEL= is set to one of the following: 'error', 'warn'
|
|
25
|
-
* šÆ Goal: log
|
|
26
|
-
* @example log.
|
|
29
|
+
* šÆ Goal: log info trace messages used only for extremely granular debugging
|
|
30
|
+
* @example log.info( `Retreived key '${ key }' of type '${ typeof key }' from localstorage: `, cache )
|
|
27
31
|
* @param {...any} messages - The messages to be logged.
|
|
28
32
|
*/
|
|
29
|
-
log.
|
|
33
|
+
log.info = function( ...messages ) {
|
|
30
34
|
|
|
31
35
|
// Check if the loglevel matches this call
|
|
32
36
|
const levels = [ 'error', 'warn', 'info' ]
|
|
@@ -74,3 +78,6 @@ log.error = function( ...messages ) {
|
|
|
74
78
|
console.trace()
|
|
75
79
|
|
|
76
80
|
}
|
|
81
|
+
|
|
82
|
+
// Set the loglevel on the log function
|
|
83
|
+
log.loglevel = loglevel
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export const round_number_to_decimals = ( number, decimals=4 ) => {
|
|
2
|
+
|
|
3
|
+
if( number == undefined ) return ''
|
|
4
|
+
|
|
5
|
+
const factor = 10 ** decimals
|
|
6
|
+
return Math.round( number * factor ) / factor
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Generates a random number between 1 and the specified maximum number.
|
|
11
|
+
*
|
|
12
|
+
* @param {number} max_number - The maximum number for generating the random number.
|
|
13
|
+
* @returns {number} The generated random number.
|
|
14
|
+
*/
|
|
15
|
+
export const random_number_of_max = max_number => Math.floor( Math.random() * max_number ) + 1
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Generates a random number of a specified length.
|
|
20
|
+
*
|
|
21
|
+
* @param {number} length - The length of the random number.
|
|
22
|
+
* @returns {number} - The generated random number.
|
|
23
|
+
*/
|
|
24
|
+
export const random_number_of_length = length => Number( [ ...Array( length ) ].map( () => random_number_of_max( 9 ) ).join( '' ) )
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Creates a retryable function that wraps an async function and adds retry logic.
|
|
3
|
+
* @param {Function} async_function - The async function to be made retryable. MUST be an UNCALLED function. See example.
|
|
4
|
+
* @param {Object} options - The options for retrying.
|
|
5
|
+
* @param {number} [options.retry_times=5] - The number of times to retry the async function.
|
|
6
|
+
* @param {number} [options.cooldown_in_s=10] - The cooldown time in seconds between retries.
|
|
7
|
+
* @param {boolean} [options.cooldown_entropy=true] - Whether to add randomness to the cooldown time.
|
|
8
|
+
* @param {Function} [options.logger=null] - The logger function to log retry attempts.
|
|
9
|
+
* @returns {Function} - The retryable function.
|
|
10
|
+
* @example
|
|
11
|
+
* make_retryable( do_thing )
|
|
12
|
+
* make_retryable( () => fetch( 'https://api.com/data' ) )
|
|
13
|
+
* @see {@link https://www.npmjs.com/package/promise-retry|promise-retry}
|
|
14
|
+
*/
|
|
15
|
+
export async function make_retryable( async_function, { retry_times=5, cooldown_in_s=10, cooldown_entropy=true, logger=null } ) {
|
|
16
|
+
|
|
17
|
+
// Function dependencies
|
|
18
|
+
const Retrier = await import( 'promise-retry' )
|
|
19
|
+
const { wait } = await import( './time.js' )
|
|
20
|
+
|
|
21
|
+
// Set a default logger that does nothing if none was provided
|
|
22
|
+
if( !logger ) logger = () => {}
|
|
23
|
+
|
|
24
|
+
// Formulate retry logic
|
|
25
|
+
const retryable_function = () => Retrier( ( do_retry, retry_counter ) => {
|
|
26
|
+
|
|
27
|
+
// Failure handling
|
|
28
|
+
return async_function().catch?.( async e => {
|
|
29
|
+
|
|
30
|
+
// If retry attempts exhausted, throw out
|
|
31
|
+
if( retry_counter >= retry_times ) {
|
|
32
|
+
logger( { message: 'Retry failed definitively', data: { retry_counter, retry_times } } )
|
|
33
|
+
throw e
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// If retries left, retry with a progressive delay
|
|
37
|
+
const entropy = !cooldown_entropy ? 0 : .1 + Math.random() // Add some randomness to cooldown
|
|
38
|
+
const cooldown_in_ms = ( cooldown_in_s + entropy ) * 1000 // Convert cooldown to milliseconds
|
|
39
|
+
const cooldown = cooldown_in_ms + cooldown_in_ms * ( retry_counter - 1 ) // Increase cooldown with each retry
|
|
40
|
+
|
|
41
|
+
// Log and wait
|
|
42
|
+
logger( { message: 'Retry failed, pausing...', data: { retry_counter, retry_times, cooldown_in_s, cooldown_in_ms, cooldown } } )
|
|
43
|
+
await wait( cooldown )
|
|
44
|
+
logger( { message: 'Cooldown complete, continuing...', data: { retry_counter, retry_times } } )
|
|
45
|
+
|
|
46
|
+
// Retry
|
|
47
|
+
return do_retry()
|
|
48
|
+
|
|
49
|
+
} )
|
|
50
|
+
|
|
51
|
+
} )
|
|
52
|
+
|
|
53
|
+
return retryable_function
|
|
54
|
+
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Throttles and retries an array of async functions.
|
|
60
|
+
*
|
|
61
|
+
* @param {Array<Function>} async_function_array - Array of async functions to be throttled and retried.
|
|
62
|
+
* @param {Object} options - Options for throttling and retrying.
|
|
63
|
+
* @param {number} [options.max_parallell=2] - Maximum number of functions to run in parallel.
|
|
64
|
+
* @param {number} [options.retry_times] - Number of times to retry each function.
|
|
65
|
+
* @param {number} [options.cooldown_in_s] - Cooldown time in seconds between retries.
|
|
66
|
+
* @param {number} [options.cooldown_entropy] - Random factor to add to the cooldown time.
|
|
67
|
+
* @param {Function} [options.logger] - Progress callback function.
|
|
68
|
+
* @param {boolean} [options.fail_fast=true] - Whether to fail fast or continue with other functions when an error occurs.
|
|
69
|
+
* @returns {Promise<Array>} - A promise that resolves to an array of results from the async functions.
|
|
70
|
+
* @see {@link https://www.npmjs.com/package/promise-parallel-throttle|promise-parallel-throttle}
|
|
71
|
+
*/
|
|
72
|
+
async function throttle_and_retry( async_function_array=[], { max_parallell=2, retry_times, cooldown_in_s, cooldown_entropy, logger, fail_fast=true } ) {
|
|
73
|
+
|
|
74
|
+
// Function dependencies
|
|
75
|
+
const Throttle = await import( 'promise-parallel-throttle' )
|
|
76
|
+
|
|
77
|
+
// Create array of retryable functions
|
|
78
|
+
const retryable_async_functions = async_function_array.map( async_function => {
|
|
79
|
+
const retryable_function = make_retryable( async_function, { retry_times, cooldown_in_s, logger, cooldown_entropy } )
|
|
80
|
+
return retryable_function
|
|
81
|
+
} )
|
|
82
|
+
|
|
83
|
+
// Throttle configuration
|
|
84
|
+
const throttle_config = {
|
|
85
|
+
maxInProgress: max_parallell,
|
|
86
|
+
failFast: fail_fast,
|
|
87
|
+
...logger && { progressCallback: logger }
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Return throttler
|
|
91
|
+
return Throttle.all( retryable_async_functions, throttle_config )
|
|
92
|
+
|
|
93
|
+
}
|
package/modules/text.js
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Truncates a given text to a specified length and appends a suffix if necessary.
|
|
3
|
+
*
|
|
4
|
+
* @param {string} text - The text to be truncated.
|
|
5
|
+
* @param {number} [length=100] - The maximum length of the truncated text.
|
|
6
|
+
* @param {string} [suffix='...'] - The suffix to be appended to the truncated text.
|
|
7
|
+
* @returns {string} The truncated text.
|
|
8
|
+
*/
|
|
9
|
+
export const truncate = ( text, length=100, suffix='...' ) => {
|
|
10
|
+
if( !text ) return ''
|
|
11
|
+
if( text.length <= length ) return text
|
|
12
|
+
return `${ text.slice( 0, length ) }${ suffix }`
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Copies the given text to the clipboard.
|
|
17
|
+
* @param {string} text - The text to be copied to the clipboard.
|
|
18
|
+
* @param {string} [success_message='Copied to clipboard'] - The success message to be displayed after copying.
|
|
19
|
+
* @returns {Promise<void>} - A promise that resolves when the text is successfully copied to the clipboard.
|
|
20
|
+
*/
|
|
21
|
+
export const copy_to_clipboard = async ( text, success_message='Copied to clipboard' ) => {
|
|
22
|
+
if( !navigator?.clipboard?.writeText ) return alert( 'Your browser does not support copying to clipboard' )
|
|
23
|
+
await navigator.clipboard.writeText( text )
|
|
24
|
+
alert( success_message )
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Capitalizes the first letter of a string.
|
|
29
|
+
*
|
|
30
|
+
* @param {string} string - The input string.
|
|
31
|
+
* @returns {string} The capitalized string.
|
|
32
|
+
*/
|
|
33
|
+
export const capitalise = string => `${ string?.charAt( 0 ).toUpperCase() }${ string?.slice( 1, string?.length ) }`
|
package/modules/time.js
CHANGED
|
@@ -1,6 +1,69 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Waits for the specified amount of time.
|
|
3
3
|
* @param {number} ms - The number of milliseconds to wait.
|
|
4
|
+
* @param {boolean} [error=false] - If true, the promise will reject after the specified time.
|
|
4
5
|
* @returns {Promise<void>} - A promise that resolves after the specified time.
|
|
5
6
|
*/
|
|
6
|
-
export const wait = ( ms=
|
|
7
|
+
export const wait = ( ms, error=false ) => new Promise( ( res, rej ) => setTimeout( error ? rej : res, ms ) )
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Converts a timestamp to RFC-822 date format.
|
|
11
|
+
* @param {number} timestamp - The timestamp to convert.
|
|
12
|
+
* @returns {string} The RFC-822 formatted date string.
|
|
13
|
+
*/
|
|
14
|
+
export function timestamp_to_RFC822( timestamp ) {
|
|
15
|
+
|
|
16
|
+
// Days and months
|
|
17
|
+
const days = [ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" ]
|
|
18
|
+
const months = [ "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" ]
|
|
19
|
+
|
|
20
|
+
// Create a date object from the timestamp
|
|
21
|
+
const date = new Date( timestamp )
|
|
22
|
+
|
|
23
|
+
// Get components of the date
|
|
24
|
+
const dayOfWeek = days[date.getUTCDay()]
|
|
25
|
+
const dayOfMonth = date.getUTCDate()
|
|
26
|
+
const month = months[date.getUTCMonth()]
|
|
27
|
+
const year = date.getUTCFullYear()
|
|
28
|
+
const hours = date.getUTCHours()
|
|
29
|
+
const minutes = date.getUTCMinutes()
|
|
30
|
+
const seconds = date.getUTCSeconds()
|
|
31
|
+
|
|
32
|
+
// Format components
|
|
33
|
+
const dayStr = dayOfMonth < 10 ? '0' + dayOfMonth : dayOfMonth
|
|
34
|
+
const hoursStr = hours < 10 ? '0' + hours : hours
|
|
35
|
+
const minutesStr = minutes < 10 ? '0' + minutes : minutes
|
|
36
|
+
const secondsStr = seconds < 10 ? '0' + seconds : seconds
|
|
37
|
+
|
|
38
|
+
// Get the timezone on the running machine
|
|
39
|
+
const { timeZone } = Intl.DateTimeFormat().resolvedOptions()
|
|
40
|
+
|
|
41
|
+
// Construct the RFC-822 date string
|
|
42
|
+
return `${ dayOfWeek }, ${ dayStr } ${ month } ${ year } ${ hoursStr }:${ minutesStr }:${ secondsStr } ${ timeZone }`
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Converts seconds to HH:MM:SS format.
|
|
47
|
+
*
|
|
48
|
+
* @param {number} seconds - The number of seconds to convert.
|
|
49
|
+
* @returns {string} The formatted time in HH:MM:SS format.
|
|
50
|
+
*/
|
|
51
|
+
export function seconds_to_hh_mm_ss( seconds ) {
|
|
52
|
+
|
|
53
|
+
// Round the input to the nearest second
|
|
54
|
+
const rounded_seconds = Math.round( seconds )
|
|
55
|
+
|
|
56
|
+
// Calculate hours, minutes, and seconds
|
|
57
|
+
const hours = Math.floor( rounded_seconds / 3600 )
|
|
58
|
+
const minutes = Math.floor( rounded_seconds % 3600 / 60 )
|
|
59
|
+
const remaining_seconds = rounded_seconds % 60
|
|
60
|
+
|
|
61
|
+
// Pad each unit with zeros and concatenate them
|
|
62
|
+
const padded_hours = String( hours ).padStart( 2, '0' )
|
|
63
|
+
const padded_minutes = String( minutes ).padStart( 2, '0' )
|
|
64
|
+
const padded_seconds = String( remaining_seconds ).padStart( 2, '0' )
|
|
65
|
+
|
|
66
|
+
// Return the formatted time
|
|
67
|
+
return `${ padded_hours }:${ padded_minutes }:${ padded_seconds }`
|
|
68
|
+
|
|
69
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Regular expression for validating email addresses.
|
|
3
|
+
* @see {@link https://emailregex.com/}
|
|
4
|
+
* @type {RegExp}
|
|
5
|
+
*/
|
|
6
|
+
export const email_regex = /(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])/i
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Sanitizes a string by removing leading and trailing whitespace and converting it to lowercase.
|
|
10
|
+
*
|
|
11
|
+
* @param {string} string - The string to be sanitized.
|
|
12
|
+
* @returns {string} - The sanitized string.
|
|
13
|
+
*/
|
|
14
|
+
export const sanetise_string = string => `${ string }`.trim().toLowerCase()
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mentie",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.11",
|
|
4
4
|
"description": "Mentor's toolbelt",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.js",
|
|
@@ -26,5 +26,9 @@
|
|
|
26
26
|
"eslint-plugin-react": "^7.35.0",
|
|
27
27
|
"eslint-plugin-unused-imports": "^3.2.0",
|
|
28
28
|
"husky": "^9.1.3"
|
|
29
|
+
},
|
|
30
|
+
"dependencies": {
|
|
31
|
+
"promise-parallel-throttle": "^3.5.0",
|
|
32
|
+
"promise-retry": "^2.0.1"
|
|
29
33
|
}
|
|
30
34
|
}
|
package/.eslintrc.cjs
DELETED
package/.husky/pre-commit
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
echo "\nš¤ [ precommit hook ] linting before committing..."
|
|
2
|
-
|
|
3
|
-
# If errors, make it clear they cannot commit
|
|
4
|
-
if ! lint_outcome=$(npm run lint); then
|
|
5
|
-
echo "šØ [ precommit hook ] lint encountered blocking issues, fix them before committing\n"
|
|
6
|
-
echo "$lint_outcome"
|
|
7
|
-
exit 1
|
|
8
|
-
fi
|
|
9
|
-
|
|
10
|
-
# If warnings, suggest they fix them
|
|
11
|
-
if echo $lint_outcome | grep -q "warning"; then
|
|
12
|
-
echo "ā ļø [ precommit hook ] lint encountered warnings, consider fixing them before pushing\n"
|
|
13
|
-
echo "$lint_outcome"
|
|
14
|
-
exit 0
|
|
15
|
-
fi
|
|
16
|
-
|
|
17
|
-
echo "ā
[ precommit hook ] lint encountered no blocking issues\n"
|
package/.vscode/settings.json
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"i18n-ally.localesPaths": [
|
|
3
|
-
"public/locales"
|
|
4
|
-
],
|
|
5
|
-
"i18n-ally.keystyle": "nested",
|
|
6
|
-
"eslint.validate": [
|
|
7
|
-
"javascript",
|
|
8
|
-
"javascriptreact",
|
|
9
|
-
"typescript",
|
|
10
|
-
"typescriptreact"
|
|
11
|
-
],
|
|
12
|
-
"editor.codeActionsOnSave": {
|
|
13
|
-
"source.fixAll.eslint": "explicit"
|
|
14
|
-
},
|
|
15
|
-
"editor.renderWhitespace": "all",
|
|
16
|
-
"editor.formatOnSave": true,
|
|
17
|
-
"eslint.format.enable": true,
|
|
18
|
-
"eslint.run": "onType",
|
|
19
|
-
"explorer.fileNesting.enabled": true,
|
|
20
|
-
"explorer.fileNesting.patterns": {
|
|
21
|
-
"vite.config.js": "*.js,.babelrc,.nvmrc,index.html,.gitignore",
|
|
22
|
-
".env": ".env*,.*.json",
|
|
23
|
-
"firebase.json": "fire*,.fire*",
|
|
24
|
-
"package.json": "package-lock.json, yarn.lock, pnpm-lock.yaml,.eslint*,config.*,.npm*,.nvm*,.ncu*",
|
|
25
|
-
"README.md": "*.md,LICENSE",
|
|
26
|
-
}
|
|
27
|
-
}
|