@vbarbarosh/node-helpers 3.67.0 → 3.69.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.
Files changed (53) hide show
  1. package/README.md +2 -2
  2. package/dist/browser.js +603 -141
  3. package/package.json +1 -1
  4. package/src/_finally.js +8 -0
  5. package/src/_finally.md +19 -0
  6. package/src/_perf.js +18 -0
  7. package/src/_perf.md +13 -0
  8. package/src/_restore.js +16 -0
  9. package/src/_restore.md +21 -0
  10. package/src/_try.js +11 -0
  11. package/src/_try.md +5 -0
  12. package/src/array_max.md +1 -0
  13. package/src/array_min.md +1 -0
  14. package/src/array_shuffle.md +5 -0
  15. package/src/array_unique.md +1 -0
  16. package/src/array_unique_last.md +1 -0
  17. package/src/browser/index.js +15 -7
  18. package/src/fastdl.md +8 -0
  19. package/src/fcmp_from_spec.js +2 -0
  20. package/src/fcmpx.md +17 -0
  21. package/src/fcmpx.test.js +16 -1
  22. package/src/filter1_from_spec.md +7 -0
  23. package/src/format_hrtime.md +1 -0
  24. package/src/fs_fopen_update.md +5 -0
  25. package/src/fs_size_enoent.md +3 -0
  26. package/src/fs_tempdir.md +6 -0
  27. package/src/now_atom.js +1 -1
  28. package/src/now_atom.md +1 -0
  29. package/src/parallel.md +5 -0
  30. package/src/ping_socket.md +3 -0
  31. package/src/stream_chunk.md +1 -0
  32. package/src/stream_data_ln.md +1 -0
  33. package/src/stream_each.md +1 -0
  34. package/src/stream_filter.md +1 -0
  35. package/src/stream_lines.md +1 -0
  36. package/src/stream_map.md +1 -0
  37. package/src/stream_map_flatten.md +1 -0
  38. package/src/stream_map_parallel.md +1 -0
  39. package/src/stream_parse_csv.md +3 -0
  40. package/src/stream_progress.js +1 -1
  41. package/src/stream_progress.md +8 -0
  42. package/src/stream_skip.md +1 -0
  43. package/src/stream_strpbrk.md +1 -0
  44. package/src/stream_tap.md +1 -0
  45. package/src/stream_xml_analyze.md +1 -0
  46. package/src/stream_xml_parse.md +1 -0
  47. package/src/throttle.md +3 -0
  48. package/src/urlclean.js +1 -1
  49. package/src/urlclean.md +2 -0
  50. package/src/urlmod.md +7 -0
  51. package/src/user_friendly_status.md +4 -0
  52. package/src/wait_while.md +5 -0
  53. package/src/waitcb.md +12 -0
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "author": "Vladimir Barbarosh <vladimir.barbarosh@gmail.com>",
3
3
  "license": "MIT",
4
4
  "name": "@vbarbarosh/node-helpers",
5
- "version": "3.67.0",
5
+ "version": "3.69.0",
6
6
  "description": "A set of helpers for JavaScript/Node.js",
7
7
  "unpkg": "dist/browser.js",
8
8
  "jsdelivr": "dist/browser.js",
