necessary 14.4.0 → 14.4.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.
- package/README.md +158 -61
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,7 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
A collection of utility functions.
|
|
4
4
|
|
|
5
|
-
This package was partly inspired by [lodash](https://lodash.com/), [async](https://caolan.github.io/async/) and the like.
|
|
5
|
+
This package was partly inspired by [lodash](https://lodash.com/), [async](https://caolan.github.io/async/) and the like.
|
|
6
|
+
The idea was to create utility functions that addressed some modest requirements and would result in a relatively small footprint.
|
|
7
|
+
That said, the bare bones implementations, especially the asynchronous functions, should provide some confidence whilst debugging.
|
|
6
8
|
|
|
7
9
|
These can only be used in the browser:
|
|
8
10
|
|
|
@@ -43,7 +45,8 @@ You can also clone the repository with [Git](https://git-scm.com/)...
|
|
|
43
45
|
|
|
44
46
|
## Usage
|
|
45
47
|
|
|
46
|
-
Each of the collections of utility functions described below is exported as a plain old JavaScript object.
|
|
48
|
+
Each of the collections of utility functions described below is exported as a plain old JavaScript object.
|
|
49
|
+
To get hold of them, import the requisite object and then destructure it:
|
|
47
50
|
|
|
48
51
|
```
|
|
49
52
|
import { arrayUtilities, asynchronousUtilities, fileSystemUtilities } from "necessary";
|
|
@@ -65,23 +68,28 @@ const { first, last } = arrayUtilities,
|
|
|
65
68
|
...
|
|
66
69
|
```
|
|
67
70
|
|
|
68
|
-
The miscellaneous functions are a special case.
|
|
71
|
+
The miscellaneous functions are a special case.
|
|
72
|
+
They can be treated as above but may well have other functions assigned to them.
|
|
73
|
+
See below.
|
|
69
74
|
|
|
70
75
|
## Ajax utilities
|
|
71
76
|
|
|
72
77
|
- `get()`
|
|
73
|
-
- `post()`
|
|
78
|
+
- `post()`
|
|
74
79
|
- `request()`
|
|
75
80
|
|
|
76
81
|
The first two `get()` and `post()` functions make use of the third `request()` function, which is more generic and can be used for arbitrary HTTP requests.
|
|
77
82
|
|
|
78
|
-
* The `get()` function sends a `GET` request, taking `host`, `uri
|
|
83
|
+
* The `get()` function sends a `GET` request, taking `host`, `uri` and `query` arguments, together with an optional `headers` argument after the `query` argument.
|
|
79
84
|
|
|
80
85
|
The `query` argument should be a plain old JavaScript object, the names and values of which are encoded and concatenated to form the query string.
|
|
81
86
|
|
|
82
|
-
The `headers` argument should also be a plain old JavaScript object.
|
|
87
|
+
The `headers` argument should also be a plain old JavaScript object.
|
|
88
|
+
If it does not have an `accept` property then one wil be provided with the value `application/json`.
|
|
83
89
|
|
|
84
|
-
The `callback` argument is expected to be a function taking `content` and `statusCode` arguments.
|
|
90
|
+
The `callback` argument is expected to be a function taking `content` and `statusCode` arguments.
|
|
91
|
+
If the `accept` property of the main `headers` argument is set to `application/json` then the function's `content` argument can be assumed to be JSON, or `null` if the request body cannot be parsed as such.
|
|
92
|
+
The `statusCode` argument will be the response status code, for example `200` for a successful `OK` response.
|
|
85
93
|
|
|
86
94
|
```
|
|
87
95
|
const host = "...",
|
|
@@ -101,7 +109,10 @@ Note that the `uri` argument must include a leading forward slash `/` since the
|
|
|
101
109
|
|
|
102
110
|
* The `post()` function behaves almost identically to the `get()` function, with the following differences.
|
|
103
111
|
|
|
104
|
-
It sends a `POST` rather than a `GET` request.
|
|
112
|
+
It sends a `POST` rather than a `GET` request.
|
|
113
|
+
There is an additional `content` argument that comes before the `callabck` argument and after the `headers` argument, which is again optional.
|
|
114
|
+
If the `headers` argument does not have a `content-type` property then one will be provided with the value of `application/json`.
|
|
115
|
+
If the `content-type` property of the `headers` argument is set to `application/json` then the `content` argument is assumed to be a plain old JavaScript object and is stringified as JSON.
|
|
105
116
|
|
|
106
117
|
```
|
|
107
118
|
const host = "...",
|
|
@@ -152,7 +163,8 @@ Note that the `headers` argument is not optional this time.
|
|
|
152
163
|
Functions for applications running on a shell such as Bash or ZSH.
|
|
153
164
|
In fact there is only one currently.
|
|
154
165
|
|
|
155
|
-
* The `prompt()` function is meant for use in shell applications.
|
|
166
|
+
* The `prompt()` function is meant for use in shell applications.
|
|
167
|
+
It takes a plain old JavaScript `options` object and a `callback` function as its first and second arguments, respectively:
|
|
156
168
|
|
|
157
169
|
```
|
|
158
170
|
const hidden = true,
|
|
@@ -173,13 +185,18 @@ prompt(options, (answer) => {
|
|
|
173
185
|
});
|
|
174
186
|
```
|
|
175
187
|
|
|
176
|
-
There are a range of properties available for the `options` object.
|
|
188
|
+
There are a range of properties available for the `options` object.
|
|
189
|
+
The `description` and `errorMessage` properties are mandatory.
|
|
190
|
+
The remaining properties are optional.
|
|
177
191
|
|
|
178
|
-
The default values of the `attempts` and `encoding` properties are `3` and `utf8`, respectively.
|
|
192
|
+
The default values of the `attempts` and `encoding` properties are `3` and `utf8`, respectively.
|
|
193
|
+
The default value of the `hidden` property is `false`. Setting it to `true` results in password-style input, that is, the characters remain hidden.
|
|
179
194
|
|
|
180
195
|
If no `validateFunction` property is given then you must set a `validatePattern` property instead, which must be a regular expression.
|
|
181
196
|
|
|
182
|
-
The `initialAnswer` property sets the initial answer at the prompt.
|
|
197
|
+
The `initialAnswer` property sets the initial answer at the prompt.
|
|
198
|
+
You might want to set it to `yes`, for example. Lastly, setting the `answer` property to anything other than `null` or `undefined` causes the `callback` function to be invoked immediately without any prompt being shown.
|
|
199
|
+
This can be useful for debugging.
|
|
183
200
|
|
|
184
201
|
## Logging utilities
|
|
185
202
|
|
|
@@ -195,7 +212,9 @@ log("...") // Results in '28-01-2018 15:44:47.363 bin/main.js(35) ...' being log
|
|
|
195
212
|
|
|
196
213
|
You can pass an error instead of a string to `log()`, in which case it will print the file path and line number of the place where the error was thrown along with the error message.
|
|
197
214
|
|
|
198
|
-
Additionally, it is possible to print to a log file if a log directory and, optionally, a base name for the log file are specified.
|
|
215
|
+
Additionally, it is possible to print to a log file if a log directory and, optionally, a base name for the log file are specified.
|
|
216
|
+
The base name here means the file name minus the extension and separator.
|
|
217
|
+
The default is `default`:
|
|
199
218
|
|
|
200
219
|
```
|
|
201
220
|
const { setLogFileBaseName, setLogDirectoryPath } = log;
|
|
@@ -218,7 +237,8 @@ log.error("...") // Printed to the console and optionally, to the log file.
|
|
|
218
237
|
log.trace("...") // Ignored, because the trace level is lower than the debug level.
|
|
219
238
|
```
|
|
220
239
|
|
|
221
|
-
There is also a `setLogOptions()` function which allows you to pass the log level, base file name and directory path as a plain old JavaScript object.
|
|
240
|
+
There is also a `setLogOptions()` function which allows you to pass the log level, base file name and directory path as a plain old JavaScript object.
|
|
241
|
+
See below for a usage example.
|
|
222
242
|
|
|
223
243
|
Finally, log files are rolled over every night. So `./log/example.log` would become `./log/example.28-01-2018.log` and a new `./log/example.log` file would be started at midnight.
|
|
224
244
|
|
|
@@ -228,11 +248,21 @@ Finally, log files are rolled over every night. So `./log/example.log` would bec
|
|
|
228
248
|
- `createGetRequest()`
|
|
229
249
|
- `createPostRequest()`
|
|
230
250
|
|
|
231
|
-
Functions that leverage Node's [HTTP](https://nodejs.org/api/http.html) nad [HTTPS](https://nodejs.org/api/https.html) inbuilt modules in order to provide HTTP request functionality.
|
|
251
|
+
Functions that leverage Node's [HTTP](https://nodejs.org/api/http.html) nad [HTTPS](https://nodejs.org/api/https.html) inbuilt modules in order to provide HTTP request functionality.
|
|
252
|
+
These functions are deliberately low level.
|
|
253
|
+
They will take away some of the pain of using the aforementioned modules but will not automatically set headers, parse responses, etc.
|
|
254
|
+
Specifically, methods have to be called on the instance of the [ClientRequest](https://nodejs.org/api/http.html#http_class_http_clientrequest) class that they each return in order to make the request and on the instance of the [IncomingMessage](https://nodejs.org/api/http.html#class-httpincomingmessage) passed to the callback function in order to parse the response.
|
|
232
255
|
|
|
233
|
-
* The `createRequest()` function provides a means to make arbitrary requests.
|
|
256
|
+
* The `createRequest()` function provides a means to make arbitrary requests.
|
|
257
|
+
* It takes `host`, `uri`, `query`, `method`, `headers` and `callback` arguments.
|
|
258
|
+
* It returns an instance of Node's ClientRequest class.
|
|
259
|
+
* The callback function must have an `error` argument, which will be `null` if the request is successful, and a `response` argument, which will be an instance of Node's IncomingMessage class.
|
|
260
|
+
* The `query` and `headers` arguments should be plain old JavaScript objects, with the former being converted into a query string.
|
|
261
|
+
* The other arguments bar the last callback argument should be strings.
|
|
234
262
|
|
|
235
|
-
In the following example a GET request is made.
|
|
263
|
+
In the following example a GET request is made.
|
|
264
|
+
Note that because the request body is empty, it is enough to call the request object's `end()` method in order to make the request.
|
|
265
|
+
Note also that the response is piped directly to a file.
|
|
236
266
|
|
|
237
267
|
```
|
|
238
268
|
const { createWriteStream } = require("fs");
|
|
@@ -257,7 +287,9 @@ const host = ...,
|
|
|
257
287
|
request.end();
|
|
258
288
|
```
|
|
259
289
|
|
|
260
|
-
In the following example the `queryStringFromQuery()` function from the HTTP utilities is used to encode the body of the request.
|
|
290
|
+
In the following example the `queryStringFromQuery()` function from the HTTP utilities is used to encode the body of the request.
|
|
291
|
+
Note that the `content-type` header is set explicitly.
|
|
292
|
+
Note that the request body is piped directly from a file:
|
|
261
293
|
|
|
262
294
|
```
|
|
263
295
|
const { createReadStream } = require("fs");
|
|
@@ -417,7 +449,8 @@ const line = "${name}, aged ${age}.",
|
|
|
417
449
|
- `renameEntry()`
|
|
418
450
|
- `removeEntry()`
|
|
419
451
|
|
|
420
|
-
An inglorious collection of functions which do no more than paper over some of Node's synchronous [native file system API](https://nodejs.org/api/fs.html) functions.
|
|
452
|
+
An inglorious collection of functions which do no more than paper over some of Node's synchronous [native file system API](https://nodejs.org/api/fs.html) functions.
|
|
453
|
+
All of the functions will throw native errors upon failure.
|
|
421
454
|
|
|
422
455
|
* The `getEntryStats()` function returns an instance of the [fs.Stats](https://nodejs.org/api/fs.html#fs_class_fs_stats) class:
|
|
423
456
|
|
|
@@ -447,25 +480,31 @@ isDirectoryEmpty("root/etc"); // returns true if the directory is empty
|
|
|
447
480
|
readDirectory("root/etc"); // returns the contents of the 'root/etc' directory
|
|
448
481
|
```
|
|
449
482
|
|
|
450
|
-
* The `readFile()` function takes the file encoding as an optional second string argument.
|
|
483
|
+
* The `readFile()` function takes the file encoding as an optional second string argument.
|
|
484
|
+
* The default is `utf8`.
|
|
485
|
+
* It returns the content of the file upon success:
|
|
451
486
|
|
|
452
487
|
```
|
|
453
488
|
readFile("root/etc/init.conf"); // returns the content of the 'root/etc/init.conf' file
|
|
454
489
|
```
|
|
455
490
|
|
|
456
|
-
* The `copyFile()` function takes as arguments the source and target file paths.
|
|
491
|
+
* The `copyFile()` function takes as arguments the source and target file paths.
|
|
492
|
+
* It will overwrite an existing file without throwing an error.
|
|
493
|
+
* It does not return anything upon success:
|
|
457
494
|
|
|
458
495
|
```
|
|
459
496
|
copyFile("root/etc/init.conf", "tmp/init.conf"); // copies the 'init.conf' file in the '/root/etc' folder to the '/tmp' folder
|
|
460
497
|
```
|
|
461
498
|
|
|
462
|
-
* The `writeFile()` function takes the content of the file as a second string argument.
|
|
499
|
+
* The `writeFile()` function takes the content of the file as a second string argument.
|
|
500
|
+
* It does not return anything upon success:
|
|
463
501
|
|
|
464
502
|
```
|
|
465
503
|
writeFile("root/etc/init.conf", ""); // writes '' to the 'root/etc/init.conf' file
|
|
466
504
|
```
|
|
467
505
|
|
|
468
|
-
* The `appendToFile()` function takes the content to append file as a second string argument.
|
|
506
|
+
* The `appendToFile()` function takes the content to append file as a second string argument.
|
|
507
|
+
* It will create teh file if necessary and does not return anything upon success:
|
|
469
508
|
|
|
470
509
|
```
|
|
471
510
|
appendToFile("root/etc/init.conf", ""); // appends '' to the 'root/etc/init.conf' file
|
|
@@ -477,7 +516,8 @@ appendToFile("root/etc/init.conf", ""); // appends '' to the 'root/etc/init.conf
|
|
|
477
516
|
createDirectory("root/etc/init"); // Creates the 'root/etc/init' directory
|
|
478
517
|
```
|
|
479
518
|
|
|
480
|
-
* The `createFile()` creates an empty file.
|
|
519
|
+
* The `createFile()` creates an empty file.
|
|
520
|
+
* It does not return anything upon success:
|
|
481
521
|
|
|
482
522
|
```
|
|
483
523
|
createFile("root/etc/init.conf"); // writes '' to the 'root/etc/init.conf' file
|
|
@@ -503,7 +543,8 @@ Note that in the case of a directory, all of its sub-entries will be removed as
|
|
|
503
543
|
renameFile("hosts", "host"); // Renames the 'hosts' file to 'host'
|
|
504
544
|
```
|
|
505
545
|
|
|
506
|
-
Note that if the parent directory of the newly named file or directory does not exist then this function will fail.
|
|
546
|
+
Note that if the parent directory of the newly named file or directory does not exist then this function will fail.
|
|
547
|
+
Instead use the `moveEntry()` function.
|
|
507
548
|
|
|
508
549
|
## Configuration utilities
|
|
509
550
|
|
|
@@ -521,7 +562,8 @@ const { logOptions } = rc;
|
|
|
521
562
|
setLogOptions(logOptions);
|
|
522
563
|
```
|
|
523
564
|
|
|
524
|
-
The default name for the file is `.rc` and it must be present in the current working directory.
|
|
565
|
+
The default name for the file is `.rc` and it must be present in the current working directory.
|
|
566
|
+
It must have the following format:
|
|
525
567
|
|
|
526
568
|
```
|
|
527
569
|
{
|
|
@@ -547,7 +589,9 @@ If neither of these checks are successful then it will return the first element
|
|
|
547
589
|
|
|
548
590
|
Note that it will not try to assign the `name` property of the chosen environment to itself, because functions already have a `name` property.
|
|
549
591
|
|
|
550
|
-
Before returning the JSON, it will search for uppercase strings.
|
|
592
|
+
Before returning the JSON, it will search for uppercase strings.
|
|
593
|
+
If such a string is the name of an environment variable, it will be replaced by the environment variable's value.
|
|
594
|
+
This means that you can choose to keep sensitive information out of the runtime configuration and therefore, for instance, safely commit it to the repository.
|
|
551
595
|
|
|
552
596
|
You can change the base extension of the file that is parsed, that is the part of the extension between the leading dot and `rc`, by making use of the `setRCBaseExtension()` function:
|
|
553
597
|
|
|
@@ -561,7 +605,10 @@ rc(); // Provides the first environment in the '.defaultrc' file
|
|
|
561
605
|
|
|
562
606
|
Note that the `rc()` function can be included in any file but only needs to be called once. But be careful that it is called before it is ever destructured.
|
|
563
607
|
|
|
564
|
-
Aside from the aforementioned `setRCBaseExtension()` functions, the `checkRCFileExists()`, `createVacuousRCFile()`, `readRCFile()` and `writeRCFile()` functions do as their names suggest.
|
|
608
|
+
Aside from the aforementioned `setRCBaseExtension()` functions, the `checkRCFileExists()`, `createVacuousRCFile()`, `readRCFile()` and `writeRCFile()` functions do as their names suggest.
|
|
609
|
+
The `updateRCFile()` function, if passed a plain old JavaScript object as the first argument, will add the properties therein, overwriting any existing properties.
|
|
610
|
+
Properties to be removed can be given as further arguments.
|
|
611
|
+
If you do not want to add as well as remove properties, set the first argument to a falsey value.
|
|
565
612
|
|
|
566
613
|
```
|
|
567
614
|
const { readRCFile, writeRCFile, updateRCFile, checkRCFileExists, createVacuousRCFile } = rc;
|
|
@@ -594,7 +641,8 @@ updateRCFile(null, "example"); // Updates the rc file, removing the 'example' p
|
|
|
594
641
|
- `pathWithoutBottommostNameFromPath()`
|
|
595
642
|
- `pathWithoutTopmostDirectoryNameFromPath()`
|
|
596
643
|
|
|
597
|
-
These functions manipulate or query strings that represent file and directory paths.
|
|
644
|
+
These functions manipulate or query strings that represent file and directory paths.
|
|
645
|
+
Note that only forward slash `/` delimiters are supported. Trailing delimiters are not needed, but tolerated.
|
|
598
646
|
|
|
599
647
|
* The `isPathName()` function returns `true` if the string argument contains no `/` delimiters apart from the first and last characters:
|
|
600
648
|
|
|
@@ -646,9 +694,11 @@ isTopmostNameInAbsolutePath("root", "/root/etc"); // returns false
|
|
|
646
694
|
isTopmostNameInAbsolutePath("etc", "/root/etc"); // returns false
|
|
647
695
|
```
|
|
648
696
|
|
|
649
|
-
Note that the function assumes that the first argument is a topmost name and that the second argument is an abolute path.
|
|
697
|
+
Note that the function assumes that the first argument is a topmost name and that the second argument is an abolute path.
|
|
698
|
+
It does not check, it simply compares the two arguments with a single regex.
|
|
650
699
|
|
|
651
|
-
* The `combinePaths()` function will combine the first string argument with the second string argument by successively removing the bottommost directory name of the former for each topmost parent directory `..` signifier it finds in the latter.
|
|
700
|
+
* The `combinePaths()` function will combine the first string argument with the second string argument by successively removing the bottommost directory name of the former for each topmost parent directory `..` signifier it finds in the latter.
|
|
701
|
+
* Current directory `.` signifiers are also removed:
|
|
652
702
|
|
|
653
703
|
```
|
|
654
704
|
combinePaths("etc/", "./init"); // returns 'etc/init'
|
|
@@ -668,7 +718,8 @@ concatenatePaths("root/", "etc/"); // returns 'root/etc/'
|
|
|
668
718
|
|
|
669
719
|
Note that the function assumes that the second argument is a relative name or path although without a leading current directory `.` or parent directory `..` signifier.
|
|
670
720
|
|
|
671
|
-
* The `bottommostNameFromPath()`, `topmostDirectoryPathFromPath()`, `topmostDirectoryNameFromPath()`, `pathWithoutBottommostNameFromPath()` and `pathWithoutTopmostDirectoryNameFromPath()` functions work as their names suggest.
|
|
721
|
+
* The `bottommostNameFromPath()`, `topmostDirectoryPathFromPath()`, `topmostDirectoryNameFromPath()`, `pathWithoutBottommostNameFromPath()` and `pathWithoutTopmostDirectoryNameFromPath()` functions work as their names suggest.
|
|
722
|
+
* Each expects there to be at least one delimiter, returning `null` otherwise:
|
|
672
723
|
|
|
673
724
|
```
|
|
674
725
|
bottommostNameFromPath("../etc"); // returns 'etc'
|
|
@@ -744,15 +795,19 @@ pathWithoutTopmostDirectoryNameFromPath("root/etc/init.conf"); // returns 'etc/i
|
|
|
744
795
|
|
|
745
796
|
Note that none of these functions take or pass on a `thisArg` argument when they might otherwise have done. Use `bind()`.
|
|
746
797
|
|
|
747
|
-
* The functions `first()` through to `last()` return the requisite element of the array argument, if passed an array of at least the required length.
|
|
798
|
+
* The functions `first()` through to `last()` return the requisite element of the array argument, if passed an array of at least the required length.
|
|
799
|
+
If the array is not long enough they return `undefined`.
|
|
748
800
|
|
|
749
|
-
* The `head()` function returns an array containing the first element of its array argument whilst the `tail()` function returns an array containing all but the first element of its array argument.
|
|
801
|
+
* The `head()` function returns an array containing the first element of its array argument whilst the `tail()` function returns an array containing all but the first element of its array argument.
|
|
802
|
+
|
|
803
|
+
* The `back()` function returns an array containing hte last element of its array argument whilst the `front()` function returns an array returning all but the last element of its array argument.
|
|
750
804
|
|
|
751
805
|
* The `push()` function is similar to its native counterpart but will push an array rather than a single element.
|
|
752
806
|
|
|
753
807
|
* The `unshift()` function is similar to its native counterpart but will unshift an array rather than a single element.
|
|
754
808
|
|
|
755
|
-
* The `concat()` function is similar to its native counterpart, however it alters the first array argument *in place*.
|
|
809
|
+
* The `concat()` function is similar to its native counterpart, however it alters the first array argument *in place*.
|
|
810
|
+
Like its native counterpart it will also take a single element as the second argument and convert it to an array.
|
|
756
811
|
|
|
757
812
|
```
|
|
758
813
|
concat([1, 2, 3], 4); // the array argument becomes [1, 2, 3, 4]
|
|
@@ -764,7 +819,8 @@ concat([1, 2, 3], 4); // the array argument becomes [1, 2, 3, 4]
|
|
|
764
819
|
clear([1, 2, 3]); // the array argument becomes []
|
|
765
820
|
```
|
|
766
821
|
|
|
767
|
-
* The `copy()` function copies the second array argument over the top of the first array argument, in other words it replaces each element of the first array argument with the corresponding element in the second array argument.
|
|
822
|
+
* The `copy()` function copies the second array argument over the top of the first array argument, in other words it replaces each element of the first array argument with the corresponding element in the second array argument.
|
|
823
|
+
If there are more elements in the second array argument that the first, the first is lengthened:
|
|
768
824
|
|
|
769
825
|
```
|
|
770
826
|
copy([1, 2, 3], [4, 5, 6, 7]); // the first array argument becomes [4, 5, 6, 7]
|
|
@@ -776,7 +832,8 @@ copy([1, 2, 3], [4, 5, 6, 7]); // the first array argument becomes [4, 5, 6, 7]
|
|
|
776
832
|
merge([1, 2, 3], [4, 5, 6, 7]); // the first array argument becomes [1, 2, 3, 4, 5, 6, 7]
|
|
777
833
|
```
|
|
778
834
|
|
|
779
|
-
* The `match()` function compares the first and second array arguments in order.
|
|
835
|
+
* The `match()` function compares the first and second array arguments in order.
|
|
836
|
+
If they are of the same length and the callback argument supplied returns a truthy value when invoked with each pair of elements then it returns true:
|
|
780
837
|
|
|
781
838
|
```
|
|
782
839
|
match([1, 2, 3], [-1, -2, -3], (valueA, valueB) => (valueA === -valueB)); // returns true
|
|
@@ -798,15 +855,21 @@ correlate([1, 2, 3], [-4, -2, -3, -1], (valueA, valueB) => (valueA === -valueB))
|
|
|
798
855
|
|
|
799
856
|
Again note that pairs of elements can match once and once only.
|
|
800
857
|
|
|
801
|
-
* The `resolve()` function repeatedly iterates over the elements of the first array argument, removing them and adding them to the second array argument if the callback function returns a truthy value.
|
|
858
|
+
* The `resolve()` function repeatedly iterates over the elements of the first array argument, removing them and adding them to the second array argument if the callback function returns a truthy value.
|
|
859
|
+
If the callback function does not return a truthy value for any of the elements of the first array argument or the length of the first array is zero, it terminates.
|
|
802
860
|
|
|
803
861
|
```
|
|
804
862
|
resolve([1, 2, 3], [], (value) => true); // moves the elemnts of the first array argument into the second array argument and returns true
|
|
805
863
|
```
|
|
806
864
|
|
|
807
|
-
The above code snippet is perhaps not very helpful so it is worth explaining this function's utility in the context of a use case.
|
|
865
|
+
The above code snippet is perhaps not very helpful so it is worth explaining this function's utility in the context of a use case.
|
|
866
|
+
Suppose a compiler has to compile all the files in a given directory.
|
|
867
|
+
There are inter-dependencies between the files, however, so some files will not compile until their dependents have compiled.
|
|
868
|
+
If there are orderings of files that allow them to all be compiled, this function will find one of them by trial and error, so to speak.
|
|
869
|
+
The second array argument will contain the elements according to this ordering.
|
|
808
870
|
|
|
809
|
-
The first array argument is left untouched whether or not the function succeeds.
|
|
871
|
+
The first array argument is left untouched whether or not the function succeeds.
|
|
872
|
+
The second array argument may contain elements if it has only been partially successful, however.
|
|
810
873
|
|
|
811
874
|
* The `find()` function is like its native counterpart, however it returns an array of all the elements for which the callback function returns a truthy value, rather than just the first:
|
|
812
875
|
|
|
@@ -820,13 +883,17 @@ find([1, 2, -1, -2], (element, index) => (element > 0)); // returns [1, 2]
|
|
|
820
883
|
replace([1, 0, -2], 3, (element, index) => (element === 0)); // the array argument becomes [1, 3, -2]
|
|
821
884
|
```
|
|
822
885
|
|
|
823
|
-
* The `splice()` function works in a similar vein to its native counterpart, however it takes an array as the optional fourth argument rather than a series of elements from the fourth argument onwards.
|
|
886
|
+
* The `splice()` function works in a similar vein to its native counterpart, however it takes an array as the optional fourth argument rather than a series of elements from the fourth argument onwards.
|
|
887
|
+
It mutates the first array argument and returns an array of the elements that have been deleted:
|
|
824
888
|
|
|
825
889
|
```
|
|
826
890
|
splice([1, 2, 3], 1, 2, [4, 5]); // the first array argument becomes [1, 4, 5]
|
|
827
891
|
```
|
|
828
892
|
|
|
829
|
-
* The `filter()` function is like its native counterpart, however it filters the first array argument *in place*.
|
|
893
|
+
* The `filter()` function is like its native counterpart, however it filters the first array argument *in place*.
|
|
894
|
+
The second argument should be a callback function that will be invoked for each element of the array.
|
|
895
|
+
If it does not return a truthy value, the corresponding element will be deleted.
|
|
896
|
+
The deleted elements are returned.
|
|
830
897
|
|
|
831
898
|
```
|
|
832
899
|
filter([1, 2, -2], (element, index) => (element > 0)); // returns [-2] and the array argument becomes [1, 2]
|
|
@@ -880,7 +947,9 @@ augment([1, 2, 3], [-1, 4, -2, 5], (element, index) => (element > 0)); // the ar
|
|
|
880
947
|
separate([1, -1, -2, 2, 3, -3], [], [], (element, index) => {(element > 0)); // the second and third array arguments become [1, 2, 3] and [-1, -2, 3], respectively.
|
|
881
948
|
```
|
|
882
949
|
|
|
883
|
-
The `forwardsXXX()` and `backwardsXXX()`functions do as their names suggest.
|
|
950
|
+
The `forwardsXXX()` and `backwardsXXX()`functions do as their names suggest.
|
|
951
|
+
The `fowardsXXX()` functions take an array for their first argument but otherwise behave identically to their native counterparts.
|
|
952
|
+
The `backwardsXXX()` functions behave similarly, only backwards.
|
|
884
953
|
|
|
885
954
|
## HTTP utilities
|
|
886
955
|
|
|
@@ -894,7 +963,9 @@ The `forwardsXXX()` and `backwardsXXX()`functions do as their names suggest. The
|
|
|
894
963
|
|
|
895
964
|
Helper functions to manipulate HTTP headers and URLs, build query strings and so on.
|
|
896
965
|
|
|
897
|
-
* The `overwrite()` function takes a plain old JavaScript object `headers` argument together with `name` and `value` string arguments.
|
|
966
|
+
* The `overwrite()` function takes a plain old JavaScript object `headers` argument together with `name` and `value` string arguments.
|
|
967
|
+
If the corresponding property of the `headers` object exists then it is replaced with the `value` value.
|
|
968
|
+
This function's utility lies in the fact that the name comparisons are case insensitive.
|
|
898
969
|
|
|
899
970
|
```
|
|
900
971
|
const headers = {
|
|
@@ -904,7 +975,9 @@ const headers = {
|
|
|
904
975
|
overwrite(headers, "content-type", "text/html"); // headers["Content-Type"] = "text/html"
|
|
905
976
|
```
|
|
906
977
|
|
|
907
|
-
* The `underwrite()` function takes a plain old JavaScript object `headers` argument together with `name` and `value` string arguments.
|
|
978
|
+
* The `underwrite()` function takes a plain old JavaScript object `headers` argument together with `name` and `value` string arguments.
|
|
979
|
+
If the corresponding property of the `headers` object does not exist then it is created with the `value` value.
|
|
980
|
+
This function's utility lies in the fact that the name comparisons are case insensitive.
|
|
908
981
|
|
|
909
982
|
```
|
|
910
983
|
const headers = {
|
|
@@ -942,7 +1015,8 @@ secureFromHost("https://site.com"); // returns true
|
|
|
942
1015
|
hostnameFromHost("http://site.com"); // returns "site.com"
|
|
943
1016
|
```
|
|
944
1017
|
|
|
945
|
-
* The `queryStringFromQuery()` function takes a plain old JavaScript object `query` argument and returns the corresponding URL encoded query string.
|
|
1018
|
+
* The `queryStringFromQuery()` function takes a plain old JavaScript object `query` argument and returns the corresponding URL encoded query string.
|
|
1019
|
+
It uses the [`encodeURIComponent`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURIComponent) to encode the names and values
|
|
946
1020
|
|
|
947
1021
|
```
|
|
948
1022
|
const query = {
|
|
@@ -952,7 +1026,8 @@ const query = {
|
|
|
952
1026
|
queryStringFromQuery(query); // returns John%20Doe
|
|
953
1027
|
```
|
|
954
1028
|
|
|
955
|
-
* The `urlFromHostURIAndQuery()` function takes `host` and `uri` string arguments together with a `query` plain old JavaScript object argument.
|
|
1029
|
+
* The `urlFromHostURIAndQuery()` function takes `host` and `uri` string arguments together with a `query` plain old JavaScript object argument.
|
|
1030
|
+
It creates a query string from the `query` object and concatenates this with the two other arguments in oder to create a fully qualified HTTP URL.
|
|
956
1031
|
|
|
957
1032
|
```
|
|
958
1033
|
const host = "https://site.com",
|
|
@@ -973,9 +1048,11 @@ Ideally the `host` argument should not include a trailing forward slash whereas
|
|
|
973
1048
|
- `indexOf()`
|
|
974
1049
|
- `substring()`
|
|
975
1050
|
|
|
976
|
-
String functions with support for Unicode.
|
|
1051
|
+
String functions with support for Unicode.
|
|
1052
|
+
Specifically, characters in Unicode astral plains are counted twice in native string functions and methods whereas these functions effectively count astral Unicode characters only once.
|
|
977
1053
|
|
|
978
|
-
* The `strlen()` function takes a single `string` argument.
|
|
1054
|
+
* The `strlen()` function takes a single `string` argument.
|
|
1055
|
+
It works in much the same way as the `length` property of the `String` prototype, however it is Unicode safe:
|
|
979
1056
|
|
|
980
1057
|
```
|
|
981
1058
|
"𝔸𝔹C".length = 5 // The 𝔹 and C characters are in an astral plane and count as two each.
|
|
@@ -984,7 +1061,10 @@ strlen("𝔸𝔹C") = 3 // The string is converted to an array whose length is 3
|
|
|
984
1061
|
|
|
985
1062
|
```
|
|
986
1063
|
|
|
987
|
-
* The `strcmp` function takes `stringA` and `stringB` arguments.
|
|
1064
|
+
* The `strcmp` function takes `stringA` and `stringB` arguments.
|
|
1065
|
+
It compares them character by character in order to find the lexicographically lesser of the two.
|
|
1066
|
+
Its return value is the difference between the code points of the first differing characters, with the code point of either string given as zero if it is empty.
|
|
1067
|
+
Some examples should clarify:
|
|
988
1068
|
|
|
989
1069
|
```
|
|
990
1070
|
strcmp("", "") = 0;
|
|
@@ -1014,7 +1094,8 @@ indexOf("𝔸b", "b"); // Returns 1.
|
|
|
1014
1094
|
|
|
1015
1095
|
In the above example the aforementioned native method would return 2.
|
|
1016
1096
|
|
|
1017
|
-
* The `substring()` function takes `string` and `start` arguments and an optional `end` argument.
|
|
1097
|
+
* The `substring()` function takes `string` and `start` arguments and an optional `end` argument.
|
|
1098
|
+
It works in much the same way as the `substring()` method of the `String` prototype, however it is Unicode safe:
|
|
1018
1099
|
|
|
1019
1100
|
```
|
|
1020
1101
|
"𝔸𝔹C".substring(3) = "C" // The 𝔹 character is in an astral plane and counts as two.
|
|
@@ -1028,7 +1109,8 @@ Note the native `substring()` method can be particularly egregious because the `
|
|
|
1028
1109
|
|
|
1029
1110
|
- `migrate()`
|
|
1030
1111
|
|
|
1031
|
-
A single `migrate()` function to handle the migration of JSON files with a required `version` entry.
|
|
1112
|
+
A single `migrate()` function to handle the migration of JSON files with a required `version` entry.
|
|
1113
|
+
This function can be used in conjunction with the configuration utilities but does not have to be.
|
|
1032
1114
|
|
|
1033
1115
|
* The `migrate` function takes `json`, `migrationMap` and `latestVersion` arguments. Perhaps the easiest way to demonstrate its use is by way of an extensive example.
|
|
1034
1116
|
|
|
@@ -1076,7 +1158,8 @@ function migrateConfigurationFile() {
|
|
|
1076
1158
|
}
|
|
1077
1159
|
```
|
|
1078
1160
|
|
|
1079
|
-
Note carefully the matching of the keys to their corresponding values. Each key matches the version that the `migrate()` function finds in the JSON.
|
|
1161
|
+
Note carefully the matching of the keys to their corresponding values. Each key matches the version that the `migrate()` function finds in the JSON.
|
|
1162
|
+
It therefore must apply the requisite migration function to migrate the JSON to the next version.
|
|
1080
1163
|
|
|
1081
1164
|
Lastly, the migration function must have the prescribed signature and return the migrated JSON. Again an example will suffice:
|
|
1082
1165
|
|
|
@@ -1094,7 +1177,8 @@ function migrateConfigurationToVersion_2_0(configuration) {
|
|
|
1094
1177
|
}
|
|
1095
1178
|
```
|
|
1096
1179
|
|
|
1097
|
-
In this admittedly somewhat trivial example, all the migration function does is to update the version number.
|
|
1180
|
+
In this admittedly somewhat trivial example, all the migration function does is to update the version number.
|
|
1181
|
+
Exactly how the JSON otherwise changes is immaterial but the version number must be updated in this way otherwise the `migrate()` function will loop indefinitely.
|
|
1098
1182
|
|
|
1099
1183
|
## Asynchronous utilities
|
|
1100
1184
|
|
|
@@ -1104,9 +1188,13 @@ In this admittedly somewhat trivial example, all the migration function does is
|
|
|
1104
1188
|
- `eventually()`
|
|
1105
1189
|
- `repeatedly()`
|
|
1106
1190
|
|
|
1107
|
-
These functions generally take either an operation or an array of operations, an operation being a function that mutates a context rather than returning a value.
|
|
1191
|
+
These functions generally take either an operation or an array of operations, an operation being a function that mutates a context rather than returning a value.
|
|
1192
|
+
They also take a `done()` function and an optional `context` argument.
|
|
1193
|
+
They all pass a `next()` function to the operations followed by the `done()` function, the `context` and then an `index` argument. Operations can call the `done()` function instead of the `next()` function in order to terminate early.
|
|
1108
1194
|
|
|
1109
|
-
* The `whilst()` function takes a single operation, which it calls each time the operation invokes the given `next()` function or until the operation invokes the given `done()` function.
|
|
1195
|
+
* The `whilst()` function takes a single operation, which it calls each time the operation invokes the given `next()` function or until the operation invokes the given `done()` function.
|
|
1196
|
+
The operation can also force termination by returning a truthy value, in which case it must *not* call the given `next()` or `done()` functions.
|
|
1197
|
+
In the example below the operation will be executed ten times:
|
|
1110
1198
|
|
|
1111
1199
|
```
|
|
1112
1200
|
const context = {}; ///
|
|
@@ -1128,7 +1216,9 @@ whilst(operation, () => {
|
|
|
1128
1216
|
}, context);
|
|
1129
1217
|
```
|
|
1130
1218
|
|
|
1131
|
-
* The `forEach()` function takes an array as the first argument followed by a single operation, which it calls for each element of the array unless the operation invokes the given `done()` function.
|
|
1219
|
+
* The `forEach()` function takes an array as the first argument followed by a single operation, which it calls for each element of the array unless the operation invokes the given `done()` function.
|
|
1220
|
+
If the `done()` function is never invoked by the operation, it is called once each of the array elements has been passed to the operation, provided the operation invokes the given `next ()` function each time.
|
|
1221
|
+
In the example below the operation will be executed four times:
|
|
1132
1222
|
|
|
1133
1223
|
```
|
|
1134
1224
|
const context = {};
|
|
@@ -1152,7 +1242,9 @@ forEach(array, operation, () => {
|
|
|
1152
1242
|
}, context);
|
|
1153
1243
|
```
|
|
1154
1244
|
|
|
1155
|
-
* The `sequence()` function takes an array of operations, which it calls in turn unless the operation invokes the given `done()` function.
|
|
1245
|
+
* The `sequence()` function takes an array of operations, which it calls in turn unless the operation invokes the given `done()` function.
|
|
1246
|
+
If the `done()` function is never invoked by a operation, it is called once each of the operations have been called, provided each operation invokes the given `next ()` function.
|
|
1247
|
+
In the example below each of the operations bar the last is executed:
|
|
1156
1248
|
|
|
1157
1249
|
```
|
|
1158
1250
|
const context = {};
|
|
@@ -1173,7 +1265,9 @@ sequence(operations, () => {
|
|
|
1173
1265
|
}, context);
|
|
1174
1266
|
```
|
|
1175
1267
|
|
|
1176
|
-
* The `eventually()` function takes an array of operations, each of which it calls immediately without waiting for the operations to invoke the given `next()` functions. When each of the operations has invoked the given `next()` function, it will call the `done()` function.
|
|
1268
|
+
* The `eventually()` function takes an array of operations, each of which it calls immediately without waiting for the operations to invoke the given `next()` functions. When each of the operations has invoked the given `next()` function, it will call the `done()` function.
|
|
1269
|
+
Note that in this case invoking the `done()` function from within a operation will not halt the execution of other operations, it is passed as an argument only for the sake of convention.
|
|
1270
|
+
In the example below each of the operations is executed:
|
|
1177
1271
|
|
|
1178
1272
|
```
|
|
1179
1273
|
const context = {};
|
|
@@ -1191,7 +1285,9 @@ eventually(operations, () => {
|
|
|
1191
1285
|
/// done
|
|
1192
1286
|
}, context);
|
|
1193
1287
|
```
|
|
1194
|
-
* The `repeatedly()` function takes a single operation and a `length` argument, immediately calling the operation a `length` number of times without waiting for it to invoke the given `next()` function each time. When the operation has invoked the given `next()` function a `length` number of times, it will call the `done()` function.
|
|
1288
|
+
* The `repeatedly()` function takes a single operation and a `length` argument, immediately calling the operation a `length` number of times without waiting for it to invoke the given `next()` function each time. When the operation has invoked the given `next()` function a `length` number of times, it will call the `done()` function.
|
|
1289
|
+
Note that in this case invoking the `done()` function from within the operation will not halt its execution the requisite number of times, it is passed as an argument only for the sake of convention.
|
|
1290
|
+
In the example below the operation is executed ten times:
|
|
1195
1291
|
|
|
1196
1292
|
```
|
|
1197
1293
|
const context = {};
|
|
@@ -1211,7 +1307,8 @@ repeatedly(operation, length, () => {
|
|
|
1211
1307
|
|
|
1212
1308
|
## Building
|
|
1213
1309
|
|
|
1214
|
-
Automation is done with [npm scripts](https://docs.npmjs.com/misc/scripts), have a look at the `package.json` file.
|
|
1310
|
+
Automation is done with [npm scripts](https://docs.npmjs.com/misc/scripts), have a look at the `package.json` file.
|
|
1311
|
+
The pertinent commands are:
|
|
1215
1312
|
|
|
1216
1313
|
npm run build-debug
|
|
1217
1314
|
npm run watch-debug
|