mentie 0.0.10 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/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'
@@ -17,13 +17,13 @@ export const is_node = typeof process !== 'undefined' && process.versions && pro
17
17
  * Checks if the code is running in a Firebase functions emulator environment.
18
18
  * @returns {boolean} Returns true if the code is running in a Firebase environment, otherwise returns false.
19
19
  */
20
- export const is_emulator = process.env.FUNCTIONS_EMULATOR === 'true'
20
+ export const is_emulator = typeof process !== 'undefined' && process.env?.FUNCTIONS_EMULATOR === 'true'
21
21
 
22
22
  // ///////////////////////////////
23
23
  // Mode and loglevel detection
24
24
  // ///////////////////////////////
25
25
 
26
- const node_dev = process.env.NODE_ENV === 'development'
26
+ const node_dev = typeof process !== 'undefined' && process.env?.NODE_ENV === 'development'
27
27
  const web_dev = typeof location !== 'undefined' && ( `${ location.href }`.includes( 'debug=true' ) || `${ location.href }`.includes( 'localhost' ) )
28
28
 
29
29
  /**
@@ -36,13 +36,13 @@ export const dev = node_dev || web_dev
36
36
  * The log level for web applications.
37
37
  * @type {string}
38
38
  */
39
- export const web_loglevel = is_web && new URLSearchParams( location.search ).get( 'loglevel' )
39
+ export const web_loglevel = is_web && new URLSearchParams( location?.search ).get( 'loglevel' )
40
40
 
41
41
  /**
42
42
  * The log level for the Node environment.
43
43
  * @type {string}
44
44
  */
45
- export const node_loglevel = process.env.LOG_LEVEL
45
+ export const node_loglevel = process.env?.LOG_LEVEL
46
46
 
47
47
 