@@ -0,0 +1,8 @@
1
+ function _finally(callback)
2
+ {
3
+ return {
4
+ [Symbol.dispose]: callback,
5
+ };
6
+ }
7
+
8
+ module.exports = _finally;
@@ -0,0 +1,19 @@
1
+ `_finally` lets you register cleanup code that always runs when the scope ends.
2
+
3
+ ```js
4
+ this.foo = 12345;
5
+ using _ = _finally(() => this.foo = null);
6
+ // ...
7
+ ```
8
+
9
+ This is essentially the same as:
10
+
11
+ ```js
12
+ this.foo = 12345;
13
+ try {
14
+ // ..
15
+ }
16
+ finally {
17
+ this.foo = null;
18
+ }
19
+ ```
package/src/_perf.js ADDED
@@ -0,0 +1,18 @@
1
+ function _perf(name, print = s => console.log(s))
2
+ {
3
+ const time0 = performance.now();
4
+ return {
5
+ times: 1,
6
+ [Symbol.dispose]: function () {
7
+ const duration = performance.now() - time0;
8
+ if (this.times > 1) {
9
+ print(`[${name}] ${duration.toFixed(4)}ms x ${this.times}`);
10
+ }
11
+ else {
12
+ print(`[${name}] ${duration.toFixed(4)}ms`);
13
+ }
14
+ },
15
+ };
16
+ }
17
+
18
+ module.exports = _perf;
package/src/_perf.md ADDED
@@ -0,0 +1,13 @@
1
+ Measures time from the current position till the end of a block scope.
2
+
3
+ ```js
4
+ app.component('sidebar', {
5
+ template: `...`,
6
+ methods: {
7
+ click_download: async function () {
8
+ using _ = _perf('download');
9
+ // ...
10
+ },
11
+ },
12
+ });
13
+ ```
@@ -0,0 +1,16 @@
1
+ // 1) Capture original value
2
+ // 2) Assign a temporary one
3
+ // 3) Automatically restore on scope exit
4
+ //
5
+ // Basically: set variable, and automatically restore the original value at the end of scope.
6
+ function _restore(value, callback)
7
+ {
8
+ const [original] = callback(value);
9
+ return {
10
+ [Symbol.dispose]: function () {
11
+ callback(original);
12
+ },
13
+ };
14
+ }
15
+
16
+ module.exports = _restore;
@@ -0,0 +1,21 @@
1
+ The pattern "set a new value now, restore the old one when scope ends".
2
+
3
+ ```js
4
+ using _ = _restore(false, v => [this.controls.play, this.controls.play = v]);
5
+ // ...
6
+ ```
7
+
8
+ This is essentially the same as:
9
+
10
+ ```js
11
+ {
12
+ const _old = this.controls.play;
13
+ this.controls.play = false;
14
+ try {
15
+ // ...
16
+ }
17
+ finally {
18
+ this.controls.play = _old;
19
+ }
20
+ }
21
+ ```
package/src/_try.js ADDED
@@ -0,0 +1,11 @@
1
+ function _try(callback, default_value)
2
+ {
3
+ try {
4
+ return callback();
5
+ }
6
+ catch {
7
+ return default_value;
8
+ }
9
+ }
10
+
11
+ module.exports = _try;
package/src/_try.md ADDED
@@ -0,0 +1,5 @@
1
+ Safely evaluates code and falls back to a default value if something goes wrong.
2
+
3
+ ```js
4
+ const time = _try(() => this.$refs.iframe.contentWindow.timeline.time(), 0);
5
+ ```
@@ -0,0 +1 @@
1
+ Returns the first element in the array with the maximal weight.
@@ -0,0 +1 @@
1
+ Returns the first element in the array with the minimal weight.
@@ -0,0 +1,5 @@
1
+ Shuffles an array using Durstenfeld shuffle algorithm.
2
+
3
+ Note: This function performs shuffle in-place. To get a shuffled copy
4
+ use call slice before calling, e.g. `array_shuffle(items.slice())`.
5
+
@@ -0,0 +1 @@
1
+ Return unique values; if a value occurs multiple times, keep the first one.
@@ -0,0 +1 @@
1
+ Return unique values; if a value occurs multiple times, keep the last one.
@@ -1,7 +1,15 @@
1
- // https://github.com/webpack/webpack/issues/625
2
- // https://webpack.js.org/guides/dependency-management/#require-context
3
- const require_tmp = require.context('..', true, /\b(array|date|http_delete|http_get_blob|http_get_buffer|http_get_json|http_get_utf8|http_head|http_patch_json|http_post_json|http_post_multipart|http_put_buffer|http_put_json|http_put_utf8|identity|ignore|fcmp|filter|format|plural|random_int|urlmod|waitcb)[^/]*(?<!\.test)\.js$/);
4
- require_tmp.keys().forEach(function (key) {
5
- const [, basename] = key.match(/([^/]+)\.js$/);
6
- window[basename] = require_tmp(key);
7
- });
1
+ const ns = new URL(document.currentScript.src).searchParams.get('var') ?? 'h';
2
+ if (typeof window[ns] !== 'undefined') {
3
+ console.log(`❌ @vbarbarosh/node-helpers@${__VERSION__} was not injected — window.${ns} is already in use`);
4
+ }
5
+ else {
6
+ console.log(`🎉 @vbarbarosh/node-helpers@${__VERSION__} successfully exposed as window.${ns}`);
7
+ window[ns] = {};
8
+ // https://github.com/webpack/webpack/issues/625
9
+ // https://webpack.js.org/guides/dependency-management/#require-context
10
+ const require_tmp = require.context('..', true, /\b(array|date|http_delete|http_get_blob|http_get_buffer|http_get_json|http_get_utf8|http_head|http_patch_json|http_post_json|http_post_multipart|http_put_buffer|http_put_json|http_put_utf8|identity|ignore|fcmp|filter|format|plural|random_int|urlmod|waitcb)[^/]*(?<!\.test)\.js$/);
11
+ require_tmp.keys().forEach(function (key) {
12
+ const [, basename] = key.match(/([^/]+)\.js$/);
13
+ window[ns][basename] = require_tmp(key);
14
+ });
15
+ }
package/src/fastdl.md ADDED
@@ -0,0 +1,8 @@
1
+ Download a file in several connections in parallel.
2
+
3
+ ```js
4
+ await fastdl({
5
+ file: fs_path_basename(new URL(url).pathname),
6
+ read_stream_with_range: (first, last) => http_get_stream_range(url, first, last),
7
+ });
8
+ ```
@@ -1,6 +1,8 @@
1
1
  /**
2
2
  * Create fcmp from an array of props. For desc order a prop should be prefixed
3
3
  * with minus sign (e.g. -price).
4
+ *
5
+ * @deprecated Deprecated in favor of fcmpx
4
6
  */
