@lowentry/utils 1.7.1 → 1.9.1

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.
Files changed (3) hide show
  1. package/LeUtils.js +251 -1
  2. package/index.js +2 -4
  3. package/package.json +2 -1
package/LeUtils.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import FastDeepEqual from 'fast-deep-equal';
2
- import {ISSET, IS_OBJECT, STRING, INT_LAX, FLOAT_LAX, INT_LAX_ANY, FLOAT_LAX_ANY} from './LeTypes.js';
2
+ import CloneDeep from 'clone-deep';
3
+ import {ISSET, IS_OBJECT, IS_ARRAY, STRING, INT_LAX, FLOAT_LAX, INT_LAX_ANY, FLOAT_LAX_ANY} from './LeTypes.js';
3
4
 
4
5
 
5
6
  /**
@@ -2765,4 +2766,253 @@ export const LeUtils = {
2765
2766
  }
2766
2767
  return transactionalValue.changes[transactionalValue.changes.length - 1].value;
2767
2768
  },
2769
+
2770
+ /**
2771
+ * Creates a worker thread. Workers have to be stored at /workers/{workerName}.worker.js for this to work.
2772
+ *
2773
+ * Example of a worker file:
2774
+ *
2775
+ * ```js
2776
+ * onmessage = (message) =>
2777
+ * {
2778
+ * postMessage({
2779
+ * ...message.data,
2780
+ * results: ['...some expensive calculation involving message.data...'],
2781
+ * });
2782
+ * };
2783
+ * ```
2784
+ *
2785
+ * Usage:
2786
+ *
2787
+ * ```js
2788
+ * const {results} = await (async () =>
2789
+ * {
2790
+ * try
2791
+ * {
2792
+ * return await LeUtils.sendWorkerMessage('my-worker', {someData:[1, 2, 3, 4, 5]});
2793
+ * }
2794
+ * catch(error)
2795
+ * {
2796
+ * console.error('MyWorker: ', error);
2797
+ * return {results:[]};
2798
+ * }
2799
+ * })();
2800
+ * ```
2801
+ *
2802
+ * or, if you want more control over the number of threads you have (the above example will only create 1 thread per worker):
2803
+ *
2804
+ * ```js
2805
+ * const myWorker1 = LeUtils.createWorkerThread('my-worker'); // creates a thread, you can create multiple worker threads of the same worker, to run multiple instances in parallel
2806
+ * const myWorker2 = LeUtils.createWorkerThread('my-worker'); // same worker, another thread
2807
+ * const {results} = await (async () =>
2808
+ * {
2809
+ * try
2810
+ * {
2811
+ * return await myWorker1.sendMessage({someData:[1, 2, 3, 4, 5]});
2812
+ * }
2813
+ * catch(error)
2814
+ * {
2815
+ * console.error('MyWorker: ', error);
2816
+ * return {results:[]};
2817
+ * }
2818
+ * })();
2819
+ * ```
2820
+ *
2821
+ * @param {string} name
2822
+ * @returns {{worker: Worker, sendMessage: function(Object, {timeout: number|undefined}|undefined): Promise<Object>}}
2823
+ */
2824
+ createWorkerThread:
2825
+ (name) =>
2826
+ {
2827
+ const worker = new Worker('/workers/' + name + '.worker.js');
2828
+ let listeners = {};
2829
+
2830
+ const addListener = (id, callback) =>
2831
+ {
2832
+ listeners[id] = callback;
2833
+ };
2834
+
2835
+ const removeListener = (id) =>
2836
+ {
2837
+ delete listeners[id];
2838
+ };
2839
+
2840
+ const sendMessage = (data, options) =>
2841
+ {
2842
+ return new Promise((resolve, reject) =>
2843
+ {
2844
+ const id = LeUtils.uniqueId();
2845
+ addListener(id, resolve);
2846
+ setTimeout(() =>
2847
+ {
2848
+ removeListener(id);
2849
+ reject('timeout');
2850
+ }, options?.timeout ?? 10000);
2851
+
2852
+ worker.postMessage({
2853
+ id,
2854
+ ...data,
2855
+ });
2856
+ });
2857
+ };
2858
+
2859
+ worker.onerror = (error) =>
2860
+ {
2861
+ console.error('Worker ' + name + ':', error);
2862
+ };
2863
+ worker.onmessage = (message) =>
2864
+ {
2865
+ const data = message.data;
2866
+ if(data?.id)
2867
+ {
2868
+ const callback = listeners[data.id];
2869
+ if(callback)
2870
+ {
2871
+ removeListener(data.id);
2872
+ callback(data);
2873
+ }
2874
+ }
2875
+ };
2876
+
2877
+ return {worker, sendMessage};
2878
+ },
2879
+
2880
+ /**
2881
+ * Sends a message to the given worker. Creates a worker thread for this worker if it doesn't exist yet.
2882
+ *
2883
+ * See {@link LeUtils#createWorkerThread} for more info on how to use workers.
2884
+ *
2885
+ * @param {string} workerName
2886
+ * @param {Object} data
2887
+ * @param {{timeout: number|undefined}} [options]
2888
+ * @returns {Promise<Object>}
2889
+ */
2890
+ sendWorkerMessage:
2891
+ (() =>
2892
+ {
2893
+ const workers = {};
2894
+ return (workerName, data, options) =>
2895
+ {
2896
+ if(!workers[workerName])
2897
+ {
2898
+ workers[workerName] = LeUtils.createWorkerThread(workerName);
2899
+ }
2900
+ return workers[workerName].sendMessage(data, options);
2901
+ };
2902
+ })(),
2903
+
2904
+ /**
2905
+ * Returns a deep copy of the given value.
2906
+ *
2907
+ * @param {*} value
2908
+ * @returns {*}
2909
+ */
2910
+ clone:
2911
+ (value) => CloneDeep(value, true),
2912
+
2913
+ /**
2914
+ * Purges the given email address, returning an empty string if it's invalid.
2915
+ *
2916
+ * @param {string} email
2917
+ * @returns {string}
2918
+ */
2919
+ purgeEmail:
2920
+ (email) =>
2921
+ {
2922
+ email = STRING(email).trim().toLowerCase().replace(/\s/g, '');
2923
+ if(!email.includes('@') || !email.includes('.'))
2924
+ {
2925
+ return '';
2926
+ }
2927
+ return email;
2928
+ },
2929
+
2930
+ /**
2931
+ * Returns true if the focus is effectively clear, meaning that the user is not typing in an input field.
2932
+ *
2933
+ * @returns {boolean}
2934
+ */
2935
+ isFocusClear:(() =>
2936
+ {
2937
+ const inputTypes = ['text', 'search', 'email', 'number', 'password', 'tel', 'time', 'url', 'week', 'month', 'date', 'datetime-local'];
2938
+ return () => !((document?.activeElement?.tagName?.toLowerCase() === 'input') && inputTypes.includes(document?.activeElement?.type?.toLowerCase()));
2939
+ })(),
2940
+
2941
+ /**
2942
+ * Returns the user's locale. Returns 'en-US' if it can't be determined.
2943
+ *
2944
+ * @returns {string}
2945
+ */
2946
+ getUserLocale:(() =>
2947
+ {
2948
+ let userLocale = null;
2949
+ return () =>
2950
+ {
2951
+ if(userLocale === null)
2952
+ {
2953
+ userLocale = (() =>
2954
+ {
2955
+ if(typeof window === 'undefined')
2956
+ {
2957
+ return 'en-US';
2958
+ }
2959
+ let locales = window.navigator.languages;
2960
+ if(!IS_ARRAY(locales) || (locales.length <= 0))
2961
+ {
2962
+ return 'en-US';
2963
+ }
2964
+ locales = locales.filter(locale => ((typeof locale === 'string') && locale.includes('-') && (locale.toLowerCase() !== 'en-us')));
2965
+ if(locales.length <= 0)
2966
+ {
2967
+ return 'en-US';
2968
+ }
2969
+ const localesNoEnglish = locales.filter(locale => !locale.toLowerCase().startsWith('en-'));
2970
+ if(localesNoEnglish.length <= 0)
2971
+ {
2972
+ return locales[0];
2973
+ }
2974
+ return localesNoEnglish[0];
2975
+ })();
2976
+ }
2977
+ return userLocale;
2978
+ };
2979
+ })(),
2980
+
2981
+ /**
2982
+ * Returns the user's locale date format. Always returns YYYY MM DD, with the character in between depending on the user's locale. Returns 'YYYY/MM/DD' if the user's locale can't be determined.
2983
+ *
2984
+ * @returns {string}
2985
+ */
2986
+ getUserLocaleDateFormat:(() =>
2987
+ {
2988
+ let userLocaleDateFormat = null;
2989
+ return () =>
2990
+ {
2991
+ if(userLocaleDateFormat === null)
2992
+ {
2993
+ userLocaleDateFormat = (() =>
2994
+ {
2995
+ let char = '/';
2996
+ if((typeof window !== 'undefined') && (typeof Intl !== 'undefined') && (typeof Intl.DateTimeFormat !== 'undefined'))
2997
+ {
2998
+ const formattedDate = new Intl.DateTimeFormat(LeUtils.getUserLocale()).format();
2999
+ if(formattedDate.includes('-'))
3000
+ {
3001
+ char = '-';
3002
+ }
3003
+ else if(formattedDate.includes('. '))
3004
+ {
3005
+ char = '.';
3006
+ }
3007
+ else if(formattedDate.includes('.'))
3008
+ {
3009
+ char = '.';
3010
+ }
3011
+ }
3012
+ return 'YYYY' + char + 'MM' + char + 'DD';
3013
+ })();
3014
+ }
3015
+ return userLocaleDateFormat;
3016
+ };
3017
+ })(),
2768
3018
  };
