.
diff --git a/README.md b/README.md
index b41b374..2d39540 100644
--- a/README.md
+++ b/README.md
@@ -1,200 +1,143 @@
-# jsfs
-
-A general-purpose, deduplicating filesystem with a REST interface, jsfs is intended to provide low-level filesystem functions for Javascript applications. Additional functionality (private file indexes, token lockers, centralized authentication, etc.) are deliberately avoided here and will be implemented in a modular fashion on top of jsfs.
-
-## REQUIREMENTS
-* Node.js
-
-## CONFIGURATION
-* Clone this repository
-* Copy config.ex to config.js
-* Create the `blocks` directory (`mkdir blocks`)
-* Start the server (`node server.js`, `npm start`, foreman, pm2, etc.)
-
-If you don't like storing the data in the same directory as the code (smart), edit config.js to change the location where data (blocks) are stored and restart jsfs for the changes to take effect.
-
-Additional storage locations can be specified to allow the JSFS pool to span physical devices. In this configuration JSFS will spread the stored blocks evenly across multiple devices (inode files will be written to all devices for redundancy).
-
-It's important to note that configuring multiple storage devices does not provide redundancy to the data stored in the pool. If a storage device becomes unavaliable, and a file is requested that is composed of blocks on the missing device, the file will be corrupt. If the device is restored, or the blocks that were stored on the device are added to the remaining device, JSFS will automatically return to delivering the undamaged files.
-
-Future versions of JSFS may include an option to use multiple storage locations for the purpose of redundancy.
-
-### REMOTE STORAGE CONFIGURATION
-By default, JSFS assumes you are working with a local file system using node's `fs` module. However, JSFS currently supports remote file storage such as blob or object storage services.
-
-To use a remote storage service:
-* Copy /lib/fs/disk-operations.js to /lib/your-storage-serice/disk-operations.js
-* Update /lib/your-storage-serice/disk-operations.js as necessary (see /lib/google-cloud-storage/disk-operations.js for examples)
-* Update `config.CONFIGURED_STORAGE` to `your-storage-service`
-* Add any additional configuration as appropriate.
+# JSFS
+
+An [operating system for the web](https://jasongullickson.com/an-operating-system-for-the-web.html).
+
+## TODO (in no particular order):
+
+- [X] Add license
+- [ ] Add API to README
+- [ ] Figure out `config`
+- [ ] Add config to README
+- [X] Document `jspace` in README
+- [ ] Finish `Auth` (mainly directory handling)
+- [ ] Write `jnode`
+- [ ] Write `verbs`
+- [ ] Write example `blockdriver`
+- [ ] Write example `metadriver`
+- [ ] Re-write [jedi](https://github.com/jjg/jedi)
+- [X] Setup repo automation (tests, lint, releases, etc.)
+- [ ] Clean-up Tasks
+
+## Usage
+
+### Run tests
+```bash
+$ npm test
+```
-When JSFS boots, it will load `./lib/${config.CONFIGURED_STORAGE || "fs"}/disk-operations.js` for all disk-type operations.
+### Start the server
+```bash
+$ npm start
+```
## API
-### Keys and Tokens
-Keys are used to unlock all operations that can be performed on an object stored in JSFS, and objects can have only one key. With an `access_key`, you can execute all supported HTTP verbs (GET, PUT, DELETE) as well as generate tokens that grant varying degrees of access to the object.
-
-Tokens are more ephemeral, and any number of them can be generated to grant varying degrees of access to an object. Token generation is described later.
+> Lots TODO here, right now just some examples.
+
+### Examples
+
+#### HEAD request missing required auth info
+```bash
+$ curl -v -X HEAD -H "x-jsfs-access-key: baz" http://localhost:7302
+* Trying 127.0.0.1:7302...
+* Connected to localhost (127.0.0.1) port 7302 (#0)
+> HEAD / HTTP/1.1
+> Host: localhost:7302
+> User-Agent: curl/7.81.0
+> Accept: */*
+>
+* Mark bundle as not supporting multiuse
+< HTTP/1.1 403 Forbidden
+< Date: Tue, 31 Dec 2024 17:06:04 GMT
+< Connection: keep-alive
+< Keep-Alive: timeout=5
+* no chunk, no close, no size. Assume close to signal end
+<
+* Closing connection 0
-#### Static access keys
-If you want to limit the entire server to a fixed set of static `access_keys`, add some keys to the `STATIC_ACCESS_KEYS` array in `config.js`:
-
-```js
-STATIC_ACCESS_KEYS: ["foo", "bar"]
```
-Any write requests for new files that do not include an `access_key` in this array will return `unauthorized`, as will any write request that includes no `access_key` or `access_token`.
-
-### Parameters and Headers
-jsfs uses several parameters to control access to objects and how they are stored. These values can also be supplied as request headers by adding a leading "x-" and changing "_" to "-" (`access_token` becomes `x-access-token`). Headers are preferred to querystring parameters because they are less likely to collide but both function the same.
-
-#### private
-By default all objects stored in jsfs are public and will be accessible via any `GET` request. If the `private` parameter is set to `true` a valid `access_key` or `access_token` must be supplied to access the object.
-
-#### access_key
-Specifying a valid access_key authorizes the use of all supported HTTP verbs and is required for requests to change the `access_key` or generate `access_token`s. When a new object is stored, jsfs will generate an `access_key` automatically if one is not specified and return the generated key in the response to a `POST` request.
-
-An `access_key` can be changed by supplying the current `access_key` along with the `replacement_access_key` parameter. This will cause any existing `access_token`s to become invalid.
-
-#### access_token
-An `access_token` must be provided to execute any request on a `private` object, and is required for `PUT` and `DELETE` if an `access_key` is not supplied.
+#### HEAD request with valid auth info
+```bash
+$ curl -v -X HEAD -H "x-jsfs-access-key: baz" http://localhost:7302
+* Trying 127.0.0.1:7302...
+* Connected to localhost (127.0.0.1) port 7302 (#0)
+> HEAD / HTTP/1.1
+> Host: localhost:7302
+> User-Agent: curl/7.81.0
+> Accept: */*
+> x-jsfs-access-key: baz
+>
+* Mark bundle as not supporting multiuse
+< HTTP/1.1 200 OK
+< x-jsfs-version: 0
+< Date: Tue, 31 Dec 2024 17:05:21 GMT
+< Connection: keep-alive
+< Keep-Alive: timeout=5
+* no chunk, no close, no size. Assume close to signal end
+<
+* Closing connection 0
+```
-##### Generating access_tokens
-Currently there are two types of `access_token`: durable and temporary. Both are generated by creating a string that describes what access is granted and then using SHA1 to generate a hash of this string, but the format and use is a little different.
+## Features
-Durable `access_token`s are generated by concatinating an object's `access_key` with the HTTP verb that the token will be used for.
+### jspace
+Internally, JSFS uses a URI format called `jspace`. This provides two neat features:
-Example to grant GET access:
+* By pointing a `A`, `AAA` records (or just your hostfile) at a JSFS server, any requests made using that record will automatically be "namespaced"
+* These namespaces can be accessed *without DNS or other name resolution*
- "077785b5e45418cf6caabdd686719813fb22e3ce" + "GET"
+For example, I can point a DNS record for `jsfs.jasongullickson.com` as well as a DNS record for `files.mystuff.com` at the same JSFS server, and if I `POST` a file to `http://jsfs.jasongullickson.com/about.html`, `http://files.mystuff.com/about.html` will be untouched.
-This string is then hashed with SHA1 and can be used to perform a `GET` request for the object whose `access_key` was used to generate the token.
+If I want to access each of these files without relying on DNS or other name resolution, I can provide the `jspace` path via a request to *any name or address that points at the JSFS server*. For example:
-To make a temporary token for this same object, concatinate the `access_key` with the HTTP verb and the expiration time in epoc format (milliseconds since midnight, 01/01/1970):
+``` bash
+$ curl http://10.1.10.1/.com.jasongullickson.jsfs/about.html
+```
- "077785b5e45418cf6caabdd686719813fb22e3ce" + "GET" + "1424877559581"
+Will return the same file as `http://jsfs.jasongullickson.com/about.html` whereas:
-This string is then hashed with SHA1 and supplied as a parameter or header with the request, along with an additional parameter named `expires` which is set to match the expiration time used above. When the jsfs server receives the request, it generates the same token based on the stored `access_key`, the HTTP method of the incoming request and the supplied `expires` parameter to validate the `access_token`.
+```bash
+$ curl http://10.1.10.1/.com.mystuff.files/about.html
+```
-*NOTE: all `access_tokens` can be immediately invalidated by changing an objects `access_key`, however if individual `access_tokens` need to be invalidated a pattern of requesting new, temporary tokens before each request is recommended.*
+Will return the same file as `http://files.mystuff.com/about.html`.
-### POST
-Stores a new object at the specified URL. If the object exists and the `access_key` is not provided, jsfs returns `405 method not allowed`.
+Aside from removing the overhead of normal name resolution, applications using `jspace` paths makes them immune to DNS hacks or DDOS attacks on DNS servers.
-#### EXAMPLE
-Request:
- curl -X POST --data-binary @Brinstar.mp3 "http://localhost:7302/music/Brinstar.mp3"
+### Keys and Tokens
+Two parameters can be used to gain access to files in JSFS: `access-key` and `access-token`. A valid `access-key` can be used for any operation (`GET`,`POST`, etc.) forever as long as it matches the `access-key` of the file (or in the case of new files, the nearest directory).
-Response:
-````
-{
- "url": "/localhost/music/Brinstar.mp3",
- "created": 1424878242595,
- "version": 0,
- "private": false,
- "encrypted": false,
- "fingerprint": "fde752ca6541c16ec626a3cf6e45e835cfd9db9b",
- "access_key": "fde752ca6541c16ec626a3cf6e45e835cfd9db9b",
- "content_type": "application/x-www-form-urlencoded",
- "file_size": 7678080,
- "block_size": 1048576,
- "blocks": [
- {
- "block_hash": "610f0b4c20a47b4162edc224db602a040cc9d243",
- "last_seen": "./blocks/"
- },
- {
- "block_hash": "60a93a7c97fd94bb730516333f1469d101ae9d44",
- "last_seen": "./blocks/"
- },
- {
- "block_hash": "62774a105ffc5f57dcf14d44afcc8880ee2fff8c",
- "last_seen": "./blocks/"
- },
- {
- "block_hash": "14c9c748e3c67d8ec52cfc2e071bbe3126cd303a",
- "last_seen": "./blocks/"
- },
- {
- "block_hash": "8697c9ba80ef824de9b0e35ad6996edaa6cc50df",
- "last_seen": "./blocks/"
- },
- {
- "block_hash": "866581c2a452160748b84dcd33a2e56290f1b585",
- "last_seen": "./blocks/"
- },
- {
- "block_hash": "6c1527902e873054b36adf46278e9938e642721c",
- "last_seen": "./blocks/"
- },
- {
- "block_hash": "10938182cd5e714dacb893d6127f8ca89359fec7",
- "last_seen": "./blocks/"
- }
- ]
-}
-````
+For more precise control, or to provide access for a limited amount of time, an `access-token` can be created. `access-token`s are method-specific and can also be made to expire.
-JSFS automatically "namespaces" URL's based on the host name used to make the request. This makes it easy to create partitioned storage by pointing multiple hostnames at the same JSFS server. This is accomplished by expanding the host in reverse notation (i.e.: `foo.com` becomes `.com.foo`); this is handled transparrently by JSFS from the client's perspective.
+To generate an `access-token`, hash the `access-key` + method using SHA1:
+```js
+import crypto from 'node:crypto';
-This means that you can point DNS records for `foo.com` and `bar.com` to the same JSFS server and then POST `http://foo.com:7302/files/baz.txt` and `http://bar.com:7302/files/baz.txt` without a conflict.
+const key = '077785b5e45418cf6caabdd686719813fb22e3ce';
+const method = 'GET';
+const token = crypto.hash('sha1', `${key}${method}`);
-This also means that `GET http://foo.com:7302/files/baz.txt` and `GET http://bar.com:7302/files/baz.txt` do not return the same file, however if you need to access a file stored via a different host you can reach it using its absolute address (in this case, `http://bar.com:7302/.com.foo/files/baz.txt`).
+console.log(token);
+```
-### GET
-Retreives the object stored at the specified URL. If the file does not exist a `404 not found` is returned.
+The output is then passed as `access-token` instead of `access-key`.
-#### EXAMPLE
+To create a temporary token, include a the expiration datetime in [Unix time](https://en.wikipedia.org/wiki/Unix_time) format:
+```js
+import crypto from 'node:crypto';
-Request:
+// This a token expires 6 hours from now.
+let d = new Date();
+d.setTime(d.getTime() + 6 * 60*60*1000);
- curl -o Brinstar.mp3 http://localhost:730s/music/Brinstar.mp3
+const key = 'foo';
+const method = 'GET';
+const expires = `${Math.floor(d.getTime() / 1000)}`;
+const hash = crypto.hash('sha1', `${key}${method}${expires}`);
+const token = `${hash}${expires}`;
-Response:
-The binary file is stored in new local file called `Brinstar.mp3`.
+console.log(token);
+```
-### PUT
-Updates an existing object stored at the specified location. This method requires authorization, so requests must include a valid `x-access-key` or `x-access-token` header for the specific file, otherwise `401 unauthorized` will be returned. If the file does not exist `405 method not allowed` is returned.
-
-#### EXAMPLE
-Request:
-
- curl -X PUT -H "x-access-key: 7092bee1ac7d4a5c55cb5ff61043b89a6e32cf71" --data-binary @Brinstar.mp3 "http://localhost:7302/music/Brinstar.mp3"
-
-Result:
-`HTTP 206`
-
-*note: `POST` and `PUT` can actualy be used interchangably, but HTTP conventions recommend using them as described here.*
-
-### DELETE
-Removes the file at the specified URL. This method requires authorization so requests must include a valid `x-access-key` or `x-access-token` header for the specified file. If the token is not supplied or is invalid `401 unauthorized` is returend. If the file does not exist `405 method not allowed` is returned.
-
-#### Example
-Request:
-
- curl -X DELETE -H "x-access-token: 7092bee1ac7d4a5c55cb5ff61043b89a6e32cf71" "http://localhost:7302/music/Brinstar.mp3"
-
-Response
-`HTTP 206` if sucessful.
-
-### HEAD
-Returns status and header information for the specified URL.
-
-#### Example
-Request:
-
- curl -I "http://localhost:7302/music/Brinstar.mp3"
-
-Response:
-````
-HTTP/1.1 200 OK
-Access-Control-Allow-Methods: GET,POST,PUT,DELETE,OPTIONS
-Access-Control-Allow-Headers: Accept,Accept-Version,Content-Type,Api-Version,Origin,X-Requested-With,Range,X_FILENAME,X-Access-Key,X-Replacement-Access-Key,X-Access-Token,X-Encrypted,X-Private
-Access-Control-Allow-Origin: *
-Content-Type: application/x-www-form-urlencoded
-Content-Length: 7678080
-Date: Wed, 25 Feb 2015 15:43:03 GMT
-Connection: keep-alive
-````
diff --git a/.travis.yml b/archive/.travis.yml
similarity index 100%
rename from .travis.yml
rename to archive/.travis.yml
diff --git a/archive/README.md b/archive/README.md
new file mode 100644
index 0000000..b41b374
--- /dev/null
+++ b/archive/README.md
@@ -0,0 +1,200 @@
+# jsfs
+
+A general-purpose, deduplicating filesystem with a REST interface, jsfs is intended to provide low-level filesystem functions for Javascript applications. Additional functionality (private file indexes, token lockers, centralized authentication, etc.) are deliberately avoided here and will be implemented in a modular fashion on top of jsfs.
+
+## REQUIREMENTS
+* Node.js
+
+## CONFIGURATION
+* Clone this repository
+* Copy config.ex to config.js
+* Create the `blocks` directory (`mkdir blocks`)
+* Start the server (`node server.js`, `npm start`, foreman, pm2, etc.)
+
+If you don't like storing the data in the same directory as the code (smart), edit config.js to change the location where data (blocks) are stored and restart jsfs for the changes to take effect.
+
+Additional storage locations can be specified to allow the JSFS pool to span physical devices. In this configuration JSFS will spread the stored blocks evenly across multiple devices (inode files will be written to all devices for redundancy).
+
+It's important to note that configuring multiple storage devices does not provide redundancy to the data stored in the pool. If a storage device becomes unavaliable, and a file is requested that is composed of blocks on the missing device, the file will be corrupt. If the device is restored, or the blocks that were stored on the device are added to the remaining device, JSFS will automatically return to delivering the undamaged files.
+
+Future versions of JSFS may include an option to use multiple storage locations for the purpose of redundancy.
+
+### REMOTE STORAGE CONFIGURATION
+By default, JSFS assumes you are working with a local file system using node's `fs` module. However, JSFS currently supports remote file storage such as blob or object storage services.
+
+To use a remote storage service:
+* Copy /lib/fs/disk-operations.js to /lib/your-storage-serice/disk-operations.js
+* Update /lib/your-storage-serice/disk-operations.js as necessary (see /lib/google-cloud-storage/disk-operations.js for examples)
+* Update `config.CONFIGURED_STORAGE` to `your-storage-service`
+* Add any additional configuration as appropriate.
+
+When JSFS boots, it will load `./lib/${config.CONFIGURED_STORAGE || "fs"}/disk-operations.js` for all disk-type operations.
+
+## API
+
+### Keys and Tokens
+Keys are used to unlock all operations that can be performed on an object stored in JSFS, and objects can have only one key. With an `access_key`, you can execute all supported HTTP verbs (GET, PUT, DELETE) as well as generate tokens that grant varying degrees of access to the object.
+
+Tokens are more ephemeral, and any number of them can be generated to grant varying degrees of access to an object. Token generation is described later.
+
+#### Static access keys
+If you want to limit the entire server to a fixed set of static `access_keys`, add some keys to the `STATIC_ACCESS_KEYS` array in `config.js`:
+
+```js
+STATIC_ACCESS_KEYS: ["foo", "bar"]
+```
+
+Any write requests for new files that do not include an `access_key` in this array will return `unauthorized`, as will any write request that includes no `access_key` or `access_token`.
+
+### Parameters and Headers
+jsfs uses several parameters to control access to objects and how they are stored. These values can also be supplied as request headers by adding a leading "x-" and changing "_" to "-" (`access_token` becomes `x-access-token`). Headers are preferred to querystring parameters because they are less likely to collide but both function the same.
+
+#### private
+By default all objects stored in jsfs are public and will be accessible via any `GET` request. If the `private` parameter is set to `true` a valid `access_key` or `access_token` must be supplied to access the object.
+
+#### access_key
+Specifying a valid access_key authorizes the use of all supported HTTP verbs and is required for requests to change the `access_key` or generate `access_token`s. When a new object is stored, jsfs will generate an `access_key` automatically if one is not specified and return the generated key in the response to a `POST` request.
+
+An `access_key` can be changed by supplying the current `access_key` along with the `replacement_access_key` parameter. This will cause any existing `access_token`s to become invalid.
+
+#### access_token
+An `access_token` must be provided to execute any request on a `private` object, and is required for `PUT` and `DELETE` if an `access_key` is not supplied.
+
+##### Generating access_tokens
+Currently there are two types of `access_token`: durable and temporary. Both are generated by creating a string that describes what access is granted and then using SHA1 to generate a hash of this string, but the format and use is a little different.
+
+Durable `access_token`s are generated by concatinating an object's `access_key` with the HTTP verb that the token will be used for.
+
+Example to grant GET access:
+
+ "077785b5e45418cf6caabdd686719813fb22e3ce" + "GET"
+
+This string is then hashed with SHA1 and can be used to perform a `GET` request for the object whose `access_key` was used to generate the token.
+
+To make a temporary token for this same object, concatinate the `access_key` with the HTTP verb and the expiration time in epoc format (milliseconds since midnight, 01/01/1970):
+
+ "077785b5e45418cf6caabdd686719813fb22e3ce" + "GET" + "1424877559581"
+
+This string is then hashed with SHA1 and supplied as a parameter or header with the request, along with an additional parameter named `expires` which is set to match the expiration time used above. When the jsfs server receives the request, it generates the same token based on the stored `access_key`, the HTTP method of the incoming request and the supplied `expires` parameter to validate the `access_token`.
+
+*NOTE: all `access_tokens` can be immediately invalidated by changing an objects `access_key`, however if individual `access_tokens` need to be invalidated a pattern of requesting new, temporary tokens before each request is recommended.*
+
+### POST
+Stores a new object at the specified URL. If the object exists and the `access_key` is not provided, jsfs returns `405 method not allowed`.
+
+#### EXAMPLE
+Request:
+
+ curl -X POST --data-binary @Brinstar.mp3 "http://localhost:7302/music/Brinstar.mp3"
+
+Response:
+````
+{
+ "url": "/localhost/music/Brinstar.mp3",
+ "created": 1424878242595,
+ "version": 0,
+ "private": false,
+ "encrypted": false,
+ "fingerprint": "fde752ca6541c16ec626a3cf6e45e835cfd9db9b",
+ "access_key": "fde752ca6541c16ec626a3cf6e45e835cfd9db9b",
+ "content_type": "application/x-www-form-urlencoded",
+ "file_size": 7678080,
+ "block_size": 1048576,
+ "blocks": [
+ {
+ "block_hash": "610f0b4c20a47b4162edc224db602a040cc9d243",
+ "last_seen": "./blocks/"
+ },
+ {
+ "block_hash": "60a93a7c97fd94bb730516333f1469d101ae9d44",
+ "last_seen": "./blocks/"
+ },
+ {
+ "block_hash": "62774a105ffc5f57dcf14d44afcc8880ee2fff8c",
+ "last_seen": "./blocks/"
+ },
+ {
+ "block_hash": "14c9c748e3c67d8ec52cfc2e071bbe3126cd303a",
+ "last_seen": "./blocks/"
+ },
+ {
+ "block_hash": "8697c9ba80ef824de9b0e35ad6996edaa6cc50df",
+ "last_seen": "./blocks/"
+ },
+ {
+ "block_hash": "866581c2a452160748b84dcd33a2e56290f1b585",
+ "last_seen": "./blocks/"
+ },
+ {
+ "block_hash": "6c1527902e873054b36adf46278e9938e642721c",
+ "last_seen": "./blocks/"
+ },
+ {
+ "block_hash": "10938182cd5e714dacb893d6127f8ca89359fec7",
+ "last_seen": "./blocks/"
+ }
+ ]
+}
+````
+
+JSFS automatically "namespaces" URL's based on the host name used to make the request. This makes it easy to create partitioned storage by pointing multiple hostnames at the same JSFS server. This is accomplished by expanding the host in reverse notation (i.e.: `foo.com` becomes `.com.foo`); this is handled transparrently by JSFS from the client's perspective.
+
+This means that you can point DNS records for `foo.com` and `bar.com` to the same JSFS server and then POST `http://foo.com:7302/files/baz.txt` and `http://bar.com:7302/files/baz.txt` without a conflict.
+
+This also means that `GET http://foo.com:7302/files/baz.txt` and `GET http://bar.com:7302/files/baz.txt` do not return the same file, however if you need to access a file stored via a different host you can reach it using its absolute address (in this case, `http://bar.com:7302/.com.foo/files/baz.txt`).
+
+### GET
+Retreives the object stored at the specified URL. If the file does not exist a `404 not found` is returned.
+
+#### EXAMPLE
+
+Request:
+
+ curl -o Brinstar.mp3 http://localhost:730s/music/Brinstar.mp3
+
+Response:
+The binary file is stored in new local file called `Brinstar.mp3`.
+
+### PUT
+Updates an existing object stored at the specified location. This method requires authorization, so requests must include a valid `x-access-key` or `x-access-token` header for the specific file, otherwise `401 unauthorized` will be returned. If the file does not exist `405 method not allowed` is returned.
+
+#### EXAMPLE
+Request:
+
+ curl -X PUT -H "x-access-key: 7092bee1ac7d4a5c55cb5ff61043b89a6e32cf71" --data-binary @Brinstar.mp3 "http://localhost:7302/music/Brinstar.mp3"
+
+Result:
+`HTTP 206`
+
+*note: `POST` and `PUT` can actualy be used interchangably, but HTTP conventions recommend using them as described here.*
+
+### DELETE
+Removes the file at the specified URL. This method requires authorization so requests must include a valid `x-access-key` or `x-access-token` header for the specified file. If the token is not supplied or is invalid `401 unauthorized` is returend. If the file does not exist `405 method not allowed` is returned.
+
+#### Example
+Request:
+
+ curl -X DELETE -H "x-access-token: 7092bee1ac7d4a5c55cb5ff61043b89a6e32cf71" "http://localhost:7302/music/Brinstar.mp3"
+
+Response
+`HTTP 206` if sucessful.
+
+### HEAD
+Returns status and header information for the specified URL.
+
+#### Example
+Request:
+
+ curl -I "http://localhost:7302/music/Brinstar.mp3"
+
+Response:
+````
+HTTP/1.1 200 OK
+Access-Control-Allow-Methods: GET,POST,PUT,DELETE,OPTIONS
+Access-Control-Allow-Headers: Accept,Accept-Version,Content-Type,Api-Version,Origin,X-Requested-With,Range,X_FILENAME,X-Access-Key,X-Replacement-Access-Key,X-Access-Token,X-Encrypted,X-Private
+Access-Control-Allow-Origin: *
+Content-Type: application/x-www-form-urlencoded
+Content-Length: 7678080
+Date: Wed, 25 Feb 2015 15:43:03 GMT
+Connection: keep-alive
+````
diff --git a/README_PROTOCOL.md b/archive/README_PROTOCOL.md
similarity index 100%
rename from README_PROTOCOL.md
rename to archive/README_PROTOCOL.md
diff --git a/boot.js b/archive/boot.js
similarity index 100%
rename from boot.js
rename to archive/boot.js
diff --git a/config.ex b/archive/config.ex
similarity index 100%
rename from config.ex
rename to archive/config.ex
diff --git a/config.json b/archive/config.json
similarity index 100%
rename from config.json
rename to archive/config.json
diff --git a/docs/roadmap.md b/archive/docs/roadmap.md
similarity index 100%
rename from docs/roadmap.md
rename to archive/docs/roadmap.md
diff --git a/docs/unikernel.md b/archive/docs/unikernel.md
similarity index 100%
rename from docs/unikernel.md
rename to archive/docs/unikernel.md
diff --git a/jlog-nomem.js b/archive/jlog-nomem.js
similarity index 100%
rename from jlog-nomem.js
rename to archive/jlog-nomem.js
diff --git a/jsfs.service b/archive/jsfs.service
similarity index 100%
rename from jsfs.service
rename to archive/jsfs.service
diff --git a/lib/constants.js b/archive/lib/constants.js
similarity index 100%
rename from lib/constants.js
rename to archive/lib/constants.js
diff --git a/lib/file-types.js b/archive/lib/file-types.js
similarity index 100%
rename from lib/file-types.js
rename to archive/lib/file-types.js
diff --git a/lib/fs/disk-operations.js b/archive/lib/fs/disk-operations.js
similarity index 100%
rename from lib/fs/disk-operations.js
rename to archive/lib/fs/disk-operations.js
diff --git a/lib/google-cloud-storage/disk-operations.js b/archive/lib/google-cloud-storage/disk-operations.js
similarity index 100%
rename from lib/google-cloud-storage/disk-operations.js
rename to archive/lib/google-cloud-storage/disk-operations.js
diff --git a/lib/inode.js b/archive/lib/inode.js
similarity index 100%
rename from lib/inode.js
rename to archive/lib/inode.js
diff --git a/lib/utils.js b/archive/lib/utils.js
similarity index 100%
rename from lib/utils.js
rename to archive/lib/utils.js
diff --git a/lib/validate.js b/archive/lib/validate.js
similarity index 100%
rename from lib/validate.js
rename to archive/lib/validate.js
diff --git a/license.md b/archive/license.md
similarity index 100%
rename from license.md
rename to archive/license.md
diff --git a/old-jlog.js b/archive/old-jlog.js
similarity index 100%
rename from old-jlog.js
rename to archive/old-jlog.js
diff --git a/archive/package-lock.json b/archive/package-lock.json
new file mode 100644
index 0000000..62ecb83
--- /dev/null
+++ b/archive/package-lock.json
@@ -0,0 +1,1699 @@
+{
+ "name": "jsfs",
+ "version": "4.2.0",
+ "lockfileVersion": 2,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "jsfs",
+ "version": "4.2.0",
+ "license": "GPL-3.0",
+ "dependencies": {
+ "through": "2.3.8"
+ },
+ "devDependencies": {
+ "chai": "^3.5.0",
+ "mocha": "^9.1.3",
+ "mock-fs": "^4.0.0-beta.1"
+ }
+ },
+ "node_modules/@ungap/promise-all-settled": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz",
+ "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==",
+ "dev": true
+ },
+ "node_modules/ansi-colors": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz",
+ "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/ansi-regex": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/anymatch": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz",
+ "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==",
+ "dev": true,
+ "dependencies": {
+ "normalize-path": "^3.0.0",
+ "picomatch": "^2.0.4"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/argparse": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
+ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
+ "dev": true
+ },
+ "node_modules/assertion-error": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz",
+ "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==",
+ "dev": true,
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/balanced-match": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
+ "dev": true
+ },
+ "node_modules/binary-extensions": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
+ "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/brace-expansion": {
+ "version": "1.1.11",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+ "dev": true,
+ "dependencies": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "node_modules/braces": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
+ "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
+ "dev": true,
+ "dependencies": {
+ "fill-range": "^7.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/browser-stdout": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz",
+ "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==",
+ "dev": true
+ },
+ "node_modules/camelcase": {
+ "version": "6.2.1",
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.1.tgz",
+ "integrity": "sha512-tVI4q5jjFV5CavAU8DXfza/TJcZutVKo/5Foskmsqcm0MsL91moHvwiGNnqaa2o6PF/7yT5ikDRcVcl8Rj6LCA==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/chai": {
+ "version": "3.5.0",
+ "resolved": "https://registry.npmjs.org/chai/-/chai-3.5.0.tgz",
+ "integrity": "sha1-TQJjewZ/6Vi9v906QOxW/vc3Mkc=",
+ "dev": true,
+ "dependencies": {
+ "assertion-error": "^1.0.1",
+ "deep-eql": "^0.1.3",
+ "type-detect": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4.0"
+ }
+ },
+ "node_modules/chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dev": true,
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/chalk/node_modules/supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "dev": true,
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/chokidar": {
+ "version": "3.5.2",
+ "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz",
+ "integrity": "sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==",
+ "dev": true,
+ "dependencies": {
+ "anymatch": "~3.1.2",
+ "braces": "~3.0.2",
+ "glob-parent": "~5.1.2",
+ "is-binary-path": "~2.1.0",
+ "is-glob": "~4.0.1",
+ "normalize-path": "~3.0.0",
+ "readdirp": "~3.6.0"
+ },
+ "engines": {
+ "node": ">= 8.10.0"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.2"
+ }
+ },
+ "node_modules/cliui": {
+ "version": "7.0.4",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz",
+ "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==",
+ "dev": true,
+ "dependencies": {
+ "string-width": "^4.2.0",
+ "strip-ansi": "^6.0.0",
+ "wrap-ansi": "^7.0.0"
+ }
+ },
+ "node_modules/color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true
+ },
+ "node_modules/concat-map": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+ "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
+ "dev": true
+ },
+ "node_modules/debug": {
+ "version": "4.3.2",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz",
+ "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==",
+ "dev": true,
+ "dependencies": {
+ "ms": "2.1.2"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/debug/node_modules/ms": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
+ "dev": true
+ },
+ "node_modules/decamelize": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz",
+ "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/deep-eql": {
+ "version": "0.1.3",
+ "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-0.1.3.tgz",
+ "integrity": "sha1-71WKyrjeJSBs1xOQbXTlaTDrafI=",
+ "dev": true,
+ "dependencies": {
+ "type-detect": "0.1.1"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/deep-eql/node_modules/type-detect": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-0.1.1.tgz",
+ "integrity": "sha1-C6XsKohWQORw6k6FBZcZANrFiCI=",
+ "dev": true,
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/diff": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz",
+ "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.3.1"
+ }
+ },
+ "node_modules/emoji-regex": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+ "dev": true
+ },
+ "node_modules/escalade": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
+ "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/escape-string-regexp": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
+ "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/fill-range": {
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
+ "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
+ "dev": true,
+ "dependencies": {
+ "to-regex-range": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/find-up": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
+ "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
+ "dev": true,
+ "dependencies": {
+ "locate-path": "^6.0.0",
+ "path-exists": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/flat": {
+ "version": "5.0.2",
+ "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz",
+ "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==",
+ "dev": true,
+ "bin": {
+ "flat": "cli.js"
+ }
+ },
+ "node_modules/fs.realpath": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+ "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
+ "dev": true
+ },
+ "node_modules/fsevents": {
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
+ "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
+ "dev": true,
+ "hasInstallScript": true,
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+ }
+ },
+ "node_modules/get-caller-file": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
+ "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
+ "dev": true,
+ "engines": {
+ "node": "6.* || 8.* || >= 10.*"
+ }
+ },
+ "node_modules/glob": {
+ "version": "7.1.7",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz",
+ "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==",
+ "dev": true,
+ "dependencies": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.0.4",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ },
+ "engines": {
+ "node": "*"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/glob-parent": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
+ "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+ "dev": true,
+ "dependencies": {
+ "is-glob": "^4.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/growl": {
+ "version": "1.10.5",
+ "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz",
+ "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==",
+ "dev": true,
+ "engines": {
+ "node": ">=4.x"
+ }
+ },
+ "node_modules/has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/he": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
+ "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==",
+ "dev": true,
+ "bin": {
+ "he": "bin/he"
+ }
+ },
+ "node_modules/inflight": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+ "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
+ "dev": true,
+ "dependencies": {
+ "once": "^1.3.0",
+ "wrappy": "1"
+ }
+ },
+ "node_modules/inherits": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
+ "dev": true
+ },
+ "node_modules/is-binary-path": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
+ "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
+ "dev": true,
+ "dependencies": {
+ "binary-extensions": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/is-extglob": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+ "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-fullwidth-code-point": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+ "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/is-glob": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
+ "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
+ "dev": true,
+ "dependencies": {
+ "is-extglob": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-number": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
+ "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.12.0"
+ }
+ },
+ "node_modules/is-plain-obj": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz",
+ "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/is-unicode-supported": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz",
+ "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/isexe": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+ "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=",
+ "dev": true
+ },
+ "node_modules/js-yaml": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
+ "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
+ "dev": true,
+ "dependencies": {
+ "argparse": "^2.0.1"
+ },
+ "bin": {
+ "js-yaml": "bin/js-yaml.js"
+ }
+ },
+ "node_modules/locate-path": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
+ "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
+ "dev": true,
+ "dependencies": {
+ "p-locate": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/log-symbols": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz",
+ "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==",
+ "dev": true,
+ "dependencies": {
+ "chalk": "^4.1.0",
+ "is-unicode-supported": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/minimatch": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
+ "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
+ "dev": true,
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/mocha": {
+ "version": "9.1.3",
+ "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.1.3.tgz",
+ "integrity": "sha512-Xcpl9FqXOAYqI3j79pEtHBBnQgVXIhpULjGQa7DVb0Po+VzmSIK9kanAiWLHoRR/dbZ2qpdPshuXr8l1VaHCzw==",
+ "dev": true,
+ "dependencies": {
+ "@ungap/promise-all-settled": "1.1.2",
+ "ansi-colors": "4.1.1",
+ "browser-stdout": "1.3.1",
+ "chokidar": "3.5.2",
+ "debug": "4.3.2",
+ "diff": "5.0.0",
+ "escape-string-regexp": "4.0.0",
+ "find-up": "5.0.0",
+ "glob": "7.1.7",
+ "growl": "1.10.5",
+ "he": "1.2.0",
+ "js-yaml": "4.1.0",
+ "log-symbols": "4.1.0",
+ "minimatch": "3.0.4",
+ "ms": "2.1.3",
+ "nanoid": "3.1.25",
+ "serialize-javascript": "6.0.0",
+ "strip-json-comments": "3.1.1",
+ "supports-color": "8.1.1",
+ "which": "2.0.2",
+ "workerpool": "6.1.5",
+ "yargs": "16.2.0",
+ "yargs-parser": "20.2.4",
+ "yargs-unparser": "2.0.0"
+ },
+ "bin": {
+ "_mocha": "bin/_mocha",
+ "mocha": "bin/mocha"
+ },
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/mochajs"
+ }
+ },
+ "node_modules/mock-fs": {
+ "version": "4.14.0",
+ "resolved": "https://registry.npmjs.org/mock-fs/-/mock-fs-4.14.0.tgz",
+ "integrity": "sha512-qYvlv/exQ4+svI3UOvPUpLDF0OMX5euvUH0Ny4N5QyRyhNdgAgUrVH3iUINSzEPLvx0kbo/Bp28GJKIqvE7URw==",
+ "dev": true
+ },
+ "node_modules/ms": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+ "dev": true
+ },
+ "node_modules/nanoid": {
+ "version": "3.1.25",
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.25.tgz",
+ "integrity": "sha512-rdwtIXaXCLFAQbnfqDRnI6jaRHp9fTcYBjtFKE8eezcZ7LuLjhUaQGNeMXf1HmRoCH32CLz6XwX0TtxEOS/A3Q==",
+ "dev": true,
+ "bin": {
+ "nanoid": "bin/nanoid.cjs"
+ },
+ "engines": {
+ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
+ }
+ },
+ "node_modules/normalize-path": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
+ "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/once": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+ "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
+ "dev": true,
+ "dependencies": {
+ "wrappy": "1"
+ }
+ },
+ "node_modules/p-limit": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
+ "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
+ "dev": true,
+ "dependencies": {
+ "yocto-queue": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/p-locate": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
+ "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
+ "dev": true,
+ "dependencies": {
+ "p-limit": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/path-exists": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
+ "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/path-is-absolute": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+ "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/picomatch": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz",
+ "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==",
+ "dev": true,
+ "engines": {
+ "node": ">=8.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/randombytes": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
+ "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==",
+ "dev": true,
+ "dependencies": {
+ "safe-buffer": "^5.1.0"
+ }
+ },
+ "node_modules/readdirp": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
+ "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
+ "dev": true,
+ "dependencies": {
+ "picomatch": "^2.2.1"
+ },
+ "engines": {
+ "node": ">=8.10.0"
+ }
+ },
+ "node_modules/require-directory": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
+ "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/safe-buffer": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+ "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ]
+ },
+ "node_modules/serialize-javascript": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz",
+ "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==",
+ "dev": true,
+ "dependencies": {
+ "randombytes": "^2.1.0"
+ }
+ },
+ "node_modules/string-width": {
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+ "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+ "dev": true,
+ "dependencies": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/strip-ansi": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "dev": true,
+ "dependencies": {
+ "ansi-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/strip-json-comments": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
+ "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/supports-color": {
+ "version": "8.1.1",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
+ "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
+ "dev": true,
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/supports-color?sponsor=1"
+ }
+ },
+ "node_modules/through": {
+ "version": "2.3.8",
+ "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
+ "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU="
+ },
+ "node_modules/to-regex-range": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
+ "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
+ "dev": true,
+ "dependencies": {
+ "is-number": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=8.0"
+ }
+ },
+ "node_modules/type-detect": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-1.0.0.tgz",
+ "integrity": "sha1-diIXzAbbJY7EiQihKY6LlRIejqI=",
+ "dev": true,
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/which": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
+ "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+ "dev": true,
+ "dependencies": {
+ "isexe": "^2.0.0"
+ },
+ "bin": {
+ "node-which": "bin/node-which"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/workerpool": {
+ "version": "6.1.5",
+ "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.1.5.tgz",
+ "integrity": "sha512-XdKkCK0Zqc6w3iTxLckiuJ81tiD/o5rBE/m+nXpRCB+/Sq4DqkfXZ/x0jW02DG1tGsfUGXbTJyZDP+eu67haSw==",
+ "dev": true
+ },
+ "node_modules/wrap-ansi": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
+ "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
+ "dev": true,
+ "dependencies": {
+ "ansi-styles": "^4.0.0",
+ "string-width": "^4.1.0",
+ "strip-ansi": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+ }
+ },
+ "node_modules/wrappy": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+ "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
+ "dev": true
+ },
+ "node_modules/y18n": {
+ "version": "5.0.8",
+ "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
+ "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/yargs": {
+ "version": "16.2.0",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz",
+ "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==",
+ "dev": true,
+ "dependencies": {
+ "cliui": "^7.0.2",
+ "escalade": "^3.1.1",
+ "get-caller-file": "^2.0.5",
+ "require-directory": "^2.1.1",
+ "string-width": "^4.2.0",
+ "y18n": "^5.0.5",
+ "yargs-parser": "^20.2.2"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/yargs-parser": {
+ "version": "20.2.4",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz",
+ "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/yargs-unparser": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz",
+ "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==",
+ "dev": true,
+ "dependencies": {
+ "camelcase": "^6.0.0",
+ "decamelize": "^4.0.0",
+ "flat": "^5.0.2",
+ "is-plain-obj": "^2.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/yocto-queue": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
+ "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ }
+ },
+ "dependencies": {
+ "@ungap/promise-all-settled": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz",
+ "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==",
+ "dev": true
+ },
+ "ansi-colors": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz",
+ "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==",
+ "dev": true
+ },
+ "ansi-regex": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "dev": true
+ },
+ "ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "requires": {
+ "color-convert": "^2.0.1"
+ }
+ },
+ "anymatch": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz",
+ "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==",
+ "dev": true,
+ "requires": {
+ "normalize-path": "^3.0.0",
+ "picomatch": "^2.0.4"
+ }
+ },
+ "argparse": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
+ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
+ "dev": true
+ },
+ "assertion-error": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz",
+ "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==",
+ "dev": true
+ },
+ "balanced-match": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
+ "dev": true
+ },
+ "binary-extensions": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
+ "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==",
+ "dev": true
+ },
+ "brace-expansion": {
+ "version": "1.1.11",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+ "dev": true,
+ "requires": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "braces": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
+ "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
+ "dev": true,
+ "requires": {
+ "fill-range": "^7.0.1"
+ }
+ },
+ "browser-stdout": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz",
+ "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==",
+ "dev": true
+ },
+ "camelcase": {
+ "version": "6.2.1",
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.1.tgz",
+ "integrity": "sha512-tVI4q5jjFV5CavAU8DXfza/TJcZutVKo/5Foskmsqcm0MsL91moHvwiGNnqaa2o6PF/7yT5ikDRcVcl8Rj6LCA==",
+ "dev": true
+ },
+ "chai": {
+ "version": "3.5.0",
+ "resolved": "https://registry.npmjs.org/chai/-/chai-3.5.0.tgz",
+ "integrity": "sha1-TQJjewZ/6Vi9v906QOxW/vc3Mkc=",
+ "dev": true,
+ "requires": {
+ "assertion-error": "^1.0.1",
+ "deep-eql": "^0.1.3",
+ "type-detect": "^1.0.0"
+ }
+ },
+ "chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "dependencies": {
+ "supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "dev": true,
+ "requires": {
+ "has-flag": "^4.0.0"
+ }
+ }
+ }
+ },
+ "chokidar": {
+ "version": "3.5.2",
+ "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz",
+ "integrity": "sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==",
+ "dev": true,
+ "requires": {
+ "anymatch": "~3.1.2",
+ "braces": "~3.0.2",
+ "fsevents": "~2.3.2",
+ "glob-parent": "~5.1.2",
+ "is-binary-path": "~2.1.0",
+ "is-glob": "~4.0.1",
+ "normalize-path": "~3.0.0",
+ "readdirp": "~3.6.0"
+ }
+ },
+ "cliui": {
+ "version": "7.0.4",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz",
+ "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==",
+ "dev": true,
+ "requires": {
+ "string-width": "^4.2.0",
+ "strip-ansi": "^6.0.0",
+ "wrap-ansi": "^7.0.0"
+ }
+ },
+ "color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "requires": {
+ "color-name": "~1.1.4"
+ }
+ },
+ "color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true
+ },
+ "concat-map": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+ "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
+ "dev": true
+ },
+ "debug": {
+ "version": "4.3.2",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz",
+ "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==",
+ "dev": true,
+ "requires": {
+ "ms": "2.1.2"
+ },
+ "dependencies": {
+ "ms": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
+ "dev": true
+ }
+ }
+ },
+ "decamelize": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz",
+ "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==",
+ "dev": true
+ },
+ "deep-eql": {
+ "version": "0.1.3",
+ "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-0.1.3.tgz",
+ "integrity": "sha1-71WKyrjeJSBs1xOQbXTlaTDrafI=",
+ "dev": true,
+ "requires": {
+ "type-detect": "0.1.1"
+ },
+ "dependencies": {
+ "type-detect": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-0.1.1.tgz",
+ "integrity": "sha1-C6XsKohWQORw6k6FBZcZANrFiCI=",
+ "dev": true
+ }
+ }
+ },
+ "diff": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz",
+ "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==",
+ "dev": true
+ },
+ "emoji-regex": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+ "dev": true
+ },
+ "escalade": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
+ "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==",
+ "dev": true
+ },
+ "escape-string-regexp": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
+ "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
+ "dev": true
+ },
+ "fill-range": {
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
+ "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
+ "dev": true,
+ "requires": {
+ "to-regex-range": "^5.0.1"
+ }
+ },
+ "find-up": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
+ "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
+ "dev": true,
+ "requires": {
+ "locate-path": "^6.0.0",
+ "path-exists": "^4.0.0"
+ }
+ },
+ "flat": {
+ "version": "5.0.2",
+ "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz",
+ "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==",
+ "dev": true
+ },
+ "fs.realpath": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+ "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
+ "dev": true
+ },
+ "fsevents": {
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
+ "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
+ "dev": true,
+ "optional": true
+ },
+ "get-caller-file": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
+ "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
+ "dev": true
+ },
+ "glob": {
+ "version": "7.1.7",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz",
+ "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==",
+ "dev": true,
+ "requires": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.0.4",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ }
+ },
+ "glob-parent": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
+ "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+ "dev": true,
+ "requires": {
+ "is-glob": "^4.0.1"
+ }
+ },
+ "growl": {
+ "version": "1.10.5",
+ "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz",
+ "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==",
+ "dev": true
+ },
+ "has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "dev": true
+ },
+ "he": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
+ "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==",
+ "dev": true
+ },
+ "inflight": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+ "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
+ "dev": true,
+ "requires": {
+ "once": "^1.3.0",
+ "wrappy": "1"
+ }
+ },
+ "inherits": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
+ "dev": true
+ },
+ "is-binary-path": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
+ "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
+ "dev": true,
+ "requires": {
+ "binary-extensions": "^2.0.0"
+ }
+ },
+ "is-extglob": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+ "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=",
+ "dev": true
+ },
+ "is-fullwidth-code-point": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+ "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+ "dev": true
+ },
+ "is-glob": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
+ "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
+ "dev": true,
+ "requires": {
+ "is-extglob": "^2.1.1"
+ }
+ },
+ "is-number": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
+ "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
+ "dev": true
+ },
+ "is-plain-obj": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz",
+ "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==",
+ "dev": true
+ },
+ "is-unicode-supported": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz",
+ "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==",
+ "dev": true
+ },
+ "isexe": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+ "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=",
+ "dev": true
+ },
+ "js-yaml": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
+ "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
+ "dev": true,
+ "requires": {
+ "argparse": "^2.0.1"
+ }
+ },
+ "locate-path": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
+ "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
+ "dev": true,
+ "requires": {
+ "p-locate": "^5.0.0"
+ }
+ },
+ "log-symbols": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz",
+ "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==",
+ "dev": true,
+ "requires": {
+ "chalk": "^4.1.0",
+ "is-unicode-supported": "^0.1.0"
+ }
+ },
+ "minimatch": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
+ "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
+ "dev": true,
+ "requires": {
+ "brace-expansion": "^1.1.7"
+ }
+ },
+ "mocha": {
+ "version": "9.1.3",
+ "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.1.3.tgz",
+ "integrity": "sha512-Xcpl9FqXOAYqI3j79pEtHBBnQgVXIhpULjGQa7DVb0Po+VzmSIK9kanAiWLHoRR/dbZ2qpdPshuXr8l1VaHCzw==",
+ "dev": true,
+ "requires": {
+ "@ungap/promise-all-settled": "1.1.2",
+ "ansi-colors": "4.1.1",
+ "browser-stdout": "1.3.1",
+ "chokidar": "3.5.2",
+ "debug": "4.3.2",
+ "diff": "5.0.0",
+ "escape-string-regexp": "4.0.0",
+ "find-up": "5.0.0",
+ "glob": "7.1.7",
+ "growl": "1.10.5",
+ "he": "1.2.0",
+ "js-yaml": "4.1.0",
+ "log-symbols": "4.1.0",
+ "minimatch": "3.0.4",
+ "ms": "2.1.3",
+ "nanoid": "3.1.25",
+ "serialize-javascript": "6.0.0",
+ "strip-json-comments": "3.1.1",
+ "supports-color": "8.1.1",
+ "which": "2.0.2",
+ "workerpool": "6.1.5",
+ "yargs": "16.2.0",
+ "yargs-parser": "20.2.4",
+ "yargs-unparser": "2.0.0"
+ }
+ },
+ "mock-fs": {
+ "version": "4.14.0",
+ "resolved": "https://registry.npmjs.org/mock-fs/-/mock-fs-4.14.0.tgz",
+ "integrity": "sha512-qYvlv/exQ4+svI3UOvPUpLDF0OMX5euvUH0Ny4N5QyRyhNdgAgUrVH3iUINSzEPLvx0kbo/Bp28GJKIqvE7URw==",
+ "dev": true
+ },
+ "ms": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+ "dev": true
+ },
+ "nanoid": {
+ "version": "3.1.25",
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.25.tgz",
+ "integrity": "sha512-rdwtIXaXCLFAQbnfqDRnI6jaRHp9fTcYBjtFKE8eezcZ7LuLjhUaQGNeMXf1HmRoCH32CLz6XwX0TtxEOS/A3Q==",
+ "dev": true
+ },
+ "normalize-path": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
+ "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
+ "dev": true
+ },
+ "once": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+ "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
+ "dev": true,
+ "requires": {
+ "wrappy": "1"
+ }
+ },
+ "p-limit": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
+ "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
+ "dev": true,
+ "requires": {
+ "yocto-queue": "^0.1.0"
+ }
+ },
+ "p-locate": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
+ "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
+ "dev": true,
+ "requires": {
+ "p-limit": "^3.0.2"
+ }
+ },
+ "path-exists": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
+ "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
+ "dev": true
+ },
+ "path-is-absolute": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+ "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
+ "dev": true
+ },
+ "picomatch": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz",
+ "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==",
+ "dev": true
+ },
+ "randombytes": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
+ "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==",
+ "dev": true,
+ "requires": {
+ "safe-buffer": "^5.1.0"
+ }
+ },
+ "readdirp": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
+ "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
+ "dev": true,
+ "requires": {
+ "picomatch": "^2.2.1"
+ }
+ },
+ "require-directory": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
+ "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=",
+ "dev": true
+ },
+ "safe-buffer": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+ "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
+ "dev": true
+ },
+ "serialize-javascript": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz",
+ "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==",
+ "dev": true,
+ "requires": {
+ "randombytes": "^2.1.0"
+ }
+ },
+ "string-width": {
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+ "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+ "dev": true,
+ "requires": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.1"
+ }
+ },
+ "strip-ansi": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^5.0.1"
+ }
+ },
+ "strip-json-comments": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
+ "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
+ "dev": true
+ },
+ "supports-color": {
+ "version": "8.1.1",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
+ "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
+ "dev": true,
+ "requires": {
+ "has-flag": "^4.0.0"
+ }
+ },
+ "through": {
+ "version": "2.3.8",
+ "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
+ "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU="
+ },
+ "to-regex-range": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
+ "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
+ "dev": true,
+ "requires": {
+ "is-number": "^7.0.0"
+ }
+ },
+ "type-detect": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-1.0.0.tgz",
+ "integrity": "sha1-diIXzAbbJY7EiQihKY6LlRIejqI=",
+ "dev": true
+ },
+ "which": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
+ "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+ "dev": true,
+ "requires": {
+ "isexe": "^2.0.0"
+ }
+ },
+ "workerpool": {
+ "version": "6.1.5",
+ "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.1.5.tgz",
+ "integrity": "sha512-XdKkCK0Zqc6w3iTxLckiuJ81tiD/o5rBE/m+nXpRCB+/Sq4DqkfXZ/x0jW02DG1tGsfUGXbTJyZDP+eu67haSw==",
+ "dev": true
+ },
+ "wrap-ansi": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
+ "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^4.0.0",
+ "string-width": "^4.1.0",
+ "strip-ansi": "^6.0.0"
+ }
+ },
+ "wrappy": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+ "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
+ "dev": true
+ },
+ "y18n": {
+ "version": "5.0.8",
+ "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
+ "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
+ "dev": true
+ },
+ "yargs": {
+ "version": "16.2.0",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz",
+ "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==",
+ "dev": true,
+ "requires": {
+ "cliui": "^7.0.2",
+ "escalade": "^3.1.1",
+ "get-caller-file": "^2.0.5",
+ "require-directory": "^2.1.1",
+ "string-width": "^4.2.0",
+ "y18n": "^5.0.5",
+ "yargs-parser": "^20.2.2"
+ }
+ },
+ "yargs-parser": {
+ "version": "20.2.4",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz",
+ "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==",
+ "dev": true
+ },
+ "yargs-unparser": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz",
+ "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==",
+ "dev": true,
+ "requires": {
+ "camelcase": "^6.0.0",
+ "decamelize": "^4.0.0",
+ "flat": "^5.0.2",
+ "is-plain-obj": "^2.1.0"
+ }
+ },
+ "yocto-queue": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
+ "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
+ "dev": true
+ }
+ }
+}
diff --git a/archive/package.json b/archive/package.json
new file mode 100644
index 0000000..5dd2591
--- /dev/null
+++ b/archive/package.json
@@ -0,0 +1,47 @@
+{
+ "name": "jsfs",
+ "version": "4.2.0",
+ "description": "deduplicating filesystem with a REST interface",
+ "main": "server.js",
+ "files": [
+ "server.js",
+ "config.ex",
+ "jlog.js",
+ "lib"
+ ],
+ "directories": {
+ "lib": "./lib",
+ "tools": "./tools"
+ },
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/jjg/jsfs.git"
+ },
+ "homepage": "https://github.com/jjg/jsfs",
+ "keywords": [
+ "filesystem",
+ "deduplicate",
+ "http",
+ "rest"
+ ],
+ "author": {
+ "name": "Jason Gullickson"
+ },
+ "contributors": [
+ {
+ "name": "Marc Brakken"
+ }
+ ],
+ "license": "GPL-3.0",
+ "scripts": {
+ "test": "./node_modules/.bin/mocha --reporter spec"
+ },
+ "dependencies": {
+ "through": "^2.3.8"
+ },
+ "devDependencies": {
+ "chai": "^3.5.0",
+ "mocha": "^9.1.3",
+ "mock-fs": "^4.0.0-beta.1"
+ }
+}
diff --git a/rock64.md b/archive/rock64.md
similarity index 100%
rename from rock64.md
rename to archive/rock64.md
diff --git a/server.js b/archive/server.js
similarity index 100%
rename from server.js
rename to archive/server.js
diff --git a/test/file-types.js b/archive/test/file-types.js
similarity index 100%
rename from test/file-types.js
rename to archive/test/file-types.js
diff --git a/test/fixtures/test.mp3 b/archive/test/fixtures/test.mp3
similarity index 100%
rename from test/fixtures/test.mp3
rename to archive/test/fixtures/test.mp3
diff --git a/test/fixtures/test.wav b/archive/test/fixtures/test.wav
similarity index 100%
rename from test/fixtures/test.wav
rename to archive/test/fixtures/test.wav
diff --git a/test/utils.js b/archive/test/utils.js
similarity index 100%
rename from test/utils.js
rename to archive/test/utils.js
diff --git a/test/validate.js b/archive/test/validate.js
similarity index 100%
rename from test/validate.js
rename to archive/test/validate.js
diff --git a/tools/compress_blocks.js b/archive/tools/compress_blocks.js
similarity index 100%
rename from tools/compress_blocks.js
rename to archive/tools/compress_blocks.js
diff --git a/tools/compression_notes.txt b/archive/tools/compression_notes.txt
similarity index 100%
rename from tools/compression_notes.txt
rename to archive/tools/compression_notes.txt
diff --git a/tools/jsfsck.js b/archive/tools/jsfsck.js
similarity index 100%
rename from tools/jsfsck.js
rename to archive/tools/jsfsck.js
diff --git a/tools/migrate_superblock.js b/archive/tools/migrate_superblock.js
similarity index 100%
rename from tools/migrate_superblock.js
rename to archive/tools/migrate_superblock.js
diff --git a/tools/movesomeblocks.sh b/archive/tools/movesomeblocks.sh
similarity index 100%
rename from tools/movesomeblocks.sh
rename to archive/tools/movesomeblocks.sh
diff --git a/tools/pool_size.js b/archive/tools/pool_size.js
similarity index 100%
rename from tools/pool_size.js
rename to archive/tools/pool_size.js
diff --git a/config/example.json b/config/example.json
new file mode 100644
index 0000000..a3c1c46
--- /dev/null
+++ b/config/example.json
@@ -0,0 +1 @@
+// TODO: Write example config
diff --git a/docs/blockdriver.md b/docs/blockdriver.md
new file mode 100644
index 0000000..46e0e57
--- /dev/null
+++ b/docs/blockdriver.md
@@ -0,0 +1,3 @@
+# Blockdriver
+
+Blockdrivers are plugins that persist [jBlock](./jblock-jnodes.md)s and [jNode](./jblock-jnode.md)s (all treated as blocks by the driver). The default driver persists jBlocks and jNodes on a local disk, but a driver can be written for anything capable of storing blocks.
diff --git a/docs/blockstore.md b/docs/blockstore.md
new file mode 100644
index 0000000..fb7fe84
--- /dev/null
+++ b/docs/blockstore.md
@@ -0,0 +1,5 @@
+# Blockstore
+
+The blockstore can be trusted to store and retrieve blocks. It implements any configurable logic for writing blocks to multiple locations via configured [Blockdriver](./blockdriver.md)'s for redundancy, performance, federation, etc.
+
+
diff --git a/docs/config.md b/docs/config.md
new file mode 100644
index 0000000..d464c9e
--- /dev/null
+++ b/docs/config.md
@@ -0,0 +1,3 @@
+# Config
+
+This is where I'll write more about how configuration works once I know more.
diff --git a/docs/jedi.md b/docs/jedi.md
new file mode 100644
index 0000000..f19efa1
--- /dev/null
+++ b/docs/jedi.md
@@ -0,0 +1,21 @@
+# Jedi
+
+Jedi is the JSFS system editor.
+
+
+## History
+
+Jedi was [originally](https://github.com/jjg/jedi) hacked-together from off-the-shelf parts as a proof-of-concept of JSFS as an application platform. The original Jedi was a browser-based editor that not only could be used to write new applications, it was able to edit it's own code on-the-fly, allowing iterations on the editor itself with no other software.
+
+
+## Jedi5
+
+Jedi is being rewritten alongside JSFS and will be included with JSFS as a ROM. In addition to basic text editing features, Jedi now includes:
+
+* File upload/download
+* Sharing link generator (automatically create links to a file, optionally with embedded editor)
+* ROM authoring (more about ROMs in [roms.md](./roms.md))
+* Pluggable render/preview view (Markdown, HTML, graphviz, etc.)
+* "wiki-like" code editing where things like functions can be clicked to reveal their source, and if the source doesn't exist, it is created
+
+Aside from being generally useful it provides a reference example for JSFS applications, establishing standards around how data is shared, how authentication is performed, etc.
diff --git a/docs/journal.md b/docs/journal.md
new file mode 100644
index 0000000..3699a61
--- /dev/null
+++ b/docs/journal.md
@@ -0,0 +1,417 @@
+# JSFS 5 Dev Journal
+
+
+## 01202025
+Looks like I left myself a mess, where was I?
+
+Cleaned-up some tests around the blockstore. The world is a fucking mess today.
+
+I *think* the blockstore works well enough to save a jnode file, maybe it's time to work on directories a bit so I can get the failing auth tests to pass?
+
+OK, jnode is now Loading and Saving itself.
+
+OK, now that we can create directory jnodes, back to working on directory-based auth.
+
+Directory-based auth seems to be working? The code is pretty messy but since my time is almost up I'm going to call it for now.
+
+Since I have a couple of minutes maybe I can leave myself some notes for what's next.
+
+Since directory I/O seems to work, maybe I can start working on the "subspace root"-based configuration stuff?
+
+Actually now that I peek at the CI, I need to do something about these hard-coded paths because it's causing [test failures in CI](https://github.com/jjg/jsfs/actions/runs/12875688555/job/35897433725?pr=161#step:7:147).
+
+I guess maybe that is related to the config stuff? Maybe.
+
+
+## 01192025
+Can't fall back asleep so I thought about JSFS and came up with a few new/revised ideas.
+
+### delegated auth
+Instead of using a static, internal auth function, allow a jnode to point to an auth function stored as an executable file inside jsfs. By default this is the current internal auth function, but it could point to a custom, user-defined functiom instead. I think this could be a simple way to support things like oauth.
+
+It means adding overhead to each call (the auth function would need to be resolved, loaded, executed, etc.) but I can imagine ways to optimize the "default" auth function if performace was more important to the applicatiom than custom auth.
+
+> Of course this can't be implemented until X support is implemented so until then the internal auth module will continue to be developed.
+
+### slorp mode
+This has been discussed many times over the years but I want to codify it now because I think it's a fascinating use case.
+
+With slorp enabled, jsfs can capture the content of any website and serve it by making a simple http request using the jspace name of the website. For example:
+
+https://.com.jasongullickson/about.html
+
+returns the contents of https://jasongullickson.com/about.html even if that file wasn't previously stored in this jsfs instance. It does this by first looking in the local store for a jnode for this file, and if one is not found, the server creates one and makes an http request to fetch the contents. The contents are then stored in jsfs and returned to the client. As a result, subsequent requests for this site are served directly from jsfs at much higher speed, and are also avaliable when the original site is unreachable. This is similar to a caching proxy, and has a number of practical uses.
+
+The primary use is migrating a site to JSFS. With slorp enabled, the site is crawled by making requests against the JSFS server. When the craw is complete, the DNS for the site can simply be changed to point to the JSFS server and the migration is complete.
+
+There are many other applicatioms for this feature, such as making replicas for avaliability or even offline use, or simply caching a site to reduce pressure on the origin and speed-up access. This also is a way to rapidly build-out the blocks in thr blockstore, potentially increasing deduplication performance.
+
+> Note: slorped files are always initially public, and inherit the root-root access key.
+
+
+## 01172025
+It's only been a few days since I wrote some functional code (as opposed to tests) here and I'm already feeling disconnected from what exactly needs to be done next; not a good sign.
+
+I have a little time now and might have a few free hours coming in a few days, so let's see if I can put the needle back in the groove.
+
+Let's start by tidying-up the test output, that should make it a little easier to see what is broken vs. what just doesn't exist yet.
+
+
+## 01132025
+Had a new idea for config, and it also solves the "static access keys" replacement problem.
+
+I want to try moving the config into jspace, as part of the jnode that is stored at the absolute root of jspace. So for example, if you look at the jspace path:
+
+http://10.1.10.1/.com.jasongullickson.jsfs/about.html
+
+The "/." after the IP address (I think...) would point to a special jnode file that would contain node configuration data in addition to the usual directory contents and access key.
+
+This allows the configuration to be modified at runtime via the JSFS API like any other file stores in JSFS, while preserving the ability to create & modify the configuration from outside as well since jnodes are just json files.
+
+> This also decides the config format going forward, its JSON.
+
+This does mean that all JSFS 5 servers will have a "local storage" blockdriver since this "boot block" will need to be readable as the server starts-up, but it could be limited to startup/restart time if desired. There's also the matter of deciding if this jnode gets copied/replicated/etc. to other persistence areas, etc. but those details can be worked-out once I'm sure this will work at all.
+
+Part of this will also be adding a startup check that walks the operator through creating the config if none is found (maybe even via optional web interface), defining standards for where this jnode file is stored, maybe providing a command argument to pass a custom location (or complete jnode file?), etc.
+
+There's a lot of interesting applications for this beyond basic configuration. For example, it provides a means for a server admin to provision host/domains as adding them will require the root access key. An admin could create the host's root directory using the root-root access key and then set the domain's root access key to something known by the domain's admin.
+
+
+## 01112025
+Had a few minutes to space so I'm just picking at adding some more test and notes to define what needs to be done.
+
+
+## 01102025
+It's the end of the mourning week (I know, it's never the end) and while I won't be able to work on this every day anymore I want to make at least one commit a week if not more. I should take some time today to package this week's work up in a way that will be easy to pick-up and set-down when I have less time to work on it.
+
+One thing I've noticed over this week is that an hour is the absolute minimum I need to make any progress at all, and really it's more like two hours. Four hours seems to be the maximum single-session length before I start to slow down, so two-to-four hours is really the goal for scheduling future work sessions.
+
+I'm almost afraid to simply "catalog" everything I can think of that is outstanding as it would be as overwhelming as it would be incorrect. Another thing I've found working this week is that I've found a "grain size" in the work that lets me comfortably work through several generations of a component, making better choices as I go. This is good for making progress and producing quality results, but it also means that what I thought I would do isn't always what I end-up doing, which means things like detailed TODO lists would just end up being wrong.
+
+Instead maybe what I should do is finish laying-down the foundation for the most obvious things (the remaining verbs, etc.) so that when I wrap things up this week the branch should have at least a toehold for each essential component.
+
+That's done with the exception of `config`. I still don't know exactly how config should work. In previous versions of JSFS it was a Javascript file, which I like but isn't the best way to go for various reasons I don't recall off-hand. It could be JSON, but you can't put comments in JSON and it could be YAML, but I don't want to add a dependency just to use YAML so unless node.js can deal with YAML natively that's not an option.
+
+I might just punt on config for right now.
+
+Another thing I'd like to get setup before the end of the week is CI-ish stuff. It would be nice to have tests and linting running automated in the repo.
+
+Got a basic GA that runs the tests against the `main` branch and PR's:
+
+https://github.com/jjg/jsfs/actions
+
+Might add a linter too...
+
+Linter added. Weird note: I couldn't get it to ignore the `test` directory using the config, so I had to cram it into the command-line arguments in `package.json`.
+
+
+## 01092025
+I've been thinking about putting the [blockstore](./blockstore.md) on a separate thread/process in a way that it can be shared by all requests. The reasoning is that it can run off it it's own corner of the computer and not block the main thread. It also opens up the possibility of caching across requests/clients, and since it works at the block level this could provide a sort of *compute-level deduplication*. Also having a very clean interface with the blockstore might make it easier to spin the blockstore out into a stand-alone program that could be run without the rest of JSFS on remote servers to support federation, replication, etc.
+
+Anyway, back to the task at hand.
+
+Yesterday I was able to load *something* via the blockstore's `Load()` method. It was very juryrigged so that needs work. I also need to get `Store()` working, and I think it makes sense to do that first so there's an automated way to write data before continuing work on reading it.
+
+Getting `Store()` to work was a matter of using an easier API than where I started.
+
+Now that I'm seeing more of how this will be wired-up I wonder if it could be as simple as not having the verb handler `await` the call to the `blockstore`? This would allow the verb handler to complete the client request without waiting for the data to be persisted and cleanly delegates things like raid, retry, etc. to the blockstore. However if some unrecoverable problem occurs writing the block there's no way to tell the client, so there will have to be another way, maybe by writing something to the `jnode` to indicate corruption status?
+
+Not sure how best to handle that but I want to design this with the idea that each component/module can trust the others to do there job, so a verb handler should be able to trust that the blockstore will store a block one way or another, and that when called upon to retrieve the block, it will do so unless it's completely impossible (in which case what is the verb handler going to do anyway?). This encourages module autonomy and prevents exception handling from leaking across module boundaries.
+
+The only thing I don't like about this is that I think it forces the block naming (hashing the block data to generate a name) back into the verb handler, which feels less clean. Hmm...
+
+I decided to move block hash/name generation out of the blockstore so `Store()` can be called w/o `await`ing the result.
+
+
+### References
+* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer
+* https://nodejs.org/docs/latest-v22.x/api/webstreams.html#class-readablestream
+
+
+## 01082025
+After thinking about it overnight I'm not going to do anything clever with directory files and will instead treat them like any other for now. Maybe I'll move the data into the jnode at some point as an optimization, but for now they will get treated like any other file.
+
+This does mean that I have a chicken-and-egg problem with implementing the parts of `auth` that use directories so that will have to get parked until I've written enough of the "write" verbs to write out some directories.
+
+So what does that mean for *what's next*?
+
+I think it means it's time to start implementing `POST`.
+
+```
+client -> index.js -> post.mjs -> blockstore.mjs -> blockdrivers
+```
+
+Once I started implementing `POST` it became clear that I should probably pass the request and response to each verb handler and just let them set the HTTP status, etc. This is essential for verbs that stream data, so why not make the interface consistent? I'm still not sure if each verb method should return anything at all because I'm not sure how that would change the main loop, so I left a note and will revisit that later.
+
+I also changed `blocks` back to an array; I had changed it to an object because I've always worried that the order of an array might change but according to the docs I can find ES6 treats arrays as objects so this isn't likely to happen. AFAIK it's never been a problem in any previous version of JSFS, so I'll leave it as an array for now and do some additional testing to see if I can break it.
+
+Added initial tests (and some implementation) for `HEAD`, `GET`.
+
+> Reminder: use `let` instead of `var`, etc.
+
+OK, it's time to make the fake `req` (`IncomingMessage`) I'm using in the tests behave more like the real thing.
+
+I thought there might be some examples of this on the web, but the answer is always to use some third-party module. I'd rather understand how this works and learn something, so I'm going to see what I can do myself before reaching for some random code that I'm going to have to evaluate introduce more dependencies/risks/etc.
+
+`http.IncomingMessag` extends `stream.Readable` so maybe I can just do that to get what I need?
+
+As I dig into this I'm reminded that there's two types of stream in Node.js, the "original" and "WebStream". I want to stick to the latter as it should be more like the rest of the web.
+
+So far so good.
+
+Now that I'm implementing some of the `Post` module I'm going to have to decide where the line is between the `Post` verb handler and the `blockstore` (and additionally, `blockdriver`(s)).
+
+OK, I'm passing a file from a test to the `Post` handler, and the handler is parsing the stream and adding block names to the `jnode`. It's not persisting any data yet, but I'll probably save that for tomorrow since it will involve starting work on the `blockstore` and that's a bit more than I'm ready to bite off today.
+
+I had a little more time today to think about the blockstore/blockdriver bits. One change I've had in mind is to consolidate all persistence via the blockstore so that both jblocks and jnodes are stored the same way. In previous versions of JSFS these were separate, which made things complicated when moving beyond local disk-based blockstores.
+
+So the blockstore should have a simple two-method interface; one to store a jblock/jnode and one to retrieve them. Hmm...
+
+
+### References
+* https://stackoverflow.com/questions/34955787/is-a-javascript-array-order-guaranteed
+* https://stackoverflow.com/questions/28152740/how-to-make-empty-placeholder-tests-intentionally-fail-in-mocha
+* https://nodejs.org/docs/latest-v22.x/api/http.html#class-httpincomingmessage
+* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/extends
+* https://nodejs.org/docs/latest-v22.x/api/stream.html#readable-streams
+* https://nodejs.org/api/buffer.html#static-method-bufferfromarraybuffer-byteoffset-length
+
+
+## 01072025
+I thought of a few things I overlooked overnight. The first is that I need to make sure that expired tokens are not authorized and also, there might be a way to eliminate the extra `expires` parameter when using a temporary token.
+
+I think I can also clean-up some of the method handling code by moving param extraction into the `utils` module, and I have an idea for supporting clients that can't use the "custom" methods as well.
+
+New temporary token implemented (no more `expires` param!), param extraction moved to the `util` module and all token-related tests passing!
+
+So what's next?
+
+Could be implementing enough of the directory logic to get the two remaining `auth` tests to pass (crawling the jspace for keys), or could be adding the method override parameter (easier, but not as important right now).
+
+It would be cool to have passing tests, and the directory thing is more interesting (maybe the verb thing isn't even a problem?).
+
+This is fairly complex and might not even be possible until I implement more of the underlying I/O code (blockstore, etc.). There's also the question of treating jnodes for directories differently from other files. I've considered writing directory data (file listing, etc.) directly into the jnode instead of into individual blocks. The upside of this is that it simplifies directory I/O (we can simply load the jnode and skip loading blocks) but it has the downside of limiting directory size to the size of a single block (jnodes are stored in a single block). It also creates a somewhat special case for this data instead of being consistent across all types of data stored in JSFS.
+
+The only real reason I'm considering this is that if the data is stored like everything else that means that directory lookups will need to go through the blockstore and blockdrivers, potentially generating an HTTP request for every directory lookup. If there a very deep jspace with no directory files but the root, this could result in a *lot* of http requests.
+
+I also just remembered that `auth` needs to differentiate between `public` and `private` files...
+
+
+## 01062025
+Dad passed away last time I was working on this. Taking me a few minutes to figure out where I left off...
+
+Looks like there's some failing `auth` tests, so that's as good a place to start as any.
+
+Added a bunch of `not implemented` tests as placeholders for things `auth` should (or shouldn't) do. Also started documenting keys and tokens in the new README.
+
+Something I'm reconsidering is the use of SHA1 for token generation (and elsewhere). It's no longer considered secure, but I'm not sure if that matters. We're not really using it for security, only to generate collision-resistant hashes, so maybe it's OK to keep? Keeping it ensures backward-compatibility, and using something more secure is probably going to be more resource-intensive (although I should verify this). Something to think about...
+
+I also decided that we're going to use directories to replace `STATIC_ACCESS_KEYS`. I think it can be as simple as creating a "root" directory, maybe I'm wrong, and if so we change it later.
+
+Got keys and tokens essentially done, still need to flesh-out the verb-specific token tests but I think this is enough for today.
+
+
+### References
+* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Conditional_operator
+* https://nodejs.org/api/url.html#class-urlsearchparams
+
+
+## 12312024
+Beginning to flesh-out `auth`. This now depends on the concept of `directory`, so I might give some more thought to that (or just stub around it for now).
+
+This also brings-up the issue of `STATIC_ACCESS_KEYS`. This is something just added to JSFS and allows the administrator to configure keys that will be used when there's no existing `jnode` for a file. This is a simple "locking" mechanism preventing writes from unknown clients. I think this should be preserved but I'm not sure if it should still be a configuration-level thing or if it could leverage the new `directory` abstraction?
+
+For example, instead of a config value, an admin could `POST` a "root" `directory` to a newly-configured host, effectively establishing a static key for every subsequent write to that hostname. Since establishing a new host *kinda* requires DNS changes (pointing an `A` or `AAA` record at the JSFS server) it has similar authority to storing these keys in the config. Of course this isn't completely true, because a `jspace` path can always be used to write to a host-specific path without DNS configuration so... I'm not sure.
+
+Maybe we start with this and think about ways to address the `jspace`-path loophole as it would be a lot more elegant if we can get everything we need from the `directory` construct.
+
+I found a simple way to stub the `req` passed-in to `auth` that I'll use to write tests for the verbs as well. It feels hacky but doesn't add any dependencies so I'll just run with it for now.
+
+Also fixed a failing test in `jspace` calculation and added some docs around `jspace` to the README.
+
+Got `auth` working in the server, and added some `curl` examples to the README.
+
+
+## 12302024
+After overthinking it, I'm just going to pass the whole request object around.
+
+I'm calling the "jsfs address" `jspace` now. I don't love this name, but I just got tired of having to re-describe it. I also corrected the formatting of it in earlier journal entries.
+
+I've established something of a pattern for invoking the verbs. I'm passing the `jnode` and the `res` object in to all verbs, and the `req` as well for verbs that will consume data from the client (`POST`, `PUT`, etc). Each verb will be responsible for handling exceptions and setting the HTTP status if something weird happens while they're in control. This feels heavy-handed, and is going to make writing tests harder, but it will work and should make streaming data easier.
+
+Server is handling it's first requests tonight. It's just a `HEAD` request, and the results are pretty much rigged, but it at least demonstrates the verb calling pattern. There's no automated test for the `head` module yet, I'll leave that for another time when I have more energy.
+
+I've also added two new verbs (`MOVE`, `COPY`) which will be used to implement zero-op data copying (essentially adding a new inode pointing to existing blocks). This is going to be like `EXECUTE` in that we'll come up with some way to trigger these using "normal" HTTP verbs with some header/etc. for clients that can't specify non-standard verbs.
+
+I've made breaking changes to the format of the `jnode` JSON. I think this is the right thing to do, but I'll have to add support for backward-compatability to live-up to the commitments I've set earlier. I think this will simply be a matter of additional logic in the `Jnode.Load()` method to detect pre-v5 `inode`s and mapping them into v5 `jnode`s in memory. This way they are simply left untouched for read operations and will be upgraded automatically during writes.
+
+
+## 12262024
+Added license.
+
+Also decided that I'm going to commit to committing *something* to this project at least once a week in 2025.
+
+Added a bunch of docs for capturing ideas as they come.
+
+
+## 12252024
+Add readme and lots of TODO's.
+
+
+## 12242024
+
+Setting up the repo and moving some of the sketching-out from below into the code.
+
+There's a lot to decide. I'm going with [mocha](https://mochajs.org/) for testing but I'm being careful to avoid adding any external dependencies to the production build. I'm also going with [es6](https://nodejs.org/api/esm.html) style modules, hopefully that works with any Blockdriver dependencies.
+
+Turns out that there is some tweaking to do to use ES6 modules, and this tripped me up setting up some initial tests.
+
+### References
+* https://nodejs.org/api/esm.html
+* https://mochajs.org/#installation
+* https://www.naukri.com/code360/library/native-es-modules-in-mocha
+* https://mocha-docs-next.netlify.app/explainers/nodejs-native-esm-support/
+
+
+## 12232024
+
+This is going to be a rambling description of JSFS3.
+
+There will be a few breaking changes to the existing JSFS API, but I don't think they will be missed (the removal of `encrypted` and `compressed` blocks). Otherwise there should be no breaking API changes, and existing JSFS pools should be mountable (this backward compatibility is configurable).
+
+Here's a high-level description of how each method works.
+
+### GET
+1. Compute JSFS address
+2. Load jnode
+3. Auth
+4. Stream data from Blockstore
+
+
+### POST
+1. Compute JSFS address
+2. Load jnode
+ + If jnode exists, return error
+3. Auth
+ Check for directory permissions
+3. Stream data to Blockstore
+5. Analyze blocks with Metadatadriver(s)
+4. Write blocks to Blockdriver(s)
+5. Create new jnode
+6. Update directory
+
+### PUT
+1. Compute JSFS address
+2. Load jnode
+ + If jnode doesn't exist, return error
+3. Auth
+3. Stream data to Blockstore
+5. Analyze blocks with Metadatadriver(s)
+4. Write blocks to Blockdriver(s)
+5. Create new jnode
+6. Version previous jnode
+
+### DELETE
+1. Compute JSFS address
+2. Load jnode
+3. Auth
+4. Create new jnode marked deleted
+5. Version previous jnode
+6. Update directory
+
+### HEAD
+1. Compute JSFS address
+2. Load jnode
+ + if jnode doesn't exist, return error
+3. Auth(?)
+3. Return metadata
+
+### EXECUTE
+1. Compute JSFS address
+2. Load jnode
+ + if jnode doesn't exist, return error
+3. Load blocks into Buffer from Blockstore
+4. Execute code in Buffer
+5. Stream the output of the running code
+
+
+### JSFS Address (jspace)
+Incoming requests are parsed into a JSFS path which is a reverse of the hostname plus the rest of the path. For example:
+
+`http://jasongullickson.com/about.html`
+
+becomes:
+
+`/com.jasongullickson/about.html`
+
+JSFS uses these paths internally to allow a JSFS instance to represent any valid DNS name without relying on DNS.
+
+The JSFS Address is a hash of this path. This hash is used to name the jnode which stores the metadata and block map for each file stored in the filesystem.
+
+
+### Jnode
+A new jsfs-specific name for inodes. Stored in a subdirectory based on the first 8 chars of the jnode filename with an extension of `.json`.
+
+During write operations Jnodes are written to all writable Blockdrivers for redundancy.
+
+When an exsting JSFS pool is mounted, the existing inodes will be used for Read operations. When Writes occur, the original inodes will be replaced with versioned Jnodes in the Jnode directory structure. This backward-compatibility feature can be disabled to improve performance.
+
+
+### Jblock
+A Jblock is a piece of a stored file, named using a hash of the data the block contains. Block length is configurable. The block length establishes the maximum size of a jnode as jnodes are written as a single block. Blocks are stored in subdirectories named for the first eight bytes of the block's name. Block files have the extension `.jblock`.
+
+When an existing JSFS pool is mounted, the original blocks will be unchanged for Read operations, but new blocks will be written as `.jblock` files into an appropriate directory structure. This backward-compatibility mode can be disabled to improve performance.
+
+
+### Auth
+Auth accepts a jnode (or `null`), an Access Token or Access Key. It returns a boolean indicating if the request is allowed or not. If a jnode is provided, Auth will use permissions data from the jnode to decide if the request is authorized. If no jnode is provided, Auth with walk the path backwards searching for Directory files (`/`) and use the first one found to check permissions. If no directory files are found, Auth will look for a "global" key, and if no global key is found the request will be allowed.
+
+
+### Blockstore
+The Blockstore is used to store and retrieve files. For storage it receives a stream of incoming data and a Blockdriver configuration. The Blockstore cuts up the stream into individual blocks, hashes the block data to generate a name used to store the block and then stores each block using the configured Blockdrivers. Retrieval works in reverse, the Blockstore is presented with a jnode and returns a stream of data composed of the blocks associated with the jnode. The Blockstore queries the configured Blockdrivers in parallel to assemble the stream as quickly as possible and begins returning data as soon as blocks are avaliable in the correct sequence.
+
+When storing data, the Blockstore will only attempt to write blocks to Blockdrivers configured as Write or Write Only. When retriving data, the Blockstore will start with Blockdrivers configured for Write access, then move on to Read and Read Only drivers.
+
+> What about stripe vs. mirror? I'm tempted to ignore this and delegate concerns of redundancy to the Blockdrivers themselves.
+
+
+### Blockdriver
+A Blockdriver loads and stores individual Blocks. A given instance of JSFS can be configured with several Blockdrivers and each driver can be configured individually for Read, Write, Read Only or Write Only operation. Each Blockdriver must provide a method to `save(block)` and `load(block)` blocks, as well as any driver-specific settings.
+
+
+### Metadatadriver
+Metadatadrivers can be configured to analyze files as they are being stored and add metadata to the file's Jnode.
+
+
+### Versioning
+File versioning is provided by preserving the existing copy of a Jnode when the file it represents is updated. During `PUT` the original Jnode is stored under a new name indicating the version of the file it represents, and the `version` property of the new Jnode is an increment of the `version` property of the previous Jnode. Versions are who integers and the `version` property is initialized to `1` when a file is created.
+
+The naming convetion for previous Jnodes is the Jnode name + `_v2` where `2` is the version. To access previous versions of the file, a `x-jsfs-version` header or `jsfs_version` querystring parameter is added to the request.
+
+
+### Execute
+The `EXECUTE` verb will attempt to execute a file stored at the specified path. The file must be marked as executable, and be a valid Javascript or Webassembly. The output from the executing file will be streamed back in the response. For convinience, the verbs `GET`, `POST` and `PUT` can also execute executable files (some clients may not be able to specify the `EXECUTE` verb) but I'm not exactly sure how we want to trigger this yet.
+
+
+### Proc
+The `/proc` path is used to access hardware resources on the JSFS host. It can be used to query or alter the host operating system or to access hardware-specific resources like GPIO, battery levels, etc. It is only accessible via the `.localhost` path, which limits access to console access or executable files running under JSFS.
+
+
+### Etc
+The `/etc` path provides access to the running configuration of JSFS. `/etc/jsfs.config` allows things like Blockdriver configuration to be changed without restarting the service. It is only accessible via the `.localhost` path.
+
+
+### Directories
+Directories `/` are JSON files stored like any other file. When a file is accessed the target path is traversed in reverse order searching for directory files. If one is found, it is queried for permissions which are then used during the Auth step to determine if the access is granted. Directories also contain an array of files stored under the path the directory file is stored in. This array is maintained automatically by JSFS, and can probably be modified manually if permissions allow it.
+
+Directories use the same permissions as files (`public`/`private`) and are created with `public` permissions by default. A `public` directory can be viewed by anyone, a `private` directory can only be viewed by providing a valid Key or token. Modifying any directory directly requires a Key or Token for that directory, but JSFS will update a directory automatically if files contained within the directory are modified (which of course requires a valid Key or Token).
+
+
+### ROMs
+ROMs are read-only packages of Blocks & Jnodes that can easily be added to any JSFS instance to make data and applications avaliable. It requires no real changes to JSFS, just a specified way to identify a range of files to export as a ROM (maybe just a path?) and a way to add them to another instance (maybe as a part of storage configuration or just another type of Blockdriver?).
+
+The primary application I have in mind for ROMs are complete applications (like Jedi) or shared Javascript libraries, etc.
+
+
+### Jedi
+Jedi is a web-based editor designed to work with JSFS. Among other things it makes writing applications on JSFS easy and can be used to modify JSFS itself.
diff --git a/docs/roms.md b/docs/roms.md
new file mode 100644
index 0000000..1275746
--- /dev/null
+++ b/docs/roms.md
@@ -0,0 +1,5 @@
+# ROMs
+
+JSFS ROMs are archives containing `jNode`s and `jBlocks` representing a self-contained and/or stand-alone piece of data or software. The most obvious use of this is for distributing web applications that can be run directly from JSFS like [jedi](./jedi.md). ROMs can be mounted into a JSFS filesystem as part of the [config](./config.md) or simply uncompressed into the directories of the [blockstore](./blockstore.md).
+
+Maybe there's some sort of "manifest" or other document/metadata that should accompany a ROM, but I'm not sure. I really like the idea that they are simply native constructs and not some special case.
diff --git a/eslint.config.js b/eslint.config.js
new file mode 100644
index 0000000..e87147c
--- /dev/null
+++ b/eslint.config.js
@@ -0,0 +1,12 @@
+import js from "@eslint/js";
+
+export default [
+ js.configs.recommended,
+ {
+ ignores: ["test/*", "archive/*"],
+ rules: {
+ "no-unused-vars": "warn",
+ "no-undef": "warn"
+ }
+ }
+];
diff --git a/index.js b/index.js
new file mode 100644
index 0000000..9982659
--- /dev/null
+++ b/index.js
@@ -0,0 +1,97 @@
+/*
+JSFS
+Copyright (C) 2025 Jason J. Gullickson
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+*/
+
+import http from 'node:http';
+
+import { GetJspace } from './lib/utils.mjs';
+import { Jnode } from './lib/jnode.mjs';
+import { Auth } from './lib/auth.mjs';
+import { Head } from './lib/verbs/head.mjs';
+import { Post } from './lib/verbs/post.mjs';
+
+
+// TODO: Start logging
+// TODO: Load the config
+
+// Configure the http listener
+const server = http.createServer();
+server.on('request', async (req, res) => {
+
+ // Translate the incoming hostname & path to jspace
+ const jspace = await GetJspace(req.headers['host'], req.url);
+
+ // Get the existing or create a new jnode for this jspace.
+ // TODO: Consider condensing this into a single constructor.
+ const jnode = new Jnode(jspace);
+ const err = await jnode.Load();
+ if(err){
+ // TODO: Some requests don't need an existing jnode,
+ // so an error here shouldn't stop all requests.
+ }
+
+ // Auth the request
+ const authorized = await Auth(req, jnode);
+ if(!authorized){
+ res.statusCode = 403;
+ res.end();
+
+ // TODO: Figure out how to end the connection immediately.
+ return;
+ }
+
+ // TODO: Add "action" param to let clients specify the
+ // method explicity (needed for clients that can't
+ // supply the extended HTTP methods used below).
+ switch(req.method) {
+ case 'HEAD':
+ await Head(req, res, jnode);
+ break;
+ case 'GET':
+ // TODO: Handle GET
+ break;
+ case 'POST':
+ // TODO: Should verb handlers return anything?
+ await Post(req, res, jnode);
+ break;
+ case 'PUT':
+ // TODO: Handle PUT
+ break;
+ case 'DELETE':
+ // TODO: Handle DELETE
+ break;
+ case 'EXECUTE':
+ // TODO: Handle EXECUTE
+ break;
+ case 'MOVE':
+ // TODO: Handle MOVE
+ break;
+ case 'COPY':
+ // TODO: Handle COPY
+ break;
+ default:
+ // TODO: Return error
+ break;
+ }
+ res.end();
+});
+server.on('clientError', async (err, socket) => {
+ socket.end('HTTP/1.1 400 Bad Request\r\n\r\n');
+});
+
+// Start listening for requests.
+server.listen(7302);
diff --git a/lib/auth.mjs b/lib/auth.mjs
new file mode 100644
index 0000000..f0d02d5
--- /dev/null
+++ b/lib/auth.mjs
@@ -0,0 +1,123 @@
+/*
+JSFS
+Copyright (C) 2025 Jason J. Gullickson
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+*/
+
+import crypto from 'node:crypto';
+
+import { GetParam } from './utils.mjs';
+import { Jnode } from './jnode.mjs';
+
+export async function Auth(req, jnode) {
+
+ // Any read method is valid for a public file
+ // TODO: Can this be formatted in a more compact way?
+ if(!jnode.private && (req.method == 'HEAD' || req.method == 'GET' || req.method == 'EXECUTE')){
+ return true;
+ }
+
+ // Determine the jnode's accessKey
+ // TODO: Is this the right way to test for un-setness?
+ if(!jnode.accessKey){
+
+ // Crawl up the jspace looking for a directory
+
+ // Split jspace into parts.
+ const jspaceParts = jnode.jspace.split('/');
+
+ // Step through parts selecting the next directory (/).
+ for(const part of jspaceParts){
+
+ // peel-off the end of the space since we know
+ // there's no jnode there
+ jspaceParts.pop();
+
+ // Compute the next directory jspace.
+ const dirJspace = jspaceParts.join('/') + '/';
+
+ // Try to load the jnode for the directory.
+ const dirJnode = new Jnode(dirJspace);
+ try {
+ await dirJnode.Load();
+ if(dirJnode && dirJnode.accessKey){
+ // The new jnode inherits the accessKey
+ // of its parent directory.
+ jnode.accessKey = dirJnode.accessKey;
+ break;
+ }
+ } catch(e) {
+ // NOTE: Do nothing and see if we find it next time.
+ }
+ }
+
+ // TODO: If we get this far without loading a directory,
+ // something probably went wrong since a rood directory
+ // must exist...
+
+ // TODO: Should we persist the jnode at this point
+ // to preserve the newly-set accessKey? Could it serve
+ // as a cache to avoid all of the logic above?
+
+ }
+
+ // Get the key from the request.
+ const reqKey = await GetParam(req, 'access-key');
+
+ // If a key was found, validate it.
+ if(reqKey == jnode.accessKey){
+ return true;
+ }
+
+ // If no key was provided in the request, try a token
+ const reqToken = await GetParam(req, 'access-token');
+
+ // If no key or token are provided, do not authorize the request.
+ // TODO: There may be a better way to structure this so it falls-
+ // through to return false at the end of the function.
+ if(reqToken == null){
+ return false;
+ }
+
+ // Validate token.
+ const accessKey = jnode.accessKey;
+ const method = req.method;
+ var expectedToken = null;
+
+ // If the token is longer than the default hash, process it as expiring.
+ // TODO: Test token length against some configuration value that
+ // is specific to the hash function used instead of hard-coded to
+ // SHA1 length.
+ if(reqToken.length > 40){
+ const expires = reqToken.slice(40);
+
+ // If the token has expired, don't authorize the request.
+ const now = Math.floor(Date.now() / 1000);
+ if(expires < now){
+ return false;
+ }
+ expectedToken = `${crypto.hash('sha1', accessKey + method + expires)}${expires}`;
+ } else {
+ expectedToken = crypto.hash('sha1', accessKey + method);
+ }
+
+ if(reqToken == expectedToken){
+ return true;
+ }
+
+ // If all else fails, don't authorize the request.
+ return false;
+}
+
diff --git a/lib/blockdrivers/example.mjs b/lib/blockdrivers/example.mjs
new file mode 100644
index 0000000..3b7c80d
--- /dev/null
+++ b/lib/blockdrivers/example.mjs
@@ -0,0 +1,28 @@
+/*
+JSFS
+Copyright (C) 2025 Jason J. Gullickson
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+*/
+
+// TODO: Write example Blockdriver
+export async function Load(blockHash) {
+ // TODO: Implement everything.
+}
+export async function Store(blockHash, blockData) {
+ // TODO: Implement everything.
+}
+export async function Purge(blockHash) {
+ // TODO: Implement everything.
+}
diff --git a/lib/blockstore.mjs b/lib/blockstore.mjs
new file mode 100644
index 0000000..c04b7eb
--- /dev/null
+++ b/lib/blockstore.mjs
@@ -0,0 +1,79 @@
+/*
+JSFS
+Copyright (C) 2025 Jason J. Gullickson
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+*/
+
+import crypto from 'node:crypto';
+import { open, writeFile, rm } from 'node:fs/promises';
+
+// TODO: Move this to config.
+const _BLOCK_SIZE = 1024;
+
+export async function Store(blockHash, blockData) {
+
+ // NOTE: This is where things like mirror/stripe/federation happen.
+
+ // TODO: Call blockdrivers to get block data instead of
+ // writing it directly to disk as is done here.
+ // TODO: Result is null unless something goes wrong, so maybe I
+ // should be testing result and dealing with the fallout constructively?
+ // Alternatively, this might get used for raid/retry logics?
+ // TODO: Maybe calling this w/o await is enough to kick-off
+ // the write asyncronously and get us back to the caller quickly?
+ const result = await writeFile(
+ `/home/jason/Projects/jsfs/test/testdata/${blockHash}.jblock`,
+ blockData
+ );
+}
+
+export async function Load(blockHash) {
+
+ // NOTE: This is where things like mirror/stripe/federation happen.
+
+ // TODO: Call blockdrivers to get block data instead of
+ // loading it directly from disk as is done here.
+ const blockFile = await open(
+ `/home/jason/Projects/jsfs/test/testdata/${blockHash}.jblock`
+ );
+
+ // NOTE: by setting size to _BLOCK_SIZE, this reads the entire
+ // block in one go. It feels kind of dumb to do this in a loop,
+ // and it might not work great for large block sizes, so this
+ // may need to be revisited at some point.
+ let blockData = new Uint8Array(_BLOCK_SIZE);
+ for await (const chunk of blockFile.readableWebStream(
+ {size: _BLOCK_SIZE}
+ )) {
+ blockData = chunk;
+ }
+ await blockFile.close();
+
+ return blockData;
+}
+
+export async function Purge(blockHash){
+
+ // TODO: Again, delegate this to the blockdrivers.
+ // TODO: Don't blow up if the file doesn't exist.
+ try{
+ await rm(
+ `/home/jason/Projects/jsfs/test/testdata/${blockHash}.jblock`
+ );
+ } catch(e) {
+ // TODO: Make sure this is ENOENT (other exceptions might
+ // be worth handling).
+ }
+}
diff --git a/lib/jnode.mjs b/lib/jnode.mjs
new file mode 100644
index 0000000..f3308ec
--- /dev/null
+++ b/lib/jnode.mjs
@@ -0,0 +1,102 @@
+/*
+JSFS
+Copyright (C) 2025 Jason J. Gullickson
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+*/
+
+import crypto from 'node:crypto';
+
+import { Load, Store } from './blockstore.mjs';
+
+// A jnode contains file metadata and a list of blocks representing file data
+class Jnode {
+ constructor(jspace) {
+ this.jspace = jspace;
+ this.created = (new Date()).getTime();
+ this.version = 0;
+ this.private = false;
+ this.fingerprint = null;
+ this.accessKey = null;
+ this.contentType = "application/octet-stream";
+ this.fileSize = 0;
+ this.blockSize = 0;
+
+ // NOTE: I had switched blocks to an object because I've always been
+ // concerned about the order of an array changing as it does in
+ // some languages. After doing some reading it seems that ES6
+ // treats arrays more like objects and therefore the order shouldn't
+ // change. I still don't trust it but it's worked so far so I'll
+ // leave it this way until I can prove that it's a problem.
+ this.blocks = [];
+
+ // create fingerprint to uniquely identify this file
+ // TODO: Do we need the fingerprint? What is it used for?
+ this.fingerprint = crypto.hash('sha1', this.jspace);
+
+ // use fingerprint as default key
+ // TODO: This was originally a convenience, but now that
+ // we're going to have directory-level permissions I think
+ // it's probably going to go away since every request will
+ // need to provide *some* sort of key or token.
+ // TODO: Should this maybe be salted so it's more secure?
+ //this.accessKey = this.fingerprint;
+ }
+ async Load(){
+ // Try to load the jnode from the blockstore.
+ //const blockHash = this.fingerprint;
+ let result = null;
+ try {
+ result = await Load(this.fingerprint);
+ } catch(e){
+ // NOTE: Let the caller decide if this is OK.
+ return e;
+ }
+
+ // TODO: Do I really need all this decoding?
+ const decoder = new TextDecoder();
+ const resultString = decoder.decode(result);
+ const resultObject = JSON.parse(resultString);
+
+ // Parse result from blockstore and assign properties.
+ // TODO: Look into a more streamlined way to unpack this.
+ this.created = resultObject.created;
+ this.version = resultObject.version;
+ this.private = resultObject.private;
+ this.fingerprint = resultObject.fingerprint;
+ this.accessKey = resultObject.accessKey;
+ this.contentType = resultObject.contentType;
+ this.fileSize = resultObject.fileSize;
+ this.blockSize = resultObject.blockSize;
+ this.blocks = resultObject.blocks;
+
+ return null;
+ }
+ async Save(){
+ // Save this jnode to the blockstore,
+ // if something goes wrong, return the error
+ // (or maybe just throw an exception?).
+
+ const blockData = JSON.stringify(this);
+ const blockHash = this.fingerprint;
+
+ // TODO: Do we *need* to await here?
+ await Store(blockHash, blockData);
+
+ return null;
+ }
+}
+
+export { Jnode };
+
diff --git a/lib/metadrivers/example.mjs b/lib/metadrivers/example.mjs
new file mode 100644
index 0000000..e89e468
--- /dev/null
+++ b/lib/metadrivers/example.mjs
@@ -0,0 +1,22 @@
+/*
+JSFS
+Copyright (C) 2025 Jason J. Gullickson
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+*/
+
+// TODO: Write example metadriver
+export async function Extract(blockData) {
+ // TODO: Implement everything.
+}
diff --git a/lib/utils.mjs b/lib/utils.mjs
new file mode 100644
index 0000000..794eff7
--- /dev/null
+++ b/lib/utils.mjs
@@ -0,0 +1,50 @@
+/*
+JSFS
+Copyright (C) 2025 Jason J. Gullickson
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+*/
+
+import url from 'node:url';
+
+
+export async function GetJspace(hostname, uri) {
+
+ // TODO: This is mostly forklifted from JSFS4,
+ // so it might benefit from refactoring.
+
+ var parsed = url.parse(uri);
+ var pathname = parsed.pathname;
+ hostname = hostname.split(":")[0];
+
+ if (pathname.substring(0,2) !== "/.") {
+ return "/" + hostname.split(".").reverse().join(".") + pathname;
+ } else {
+ return "/" + pathname.substring(2);
+ }
+};
+
+export async function GetParam(req, param){
+
+ // TODO: Use the real hostname, etc. not this hard-coded thing.
+ const url = new URL(req.url, 'http://localhost:7302/');
+
+ const queryParam = url.searchParams.get(param);
+ const headerParam = req.headers[`x-jsfs-${param}`];
+
+ // Query parameters override headers (favor visible over invisible).
+ const reqParam = queryParam == null ? headerParam : queryParam;
+
+ return reqParam;
+}
diff --git a/lib/verbs/copy.mjs b/lib/verbs/copy.mjs
new file mode 100644
index 0000000..41c2cb2
--- /dev/null
+++ b/lib/verbs/copy.mjs
@@ -0,0 +1,23 @@
+/*
+JSFS
+Copyright (C) 2025 Jason J. Gullickson
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+*/
+
+
+export async function Copy(req, res, jnode) {
+ // TODO: Implement everything.
+}
+
diff --git a/lib/verbs/delete.mjs b/lib/verbs/delete.mjs
new file mode 100644
index 0000000..e110c3b
--- /dev/null
+++ b/lib/verbs/delete.mjs
@@ -0,0 +1,23 @@
+/*
+JSFS
+Copyright (C) 2025 Jason J. Gullickson
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+*/
+
+
+export async function Delete(req, res, jnode) {
+ // TODO: Implement everything.
+}
+
diff --git a/lib/verbs/execute.mjs b/lib/verbs/execute.mjs
new file mode 100644
index 0000000..558f536
--- /dev/null
+++ b/lib/verbs/execute.mjs
@@ -0,0 +1,23 @@
+/*
+JSFS
+Copyright (C) 2025 Jason J. Gullickson
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+*/
+
+
+export async function Execute(req, res, jnode) {
+ // TODO: Implement everything.
+}
+
diff --git a/lib/verbs/get.mjs b/lib/verbs/get.mjs
new file mode 100644
index 0000000..d634142
--- /dev/null
+++ b/lib/verbs/get.mjs
@@ -0,0 +1,24 @@
+/*
+JSFS
+Copyright (C) 2025 Jason J. Gullickson
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+*/
+
+import { Jnode } from '../jnode.mjs';
+
+export async function Get(req, res, jnode) {
+ // TODO: Implement everything
+ res.status = 200;
+}
diff --git a/lib/verbs/head.mjs b/lib/verbs/head.mjs
new file mode 100644
index 0000000..68a3b38
--- /dev/null
+++ b/lib/verbs/head.mjs
@@ -0,0 +1,29 @@
+/*
+JSFS
+Copyright (C) 2025 Jason J. Gullickson
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+*/
+
+import { Jnode } from '../jnode.mjs';
+
+export async function Head(req, res, jnode) {
+ // TODO: Test the jnode to see if it was loaded,
+ // if so, set the status to 200 and return the meatadata.
+ // If not, just set the status to 404.
+ res.writeHead(200, {
+ 'x-jsfs-version': jnode.version,
+ });
+}
+
diff --git a/lib/verbs/move.mjs b/lib/verbs/move.mjs
new file mode 100644
index 0000000..f1c4b2f
--- /dev/null
+++ b/lib/verbs/move.mjs
@@ -0,0 +1,23 @@
+/*
+JSFS
+Copyright (C) 2025 Jason J. Gullickson
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+*/
+
+
+export async function Move(req, res, jnode) {
+ // TODO: Implement everything.
+}
+
diff --git a/lib/verbs/post.mjs b/lib/verbs/post.mjs
new file mode 100644
index 0000000..b1c0d31
--- /dev/null
+++ b/lib/verbs/post.mjs
@@ -0,0 +1,51 @@
+/*
+JSFS
+Copyright (C) 2025 Jason J. Gullickson
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+*/
+
+import crypto from 'node:crypto';
+import { Buffer } from 'node:buffer';
+
+import { GetParam } from '../utils.mjs';
+import { Jnode } from '../jnode.mjs';
+
+
+export async function Post(req, res, jnode) {
+
+ // TODO: Figure out if this will still work with a real HTTP request.
+ for await (const chunk of req.readableWebStream()) {
+
+ // TODO: Slice whole chunks (of unknown size) down to fit into
+ // the configured block size. This code cheats for now.
+ const blockData = Buffer.from(chunk);
+
+ // Compute a name for the block by hashing it.
+ // TODO: Move this into blockstore.
+ const blockName = crypto.hash('sha1', blockData);
+
+ // TODO: Send the chunk to the blockstore.
+
+ // Add the name of each block to the jnode.
+ jnode.blocks.push(blockName);
+ }
+
+ // TODO: Update any other jnode properties that make sense.
+ // TODO: Save the updated jnode.
+ // TODO: Update the directory.
+
+ // TODO: Set the status to reflect the actual result of POST processing
+ res.status = 200;
+}
diff --git a/lib/verbs/put.mjs b/lib/verbs/put.mjs
new file mode 100644
index 0000000..96c07aa
--- /dev/null
+++ b/lib/verbs/put.mjs
@@ -0,0 +1,25 @@
+/*
+JSFS
+Copyright (C) 2025 Jason J. Gullickson
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+*/
+
+
+export async function Put(req, res, jnode) {
+
+ // TODO: Increment version.
+ // TODO: Implement everything else.
+}
+
diff --git a/package-lock.json b/package-lock.json
index 62ecb83..2dda07d 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,44 +1,364 @@
{
"name": "jsfs",
- "version": "4.2.0",
- "lockfileVersion": 2,
+ "version": "5.0.0",
+ "lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "jsfs",
- "version": "4.2.0",
- "license": "GPL-3.0",
+ "version": "5.0.0",
+ "license": "GPL-3.0-or-later",
+ "devDependencies": {
+ "@eslint/js": "^9.17.0",
+ "eslint": "^9.17.0",
+ "mocha": "^11.0.1"
+ }
+ },
+ "node_modules/@eslint-community/eslint-utils": {
+ "version": "4.4.1",
+ "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.1.tgz",
+ "integrity": "sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA==",
+ "dev": true,
+ "license": "MIT",
"dependencies": {
- "through": "2.3.8"
+ "eslint-visitor-keys": "^3.4.3"
},
- "devDependencies": {
- "chai": "^3.5.0",
- "mocha": "^9.1.3",
- "mock-fs": "^4.0.0-beta.1"
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0"
+ }
+ },
+ "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": {
+ "version": "3.4.3",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
+ "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/@eslint-community/regexpp": {
+ "version": "4.12.1",
+ "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz",
+ "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^12.0.0 || ^14.0.0 || >=16.0.0"
+ }
+ },
+ "node_modules/@eslint/config-array": {
+ "version": "0.19.1",
+ "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.19.1.tgz",
+ "integrity": "sha512-fo6Mtm5mWyKjA/Chy1BYTdn5mGJoDNjC7C64ug20ADsRDGrA85bN3uK3MaKbeRkRuuIEAR5N33Jr1pbm411/PA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@eslint/object-schema": "^2.1.5",
+ "debug": "^4.3.1",
+ "minimatch": "^3.1.2"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@eslint/config-array/node_modules/brace-expansion": {
+ "version": "1.1.11",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "node_modules/@eslint/config-array/node_modules/minimatch": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/@eslint/core": {
+ "version": "0.9.1",
+ "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.9.1.tgz",
+ "integrity": "sha512-GuUdqkyyzQI5RMIWkHhvTWLCyLo1jNK3vzkSyaExH5kHPDHcuL2VOpHjmMY+y3+NC69qAKToBqldTBgYeLSr9Q==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@types/json-schema": "^7.0.15"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@eslint/eslintrc": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.2.0.tgz",
+ "integrity": "sha512-grOjVNN8P3hjJn/eIETF1wwd12DdnwFDoyceUJLYYdkpbwq3nLi+4fqrTAONx7XDALqlL220wC/RHSC/QTI/0w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ajv": "^6.12.4",
+ "debug": "^4.3.2",
+ "espree": "^10.0.1",
+ "globals": "^14.0.0",
+ "ignore": "^5.2.0",
+ "import-fresh": "^3.2.1",
+ "js-yaml": "^4.1.0",
+ "minimatch": "^3.1.2",
+ "strip-json-comments": "^3.1.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/@eslint/eslintrc/node_modules/brace-expansion": {
+ "version": "1.1.11",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "node_modules/@eslint/eslintrc/node_modules/minimatch": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/@eslint/js": {
+ "version": "9.17.0",
+ "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.17.0.tgz",
+ "integrity": "sha512-Sxc4hqcs1kTu0iID3kcZDW3JHq2a77HO9P8CP6YEA/FpH3Ll8UXE2r/86Rz9YJLKme39S9vU5OWNjC6Xl0Cr3w==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@eslint/object-schema": {
+ "version": "2.1.5",
+ "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.5.tgz",
+ "integrity": "sha512-o0bhxnL89h5Bae5T318nFoFzGy+YE5i/gGkoPAgkmTVdRKTiv3p8JHevPiPaMwoloKfEiiaHlawCqaZMqRm+XQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@eslint/plugin-kit": {
+ "version": "0.2.4",
+ "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.4.tgz",
+ "integrity": "sha512-zSkKow6H5Kdm0ZUQUB2kV5JIXqoG0+uH5YADhaEHswm664N9Db8dXSi0nMJpacpMf+MyyglF1vnZohpEg5yUtg==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "levn": "^0.4.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@humanfs/core": {
+ "version": "0.19.1",
+ "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz",
+ "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=18.18.0"
+ }
+ },
+ "node_modules/@humanfs/node": {
+ "version": "0.16.6",
+ "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz",
+ "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@humanfs/core": "^0.19.1",
+ "@humanwhocodes/retry": "^0.3.0"
+ },
+ "engines": {
+ "node": ">=18.18.0"
+ }
+ },
+ "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": {
+ "version": "0.3.1",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz",
+ "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=18.18"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/nzakas"
+ }
+ },
+ "node_modules/@humanwhocodes/module-importer": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz",
+ "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=12.22"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/nzakas"
+ }
+ },
+ "node_modules/@humanwhocodes/retry": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.1.tgz",
+ "integrity": "sha512-c7hNEllBlenFTHBky65mhq8WD2kbN9Q6gk0bTk8lSBvc554jpXSkST1iePudpt7+A/AQvuHs9EMqjHDXMY1lrA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=18.18"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/nzakas"
+ }
+ },
+ "node_modules/@isaacs/cliui": {
+ "version": "8.0.2",
+ "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz",
+ "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "string-width": "^5.1.2",
+ "string-width-cjs": "npm:string-width@^4.2.0",
+ "strip-ansi": "^7.0.1",
+ "strip-ansi-cjs": "npm:strip-ansi@^6.0.1",
+ "wrap-ansi": "^8.1.0",
+ "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@pkgjs/parseargs": {
+ "version": "0.11.0",
+ "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
+ "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "engines": {
+ "node": ">=14"
+ }
+ },
+ "node_modules/@types/estree": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz",
+ "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/json-schema": {
+ "version": "7.0.15",
+ "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
+ "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/acorn": {
+ "version": "8.14.0",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz",
+ "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "acorn": "bin/acorn"
+ },
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/acorn-jsx": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
+ "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
+ "dev": true,
+ "license": "MIT",
+ "peerDependencies": {
+ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
}
},
- "node_modules/@ungap/promise-all-settled": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz",
- "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==",
- "dev": true
+ "node_modules/ajv": {
+ "version": "6.12.6",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
+ "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fast-deep-equal": "^3.1.1",
+ "fast-json-stable-stringify": "^2.0.0",
+ "json-schema-traverse": "^0.4.1",
+ "uri-js": "^4.2.2"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/epoberezkin"
+ }
},
"node_modules/ansi-colors": {
- "version": "4.1.1",
- "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz",
- "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==",
+ "version": "4.1.3",
+ "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz",
+ "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==",
"dev": true,
+ "license": "MIT",
"engines": {
"node": ">=6"
}
},
"node_modules/ansi-regex": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
- "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz",
+ "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==",
"dev": true,
+ "license": "MIT",
"engines": {
- "node": ">=8"
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-regex?sponsor=1"
}
},
"node_modules/ansi-styles": {
@@ -46,6 +366,7 @@
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"color-convert": "^2.0.1"
},
@@ -57,10 +378,11 @@
}
},
"node_modules/anymatch": {
- "version": "3.1.2",
- "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz",
- "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==",
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
+ "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
"dev": true,
+ "license": "ISC",
"dependencies": {
"normalize-path": "^3.0.0",
"picomatch": "^2.0.4"
@@ -73,49 +395,47 @@
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
- "dev": true
- },
- "node_modules/assertion-error": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz",
- "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==",
"dev": true,
- "engines": {
- "node": "*"
- }
+ "license": "Python-2.0"
},
"node_modules/balanced-match": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
- "dev": true
+ "dev": true,
+ "license": "MIT"
},
"node_modules/binary-extensions": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
- "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==",
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
+ "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==",
"dev": true,
+ "license": "MIT",
"engines": {
"node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/brace-expansion": {
- "version": "1.1.11",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
- "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
+ "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
"dev": true,
+ "license": "MIT",
"dependencies": {
- "balanced-match": "^1.0.0",
- "concat-map": "0.0.1"
+ "balanced-match": "^1.0.0"
}
},
"node_modules/braces": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
- "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
+ "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
"dev": true,
+ "license": "MIT",
"dependencies": {
- "fill-range": "^7.0.1"
+ "fill-range": "^7.1.1"
},
"engines": {
"node": ">=8"
@@ -125,13 +445,25 @@
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz",
"integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==",
- "dev": true
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/callsites": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
+ "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
},
"node_modules/camelcase": {
- "version": "6.2.1",
- "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.1.tgz",
- "integrity": "sha512-tVI4q5jjFV5CavAU8DXfza/TJcZutVKo/5Foskmsqcm0MsL91moHvwiGNnqaa2o6PF/7yT5ikDRcVcl8Rj6LCA==",
+ "version": "6.3.0",
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz",
+ "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==",
"dev": true,
+ "license": "MIT",
"engines": {
"node": ">=10"
},
@@ -139,25 +471,12 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
- "node_modules/chai": {
- "version": "3.5.0",
- "resolved": "https://registry.npmjs.org/chai/-/chai-3.5.0.tgz",
- "integrity": "sha1-TQJjewZ/6Vi9v906QOxW/vc3Mkc=",
- "dev": true,
- "dependencies": {
- "assertion-error": "^1.0.1",
- "deep-eql": "^0.1.3",
- "type-detect": "^1.0.0"
- },
- "engines": {
- "node": ">= 0.4.0"
- }
- },
"node_modules/chalk": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
@@ -174,6 +493,7 @@
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"has-flag": "^4.0.0"
},
@@ -182,10 +502,11 @@
}
},
"node_modules/chokidar": {
- "version": "3.5.2",
- "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz",
- "integrity": "sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==",
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
+ "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"anymatch": "~3.1.2",
"braces": "~3.0.2",
@@ -198,6 +519,9 @@
"engines": {
"node": ">= 8.10.0"
},
+ "funding": {
+ "url": "https://paulmillr.com/funding/"
+ },
"optionalDependencies": {
"fsevents": "~2.3.2"
}
@@ -207,17 +531,82 @@
"resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz",
"integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==",
"dev": true,
+ "license": "ISC",
"dependencies": {
"string-width": "^4.2.0",
"strip-ansi": "^6.0.0",
"wrap-ansi": "^7.0.0"
}
},
+ "node_modules/cliui/node_modules/ansi-regex": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/cliui/node_modules/emoji-regex": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/cliui/node_modules/string-width": {
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+ "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/cliui/node_modules/strip-ansi": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/cliui/node_modules/wrap-ansi": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
+ "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^4.0.0",
+ "string-width": "^4.1.0",
+ "strip-ansi": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+ }
+ },
"node_modules/color-convert": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"color-name": "~1.1.4"
},
@@ -229,21 +618,39 @@
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
- "dev": true
+ "dev": true,
+ "license": "MIT"
},
"node_modules/concat-map": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
- "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
- "dev": true
+ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/cross-spawn": {
+ "version": "7.0.6",
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
+ "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "path-key": "^3.1.0",
+ "shebang-command": "^2.0.0",
+ "which": "^2.0.1"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
},
"node_modules/debug": {
- "version": "4.3.2",
- "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz",
- "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==",
+ "version": "4.4.0",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz",
+ "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==",
"dev": true,
+ "license": "MIT",
"dependencies": {
- "ms": "2.1.2"
+ "ms": "^2.1.3"
},
"engines": {
"node": ">=6.0"
@@ -254,17 +661,12 @@
}
}
},
- "node_modules/debug/node_modules/ms": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
- "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
- "dev": true
- },
"node_modules/decamelize": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz",
"integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==",
"dev": true,
+ "license": "MIT",
"engines": {
"node": ">=10"
},
@@ -272,47 +674,43 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
- "node_modules/deep-eql": {
- "version": "0.1.3",
- "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-0.1.3.tgz",
- "integrity": "sha1-71WKyrjeJSBs1xOQbXTlaTDrafI=",
+ "node_modules/deep-is": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
+ "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==",
"dev": true,
- "dependencies": {
- "type-detect": "0.1.1"
- },
- "engines": {
- "node": "*"
- }
- },
- "node_modules/deep-eql/node_modules/type-detect": {
- "version": "0.1.1",
- "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-0.1.1.tgz",
- "integrity": "sha1-C6XsKohWQORw6k6FBZcZANrFiCI=",
- "dev": true,
- "engines": {
- "node": "*"
- }
+ "license": "MIT"
},
"node_modules/diff": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz",
- "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==",
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz",
+ "integrity": "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==",
"dev": true,
+ "license": "BSD-3-Clause",
"engines": {
"node": ">=0.3.1"
}
},
+ "node_modules/eastasianwidth": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
+ "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/emoji-regex": {
- "version": "8.0.0",
- "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
- "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
- "dev": true
+ "version": "9.2.2",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
+ "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==",
+ "dev": true,
+ "license": "MIT"
},
"node_modules/escalade": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
- "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==",
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
+ "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
"dev": true,
+ "license": "MIT",
"engines": {
"node": ">=6"
}
@@ -322,6 +720,7 @@
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
"integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
"dev": true,
+ "license": "MIT",
"engines": {
"node": ">=10"
},
@@ -329,11 +728,237 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/eslint": {
+ "version": "9.17.0",
+ "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.17.0.tgz",
+ "integrity": "sha512-evtlNcpJg+cZLcnVKwsai8fExnqjGPicK7gnUtlNuzu+Fv9bI0aLpND5T44VLQtoMEnI57LoXO9XAkIXwohKrA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@eslint-community/eslint-utils": "^4.2.0",
+ "@eslint-community/regexpp": "^4.12.1",
+ "@eslint/config-array": "^0.19.0",
+ "@eslint/core": "^0.9.0",
+ "@eslint/eslintrc": "^3.2.0",
+ "@eslint/js": "9.17.0",
+ "@eslint/plugin-kit": "^0.2.3",
+ "@humanfs/node": "^0.16.6",
+ "@humanwhocodes/module-importer": "^1.0.1",
+ "@humanwhocodes/retry": "^0.4.1",
+ "@types/estree": "^1.0.6",
+ "@types/json-schema": "^7.0.15",
+ "ajv": "^6.12.4",
+ "chalk": "^4.0.0",
+ "cross-spawn": "^7.0.6",
+ "debug": "^4.3.2",
+ "escape-string-regexp": "^4.0.0",
+ "eslint-scope": "^8.2.0",
+ "eslint-visitor-keys": "^4.2.0",
+ "espree": "^10.3.0",
+ "esquery": "^1.5.0",
+ "esutils": "^2.0.2",
+ "fast-deep-equal": "^3.1.3",
+ "file-entry-cache": "^8.0.0",
+ "find-up": "^5.0.0",
+ "glob-parent": "^6.0.2",
+ "ignore": "^5.2.0",
+ "imurmurhash": "^0.1.4",
+ "is-glob": "^4.0.0",
+ "json-stable-stringify-without-jsonify": "^1.0.1",
+ "lodash.merge": "^4.6.2",
+ "minimatch": "^3.1.2",
+ "natural-compare": "^1.4.0",
+ "optionator": "^0.9.3"
+ },
+ "bin": {
+ "eslint": "bin/eslint.js"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://eslint.org/donate"
+ },
+ "peerDependencies": {
+ "jiti": "*"
+ },
+ "peerDependenciesMeta": {
+ "jiti": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/eslint-scope": {
+ "version": "8.2.0",
+ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.2.0.tgz",
+ "integrity": "sha512-PHlWUfG6lvPc3yvP5A4PNyBL1W8fkDUccmI21JUu/+GKZBoH/W5u6usENXUrWFRsyoW5ACUjFGgAFQp5gUlb/A==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "esrecurse": "^4.3.0",
+ "estraverse": "^5.2.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/eslint-visitor-keys": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz",
+ "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/eslint/node_modules/brace-expansion": {
+ "version": "1.1.11",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "node_modules/eslint/node_modules/glob-parent": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
+ "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "is-glob": "^4.0.3"
+ },
+ "engines": {
+ "node": ">=10.13.0"
+ }
+ },
+ "node_modules/eslint/node_modules/minimatch": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/espree": {
+ "version": "10.3.0",
+ "resolved": "https://registry.npmjs.org/espree/-/espree-10.3.0.tgz",
+ "integrity": "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "acorn": "^8.14.0",
+ "acorn-jsx": "^5.3.2",
+ "eslint-visitor-keys": "^4.2.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/esquery": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz",
+ "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "estraverse": "^5.1.0"
+ },
+ "engines": {
+ "node": ">=0.10"
+ }
+ },
+ "node_modules/esrecurse": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
+ "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "estraverse": "^5.2.0"
+ },
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/estraverse": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
+ "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/esutils": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
+ "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/fast-deep-equal": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/fast-json-stable-stringify": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
+ "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/fast-levenshtein": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
+ "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/file-entry-cache": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz",
+ "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "flat-cache": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=16.0.0"
+ }
+ },
"node_modules/fill-range": {
- "version": "7.0.1",
- "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
- "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
+ "version": "7.1.1",
+ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
+ "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"to-regex-range": "^5.0.1"
},
@@ -346,6 +971,7 @@
"resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
"integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"locate-path": "^6.0.0",
"path-exists": "^4.0.0"
@@ -362,22 +988,56 @@
"resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz",
"integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==",
"dev": true,
+ "license": "BSD-3-Clause",
"bin": {
"flat": "cli.js"
}
},
- "node_modules/fs.realpath": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
- "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
- "dev": true
+ "node_modules/flat-cache": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz",
+ "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "flatted": "^3.2.9",
+ "keyv": "^4.5.4"
+ },
+ "engines": {
+ "node": ">=16"
+ }
+ },
+ "node_modules/flatted": {
+ "version": "3.3.2",
+ "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.2.tgz",
+ "integrity": "sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/foreground-child": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz",
+ "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "cross-spawn": "^7.0.0",
+ "signal-exit": "^4.0.1"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
},
"node_modules/fsevents": {
- "version": "2.3.2",
- "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
- "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
+ "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
"dev": true,
"hasInstallScript": true,
+ "license": "MIT",
"optional": true,
"os": [
"darwin"
@@ -391,25 +1051,27 @@
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
"integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
"dev": true,
+ "license": "ISC",
"engines": {
"node": "6.* || 8.* || >= 10.*"
}
},
"node_modules/glob": {
- "version": "7.1.7",
- "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz",
- "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==",
+ "version": "10.4.5",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz",
+ "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==",
"dev": true,
+ "license": "ISC",
"dependencies": {
- "fs.realpath": "^1.0.0",
- "inflight": "^1.0.4",
- "inherits": "2",
- "minimatch": "^3.0.4",
- "once": "^1.3.0",
- "path-is-absolute": "^1.0.0"
+ "foreground-child": "^3.1.0",
+ "jackspeak": "^3.1.2",
+ "minimatch": "^9.0.4",
+ "minipass": "^7.1.2",
+ "package-json-from-dist": "^1.0.0",
+ "path-scurry": "^1.11.1"
},
- "engines": {
- "node": "*"
+ "bin": {
+ "glob": "dist/esm/bin.mjs"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
@@ -420,6 +1082,7 @@
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
"integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
"dev": true,
+ "license": "ISC",
"dependencies": {
"is-glob": "^4.0.1"
},
@@ -427,13 +1090,33 @@
"node": ">= 6"
}
},
- "node_modules/growl": {
- "version": "1.10.5",
- "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz",
- "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==",
+ "node_modules/glob/node_modules/minimatch": {
+ "version": "9.0.5",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
+ "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/globals": {
+ "version": "14.0.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz",
+ "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==",
"dev": true,
+ "license": "MIT",
"engines": {
- "node": ">=4.x"
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/has-flag": {
@@ -441,6 +1124,7 @@
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
"dev": true,
+ "license": "MIT",
"engines": {
"node": ">=8"
}
@@ -450,31 +1134,54 @@
"resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
"integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==",
"dev": true,
+ "license": "MIT",
"bin": {
"he": "bin/he"
}
},
- "node_modules/inflight": {
- "version": "1.0.6",
- "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
- "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
+ "node_modules/ignore": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
+ "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==",
"dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 4"
+ }
+ },
+ "node_modules/import-fresh": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
+ "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==",
+ "dev": true,
+ "license": "MIT",
"dependencies": {
- "once": "^1.3.0",
- "wrappy": "1"
+ "parent-module": "^1.0.0",
+ "resolve-from": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
}
},
- "node_modules/inherits": {
- "version": "2.0.4",
- "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
- "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
- "dev": true
+ "node_modules/imurmurhash": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
+ "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.8.19"
+ }
},
"node_modules/is-binary-path": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
"integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"binary-extensions": "^2.0.0"
},
@@ -485,8 +1192,9 @@
"node_modules/is-extglob": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
- "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=",
+ "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
"dev": true,
+ "license": "MIT",
"engines": {
"node": ">=0.10.0"
}
@@ -496,6 +1204,7 @@
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
"dev": true,
+ "license": "MIT",
"engines": {
"node": ">=8"
}
@@ -505,6 +1214,7 @@
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
"integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"is-extglob": "^2.1.1"
},
@@ -517,6 +1227,7 @@
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
"dev": true,
+ "license": "MIT",
"engines": {
"node": ">=0.12.0"
}
@@ -526,6 +1237,7 @@
"resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz",
"integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==",
"dev": true,
+ "license": "MIT",
"engines": {
"node": ">=8"
}
@@ -535,6 +1247,7 @@
"resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz",
"integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==",
"dev": true,
+ "license": "MIT",
"engines": {
"node": ">=10"
},
@@ -545,14 +1258,32 @@
"node_modules/isexe": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
- "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=",
- "dev": true
+ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/jackspeak": {
+ "version": "3.4.3",
+ "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz",
+ "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==",
+ "dev": true,
+ "license": "BlueOak-1.0.0",
+ "dependencies": {
+ "@isaacs/cliui": "^8.0.2"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ },
+ "optionalDependencies": {
+ "@pkgjs/parseargs": "^0.11.0"
+ }
},
"node_modules/js-yaml": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
"integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"argparse": "^2.0.1"
},
@@ -560,11 +1291,57 @@
"js-yaml": "bin/js-yaml.js"
}
},
+ "node_modules/json-buffer": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
+ "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/json-schema-traverse": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/json-stable-stringify-without-jsonify": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
+ "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/keyv": {
+ "version": "4.5.4",
+ "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
+ "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "json-buffer": "3.0.1"
+ }
+ },
+ "node_modules/levn": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
+ "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "prelude-ls": "^1.2.1",
+ "type-check": "~0.4.0"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
"node_modules/locate-path": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
"integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"p-locate": "^5.0.0"
},
@@ -575,11 +1352,19 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/lodash.merge": {
+ "version": "4.6.2",
+ "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
+ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/log-symbols": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz",
"integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"chalk": "^4.1.0",
"is-unicode-supported": "^0.1.0"
@@ -591,101 +1376,112 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/lru-cache": {
+ "version": "10.4.3",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz",
+ "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==",
+ "dev": true,
+ "license": "ISC"
+ },
"node_modules/minimatch": {
- "version": "3.0.4",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
- "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
+ "version": "5.1.6",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz",
+ "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==",
"dev": true,
+ "license": "ISC",
"dependencies": {
- "brace-expansion": "^1.1.7"
+ "brace-expansion": "^2.0.1"
},
"engines": {
- "node": "*"
+ "node": ">=10"
+ }
+ },
+ "node_modules/minipass": {
+ "version": "7.1.2",
+ "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz",
+ "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
}
},
"node_modules/mocha": {
- "version": "9.1.3",
- "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.1.3.tgz",
- "integrity": "sha512-Xcpl9FqXOAYqI3j79pEtHBBnQgVXIhpULjGQa7DVb0Po+VzmSIK9kanAiWLHoRR/dbZ2qpdPshuXr8l1VaHCzw==",
- "dev": true,
- "dependencies": {
- "@ungap/promise-all-settled": "1.1.2",
- "ansi-colors": "4.1.1",
- "browser-stdout": "1.3.1",
- "chokidar": "3.5.2",
- "debug": "4.3.2",
- "diff": "5.0.0",
- "escape-string-regexp": "4.0.0",
- "find-up": "5.0.0",
- "glob": "7.1.7",
- "growl": "1.10.5",
- "he": "1.2.0",
- "js-yaml": "4.1.0",
- "log-symbols": "4.1.0",
- "minimatch": "3.0.4",
- "ms": "2.1.3",
- "nanoid": "3.1.25",
- "serialize-javascript": "6.0.0",
- "strip-json-comments": "3.1.1",
- "supports-color": "8.1.1",
- "which": "2.0.2",
- "workerpool": "6.1.5",
- "yargs": "16.2.0",
- "yargs-parser": "20.2.4",
- "yargs-unparser": "2.0.0"
+ "version": "11.0.1",
+ "resolved": "https://registry.npmjs.org/mocha/-/mocha-11.0.1.tgz",
+ "integrity": "sha512-+3GkODfsDG71KSCQhc4IekSW+ItCK/kiez1Z28ksWvYhKXV/syxMlerR/sC7whDp7IyreZ4YxceMLdTs5hQE8A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-colors": "^4.1.3",
+ "browser-stdout": "^1.3.1",
+ "chokidar": "^3.5.3",
+ "debug": "^4.3.5",
+ "diff": "^5.2.0",
+ "escape-string-regexp": "^4.0.0",
+ "find-up": "^5.0.0",
+ "glob": "^10.4.5",
+ "he": "^1.2.0",
+ "js-yaml": "^4.1.0",
+ "log-symbols": "^4.1.0",
+ "minimatch": "^5.1.6",
+ "ms": "^2.1.3",
+ "serialize-javascript": "^6.0.2",
+ "strip-json-comments": "^3.1.1",
+ "supports-color": "^8.1.1",
+ "workerpool": "^6.5.1",
+ "yargs": "^16.2.0",
+ "yargs-parser": "^20.2.9",
+ "yargs-unparser": "^2.0.0"
},
"bin": {
"_mocha": "bin/_mocha",
- "mocha": "bin/mocha"
+ "mocha": "bin/mocha.js"
},
"engines": {
- "node": ">= 12.0.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/mochajs"
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
}
},
- "node_modules/mock-fs": {
- "version": "4.14.0",
- "resolved": "https://registry.npmjs.org/mock-fs/-/mock-fs-4.14.0.tgz",
- "integrity": "sha512-qYvlv/exQ4+svI3UOvPUpLDF0OMX5euvUH0Ny4N5QyRyhNdgAgUrVH3iUINSzEPLvx0kbo/Bp28GJKIqvE7URw==",
- "dev": true
- },
"node_modules/ms": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
- "dev": true
+ "dev": true,
+ "license": "MIT"
},
- "node_modules/nanoid": {
- "version": "3.1.25",
- "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.25.tgz",
- "integrity": "sha512-rdwtIXaXCLFAQbnfqDRnI6jaRHp9fTcYBjtFKE8eezcZ7LuLjhUaQGNeMXf1HmRoCH32CLz6XwX0TtxEOS/A3Q==",
+ "node_modules/natural-compare": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
+ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
"dev": true,
- "bin": {
- "nanoid": "bin/nanoid.cjs"
- },
- "engines": {
- "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
- }
+ "license": "MIT"
},
"node_modules/normalize-path": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
"integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
"dev": true,
+ "license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
- "node_modules/once": {
- "version": "1.4.0",
- "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
- "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
+ "node_modules/optionator": {
+ "version": "0.9.4",
+ "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
+ "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==",
"dev": true,
+ "license": "MIT",
"dependencies": {
- "wrappy": "1"
+ "deep-is": "^0.1.3",
+ "fast-levenshtein": "^2.0.6",
+ "levn": "^0.4.1",
+ "prelude-ls": "^1.2.1",
+ "type-check": "^0.4.0",
+ "word-wrap": "^1.2.5"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
}
},
"node_modules/p-limit": {
@@ -693,6 +1489,7 @@
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
"integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"yocto-queue": "^0.1.0"
},
@@ -708,6 +1505,7 @@
"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
"integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"p-limit": "^3.0.2"
},
@@ -718,29 +1516,69 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/package-json-from-dist": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz",
+ "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==",
+ "dev": true,
+ "license": "BlueOak-1.0.0"
+ },
+ "node_modules/parent-module": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
+ "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "callsites": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
"node_modules/path-exists": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
"integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
"dev": true,
+ "license": "MIT",
"engines": {
"node": ">=8"
}
},
- "node_modules/path-is-absolute": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
- "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
+ "node_modules/path-key": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
+ "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
"dev": true,
+ "license": "MIT",
"engines": {
- "node": ">=0.10.0"
+ "node": ">=8"
+ }
+ },
+ "node_modules/path-scurry": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz",
+ "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==",
+ "dev": true,
+ "license": "BlueOak-1.0.0",
+ "dependencies": {
+ "lru-cache": "^10.2.0",
+ "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/picomatch": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz",
- "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==",
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
+ "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
"dev": true,
+ "license": "MIT",
"engines": {
"node": ">=8.6"
},
@@ -748,11 +1586,32 @@
"url": "https://github.com/sponsors/jonschlinkert"
}
},
+ "node_modules/prelude-ls": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
+ "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/punycode": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
+ "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
"node_modules/randombytes": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
"integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"safe-buffer": "^5.1.0"
}
@@ -762,6 +1621,7 @@
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
"integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"picomatch": "^2.2.1"
},
@@ -772,12 +1632,23 @@
"node_modules/require-directory": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
- "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=",
+ "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
"dev": true,
+ "license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
+ "node_modules/resolve-from": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
+ "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
"node_modules/safe-buffer": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
@@ -796,24 +1667,82 @@
"type": "consulting",
"url": "https://feross.org/support"
}
- ]
+ ],
+ "license": "MIT"
},
"node_modules/serialize-javascript": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz",
- "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==",
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz",
+ "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==",
"dev": true,
+ "license": "BSD-3-Clause",
"dependencies": {
"randombytes": "^2.1.0"
}
},
- "node_modules/string-width": {
- "version": "4.2.3",
- "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
- "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+ "node_modules/shebang-command": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
+ "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
"dev": true,
+ "license": "MIT",
"dependencies": {
- "emoji-regex": "^8.0.0",
+ "shebang-regex": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/shebang-regex": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
+ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/signal-exit": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz",
+ "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/string-width": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz",
+ "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "eastasianwidth": "^0.2.0",
+ "emoji-regex": "^9.2.2",
+ "strip-ansi": "^7.0.1"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/string-width-cjs": {
+ "name": "string-width",
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+ "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "emoji-regex": "^8.0.0",
"is-fullwidth-code-point": "^3.0.0",
"strip-ansi": "^6.0.1"
},
@@ -821,11 +1750,59 @@
"node": ">=8"
}
},
+ "node_modules/string-width-cjs/node_modules/ansi-regex": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/string-width-cjs/node_modules/emoji-regex": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/string-width-cjs/node_modules/strip-ansi": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
"node_modules/strip-ansi": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
+ "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/strip-ansi?sponsor=1"
+ }
+ },
+ "node_modules/strip-ansi-cjs": {
+ "name": "strip-ansi",
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"ansi-regex": "^5.0.1"
},
@@ -833,11 +1810,22 @@
"node": ">=8"
}
},
+ "node_modules/strip-ansi-cjs/node_modules/ansi-regex": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
"node_modules/strip-json-comments": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
"integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
"dev": true,
+ "license": "MIT",
"engines": {
"node": ">=8"
},
@@ -850,6 +1838,7 @@
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
"integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"has-flag": "^4.0.0"
},
@@ -860,16 +1849,12 @@
"url": "https://github.com/chalk/supports-color?sponsor=1"
}
},
- "node_modules/through": {
- "version": "2.3.8",
- "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
- "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU="
- },
"node_modules/to-regex-range": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"is-number": "^7.0.0"
},
@@ -877,13 +1862,27 @@
"node": ">=8.0"
}
},
- "node_modules/type-detect": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-1.0.0.tgz",
- "integrity": "sha1-diIXzAbbJY7EiQihKY6LlRIejqI=",
+ "node_modules/type-check": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
+ "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==",
"dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "prelude-ls": "^1.2.1"
+ },
"engines": {
- "node": "*"
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/uri-js": {
+ "version": "4.4.1",
+ "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
+ "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "punycode": "^2.1.0"
}
},
"node_modules/which": {
@@ -891,6 +1890,7 @@
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
"integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
"dev": true,
+ "license": "ISC",
"dependencies": {
"isexe": "^2.0.0"
},
@@ -901,17 +1901,48 @@
"node": ">= 8"
}
},
+ "node_modules/word-wrap": {
+ "version": "1.2.5",
+ "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz",
+ "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
"node_modules/workerpool": {
- "version": "6.1.5",
- "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.1.5.tgz",
- "integrity": "sha512-XdKkCK0Zqc6w3iTxLckiuJ81tiD/o5rBE/m+nXpRCB+/Sq4DqkfXZ/x0jW02DG1tGsfUGXbTJyZDP+eu67haSw==",
- "dev": true
+ "version": "6.5.1",
+ "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.5.1.tgz",
+ "integrity": "sha512-Fs4dNYcsdpYSAfVxhnl1L5zTksjvOJxtC5hzMNl+1t9B8hTJTdKDyZ5ju7ztgPy+ft9tBFXoOlDNiOT9WUXZlA==",
+ "dev": true,
+ "license": "Apache-2.0"
},
"node_modules/wrap-ansi": {
+ "version": "8.1.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz",
+ "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^6.1.0",
+ "string-width": "^5.0.1",
+ "strip-ansi": "^7.0.1"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+ }
+ },
+ "node_modules/wrap-ansi-cjs": {
+ "name": "wrap-ansi",
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
"integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"ansi-styles": "^4.0.0",
"string-width": "^4.1.0",
@@ -924,17 +1955,70 @@
"url": "https://github.com/chalk/wrap-ansi?sponsor=1"
}
},
- "node_modules/wrappy": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
- "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
- "dev": true
+ "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/wrap-ansi-cjs/node_modules/string-width": {
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+ "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/wrap-ansi/node_modules/ansi-styles": {
+ "version": "6.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz",
+ "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
},
"node_modules/y18n": {
"version": "5.0.8",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
"integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
"dev": true,
+ "license": "ISC",
"engines": {
"node": ">=10"
}
@@ -944,6 +2028,7 @@
"resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz",
"integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"cliui": "^7.0.2",
"escalade": "^3.1.1",
@@ -958,10 +2043,11 @@
}
},
"node_modules/yargs-parser": {
- "version": "20.2.4",
- "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz",
- "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==",
+ "version": "20.2.9",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz",
+ "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==",
"dev": true,
+ "license": "ISC",
"engines": {
"node": ">=10"
}
@@ -971,6 +2057,7 @@
"resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz",
"integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==",
"dev": true,
+ "license": "MIT",
"dependencies": {
"camelcase": "^6.0.0",
"decamelize": "^4.0.0",
@@ -981,719 +2068,63 @@
"node": ">=10"
}
},
- "node_modules/yocto-queue": {
- "version": "0.1.0",
- "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
- "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
- "dev": true,
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- }
- },
- "dependencies": {
- "@ungap/promise-all-settled": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz",
- "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==",
- "dev": true
- },
- "ansi-colors": {
- "version": "4.1.1",
- "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz",
- "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==",
- "dev": true
- },
- "ansi-regex": {
+ "node_modules/yargs/node_modules/ansi-regex": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
- "dev": true
- },
- "ansi-styles": {
- "version": "4.3.0",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
- "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
"dev": true,
- "requires": {
- "color-convert": "^2.0.1"
- }
- },
- "anymatch": {
- "version": "3.1.2",
- "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz",
- "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==",
- "dev": true,
- "requires": {
- "normalize-path": "^3.0.0",
- "picomatch": "^2.0.4"
- }
- },
- "argparse": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
- "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
- "dev": true
- },
- "assertion-error": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz",
- "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==",
- "dev": true
- },
- "balanced-match": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
- "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
- "dev": true
- },
- "binary-extensions": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
- "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==",
- "dev": true
- },
- "brace-expansion": {
- "version": "1.1.11",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
- "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
- "dev": true,
- "requires": {
- "balanced-match": "^1.0.0",
- "concat-map": "0.0.1"
- }
- },
- "braces": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
- "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
- "dev": true,
- "requires": {
- "fill-range": "^7.0.1"
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
}
},
- "browser-stdout": {
- "version": "1.3.1",
- "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz",
- "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==",
- "dev": true
- },
- "camelcase": {
- "version": "6.2.1",
- "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.1.tgz",
- "integrity": "sha512-tVI4q5jjFV5CavAU8DXfza/TJcZutVKo/5Foskmsqcm0MsL91moHvwiGNnqaa2o6PF/7yT5ikDRcVcl8Rj6LCA==",
- "dev": true
- },
- "chai": {
- "version": "3.5.0",
- "resolved": "https://registry.npmjs.org/chai/-/chai-3.5.0.tgz",
- "integrity": "sha1-TQJjewZ/6Vi9v906QOxW/vc3Mkc=",
+ "node_modules/yargs/node_modules/emoji-regex": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
"dev": true,
- "requires": {
- "assertion-error": "^1.0.1",
- "deep-eql": "^0.1.3",
- "type-detect": "^1.0.0"
- }
+ "license": "MIT"
},
- "chalk": {
- "version": "4.1.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
- "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "node_modules/yargs/node_modules/string-width": {
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+ "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
"dev": true,
- "requires": {
- "ansi-styles": "^4.1.0",
- "supports-color": "^7.1.0"
- },
+ "license": "MIT",
"dependencies": {
- "supports-color": {
- "version": "7.2.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
- "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
- "dev": true,
- "requires": {
- "has-flag": "^4.0.0"
- }
- }
- }
- },
- "chokidar": {
- "version": "3.5.2",
- "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz",
- "integrity": "sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==",
- "dev": true,
- "requires": {
- "anymatch": "~3.1.2",
- "braces": "~3.0.2",
- "fsevents": "~2.3.2",
- "glob-parent": "~5.1.2",
- "is-binary-path": "~2.1.0",
- "is-glob": "~4.0.1",
- "normalize-path": "~3.0.0",
- "readdirp": "~3.6.0"
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=8"
}
},
- "cliui": {
- "version": "7.0.4",
- "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz",
- "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==",
+ "node_modules/yargs/node_modules/strip-ansi": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
"dev": true,
- "requires": {
- "string-width": "^4.2.0",
- "strip-ansi": "^6.0.0",
- "wrap-ansi": "^7.0.0"
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
}
},
- "color-convert": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
- "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "node_modules/yocto-queue": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
+ "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
"dev": true,
- "requires": {
- "color-name": "~1.1.4"
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
}
- },
- "color-name": {
- "version": "1.1.4",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
- "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
- "dev": true
- },
- "concat-map": {
- "version": "0.0.1",
- "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
- "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
- "dev": true
- },
- "debug": {
- "version": "4.3.2",
- "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz",
- "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==",
- "dev": true,
- "requires": {
- "ms": "2.1.2"
- },
- "dependencies": {
- "ms": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
- "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
- "dev": true
- }
- }
- },
- "decamelize": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz",
- "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==",
- "dev": true
- },
- "deep-eql": {
- "version": "0.1.3",
- "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-0.1.3.tgz",
- "integrity": "sha1-71WKyrjeJSBs1xOQbXTlaTDrafI=",
- "dev": true,
- "requires": {
- "type-detect": "0.1.1"
- },
- "dependencies": {
- "type-detect": {
- "version": "0.1.1",
- "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-0.1.1.tgz",
- "integrity": "sha1-C6XsKohWQORw6k6FBZcZANrFiCI=",
- "dev": true
- }
- }
- },
- "diff": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz",
- "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==",
- "dev": true
- },
- "emoji-regex": {
- "version": "8.0.0",
- "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
- "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
- "dev": true
- },
- "escalade": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz",
- "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==",
- "dev": true
- },
- "escape-string-regexp": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
- "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
- "dev": true
- },
- "fill-range": {
- "version": "7.0.1",
- "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
- "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
- "dev": true,
- "requires": {
- "to-regex-range": "^5.0.1"
- }
- },
- "find-up": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
- "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
- "dev": true,
- "requires": {
- "locate-path": "^6.0.0",
- "path-exists": "^4.0.0"
- }
- },
- "flat": {
- "version": "5.0.2",
- "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz",
- "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==",
- "dev": true
- },
- "fs.realpath": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
- "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
- "dev": true
- },
- "fsevents": {
- "version": "2.3.2",
- "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
- "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
- "dev": true,
- "optional": true
- },
- "get-caller-file": {
- "version": "2.0.5",
- "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
- "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
- "dev": true
- },
- "glob": {
- "version": "7.1.7",
- "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz",
- "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==",
- "dev": true,
- "requires": {
- "fs.realpath": "^1.0.0",
- "inflight": "^1.0.4",
- "inherits": "2",
- "minimatch": "^3.0.4",
- "once": "^1.3.0",
- "path-is-absolute": "^1.0.0"
- }
- },
- "glob-parent": {
- "version": "5.1.2",
- "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
- "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
- "dev": true,
- "requires": {
- "is-glob": "^4.0.1"
- }
- },
- "growl": {
- "version": "1.10.5",
- "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz",
- "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==",
- "dev": true
- },
- "has-flag": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
- "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
- "dev": true
- },
- "he": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
- "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==",
- "dev": true
- },
- "inflight": {
- "version": "1.0.6",
- "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
- "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
- "dev": true,
- "requires": {
- "once": "^1.3.0",
- "wrappy": "1"
- }
- },
- "inherits": {
- "version": "2.0.4",
- "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
- "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
- "dev": true
- },
- "is-binary-path": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
- "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
- "dev": true,
- "requires": {
- "binary-extensions": "^2.0.0"
- }
- },
- "is-extglob": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
- "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=",
- "dev": true
- },
- "is-fullwidth-code-point": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
- "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
- "dev": true
- },
- "is-glob": {
- "version": "4.0.3",
- "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
- "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
- "dev": true,
- "requires": {
- "is-extglob": "^2.1.1"
- }
- },
- "is-number": {
- "version": "7.0.0",
- "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
- "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
- "dev": true
- },
- "is-plain-obj": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz",
- "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==",
- "dev": true
- },
- "is-unicode-supported": {
- "version": "0.1.0",
- "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz",
- "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==",
- "dev": true
- },
- "isexe": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
- "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=",
- "dev": true
- },
- "js-yaml": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
- "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
- "dev": true,
- "requires": {
- "argparse": "^2.0.1"
- }
- },
- "locate-path": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
- "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
- "dev": true,
- "requires": {
- "p-locate": "^5.0.0"
- }
- },
- "log-symbols": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz",
- "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==",
- "dev": true,
- "requires": {
- "chalk": "^4.1.0",
- "is-unicode-supported": "^0.1.0"
- }
- },
- "minimatch": {
- "version": "3.0.4",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
- "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
- "dev": true,
- "requires": {
- "brace-expansion": "^1.1.7"
- }
- },
- "mocha": {
- "version": "9.1.3",
- "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.1.3.tgz",
- "integrity": "sha512-Xcpl9FqXOAYqI3j79pEtHBBnQgVXIhpULjGQa7DVb0Po+VzmSIK9kanAiWLHoRR/dbZ2qpdPshuXr8l1VaHCzw==",
- "dev": true,
- "requires": {
- "@ungap/promise-all-settled": "1.1.2",
- "ansi-colors": "4.1.1",
- "browser-stdout": "1.3.1",
- "chokidar": "3.5.2",
- "debug": "4.3.2",
- "diff": "5.0.0",
- "escape-string-regexp": "4.0.0",
- "find-up": "5.0.0",
- "glob": "7.1.7",
- "growl": "1.10.5",
- "he": "1.2.0",
- "js-yaml": "4.1.0",
- "log-symbols": "4.1.0",
- "minimatch": "3.0.4",
- "ms": "2.1.3",
- "nanoid": "3.1.25",
- "serialize-javascript": "6.0.0",
- "strip-json-comments": "3.1.1",
- "supports-color": "8.1.1",
- "which": "2.0.2",
- "workerpool": "6.1.5",
- "yargs": "16.2.0",
- "yargs-parser": "20.2.4",
- "yargs-unparser": "2.0.0"
- }
- },
- "mock-fs": {
- "version": "4.14.0",
- "resolved": "https://registry.npmjs.org/mock-fs/-/mock-fs-4.14.0.tgz",
- "integrity": "sha512-qYvlv/exQ4+svI3UOvPUpLDF0OMX5euvUH0Ny4N5QyRyhNdgAgUrVH3iUINSzEPLvx0kbo/Bp28GJKIqvE7URw==",
- "dev": true
- },
- "ms": {
- "version": "2.1.3",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
- "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
- "dev": true
- },
- "nanoid": {
- "version": "3.1.25",
- "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.25.tgz",
- "integrity": "sha512-rdwtIXaXCLFAQbnfqDRnI6jaRHp9fTcYBjtFKE8eezcZ7LuLjhUaQGNeMXf1HmRoCH32CLz6XwX0TtxEOS/A3Q==",
- "dev": true
- },
- "normalize-path": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
- "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
- "dev": true
- },
- "once": {
- "version": "1.4.0",
- "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
- "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
- "dev": true,
- "requires": {
- "wrappy": "1"
- }
- },
- "p-limit": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
- "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
- "dev": true,
- "requires": {
- "yocto-queue": "^0.1.0"
- }
- },
- "p-locate": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
- "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
- "dev": true,
- "requires": {
- "p-limit": "^3.0.2"
- }
- },
- "path-exists": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
- "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
- "dev": true
- },
- "path-is-absolute": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
- "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
- "dev": true
- },
- "picomatch": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz",
- "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==",
- "dev": true
- },
- "randombytes": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
- "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==",
- "dev": true,
- "requires": {
- "safe-buffer": "^5.1.0"
- }
- },
- "readdirp": {
- "version": "3.6.0",
- "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
- "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
- "dev": true,
- "requires": {
- "picomatch": "^2.2.1"
- }
- },
- "require-directory": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
- "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=",
- "dev": true
- },
- "safe-buffer": {
- "version": "5.2.1",
- "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
- "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
- "dev": true
- },
- "serialize-javascript": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz",
- "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==",
- "dev": true,
- "requires": {
- "randombytes": "^2.1.0"
- }
- },
- "string-width": {
- "version": "4.2.3",
- "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
- "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
- "dev": true,
- "requires": {
- "emoji-regex": "^8.0.0",
- "is-fullwidth-code-point": "^3.0.0",
- "strip-ansi": "^6.0.1"
- }
- },
- "strip-ansi": {
- "version": "6.0.1",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
- "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
- "dev": true,
- "requires": {
- "ansi-regex": "^5.0.1"
- }
- },
- "strip-json-comments": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
- "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
- "dev": true
- },
- "supports-color": {
- "version": "8.1.1",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
- "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
- "dev": true,
- "requires": {
- "has-flag": "^4.0.0"
- }
- },
- "through": {
- "version": "2.3.8",
- "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
- "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU="
- },
- "to-regex-range": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
- "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
- "dev": true,
- "requires": {
- "is-number": "^7.0.0"
- }
- },
- "type-detect": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-1.0.0.tgz",
- "integrity": "sha1-diIXzAbbJY7EiQihKY6LlRIejqI=",
- "dev": true
- },
- "which": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
- "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
- "dev": true,
- "requires": {
- "isexe": "^2.0.0"
- }
- },
- "workerpool": {
- "version": "6.1.5",
- "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.1.5.tgz",
- "integrity": "sha512-XdKkCK0Zqc6w3iTxLckiuJ81tiD/o5rBE/m+nXpRCB+/Sq4DqkfXZ/x0jW02DG1tGsfUGXbTJyZDP+eu67haSw==",
- "dev": true
- },
- "wrap-ansi": {
- "version": "7.0.0",
- "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
- "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
- "dev": true,
- "requires": {
- "ansi-styles": "^4.0.0",
- "string-width": "^4.1.0",
- "strip-ansi": "^6.0.0"
- }
- },
- "wrappy": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
- "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
- "dev": true
- },
- "y18n": {
- "version": "5.0.8",
- "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
- "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
- "dev": true
- },
- "yargs": {
- "version": "16.2.0",
- "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz",
- "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==",
- "dev": true,
- "requires": {
- "cliui": "^7.0.2",
- "escalade": "^3.1.1",
- "get-caller-file": "^2.0.5",
- "require-directory": "^2.1.1",
- "string-width": "^4.2.0",
- "y18n": "^5.0.5",
- "yargs-parser": "^20.2.2"
- }
- },
- "yargs-parser": {
- "version": "20.2.4",
- "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz",
- "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==",
- "dev": true
- },
- "yargs-unparser": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz",
- "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==",
- "dev": true,
- "requires": {
- "camelcase": "^6.0.0",
- "decamelize": "^4.0.0",
- "flat": "^5.0.2",
- "is-plain-obj": "^2.1.0"
- }
- },
- "yocto-queue": {
- "version": "0.1.0",
- "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
- "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
- "dev": true
}
}
}
diff --git a/package.json b/package.json
index 5dd2591..0f2fe9d 100644
--- a/package.json
+++ b/package.json
@@ -1,47 +1,32 @@
{
"name": "jsfs",
- "version": "4.2.0",
- "description": "deduplicating filesystem with a REST interface",
- "main": "server.js",
- "files": [
- "server.js",
- "config.ex",
- "jlog.js",
- "lib"
- ],
- "directories": {
- "lib": "./lib",
- "tools": "./tools"
+ "version": "5.0.0",
+ "description": "An operating system for the web",
+ "main": "index.js",
+ "type": "module",
+ "scripts": {
+ "test": "mocha --config=test/.mocharc.json --node-env=test --exit",
+ "lint": "npx eslint . --ignore-pattern 'test/*' --ignore-pattern 'archive/*'",
+ "start": "node index.js"
},
"repository": {
"type": "git",
- "url": "https://github.com/jjg/jsfs.git"
+ "url": "git+https://github.com/jjg/jsfs.git"
},
- "homepage": "https://github.com/jjg/jsfs",
"keywords": [
+ "javascript",
"filesystem",
- "deduplicate",
- "http",
- "rest"
- ],
- "author": {
- "name": "Jason Gullickson"
- },
- "contributors": [
- {
- "name": "Marc Brakken"
- }
+ "jsfs"
],
- "license": "GPL-3.0",
- "scripts": {
- "test": "./node_modules/.bin/mocha --reporter spec"
- },
- "dependencies": {
- "through": "^2.3.8"
+ "author": "Jason J. Gullickson",
+ "license": "GPL-3.0-or-later",
+ "bugs": {
+ "url": "https://github.com/jjg/jsfs/issues"
},
+ "homepage": "https://github.com/jjg/jsfs#readme",
"devDependencies": {
- "chai": "^3.5.0",
- "mocha": "^9.1.3",
- "mock-fs": "^4.0.0-beta.1"
+ "@eslint/js": "^9.17.0",
+ "eslint": "^9.17.0",
+ "mocha": "^11.0.1"
}
}
diff --git a/test/.mocharc.json b/test/.mocharc.json
new file mode 100644
index 0000000..92774b0
--- /dev/null
+++ b/test/.mocharc.json
@@ -0,0 +1,3 @@
+{
+ "recursive": true
+}
diff --git a/test/lib/auth.mjs b/test/lib/auth.mjs
new file mode 100644
index 0000000..c550261
--- /dev/null
+++ b/test/lib/auth.mjs
@@ -0,0 +1,578 @@
+/*
+JSFS
+Copyright (C) 2025 Jason J. Gullickson
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+*/
+
+import http from 'node:http';
+import crypto from 'node:crypto';
+import assert from 'assert';
+
+import { Auth } from '../../lib/auth.mjs';
+import { Jnode } from '../../lib/jnode.mjs';
+import { GetJspace } from '../../lib/utils.mjs';
+
+
+describe('auth', function () {
+ describe('#Auth()', function () {
+
+ // Public files
+ it('should allow access to public files without a key', async function () {
+ const req = {
+ url: '/about.html',
+ method: 'GET',
+ headers: {
+ 'host': 'jasongullickson.com',
+ }
+ }
+ const jspace = await GetJspace(req.headers['host'], req.url);
+ const jnode = new Jnode(jspace);
+ jnode.accessKey = 'foo';
+ jnode.private = false;
+ const authResult = await Auth(req, jnode);
+ assert.equal(authResult, true);
+ });
+ it('should deny access to private files without a key', async function () {
+ const req = {
+ url: '/about.html',
+ method: 'GET',
+ headers: {
+ 'host': 'jasongullickson.com',
+ }
+ }
+ const jspace = await GetJspace(req.headers['host'], req.url);
+ const jnode = new Jnode(jspace);
+ jnode.accessKey = 'foo';
+ jnode.private = true;
+ const authResult = await Auth(req, jnode);
+ assert.equal(authResult, false);
+ });
+
+ // Keys
+ it('should allow a valid key for an existing jnode', async function () {
+ // TODO: See if there is a better way to mock http.IncomingMessage
+ const req = {
+ url: '/about.html',
+ headers: {
+ 'host': 'jasongullickson.com',
+ 'x-jsfs-access-key': 'foo',
+ }
+ }
+ const jspace = await GetJspace(req.headers['host'], req.url);
+ const jnode = new Jnode(jspace);
+ jnode.accessKey = 'foo';
+ jnode.private = true;
+ const authResult = await Auth(req, jnode);
+ assert.equal(authResult, true);
+ });
+ it('should allow a valid key for an upstream directory', async function () {
+ // This request POSTs a new file, so there is no jnode
+ // to use to check authorization, so we need to check
+ // the upstream directorie(s) for an access key.
+
+ // Create the root directory jnode
+ const rootJspace = await GetJspace('jasongullickson.com', '/');
+ const rootJnode = new Jnode(rootJspace);
+ rootJnode.accessKey = 'foo';
+ const err = await rootJnode.Save();
+ if(err){
+ // TODO: Fail the test.
+ console.log(err);
+ }
+
+ // Simulate an HTTP request to create a new file
+ // under the root directory.
+ const req = {
+ url: '/about.html',
+ method: 'POST',
+ headers: {
+ 'host': 'jasongullickson.com',
+ 'x-jsfs-access-key': 'foo',
+ }
+ }
+
+ // Simulate the request processing pipeline.
+ const jspace = await GetJspace(req.headers['host'], req.url);
+ const jnode = new Jnode(jspace);
+ await jnode.Load(); // NOTE: this is expecected to fail.
+ const authResult = await Auth(req, jnode);
+
+ assert.equal(authResult, true);
+ });
+ it('should deny an invalid key for an existing directory', async function () {
+ // This request POSTs a new file, so there is no jnode
+ // to use to check authorization, so we need to check
+ // the upstream directorie(s) for an access key.
+
+ // Create the root directory jnode
+ const rootJspace = await GetJspace('jasongullickson.com', '/');
+ const rootJnode = new Jnode(rootJspace);
+ rootJnode.accessKey = 'foo';
+ const err = await rootJnode.Save();
+ if(err){
+ // TODO: Fail the test.
+ console.log(err);
+ }
+
+ // Simulate an HTTP request to create a new file
+ // under the root directory.
+ const req = {
+ url: '/about.html',
+ method: 'POST',
+ headers: {
+ 'host': 'jasongullickson.com',
+ 'x-jsfs-access-key': 'bar',
+ }
+ }
+
+ // Simulate the request processing pipeline.
+ const jspace = await GetJspace(req.headers['host'], req.url);
+ const jnode = new Jnode(jspace);
+ await jnode.Load(); // NOTE: this is expecected to fail.
+ const authResult = await Auth(req, jnode);
+
+ assert.equal(authResult, false);
+ });
+ it('should accept key provied in querystring', async function () {
+ const req = {
+ url: '/about.html?access-key=foo',
+ headers: {
+ 'host': 'jasongullickson.com',
+ }
+ }
+
+ const jspace = await GetJspace(req.headers['host'], req.url);
+ const jnode = new Jnode(jspace);
+ jnode.accessKey = 'foo';
+ jnode.private = true;
+
+ const authResult = await Auth(req, jnode);
+
+ assert.equal(authResult, true);
+ });
+
+ // Tokens
+ it('should allow a durable token ', async function () {
+ const req = {
+ url: '/about.html',
+ method: 'GET',
+ headers: {
+ 'host': 'jasongullickson.com',
+ 'x-jsfs-access-token': '15bc6cfd907fcf2a86b1da6d1b7b75c0a79536a9',
+ }
+ }
+ const jspace = await GetJspace(req.headers['host'], req.url);
+ const jnode = new Jnode(jspace);
+ jnode.accessKey = 'foo';
+ jnode.private = true;
+
+ const authResult = await Auth(req, jnode);
+ assert.equal(authResult, true);
+ });
+ it('should allow a temporary token ', async function () {
+ // Create a token that expires 6 hours from now.
+ let d = new Date();
+ d.setTime(d.getTime() + 6 * 60*60*1000);
+
+ const key = 'foo';
+ const method = 'GET';
+ const expires = `${Math.floor(d.getTime() / 1000)}`;
+ const hash = crypto.hash('sha1', `${key}${method}${expires}`);
+ const token = `${hash}${expires}`;
+
+ const req = {
+ url: '/about.html',
+ method: 'GET',
+ headers: {
+ 'host': 'jasongullickson.com',
+ 'x-jsfs-access-token': token,
+ }
+ }
+
+ const jspace = await GetJspace(req.headers['host'], req.url);
+ const jnode = new Jnode(jspace);
+ jnode.accessKey = 'foo';
+ jnode.private = true;
+
+ const authResult = await Auth(req, jnode);
+ assert.equal(authResult, true);
+ });
+ it('should accept a valid token in the querystring', async function () {
+ const req = {
+ url: '/about.html?access-token=15bc6cfd907fcf2a86b1da6d1b7b75c0a79536a9',
+ method: 'GET',
+ headers: {
+ 'host': 'jasongullickson.com',
+ }
+ }
+ const jspace = await GetJspace(req.headers['host'], req.url);
+ const jnode = new Jnode(jspace);
+ jnode.accessKey = 'foo';
+ jnode.private = true;
+
+ const authResult = await Auth(req, jnode);
+ assert.equal(authResult, true);
+ });
+
+ it('should deny an invalid durable token ', async function () {
+ const req = {
+ url: '/about.html?access-token=bogus',
+ method: 'GET',
+ headers: {
+ 'host': 'jasongullickson.com',
+ }
+ }
+ const jspace = await GetJspace(req.headers['host'], req.url);
+ const jnode = new Jnode(jspace);
+ jnode.accessKey = 'foo';
+ jnode.private = true;
+
+ const authResult = await Auth(req, jnode);
+ assert.equal(authResult, false);
+ });
+ it('should deny an invalid temporary token ', async function () {
+ // Create a token that expires 6 hours from now.
+ let d = new Date();
+ d.setTime(d.getTime() + 6 * 60*60*1000);
+
+ const key = 'bar';
+ const method = 'GET';
+ const expires = `${Math.floor(d.getTime() / 1000)}`;
+ const hash = crypto.hash('sha1', `${key}${method}${expires}`);
+ const token = `${hash}${expires}`;
+
+ const req = {
+ url: '/about.html',
+ method: 'GET',
+ headers: {
+ 'host': 'jasongullickson.com',
+ 'x-jsfs-access-token': token,
+ }
+ }
+
+ const jspace = await GetJspace(req.headers['host'], req.url);
+ const jnode = new Jnode(jspace);
+ jnode.accessKey = 'foo';
+ jnode.private = true;
+
+ const authResult = await Auth(req, jnode);
+ assert.equal(authResult, false);
+ });
+ it('should deny an expired temporary token ', async function () {
+
+ // Create a token that expires 6 hours ago.
+ let d = new Date();
+ d.setTime(d.getTime() - 6 * 60*60*1000);
+
+ const key = 'foo';
+ const method = 'GET';
+ const expires = `${Math.floor(d.getTime() / 1000)}`;
+ const hash = crypto.hash('sha1', `${key}${method}${expires}`);
+ const token = `${hash}${expires}`;
+
+ const req = {
+ url: '/about.html',
+ method: 'GET',
+ headers: {
+ 'host': 'jasongullickson.com',
+ 'x-jsfs-access-token': token,
+ }
+ }
+
+ const jspace = await GetJspace(req.headers['host'], req.url);
+ const jnode = new Jnode(jspace);
+ jnode.accessKey = 'foo';
+ jnode.private = true;
+
+ const authResult = await Auth(req, jnode);
+ assert.equal(authResult, false);
+ });
+
+ // Verb-specific tokens
+ it('should allow a valid HEAD token ', async function () {
+ const req = {
+ url: '/about.html',
+ method: 'HEAD',
+ headers: {
+ 'host': 'jasongullickson.com',
+ 'x-jsfs-access-token': '64e6670827dc90c6e3106c9085afd71e44efb349',
+ }
+ }
+ const jspace = await GetJspace(req.headers['host'], req.url);
+ const jnode = new Jnode(jspace);
+ jnode.accessKey = 'foo';
+ jnode.private = true;
+
+ const authResult = await Auth(req, jnode);
+ assert.equal(authResult, true);
+ });
+ it('should allow a valid GET token ', async function () {
+ const req = {
+ url: '/about.html',
+ method: 'GET',
+ headers: {
+ 'host': 'jasongullickson.com',
+ 'x-jsfs-access-token': '15bc6cfd907fcf2a86b1da6d1b7b75c0a79536a9',
+ }
+ }
+ const jspace = await GetJspace(req.headers['host'], req.url);
+ const jnode = new Jnode(jspace);
+ jnode.accessKey = 'foo';
+ jnode.private = true;
+
+ const authResult = await Auth(req, jnode);
+ assert.equal(authResult, true);
+ });
+ it('should allow a valid POST token ', async function () {
+ const req = {
+ url: '/about.html',
+ method: 'POST',
+ headers: {
+ 'host': 'jasongullickson.com',
+ 'x-jsfs-access-token': 'af27af8f7e7147e5b21bb9592a2ba18ba207c245',
+ }
+ }
+ const jspace = await GetJspace(req.headers['host'], req.url);
+ const jnode = new Jnode(jspace);
+ jnode.accessKey = 'foo';
+ jnode.private = true;
+
+ const authResult = await Auth(req, jnode);
+ assert.equal(authResult, true);
+ });
+ it('should allow a valid PUT token ', async function () {
+ const req = {
+ url: '/about.html',
+ method: 'PUT',
+ headers: {
+ 'host': 'jasongullickson.com',
+ 'x-jsfs-access-token': '8cf9096295099296c898f9f832acaa3604149f2c',
+ }
+ }
+ const jspace = await GetJspace(req.headers['host'], req.url);
+ const jnode = new Jnode(jspace);
+ jnode.accessKey = 'foo';
+ jnode.private = true;
+
+ const authResult = await Auth(req, jnode);
+ assert.equal(authResult, true);
+ });
+ it('should allow a valid DELETE token ', async function () {
+ const req = {
+ url: '/about.html',
+ method: 'DELETE',
+ headers: {
+ 'host': 'jasongullickson.com',
+ 'x-jsfs-access-token': '81b0c5682956c3e89cce66be99bdc0159f61daee',
+ }
+ }
+ const jspace = await GetJspace(req.headers['host'], req.url);
+ const jnode = new Jnode(jspace);
+ jnode.accessKey = 'foo';
+ jnode.private = true;
+
+ const authResult = await Auth(req, jnode);
+ assert.equal(authResult, true);
+ });
+ it('should allow a valid EXECUTE token ', async function () {
+ const req = {
+ url: '/about.html',
+ method: 'EXECUTE',
+ headers: {
+ 'host': 'jasongullickson.com',
+ 'x-jsfs-access-token': 'c89bf9f44398fceb7b3542361802bdaedd754242',
+ }
+ }
+ const jspace = await GetJspace(req.headers['host'], req.url);
+ const jnode = new Jnode(jspace);
+ jnode.accessKey = 'foo';
+ jnode.private = true;
+
+ const authResult = await Auth(req, jnode);
+ assert.equal(authResult, true);
+ });
+ it('should allow a valid MOVE token ', async function () {
+ const req = {
+ url: '/about.html',
+ method: 'MOVE',
+ headers: {
+ 'host': 'jasongullickson.com',
+ 'x-jsfs-access-token': 'f973af2f03f81e761c53b5fb13c344f492a64b25',
+ }
+ }
+ const jspace = await GetJspace(req.headers['host'], req.url);
+ const jnode = new Jnode(jspace);
+ jnode.accessKey = 'foo';
+ jnode.private = true;
+
+ const authResult = await Auth(req, jnode);
+ assert.equal(authResult, true);
+ });
+ it('should allow a valid COPY token ', async function () {
+ const req = {
+ url: '/about.html',
+ method: 'COPY',
+ headers: {
+ 'host': 'jasongullickson.com',
+ 'x-jsfs-access-token': '55ba55476ceef72ae9ed6b68807e601bc6001aa2',
+ }
+ }
+ const jspace = await GetJspace(req.headers['host'], req.url);
+ const jnode = new Jnode(jspace);
+ jnode.accessKey = 'foo';
+ jnode.private = true;
+
+ const authResult = await Auth(req, jnode);
+ assert.equal(authResult, true);
+ });
+
+ it('should deny an invalid HEAD token ', async function () {
+ const req = {
+ url: '/about.html',
+ method: 'HEAD',
+ headers: {
+ 'host': 'jasongullickson.com',
+ 'x-jsfs-access-token': 'bad',
+ }
+ }
+ const jspace = await GetJspace(req.headers['host'], req.url);
+ const jnode = new Jnode(jspace);
+ jnode.accessKey = 'foo';
+ jnode.private = true;
+
+ const authResult = await Auth(req, jnode);
+ assert.equal(authResult, false);
+ });
+ it('should deny an invalid GET token ', async function () {
+ const req = {
+ url: '/about.html',
+ method: 'GET',
+ headers: {
+ 'host': 'jasongullickson.com',
+ 'x-jsfs-access-token': 'bad',
+ }
+ }
+ const jspace = await GetJspace(req.headers['host'], req.url);
+ const jnode = new Jnode(jspace);
+ jnode.accessKey = 'foo';
+ jnode.private = true;
+
+ const authResult = await Auth(req, jnode);
+ assert.equal(authResult, false);
+ });
+ it('should deny an invalid POST token ', async function () {
+ const req = {
+ url: '/about.html',
+ method: 'POST',
+ headers: {
+ 'host': 'jasongullickson.com',
+ 'x-jsfs-access-token': 'bad',
+ }
+ }
+ const jspace = await GetJspace(req.headers['host'], req.url);
+ const jnode = new Jnode(jspace);
+ jnode.accessKey = 'foo';
+ jnode.private = true;
+
+ const authResult = await Auth(req, jnode);
+ assert.equal(authResult, false);
+ });
+ it('should deny an invalid PUT token ', async function () {
+ const req = {
+ url: '/about.html',
+ method: 'PUT',
+ headers: {
+ 'host': 'jasongullickson.com',
+ 'x-jsfs-access-token': 'bad',
+ }
+ }
+ const jspace = await GetJspace(req.headers['host'], req.url);
+ const jnode = new Jnode(jspace);
+ jnode.accessKey = 'foo';
+ jnode.private = true;
+
+ const authResult = await Auth(req, jnode);
+ assert.equal(authResult, false);
+ });
+ it('should deny an invalid DELETE token ', async function () {
+ const req = {
+ url: '/about.html',
+ method: 'DELETE',
+ headers: {
+ 'host': 'jasongullickson.com',
+ 'x-jsfs-access-token': 'bad',
+ }
+ }
+ const jspace = await GetJspace(req.headers['host'], req.url);
+ const jnode = new Jnode(jspace);
+ jnode.accessKey = 'foo';
+ jnode.private = true;
+
+ const authResult = await Auth(req, jnode);
+ assert.equal(authResult, false);
+ });
+ it('should deny an invalid EXECUTE token ', async function () {
+ const req = {
+ url: '/about.html',
+ method: 'EXECUTE',
+ headers: {
+ 'host': 'jasongullickson.com',
+ 'x-jsfs-access-token': 'bad',
+ }
+ }
+ const jspace = await GetJspace(req.headers['host'], req.url);
+ const jnode = new Jnode(jspace);
+ jnode.accessKey = 'foo';
+ jnode.private = true;
+
+ const authResult = await Auth(req, jnode);
+ assert.equal(authResult, false);
+ });
+ it('should deny an invalid MOVE token ', async function () {
+ const req = {
+ url: '/about.html',
+ method: 'MOVE',
+ headers: {
+ 'host': 'jasongullickson.com',
+ 'x-jsfs-access-token': 'bad',
+ }
+ }
+ const jspace = await GetJspace(req.headers['host'], req.url);
+ const jnode = new Jnode(jspace);
+ jnode.accessKey = 'foo';
+ jnode.private = true;
+
+ const authResult = await Auth(req, jnode);
+ assert.equal(authResult, false);
+ });
+ it('should deny an invalid COPY token ', async function () {
+ const req = {
+ url: '/about.html',
+ method: 'COPY',
+ headers: {
+ 'host': 'jasongullickson.com',
+ 'x-jsfs-access-token': 'bad',
+ }
+ }
+ const jspace = await GetJspace(req.headers['host'], req.url);
+ const jnode = new Jnode(jspace);
+ jnode.accessKey = 'foo';
+ jnode.private = true;
+
+ const authResult = await Auth(req, jnode);
+ assert.equal(authResult, false);
+ });
+ });
+});
diff --git a/test/lib/blockdrivers/example.mjs b/test/lib/blockdrivers/example.mjs
new file mode 100644
index 0000000..dd72733
--- /dev/null
+++ b/test/lib/blockdrivers/example.mjs
@@ -0,0 +1,34 @@
+/*
+JSFS
+Copyright (C) 2025 Jason J. Gullickson
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+*/
+
+import assert from 'assert';
+
+import { Store, Load, Purge } from '../../../lib/blockdrivers/example.mjs';
+
+
+describe('Example Blockdriver', function () {
+ describe('#Store()', function () {
+ it('should store a block')
+ });
+ describe('#Load()', function () {
+ it('should load a block')
+ });
+ describe('#Purge()', function () {
+ it('should purge a block')
+ });
+});
diff --git a/test/lib/blockstore.mjs b/test/lib/blockstore.mjs
new file mode 100644
index 0000000..cf50ebc
--- /dev/null
+++ b/test/lib/blockstore.mjs
@@ -0,0 +1,73 @@
+/*
+JSFS
+Copyright (C) 2025 Jason J. Gullickson
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+*/
+
+//import http from 'node:http';
+import crypto from 'node:crypto';
+import assert from 'assert';
+
+import { Load, Store, Purge } from '../../lib/blockstore.mjs';
+
+
+describe('blockstore', function () {
+
+ // NOTE: This test has cross-describe dependencies
+ // on files written to the filesystem. I'm not sure
+ // if that's OK, so this might have to be revisited.
+
+ const blockData = 'foo';
+ const blockHash = crypto.hash('sha1', blockData);
+
+ describe('#Store()', function () {
+ it('should store a block of data', async function() {
+ Store(blockHash, blockData);
+
+ // TODO: Right now this test will pass unless
+ // an exception is thrown. It would be better
+ // to also try reading the block back, but since
+ // this is a detached async operation, I'm not
+ // sure how to do that in a way that doesn't
+ // result in false-positives (i.e. flaky tests).
+ //
+ // I guess for testing purposes Store() could be
+ // awaited since this test isn't about maximizing
+ // performance...?
+ });
+ });
+
+ describe('#Load()', function () {
+ it('should load the requested block data', async function(){
+ const result = await Load(blockHash);
+
+ // TODO: Test this in a more meaningful way.
+ assert.notEqual(result, null);
+
+ // TODO: Do I really need all this decoding?
+ const decoder = new TextDecoder();
+ const resultString = decoder.decode(result);
+ assert.equal(resultString, blockData);
+ });
+ });
+ describe('#Purge()', function () {
+ it('should erase a block from the store', async function (){
+
+ // TODO: Maybe a test should confirm the purge occured,
+ // but for now we'll just be happy if it doesn't throw.
+ await Purge(blockHash);
+ });
+ });
+});
diff --git a/test/lib/jnode.mjs b/test/lib/jnode.mjs
new file mode 100644
index 0000000..d85c5c8
--- /dev/null
+++ b/test/lib/jnode.mjs
@@ -0,0 +1,53 @@
+/*
+JSFS
+Copyright (C) 2025 Jason J. Gullickson
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+*/
+import assert from 'assert';
+
+import { Jnode } from '../../lib/jnode.mjs';
+
+
+describe('jnode', function () {
+ describe('#New()', function () {
+ it('should return a new jnode', function () {
+ const aJnode = new Jnode('/com.jasongullickson/home/welcome.html');
+ assert.equal(aJnode.version, 0);
+ });
+ });
+ describe('#Save()', function () {
+ it('should return a new jnode', async function () {
+ const aJnode = new Jnode('/com.jasongullickson/home/welcome.html');
+ aJnode.accessKey = 'foo';
+
+ const err = await aJnode.Save();
+
+ // TODO: Maybe test the results, but for now consider
+ // it a win if we don't throw an exception.
+ });
+ });
+ describe('#Load()', function () {
+ it('should return an existing jnode', async function () {
+ const aJnode = new Jnode('/com.jasongullickson/home/welcome.html');
+ const err = await aJnode.Load();
+ if(err){
+ console.log(err);
+ // TODO: fail test
+ }
+
+ assert.equal(aJnode.accessKey, 'foo');
+ });
+ });
+});
diff --git a/test/lib/metadrivers/example.mjs b/test/lib/metadrivers/example.mjs
new file mode 100644
index 0000000..6dc7ed1
--- /dev/null
+++ b/test/lib/metadrivers/example.mjs
@@ -0,0 +1,28 @@
+/*
+JSFS
+Copyright (C) 2025 Jason J. Gullickson
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+*/
+
+import assert from 'assert';
+
+import { Extract } from '../../../lib/metadrivers/example.mjs';
+
+
+describe('Example Metadriver', function () {
+ describe('#Extract()', function () {
+ it('should extract metadata from block data')
+ });
+});
diff --git a/test/lib/utils.mjs b/test/lib/utils.mjs
new file mode 100644
index 0000000..1e01dae
--- /dev/null
+++ b/test/lib/utils.mjs
@@ -0,0 +1,66 @@
+/*
+JSFS
+Copyright (C) 2025 Jason J. Gullickson
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+*/
+
+import { GetJspace, GetParam } from '../../lib/utils.mjs';
+import assert from 'assert';
+
+describe('utils', function () {
+ describe('#GetJspace()', function () {
+ it('should return a JSFS jspace string', async function () {
+ const jspace = await GetJspace("jasongullickson.com", "/home/welcome.html");
+ assert.equal(jspace, "/com.jasongullickson/home/welcome.html");
+ });
+ it('should not modify a provided jspace string', async function () {
+ const jspace = await GetJspace("jasongullickson.com", "/.com.jasongullickson/home/welcome.html");
+ assert.equal(jspace, "/com.jasongullickson/home/welcome.html");
+ });
+ });
+ describe('#GetParam()', function () {
+ it('should return an access-key provided in a header', async function () {
+ const req = {
+ url: '/about.html',
+ headers: {
+ 'host': 'jasongullickson.com',
+ 'x-jsfs-access-key': 'foo',
+ }
+ }
+ const result = await GetParam(req, 'access-key');
+ assert.equal(result, "foo");
+ });
+ it('should return an access-key provided in the querystring', async function () {
+ const req = {
+ url: '/about.html?access-key=foo',
+ headers: {
+ 'host': 'jasongullickson.com',
+ }
+ }
+ const result = await GetParam(req, 'access-key');
+ assert.equal(result, "foo");
+ });
+ it('should return null if the param is not found', async function () {
+ const req = {
+ url: '/about.html',
+ headers: {
+ 'host': 'jasongullickson.com',
+ }
+ }
+ const result = await GetParam(req, 'access-key');
+ assert.equal(result, null);
+ });
+ });
+});
diff --git a/test/lib/verbs/copy.mjs b/test/lib/verbs/copy.mjs
new file mode 100644
index 0000000..7114228
--- /dev/null
+++ b/test/lib/verbs/copy.mjs
@@ -0,0 +1,30 @@
+/*
+JSFS
+Copyright (C) 2025 Jason J. Gullickson
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+*/
+
+import assert from 'assert';
+
+import { Copy } from '../../../lib/verbs/copy.mjs';
+
+
+describe('copy', function () {
+ describe('#Copy()', function () {
+ it('should copy an existing file to a new jspace')
+ it('should 404 if file doesnt exist')
+ it('should 401 if auth fails?')
+ });
+});
diff --git a/test/lib/verbs/delete.mjs b/test/lib/verbs/delete.mjs
new file mode 100644
index 0000000..bd3e2c1
--- /dev/null
+++ b/test/lib/verbs/delete.mjs
@@ -0,0 +1,30 @@
+/*
+JSFS
+Copyright (C) 2025 Jason J. Gullickson
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+*/
+
+import assert from 'assert';
+
+import { Delete } from '../../../lib/verbs/delete.mjs';
+
+
+describe('delete', function () {
+ describe('#Delete()', function () {
+ it('should remove an existing file')
+ it('should 404 if file doesnt exist')
+ it('should 401 if auth fails?')
+ });
+});
diff --git a/test/lib/verbs/execute.mjs b/test/lib/verbs/execute.mjs
new file mode 100644
index 0000000..3bdd3cc
--- /dev/null
+++ b/test/lib/verbs/execute.mjs
@@ -0,0 +1,30 @@
+/*
+JSFS
+Copyright (C) 2025 Jason J. Gullickson
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+*/
+
+import assert from 'assert';
+
+import { Execute } from '../../../lib/verbs/execute.mjs';
+
+
+describe('execute', function () {
+ describe('#Execute()', function () {
+ it('should execute an existing file')
+ it('should 404 if file doesnt exist')
+ it('should 401 if auth fails?')
+ });
+});
diff --git a/test/lib/verbs/get.mjs b/test/lib/verbs/get.mjs
new file mode 100644
index 0000000..87af9f1
--- /dev/null
+++ b/test/lib/verbs/get.mjs
@@ -0,0 +1,58 @@
+/*
+JSFS
+Copyright (C) 2025 Jason J. Gullickson
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+*/
+
+import http from 'node:http';
+import crypto from 'node:crypto';
+import assert from 'assert';
+
+import { GetJspace } from '../../../lib/utils.mjs';
+import { Jnode } from '../../../lib/jnode.mjs';
+import { Auth } from '../../../lib/auth.mjs';
+import { Get } from '../../../lib/verbs/get.mjs';
+
+
+describe('get', function () {
+ describe('#Get()', function () {
+ it('should return a stored file', async function () {
+
+ const req = {
+ url: '/about.html',
+ method: 'GET',
+ headers: {
+ 'host': 'jasongullickson.com',
+ }
+ }
+ // TODO: Mock a more complete response object()?
+ const res = {};
+
+ // Simulate the steps taken when processing the client request.
+ const jspace = await GetJspace(req.headers['host'], req.url);
+ const jnode = new Jnode(jspace);
+ const authResult = await Auth(req, res, jnode);
+
+ // Do the GET
+ await Get(req, res, jnode);
+
+ // Make sure HTTP status is sucessful.
+ assert.equal(res.status, 200);
+
+ // TODO: Test to make sure file was correctly returned.
+ });
+ it('should 404 if file doesnt exist')
+ });
+});
diff --git a/test/lib/verbs/head.mjs b/test/lib/verbs/head.mjs
new file mode 100644
index 0000000..67e5522
--- /dev/null
+++ b/test/lib/verbs/head.mjs
@@ -0,0 +1,64 @@
+/*
+JSFS
+Copyright (C) 2025 Jason J. Gullickson
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+*/
+
+import http from 'node:http';
+import crypto from 'node:crypto';
+import assert from 'assert';
+
+import { GetJspace } from '../../../lib/utils.mjs';
+import { Jnode } from '../../../lib/jnode.mjs';
+import { Auth } from '../../../lib/auth.mjs';
+import { Head } from '../../../lib/verbs/head.mjs';
+
+
+describe('head', function () {
+ describe('#Head()', function () {
+ it('should return metadata as headers', async function () {
+
+ const req = {
+ url: '/about.html',
+ method: 'HEAD',
+ headers: {
+ 'host': 'jasongullickson.com',
+ }
+ }
+ // TODO: Mock a more complete response object()?
+ const res = {
+ writeHead: function (status, headers){
+ //console.log('Writing headers');
+ this.status = status;
+ }
+ };
+
+ // Simulate the steps taken when processing the client request.
+ const jspace = await GetJspace(req.headers['host'], req.url);
+ const jnode = new Jnode(jspace);
+ const authResult = await Auth(req, res, jnode);
+
+ // Do the HEAD
+ await Head(req, res, jnode);
+
+ // Make sure HTTP status is sucessful.
+ assert.equal(res.status, 200);
+
+ // TODO: Test to make sure headers look correct
+ });
+ it('should 404 if file doesnt exist')
+ it('should 401 if auth fails?')
+ });
+});
diff --git a/test/lib/verbs/move.mjs b/test/lib/verbs/move.mjs
new file mode 100644
index 0000000..ee960c6
--- /dev/null
+++ b/test/lib/verbs/move.mjs
@@ -0,0 +1,30 @@
+/*
+JSFS
+Copyright (C) 2025 Jason J. Gullickson
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+*/
+
+import assert from 'assert';
+
+import { Move } from '../../../lib/verbs/move.mjs';
+
+
+describe('move', function () {
+ describe('#Move()', function () {
+ it('should move an existing file from one jspace to another')
+ it('should 404 if file doesnt exist')
+ it('should 401 if auth fails?')
+ });
+});
diff --git a/test/lib/verbs/post.mjs b/test/lib/verbs/post.mjs
new file mode 100644
index 0000000..8d1fb96
--- /dev/null
+++ b/test/lib/verbs/post.mjs
@@ -0,0 +1,83 @@
+/*
+JSFS
+Copyright (C) 2025 Jason J. Gullickson
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+*/
+
+import http from 'node:http';
+import assert from 'assert';
+import { open } from 'node:fs/promises';
+
+import { GetJspace } from '../../../lib/utils.mjs';
+import { Jnode } from '../../../lib/jnode.mjs';
+import { Auth } from '../../../lib/auth.mjs';
+import { Post } from '../../../lib/verbs/post.mjs';
+
+
+describe('POST verb handling', function () {
+ describe('#Post()', function () {
+ it('should create a new file from posted data', async function () {
+
+ // Simulate a request by mutating a ...filehandle?
+ // TODO: Figure out how to not use an absolute path here.
+ const req = await open('/home/jason/Projects/jsfs/test/testdata/about.html');
+ req.url = '/about.html';
+ req.method = 'POST';
+ req.headers = {
+ 'host': 'jasongullickson.com',
+ 'x-jsfs-access-key': 'foo',
+ };
+
+ // TODO: Mock a more complete response object()?
+ const res = {};
+
+ // Simulate the steps taken when processing the client request.
+ const jspace = await GetJspace(req.headers['host'], req.url);
+
+ // TODO: Replace this fake jnode by loading a real one?
+ const jnode = new Jnode(jspace);
+ jnode.accessKey = 'foo';
+
+ const authResult = await Auth(req, jnode);
+
+ // Process as Post
+ await Post(req, res, jnode);
+
+ // Make sure HTTP status is sucessful.
+ assert.equal(res.status, 200);
+
+ // TODO: Do a more complete job of testing the result
+ // TODO: Test version
+ // TODO: Test fingerprint
+ // TODO: Test accessKey
+ // TODO: Test fileSize
+ // TODO: Test blockSize
+ assert.equal(jnode.blocks.length, 1);
+
+ // TODO: Issue additional verbs (HEAD, GET) to ensure that
+ // the file has been stored correctly?
+
+ // Cleanup
+ await req.close();
+ });
+ it('should utilize a client-provided accessKey')
+ it('should set HTTP status to 401 if authorization is denied')
+ it('should set HTTP status to 500 if an error occurs')
+ it('should handle streaming data correctly')
+ it('should write the correct content-length header')
+ it('should set HTTP method not allowed if file exists')
+ it('should handle range requests correctly')
+ });
+});
diff --git a/test/lib/verbs/put.mjs b/test/lib/verbs/put.mjs
new file mode 100644
index 0000000..63f6514
--- /dev/null
+++ b/test/lib/verbs/put.mjs
@@ -0,0 +1,31 @@
+/*
+JSFS
+Copyright (C) 2025 Jason J. Gullickson
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+*/
+
+import assert from 'assert';
+
+import { Put } from '../../../lib/verbs/put.mjs';
+
+
+describe('put', function () {
+ describe('#Put()', function () {
+ it('should update an existing file')
+ it('should error if the file doesnt exist')
+ it('should increment the file version')
+ it('should 401 if auth fails')
+ });
+});
diff --git a/test/testdata/about.html b/test/testdata/about.html
new file mode 100644
index 0000000..af586cc
--- /dev/null
+++ b/test/testdata/about.html
@@ -0,0 +1,9 @@
+
+
+ About
+
+
+ About JSFS
+ This is just a test file.
+
+