mentie 0.3.14 → 0.3.16

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/modules/cache.js CHANGED
@@ -25,8 +25,8 @@ export function cache( key, value, expires_in_ms=Infinity ) {
25
25
  }
26
26
 
27
27
  // Warn if the key contains 'undefined'
28
- if( `${ key }`.includes( 'undefined' ) ) {
29
- log.warn( `The cache key ${ key } contains 'undefined', this may indicate a bug in your cache logic` )
28
+ if( `${ key }`.includes( 'undefined' ) || `${ key }`.includes( 'null' ) ) {
29
+ log.warn( `The cache key is ${ key }, this may indicate a bug in your cache logic` )
30
30
  }
31
31
 
32
32
  // If value is provided, save value and expiration
@@ -42,6 +42,49 @@ export function cache( key, value, expires_in_ms=Infinity ) {
42
42
  return _cache[key]?.value
43
43
  }
44
44
 
45
+ /**
46
+ * Restores the cache from a given cache object.
47
+ * If the cache object is empty, it will overwrite the current cache.
48
+ * If the cache object has keys that already exist in the current cache, those keys will be skipped.
49
+ * @param {Object} cache_object - The cache object to restore.
50
+ * @returns {Object} The updated cache object.
51
+ * @throws {Error} If the cache_object is not an object or is null.
52
+ */
53
+ cache.restore = ( cache_object ) => {
54
+
55
+ // Impute type of cache_object
56
+ let input_type = typeof cache_object
57
+ if( cache_object === null ) input_type = 'null'
58
+ if( Array.isArray( cache_object ) ) input_type = 'array'
59
+
60
+ // Check if the cache_object is an object, specifically check it is not an array
61
+ if( input_type != 'object' ) {
62
+ throw new Error( `cache.restore() expects an object, got ${ input_type }` )
63
+ }
64
+
65
+ // If current cache has no keys, restore the cache object
66
+ if( !Object.keys( _cache ).length ) {
67
+ // Assign the cache_object content to the cache obect
68
+ Object.assign( _cache, cache_object )
69
+ log.info( `Cache restored with ${ Object.keys( cache_object ).length } keys` )
70
+ return _cache
71
+ }
72
+
73
+ // If current cache has keys, only overwrite keys that are not already in cache, warn for keys that are skipped
74
+ const keys = Object.keys( cache_object )
75
+ for( const key of keys ) {
76
+ if( _cache[key] ) {
77
+ log.warn( `Cache key ${ key } already exists, skipping restore` )
78
+ } else {
79
+ _cache[key] = cache_object[ key ]
80
+ }
81
+ }
82
+
83
+ log.info( `Cache restored with ${ keys.length } keys, ${ Object.keys( _cache ).length } total keys in cache` )
84
+ return _cache
85
+
86
+ }
87
+
45
88
  /**
46
89
  *
47
90
  * @returns {Object} - A copy of the cache object.
@@ -84,11 +127,17 @@ cache.stats = () => {
84
127
  }
85
128
 
86
129
  /**
87
- * Function to inspect concurrency.
130
+ * Function to inspect concurrency calls of a function. Add this as a logger in a function to log out the concurrency value.
88
131
  *
89
132
  * @param {Function} logger - The logger function.
90
133
  * @param {string} key_prefix - The key for the concurrency value, used to tag the logging
91
134
  * @returns {number} - The concurrency value.
135
+ * @example
136
+ * import { cache, concurrency } from 'mentie';
137
+ * function often_called_function() {
138
+ * concurrency( console.log, 'important_function' );
139
+ * // ... function logic ...
140
+ * }
92
141
  */
93
142
  export function concurrency( logger, key_prefix ) {
94
143
 
package/modules/crypto.js CHANGED
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * @param {string} data - The data to be hashed.
5
5
  * @param {string} [algo='sha256'] - The algorithm to be used for hashing. Defaults to 'sha256'.
6
- * @param {string} [_digest='hex'] - The encoding of the output hash. Defaults to '
6
+ * @param {string} [_digest='hex'] - The encoding of the output hash. Defaults to 'hex'.
7
7
  * @returns {Promise<string>} The hashed value of the data.
8
8
  */
9
9
  export async function hash( data, algo='sha256', _digest='hex' ) {
@@ -165,7 +165,7 @@ export const is_emulator = env.is_emulator()
165
165
  * Checks if the code is running in a development environment.
166
166
  * @returns {boolean} Returns true if the code is running in a development environment, otherwise returns false.
167
167
  */
168
- export const is_github_actions = typeof process !== 'undefined' && process.env?.GITHUB_ACTIONS == true
168
+ export const is_github_actions = typeof process !== 'undefined' && process.env?.GITHUB_ACTIONS === 'true'
169
169
 
170
170
  /**
171
171
  * @returns {boolean} Returns true if the code is running on an Apple device, otherwise returns false.
@@ -109,7 +109,7 @@ export function log( ...messages ) {
109
109
  if( dev || should_log( levels ) ) {
110
110
 
111
111
  // Annotate the provided messages
112
- annotate_messages( messages )
112
+ messages = annotate_messages( messages )
113
113
 
114
114
  // Log the messages
115
115
  console.log( ...messages )
@@ -134,7 +134,7 @@ log.info = function( ...messages ) {
134
134
  if( env.is_emulator() || should_log( levels ) ) {
135
135
 
136
136
  // Annotate the provided messages
137
- annotate_messages( messages )
137
+ messages = annotate_messages( messages )
138
138
 
139
139
  // Log the messages
140
140
  console.info( ...messages )
@@ -159,7 +159,7 @@ log.warn = function( ...messages ) {
159
159
  if( dev || should_log( levels ) ) {
160
160
 
161
161
  // Annotate the provided messages
162
- annotate_messages( messages )
162
+ messages = annotate_messages( messages )
163
163
 
164
164
  // Log the messages
165
165
  console.warn( '⚠️ ', ...messages )
@@ -181,6 +181,9 @@ log.error = function( ...messages ) {
181
181
  const levels = [ 'error', 'warn', 'info' ]
182
182
  if( !should_log( levels ) ) return
183
183
 
184
+ // Annotate the provided messages
185
+ messages = annotate_messages( messages )
186
+
184
187
  // Log the messages if the loglevel matches
185
188
  console.error( '🚨 ', ...messages )
186
189
  console.trace()
package/modules/text.js CHANGED
@@ -22,12 +22,11 @@ export const truncate = ( text, length=100, suffix='...' ) => {
22
22
  * @param {function} [options.alerter] - The custom function to display the success message.
23
23
  * @returns {Promise<void>} - A promise that resolves when the text is successfully copied to the clipboard.
24
24
  */
25
- export const copy_to_clipboard = async ( text, { success_message='Copied to clipboard', alerter } ) => {
26
- if( !navigator?.clipboard?.writeText ) return alert( 'Your browser does not support copying to clipboard' )
25
+ export const copy_to_clipboard = async ( text, { success_message='Copied to clipboard', alerter=alert } ) => {
26
+ if( !navigator?.clipboard?.writeText ) return alerter( 'Your browser does not support copying to clipboard' )
27
27
  await navigator.clipboard.writeText( text )
28
28
 
29
- if( alerter ) alerter( success_message )
30
- else alert( success_message )
29
+ alerter( success_message )
31
30
  }
32
31
 
33
32
  /**
@@ -58,12 +57,12 @@ export const random_letter = ( allowed_chars, capitals=true ) => {
58
57
 
59
58
  }
60
59
 
61
- export const random_string_of_length = ( length, allowed_chars, numbers=true ) => {
60
+ export const random_string_of_length = ( length, allowed_chars, numbers=true, capitals=true ) => {
62
61
 
63
62
  return Array.from( { length }, () => {
64
63
  // If numbers are allowed, add a 50% chance of generating a number
65
64
  if( numbers && Math.random() > 0.5 ) return `${ random_number_between( 9, 0 ) }`
66
- return random_letter( allowed_chars )
65
+ return random_letter( allowed_chars, capitals )
67
66
  } ).join( '' )
68
67
 
69
68
  }
@@ -15,6 +15,51 @@ export const email_regex = /(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/
15
15
  */
16
16
  export const sanetise_string = string => `${ string }`.trim().toLowerCase()
17
17
 
18
+ /**
19
+ * Validates whether a given string is a valid IPv4 address.
20
+ * @param {string} ip - The IP address string to validate.
21
+ * @returns {boolean} True if the IP address is valid, otherwise false.
22
+ */
23
+ export const is_ipv4 = ip => {
24
+
25
+ // Split the IP address into its components and make then numbers
26
+ const octets = ip.split( '.' ).map( octet => parseInt( octet, 10 ) )
27
+
28
+ // Check if the IP address has 4 octets and each octet is between 0 and 255
29
+ return octets.length === 4 && octets.every( octet => !isNaN( octet ) && octet >= 0 && octet <= 255 )
30
+
31
+ }
32
+
33
+ /**
34
+ * Sanitizes and optionally validates an IPv4 address.
35
+ * @param {Object} options - The options object.
36
+ * @param {string} options.ip - The IP address to sanitize.
37
+ * @param {boolean} [options.validate=false] - Whether to validate the IP address.
38
+ * @param {boolean} [options.error_on_invalid=false] - Whether to throw an error on an invalid IP.
39
+ * @returns {string|null} The sanitized IPv4 address, or null if validation fails and error_on_invalid is false.
40
+ * @throws {Error} If the IP address is not provided or invalid when error_on_invalid is true.
41
+ */
42
+ export const sanetise_ipv4 = ( { ip, validate=false, error_on_invalid=false } ) => {
43
+
44
+ // Ensure ip was provided
45
+ if( !ip ) throw new Error( 'IP address is required' )
46
+
47
+ // Sanetise ip address as string
48
+ ip = sanetise_string( ip )
49
+
50
+ // Remove ipv6 prefix if present
51
+ ip = ip.replace( '::ffff:', '' )
52
+
53
+ // Check if the IP address is valid
54
+ if( validate && !is_ipv4( ip ) ) {
55
+ if( error_on_invalid ) throw new Error( `Invalid IPv4 address: ${ ip }` )
56
+ return null
57
+ }
58
+
59
+ // Return the sanitized IP address
60
+ return ip
61
+
62
+ }
18
63
 
19
64
  /**
20
65
  * Checks if an object contains all the required properties.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mentie",
3
- "version": "0.3.14",
3
+ "version": "0.3.16",
4
4
  "description": "Mentor's toolbelt",
5
5
  "type": "module",
6
6
  "main": "index.js",