5
7
  function fcmp_from_spec(props)
6
8
  {
package/src/fcmpx.md ADDED
@@ -0,0 +1,17 @@
1
+ Comparator builder using compact expressions.
2
+
3
+ fcmp expression — Creates an `fcmp` function from an expression, suitable for use with `[].sort()`.
4
+
5
+ ```js
6
+ fcmpx('user.email')
7
+ fcmpx('-user.age,user.email')
8
+ fcmpx(v => v.user.email)
9
+ fcmpx(['user.age', 'user.email'])
10
+ fcmpx(['-user.age', 'user.email'])
11
+ ```
12
+
13
+ Sorting rule: Missing values are considered greater than any defined value
14
+ and are therefore placed after all defined values.
15
+
16
+ - YouTube: [Сортировка объектов в JavaScript](https://www.youtube.com/watch?v=rHEH_JfQjL4)
17
+ - https://github.com/vbarbarosh/speaking/tree/master/20250822-js-sort-fcmpx
package/src/fcmpx.test.js CHANGED
@@ -8,6 +8,21 @@ const users = [
8
8
  {id: 'betty|32|-', user: {name: 'betty', tag: {short: 'bar', long: 'barbar'}, age: 32}},
9
9
  ];
10
10
 
11
+ // https://dummyjson.com/docs/products
12
+
13
+ const products = [
14
+ {id: 1, name: 'Yoga Mat', category: 'Yoga', price: 25.99, brand: 'BalanceFrom', rating: 4.6, discount: 0.1, availability: 'in_stock'},
15
+ {id: 2, name: 'Adjustable Dumbbells', category: 'Strength', price: 199.99, brand: 'Bowflex', rating: 4.8, availability: 'preorder'},
16
+ {id: 3, name: 'Resistance Bands Set', category: 'Strength', price: 32.5, brand: 'Fit Simplify', rating: 4.5, discount: 0.15, availability: 'in_stock'},
17
+ {id: 4, name: 'Treadmill', category: 'Cardio', price: 899, brand: 'NordicTrack', rating: 4.7, availability: 'out_of_stock'},
18
+ {id: 5, name: 'Kettlebell 20lb', category: 'Strength', price: 45, brand: 'CAP Barbell', rating: 4.4, discount: 0.05, availability: 'in_stock'},
19
+ {id: 6, name: 'Foam Roller', category: 'Recovery', price: 19.99, brand: 'TriggerPoint', rating: 4.6, availability: 'limited'},
20
+ {id: 7, name: 'Exercise Bike', category: 'Cardio', price: 599, brand: 'Peloton', rating: 4.9, discount: 0.2, availability: 'in_stock'},
21
+ {id: 8, name: 'Pull-Up Bar', category: 'Strength', price: 29.95, brand: 'Iron Gym', rating: 4.3, availability: 'in_stock'},
22
+ {id: 9, name: 'Jump Rope', category: 'Cardio', price: 12.99, brand: 'WOD Nation', rating: 4.2, discount: 0.25, availability: 'limited'},
23
+ {id: 10, name: 'Ab Roller', category: 'Core', price: 21.49, brand: 'Perfect Fitness', rating: 4.5, availability: 'in_stock'},
24
+ ];
25
+
11
26
  const tests = [
12
27
  {
13
28
  description: 'missing: should move undefined values to the bottom',
@@ -149,7 +164,7 @@ const tests = [
149
164
  describe('fcmpx', function () {
150
165
  tests.forEach(function (test) {
151
166
  test.expressions.forEach(function (expr, i) {
152
- it(`${test.description}: #${i+1} | ${test.description}`, function () {
167
+ it(`${test.description}: #${i + 1} | ${test.description}`, function () {
153
168
  const actual = test.items.slice().sort(fcmpx(expr)).map(test.extract);
154
169
  assert.deepStrictEqual(actual, test.expected);
155
170
  });
@@ -0,0 +1,7 @@
1
+ Creates a filtering function based on the following spec:
2
+
3
+ 1. Substrings are separated by `/`.
4
+ 2. Each substring may have special characters:
5
+ - `^`: Substring must appear at the beginning.
6
+ - `$`: Substring must appear at the end.
7
+ - `!`: Negates the condition (substring must not appear).
@@ -0,0 +1 @@
1
+ Return a human-readable representation of hrtime (the return value from `process.hrtime(hrtime0)`).
@@ -0,0 +1,5 @@
1
+ The main use case is uploading files in chunks.
2
+
3
+ Each time a chunk is uploaded, update the target file.
4
+
5
+ By the end, you will have a ready-to-use file.
@@ -0,0 +1,3 @@
1
+ Returns the size of a file, or 0 if the file is not present.
2
+
3
+ It was created for cases when you need to resume a download.
@@ -0,0 +1,6 @@
1
+ ```js
2
+ await fs_tempdir(async function (d) {
3
+ await fs_write(`${d}/input.txt`, 'hello');
4
+ await shell(['ls', '-alh'], {cwd: d});
5
+ });
6
+ ```
package/src/now_atom.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Return current date in ATOM format (e.g. "2021-07-17T23:27:01.030Z")
2
+ * Return the current date in ATOM format (e.g. "2021-07-17T23:27:01.030Z")
3
3
  *
4
4
  * @returns {string}
5
5
  * @link https://www.php.net/manual/en/class.datetimeinterface.php#datetime.constants.atom
@@ -0,0 +1 @@
1
+ Return the current date in ATOM format (e.g. "2021-07-17T23:27:01.030Z")
@@ -0,0 +1,5 @@
1
+ - Run until `spawn` return `null`.
2
+ - Keep no more than `concurrency` number of workers at a time.
3
+
4
+ ⚠️ Warning: `spawn` should not be async function (async functions are always return `promise`).
5
+ Instead, it should be a simple function returning either `null` or a `promise`.
@@ -0,0 +1,3 @@
1
+ - Always closes the socket.
2
+ - Throws on any error or timeout.
3
+ - Doesn’t hang if the server sends nothing.
@@ -0,0 +1 @@
1
+ Split a stream into chunks of objects.
@@ -0,0 +1 @@
1
+ Split stream on each `\n` and call `fn` for each line.
@@ -0,0 +1 @@
1
+ Pass each item to a simple function. This is a final step.
@@ -0,0 +1 @@
1
+ Pass down only those items passed user-defined criteria.
@@ -0,0 +1 @@
1
+ Split stream into lines.
@@ -0,0 +1 @@
1
+ Transform each item using a simple function.
@@ -0,0 +1 @@
1
+ Pass each item through a user-defined generator, passes down each yielded item.
@@ -0,0 +1 @@
1
+ Transform each item using a simple function in parallel.
@@ -0,0 +1,3 @@
1
+ Parse input stream as CSV data.
2
+
3
+ - https://csv.js.org/parse/options/
@@ -7,7 +7,7 @@ const stream = require('stream');
7
7
  * Monitor the progress of data through a pipe, similar to the UNIX `pv` command.
8
8
  *
9
9
  * Requirements:
10
- * - should emit first message as fast as possible
10
+ * - should emit the first message as fast as possible
11
11
  * - should always emit 100% message
12
12
  *
13
13
  * @similar https://www.npmjs.com/package/progress-stream
@@ -0,0 +1,8 @@
1
+ Monitor the progress of data through a pipe, similar to the UNIX `pv` command.
2
+
3
+ Requirements:
4
+
5
+ - should emit the first message as fast as possible
6
+ - should always emit 100% message
7
+
8
+ - Similar: https://www.npmjs.com/package/progress-stream
@@ -0,0 +1 @@
1
+ Skip first `n` records.
@@ -0,0 +1 @@
1
+ Split the input stream by consecutive delimiters in `chars`.
@@ -0,0 +1 @@
1
+ Call `fn` on each item.
@@ -0,0 +1 @@
1
+ Consume XML, produce an object with analytics about provided xml stream.
@@ -0,0 +1 @@
1
+ Consume XML, produce objects.
@@ -0,0 +1,3 @@
1
+ Ensure that only one event was fired for each ms.
2
+
3
+ - https://stackoverflow.com/questions/25991367/difference-between-throttling-and-debouncing-a-function
package/src/urlclean.js CHANGED
@@ -2,7 +2,7 @@ const querystring = require('querystring');
2
2
 
3
3
  /**
4
4
  * Replace unnecessary encodes symbols with chars. The returned url will be
5
- * more human frieldy, at the same time it still be valid url.
5
+ * more human-friendly, at the same time it will still be valid url.
6
6
  */
7
7
  function urlclean(url)
8
8
  {
@@ -0,0 +1,2 @@
1
+ Replace unnecessary encodes symbols with chars. The returned url will be
2
+ more human-friendly, at the same time it will still be valid url.
package/src/urlmod.md ADDED
@@ -0,0 +1,7 @@
1
+ Set, change, or remove query string parameters.
2
+
3
+ ```js
4
+ urlmod('', {a: 1}) // '?a=1' set
5
+ urlmod('?a=1', {a: 2}) // '?a=2' change
6
+ urlmod('?a=1', {a: null}) // '' remove
7
+ ```
@@ -0,0 +1,4 @@
1
+ Concept. Represents a way to display a status of a currently running task to the end-user.
2
+
3
+ @see countdown
4
+ @see progress
@@ -0,0 +1,5 @@
1
+ Wait until `fn` returns `true`.
2
+
3
+ W A R N I N G
4
+ This method was designed mainly for prototyping (where code cleanliness is important).
5
+ It might drastically decrease performance.
package/src/waitcb.md ADDED
@@ -0,0 +1,12 @@
1
+ Wait for a Node-like function to finish (which will call `callback`
2
+ with 2 arguments: `error` and `value`).
3
+
4
+ ```js
5
+ await waitcb(cb => fs.writeFile('a', 'hello\n', cb));
6
+ ```
7
+
8
+ The same as:
9
+
10
+ ```js
11
+ const [promise, resolve, reject] = (a => [new Promise((...v) => a = v), ...a])();
12
+ ```