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 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'
@@ -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 verbose trace messages used only for extremely granular debugging
26
- * @example log.verbose( `Retreived key '${ key }' of type '${ typeof key }' from localstorage: `, cache )
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
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
+ }
@@ -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=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.
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.9",
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
@@ -1,7 +0,0 @@
1
- // const { eslint_config } = require( './index.cjs' )
2
- const { eslint_config } = require( 'airier' )
3
-
4
- // Export the default eslint config
5
- module.exports = {
6
- ...eslint_config
7
- }
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"
@@ -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
- }