48
48
  /**
@@ -7,11 +7,15 @@ 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
 
13
17
  // Check if the loglevel matches this call
14
- const levels = [ 'error', 'warn', 'info' ]
18
+ const levels = [ 'info' ]
15
19
  const should_log = dev || levels.includes( loglevel )
16
20
 
17
21
  // Log the messages if the loglevel matches
@@ -21,16 +25,16 @@ export function log( ...messages ) {
21
25
 
22
26
  /**
23
27
  * Logs the provided info messages to the console.
24
- * Only logs in development mode OR if ?loglevel= or LOG_LEVEL= is set to one of the following: 'error', 'warn'
25
- * 🎯 Goal: log verbose trace messages used only for extremely granular debugging
26
- * @example log.verbose( `Retreived key '${ key }' of type '${ typeof key }' from localstorage: `, cache )
28
+ * Only logs if ?loglevel= or LOG_LEVEL= is set to: 'info'
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.verbose = function( ...messages ) {
33
+ log.info = function( ...messages ) {
30
34
 
31
35
  // Check if the loglevel matches this call
32
- const levels = [ 'error', 'warn', 'info' ]
33
- const should_log = dev || levels.includes( loglevel )
36
+ const levels = [ 'info' ]
37
+ const should_log = levels.includes( loglevel )
34
38
 
35
39
  // Log the messages if the loglevel matches
36
40
  if( should_log ) console.info( ...messages )
@@ -39,7 +43,7 @@ log.verbose = function( ...messages ) {
39
43
 
40
44
  /**
41
45
  * Logs the provided info messages to the console.
42
- * Only logs in development mode OR if ?loglevel= or LOG_LEVEL= is set to one of the following: 'error', 'warn'
46
+ * Only logs in development mode OR if ?loglevel= or LOG_LEVEL= is set to one of the following: 'warn', 'info'
43
47
  * 🎯 Goal: log warnings of things that should not happen, but do not break functionality
44
48
  * @example log.warn( `Transaction history was empty, this should never happen: `, history )
45
49
  * @param {...any} messages - The messages to be logged.
@@ -47,7 +51,7 @@ log.verbose = function( ...messages ) {
47
51
  log.warn = function( ...messages ) {
48
52
 
49
53
  // Check if the loglevel matches this call
50
- const levels = [ 'error', 'warn' ]
54
+ const levels = [ 'warn', 'info' ]
51
55
  const should_log = dev || levels.includes( loglevel )
52
56
 
53
57
  // Log the messages if the loglevel matches
@@ -57,7 +61,7 @@ log.warn = function( ...messages ) {
57
61
 
58
62
  /**
59
63
  * Logs the provided error messages to the console.
60
- * Only logs in development mode OR if ?loglevel= or LOG_LEVEL= is set to one of the following: 'error'
64
+ * Only logs in development mode OR if ?loglevel= or LOG_LEVEL= is set to one of the following: 'error', 'warn', 'info'
61
65
  * @scope log errors that impact proper functioning of the application
62
66
  * @example log.error( `Error connecting to database: `, error )
63
67
  * @param {...any} messages - The messages to be logged.
@@ -65,7 +69,7 @@ log.warn = function( ...messages ) {
65
69
  log.error = function( ...messages ) {
66
70
 
67
71
  // Check if the loglevel matches this call
68
- const levels = [ 'error' ]
72
+ const levels = [ 'error', 'warn', 'info' ]
69
73
  const should_log = dev || levels.includes( loglevel )
70
74
  if( !should_log ) return
71
75
 
@@ -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,32 @@
1
+ /**
2
+ * Rounds a number to the specified number of decimals.
3
+ *
4
+ * @param {number} number - The number to round.
5
+ * @param {number} [decimals=4] - The number of decimals to round to. Default is 4.
6
+ * @returns {number} The rounded number.
7
+ */
8
+ export const round_number_to_decimals = ( number, decimals=4 ) => {
9
+
10
+ if( number == undefined ) return ''
11
+
12
+ const factor = 10 ** decimals
13
+ return Math.round( number * factor ) / factor
14
+ }
15
+
16
+ /**
17
+ * Generates a random number between a minimum and maximum value.
18
+ *
19
+ * @param {number} max_num - The maximum value for the random number.
20
+ * @param {number} [min_num=1] - The minimum value for the random number
21
+ * @returns {number} The generated random number.
22
+ */
23
+ export const random_number_between = ( max_num, min_num=1 ) => Math.floor( Math.random() * ( max_num - min_num + 1 ) ) + min_num
24
+
25
+
26
+ /**
27
+ * Generates a random number of a specified length.
28
+ *
29
+ * @param {number} length - The length of the random number.
30
+ * @returns {number} - The generated random number.
31
+ */
32
+ export const random_number_of_length = length => parseInt( Array.from( { length }, () => Math.floor( Math.random() * 10 ) ).join( '' ), 10 )
@@ -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
+ export async function throttle_and_retry( async_function_array=[], { max_parallel=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_parallel,
86
+ failFast: fail_fast,
87
+ ...logger && { progressCallback: logger }
88
+ }
89
+
90
+ // Return throttler
91
+ return Throttle.all( retryable_async_functions, throttle_config )
92
+
93
+ }
@@ -0,0 +1,40 @@
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 ).trim() }${ 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 {object} options - The options for copying to the clipboard.
19
+ * @param {string} [options.success_message='Copied to clipboard'] - The success message to be displayed after copying.
20
+ * @param {function} [options.alerter] - The custom function to display the success message.
21
+ * @returns {Promise<void>} - A promise that resolves when the text is successfully copied to the clipboard.
22
+ */
23
+ export const copy_to_clipboard = async ( text, { success_message='Copied to clipboard', alerter } ) => {
24
+ if( !navigator?.clipboard?.writeText ) return alert( 'Your browser does not support copying to clipboard' )
25
+ await navigator.clipboard.writeText( text )
26
+
27
+ if( alerter ) alerter( success_message )
28
+ else alert( success_message )
29
+ }
30
+
31
+ /**
32
+ * Capitalizes the first letter of a string.
33
+ *
34
+ * @param {string} string - The input string.
35
+ * @returns {string} The capitalized string.
36
+ */
37
+ export const capitalise = string => {
38
+ if( !string ) return ''
39
+ return `${ string?.charAt( 0 ).toUpperCase() }${ string?.slice( 1, string?.length ) }`
40
+ }
package/modules/time.js CHANGED
@@ -1,6 +1,68 @@
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=0 ) => new Promise( resolve => setTimeout( resolve, 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, specifically in GMT.
11
+ * @param {number} timestamp - The timestamp to convert.
12
+ * @returns {string} The RFC-822 formatted date string in GMT.
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
+
39
+
40
+ // Construct the RFC-822 date string
41
+ return `${ dayOfWeek }, ${ dayStr } ${ month } ${ year } ${ hoursStr }:${ minutesStr }:${ secondsStr } GMT`
42
+ }
43
+
44
+ /**
45
+ * Converts seconds to HH:MM:SS format.
46
+ *
47
+ * @param {number} seconds - The number of seconds to convert.
48
+ * @returns {string} The formatted time in HH:MM:SS format.
49
+ */
50
+ export function seconds_to_hh_mm_ss( seconds ) {
51
+
52
+ // Round the input to the nearest second
53
+ const rounded_seconds = Math.round( seconds )
54
+
55
+ // Calculate hours, minutes, and seconds
56
+ const hours = Math.floor( rounded_seconds / 3600 )
57
+ const minutes = Math.floor( rounded_seconds % 3600 / 60 )
58
+ const remaining_seconds = rounded_seconds % 60
59
+
60
+ // Pad each unit with zeros and concatenate them
61
+ const padded_hours = String( hours ).padStart( 2, '0' )
62
+ const padded_minutes = String( minutes ).padStart( 2, '0' )
63
+ const padded_seconds = String( remaining_seconds ).padStart( 2, '0' )
64
+
65
+ // Return the formatted time
66
+ return `${ padded_hours }:${ padded_minutes }:${ padded_seconds }`
67
+
68
+ }
@@ -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.10",
3
+ "version": "0.1.0",
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
  }