package/index.js CHANGED
@@ -1,4 +1,2 @@
1
- import {LeUtils} from './LeUtils.js';
2
- import {ISSET, IS_ARRAY, ARRAY, IS_OBJECT, OBJECT, STRING, STRING_ANY, INT, INT_ANY, FLOAT, FLOAT_ANY, INT_LAX, INT_LAX_ANY, FLOAT_LAX, FLOAT_LAX_ANY} from './LeTypes.js';
3
-
4
- export {LeUtils, ISSET, IS_ARRAY, ARRAY, IS_OBJECT, OBJECT, STRING, STRING_ANY, INT, INT_ANY, FLOAT, FLOAT_ANY, INT_LAX, INT_LAX_ANY, FLOAT_LAX, FLOAT_LAX_ANY};
1
+ export {LeUtils} from './LeUtils.js';
2
+ export {ISSET, IS_ARRAY, ARRAY, IS_OBJECT, OBJECT, STRING, STRING_ANY, INT, INT_ANY, FLOAT, FLOAT_ANY, INT_LAX, INT_LAX_ANY, FLOAT_LAX, FLOAT_LAX_ANY} from './LeTypes.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lowentry/utils",
3
- "version": "1.7.1",
3
+ "version": "1.9.1",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "description": "Provides utilities for general JavaScript development.",
@@ -20,6 +20,7 @@
20
20
  "test": "node --check index.js"
21
21
  },
22
22
  "dependencies": {
23
+ "clone-deep": "^4",
23
24
  "fast-deep-equal": "^3"
24
25
  }
25
26
  }