repertory 2.0
This commit is contained in:
14
.gitignore
vendored
14
.gitignore
vendored
@@ -1,7 +1,7 @@
|
||||
node_modules/
|
||||
coverage/
|
||||
.vscode/
|
||||
dist/
|
||||
backup/
|
||||
.idea/
|
||||
/package-lock.json
|
||||
node_modules/
|
||||
coverage/
|
||||
.vscode/
|
||||
dist/
|
||||
backup/
|
||||
.idea/
|
||||
/package-lock.json
|
||||
|
||||
6
.nvimrc
6
.nvimrc
@@ -1,3 +1,3 @@
|
||||
set path+=.,src/**
|
||||
let &makeprg="npm run build"
|
||||
|
||||
set path+=.,src/**
|
||||
let &makeprg="npm run build"
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"trailingComma": "es5",
|
||||
"tabWidth": 2,
|
||||
"semi": true,
|
||||
"singleQuote": true,
|
||||
"jsxBracketSameLine": true
|
||||
}
|
||||
|
||||
{
|
||||
"trailingComma": "es5",
|
||||
"tabWidth": 2,
|
||||
"semi": true,
|
||||
"singleQuote": true,
|
||||
"jsxBracketSameLine": true
|
||||
}
|
||||
|
||||
|
||||
28
CHANGELOG.md
28
CHANGELOG.md
@@ -1,14 +1,14 @@
|
||||
# Changelog
|
||||
|
||||
## 1.4.0-r1
|
||||
- Switched packet encryption to XChaCha20-Poly1305
|
||||
- Allow external XChaCha20-Poly1305 encryption/decryption
|
||||
- Support writing base64 string data
|
||||
- Renamed 'delete/delete_file' to 'remove/remove_file'
|
||||
|
||||
## 1.3.1-r3
|
||||
- Added directory/file exists
|
||||
- Fix unit tests
|
||||
|
||||
## 1.3.1-r2
|
||||
- Initial release
|
||||
# Changelog
|
||||
|
||||
## 1.4.0-r1
|
||||
- Switched packet encryption to XChaCha20-Poly1305
|
||||
- Allow external XChaCha20-Poly1305 encryption/decryption
|
||||
- Support writing base64 string data
|
||||
- Renamed 'delete/delete_file' to 'remove/remove_file'
|
||||
|
||||
## 1.3.1-r3
|
||||
- Added directory/file exists
|
||||
- Fix unit tests
|
||||
|
||||
## 1.3.1-r2
|
||||
- Initial release
|
||||
|
||||
36
LICENSE.md
36
LICENSE.md
@@ -1,18 +1,18 @@
|
||||
# `repertory-js` MIT License
|
||||
|
||||
### Copyright <2021> <scott.e.graves@protonmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
|
||||
associated documentation files (the "Software"), to deal in the Software without restriction,
|
||||
including without limitation the rights to use, copy, modify, merge, publish, distribute,
|
||||
sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial
|
||||
portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
|
||||
NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES
|
||||
OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
# `repertory-js` MIT License
|
||||
|
||||
### Copyright <2021> <scott.e.graves@protonmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
|
||||
associated documentation files (the "Software"), to deal in the Software without restriction,
|
||||
including without limitation the rights to use, copy, modify, merge, publish, distribute,
|
||||
sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial
|
||||
portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
|
||||
NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES
|
||||
OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
394
README.md
394
README.md
@@ -1,197 +1,197 @@
|
||||
# About
|
||||
|
||||
`repertory-js` is a Node.js module for interfacing with `repertory's` remote mount API.
|
||||
|
||||
## Installing
|
||||
|
||||
```shell
|
||||
npm i @blockstorage/repertory-js
|
||||
```
|
||||
|
||||
## Repertory Configuration
|
||||
|
||||
A Repertory mount must be active with the `EnableRemoteMount` setting enabled. `RemoteToken` should
|
||||
also be set to a strong, random password.
|
||||
|
||||
### Enabling Sia Remote Mount API on Windows Systems
|
||||
|
||||
```shell
|
||||
repertory.exe -unmount
|
||||
repertory.exe -set RemoteMount.EnableRemoteMount true
|
||||
repertory.exe -set RemoteMount.RemoteToken "my password"
|
||||
|
||||
[Optional - change listening port]
|
||||
repertory.exe -set RemoteMount.RemotePort 20202
|
||||
```
|
||||
|
||||
### Enabling Sia Remote Mount API on *NIX Systems
|
||||
|
||||
```shell
|
||||
./repertory -unmount
|
||||
./repertory -set RemoteMount.EnableRemoteMount true
|
||||
./repertory -set RemoteMount.RemoteToken "my password"
|
||||
|
||||
[Optional - change listening port]
|
||||
./repertory -set RemoteMount.RemotePort 20202
|
||||
```
|
||||
|
||||
### Skynet and ScPrime Mounts
|
||||
|
||||
* For Skynet mounts, add `-sk` argument to all commands listed above.
|
||||
* For ScPrime mounts, add `-sp` argument to all commands listed above.
|
||||
|
||||
## Module Environment Variables
|
||||
|
||||
* To successfully complete unit tests, a `repertory` mount supporting remote mount needs to be
|
||||
active. Set the following environment variables prior to running tests:
|
||||
* `TEST_HOST`
|
||||
* `TEST_PASSWORD`
|
||||
* `TEST_PORT`
|
||||
|
||||
## Example API Usage
|
||||
|
||||
```javascript
|
||||
import * as rep from "@blockstorage/repertory-js";
|
||||
//const rep = require("@blockstorage/repertory-js");
|
||||
|
||||
|
||||
// Repertory host settings
|
||||
const MY_HOST_OR_IP = 'localhost';
|
||||
const MY_PORT = 20000;
|
||||
const MY_TOKEN = 'password';
|
||||
|
||||
// Progress callback for uploads / downloads
|
||||
const progress_cb = (local_path, remote_path, progress, completed) => {
|
||||
console.log(local_path, remote_path, progress, completed);
|
||||
};
|
||||
|
||||
|
||||
//************************************************************************************************//
|
||||
// Step 1. Create a connection pool (recommended) //
|
||||
//************************************************************************************************//
|
||||
|
||||
const conn = await rep.create_pool(8, MY_HOST_OR_IP, MY_PORT, MY_TOKEN);
|
||||
/* Or create a single connection for light operations
|
||||
const conn = await rep.connect(MY_HOST_OR_IP, MY_PORT, MY_TOKEN);
|
||||
*/
|
||||
|
||||
/* Disconnect when complete
|
||||
await conn.disconnect();
|
||||
*/
|
||||
|
||||
//************************************************************************************************//
|
||||
// Step 2. Create an 'api' instance using the connection pool / connection //
|
||||
//************************************************************************************************//
|
||||
|
||||
const api = rep.create_api(conn);
|
||||
|
||||
|
||||
//************************************************************************************************//
|
||||
// Step 3. Use 'api' //
|
||||
//************************************************************************************************//
|
||||
|
||||
//------------------------------------------------------------------------------------------------//
|
||||
// *********** Directory Operations *********** //
|
||||
//------------------------------------------------------------------------------------------------//
|
||||
|
||||
// Check if directory exists
|
||||
const exists = await api.directory.exists('/my_directory');
|
||||
|
||||
// List directory contents
|
||||
await api.directory.list('/', async (remote_path, page_count, get_page) => {
|
||||
for (let i = 0; i < page_count; i++) {
|
||||
const items = await get_page(i); // Always 'await'
|
||||
console.log(items);
|
||||
}
|
||||
});
|
||||
|
||||
// Asynchronous directory list
|
||||
const snap = await api.directory.snapshot('/');
|
||||
try {
|
||||
for (let i = 0; i < snap.page_count; i++) {
|
||||
const items = await snap.get_page(i); // Always 'await'
|
||||
console.log(items);
|
||||
}
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
} finally {
|
||||
await snap.release();
|
||||
}
|
||||
|
||||
// Create new directory
|
||||
await api.directory.create('/test');
|
||||
|
||||
// Remove existing directory
|
||||
await api.directory.remove('/test')
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------------------------//
|
||||
// *********** File Operations *********** //
|
||||
//------------------------------------------------------------------------------------------------//
|
||||
|
||||
// Check if file exists
|
||||
const exists = await api.file.exists('/my_file.txt')
|
||||
|
||||
// Remove a file
|
||||
await api.file.remove('/my_file.txt')
|
||||
|
||||
// Download a remote file
|
||||
await api.file.download('/my_file.txt', 'C:\\my_file.txt', progress_cb);
|
||||
|
||||
// Download a remote file and overwrite existing local file
|
||||
await api.file.download('/my_file.txt', 'C:\\my_file.txt', progress_cb, true);
|
||||
|
||||
// Resume failed download
|
||||
await api.file.download('/my_file.txt', 'C:\\my_file.txt', progress_cb, false, true);
|
||||
|
||||
// Upload a local file
|
||||
await api.file.upload('C:\\my_file.txt', '/my_file.txt', progress_cb);
|
||||
|
||||
// Upload a local file and overwrite existing remote file
|
||||
await api.file.upload('C:\\my_file.txt', '/my_file.txt', progress_cb, true);
|
||||
|
||||
// Resume failed upload
|
||||
await api.file.upload('C:\\my_file.txt', '/my_file.txt', progress_cb, false, true);
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------------------------//
|
||||
// *********** Low-Level File Operations *********** //
|
||||
//------------------------------------------------------------------------------------------------//
|
||||
|
||||
// Create or open a remote file
|
||||
{
|
||||
const f = await api.file.create_or_open('/my_file.txt');
|
||||
await f.close();
|
||||
}
|
||||
|
||||
// Open an existing remote file
|
||||
{
|
||||
const f = await api.file.open('/my_file.txt');
|
||||
await f.close();
|
||||
}
|
||||
|
||||
// Write to a file
|
||||
{
|
||||
const f = await api.file.create_or_open('/my_file.txt');
|
||||
|
||||
const b = Buffer.alloc(1);
|
||||
b[0] = 1;
|
||||
await f.write(0, b); // write '1' byte at file offset '0'
|
||||
|
||||
await f.close();
|
||||
}
|
||||
|
||||
// Read from a file
|
||||
{
|
||||
const f = await api.file.create_or_open('/my_file.txt');
|
||||
const b = await f.read(0, 1); // read '1' byte from file offset '0'
|
||||
await f.close();
|
||||
}
|
||||
|
||||
// Truncate / resize file
|
||||
{
|
||||
const f = await api.file.create_or_open('/my_file.txt');
|
||||
await f.truncate(10);
|
||||
await f.close();
|
||||
}
|
||||
```
|
||||
# About
|
||||
|
||||
`repertory-js` is a Node.js module for interfacing with `repertory's` remote mount API.
|
||||
|
||||
## Installing
|
||||
|
||||
```shell
|
||||
npm i @blockstorage/repertory-js
|
||||
```
|
||||
|
||||
## Repertory Configuration
|
||||
|
||||
A Repertory mount must be active with the `EnableRemoteMount` setting enabled. `RemoteToken` should
|
||||
also be set to a strong, random password.
|
||||
|
||||
### Enabling Sia Remote Mount API on Windows Systems
|
||||
|
||||
```shell
|
||||
repertory.exe -unmount
|
||||
repertory.exe -set RemoteMount.EnableRemoteMount true
|
||||
repertory.exe -set RemoteMount.RemoteToken "my password"
|
||||
|
||||
[Optional - change listening port]
|
||||
repertory.exe -set RemoteMount.RemotePort 20202
|
||||
```
|
||||
|
||||
### Enabling Sia Remote Mount API on *NIX Systems
|
||||
|
||||
```shell
|
||||
./repertory -unmount
|
||||
./repertory -set RemoteMount.EnableRemoteMount true
|
||||
./repertory -set RemoteMount.RemoteToken "my password"
|
||||
|
||||
[Optional - change listening port]
|
||||
./repertory -set RemoteMount.RemotePort 20202
|
||||
```
|
||||
|
||||
### Skynet and ScPrime Mounts
|
||||
|
||||
* For Skynet mounts, add `-sk` argument to all commands listed above.
|
||||
* For ScPrime mounts, add `-sp` argument to all commands listed above.
|
||||
|
||||
## Module Environment Variables
|
||||
|
||||
* To successfully complete unit tests, a `repertory` mount supporting remote mount needs to be
|
||||
active. Set the following environment variables prior to running tests:
|
||||
* `TEST_HOST`
|
||||
* `TEST_PASSWORD`
|
||||
* `TEST_PORT`
|
||||
|
||||
## Example API Usage
|
||||
|
||||
```javascript
|
||||
import * as rep from "@blockstorage/repertory-js";
|
||||
//const rep = require("@blockstorage/repertory-js");
|
||||
|
||||
|
||||
// Repertory host settings
|
||||
const MY_HOST_OR_IP = 'localhost';
|
||||
const MY_PORT = 20000;
|
||||
const MY_TOKEN = 'password';
|
||||
|
||||
// Progress callback for uploads / downloads
|
||||
const progress_cb = (local_path, remote_path, progress, completed) => {
|
||||
console.log(local_path, remote_path, progress, completed);
|
||||
};
|
||||
|
||||
|
||||
//************************************************************************************************//
|
||||
// Step 1. Create a connection pool (recommended) //
|
||||
//************************************************************************************************//
|
||||
|
||||
const conn = await rep.create_pool(8, MY_HOST_OR_IP, MY_PORT, MY_TOKEN);
|
||||
/* Or create a single connection for light operations
|
||||
const conn = await rep.connect(MY_HOST_OR_IP, MY_PORT, MY_TOKEN);
|
||||
*/
|
||||
|
||||
/* Disconnect when complete
|
||||
await conn.disconnect();
|
||||
*/
|
||||
|
||||
//************************************************************************************************//
|
||||
// Step 2. Create an 'api' instance using the connection pool / connection //
|
||||
//************************************************************************************************//
|
||||
|
||||
const api = rep.create_api(conn);
|
||||
|
||||
|
||||
//************************************************************************************************//
|
||||
// Step 3. Use 'api' //
|
||||
//************************************************************************************************//
|
||||
|
||||
//------------------------------------------------------------------------------------------------//
|
||||
// *********** Directory Operations *********** //
|
||||
//------------------------------------------------------------------------------------------------//
|
||||
|
||||
// Check if directory exists
|
||||
const exists = await api.directory.exists('/my_directory');
|
||||
|
||||
// List directory contents
|
||||
await api.directory.list('/', async (remote_path, page_count, get_page) => {
|
||||
for (let i = 0; i < page_count; i++) {
|
||||
const items = await get_page(i); // Always 'await'
|
||||
console.log(items);
|
||||
}
|
||||
});
|
||||
|
||||
// Asynchronous directory list
|
||||
const snap = await api.directory.snapshot('/');
|
||||
try {
|
||||
for (let i = 0; i < snap.page_count; i++) {
|
||||
const items = await snap.get_page(i); // Always 'await'
|
||||
console.log(items);
|
||||
}
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
} finally {
|
||||
await snap.release();
|
||||
}
|
||||
|
||||
// Create new directory
|
||||
await api.directory.create('/test');
|
||||
|
||||
// Remove existing directory
|
||||
await api.directory.remove('/test')
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------------------------//
|
||||
// *********** File Operations *********** //
|
||||
//------------------------------------------------------------------------------------------------//
|
||||
|
||||
// Check if file exists
|
||||
const exists = await api.file.exists('/my_file.txt')
|
||||
|
||||
// Remove a file
|
||||
await api.file.remove('/my_file.txt')
|
||||
|
||||
// Download a remote file
|
||||
await api.file.download('/my_file.txt', 'C:\\my_file.txt', progress_cb);
|
||||
|
||||
// Download a remote file and overwrite existing local file
|
||||
await api.file.download('/my_file.txt', 'C:\\my_file.txt', progress_cb, true);
|
||||
|
||||
// Resume failed download
|
||||
await api.file.download('/my_file.txt', 'C:\\my_file.txt', progress_cb, false, true);
|
||||
|
||||
// Upload a local file
|
||||
await api.file.upload('C:\\my_file.txt', '/my_file.txt', progress_cb);
|
||||
|
||||
// Upload a local file and overwrite existing remote file
|
||||
await api.file.upload('C:\\my_file.txt', '/my_file.txt', progress_cb, true);
|
||||
|
||||
// Resume failed upload
|
||||
await api.file.upload('C:\\my_file.txt', '/my_file.txt', progress_cb, false, true);
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------------------------//
|
||||
// *********** Low-Level File Operations *********** //
|
||||
//------------------------------------------------------------------------------------------------//
|
||||
|
||||
// Create or open a remote file
|
||||
{
|
||||
const f = await api.file.create_or_open('/my_file.txt');
|
||||
await f.close();
|
||||
}
|
||||
|
||||
// Open an existing remote file
|
||||
{
|
||||
const f = await api.file.open('/my_file.txt');
|
||||
await f.close();
|
||||
}
|
||||
|
||||
// Write to a file
|
||||
{
|
||||
const f = await api.file.create_or_open('/my_file.txt');
|
||||
|
||||
const b = Buffer.alloc(1);
|
||||
b[0] = 1;
|
||||
await f.write(0, b); // write '1' byte at file offset '0'
|
||||
|
||||
await f.close();
|
||||
}
|
||||
|
||||
// Read from a file
|
||||
{
|
||||
const f = await api.file.create_or_open('/my_file.txt');
|
||||
const b = await f.read(0, 1); // read '1' byte from file offset '0'
|
||||
await f.close();
|
||||
}
|
||||
|
||||
// Truncate / resize file
|
||||
{
|
||||
const f = await api.file.create_or_open('/my_file.txt');
|
||||
await f.truncate(10);
|
||||
await f.close();
|
||||
}
|
||||
```
|
||||
|
||||
22
fixup
22
fixup
@@ -1,11 +1,11 @@
|
||||
cat <<EOF >dist/cjs/package.json
|
||||
{
|
||||
"type": "commonjs"
|
||||
}
|
||||
EOF
|
||||
|
||||
cat <<EOF >dist/mjs/package.json
|
||||
{
|
||||
"type": "module"
|
||||
}
|
||||
EOF
|
||||
cat <<EOF >dist/cjs/package.json
|
||||
{
|
||||
"type": "commonjs"
|
||||
}
|
||||
EOF
|
||||
|
||||
cat <<EOF >dist/mjs/package.json
|
||||
{
|
||||
"type": "module"
|
||||
}
|
||||
EOF
|
||||
|
||||
138
package.json
138
package.json
@@ -1,69 +1,69 @@
|
||||
{
|
||||
"name": "@blockstorage/repertory-js",
|
||||
"version": "1.4.0-r1",
|
||||
"description": "A Node.js module for interfacing with Repertory's remote mount API",
|
||||
"author": "scott.e.graves@protonmail.com",
|
||||
"license": "MIT",
|
||||
"homepage": "https://bitbucket.org/blockstorage/repertory-js",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://bitbucket.org/blockstorage/repertory-js.git"
|
||||
},
|
||||
"keywords": [
|
||||
"repertory",
|
||||
"repertory-ui",
|
||||
"library",
|
||||
"mount",
|
||||
"fuse",
|
||||
"winfsp",
|
||||
"blockchain",
|
||||
"decentralized",
|
||||
"cloud",
|
||||
"storage",
|
||||
"altcoin",
|
||||
"cryptocurrency"
|
||||
],
|
||||
"main": "dist/cjs/index.js",
|
||||
"module": "dist/mjs/index.js",
|
||||
"exports": {
|
||||
".": {
|
||||
"import": "./dist/mjs/index.js",
|
||||
"require": "./dist/cjs/index.js"
|
||||
}
|
||||
},
|
||||
"files": [
|
||||
"dist/cjs",
|
||||
"dist/mjs"
|
||||
],
|
||||
"scripts": {
|
||||
"build": "rollup -c && ./fixup",
|
||||
"test": "jest",
|
||||
"prepublish": "rollup -c --silent && ./fixup"
|
||||
},
|
||||
"dependencies": {
|
||||
"@stablelib/xchacha20poly1305": "^1.0.1",
|
||||
"int64-buffer": "^1.0.0",
|
||||
"socket-pool": "^1.2.3",
|
||||
"text-encoding": "^0.7.0",
|
||||
"uuid": "^8.3.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.14.3",
|
||||
"@babel/plugin-proposal-class-properties": "^7.13.0",
|
||||
"@babel/plugin-proposal-private-methods": "^7.13.0",
|
||||
"@babel/plugin-transform-async-to-generator": "^7.13.0",
|
||||
"@babel/plugin-transform-regenerator": "^7.13.15",
|
||||
"@babel/plugin-transform-runtime": "^7.14.3",
|
||||
"@babel/preset-env": "^7.14.2",
|
||||
"@rollup/plugin-babel": "^5.3.0",
|
||||
"@rollup/plugin-commonjs": "^15.1.0",
|
||||
"@rollup/plugin-json": "^4.0.0",
|
||||
"@rollup/plugin-node-resolve": "^9.0.0",
|
||||
"@types/jest": "^26.0.23",
|
||||
"babel-eslint": "^10.1.0",
|
||||
"jest": "^26.6.3",
|
||||
"rollup": "^2.50.0",
|
||||
"rollup-plugin-eslint": "^7.0.0",
|
||||
"rollup-plugin-terser": "^7.0.2"
|
||||
}
|
||||
}
|
||||
{
|
||||
"name": "@blockstorage/repertory-js",
|
||||
"version": "2.0.0-r1",
|
||||
"description": "A Node.js module for interfacing with Repertory's remote mount API",
|
||||
"author": "scott.e.graves@protonmail.com",
|
||||
"license": "MIT",
|
||||
"homepage": "https://bitbucket.org/blockstorage/repertory-js",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://bitbucket.org/blockstorage/repertory-js.git"
|
||||
},
|
||||
"keywords": [
|
||||
"repertory",
|
||||
"repertory-ui",
|
||||
"library",
|
||||
"mount",
|
||||
"fuse",
|
||||
"winfsp",
|
||||
"blockchain",
|
||||
"decentralized",
|
||||
"cloud",
|
||||
"storage",
|
||||
"altcoin",
|
||||
"cryptocurrency"
|
||||
],
|
||||
"main": "dist/cjs/index.js",
|
||||
"module": "dist/mjs/index.js",
|
||||
"exports": {
|
||||
".": {
|
||||
"import": "./dist/mjs/index.js",
|
||||
"require": "./dist/cjs/index.js"
|
||||
}
|
||||
},
|
||||
"files": [
|
||||
"dist/cjs",
|
||||
"dist/mjs"
|
||||
],
|
||||
"scripts": {
|
||||
"build": "rollup -c && ./fixup",
|
||||
"test": "jest",
|
||||
"prepublish": "rollup -c --silent && ./fixup"
|
||||
},
|
||||
"dependencies": {
|
||||
"@stablelib/xchacha20poly1305": "^1.0.1",
|
||||
"int64-buffer": "^1.0.0",
|
||||
"socket-pool": "^1.2.3",
|
||||
"text-encoding": "^0.7.0",
|
||||
"uuid": "^8.3.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.14.3",
|
||||
"@babel/plugin-proposal-class-properties": "^7.13.0",
|
||||
"@babel/plugin-proposal-private-methods": "^7.13.0",
|
||||
"@babel/plugin-transform-async-to-generator": "^7.13.0",
|
||||
"@babel/plugin-transform-regenerator": "^7.13.15",
|
||||
"@babel/plugin-transform-runtime": "^7.14.3",
|
||||
"@babel/preset-env": "^7.14.2",
|
||||
"@rollup/plugin-babel": "^5.3.0",
|
||||
"@rollup/plugin-commonjs": "^15.1.0",
|
||||
"@rollup/plugin-json": "^4.0.0",
|
||||
"@rollup/plugin-node-resolve": "^9.0.0",
|
||||
"@types/jest": "^26.0.23",
|
||||
"babel-eslint": "^10.1.0",
|
||||
"jest": "^26.6.3",
|
||||
"rollup": "^2.50.0",
|
||||
"rollup-plugin-eslint": "^7.0.0",
|
||||
"rollup-plugin-terser": "^7.0.2"
|
||||
}
|
||||
}
|
||||
|
||||
120
rollup.config.js
120
rollup.config.js
@@ -1,60 +1,60 @@
|
||||
import resolve from '@rollup/plugin-node-resolve';
|
||||
import commonjs from '@rollup/plugin-commonjs';
|
||||
import babel from '@rollup/plugin-babel';
|
||||
import { terser } from 'rollup-plugin-terser';
|
||||
import json from '@rollup/plugin-json';
|
||||
|
||||
const commonConfig = {
|
||||
input: 'src/index.js',
|
||||
output: {
|
||||
name: 'index',
|
||||
sourcemap: true,
|
||||
},
|
||||
plugins: [
|
||||
resolve({
|
||||
customResolveOptions: {
|
||||
moduleDirectory: 'node_modules',
|
||||
},
|
||||
}),
|
||||
babel({
|
||||
exclude: 'node_modules/**',
|
||||
babelHelpers: 'runtime',
|
||||
}),
|
||||
commonjs(),
|
||||
json(),
|
||||
],
|
||||
};
|
||||
|
||||
// ESM config
|
||||
const esmConfig = Object.assign({}, commonConfig);
|
||||
esmConfig.output = Object.assign({}, commonConfig.output, {
|
||||
file: 'dist/mjs/index.js',
|
||||
format: 'esm',
|
||||
});
|
||||
|
||||
// ESM prod config
|
||||
const esmProdConfig = Object.assign({}, esmConfig);
|
||||
esmProdConfig.output = Object.assign({}, esmConfig.output, {
|
||||
file: 'dist/mjs/index.min.js',
|
||||
sourcemap: false,
|
||||
});
|
||||
esmProdConfig.plugins = [...esmConfig.plugins, terser()];
|
||||
|
||||
// CJS config
|
||||
const cjsConfig = Object.assign({}, commonConfig);
|
||||
cjsConfig.output = Object.assign({}, commonConfig.output, {
|
||||
file: 'dist/cjs/index.js',
|
||||
format: 'cjs',
|
||||
});
|
||||
|
||||
// CJS prod config
|
||||
const cjsProdConfig = Object.assign({}, cjsConfig);
|
||||
cjsProdConfig.output = Object.assign({}, cjsConfig.output, {
|
||||
file: 'dist/cjs/index.min.js',
|
||||
sourcemap: false,
|
||||
});
|
||||
cjsProdConfig.plugins = [...cjsConfig.plugins, terser()];
|
||||
let configurations = [];
|
||||
configurations.push(esmConfig, esmProdConfig, cjsConfig, cjsProdConfig);
|
||||
|
||||
export default configurations;
|
||||
import resolve from '@rollup/plugin-node-resolve';
|
||||
import commonjs from '@rollup/plugin-commonjs';
|
||||
import babel from '@rollup/plugin-babel';
|
||||
import { terser } from 'rollup-plugin-terser';
|
||||
import json from '@rollup/plugin-json';
|
||||
|
||||
const commonConfig = {
|
||||
input: 'src/index.js',
|
||||
output: {
|
||||
name: 'index',
|
||||
sourcemap: true,
|
||||
},
|
||||
plugins: [
|
||||
resolve({
|
||||
customResolveOptions: {
|
||||
moduleDirectory: 'node_modules',
|
||||
},
|
||||
}),
|
||||
babel({
|
||||
exclude: 'node_modules/**',
|
||||
babelHelpers: 'runtime',
|
||||
}),
|
||||
commonjs(),
|
||||
json(),
|
||||
],
|
||||
};
|
||||
|
||||
// ESM config
|
||||
const esmConfig = Object.assign({}, commonConfig);
|
||||
esmConfig.output = Object.assign({}, commonConfig.output, {
|
||||
file: 'dist/mjs/index.js',
|
||||
format: 'esm',
|
||||
});
|
||||
|
||||
// ESM prod config
|
||||
const esmProdConfig = Object.assign({}, esmConfig);
|
||||
esmProdConfig.output = Object.assign({}, esmConfig.output, {
|
||||
file: 'dist/mjs/index.min.js',
|
||||
sourcemap: false,
|
||||
});
|
||||
esmProdConfig.plugins = [...esmConfig.plugins, terser()];
|
||||
|
||||
// CJS config
|
||||
const cjsConfig = Object.assign({}, commonConfig);
|
||||
cjsConfig.output = Object.assign({}, commonConfig.output, {
|
||||
file: 'dist/cjs/index.js',
|
||||
format: 'cjs',
|
||||
});
|
||||
|
||||
// CJS prod config
|
||||
const cjsProdConfig = Object.assign({}, cjsConfig);
|
||||
cjsProdConfig.output = Object.assign({}, cjsConfig.output, {
|
||||
file: 'dist/cjs/index.min.js',
|
||||
sourcemap: false,
|
||||
});
|
||||
cjsProdConfig.plugins = [...cjsConfig.plugins, terser()];
|
||||
let configurations = [];
|
||||
configurations.push(esmConfig, esmProdConfig, cjsConfig, cjsProdConfig);
|
||||
|
||||
export default configurations;
|
||||
|
||||
@@ -1,115 +1,115 @@
|
||||
import connection from '../networking/connection';
|
||||
import packet from '../networking/packet';
|
||||
import Socket from 'net';
|
||||
|
||||
test(`connect fails when error occurs during createConnection`, async () => {
|
||||
const mock_create = (port, host, cb) => {
|
||||
cb(new Error('mock create error'));
|
||||
};
|
||||
jest.spyOn(Socket, 'createConnection').mockImplementation(mock_create);
|
||||
|
||||
const conn = new connection('localhost', 20000);
|
||||
await expect(conn.connect()).rejects.toThrow(Error);
|
||||
});
|
||||
|
||||
test(`socket receive data fails when decryption fails`, async () => {
|
||||
let cbl = {};
|
||||
const socket = {
|
||||
setNoDelay: () => {},
|
||||
setKeepAlive: () => {},
|
||||
on: (name, cb) => {
|
||||
cbl[name] = cb;
|
||||
},
|
||||
};
|
||||
|
||||
const conn = new connection('', 0, 'b', socket);
|
||||
let reject;
|
||||
const mock_reject = jest.fn().mockImplementation((e) => reject(e));
|
||||
conn.reject = mock_reject;
|
||||
conn.resolve = jest.fn();
|
||||
|
||||
const p = new packet('a');
|
||||
await p.encrypt();
|
||||
p.encode_top_ui32(p.buffer.length);
|
||||
await expect(
|
||||
new Promise((_, r) => {
|
||||
reject = r;
|
||||
cbl['data'](Buffer.from(p.buffer));
|
||||
})
|
||||
).rejects.toThrow(Error);
|
||||
expect(mock_reject.mock.calls.length).toBe(1);
|
||||
});
|
||||
|
||||
test(`disconnect succeeds if an error is thrown`, async () => {
|
||||
const socket = {
|
||||
setNoDelay: () => {},
|
||||
setKeepAlive: () => {},
|
||||
destroy: () => {
|
||||
throw new Error('mock destroy error');
|
||||
},
|
||||
on: () => {},
|
||||
};
|
||||
|
||||
const conn = new connection('', 0, 'b', socket);
|
||||
await conn.disconnect();
|
||||
});
|
||||
|
||||
test(`send fails on socket error`, async () => {
|
||||
let cbl = {};
|
||||
const socket = {
|
||||
setNoDelay: () => {},
|
||||
setKeepAlive: () => {},
|
||||
on: (name, cb) => {
|
||||
cbl[name] = cb;
|
||||
},
|
||||
};
|
||||
|
||||
const conn = new connection('', 0, 'b', socket);
|
||||
const mock_reject = jest.fn();
|
||||
conn.reject = mock_reject;
|
||||
conn.resolve = jest.fn();
|
||||
|
||||
cbl['error']('socket error');
|
||||
expect(mock_reject).toBeCalled();
|
||||
});
|
||||
|
||||
test(`error is thrown when socket is closed`, async () => {
|
||||
let cbl = {};
|
||||
const socket = {
|
||||
setNoDelay: () => {},
|
||||
setKeepAlive: () => {},
|
||||
on: (name, cb) => {
|
||||
cbl[name] = cb;
|
||||
},
|
||||
};
|
||||
|
||||
const conn = new connection('', 0, 'b', socket);
|
||||
const mock_reject = jest.fn();
|
||||
conn.reject = mock_reject;
|
||||
conn.resolve = jest.fn();
|
||||
|
||||
cbl['close']();
|
||||
expect(mock_reject).toBeCalled();
|
||||
});
|
||||
|
||||
test(`send fails when write error occurs`, async () => {
|
||||
let cbl = {};
|
||||
const socket = {
|
||||
setNoDelay: () => {},
|
||||
setKeepAlive: () => {},
|
||||
on: (name, cb) => {
|
||||
cbl[name] = cb;
|
||||
},
|
||||
write: (b, c, cb) => {
|
||||
cb('mock write error');
|
||||
},
|
||||
};
|
||||
|
||||
const conn = new connection('', 0, 'b', socket);
|
||||
try {
|
||||
await conn.send('c', new packet('b'));
|
||||
expect('send should fail').toBeNull();
|
||||
} catch (err) {
|
||||
expect(err).toBeDefined();
|
||||
}
|
||||
});
|
||||
import connection from '../networking/connection';
|
||||
import packet from '../networking/packet';
|
||||
import Socket from 'net';
|
||||
|
||||
test(`connect fails when error occurs during createConnection`, async () => {
|
||||
const mock_create = (port, host, cb) => {
|
||||
cb(new Error('mock create error'));
|
||||
};
|
||||
jest.spyOn(Socket, 'createConnection').mockImplementation(mock_create);
|
||||
|
||||
const conn = new connection('localhost', 20000);
|
||||
await expect(conn.connect()).rejects.toThrow(Error);
|
||||
});
|
||||
|
||||
test(`socket receive data fails when decryption fails`, async () => {
|
||||
let cbl = {};
|
||||
const socket = {
|
||||
setNoDelay: () => {},
|
||||
setKeepAlive: () => {},
|
||||
on: (name, cb) => {
|
||||
cbl[name] = cb;
|
||||
},
|
||||
};
|
||||
|
||||
const conn = new connection('', 0, 'b', socket);
|
||||
let reject;
|
||||
const mock_reject = jest.fn().mockImplementation((e) => reject(e));
|
||||
conn.reject = mock_reject;
|
||||
conn.resolve = jest.fn();
|
||||
|
||||
const p = new packet('a');
|
||||
await p.encrypt();
|
||||
p.encode_top_ui32(p.buffer.length);
|
||||
await expect(
|
||||
new Promise((_, r) => {
|
||||
reject = r;
|
||||
cbl['data'](Buffer.from(p.buffer));
|
||||
})
|
||||
).rejects.toThrow(Error);
|
||||
expect(mock_reject.mock.calls.length).toBe(1);
|
||||
});
|
||||
|
||||
test(`disconnect succeeds if an error is thrown`, async () => {
|
||||
const socket = {
|
||||
setNoDelay: () => {},
|
||||
setKeepAlive: () => {},
|
||||
destroy: () => {
|
||||
throw new Error('mock destroy error');
|
||||
},
|
||||
on: () => {},
|
||||
};
|
||||
|
||||
const conn = new connection('', 0, 'b', socket);
|
||||
await conn.disconnect();
|
||||
});
|
||||
|
||||
test(`send fails on socket error`, async () => {
|
||||
let cbl = {};
|
||||
const socket = {
|
||||
setNoDelay: () => {},
|
||||
setKeepAlive: () => {},
|
||||
on: (name, cb) => {
|
||||
cbl[name] = cb;
|
||||
},
|
||||
};
|
||||
|
||||
const conn = new connection('', 0, 'b', socket);
|
||||
const mock_reject = jest.fn();
|
||||
conn.reject = mock_reject;
|
||||
conn.resolve = jest.fn();
|
||||
|
||||
cbl['error']('socket error');
|
||||
expect(mock_reject).toBeCalled();
|
||||
});
|
||||
|
||||
test(`error is thrown when socket is closed`, async () => {
|
||||
let cbl = {};
|
||||
const socket = {
|
||||
setNoDelay: () => {},
|
||||
setKeepAlive: () => {},
|
||||
on: (name, cb) => {
|
||||
cbl[name] = cb;
|
||||
},
|
||||
};
|
||||
|
||||
const conn = new connection('', 0, 'b', socket);
|
||||
const mock_reject = jest.fn();
|
||||
conn.reject = mock_reject;
|
||||
conn.resolve = jest.fn();
|
||||
|
||||
cbl['close']();
|
||||
expect(mock_reject).toBeCalled();
|
||||
});
|
||||
|
||||
test(`send fails when write error occurs`, async () => {
|
||||
let cbl = {};
|
||||
const socket = {
|
||||
setNoDelay: () => {},
|
||||
setKeepAlive: () => {},
|
||||
on: (name, cb) => {
|
||||
cbl[name] = cb;
|
||||
},
|
||||
write: (b, c, cb) => {
|
||||
cb('mock write error');
|
||||
},
|
||||
};
|
||||
|
||||
const conn = new connection('', 0, 'b', socket);
|
||||
try {
|
||||
await conn.send('c', new packet('b'));
|
||||
expect('send should fail').toBeNull();
|
||||
} catch (err) {
|
||||
expect(err).toBeDefined();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,59 +1,59 @@
|
||||
import connection_pool from '../networking/connection_pool';
|
||||
import packet from '../networking/packet';
|
||||
import connection from '../networking/connection';
|
||||
|
||||
jest.mock('../networking/connection');
|
||||
|
||||
test(`construction fails if pool size is <= 1`, () => {
|
||||
expect(() => new connection_pool(1)).toThrow(Error);
|
||||
expect(() => new connection_pool(0)).toThrow(Error);
|
||||
expect(() => new connection_pool(-1)).toThrow(Error);
|
||||
});
|
||||
|
||||
test(`error on socket release is ignored`, async () => {
|
||||
const conn = new connection_pool(2, '', 20000);
|
||||
let invoked = false;
|
||||
jest.spyOn(conn.pool, 'acquire').mockImplementation(() => {
|
||||
return {
|
||||
release: () => {
|
||||
invoked = true;
|
||||
throw new Error('mock release error');
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
const mock_send = jest.fn();
|
||||
connection.prototype.send = async () => {
|
||||
return mock_send();
|
||||
};
|
||||
mock_send.mockResolvedValue(0);
|
||||
|
||||
expect(await conn.send('', new packet())).toEqual(0);
|
||||
expect(invoked).toBeTruthy();
|
||||
});
|
||||
|
||||
test(`connection pool send fails if socket acquire fails`, async () => {
|
||||
const conn = new connection_pool(2, '', 20000);
|
||||
jest.spyOn(conn.pool, 'acquire').mockImplementation(() => {
|
||||
throw new Error('mock acquire exception');
|
||||
});
|
||||
|
||||
await expect(conn.send('', new packet())).rejects.toThrow(Error);
|
||||
});
|
||||
|
||||
test(`connection pool send fails when connection send fails`, async () => {
|
||||
const conn = new connection_pool(2, '', 20000);
|
||||
jest.spyOn(conn.pool, 'acquire').mockImplementation(() => {
|
||||
return {
|
||||
release: () => {},
|
||||
};
|
||||
});
|
||||
|
||||
const mock_send = jest.fn();
|
||||
connection.prototype.send = async () => {
|
||||
return mock_send();
|
||||
};
|
||||
mock_send.mockRejectedValue(new Error('mock send failed'));
|
||||
|
||||
await expect(conn.send('', new packet())).rejects.toThrow(Error);
|
||||
});
|
||||
import connection_pool from '../networking/connection_pool';
|
||||
import packet from '../networking/packet';
|
||||
import connection from '../networking/connection';
|
||||
|
||||
jest.mock('../networking/connection');
|
||||
|
||||
test(`construction fails if pool size is <= 1`, () => {
|
||||
expect(() => new connection_pool(1)).toThrow(Error);
|
||||
expect(() => new connection_pool(0)).toThrow(Error);
|
||||
expect(() => new connection_pool(-1)).toThrow(Error);
|
||||
});
|
||||
|
||||
test(`error on socket release is ignored`, async () => {
|
||||
const conn = new connection_pool(2, '', 20000);
|
||||
let invoked = false;
|
||||
jest.spyOn(conn.pool, 'acquire').mockImplementation(() => {
|
||||
return {
|
||||
release: () => {
|
||||
invoked = true;
|
||||
throw new Error('mock release error');
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
const mock_send = jest.fn();
|
||||
connection.prototype.send = async () => {
|
||||
return mock_send();
|
||||
};
|
||||
mock_send.mockResolvedValue(0);
|
||||
|
||||
expect(await conn.send('', new packet())).toEqual(0);
|
||||
expect(invoked).toBeTruthy();
|
||||
});
|
||||
|
||||
test(`connection pool send fails if socket acquire fails`, async () => {
|
||||
const conn = new connection_pool(2, '', 20000);
|
||||
jest.spyOn(conn.pool, 'acquire').mockImplementation(() => {
|
||||
throw new Error('mock acquire exception');
|
||||
});
|
||||
|
||||
await expect(conn.send('', new packet())).rejects.toThrow(Error);
|
||||
});
|
||||
|
||||
test(`connection pool send fails when connection send fails`, async () => {
|
||||
const conn = new connection_pool(2, '', 20000);
|
||||
jest.spyOn(conn.pool, 'acquire').mockImplementation(() => {
|
||||
return {
|
||||
release: () => {},
|
||||
};
|
||||
});
|
||||
|
||||
const mock_send = jest.fn();
|
||||
connection.prototype.send = async () => {
|
||||
return mock_send();
|
||||
};
|
||||
mock_send.mockRejectedValue(new Error('mock send failed'));
|
||||
|
||||
await expect(conn.send('', new packet())).rejects.toThrow(Error);
|
||||
});
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
import { get_version, instance_id, package_json } from '../utils/constants';
|
||||
import * as uuid from 'uuid';
|
||||
|
||||
test(`can read 'package.json'`, () => {
|
||||
console.log(package_json);
|
||||
expect(package_json).toBeDefined();
|
||||
});
|
||||
|
||||
test(`'instance_id' is valid`, () => {
|
||||
console.log(instance_id);
|
||||
expect(instance_id).toBeDefined();
|
||||
expect(uuid.parse(instance_id)).toBeInstanceOf(Uint8Array);
|
||||
});
|
||||
|
||||
test(`'version' can be read from 'package.json'`, () => {
|
||||
console.log(get_version());
|
||||
expect(get_version()).toBe('1.4.0-r1');
|
||||
});
|
||||
import { get_version, instance_id, package_json } from '../utils/constants';
|
||||
import * as uuid from 'uuid';
|
||||
|
||||
test(`can read 'package.json'`, () => {
|
||||
console.log(package_json);
|
||||
expect(package_json).toBeDefined();
|
||||
});
|
||||
|
||||
test(`'instance_id' is valid`, () => {
|
||||
console.log(instance_id);
|
||||
expect(instance_id).toBeDefined();
|
||||
expect(uuid.parse(instance_id)).toBeInstanceOf(Uint8Array);
|
||||
});
|
||||
|
||||
test(`'version' can be read from 'package.json'`, () => {
|
||||
console.log(get_version());
|
||||
expect(get_version()).toBe('1.4.0-r1');
|
||||
});
|
||||
|
||||
@@ -1,47 +1,47 @@
|
||||
import file from '../io/file';
|
||||
|
||||
jest.mock('../ops/index.js', () => ({
|
||||
...jest.requireActual('../ops/index.js'),
|
||||
close_file: jest.fn(),
|
||||
}));
|
||||
|
||||
import { close_file } from '../ops/index';
|
||||
|
||||
test(`can close a closed file`, async () => {
|
||||
const f = new file();
|
||||
expect(await f.close()).toEqual(0);
|
||||
});
|
||||
|
||||
test(`'get_size' fails on closed file`, async () => {
|
||||
const f = new file();
|
||||
await expect(f.get_size()).rejects.toThrow(Error);
|
||||
});
|
||||
|
||||
test(`'read' fails on closed file`, async () => {
|
||||
const f = new file();
|
||||
await expect(f.read(0, 10)).rejects.toThrow(Error);
|
||||
});
|
||||
|
||||
test(`'truncate' fails on closed file`, async () => {
|
||||
const f = new file();
|
||||
await expect(f.truncate(0)).rejects.toThrow(Error);
|
||||
});
|
||||
|
||||
test(`'write' fails on closed file`, async () => {
|
||||
const f = new file();
|
||||
await expect(f.write(0, Buffer.alloc(2))).rejects.toThrow(Error);
|
||||
});
|
||||
|
||||
test(`handle is set to null on close`, async () => {
|
||||
const f = new file(null, 1, '/path');
|
||||
close_file.mockReturnValue(0);
|
||||
expect(await f.close()).toEqual(0);
|
||||
expect(f.handle).toBeNull();
|
||||
});
|
||||
|
||||
test(`handle is not changed on close if return is not 0`, async () => {
|
||||
const f = new file(null, 1, '/path');
|
||||
close_file.mockReturnValue(1);
|
||||
expect(await f.close()).toEqual(1);
|
||||
expect(f.handle).toBe(1);
|
||||
});
|
||||
import file from '../io/file';
|
||||
|
||||
jest.mock('../ops/index.js', () => ({
|
||||
...jest.requireActual('../ops/index.js'),
|
||||
close_file: jest.fn(),
|
||||
}));
|
||||
|
||||
import { close_file } from '../ops/index';
|
||||
|
||||
test(`can close a closed file`, async () => {
|
||||
const f = new file();
|
||||
expect(await f.close()).toEqual(0);
|
||||
});
|
||||
|
||||
test(`'get_size' fails on closed file`, async () => {
|
||||
const f = new file();
|
||||
await expect(f.get_size()).rejects.toThrow(Error);
|
||||
});
|
||||
|
||||
test(`'read' fails on closed file`, async () => {
|
||||
const f = new file();
|
||||
await expect(f.read(0, 10)).rejects.toThrow(Error);
|
||||
});
|
||||
|
||||
test(`'truncate' fails on closed file`, async () => {
|
||||
const f = new file();
|
||||
await expect(f.truncate(0)).rejects.toThrow(Error);
|
||||
});
|
||||
|
||||
test(`'write' fails on closed file`, async () => {
|
||||
const f = new file();
|
||||
await expect(f.write(0, Buffer.alloc(2))).rejects.toThrow(Error);
|
||||
});
|
||||
|
||||
test(`handle is set to null on close`, async () => {
|
||||
const f = new file(null, 1, '/path');
|
||||
close_file.mockReturnValue(0);
|
||||
expect(await f.close()).toEqual(0);
|
||||
expect(f.handle).toBeNull();
|
||||
});
|
||||
|
||||
test(`handle is not changed on close if return is not 0`, async () => {
|
||||
const f = new file(null, 1, '/path');
|
||||
close_file.mockReturnValue(1);
|
||||
expect(await f.close()).toEqual(1);
|
||||
expect(f.handle).toBe(1);
|
||||
});
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
import packet from '../networking/packet';
|
||||
|
||||
test('can construct a packet', () => {
|
||||
const p = new packet('my password');
|
||||
console.log(p);
|
||||
expect(p.token).toEqual('my password');
|
||||
expect(p.buffer).toEqual(new Uint8Array(0));
|
||||
expect(p.decode_offset).toEqual(0);
|
||||
});
|
||||
|
||||
test('can encrypt and decrypt a packet', async () => {
|
||||
console.log('testing');
|
||||
const p = new packet('my password');
|
||||
p.encode_utf8('moose');
|
||||
await p.encrypt();
|
||||
await p.decrypt();
|
||||
const str = p.decode_utf8();
|
||||
expect(str).toEqual('moose');
|
||||
});
|
||||
import packet from '../networking/packet';
|
||||
|
||||
test('can construct a packet', () => {
|
||||
const p = new packet('my password');
|
||||
console.log(p);
|
||||
expect(p.token).toEqual('my password');
|
||||
expect(p.buffer).toEqual(new Uint8Array(0));
|
||||
expect(p.decode_offset).toEqual(0);
|
||||
});
|
||||
|
||||
test('can encrypt and decrypt a packet', async () => {
|
||||
console.log('testing');
|
||||
const p = new packet('my password');
|
||||
p.encode_utf8('moose');
|
||||
await p.encrypt();
|
||||
await p.decrypt();
|
||||
const str = p.decode_utf8();
|
||||
expect(str).toEqual('moose');
|
||||
});
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
188
src/index.js
188
src/index.js
@@ -1,94 +1,94 @@
|
||||
import file from './io/file';
|
||||
import connection from './networking/connection';
|
||||
import connection_pool from './networking/connection_pool';
|
||||
import * as ops from './ops';
|
||||
|
||||
export * as byte_order from './utils/byte_order';
|
||||
export { getCustomEncryption, setCustomEncryption } from './utils/constants';
|
||||
export { default as packet } from './networking/packet';
|
||||
|
||||
export const connect = async (host_or_ip, port, password) => {
|
||||
const conn = new connection(host_or_ip, port, password);
|
||||
await conn.connect();
|
||||
return conn;
|
||||
};
|
||||
|
||||
export const create_api = (conn) => {
|
||||
return {
|
||||
directory: {
|
||||
create: async (remote_path) => ops.create_directory(conn, remote_path),
|
||||
exists: async (remote_path) => {
|
||||
try {
|
||||
const info = await ops.get_file_attributes2(conn, remote_path);
|
||||
return info.directory;
|
||||
} catch (e) {
|
||||
if (e.message.split(':')[1].trim() == '-2') {
|
||||
return false;
|
||||
}
|
||||
throw new Error(e.message);
|
||||
}
|
||||
},
|
||||
list: async (remote_path, page_reader_cb) =>
|
||||
ops.list_directory(conn, remote_path, page_reader_cb),
|
||||
remove: async (remote_path) => ops.remove_directory(conn, remote_path),
|
||||
snapshot: async (remote_path) => {
|
||||
return ops.snapshot_directory(conn, remote_path);
|
||||
},
|
||||
},
|
||||
file: {
|
||||
create_or_open: async (remote_path) =>
|
||||
new file(
|
||||
conn,
|
||||
await ops.create_or_open_file(conn, remote_path),
|
||||
remote_path
|
||||
),
|
||||
download: async (
|
||||
remote_path,
|
||||
local_path,
|
||||
progress_cb,
|
||||
overwrite,
|
||||
resume
|
||||
) =>
|
||||
ops.download_file(
|
||||
conn,
|
||||
remote_path,
|
||||
local_path,
|
||||
progress_cb,
|
||||
overwrite,
|
||||
resume
|
||||
),
|
||||
exists: async (remote_path) => {
|
||||
try {
|
||||
const info = await ops.get_file_attributes2(conn, remote_path);
|
||||
return !info.directory;
|
||||
} catch (e) {
|
||||
if (e.message.split(':')[1].trim() == '-2') {
|
||||
return false;
|
||||
}
|
||||
throw new Error(e.message);
|
||||
}
|
||||
},
|
||||
open: async (remote_path) =>
|
||||
new file(conn, await ops.open_file(conn, remote_path), remote_path),
|
||||
remove: async (remote_path) => ops.remove_file(conn, remote_path),
|
||||
upload: async (local_path, remote_path, progress_cb, overwrite, resume) =>
|
||||
ops.upload_file(
|
||||
conn,
|
||||
local_path,
|
||||
remote_path,
|
||||
progress_cb,
|
||||
overwrite,
|
||||
resume
|
||||
),
|
||||
},
|
||||
get_drive_information: async () => ops.get_drive_information(conn),
|
||||
};
|
||||
};
|
||||
|
||||
export const create_pool = async (pool_size, host_or_ip, port, password) => {
|
||||
if (pool_size <= 1) {
|
||||
return connect(host_or_ip, port, password);
|
||||
}
|
||||
|
||||
return new connection_pool(pool_size, host_or_ip, port, password);
|
||||
};
|
||||
import file from './io/file';
|
||||
import connection from './networking/connection';
|
||||
import connection_pool from './networking/connection_pool';
|
||||
import * as ops from './ops';
|
||||
|
||||
export * as byte_order from './utils/byte_order';
|
||||
export { getCustomEncryption, setCustomEncryption } from './utils/constants';
|
||||
export { default as packet } from './networking/packet';
|
||||
|
||||
export const connect = async (host_or_ip, port, password) => {
|
||||
const conn = new connection(host_or_ip, port, password);
|
||||
await conn.connect();
|
||||
return conn;
|
||||
};
|
||||
|
||||
export const create_api = (conn) => {
|
||||
return {
|
||||
directory: {
|
||||
create: async (remote_path) => ops.create_directory(conn, remote_path),
|
||||
exists: async (remote_path) => {
|
||||
try {
|
||||
const info = await ops.get_file_attributes2(conn, remote_path);
|
||||
return info.directory;
|
||||
} catch (e) {
|
||||
if (e.message.split(':')[1].trim() == '-2') {
|
||||
return false;
|
||||
}
|
||||
throw new Error(e.message);
|
||||
}
|
||||
},
|
||||
list: async (remote_path, page_reader_cb) =>
|
||||
ops.list_directory(conn, remote_path, page_reader_cb),
|
||||
remove: async (remote_path) => ops.remove_directory(conn, remote_path),
|
||||
snapshot: async (remote_path) => {
|
||||
return ops.snapshot_directory(conn, remote_path);
|
||||
},
|
||||
},
|
||||
file: {
|
||||
create_or_open: async (remote_path) =>
|
||||
new file(
|
||||
conn,
|
||||
await ops.create_or_open_file(conn, remote_path),
|
||||
remote_path
|
||||
),
|
||||
download: async (
|
||||
remote_path,
|
||||
local_path,
|
||||
progress_cb,
|
||||
overwrite,
|
||||
resume
|
||||
) =>
|
||||
ops.download_file(
|
||||
conn,
|
||||
remote_path,
|
||||
local_path,
|
||||
progress_cb,
|
||||
overwrite,
|
||||
resume
|
||||
),
|
||||
exists: async (remote_path) => {
|
||||
try {
|
||||
const info = await ops.get_file_attributes2(conn, remote_path);
|
||||
return !info.directory;
|
||||
} catch (e) {
|
||||
if (e.message.split(':')[1].trim() == '-2') {
|
||||
return false;
|
||||
}
|
||||
throw new Error(e.message);
|
||||
}
|
||||
},
|
||||
open: async (remote_path) =>
|
||||
new file(conn, await ops.open_file(conn, remote_path), remote_path),
|
||||
remove: async (remote_path) => ops.remove_file(conn, remote_path),
|
||||
upload: async (local_path, remote_path, progress_cb, overwrite, resume) =>
|
||||
ops.upload_file(
|
||||
conn,
|
||||
local_path,
|
||||
remote_path,
|
||||
progress_cb,
|
||||
overwrite,
|
||||
resume
|
||||
),
|
||||
},
|
||||
get_drive_information: async () => ops.get_drive_information(conn),
|
||||
};
|
||||
};
|
||||
|
||||
export const create_pool = async (pool_size, host_or_ip, port, password) => {
|
||||
if (pool_size <= 1) {
|
||||
return connect(host_or_ip, port, password);
|
||||
}
|
||||
|
||||
return new connection_pool(pool_size, host_or_ip, port, password);
|
||||
};
|
||||
|
||||
216
src/io/file.js
216
src/io/file.js
@@ -1,108 +1,108 @@
|
||||
import * as ops from '../ops';
|
||||
|
||||
let next_thread_id = 1;
|
||||
|
||||
export default class file {
|
||||
constructor(conn, handle, remote_path) {
|
||||
this.conn = conn;
|
||||
this.handle = handle || null;
|
||||
this.remote_path = remote_path;
|
||||
this.thread_id = next_thread_id++;
|
||||
}
|
||||
|
||||
conn;
|
||||
handle = null;
|
||||
thread_id;
|
||||
remote_path;
|
||||
|
||||
async close() {
|
||||
if (this.handle !== null) {
|
||||
const result = await ops.close_file(
|
||||
this.conn,
|
||||
this.remote_path,
|
||||
this.handle,
|
||||
this.thread_id
|
||||
);
|
||||
if (result === 0) {
|
||||
this.handle = null;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
async get_size() {
|
||||
if (this.handle === null) {
|
||||
return Promise.reject(new Error("'get_size()' failed: invalid handle"));
|
||||
}
|
||||
|
||||
const attrs = await ops.get_file_attributes(
|
||||
this.conn,
|
||||
this.handle,
|
||||
this.remote_path,
|
||||
this.thread_id
|
||||
);
|
||||
return attrs.size;
|
||||
}
|
||||
|
||||
async read(offset, length) {
|
||||
if (this.handle === null) {
|
||||
return Promise.reject(new Error("'read()' failed: invalid handle"));
|
||||
}
|
||||
|
||||
return ops.read_file(
|
||||
this.conn,
|
||||
this.handle,
|
||||
this.remote_path,
|
||||
offset,
|
||||
length,
|
||||
this.thread_id
|
||||
);
|
||||
}
|
||||
|
||||
async truncate(length) {
|
||||
if (this.handle === null) {
|
||||
return Promise.reject(new Error("'truncate()' failed: invalid handle"));
|
||||
}
|
||||
|
||||
return ops.truncate_file(
|
||||
this.conn,
|
||||
this.handle,
|
||||
this.remote_path,
|
||||
length,
|
||||
this.thread_id
|
||||
);
|
||||
}
|
||||
|
||||
async write(offset, buffer) {
|
||||
if (this.handle === null) {
|
||||
return Promise.reject(new Error("'write()' failed: invalid handle"));
|
||||
}
|
||||
|
||||
return ops.write_file(
|
||||
this.conn,
|
||||
this.handle,
|
||||
this.remote_path,
|
||||
offset,
|
||||
buffer,
|
||||
this.thread_id
|
||||
);
|
||||
}
|
||||
|
||||
async write_base64(offset, base64_string) {
|
||||
if (this.handle === null) {
|
||||
return Promise.reject(
|
||||
new Error("'write_base64()' failed: invalid handle")
|
||||
);
|
||||
}
|
||||
return ops.write_base64_file(
|
||||
this.conn,
|
||||
this.handle,
|
||||
this.remote_path,
|
||||
offset,
|
||||
base64_string,
|
||||
this.thread_id
|
||||
);
|
||||
}
|
||||
}
|
||||
import * as ops from '../ops';
|
||||
|
||||
let next_thread_id = 1;
|
||||
|
||||
export default class file {
|
||||
constructor(conn, handle, remote_path) {
|
||||
this.conn = conn;
|
||||
this.handle = handle || null;
|
||||
this.remote_path = remote_path;
|
||||
this.thread_id = next_thread_id++;
|
||||
}
|
||||
|
||||
conn;
|
||||
handle = null;
|
||||
thread_id;
|
||||
remote_path;
|
||||
|
||||
async close() {
|
||||
if (this.handle !== null) {
|
||||
const result = await ops.close_file(
|
||||
this.conn,
|
||||
this.remote_path,
|
||||
this.handle,
|
||||
this.thread_id
|
||||
);
|
||||
if (result === 0) {
|
||||
this.handle = null;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
async get_size() {
|
||||
if (this.handle === null) {
|
||||
return Promise.reject(new Error("'get_size()' failed: invalid handle"));
|
||||
}
|
||||
|
||||
const attrs = await ops.get_file_attributes(
|
||||
this.conn,
|
||||
this.handle,
|
||||
this.remote_path,
|
||||
this.thread_id
|
||||
);
|
||||
return attrs.size;
|
||||
}
|
||||
|
||||
async read(offset, length) {
|
||||
if (this.handle === null) {
|
||||
return Promise.reject(new Error("'read()' failed: invalid handle"));
|
||||
}
|
||||
|
||||
return ops.read_file(
|
||||
this.conn,
|
||||
this.handle,
|
||||
this.remote_path,
|
||||
offset,
|
||||
length,
|
||||
this.thread_id
|
||||
);
|
||||
}
|
||||
|
||||
async truncate(length) {
|
||||
if (this.handle === null) {
|
||||
return Promise.reject(new Error("'truncate()' failed: invalid handle"));
|
||||
}
|
||||
|
||||
return ops.truncate_file(
|
||||
this.conn,
|
||||
this.handle,
|
||||
this.remote_path,
|
||||
length,
|
||||
this.thread_id
|
||||
);
|
||||
}
|
||||
|
||||
async write(offset, buffer) {
|
||||
if (this.handle === null) {
|
||||
return Promise.reject(new Error("'write()' failed: invalid handle"));
|
||||
}
|
||||
|
||||
return ops.write_file(
|
||||
this.conn,
|
||||
this.handle,
|
||||
this.remote_path,
|
||||
offset,
|
||||
buffer,
|
||||
this.thread_id
|
||||
);
|
||||
}
|
||||
|
||||
async write_base64(offset, base64_string) {
|
||||
if (this.handle === null) {
|
||||
return Promise.reject(
|
||||
new Error("'write_base64()' failed: invalid handle")
|
||||
);
|
||||
}
|
||||
return ops.write_base64_file(
|
||||
this.conn,
|
||||
this.handle,
|
||||
this.remote_path,
|
||||
offset,
|
||||
base64_string,
|
||||
this.thread_id
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,177 +1,177 @@
|
||||
import Socket from 'net';
|
||||
|
||||
import * as constants from '../utils/constants';
|
||||
|
||||
import packet from './packet';
|
||||
|
||||
export default class connection {
|
||||
constructor(host_or_ip, port, password, socket) {
|
||||
this.host_or_ip = host_or_ip;
|
||||
this.port = port;
|
||||
this.password = password;
|
||||
if (socket) {
|
||||
this.socket = socket;
|
||||
this.connected = true;
|
||||
this.setup_socket();
|
||||
}
|
||||
}
|
||||
|
||||
connected = false;
|
||||
host_or_ip = '';
|
||||
nonce = '';
|
||||
password = '';
|
||||
port = 20000;
|
||||
reject;
|
||||
resolve;
|
||||
socket;
|
||||
|
||||
cleanup_handlers() {
|
||||
this.reject = null;
|
||||
this.resolve = null;
|
||||
}
|
||||
|
||||
async connect() {
|
||||
if (!this.socket) {
|
||||
try {
|
||||
await new Promise((resolve, reject) => {
|
||||
this.socket = Socket.createConnection(
|
||||
this.port,
|
||||
this.host_or_ip,
|
||||
(err) => {
|
||||
if (err) {
|
||||
console.log(err);
|
||||
return reject(err);
|
||||
}
|
||||
|
||||
this.reject = reject;
|
||||
this.resolve = resolve;
|
||||
}
|
||||
);
|
||||
});
|
||||
} catch (err) {
|
||||
return Promise.reject(new Error(`'connect()' failed: ${err}`));
|
||||
}
|
||||
|
||||
this.connected = true;
|
||||
this.setup_socket();
|
||||
}
|
||||
}
|
||||
|
||||
setup_socket() {
|
||||
let buffer;
|
||||
const cleanup = () => {
|
||||
this.cleanup_handlers();
|
||||
buffer = null;
|
||||
};
|
||||
if (this.socket._socket) {
|
||||
this.socket._socket.setNoDelay(true);
|
||||
this.socket._socket.setKeepAlive(true);
|
||||
} else {
|
||||
this.socket.setNoDelay(true);
|
||||
this.socket.setKeepAlive(true);
|
||||
}
|
||||
|
||||
this.socket.on('data', (chunk) => {
|
||||
buffer = buffer ? Buffer.concat([buffer, chunk]) : chunk;
|
||||
if (buffer.length > 4) {
|
||||
const size = buffer.readUInt32BE(0);
|
||||
if (buffer.length >= size + 4) {
|
||||
const packet_data = buffer.slice(4, 4 + size);
|
||||
if (this.resolve) {
|
||||
const complete = () => {
|
||||
const reject = this.reject;
|
||||
const resolve = this.resolve;
|
||||
cleanup();
|
||||
return {
|
||||
reject,
|
||||
resolve,
|
||||
};
|
||||
};
|
||||
|
||||
const response = new packet(this.password);
|
||||
response.buffer = new Uint8Array(packet_data);
|
||||
response
|
||||
.decrypt()
|
||||
.then(() => {
|
||||
this.nonce = response.decode_utf8();
|
||||
const { resolve } = complete();
|
||||
if (resolve) {
|
||||
resolve(response);
|
||||
}
|
||||
})
|
||||
.catch((e) => {
|
||||
console.log(e);
|
||||
const { reject } = complete();
|
||||
if (reject) {
|
||||
reject(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
this.socket.on('error', (e) => {
|
||||
if (this.reject) {
|
||||
const reject = this.reject;
|
||||
|
||||
cleanup();
|
||||
|
||||
this.connected = false;
|
||||
console.log(e);
|
||||
if (reject) {
|
||||
reject(e);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
this.socket.on('close', () => {
|
||||
if (this.reject) {
|
||||
const reject = this.reject;
|
||||
|
||||
cleanup();
|
||||
|
||||
this.connected = false;
|
||||
console.log('socket closed');
|
||||
if (reject) {
|
||||
reject(new Error('socket closed'));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async disconnect() {
|
||||
try {
|
||||
if (this.socket) {
|
||||
this.socket.destroy();
|
||||
this.socket = null;
|
||||
this.cleanup_handlers();
|
||||
this.connected = false;
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
}
|
||||
|
||||
async send(method_name, packet, optional_thread_id) {
|
||||
packet.token = this.password;
|
||||
packet.encode_top_utf8(method_name);
|
||||
packet.encode_top_ui64(optional_thread_id || 1);
|
||||
packet.encode_top_utf8(constants.instance_id);
|
||||
packet.encode_top_ui32(0); // Service flags
|
||||
packet.encode_top_utf8(constants.get_version());
|
||||
packet.encode_top_utf8(nonce);
|
||||
await packet.encrypt();
|
||||
packet.encode_top_ui32(packet.buffer.length);
|
||||
return new Promise((resolve, reject) => {
|
||||
this.reject = reject;
|
||||
this.resolve = resolve;
|
||||
this.socket.write(Buffer.from(packet.buffer), null, (err) => {
|
||||
if (err) {
|
||||
this.cleanup_handlers();
|
||||
reject(err);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
import Socket from 'net';
|
||||
|
||||
import * as constants from '../utils/constants';
|
||||
|
||||
import packet from './packet';
|
||||
|
||||
export default class connection {
|
||||
constructor(host_or_ip, port, password, socket) {
|
||||
this.host_or_ip = host_or_ip;
|
||||
this.port = port;
|
||||
this.password = password;
|
||||
if (socket) {
|
||||
this.socket = socket;
|
||||
this.connected = true;
|
||||
this.setup_socket();
|
||||
}
|
||||
}
|
||||
|
||||
connected = false;
|
||||
host_or_ip = '';
|
||||
nonce = '';
|
||||
password = '';
|
||||
port = 20000;
|
||||
reject;
|
||||
resolve;
|
||||
socket;
|
||||
|
||||
cleanup_handlers() {
|
||||
this.reject = null;
|
||||
this.resolve = null;
|
||||
}
|
||||
|
||||
async connect() {
|
||||
if (!this.socket) {
|
||||
try {
|
||||
await new Promise((resolve, reject) => {
|
||||
this.socket = Socket.createConnection(
|
||||
this.port,
|
||||
this.host_or_ip,
|
||||
(err) => {
|
||||
if (err) {
|
||||
console.log(err);
|
||||
return reject(err);
|
||||
}
|
||||
|
||||
this.reject = reject;
|
||||
this.resolve = resolve;
|
||||
}
|
||||
);
|
||||
});
|
||||
} catch (err) {
|
||||
return Promise.reject(new Error(`'connect()' failed: ${err}`));
|
||||
}
|
||||
|
||||
this.connected = true;
|
||||
this.setup_socket();
|
||||
}
|
||||
}
|
||||
|
||||
setup_socket() {
|
||||
let buffer;
|
||||
const cleanup = () => {
|
||||
this.cleanup_handlers();
|
||||
buffer = null;
|
||||
};
|
||||
if (this.socket._socket) {
|
||||
this.socket._socket.setNoDelay(true);
|
||||
this.socket._socket.setKeepAlive(true);
|
||||
} else {
|
||||
this.socket.setNoDelay(true);
|
||||
this.socket.setKeepAlive(true);
|
||||
}
|
||||
|
||||
this.socket.on('data', (chunk) => {
|
||||
buffer = buffer ? Buffer.concat([buffer, chunk]) : chunk;
|
||||
if (buffer.length > 4) {
|
||||
const size = buffer.readUInt32BE(0);
|
||||
if (buffer.length >= size + 4) {
|
||||
const packet_data = buffer.slice(4, 4 + size);
|
||||
if (this.resolve) {
|
||||
const complete = () => {
|
||||
const reject = this.reject;
|
||||
const resolve = this.resolve;
|
||||
cleanup();
|
||||
return {
|
||||
reject,
|
||||
resolve,
|
||||
};
|
||||
};
|
||||
|
||||
const response = new packet(this.password);
|
||||
response.buffer = new Uint8Array(packet_data);
|
||||
response
|
||||
.decrypt()
|
||||
.then(() => {
|
||||
this.nonce = response.decode_utf8();
|
||||
const { resolve } = complete();
|
||||
if (resolve) {
|
||||
resolve(response);
|
||||
}
|
||||
})
|
||||
.catch((e) => {
|
||||
console.log(e);
|
||||
const { reject } = complete();
|
||||
if (reject) {
|
||||
reject(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
this.socket.on('error', (e) => {
|
||||
if (this.reject) {
|
||||
const reject = this.reject;
|
||||
|
||||
cleanup();
|
||||
|
||||
this.connected = false;
|
||||
console.log(e);
|
||||
if (reject) {
|
||||
reject(e);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
this.socket.on('close', () => {
|
||||
if (this.reject) {
|
||||
const reject = this.reject;
|
||||
|
||||
cleanup();
|
||||
|
||||
this.connected = false;
|
||||
console.log('socket closed');
|
||||
if (reject) {
|
||||
reject(new Error('socket closed'));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async disconnect() {
|
||||
try {
|
||||
if (this.socket) {
|
||||
this.socket.destroy();
|
||||
this.socket = null;
|
||||
this.cleanup_handlers();
|
||||
this.connected = false;
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
}
|
||||
|
||||
async send(method_name, packet, optional_thread_id) {
|
||||
packet.token = this.password;
|
||||
packet.encode_top_utf8(method_name);
|
||||
packet.encode_top_ui64(optional_thread_id || 1);
|
||||
packet.encode_top_utf8(constants.instance_id);
|
||||
packet.encode_top_ui32(0); // Service flags
|
||||
packet.encode_top_utf8(constants.get_version());
|
||||
packet.encode_top_utf8(nonce);
|
||||
await packet.encrypt();
|
||||
packet.encode_top_ui32(packet.buffer.length);
|
||||
return new Promise((resolve, reject) => {
|
||||
this.reject = reject;
|
||||
this.resolve = resolve;
|
||||
this.socket.write(Buffer.from(packet.buffer), null, (err) => {
|
||||
if (err) {
|
||||
this.cleanup_handlers();
|
||||
reject(err);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,69 +1,69 @@
|
||||
import Pool from 'socket-pool';
|
||||
|
||||
import connection from './connection';
|
||||
|
||||
export default class connection_pool {
|
||||
constructor(pool_size, host_or_ip, port, password) {
|
||||
this.host_or_ip = host_or_ip;
|
||||
this.port = port;
|
||||
this.password = password;
|
||||
if (pool_size > 1) {
|
||||
this.pool = new Pool({
|
||||
connect: { host: host_or_ip, port: port },
|
||||
connectTimeout: 5000,
|
||||
pool: { max: pool_size, min: 2 },
|
||||
});
|
||||
} else {
|
||||
throw new Error("'pool_size' must be > 1");
|
||||
}
|
||||
}
|
||||
|
||||
host_or_ip = '';
|
||||
next_thread_id = 1;
|
||||
password = '';
|
||||
port = 20000;
|
||||
pool;
|
||||
shutdown = false;
|
||||
|
||||
async disconnect() {
|
||||
await this.pool._pool.drain();
|
||||
await this.pool._pool.clear();
|
||||
this.pool = null;
|
||||
this.shutdown = true;
|
||||
}
|
||||
|
||||
async send(method_name, packet, optional_thread_id) {
|
||||
try {
|
||||
const socket = await this.pool.acquire();
|
||||
if (!socket.thread_id) {
|
||||
socket.thread_id = this.next_thread_id++;
|
||||
}
|
||||
|
||||
const cleanup = () => {
|
||||
try {
|
||||
socket.release();
|
||||
} catch (err) {
|
||||
console.log(`'release()' failed: ${err}`);
|
||||
}
|
||||
};
|
||||
|
||||
try {
|
||||
const result = await new connection(
|
||||
this.host_or_ip,
|
||||
this.port,
|
||||
this.password,
|
||||
socket
|
||||
).send(method_name, packet, optional_thread_id || socket.thread_id);
|
||||
cleanup();
|
||||
return result;
|
||||
} catch (err) {
|
||||
cleanup();
|
||||
return Promise.reject(
|
||||
new Error(`'send(${method_name})' failed: ${err}`)
|
||||
);
|
||||
}
|
||||
} catch (err) {
|
||||
return Promise.reject(new Error(`'acquire()' socket failed: ${err}`));
|
||||
}
|
||||
}
|
||||
}
|
||||
import Pool from 'socket-pool';
|
||||
|
||||
import connection from './connection';
|
||||
|
||||
export default class connection_pool {
|
||||
constructor(pool_size, host_or_ip, port, password) {
|
||||
this.host_or_ip = host_or_ip;
|
||||
this.port = port;
|
||||
this.password = password;
|
||||
if (pool_size > 1) {
|
||||
this.pool = new Pool({
|
||||
connect: { host: host_or_ip, port: port },
|
||||
connectTimeout: 5000,
|
||||
pool: { max: pool_size, min: 2 },
|
||||
});
|
||||
} else {
|
||||
throw new Error("'pool_size' must be > 1");
|
||||
}
|
||||
}
|
||||
|
||||
host_or_ip = '';
|
||||
next_thread_id = 1;
|
||||
password = '';
|
||||
port = 20000;
|
||||
pool;
|
||||
shutdown = false;
|
||||
|
||||
async disconnect() {
|
||||
await this.pool._pool.drain();
|
||||
await this.pool._pool.clear();
|
||||
this.pool = null;
|
||||
this.shutdown = true;
|
||||
}
|
||||
|
||||
async send(method_name, packet, optional_thread_id) {
|
||||
try {
|
||||
const socket = await this.pool.acquire();
|
||||
if (!socket.thread_id) {
|
||||
socket.thread_id = this.next_thread_id++;
|
||||
}
|
||||
|
||||
const cleanup = () => {
|
||||
try {
|
||||
socket.release();
|
||||
} catch (err) {
|
||||
console.log(`'release()' failed: ${err}`);
|
||||
}
|
||||
};
|
||||
|
||||
try {
|
||||
const result = await new connection(
|
||||
this.host_or_ip,
|
||||
this.port,
|
||||
this.password,
|
||||
socket
|
||||
).send(method_name, packet, optional_thread_id || socket.thread_id);
|
||||
cleanup();
|
||||
return result;
|
||||
} catch (err) {
|
||||
cleanup();
|
||||
return Promise.reject(
|
||||
new Error(`'send(${method_name})' failed: ${err}`)
|
||||
);
|
||||
}
|
||||
} catch (err) {
|
||||
return Promise.reject(new Error(`'acquire()' socket failed: ${err}`));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,330 +1,330 @@
|
||||
import { randomBytes } from 'crypto';
|
||||
import { Int64BE, Uint64BE } from 'int64-buffer';
|
||||
import crypto from 'crypto';
|
||||
import { TextEncoder } from 'text-encoding';
|
||||
import { getCustomEncryption } from '../utils/constants';
|
||||
import {
|
||||
be_ui8_array_to_i16,
|
||||
be_ui8_array_to_i32,
|
||||
be_ui8_array_to_ui16,
|
||||
be_ui8_array_to_ui32,
|
||||
i16_to_be_ui8_array,
|
||||
i32_to_be_ui8_array,
|
||||
i8_to_ui8_array,
|
||||
ui16_to_be_ui8_array,
|
||||
ui32_to_be_ui8_array,
|
||||
ui8_array_to_i8,
|
||||
ui8_array_to_ui8,
|
||||
ui8_to_ui8_array,
|
||||
} from '../utils/byte_order';
|
||||
import { XChaCha20Poly1305 } from '@stablelib/xchacha20poly1305';
|
||||
|
||||
export default class packet {
|
||||
constructor(token) {
|
||||
this.token = token;
|
||||
}
|
||||
|
||||
buffer = new Uint8Array(0);
|
||||
decode_offset = 0;
|
||||
token;
|
||||
|
||||
append_buffer = (buffer) => {
|
||||
if (!(buffer instanceof Uint8Array)) {
|
||||
throw new Error('Buffer must be of type Uint8Array');
|
||||
}
|
||||
|
||||
this.buffer = this.buffer
|
||||
? new Uint8Array([...this.buffer, ...buffer])
|
||||
: buffer;
|
||||
};
|
||||
|
||||
clear = () => {
|
||||
this.buffer = null;
|
||||
this.decode_offset = 0;
|
||||
};
|
||||
|
||||
decode_buffer = (length) => {
|
||||
if (!this.buffer) {
|
||||
throw new Error('Invalid buffer');
|
||||
}
|
||||
|
||||
const ret = this.buffer.slice(
|
||||
this.decode_offset,
|
||||
this.decode_offset + length
|
||||
);
|
||||
this.decode_offset += length;
|
||||
return Buffer.from(ret);
|
||||
};
|
||||
|
||||
decode_stat = () => {
|
||||
const mode = this.decode_ui16();
|
||||
const nlink = this.decode_ui16();
|
||||
const uid = this.decode_ui32();
|
||||
const gid = this.decode_ui32();
|
||||
const atime = this.decode_ui64();
|
||||
const mtime = this.decode_ui64();
|
||||
const ctime = this.decode_ui64();
|
||||
const birth_time = this.decode_ui64();
|
||||
const size = this.decode_ui64();
|
||||
const blocks = this.decode_ui64();
|
||||
const blksize = this.decode_ui32();
|
||||
const flags = this.decode_ui32();
|
||||
const directory = !!this.decode_ui8();
|
||||
return {
|
||||
mode,
|
||||
nlink,
|
||||
uid,
|
||||
gid,
|
||||
atime,
|
||||
mtime,
|
||||
ctime,
|
||||
birth_time,
|
||||
size,
|
||||
blocks,
|
||||
blksize,
|
||||
flags,
|
||||
directory,
|
||||
};
|
||||
};
|
||||
|
||||
decode_utf8 = () => {
|
||||
if (!this.buffer) {
|
||||
throw new Error('Invalid buffer');
|
||||
}
|
||||
|
||||
const startIndex = this.decode_offset;
|
||||
const endIndex = this.buffer.indexOf(0, startIndex);
|
||||
if (endIndex >= 0) {
|
||||
let ret = '';
|
||||
for (let i = startIndex; i < endIndex; i++) {
|
||||
ret += String.fromCharCode(this.buffer[i]);
|
||||
}
|
||||
this.decode_offset = endIndex + 1;
|
||||
return ret;
|
||||
}
|
||||
|
||||
throw new Error('String not found in buffer');
|
||||
};
|
||||
|
||||
decode_i8 = () => {
|
||||
return ui8_array_to_i8(this.buffer, this.decode_offset++);
|
||||
};
|
||||
|
||||
decode_ui8 = () => {
|
||||
return ui8_array_to_ui8(this.buffer, this.decode_offset++);
|
||||
};
|
||||
|
||||
decode_i16 = () => {
|
||||
const ret = be_ui8_array_to_i16(this.buffer, this.decode_offset);
|
||||
this.decode_offset += 2;
|
||||
return ret;
|
||||
};
|
||||
|
||||
decode_ui16 = () => {
|
||||
const ret = be_ui8_array_to_ui16(this.buffer, this.decode_offset);
|
||||
this.decode_offset += 2;
|
||||
return ret;
|
||||
};
|
||||
|
||||
decode_i32 = () => {
|
||||
const ret = be_ui8_array_to_i32(this.buffer, this.decode_offset);
|
||||
this.decode_offset += 4;
|
||||
return ret;
|
||||
};
|
||||
|
||||
decode_ui32 = () => {
|
||||
const ret = be_ui8_array_to_ui32(this.buffer, this.decode_offset);
|
||||
this.decode_offset += 4;
|
||||
return ret;
|
||||
};
|
||||
|
||||
decode_i64 = () => {
|
||||
const ret = new Int64BE(
|
||||
this.buffer.slice(this.decode_offset, this.decode_offset + 8)
|
||||
);
|
||||
this.decode_offset += 8;
|
||||
return ret.toString(10);
|
||||
};
|
||||
|
||||
decode_ui64 = () => {
|
||||
const ret = new Uint64BE(
|
||||
this.buffer.slice(this.decode_offset, this.decode_offset + 8)
|
||||
);
|
||||
this.decode_offset += 8;
|
||||
return ret.toString(10);
|
||||
};
|
||||
|
||||
decrypt = async () => {
|
||||
try {
|
||||
let hash = crypto.createHash('sha256');
|
||||
hash = hash.update(new TextEncoder().encode(this.token));
|
||||
|
||||
const key = Uint8Array.from(hash.digest());
|
||||
const nonce = this.buffer.slice(0, 24);
|
||||
const mac = this.buffer.slice(24, 16 + 24);
|
||||
|
||||
const customEncryption = getCustomEncryption();
|
||||
if (customEncryption) {
|
||||
this.buffer = Buffer.from(
|
||||
await customEncryption.decrypt(
|
||||
Buffer.from(key).toString('base64'),
|
||||
Buffer.from(nonce).toString('base64'),
|
||||
Buffer.from(mac).toString('base64'),
|
||||
Buffer.from(this.buffer.slice(40)).toString('base64')
|
||||
),
|
||||
'base64'
|
||||
);
|
||||
} else {
|
||||
const aad = ui32_to_be_ui8_array(this.buffer.length);
|
||||
this.buffer = new Uint8Array([
|
||||
...this.buffer.slice(nonce.length + mac.length),
|
||||
...this.buffer.slice(nonce.length, nonce.length + mac.length),
|
||||
]);
|
||||
const result = new XChaCha20Poly1305(key).open(nonce, this.buffer, aad);
|
||||
if (!result) {
|
||||
throw new Error('decryption failed');
|
||||
}
|
||||
this.buffer = Buffer.from(result);
|
||||
}
|
||||
|
||||
this.buffer = new Uint8Array(this.buffer);
|
||||
return this.buffer;
|
||||
} catch (e) {
|
||||
return Promise.reject(e);
|
||||
}
|
||||
};
|
||||
|
||||
encode_buffer = (buffer) => {
|
||||
this.append_buffer(new Uint8Array(buffer));
|
||||
};
|
||||
|
||||
encode_i8 = (num) => {
|
||||
this.append_buffer(i8_to_ui8_array(num));
|
||||
};
|
||||
|
||||
encode_top_i8 = (num) => {
|
||||
this.push_buffer(i8_to_ui8_array(num));
|
||||
};
|
||||
|
||||
encode_u8 = (num) => {
|
||||
this.append_buffer(ui8_to_ui8_array(num));
|
||||
};
|
||||
|
||||
encode_top_u8 = (num) => {
|
||||
this.push_buffer(ui8_to_ui8_array(num));
|
||||
};
|
||||
|
||||
encode_i16 = (num) => {
|
||||
this.append_buffer(i16_to_be_ui8_array(num));
|
||||
};
|
||||
|
||||
encode_top_i16 = (num) => {
|
||||
this.push_buffer(i16_to_be_ui8_array(num));
|
||||
};
|
||||
|
||||
encode_ui16 = (num) => {
|
||||
this.append_buffer(ui16_to_be_ui8_array(num));
|
||||
};
|
||||
|
||||
encode_top_ui16 = (num) => {
|
||||
this.push_buffer(ui16_to_be_ui8_array(num));
|
||||
};
|
||||
|
||||
encode_i32 = (num) => {
|
||||
this.append_buffer(i32_to_be_ui8_array(num));
|
||||
};
|
||||
|
||||
encode_top_i32 = (num) => {
|
||||
this.push_buffer(i32_to_be_ui8_array(num));
|
||||
};
|
||||
|
||||
encode_ui32 = (num) => {
|
||||
this.append_buffer(ui32_to_be_ui8_array(num));
|
||||
};
|
||||
|
||||
encode_top_ui32 = (num) => {
|
||||
this.push_buffer(ui32_to_be_ui8_array(num));
|
||||
};
|
||||
|
||||
encode_i64 = (num) => {
|
||||
this.append_buffer(new Uint8Array(new Int64BE(num).toArray()));
|
||||
};
|
||||
|
||||
encode_top_i64 = (num) => {
|
||||
this.push_buffer(new Uint8Array(new Int64BE(num).toArray()));
|
||||
};
|
||||
|
||||
encode_ui64 = (num) => {
|
||||
this.append_buffer(new Uint8Array(new Uint64BE(num).toArray()));
|
||||
};
|
||||
|
||||
encode_top_ui64 = (num) => {
|
||||
this.push_buffer(new Uint8Array(new Uint64BE(num).toArray()));
|
||||
};
|
||||
|
||||
encode_utf8 = (str) => {
|
||||
if (!(typeof str === 'string' || str instanceof String)) {
|
||||
throw new Error('Value must be of type string');
|
||||
}
|
||||
|
||||
const buffer = new Uint8Array([...new TextEncoder().encode(str), 0]);
|
||||
this.append_buffer(buffer);
|
||||
};
|
||||
|
||||
encode_top_utf8 = (str) => {
|
||||
if (!(typeof str === 'string' || str instanceof String)) {
|
||||
throw new Error('Value must be of type string');
|
||||
}
|
||||
|
||||
const buffer = new Uint8Array([...new TextEncoder().encode(str), 0]);
|
||||
this.push_buffer(buffer);
|
||||
};
|
||||
|
||||
encrypt = async (nonce) => {
|
||||
try {
|
||||
let hash = crypto.createHash('sha256');
|
||||
hash = hash.update(new TextEncoder().encode(this.token));
|
||||
|
||||
const key = Uint8Array.from(hash.digest());
|
||||
if (!nonce) {
|
||||
nonce = Uint8Array.from(randomBytes(24));
|
||||
}
|
||||
|
||||
const customEncryption = getCustomEncryption();
|
||||
if (customEncryption) {
|
||||
this.buffer = new Uint8Array(
|
||||
Buffer.from(
|
||||
await customEncryption.encrypt(
|
||||
Buffer.from(key).toString('base64'),
|
||||
Buffer.from(nonce).toString('base64'),
|
||||
Buffer.from(this.buffer).toString('base64')
|
||||
),
|
||||
'base64'
|
||||
)
|
||||
);
|
||||
} else {
|
||||
const aad = ui32_to_be_ui8_array(this.buffer.length + 40);
|
||||
this.buffer = new XChaCha20Poly1305(key).seal(nonce, this.buffer, aad);
|
||||
this.buffer = new Uint8Array([
|
||||
...this.buffer.slice(this.buffer.length - 16),
|
||||
...this.buffer.slice(0, this.buffer.length - 16),
|
||||
]);
|
||||
this.push_buffer(nonce);
|
||||
}
|
||||
|
||||
return this.buffer;
|
||||
} catch (e) {
|
||||
return Promise.reject(e);
|
||||
}
|
||||
};
|
||||
|
||||
push_buffer = (buffer) => {
|
||||
if (!(buffer instanceof Uint8Array)) {
|
||||
throw new Error('Buffer must be of type Uint8Array');
|
||||
}
|
||||
|
||||
this.buffer = this.buffer
|
||||
? new Uint8Array([...buffer, ...this.buffer])
|
||||
: buffer;
|
||||
};
|
||||
}
|
||||
import { randomBytes } from 'crypto';
|
||||
import { Int64BE, Uint64BE } from 'int64-buffer';
|
||||
import crypto from 'crypto';
|
||||
import { TextEncoder } from 'text-encoding';
|
||||
import { getCustomEncryption } from '../utils/constants';
|
||||
import {
|
||||
be_ui8_array_to_i16,
|
||||
be_ui8_array_to_i32,
|
||||
be_ui8_array_to_ui16,
|
||||
be_ui8_array_to_ui32,
|
||||
i16_to_be_ui8_array,
|
||||
i32_to_be_ui8_array,
|
||||
i8_to_ui8_array,
|
||||
ui16_to_be_ui8_array,
|
||||
ui32_to_be_ui8_array,
|
||||
ui8_array_to_i8,
|
||||
ui8_array_to_ui8,
|
||||
ui8_to_ui8_array,
|
||||
} from '../utils/byte_order';
|
||||
import { XChaCha20Poly1305 } from '@stablelib/xchacha20poly1305';
|
||||
|
||||
export default class packet {
|
||||
constructor(token) {
|
||||
this.token = token;
|
||||
}
|
||||
|
||||
buffer = new Uint8Array(0);
|
||||
decode_offset = 0;
|
||||
token;
|
||||
|
||||
append_buffer = (buffer) => {
|
||||
if (!(buffer instanceof Uint8Array)) {
|
||||
throw new Error('Buffer must be of type Uint8Array');
|
||||
}
|
||||
|
||||
this.buffer = this.buffer
|
||||
? new Uint8Array([...this.buffer, ...buffer])
|
||||
: buffer;
|
||||
};
|
||||
|
||||
clear = () => {
|
||||
this.buffer = null;
|
||||
this.decode_offset = 0;
|
||||
};
|
||||
|
||||
decode_buffer = (length) => {
|
||||
if (!this.buffer) {
|
||||
throw new Error('Invalid buffer');
|
||||
}
|
||||
|
||||
const ret = this.buffer.slice(
|
||||
this.decode_offset,
|
||||
this.decode_offset + length
|
||||
);
|
||||
this.decode_offset += length;
|
||||
return Buffer.from(ret);
|
||||
};
|
||||
|
||||
decode_stat = () => {
|
||||
const mode = this.decode_ui16();
|
||||
const nlink = this.decode_ui16();
|
||||
const uid = this.decode_ui32();
|
||||
const gid = this.decode_ui32();
|
||||
const atime = this.decode_ui64();
|
||||
const mtime = this.decode_ui64();
|
||||
const ctime = this.decode_ui64();
|
||||
const birth_time = this.decode_ui64();
|
||||
const size = this.decode_ui64();
|
||||
const blocks = this.decode_ui64();
|
||||
const blksize = this.decode_ui32();
|
||||
const flags = this.decode_ui32();
|
||||
const directory = !!this.decode_ui8();
|
||||
return {
|
||||
mode,
|
||||
nlink,
|
||||
uid,
|
||||
gid,
|
||||
atime,
|
||||
mtime,
|
||||
ctime,
|
||||
birth_time,
|
||||
size,
|
||||
blocks,
|
||||
blksize,
|
||||
flags,
|
||||
directory,
|
||||
};
|
||||
};
|
||||
|
||||
decode_utf8 = () => {
|
||||
if (!this.buffer) {
|
||||
throw new Error('Invalid buffer');
|
||||
}
|
||||
|
||||
const startIndex = this.decode_offset;
|
||||
const endIndex = this.buffer.indexOf(0, startIndex);
|
||||
if (endIndex >= 0) {
|
||||
let ret = '';
|
||||
for (let i = startIndex; i < endIndex; i++) {
|
||||
ret += String.fromCharCode(this.buffer[i]);
|
||||
}
|
||||
this.decode_offset = endIndex + 1;
|
||||
return ret;
|
||||
}
|
||||
|
||||
throw new Error('String not found in buffer');
|
||||
};
|
||||
|
||||
decode_i8 = () => {
|
||||
return ui8_array_to_i8(this.buffer, this.decode_offset++);
|
||||
};
|
||||
|
||||
decode_ui8 = () => {
|
||||
return ui8_array_to_ui8(this.buffer, this.decode_offset++);
|
||||
};
|
||||
|
||||
decode_i16 = () => {
|
||||
const ret = be_ui8_array_to_i16(this.buffer, this.decode_offset);
|
||||
this.decode_offset += 2;
|
||||
return ret;
|
||||
};
|
||||
|
||||
decode_ui16 = () => {
|
||||
const ret = be_ui8_array_to_ui16(this.buffer, this.decode_offset);
|
||||
this.decode_offset += 2;
|
||||
return ret;
|
||||
};
|
||||
|
||||
decode_i32 = () => {
|
||||
const ret = be_ui8_array_to_i32(this.buffer, this.decode_offset);
|
||||
this.decode_offset += 4;
|
||||
return ret;
|
||||
};
|
||||
|
||||
decode_ui32 = () => {
|
||||
const ret = be_ui8_array_to_ui32(this.buffer, this.decode_offset);
|
||||
this.decode_offset += 4;
|
||||
return ret;
|
||||
};
|
||||
|
||||
decode_i64 = () => {
|
||||
const ret = new Int64BE(
|
||||
this.buffer.slice(this.decode_offset, this.decode_offset + 8)
|
||||
);
|
||||
this.decode_offset += 8;
|
||||
return ret.toString(10);
|
||||
};
|
||||
|
||||
decode_ui64 = () => {
|
||||
const ret = new Uint64BE(
|
||||
this.buffer.slice(this.decode_offset, this.decode_offset + 8)
|
||||
);
|
||||
this.decode_offset += 8;
|
||||
return ret.toString(10);
|
||||
};
|
||||
|
||||
decrypt = async () => {
|
||||
try {
|
||||
let hash = crypto.createHash('sha256');
|
||||
hash = hash.update(new TextEncoder().encode(this.token));
|
||||
|
||||
const key = Uint8Array.from(hash.digest());
|
||||
const nonce = this.buffer.slice(0, 24);
|
||||
const mac = this.buffer.slice(24, 16 + 24);
|
||||
|
||||
const customEncryption = getCustomEncryption();
|
||||
if (customEncryption) {
|
||||
this.buffer = Buffer.from(
|
||||
await customEncryption.decrypt(
|
||||
Buffer.from(key).toString('base64'),
|
||||
Buffer.from(nonce).toString('base64'),
|
||||
Buffer.from(mac).toString('base64'),
|
||||
Buffer.from(this.buffer.slice(40)).toString('base64')
|
||||
),
|
||||
'base64'
|
||||
);
|
||||
} else {
|
||||
const aad = ui32_to_be_ui8_array(this.buffer.length);
|
||||
this.buffer = new Uint8Array([
|
||||
...this.buffer.slice(nonce.length + mac.length),
|
||||
...this.buffer.slice(nonce.length, nonce.length + mac.length),
|
||||
]);
|
||||
const result = new XChaCha20Poly1305(key).open(nonce, this.buffer, aad);
|
||||
if (!result) {
|
||||
throw new Error('decryption failed');
|
||||
}
|
||||
this.buffer = Buffer.from(result);
|
||||
}
|
||||
|
||||
this.buffer = new Uint8Array(this.buffer);
|
||||
return this.buffer;
|
||||
} catch (e) {
|
||||
return Promise.reject(e);
|
||||
}
|
||||
};
|
||||
|
||||
encode_buffer = (buffer) => {
|
||||
this.append_buffer(new Uint8Array(buffer));
|
||||
};
|
||||
|
||||
encode_i8 = (num) => {
|
||||
this.append_buffer(i8_to_ui8_array(num));
|
||||
};
|
||||
|
||||
encode_top_i8 = (num) => {
|
||||
this.push_buffer(i8_to_ui8_array(num));
|
||||
};
|
||||
|
||||
encode_u8 = (num) => {
|
||||
this.append_buffer(ui8_to_ui8_array(num));
|
||||
};
|
||||
|
||||
encode_top_u8 = (num) => {
|
||||
this.push_buffer(ui8_to_ui8_array(num));
|
||||
};
|
||||
|
||||
encode_i16 = (num) => {
|
||||
this.append_buffer(i16_to_be_ui8_array(num));
|
||||
};
|
||||
|
||||
encode_top_i16 = (num) => {
|
||||
this.push_buffer(i16_to_be_ui8_array(num));
|
||||
};
|
||||
|
||||
encode_ui16 = (num) => {
|
||||
this.append_buffer(ui16_to_be_ui8_array(num));
|
||||
};
|
||||
|
||||
encode_top_ui16 = (num) => {
|
||||
this.push_buffer(ui16_to_be_ui8_array(num));
|
||||
};
|
||||
|
||||
encode_i32 = (num) => {
|
||||
this.append_buffer(i32_to_be_ui8_array(num));
|
||||
};
|
||||
|
||||
encode_top_i32 = (num) => {
|
||||
this.push_buffer(i32_to_be_ui8_array(num));
|
||||
};
|
||||
|
||||
encode_ui32 = (num) => {
|
||||
this.append_buffer(ui32_to_be_ui8_array(num));
|
||||
};
|
||||
|
||||
encode_top_ui32 = (num) => {
|
||||
this.push_buffer(ui32_to_be_ui8_array(num));
|
||||
};
|
||||
|
||||
encode_i64 = (num) => {
|
||||
this.append_buffer(new Uint8Array(new Int64BE(num).toArray()));
|
||||
};
|
||||
|
||||
encode_top_i64 = (num) => {
|
||||
this.push_buffer(new Uint8Array(new Int64BE(num).toArray()));
|
||||
};
|
||||
|
||||
encode_ui64 = (num) => {
|
||||
this.append_buffer(new Uint8Array(new Uint64BE(num).toArray()));
|
||||
};
|
||||
|
||||
encode_top_ui64 = (num) => {
|
||||
this.push_buffer(new Uint8Array(new Uint64BE(num).toArray()));
|
||||
};
|
||||
|
||||
encode_utf8 = (str) => {
|
||||
if (!(typeof str === 'string' || str instanceof String)) {
|
||||
throw new Error('Value must be of type string');
|
||||
}
|
||||
|
||||
const buffer = new Uint8Array([...new TextEncoder().encode(str), 0]);
|
||||
this.append_buffer(buffer);
|
||||
};
|
||||
|
||||
encode_top_utf8 = (str) => {
|
||||
if (!(typeof str === 'string' || str instanceof String)) {
|
||||
throw new Error('Value must be of type string');
|
||||
}
|
||||
|
||||
const buffer = new Uint8Array([...new TextEncoder().encode(str), 0]);
|
||||
this.push_buffer(buffer);
|
||||
};
|
||||
|
||||
encrypt = async (nonce) => {
|
||||
try {
|
||||
let hash = crypto.createHash('sha256');
|
||||
hash = hash.update(new TextEncoder().encode(this.token));
|
||||
|
||||
const key = Uint8Array.from(hash.digest());
|
||||
if (!nonce) {
|
||||
nonce = Uint8Array.from(randomBytes(24));
|
||||
}
|
||||
|
||||
const customEncryption = getCustomEncryption();
|
||||
if (customEncryption) {
|
||||
this.buffer = new Uint8Array(
|
||||
Buffer.from(
|
||||
await customEncryption.encrypt(
|
||||
Buffer.from(key).toString('base64'),
|
||||
Buffer.from(nonce).toString('base64'),
|
||||
Buffer.from(this.buffer).toString('base64')
|
||||
),
|
||||
'base64'
|
||||
)
|
||||
);
|
||||
} else {
|
||||
const aad = ui32_to_be_ui8_array(this.buffer.length + 40);
|
||||
this.buffer = new XChaCha20Poly1305(key).seal(nonce, this.buffer, aad);
|
||||
this.buffer = new Uint8Array([
|
||||
...this.buffer.slice(this.buffer.length - 16),
|
||||
...this.buffer.slice(0, this.buffer.length - 16),
|
||||
]);
|
||||
this.push_buffer(nonce);
|
||||
}
|
||||
|
||||
return this.buffer;
|
||||
} catch (e) {
|
||||
return Promise.reject(e);
|
||||
}
|
||||
};
|
||||
|
||||
push_buffer = (buffer) => {
|
||||
if (!(buffer instanceof Uint8Array)) {
|
||||
throw new Error('Buffer must be of type Uint8Array');
|
||||
}
|
||||
|
||||
this.buffer = this.buffer
|
||||
? new Uint8Array([...buffer, ...this.buffer])
|
||||
: buffer;
|
||||
};
|
||||
}
|
||||
|
||||
1292
src/ops/index.js
1292
src/ops/index.js
File diff suppressed because it is too large
Load Diff
@@ -1,112 +1,112 @@
|
||||
export const is_big_endian_system =
|
||||
new Uint8Array(new Uint32Array([0x12345678]).buffer)[0] === 0x12;
|
||||
|
||||
export const is_little_endian_system =
|
||||
new Uint8Array(new Uint32Array([0x12345678]).buffer)[0] === 0x78;
|
||||
|
||||
export const i8_to_ui8_array = (num) => {
|
||||
if (typeof num === 'string' || num instanceof String) {
|
||||
num = parseInt(num, 10);
|
||||
}
|
||||
|
||||
const buffer = Buffer.alloc(1);
|
||||
buffer.writeInt8(num);
|
||||
return new Uint8Array(buffer);
|
||||
};
|
||||
|
||||
export const ui8_array_to_i8 = (ar, offset) => {
|
||||
const buffer = Buffer.alloc(1);
|
||||
buffer[0] = ar[offset];
|
||||
|
||||
return buffer.readInt8(0);
|
||||
};
|
||||
|
||||
export const ui8_to_ui8_array = (num) => {
|
||||
if (typeof num === 'string' || num instanceof String) {
|
||||
num = parseInt(num, 10);
|
||||
}
|
||||
|
||||
const buffer = Buffer.alloc(1);
|
||||
buffer.writeUInt8(num);
|
||||
return new Uint8Array(buffer);
|
||||
};
|
||||
|
||||
export const ui8_array_to_ui8 = (ar, offset) => {
|
||||
const buffer = Buffer.alloc(1);
|
||||
buffer[0] = ar[offset];
|
||||
|
||||
return buffer.readUInt8(0);
|
||||
};
|
||||
|
||||
export const i16_to_be_ui8_array = (num) => {
|
||||
if (typeof num === 'string' || num instanceof String) {
|
||||
num = parseInt(num, 10);
|
||||
}
|
||||
|
||||
const buffer = Buffer.alloc(2);
|
||||
buffer.writeInt16BE(num);
|
||||
return new Uint8Array(buffer);
|
||||
};
|
||||
|
||||
export const be_ui8_array_to_i16 = (ar, offset) => {
|
||||
const buffer = Buffer.alloc(2);
|
||||
for (let i = offset; i < buffer.length + offset; i++) {
|
||||
buffer[i - offset] = ar[i];
|
||||
}
|
||||
return buffer.readInt16BE(0);
|
||||
};
|
||||
|
||||
export const ui16_to_be_ui8_array = (num) => {
|
||||
if (typeof num === 'string' || num instanceof String) {
|
||||
num = parseInt(num, 10);
|
||||
}
|
||||
|
||||
const buffer = Buffer.alloc(2);
|
||||
buffer.writeUInt16BE(num);
|
||||
return new Uint8Array(buffer);
|
||||
};
|
||||
|
||||
export const be_ui8_array_to_ui16 = (ar, offset) => {
|
||||
const buffer = Buffer.alloc(2);
|
||||
for (let i = offset; i < buffer.length + offset; i++) {
|
||||
buffer[i - offset] = ar[i];
|
||||
}
|
||||
return buffer.readUInt16BE(0);
|
||||
};
|
||||
|
||||
export const i32_to_be_ui8_array = (num) => {
|
||||
if (typeof num === 'string' || num instanceof String) {
|
||||
num = parseInt(num, 10);
|
||||
}
|
||||
|
||||
const buffer = Buffer.alloc(4);
|
||||
buffer.writeInt32BE(num);
|
||||
return new Uint8Array(buffer);
|
||||
};
|
||||
|
||||
export const be_ui8_array_to_i32 = (ar, offset) => {
|
||||
const buffer = Buffer.alloc(4);
|
||||
for (let i = offset; i < buffer.length + offset; i++) {
|
||||
buffer[i - offset] = ar[i];
|
||||
}
|
||||
return buffer.readInt32BE(0);
|
||||
};
|
||||
|
||||
export const ui32_to_be_ui8_array = (num) => {
|
||||
if (typeof num === 'string' || num instanceof String) {
|
||||
num = parseInt(num, 10);
|
||||
}
|
||||
|
||||
const buffer = Buffer.alloc(4);
|
||||
buffer.writeUInt32BE(num);
|
||||
return new Uint8Array(buffer);
|
||||
};
|
||||
|
||||
export const be_ui8_array_to_ui32 = (ar, offset) => {
|
||||
const buffer = Buffer.alloc(4);
|
||||
for (let i = offset; i < buffer.length + offset; i++) {
|
||||
buffer[i - offset] = ar[i];
|
||||
}
|
||||
|
||||
return buffer.readUInt32BE(0);
|
||||
};
|
||||
export const is_big_endian_system =
|
||||
new Uint8Array(new Uint32Array([0x12345678]).buffer)[0] === 0x12;
|
||||
|
||||
export const is_little_endian_system =
|
||||
new Uint8Array(new Uint32Array([0x12345678]).buffer)[0] === 0x78;
|
||||
|
||||
export const i8_to_ui8_array = (num) => {
|
||||
if (typeof num === 'string' || num instanceof String) {
|
||||
num = parseInt(num, 10);
|
||||
}
|
||||
|
||||
const buffer = Buffer.alloc(1);
|
||||
buffer.writeInt8(num);
|
||||
return new Uint8Array(buffer);
|
||||
};
|
||||
|
||||
export const ui8_array_to_i8 = (ar, offset) => {
|
||||
const buffer = Buffer.alloc(1);
|
||||
buffer[0] = ar[offset];
|
||||
|
||||
return buffer.readInt8(0);
|
||||
};
|
||||
|
||||
export const ui8_to_ui8_array = (num) => {
|
||||
if (typeof num === 'string' || num instanceof String) {
|
||||
num = parseInt(num, 10);
|
||||
}
|
||||
|
||||
const buffer = Buffer.alloc(1);
|
||||
buffer.writeUInt8(num);
|
||||
return new Uint8Array(buffer);
|
||||
};
|
||||
|
||||
export const ui8_array_to_ui8 = (ar, offset) => {
|
||||
const buffer = Buffer.alloc(1);
|
||||
buffer[0] = ar[offset];
|
||||
|
||||
return buffer.readUInt8(0);
|
||||
};
|
||||
|
||||
export const i16_to_be_ui8_array = (num) => {
|
||||
if (typeof num === 'string' || num instanceof String) {
|
||||
num = parseInt(num, 10);
|
||||
}
|
||||
|
||||
const buffer = Buffer.alloc(2);
|
||||
buffer.writeInt16BE(num);
|
||||
return new Uint8Array(buffer);
|
||||
};
|
||||
|
||||
export const be_ui8_array_to_i16 = (ar, offset) => {
|
||||
const buffer = Buffer.alloc(2);
|
||||
for (let i = offset; i < buffer.length + offset; i++) {
|
||||
buffer[i - offset] = ar[i];
|
||||
}
|
||||
return buffer.readInt16BE(0);
|
||||
};
|
||||
|
||||
export const ui16_to_be_ui8_array = (num) => {
|
||||
if (typeof num === 'string' || num instanceof String) {
|
||||
num = parseInt(num, 10);
|
||||
}
|
||||
|
||||
const buffer = Buffer.alloc(2);
|
||||
buffer.writeUInt16BE(num);
|
||||
return new Uint8Array(buffer);
|
||||
};
|
||||
|
||||
export const be_ui8_array_to_ui16 = (ar, offset) => {
|
||||
const buffer = Buffer.alloc(2);
|
||||
for (let i = offset; i < buffer.length + offset; i++) {
|
||||
buffer[i - offset] = ar[i];
|
||||
}
|
||||
return buffer.readUInt16BE(0);
|
||||
};
|
||||
|
||||
export const i32_to_be_ui8_array = (num) => {
|
||||
if (typeof num === 'string' || num instanceof String) {
|
||||
num = parseInt(num, 10);
|
||||
}
|
||||
|
||||
const buffer = Buffer.alloc(4);
|
||||
buffer.writeInt32BE(num);
|
||||
return new Uint8Array(buffer);
|
||||
};
|
||||
|
||||
export const be_ui8_array_to_i32 = (ar, offset) => {
|
||||
const buffer = Buffer.alloc(4);
|
||||
for (let i = offset; i < buffer.length + offset; i++) {
|
||||
buffer[i - offset] = ar[i];
|
||||
}
|
||||
return buffer.readInt32BE(0);
|
||||
};
|
||||
|
||||
export const ui32_to_be_ui8_array = (num) => {
|
||||
if (typeof num === 'string' || num instanceof String) {
|
||||
num = parseInt(num, 10);
|
||||
}
|
||||
|
||||
const buffer = Buffer.alloc(4);
|
||||
buffer.writeUInt32BE(num);
|
||||
return new Uint8Array(buffer);
|
||||
};
|
||||
|
||||
export const be_ui8_array_to_ui32 = (ar, offset) => {
|
||||
const buffer = Buffer.alloc(4);
|
||||
for (let i = offset; i < buffer.length + offset; i++) {
|
||||
buffer[i - offset] = ar[i];
|
||||
}
|
||||
|
||||
return buffer.readUInt32BE(0);
|
||||
};
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import _package_json from '../../package.json';
|
||||
|
||||
let customEncryption;
|
||||
|
||||
export const getCustomEncryption = () => {
|
||||
return customEncryption;
|
||||
};
|
||||
|
||||
export const setCustomEncryption = (ce) => {
|
||||
customEncryption = ce;
|
||||
};
|
||||
|
||||
export const instance_id = uuidv4();
|
||||
export const package_json = _package_json;
|
||||
export const get_version = () => _package_json.version;
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import _package_json from '../../package.json';
|
||||
|
||||
let customEncryption;
|
||||
|
||||
export const getCustomEncryption = () => {
|
||||
return customEncryption;
|
||||
};
|
||||
|
||||
export const setCustomEncryption = (ce) => {
|
||||
customEncryption = ce;
|
||||
};
|
||||
|
||||
export const instance_id = uuidv4();
|
||||
export const package_json = _package_json;
|
||||
export const get_version = () => _package_json.version;
|
||||
|
||||
Reference in New Issue
Block a user