repertory 2.0

This commit is contained in:
2022-02-17 17:11:18 -06:00
parent 814d2a634e
commit bef612b343
23 changed files with 2745 additions and 2745 deletions

14
.gitignore vendored
View File

@@ -1,7 +1,7 @@
node_modules/ node_modules/
coverage/ coverage/
.vscode/ .vscode/
dist/ dist/
backup/ backup/
.idea/ .idea/
/package-lock.json /package-lock.json

View File

@@ -1,3 +1,3 @@
set path+=.,src/** set path+=.,src/**
let &makeprg="npm run build" let &makeprg="npm run build"

View File

@@ -1,8 +1,8 @@
{ {
"trailingComma": "es5", "trailingComma": "es5",
"tabWidth": 2, "tabWidth": 2,
"semi": true, "semi": true,
"singleQuote": true, "singleQuote": true,
"jsxBracketSameLine": true "jsxBracketSameLine": true
} }

View File

@@ -1,14 +1,14 @@
# Changelog # Changelog
## 1.4.0-r1 ## 1.4.0-r1
- Switched packet encryption to XChaCha20-Poly1305 - Switched packet encryption to XChaCha20-Poly1305
- Allow external XChaCha20-Poly1305 encryption/decryption - Allow external XChaCha20-Poly1305 encryption/decryption
- Support writing base64 string data - Support writing base64 string data
- Renamed 'delete/delete_file' to 'remove/remove_file' - Renamed 'delete/delete_file' to 'remove/remove_file'
## 1.3.1-r3 ## 1.3.1-r3
- Added directory/file exists - Added directory/file exists
- Fix unit tests - Fix unit tests
## 1.3.1-r2 ## 1.3.1-r2
- Initial release - Initial release

View File

@@ -1,18 +1,18 @@
# `repertory-js` MIT License # `repertory-js` MIT License
### Copyright <2021> <scott.e.graves@protonmail.com> ### Copyright <2021> <scott.e.graves@protonmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and 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, associated documentation files (the "Software"), to deal in the Software without restriction,
including without limitation the rights to use, copy, modify, merge, publish, distribute, 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 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: 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 The above copyright notice and this permission notice shall be included in all copies or substantial
portions of the Software. portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT 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 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 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 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. CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

394
README.md
View File

@@ -1,197 +1,197 @@
# About # About
`repertory-js` is a Node.js module for interfacing with `repertory's` remote mount API. `repertory-js` is a Node.js module for interfacing with `repertory's` remote mount API.
## Installing ## Installing
```shell ```shell
npm i @blockstorage/repertory-js npm i @blockstorage/repertory-js
``` ```
## Repertory Configuration ## Repertory Configuration
A Repertory mount must be active with the `EnableRemoteMount` setting enabled. `RemoteToken` should A Repertory mount must be active with the `EnableRemoteMount` setting enabled. `RemoteToken` should
also be set to a strong, random password. also be set to a strong, random password.
### Enabling Sia Remote Mount API on Windows Systems ### Enabling Sia Remote Mount API on Windows Systems
```shell ```shell
repertory.exe -unmount repertory.exe -unmount
repertory.exe -set RemoteMount.EnableRemoteMount true repertory.exe -set RemoteMount.EnableRemoteMount true
repertory.exe -set RemoteMount.RemoteToken "my password" repertory.exe -set RemoteMount.RemoteToken "my password"
[Optional - change listening port] [Optional - change listening port]
repertory.exe -set RemoteMount.RemotePort 20202 repertory.exe -set RemoteMount.RemotePort 20202
``` ```
### Enabling Sia Remote Mount API on *NIX Systems ### Enabling Sia Remote Mount API on *NIX Systems
```shell ```shell
./repertory -unmount ./repertory -unmount
./repertory -set RemoteMount.EnableRemoteMount true ./repertory -set RemoteMount.EnableRemoteMount true
./repertory -set RemoteMount.RemoteToken "my password" ./repertory -set RemoteMount.RemoteToken "my password"
[Optional - change listening port] [Optional - change listening port]
./repertory -set RemoteMount.RemotePort 20202 ./repertory -set RemoteMount.RemotePort 20202
``` ```
### Skynet and ScPrime Mounts ### Skynet and ScPrime Mounts
* For Skynet mounts, add `-sk` argument to all commands listed above. * For Skynet mounts, add `-sk` argument to all commands listed above.
* For ScPrime mounts, add `-sp` argument to all commands listed above. * For ScPrime mounts, add `-sp` argument to all commands listed above.
## Module Environment Variables ## Module Environment Variables
* To successfully complete unit tests, a `repertory` mount supporting remote mount needs to be * To successfully complete unit tests, a `repertory` mount supporting remote mount needs to be
active. Set the following environment variables prior to running tests: active. Set the following environment variables prior to running tests:
* `TEST_HOST` * `TEST_HOST`
* `TEST_PASSWORD` * `TEST_PASSWORD`
* `TEST_PORT` * `TEST_PORT`
## Example API Usage ## Example API Usage
```javascript ```javascript
import * as rep from "@blockstorage/repertory-js"; import * as rep from "@blockstorage/repertory-js";
//const rep = require("@blockstorage/repertory-js"); //const rep = require("@blockstorage/repertory-js");
// Repertory host settings // Repertory host settings
const MY_HOST_OR_IP = 'localhost'; const MY_HOST_OR_IP = 'localhost';
const MY_PORT = 20000; const MY_PORT = 20000;
const MY_TOKEN = 'password'; const MY_TOKEN = 'password';
// Progress callback for uploads / downloads // Progress callback for uploads / downloads
const progress_cb = (local_path, remote_path, progress, completed) => { const progress_cb = (local_path, remote_path, progress, completed) => {
console.log(local_path, remote_path, progress, completed); console.log(local_path, remote_path, progress, completed);
}; };
//************************************************************************************************// //************************************************************************************************//
// Step 1. Create a connection pool (recommended) // // Step 1. Create a connection pool (recommended) //
//************************************************************************************************// //************************************************************************************************//
const conn = await rep.create_pool(8, MY_HOST_OR_IP, MY_PORT, MY_TOKEN); const conn = await rep.create_pool(8, MY_HOST_OR_IP, MY_PORT, MY_TOKEN);
/* Or create a single connection for light operations /* Or create a single connection for light operations
const conn = await rep.connect(MY_HOST_OR_IP, MY_PORT, MY_TOKEN); const conn = await rep.connect(MY_HOST_OR_IP, MY_PORT, MY_TOKEN);
*/ */
/* Disconnect when complete /* Disconnect when complete
await conn.disconnect(); await conn.disconnect();
*/ */
//************************************************************************************************// //************************************************************************************************//
// Step 2. Create an 'api' instance using the connection pool / connection // // Step 2. Create an 'api' instance using the connection pool / connection //
//************************************************************************************************// //************************************************************************************************//
const api = rep.create_api(conn); const api = rep.create_api(conn);
//************************************************************************************************// //************************************************************************************************//
// Step 3. Use 'api' // // Step 3. Use 'api' //
//************************************************************************************************// //************************************************************************************************//
//------------------------------------------------------------------------------------------------// //------------------------------------------------------------------------------------------------//
// *********** Directory Operations *********** // // *********** Directory Operations *********** //
//------------------------------------------------------------------------------------------------// //------------------------------------------------------------------------------------------------//
// Check if directory exists // Check if directory exists
const exists = await api.directory.exists('/my_directory'); const exists = await api.directory.exists('/my_directory');
// List directory contents // List directory contents
await api.directory.list('/', async (remote_path, page_count, get_page) => { await api.directory.list('/', async (remote_path, page_count, get_page) => {
for (let i = 0; i < page_count; i++) { for (let i = 0; i < page_count; i++) {
const items = await get_page(i); // Always 'await' const items = await get_page(i); // Always 'await'
console.log(items); console.log(items);
} }
}); });
// Asynchronous directory list // Asynchronous directory list
const snap = await api.directory.snapshot('/'); const snap = await api.directory.snapshot('/');
try { try {
for (let i = 0; i < snap.page_count; i++) { for (let i = 0; i < snap.page_count; i++) {
const items = await snap.get_page(i); // Always 'await' const items = await snap.get_page(i); // Always 'await'
console.log(items); console.log(items);
} }
} catch (err) { } catch (err) {
console.log(err); console.log(err);
} finally { } finally {
await snap.release(); await snap.release();
} }
// Create new directory // Create new directory
await api.directory.create('/test'); await api.directory.create('/test');
// Remove existing directory // Remove existing directory
await api.directory.remove('/test') await api.directory.remove('/test')
//------------------------------------------------------------------------------------------------// //------------------------------------------------------------------------------------------------//
// *********** File Operations *********** // // *********** File Operations *********** //
//------------------------------------------------------------------------------------------------// //------------------------------------------------------------------------------------------------//
// Check if file exists // Check if file exists
const exists = await api.file.exists('/my_file.txt') const exists = await api.file.exists('/my_file.txt')
// Remove a file // Remove a file
await api.file.remove('/my_file.txt') await api.file.remove('/my_file.txt')
// Download a remote file // Download a remote file
await api.file.download('/my_file.txt', 'C:\\my_file.txt', progress_cb); await api.file.download('/my_file.txt', 'C:\\my_file.txt', progress_cb);
// Download a remote file and overwrite existing local file // Download a remote file and overwrite existing local file
await api.file.download('/my_file.txt', 'C:\\my_file.txt', progress_cb, true); await api.file.download('/my_file.txt', 'C:\\my_file.txt', progress_cb, true);
// Resume failed download // Resume failed download
await api.file.download('/my_file.txt', 'C:\\my_file.txt', progress_cb, false, true); await api.file.download('/my_file.txt', 'C:\\my_file.txt', progress_cb, false, true);
// Upload a local file // Upload a local file
await api.file.upload('C:\\my_file.txt', '/my_file.txt', progress_cb); await api.file.upload('C:\\my_file.txt', '/my_file.txt', progress_cb);
// Upload a local file and overwrite existing remote file // Upload a local file and overwrite existing remote file
await api.file.upload('C:\\my_file.txt', '/my_file.txt', progress_cb, true); await api.file.upload('C:\\my_file.txt', '/my_file.txt', progress_cb, true);
// Resume failed upload // Resume failed upload
await api.file.upload('C:\\my_file.txt', '/my_file.txt', progress_cb, false, true); await api.file.upload('C:\\my_file.txt', '/my_file.txt', progress_cb, false, true);
//------------------------------------------------------------------------------------------------// //------------------------------------------------------------------------------------------------//
// *********** Low-Level File Operations *********** // // *********** Low-Level File Operations *********** //
//------------------------------------------------------------------------------------------------// //------------------------------------------------------------------------------------------------//
// Create or open a remote file // Create or open a remote file
{ {
const f = await api.file.create_or_open('/my_file.txt'); const f = await api.file.create_or_open('/my_file.txt');
await f.close(); await f.close();
} }
// Open an existing remote file // Open an existing remote file
{ {
const f = await api.file.open('/my_file.txt'); const f = await api.file.open('/my_file.txt');
await f.close(); await f.close();
} }
// Write to a file // Write to a file
{ {
const f = await api.file.create_or_open('/my_file.txt'); const f = await api.file.create_or_open('/my_file.txt');
const b = Buffer.alloc(1); const b = Buffer.alloc(1);
b[0] = 1; b[0] = 1;
await f.write(0, b); // write '1' byte at file offset '0' await f.write(0, b); // write '1' byte at file offset '0'
await f.close(); await f.close();
} }
// Read from a file // Read from a file
{ {
const f = await api.file.create_or_open('/my_file.txt'); 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' const b = await f.read(0, 1); // read '1' byte from file offset '0'
await f.close(); await f.close();
} }
// Truncate / resize file // Truncate / resize file
{ {
const f = await api.file.create_or_open('/my_file.txt'); const f = await api.file.create_or_open('/my_file.txt');
await f.truncate(10); await f.truncate(10);
await f.close(); await f.close();
} }
``` ```

22
fixup
View File

@@ -1,11 +1,11 @@
cat <<EOF >dist/cjs/package.json cat <<EOF >dist/cjs/package.json
{ {
"type": "commonjs" "type": "commonjs"
} }
EOF EOF
cat <<EOF >dist/mjs/package.json cat <<EOF >dist/mjs/package.json
{ {
"type": "module" "type": "module"
} }
EOF EOF

View File

@@ -1,69 +1,69 @@
{ {
"name": "@blockstorage/repertory-js", "name": "@blockstorage/repertory-js",
"version": "1.4.0-r1", "version": "2.0.0-r1",
"description": "A Node.js module for interfacing with Repertory's remote mount API", "description": "A Node.js module for interfacing with Repertory's remote mount API",
"author": "scott.e.graves@protonmail.com", "author": "scott.e.graves@protonmail.com",
"license": "MIT", "license": "MIT",
"homepage": "https://bitbucket.org/blockstorage/repertory-js", "homepage": "https://bitbucket.org/blockstorage/repertory-js",
"repository": { "repository": {
"type": "git", "type": "git",
"url": "https://bitbucket.org/blockstorage/repertory-js.git" "url": "https://bitbucket.org/blockstorage/repertory-js.git"
}, },
"keywords": [ "keywords": [
"repertory", "repertory",
"repertory-ui", "repertory-ui",
"library", "library",
"mount", "mount",
"fuse", "fuse",
"winfsp", "winfsp",
"blockchain", "blockchain",
"decentralized", "decentralized",
"cloud", "cloud",
"storage", "storage",
"altcoin", "altcoin",
"cryptocurrency" "cryptocurrency"
], ],
"main": "dist/cjs/index.js", "main": "dist/cjs/index.js",
"module": "dist/mjs/index.js", "module": "dist/mjs/index.js",
"exports": { "exports": {
".": { ".": {
"import": "./dist/mjs/index.js", "import": "./dist/mjs/index.js",
"require": "./dist/cjs/index.js" "require": "./dist/cjs/index.js"
} }
}, },
"files": [ "files": [
"dist/cjs", "dist/cjs",
"dist/mjs" "dist/mjs"
], ],
"scripts": { "scripts": {
"build": "rollup -c && ./fixup", "build": "rollup -c && ./fixup",
"test": "jest", "test": "jest",
"prepublish": "rollup -c --silent && ./fixup" "prepublish": "rollup -c --silent && ./fixup"
}, },
"dependencies": { "dependencies": {
"@stablelib/xchacha20poly1305": "^1.0.1", "@stablelib/xchacha20poly1305": "^1.0.1",
"int64-buffer": "^1.0.0", "int64-buffer": "^1.0.0",
"socket-pool": "^1.2.3", "socket-pool": "^1.2.3",
"text-encoding": "^0.7.0", "text-encoding": "^0.7.0",
"uuid": "^8.3.2" "uuid": "^8.3.2"
}, },
"devDependencies": { "devDependencies": {
"@babel/core": "^7.14.3", "@babel/core": "^7.14.3",
"@babel/plugin-proposal-class-properties": "^7.13.0", "@babel/plugin-proposal-class-properties": "^7.13.0",
"@babel/plugin-proposal-private-methods": "^7.13.0", "@babel/plugin-proposal-private-methods": "^7.13.0",
"@babel/plugin-transform-async-to-generator": "^7.13.0", "@babel/plugin-transform-async-to-generator": "^7.13.0",
"@babel/plugin-transform-regenerator": "^7.13.15", "@babel/plugin-transform-regenerator": "^7.13.15",
"@babel/plugin-transform-runtime": "^7.14.3", "@babel/plugin-transform-runtime": "^7.14.3",
"@babel/preset-env": "^7.14.2", "@babel/preset-env": "^7.14.2",
"@rollup/plugin-babel": "^5.3.0", "@rollup/plugin-babel": "^5.3.0",
"@rollup/plugin-commonjs": "^15.1.0", "@rollup/plugin-commonjs": "^15.1.0",
"@rollup/plugin-json": "^4.0.0", "@rollup/plugin-json": "^4.0.0",
"@rollup/plugin-node-resolve": "^9.0.0", "@rollup/plugin-node-resolve": "^9.0.0",
"@types/jest": "^26.0.23", "@types/jest": "^26.0.23",
"babel-eslint": "^10.1.0", "babel-eslint": "^10.1.0",
"jest": "^26.6.3", "jest": "^26.6.3",
"rollup": "^2.50.0", "rollup": "^2.50.0",
"rollup-plugin-eslint": "^7.0.0", "rollup-plugin-eslint": "^7.0.0",
"rollup-plugin-terser": "^7.0.2" "rollup-plugin-terser": "^7.0.2"
} }
} }

View File

@@ -1,60 +1,60 @@
import resolve from '@rollup/plugin-node-resolve'; import resolve from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs'; import commonjs from '@rollup/plugin-commonjs';
import babel from '@rollup/plugin-babel'; import babel from '@rollup/plugin-babel';
import { terser } from 'rollup-plugin-terser'; import { terser } from 'rollup-plugin-terser';
import json from '@rollup/plugin-json'; import json from '@rollup/plugin-json';
const commonConfig = { const commonConfig = {
input: 'src/index.js', input: 'src/index.js',
output: { output: {
name: 'index', name: 'index',
sourcemap: true, sourcemap: true,
}, },
plugins: [ plugins: [
resolve({ resolve({
customResolveOptions: { customResolveOptions: {
moduleDirectory: 'node_modules', moduleDirectory: 'node_modules',
}, },
}), }),
babel({ babel({
exclude: 'node_modules/**', exclude: 'node_modules/**',
babelHelpers: 'runtime', babelHelpers: 'runtime',
}), }),
commonjs(), commonjs(),
json(), json(),
], ],
}; };
// ESM config // ESM config
const esmConfig = Object.assign({}, commonConfig); const esmConfig = Object.assign({}, commonConfig);
esmConfig.output = Object.assign({}, commonConfig.output, { esmConfig.output = Object.assign({}, commonConfig.output, {
file: 'dist/mjs/index.js', file: 'dist/mjs/index.js',
format: 'esm', format: 'esm',
}); });
// ESM prod config // ESM prod config
const esmProdConfig = Object.assign({}, esmConfig); const esmProdConfig = Object.assign({}, esmConfig);
esmProdConfig.output = Object.assign({}, esmConfig.output, { esmProdConfig.output = Object.assign({}, esmConfig.output, {
file: 'dist/mjs/index.min.js', file: 'dist/mjs/index.min.js',
sourcemap: false, sourcemap: false,
}); });
esmProdConfig.plugins = [...esmConfig.plugins, terser()]; esmProdConfig.plugins = [...esmConfig.plugins, terser()];
// CJS config // CJS config
const cjsConfig = Object.assign({}, commonConfig); const cjsConfig = Object.assign({}, commonConfig);
cjsConfig.output = Object.assign({}, commonConfig.output, { cjsConfig.output = Object.assign({}, commonConfig.output, {
file: 'dist/cjs/index.js', file: 'dist/cjs/index.js',
format: 'cjs', format: 'cjs',
}); });
// CJS prod config // CJS prod config
const cjsProdConfig = Object.assign({}, cjsConfig); const cjsProdConfig = Object.assign({}, cjsConfig);
cjsProdConfig.output = Object.assign({}, cjsConfig.output, { cjsProdConfig.output = Object.assign({}, cjsConfig.output, {
file: 'dist/cjs/index.min.js', file: 'dist/cjs/index.min.js',
sourcemap: false, sourcemap: false,
}); });
cjsProdConfig.plugins = [...cjsConfig.plugins, terser()]; cjsProdConfig.plugins = [...cjsConfig.plugins, terser()];
let configurations = []; let configurations = [];
configurations.push(esmConfig, esmProdConfig, cjsConfig, cjsProdConfig); configurations.push(esmConfig, esmProdConfig, cjsConfig, cjsProdConfig);
export default configurations; export default configurations;

View File

@@ -1,115 +1,115 @@
import connection from '../networking/connection'; import connection from '../networking/connection';
import packet from '../networking/packet'; import packet from '../networking/packet';
import Socket from 'net'; import Socket from 'net';
test(`connect fails when error occurs during createConnection`, async () => { test(`connect fails when error occurs during createConnection`, async () => {
const mock_create = (port, host, cb) => { const mock_create = (port, host, cb) => {
cb(new Error('mock create error')); cb(new Error('mock create error'));
}; };
jest.spyOn(Socket, 'createConnection').mockImplementation(mock_create); jest.spyOn(Socket, 'createConnection').mockImplementation(mock_create);
const conn = new connection('localhost', 20000); const conn = new connection('localhost', 20000);
await expect(conn.connect()).rejects.toThrow(Error); await expect(conn.connect()).rejects.toThrow(Error);
}); });
test(`socket receive data fails when decryption fails`, async () => { test(`socket receive data fails when decryption fails`, async () => {
let cbl = {}; let cbl = {};
const socket = { const socket = {
setNoDelay: () => {}, setNoDelay: () => {},
setKeepAlive: () => {}, setKeepAlive: () => {},
on: (name, cb) => { on: (name, cb) => {
cbl[name] = cb; cbl[name] = cb;
}, },
}; };
const conn = new connection('', 0, 'b', socket); const conn = new connection('', 0, 'b', socket);
let reject; let reject;
const mock_reject = jest.fn().mockImplementation((e) => reject(e)); const mock_reject = jest.fn().mockImplementation((e) => reject(e));
conn.reject = mock_reject; conn.reject = mock_reject;
conn.resolve = jest.fn(); conn.resolve = jest.fn();
const p = new packet('a'); const p = new packet('a');
await p.encrypt(); await p.encrypt();
p.encode_top_ui32(p.buffer.length); p.encode_top_ui32(p.buffer.length);
await expect( await expect(
new Promise((_, r) => { new Promise((_, r) => {
reject = r; reject = r;
cbl['data'](Buffer.from(p.buffer)); cbl['data'](Buffer.from(p.buffer));
}) })
).rejects.toThrow(Error); ).rejects.toThrow(Error);
expect(mock_reject.mock.calls.length).toBe(1); expect(mock_reject.mock.calls.length).toBe(1);
}); });
test(`disconnect succeeds if an error is thrown`, async () => { test(`disconnect succeeds if an error is thrown`, async () => {
const socket = { const socket = {
setNoDelay: () => {}, setNoDelay: () => {},
setKeepAlive: () => {}, setKeepAlive: () => {},
destroy: () => { destroy: () => {
throw new Error('mock destroy error'); throw new Error('mock destroy error');
}, },
on: () => {}, on: () => {},
}; };
const conn = new connection('', 0, 'b', socket); const conn = new connection('', 0, 'b', socket);
await conn.disconnect(); await conn.disconnect();
}); });
test(`send fails on socket error`, async () => { test(`send fails on socket error`, async () => {
let cbl = {}; let cbl = {};
const socket = { const socket = {
setNoDelay: () => {}, setNoDelay: () => {},
setKeepAlive: () => {}, setKeepAlive: () => {},
on: (name, cb) => { on: (name, cb) => {
cbl[name] = cb; cbl[name] = cb;
}, },
}; };
const conn = new connection('', 0, 'b', socket); const conn = new connection('', 0, 'b', socket);
const mock_reject = jest.fn(); const mock_reject = jest.fn();
conn.reject = mock_reject; conn.reject = mock_reject;
conn.resolve = jest.fn(); conn.resolve = jest.fn();
cbl['error']('socket error'); cbl['error']('socket error');
expect(mock_reject).toBeCalled(); expect(mock_reject).toBeCalled();
}); });
test(`error is thrown when socket is closed`, async () => { test(`error is thrown when socket is closed`, async () => {
let cbl = {}; let cbl = {};
const socket = { const socket = {
setNoDelay: () => {}, setNoDelay: () => {},
setKeepAlive: () => {}, setKeepAlive: () => {},
on: (name, cb) => { on: (name, cb) => {
cbl[name] = cb; cbl[name] = cb;
}, },
}; };
const conn = new connection('', 0, 'b', socket); const conn = new connection('', 0, 'b', socket);
const mock_reject = jest.fn(); const mock_reject = jest.fn();
conn.reject = mock_reject; conn.reject = mock_reject;
conn.resolve = jest.fn(); conn.resolve = jest.fn();
cbl['close'](); cbl['close']();
expect(mock_reject).toBeCalled(); expect(mock_reject).toBeCalled();
}); });
test(`send fails when write error occurs`, async () => { test(`send fails when write error occurs`, async () => {
let cbl = {}; let cbl = {};
const socket = { const socket = {
setNoDelay: () => {}, setNoDelay: () => {},
setKeepAlive: () => {}, setKeepAlive: () => {},
on: (name, cb) => { on: (name, cb) => {
cbl[name] = cb; cbl[name] = cb;
}, },
write: (b, c, cb) => { write: (b, c, cb) => {
cb('mock write error'); cb('mock write error');
}, },
}; };
const conn = new connection('', 0, 'b', socket); const conn = new connection('', 0, 'b', socket);
try { try {
await conn.send('c', new packet('b')); await conn.send('c', new packet('b'));
expect('send should fail').toBeNull(); expect('send should fail').toBeNull();
} catch (err) { } catch (err) {
expect(err).toBeDefined(); expect(err).toBeDefined();
} }
}); });

View File

@@ -1,59 +1,59 @@
import connection_pool from '../networking/connection_pool'; import connection_pool from '../networking/connection_pool';
import packet from '../networking/packet'; import packet from '../networking/packet';
import connection from '../networking/connection'; import connection from '../networking/connection';
jest.mock('../networking/connection'); jest.mock('../networking/connection');
test(`construction fails if pool size is <= 1`, () => { test(`construction fails if pool size is <= 1`, () => {
expect(() => new connection_pool(1)).toThrow(Error); expect(() => new connection_pool(1)).toThrow(Error);
expect(() => new connection_pool(0)).toThrow(Error); expect(() => new connection_pool(0)).toThrow(Error);
expect(() => new connection_pool(-1)).toThrow(Error); expect(() => new connection_pool(-1)).toThrow(Error);
}); });
test(`error on socket release is ignored`, async () => { test(`error on socket release is ignored`, async () => {
const conn = new connection_pool(2, '', 20000); const conn = new connection_pool(2, '', 20000);
let invoked = false; let invoked = false;
jest.spyOn(conn.pool, 'acquire').mockImplementation(() => { jest.spyOn(conn.pool, 'acquire').mockImplementation(() => {
return { return {
release: () => { release: () => {
invoked = true; invoked = true;
throw new Error('mock release error'); throw new Error('mock release error');
}, },
}; };
}); });
const mock_send = jest.fn(); const mock_send = jest.fn();
connection.prototype.send = async () => { connection.prototype.send = async () => {
return mock_send(); return mock_send();
}; };
mock_send.mockResolvedValue(0); mock_send.mockResolvedValue(0);
expect(await conn.send('', new packet())).toEqual(0); expect(await conn.send('', new packet())).toEqual(0);
expect(invoked).toBeTruthy(); expect(invoked).toBeTruthy();
}); });
test(`connection pool send fails if socket acquire fails`, async () => { test(`connection pool send fails if socket acquire fails`, async () => {
const conn = new connection_pool(2, '', 20000); const conn = new connection_pool(2, '', 20000);
jest.spyOn(conn.pool, 'acquire').mockImplementation(() => { jest.spyOn(conn.pool, 'acquire').mockImplementation(() => {
throw new Error('mock acquire exception'); throw new Error('mock acquire exception');
}); });
await expect(conn.send('', new packet())).rejects.toThrow(Error); await expect(conn.send('', new packet())).rejects.toThrow(Error);
}); });
test(`connection pool send fails when connection send fails`, async () => { test(`connection pool send fails when connection send fails`, async () => {
const conn = new connection_pool(2, '', 20000); const conn = new connection_pool(2, '', 20000);
jest.spyOn(conn.pool, 'acquire').mockImplementation(() => { jest.spyOn(conn.pool, 'acquire').mockImplementation(() => {
return { return {
release: () => {}, release: () => {},
}; };
}); });
const mock_send = jest.fn(); const mock_send = jest.fn();
connection.prototype.send = async () => { connection.prototype.send = async () => {
return mock_send(); return mock_send();
}; };
mock_send.mockRejectedValue(new Error('mock send failed')); mock_send.mockRejectedValue(new Error('mock send failed'));
await expect(conn.send('', new packet())).rejects.toThrow(Error); await expect(conn.send('', new packet())).rejects.toThrow(Error);
}); });

View File

@@ -1,18 +1,18 @@
import { get_version, instance_id, package_json } from '../utils/constants'; import { get_version, instance_id, package_json } from '../utils/constants';
import * as uuid from 'uuid'; import * as uuid from 'uuid';
test(`can read 'package.json'`, () => { test(`can read 'package.json'`, () => {
console.log(package_json); console.log(package_json);
expect(package_json).toBeDefined(); expect(package_json).toBeDefined();
}); });
test(`'instance_id' is valid`, () => { test(`'instance_id' is valid`, () => {
console.log(instance_id); console.log(instance_id);
expect(instance_id).toBeDefined(); expect(instance_id).toBeDefined();
expect(uuid.parse(instance_id)).toBeInstanceOf(Uint8Array); expect(uuid.parse(instance_id)).toBeInstanceOf(Uint8Array);
}); });
test(`'version' can be read from 'package.json'`, () => { test(`'version' can be read from 'package.json'`, () => {
console.log(get_version()); console.log(get_version());
expect(get_version()).toBe('1.4.0-r1'); expect(get_version()).toBe('1.4.0-r1');
}); });

View File

@@ -1,47 +1,47 @@
import file from '../io/file'; import file from '../io/file';
jest.mock('../ops/index.js', () => ({ jest.mock('../ops/index.js', () => ({
...jest.requireActual('../ops/index.js'), ...jest.requireActual('../ops/index.js'),
close_file: jest.fn(), close_file: jest.fn(),
})); }));
import { close_file } from '../ops/index'; import { close_file } from '../ops/index';
test(`can close a closed file`, async () => { test(`can close a closed file`, async () => {
const f = new file(); const f = new file();
expect(await f.close()).toEqual(0); expect(await f.close()).toEqual(0);
}); });
test(`'get_size' fails on closed file`, async () => { test(`'get_size' fails on closed file`, async () => {
const f = new file(); const f = new file();
await expect(f.get_size()).rejects.toThrow(Error); await expect(f.get_size()).rejects.toThrow(Error);
}); });
test(`'read' fails on closed file`, async () => { test(`'read' fails on closed file`, async () => {
const f = new file(); const f = new file();
await expect(f.read(0, 10)).rejects.toThrow(Error); await expect(f.read(0, 10)).rejects.toThrow(Error);
}); });
test(`'truncate' fails on closed file`, async () => { test(`'truncate' fails on closed file`, async () => {
const f = new file(); const f = new file();
await expect(f.truncate(0)).rejects.toThrow(Error); await expect(f.truncate(0)).rejects.toThrow(Error);
}); });
test(`'write' fails on closed file`, async () => { test(`'write' fails on closed file`, async () => {
const f = new file(); const f = new file();
await expect(f.write(0, Buffer.alloc(2))).rejects.toThrow(Error); await expect(f.write(0, Buffer.alloc(2))).rejects.toThrow(Error);
}); });
test(`handle is set to null on close`, async () => { test(`handle is set to null on close`, async () => {
const f = new file(null, 1, '/path'); const f = new file(null, 1, '/path');
close_file.mockReturnValue(0); close_file.mockReturnValue(0);
expect(await f.close()).toEqual(0); expect(await f.close()).toEqual(0);
expect(f.handle).toBeNull(); expect(f.handle).toBeNull();
}); });
test(`handle is not changed on close if return is not 0`, async () => { test(`handle is not changed on close if return is not 0`, async () => {
const f = new file(null, 1, '/path'); const f = new file(null, 1, '/path');
close_file.mockReturnValue(1); close_file.mockReturnValue(1);
expect(await f.close()).toEqual(1); expect(await f.close()).toEqual(1);
expect(f.handle).toBe(1); expect(f.handle).toBe(1);
}); });

View File

@@ -1,19 +1,19 @@
import packet from '../networking/packet'; import packet from '../networking/packet';
test('can construct a packet', () => { test('can construct a packet', () => {
const p = new packet('my password'); const p = new packet('my password');
console.log(p); console.log(p);
expect(p.token).toEqual('my password'); expect(p.token).toEqual('my password');
expect(p.buffer).toEqual(new Uint8Array(0)); expect(p.buffer).toEqual(new Uint8Array(0));
expect(p.decode_offset).toEqual(0); expect(p.decode_offset).toEqual(0);
}); });
test('can encrypt and decrypt a packet', async () => { test('can encrypt and decrypt a packet', async () => {
console.log('testing'); console.log('testing');
const p = new packet('my password'); const p = new packet('my password');
p.encode_utf8('moose'); p.encode_utf8('moose');
await p.encrypt(); await p.encrypt();
await p.decrypt(); await p.decrypt();
const str = p.decode_utf8(); const str = p.decode_utf8();
expect(str).toEqual('moose'); expect(str).toEqual('moose');
}); });

File diff suppressed because it is too large Load Diff

View File

@@ -1,94 +1,94 @@
import file from './io/file'; import file from './io/file';
import connection from './networking/connection'; import connection from './networking/connection';
import connection_pool from './networking/connection_pool'; import connection_pool from './networking/connection_pool';
import * as ops from './ops'; import * as ops from './ops';
export * as byte_order from './utils/byte_order'; export * as byte_order from './utils/byte_order';
export { getCustomEncryption, setCustomEncryption } from './utils/constants'; export { getCustomEncryption, setCustomEncryption } from './utils/constants';
export { default as packet } from './networking/packet'; export { default as packet } from './networking/packet';
export const connect = async (host_or_ip, port, password) => { export const connect = async (host_or_ip, port, password) => {
const conn = new connection(host_or_ip, port, password); const conn = new connection(host_or_ip, port, password);
await conn.connect(); await conn.connect();
return conn; return conn;
}; };
export const create_api = (conn) => { export const create_api = (conn) => {
return { return {
directory: { directory: {
create: async (remote_path) => ops.create_directory(conn, remote_path), create: async (remote_path) => ops.create_directory(conn, remote_path),
exists: async (remote_path) => { exists: async (remote_path) => {
try { try {
const info = await ops.get_file_attributes2(conn, remote_path); const info = await ops.get_file_attributes2(conn, remote_path);
return info.directory; return info.directory;
} catch (e) { } catch (e) {
if (e.message.split(':')[1].trim() == '-2') { if (e.message.split(':')[1].trim() == '-2') {
return false; return false;
} }
throw new Error(e.message); throw new Error(e.message);
} }
}, },
list: async (remote_path, page_reader_cb) => list: async (remote_path, page_reader_cb) =>
ops.list_directory(conn, remote_path, page_reader_cb), ops.list_directory(conn, remote_path, page_reader_cb),
remove: async (remote_path) => ops.remove_directory(conn, remote_path), remove: async (remote_path) => ops.remove_directory(conn, remote_path),
snapshot: async (remote_path) => { snapshot: async (remote_path) => {
return ops.snapshot_directory(conn, remote_path); return ops.snapshot_directory(conn, remote_path);
}, },
}, },
file: { file: {
create_or_open: async (remote_path) => create_or_open: async (remote_path) =>
new file( new file(
conn, conn,
await ops.create_or_open_file(conn, remote_path), await ops.create_or_open_file(conn, remote_path),
remote_path remote_path
), ),
download: async ( download: async (
remote_path, remote_path,
local_path, local_path,
progress_cb, progress_cb,
overwrite, overwrite,
resume resume
) => ) =>
ops.download_file( ops.download_file(
conn, conn,
remote_path, remote_path,
local_path, local_path,
progress_cb, progress_cb,
overwrite, overwrite,
resume resume
), ),
exists: async (remote_path) => { exists: async (remote_path) => {
try { try {
const info = await ops.get_file_attributes2(conn, remote_path); const info = await ops.get_file_attributes2(conn, remote_path);
return !info.directory; return !info.directory;
} catch (e) { } catch (e) {
if (e.message.split(':')[1].trim() == '-2') { if (e.message.split(':')[1].trim() == '-2') {
return false; return false;
} }
throw new Error(e.message); throw new Error(e.message);
} }
}, },
open: async (remote_path) => open: async (remote_path) =>
new file(conn, await ops.open_file(conn, remote_path), remote_path), new file(conn, await ops.open_file(conn, remote_path), remote_path),
remove: async (remote_path) => ops.remove_file(conn, remote_path), remove: async (remote_path) => ops.remove_file(conn, remote_path),
upload: async (local_path, remote_path, progress_cb, overwrite, resume) => upload: async (local_path, remote_path, progress_cb, overwrite, resume) =>
ops.upload_file( ops.upload_file(
conn, conn,
local_path, local_path,
remote_path, remote_path,
progress_cb, progress_cb,
overwrite, overwrite,
resume resume
), ),
}, },
get_drive_information: async () => ops.get_drive_information(conn), get_drive_information: async () => ops.get_drive_information(conn),
}; };
}; };
export const create_pool = async (pool_size, host_or_ip, port, password) => { export const create_pool = async (pool_size, host_or_ip, port, password) => {
if (pool_size <= 1) { if (pool_size <= 1) {
return connect(host_or_ip, port, password); return connect(host_or_ip, port, password);
} }
return new connection_pool(pool_size, host_or_ip, port, password); return new connection_pool(pool_size, host_or_ip, port, password);
}; };

View File

@@ -1,108 +1,108 @@
import * as ops from '../ops'; import * as ops from '../ops';
let next_thread_id = 1; let next_thread_id = 1;
export default class file { export default class file {
constructor(conn, handle, remote_path) { constructor(conn, handle, remote_path) {
this.conn = conn; this.conn = conn;
this.handle = handle || null; this.handle = handle || null;
this.remote_path = remote_path; this.remote_path = remote_path;
this.thread_id = next_thread_id++; this.thread_id = next_thread_id++;
} }
conn; conn;
handle = null; handle = null;
thread_id; thread_id;
remote_path; remote_path;
async close() { async close() {
if (this.handle !== null) { if (this.handle !== null) {
const result = await ops.close_file( const result = await ops.close_file(
this.conn, this.conn,
this.remote_path, this.remote_path,
this.handle, this.handle,
this.thread_id this.thread_id
); );
if (result === 0) { if (result === 0) {
this.handle = null; this.handle = null;
} }
return result; return result;
} }
return 0; return 0;
} }
async get_size() { async get_size() {
if (this.handle === null) { if (this.handle === null) {
return Promise.reject(new Error("'get_size()' failed: invalid handle")); return Promise.reject(new Error("'get_size()' failed: invalid handle"));
} }
const attrs = await ops.get_file_attributes( const attrs = await ops.get_file_attributes(
this.conn, this.conn,
this.handle, this.handle,
this.remote_path, this.remote_path,
this.thread_id this.thread_id
); );
return attrs.size; return attrs.size;
} }
async read(offset, length) { async read(offset, length) {
if (this.handle === null) { if (this.handle === null) {
return Promise.reject(new Error("'read()' failed: invalid handle")); return Promise.reject(new Error("'read()' failed: invalid handle"));
} }
return ops.read_file( return ops.read_file(
this.conn, this.conn,
this.handle, this.handle,
this.remote_path, this.remote_path,
offset, offset,
length, length,
this.thread_id this.thread_id
); );
} }
async truncate(length) { async truncate(length) {
if (this.handle === null) { if (this.handle === null) {
return Promise.reject(new Error("'truncate()' failed: invalid handle")); return Promise.reject(new Error("'truncate()' failed: invalid handle"));
} }
return ops.truncate_file( return ops.truncate_file(
this.conn, this.conn,
this.handle, this.handle,
this.remote_path, this.remote_path,
length, length,
this.thread_id this.thread_id
); );
} }
async write(offset, buffer) { async write(offset, buffer) {
if (this.handle === null) { if (this.handle === null) {
return Promise.reject(new Error("'write()' failed: invalid handle")); return Promise.reject(new Error("'write()' failed: invalid handle"));
} }
return ops.write_file( return ops.write_file(
this.conn, this.conn,
this.handle, this.handle,
this.remote_path, this.remote_path,
offset, offset,
buffer, buffer,
this.thread_id this.thread_id
); );
} }
async write_base64(offset, base64_string) { async write_base64(offset, base64_string) {
if (this.handle === null) { if (this.handle === null) {
return Promise.reject( return Promise.reject(
new Error("'write_base64()' failed: invalid handle") new Error("'write_base64()' failed: invalid handle")
); );
} }
return ops.write_base64_file( return ops.write_base64_file(
this.conn, this.conn,
this.handle, this.handle,
this.remote_path, this.remote_path,
offset, offset,
base64_string, base64_string,
this.thread_id this.thread_id
); );
} }
} }

View File

@@ -1,177 +1,177 @@
import Socket from 'net'; import Socket from 'net';
import * as constants from '../utils/constants'; import * as constants from '../utils/constants';
import packet from './packet'; import packet from './packet';
export default class connection { export default class connection {
constructor(host_or_ip, port, password, socket) { constructor(host_or_ip, port, password, socket) {
this.host_or_ip = host_or_ip; this.host_or_ip = host_or_ip;
this.port = port; this.port = port;
this.password = password; this.password = password;
if (socket) { if (socket) {
this.socket = socket; this.socket = socket;
this.connected = true; this.connected = true;
this.setup_socket(); this.setup_socket();
} }
} }
connected = false; connected = false;
host_or_ip = ''; host_or_ip = '';
nonce = ''; nonce = '';
password = ''; password = '';
port = 20000; port = 20000;
reject; reject;
resolve; resolve;
socket; socket;
cleanup_handlers() { cleanup_handlers() {
this.reject = null; this.reject = null;
this.resolve = null; this.resolve = null;
} }
async connect() { async connect() {
if (!this.socket) { if (!this.socket) {
try { try {
await new Promise((resolve, reject) => { await new Promise((resolve, reject) => {
this.socket = Socket.createConnection( this.socket = Socket.createConnection(
this.port, this.port,
this.host_or_ip, this.host_or_ip,
(err) => { (err) => {
if (err) { if (err) {
console.log(err); console.log(err);
return reject(err); return reject(err);
} }
this.reject = reject; this.reject = reject;
this.resolve = resolve; this.resolve = resolve;
} }
); );
}); });
} catch (err) { } catch (err) {
return Promise.reject(new Error(`'connect()' failed: ${err}`)); return Promise.reject(new Error(`'connect()' failed: ${err}`));
} }
this.connected = true; this.connected = true;
this.setup_socket(); this.setup_socket();
} }
} }
setup_socket() { setup_socket() {
let buffer; let buffer;
const cleanup = () => { const cleanup = () => {
this.cleanup_handlers(); this.cleanup_handlers();
buffer = null; buffer = null;
}; };
if (this.socket._socket) { if (this.socket._socket) {
this.socket._socket.setNoDelay(true); this.socket._socket.setNoDelay(true);
this.socket._socket.setKeepAlive(true); this.socket._socket.setKeepAlive(true);
} else { } else {
this.socket.setNoDelay(true); this.socket.setNoDelay(true);
this.socket.setKeepAlive(true); this.socket.setKeepAlive(true);
} }
this.socket.on('data', (chunk) => { this.socket.on('data', (chunk) => {
buffer = buffer ? Buffer.concat([buffer, chunk]) : chunk; buffer = buffer ? Buffer.concat([buffer, chunk]) : chunk;
if (buffer.length > 4) { if (buffer.length > 4) {
const size = buffer.readUInt32BE(0); const size = buffer.readUInt32BE(0);
if (buffer.length >= size + 4) { if (buffer.length >= size + 4) {
const packet_data = buffer.slice(4, 4 + size); const packet_data = buffer.slice(4, 4 + size);
if (this.resolve) { if (this.resolve) {
const complete = () => { const complete = () => {
const reject = this.reject; const reject = this.reject;
const resolve = this.resolve; const resolve = this.resolve;
cleanup(); cleanup();
return { return {
reject, reject,
resolve, resolve,
}; };
}; };
const response = new packet(this.password); const response = new packet(this.password);
response.buffer = new Uint8Array(packet_data); response.buffer = new Uint8Array(packet_data);
response response
.decrypt() .decrypt()
.then(() => { .then(() => {
this.nonce = response.decode_utf8(); this.nonce = response.decode_utf8();
const { resolve } = complete(); const { resolve } = complete();
if (resolve) { if (resolve) {
resolve(response); resolve(response);
} }
}) })
.catch((e) => { .catch((e) => {
console.log(e); console.log(e);
const { reject } = complete(); const { reject } = complete();
if (reject) { if (reject) {
reject(e); reject(e);
} }
}); });
} }
} }
} }
}); });
this.socket.on('error', (e) => { this.socket.on('error', (e) => {
if (this.reject) { if (this.reject) {
const reject = this.reject; const reject = this.reject;
cleanup(); cleanup();
this.connected = false; this.connected = false;
console.log(e); console.log(e);
if (reject) { if (reject) {
reject(e); reject(e);
} }
} }
}); });
this.socket.on('close', () => { this.socket.on('close', () => {
if (this.reject) { if (this.reject) {
const reject = this.reject; const reject = this.reject;
cleanup(); cleanup();
this.connected = false; this.connected = false;
console.log('socket closed'); console.log('socket closed');
if (reject) { if (reject) {
reject(new Error('socket closed')); reject(new Error('socket closed'));
} }
} }
}); });
} }
async disconnect() { async disconnect() {
try { try {
if (this.socket) { if (this.socket) {
this.socket.destroy(); this.socket.destroy();
this.socket = null; this.socket = null;
this.cleanup_handlers(); this.cleanup_handlers();
this.connected = false; this.connected = false;
} }
} catch (e) { } catch (e) {
console.log(e); console.log(e);
} }
} }
async send(method_name, packet, optional_thread_id) { async send(method_name, packet, optional_thread_id) {
packet.token = this.password; packet.token = this.password;
packet.encode_top_utf8(method_name); packet.encode_top_utf8(method_name);
packet.encode_top_ui64(optional_thread_id || 1); packet.encode_top_ui64(optional_thread_id || 1);
packet.encode_top_utf8(constants.instance_id); packet.encode_top_utf8(constants.instance_id);
packet.encode_top_ui32(0); // Service flags packet.encode_top_ui32(0); // Service flags
packet.encode_top_utf8(constants.get_version()); packet.encode_top_utf8(constants.get_version());
packet.encode_top_utf8(nonce); packet.encode_top_utf8(nonce);
await packet.encrypt(); await packet.encrypt();
packet.encode_top_ui32(packet.buffer.length); packet.encode_top_ui32(packet.buffer.length);
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
this.reject = reject; this.reject = reject;
this.resolve = resolve; this.resolve = resolve;
this.socket.write(Buffer.from(packet.buffer), null, (err) => { this.socket.write(Buffer.from(packet.buffer), null, (err) => {
if (err) { if (err) {
this.cleanup_handlers(); this.cleanup_handlers();
reject(err); reject(err);
} }
}); });
}); });
} }
} }

View File

@@ -1,69 +1,69 @@
import Pool from 'socket-pool'; import Pool from 'socket-pool';
import connection from './connection'; import connection from './connection';
export default class connection_pool { export default class connection_pool {
constructor(pool_size, host_or_ip, port, password) { constructor(pool_size, host_or_ip, port, password) {
this.host_or_ip = host_or_ip; this.host_or_ip = host_or_ip;
this.port = port; this.port = port;
this.password = password; this.password = password;
if (pool_size > 1) { if (pool_size > 1) {
this.pool = new Pool({ this.pool = new Pool({
connect: { host: host_or_ip, port: port }, connect: { host: host_or_ip, port: port },
connectTimeout: 5000, connectTimeout: 5000,
pool: { max: pool_size, min: 2 }, pool: { max: pool_size, min: 2 },
}); });
} else { } else {
throw new Error("'pool_size' must be > 1"); throw new Error("'pool_size' must be > 1");
} }
} }
host_or_ip = ''; host_or_ip = '';
next_thread_id = 1; next_thread_id = 1;
password = ''; password = '';
port = 20000; port = 20000;
pool; pool;
shutdown = false; shutdown = false;
async disconnect() { async disconnect() {
await this.pool._pool.drain(); await this.pool._pool.drain();
await this.pool._pool.clear(); await this.pool._pool.clear();
this.pool = null; this.pool = null;
this.shutdown = true; this.shutdown = true;
} }
async send(method_name, packet, optional_thread_id) { async send(method_name, packet, optional_thread_id) {
try { try {
const socket = await this.pool.acquire(); const socket = await this.pool.acquire();
if (!socket.thread_id) { if (!socket.thread_id) {
socket.thread_id = this.next_thread_id++; socket.thread_id = this.next_thread_id++;
} }
const cleanup = () => { const cleanup = () => {
try { try {
socket.release(); socket.release();
} catch (err) { } catch (err) {
console.log(`'release()' failed: ${err}`); console.log(`'release()' failed: ${err}`);
} }
}; };
try { try {
const result = await new connection( const result = await new connection(
this.host_or_ip, this.host_or_ip,
this.port, this.port,
this.password, this.password,
socket socket
).send(method_name, packet, optional_thread_id || socket.thread_id); ).send(method_name, packet, optional_thread_id || socket.thread_id);
cleanup(); cleanup();
return result; return result;
} catch (err) { } catch (err) {
cleanup(); cleanup();
return Promise.reject( return Promise.reject(
new Error(`'send(${method_name})' failed: ${err}`) new Error(`'send(${method_name})' failed: ${err}`)
); );
} }
} catch (err) { } catch (err) {
return Promise.reject(new Error(`'acquire()' socket failed: ${err}`)); return Promise.reject(new Error(`'acquire()' socket failed: ${err}`));
} }
} }
} }

View File

@@ -1,330 +1,330 @@
import { randomBytes } from 'crypto'; import { randomBytes } from 'crypto';
import { Int64BE, Uint64BE } from 'int64-buffer'; import { Int64BE, Uint64BE } from 'int64-buffer';
import crypto from 'crypto'; import crypto from 'crypto';
import { TextEncoder } from 'text-encoding'; import { TextEncoder } from 'text-encoding';
import { getCustomEncryption } from '../utils/constants'; import { getCustomEncryption } from '../utils/constants';
import { import {
be_ui8_array_to_i16, be_ui8_array_to_i16,
be_ui8_array_to_i32, be_ui8_array_to_i32,
be_ui8_array_to_ui16, be_ui8_array_to_ui16,
be_ui8_array_to_ui32, be_ui8_array_to_ui32,
i16_to_be_ui8_array, i16_to_be_ui8_array,
i32_to_be_ui8_array, i32_to_be_ui8_array,
i8_to_ui8_array, i8_to_ui8_array,
ui16_to_be_ui8_array, ui16_to_be_ui8_array,
ui32_to_be_ui8_array, ui32_to_be_ui8_array,
ui8_array_to_i8, ui8_array_to_i8,
ui8_array_to_ui8, ui8_array_to_ui8,
ui8_to_ui8_array, ui8_to_ui8_array,
} from '../utils/byte_order'; } from '../utils/byte_order';
import { XChaCha20Poly1305 } from '@stablelib/xchacha20poly1305'; import { XChaCha20Poly1305 } from '@stablelib/xchacha20poly1305';
export default class packet { export default class packet {
constructor(token) { constructor(token) {
this.token = token; this.token = token;
} }
buffer = new Uint8Array(0); buffer = new Uint8Array(0);
decode_offset = 0; decode_offset = 0;
token; token;
append_buffer = (buffer) => { append_buffer = (buffer) => {
if (!(buffer instanceof Uint8Array)) { if (!(buffer instanceof Uint8Array)) {
throw new Error('Buffer must be of type Uint8Array'); throw new Error('Buffer must be of type Uint8Array');
} }
this.buffer = this.buffer this.buffer = this.buffer
? new Uint8Array([...this.buffer, ...buffer]) ? new Uint8Array([...this.buffer, ...buffer])
: buffer; : buffer;
}; };
clear = () => { clear = () => {
this.buffer = null; this.buffer = null;
this.decode_offset = 0; this.decode_offset = 0;
}; };
decode_buffer = (length) => { decode_buffer = (length) => {
if (!this.buffer) { if (!this.buffer) {
throw new Error('Invalid buffer'); throw new Error('Invalid buffer');
} }
const ret = this.buffer.slice( const ret = this.buffer.slice(
this.decode_offset, this.decode_offset,
this.decode_offset + length this.decode_offset + length
); );
this.decode_offset += length; this.decode_offset += length;
return Buffer.from(ret); return Buffer.from(ret);
}; };
decode_stat = () => { decode_stat = () => {
const mode = this.decode_ui16(); const mode = this.decode_ui16();
const nlink = this.decode_ui16(); const nlink = this.decode_ui16();
const uid = this.decode_ui32(); const uid = this.decode_ui32();
const gid = this.decode_ui32(); const gid = this.decode_ui32();
const atime = this.decode_ui64(); const atime = this.decode_ui64();
const mtime = this.decode_ui64(); const mtime = this.decode_ui64();
const ctime = this.decode_ui64(); const ctime = this.decode_ui64();
const birth_time = this.decode_ui64(); const birth_time = this.decode_ui64();
const size = this.decode_ui64(); const size = this.decode_ui64();
const blocks = this.decode_ui64(); const blocks = this.decode_ui64();
const blksize = this.decode_ui32(); const blksize = this.decode_ui32();
const flags = this.decode_ui32(); const flags = this.decode_ui32();
const directory = !!this.decode_ui8(); const directory = !!this.decode_ui8();
return { return {
mode, mode,
nlink, nlink,
uid, uid,
gid, gid,
atime, atime,
mtime, mtime,
ctime, ctime,
birth_time, birth_time,
size, size,
blocks, blocks,
blksize, blksize,
flags, flags,
directory, directory,
}; };
}; };
decode_utf8 = () => { decode_utf8 = () => {
if (!this.buffer) { if (!this.buffer) {
throw new Error('Invalid buffer'); throw new Error('Invalid buffer');
} }
const startIndex = this.decode_offset; const startIndex = this.decode_offset;
const endIndex = this.buffer.indexOf(0, startIndex); const endIndex = this.buffer.indexOf(0, startIndex);
if (endIndex >= 0) { if (endIndex >= 0) {
let ret = ''; let ret = '';
for (let i = startIndex; i < endIndex; i++) { for (let i = startIndex; i < endIndex; i++) {
ret += String.fromCharCode(this.buffer[i]); ret += String.fromCharCode(this.buffer[i]);
} }
this.decode_offset = endIndex + 1; this.decode_offset = endIndex + 1;
return ret; return ret;
} }
throw new Error('String not found in buffer'); throw new Error('String not found in buffer');
}; };
decode_i8 = () => { decode_i8 = () => {
return ui8_array_to_i8(this.buffer, this.decode_offset++); return ui8_array_to_i8(this.buffer, this.decode_offset++);
}; };
decode_ui8 = () => { decode_ui8 = () => {
return ui8_array_to_ui8(this.buffer, this.decode_offset++); return ui8_array_to_ui8(this.buffer, this.decode_offset++);
}; };
decode_i16 = () => { decode_i16 = () => {
const ret = be_ui8_array_to_i16(this.buffer, this.decode_offset); const ret = be_ui8_array_to_i16(this.buffer, this.decode_offset);
this.decode_offset += 2; this.decode_offset += 2;
return ret; return ret;
}; };
decode_ui16 = () => { decode_ui16 = () => {
const ret = be_ui8_array_to_ui16(this.buffer, this.decode_offset); const ret = be_ui8_array_to_ui16(this.buffer, this.decode_offset);
this.decode_offset += 2; this.decode_offset += 2;
return ret; return ret;
}; };
decode_i32 = () => { decode_i32 = () => {
const ret = be_ui8_array_to_i32(this.buffer, this.decode_offset); const ret = be_ui8_array_to_i32(this.buffer, this.decode_offset);
this.decode_offset += 4; this.decode_offset += 4;
return ret; return ret;
}; };
decode_ui32 = () => { decode_ui32 = () => {
const ret = be_ui8_array_to_ui32(this.buffer, this.decode_offset); const ret = be_ui8_array_to_ui32(this.buffer, this.decode_offset);
this.decode_offset += 4; this.decode_offset += 4;
return ret; return ret;
}; };
decode_i64 = () => { decode_i64 = () => {
const ret = new Int64BE( const ret = new Int64BE(
this.buffer.slice(this.decode_offset, this.decode_offset + 8) this.buffer.slice(this.decode_offset, this.decode_offset + 8)
); );
this.decode_offset += 8; this.decode_offset += 8;
return ret.toString(10); return ret.toString(10);
}; };
decode_ui64 = () => { decode_ui64 = () => {
const ret = new Uint64BE( const ret = new Uint64BE(
this.buffer.slice(this.decode_offset, this.decode_offset + 8) this.buffer.slice(this.decode_offset, this.decode_offset + 8)
); );
this.decode_offset += 8; this.decode_offset += 8;
return ret.toString(10); return ret.toString(10);
}; };
decrypt = async () => { decrypt = async () => {
try { try {
let hash = crypto.createHash('sha256'); let hash = crypto.createHash('sha256');
hash = hash.update(new TextEncoder().encode(this.token)); hash = hash.update(new TextEncoder().encode(this.token));
const key = Uint8Array.from(hash.digest()); const key = Uint8Array.from(hash.digest());
const nonce = this.buffer.slice(0, 24); const nonce = this.buffer.slice(0, 24);
const mac = this.buffer.slice(24, 16 + 24); const mac = this.buffer.slice(24, 16 + 24);
const customEncryption = getCustomEncryption(); const customEncryption = getCustomEncryption();
if (customEncryption) { if (customEncryption) {
this.buffer = Buffer.from( this.buffer = Buffer.from(
await customEncryption.decrypt( await customEncryption.decrypt(
Buffer.from(key).toString('base64'), Buffer.from(key).toString('base64'),
Buffer.from(nonce).toString('base64'), Buffer.from(nonce).toString('base64'),
Buffer.from(mac).toString('base64'), Buffer.from(mac).toString('base64'),
Buffer.from(this.buffer.slice(40)).toString('base64') Buffer.from(this.buffer.slice(40)).toString('base64')
), ),
'base64' 'base64'
); );
} else { } else {
const aad = ui32_to_be_ui8_array(this.buffer.length); const aad = ui32_to_be_ui8_array(this.buffer.length);
this.buffer = new Uint8Array([ this.buffer = new Uint8Array([
...this.buffer.slice(nonce.length + mac.length), ...this.buffer.slice(nonce.length + mac.length),
...this.buffer.slice(nonce.length, nonce.length + mac.length), ...this.buffer.slice(nonce.length, nonce.length + mac.length),
]); ]);
const result = new XChaCha20Poly1305(key).open(nonce, this.buffer, aad); const result = new XChaCha20Poly1305(key).open(nonce, this.buffer, aad);
if (!result) { if (!result) {
throw new Error('decryption failed'); throw new Error('decryption failed');
} }
this.buffer = Buffer.from(result); this.buffer = Buffer.from(result);
} }
this.buffer = new Uint8Array(this.buffer); this.buffer = new Uint8Array(this.buffer);
return this.buffer; return this.buffer;
} catch (e) { } catch (e) {
return Promise.reject(e); return Promise.reject(e);
} }
}; };
encode_buffer = (buffer) => { encode_buffer = (buffer) => {
this.append_buffer(new Uint8Array(buffer)); this.append_buffer(new Uint8Array(buffer));
}; };
encode_i8 = (num) => { encode_i8 = (num) => {
this.append_buffer(i8_to_ui8_array(num)); this.append_buffer(i8_to_ui8_array(num));
}; };
encode_top_i8 = (num) => { encode_top_i8 = (num) => {
this.push_buffer(i8_to_ui8_array(num)); this.push_buffer(i8_to_ui8_array(num));
}; };
encode_u8 = (num) => { encode_u8 = (num) => {
this.append_buffer(ui8_to_ui8_array(num)); this.append_buffer(ui8_to_ui8_array(num));
}; };
encode_top_u8 = (num) => { encode_top_u8 = (num) => {
this.push_buffer(ui8_to_ui8_array(num)); this.push_buffer(ui8_to_ui8_array(num));
}; };
encode_i16 = (num) => { encode_i16 = (num) => {
this.append_buffer(i16_to_be_ui8_array(num)); this.append_buffer(i16_to_be_ui8_array(num));
}; };
encode_top_i16 = (num) => { encode_top_i16 = (num) => {
this.push_buffer(i16_to_be_ui8_array(num)); this.push_buffer(i16_to_be_ui8_array(num));
}; };
encode_ui16 = (num) => { encode_ui16 = (num) => {
this.append_buffer(ui16_to_be_ui8_array(num)); this.append_buffer(ui16_to_be_ui8_array(num));
}; };
encode_top_ui16 = (num) => { encode_top_ui16 = (num) => {
this.push_buffer(ui16_to_be_ui8_array(num)); this.push_buffer(ui16_to_be_ui8_array(num));
}; };
encode_i32 = (num) => { encode_i32 = (num) => {
this.append_buffer(i32_to_be_ui8_array(num)); this.append_buffer(i32_to_be_ui8_array(num));
}; };
encode_top_i32 = (num) => { encode_top_i32 = (num) => {
this.push_buffer(i32_to_be_ui8_array(num)); this.push_buffer(i32_to_be_ui8_array(num));
}; };
encode_ui32 = (num) => { encode_ui32 = (num) => {
this.append_buffer(ui32_to_be_ui8_array(num)); this.append_buffer(ui32_to_be_ui8_array(num));
}; };
encode_top_ui32 = (num) => { encode_top_ui32 = (num) => {
this.push_buffer(ui32_to_be_ui8_array(num)); this.push_buffer(ui32_to_be_ui8_array(num));
}; };
encode_i64 = (num) => { encode_i64 = (num) => {
this.append_buffer(new Uint8Array(new Int64BE(num).toArray())); this.append_buffer(new Uint8Array(new Int64BE(num).toArray()));
}; };
encode_top_i64 = (num) => { encode_top_i64 = (num) => {
this.push_buffer(new Uint8Array(new Int64BE(num).toArray())); this.push_buffer(new Uint8Array(new Int64BE(num).toArray()));
}; };
encode_ui64 = (num) => { encode_ui64 = (num) => {
this.append_buffer(new Uint8Array(new Uint64BE(num).toArray())); this.append_buffer(new Uint8Array(new Uint64BE(num).toArray()));
}; };
encode_top_ui64 = (num) => { encode_top_ui64 = (num) => {
this.push_buffer(new Uint8Array(new Uint64BE(num).toArray())); this.push_buffer(new Uint8Array(new Uint64BE(num).toArray()));
}; };
encode_utf8 = (str) => { encode_utf8 = (str) => {
if (!(typeof str === 'string' || str instanceof String)) { if (!(typeof str === 'string' || str instanceof String)) {
throw new Error('Value must be of type string'); throw new Error('Value must be of type string');
} }
const buffer = new Uint8Array([...new TextEncoder().encode(str), 0]); const buffer = new Uint8Array([...new TextEncoder().encode(str), 0]);
this.append_buffer(buffer); this.append_buffer(buffer);
}; };
encode_top_utf8 = (str) => { encode_top_utf8 = (str) => {
if (!(typeof str === 'string' || str instanceof String)) { if (!(typeof str === 'string' || str instanceof String)) {
throw new Error('Value must be of type string'); throw new Error('Value must be of type string');
} }
const buffer = new Uint8Array([...new TextEncoder().encode(str), 0]); const buffer = new Uint8Array([...new TextEncoder().encode(str), 0]);
this.push_buffer(buffer); this.push_buffer(buffer);
}; };
encrypt = async (nonce) => { encrypt = async (nonce) => {
try { try {
let hash = crypto.createHash('sha256'); let hash = crypto.createHash('sha256');
hash = hash.update(new TextEncoder().encode(this.token)); hash = hash.update(new TextEncoder().encode(this.token));
const key = Uint8Array.from(hash.digest()); const key = Uint8Array.from(hash.digest());
if (!nonce) { if (!nonce) {
nonce = Uint8Array.from(randomBytes(24)); nonce = Uint8Array.from(randomBytes(24));
} }
const customEncryption = getCustomEncryption(); const customEncryption = getCustomEncryption();
if (customEncryption) { if (customEncryption) {
this.buffer = new Uint8Array( this.buffer = new Uint8Array(
Buffer.from( Buffer.from(
await customEncryption.encrypt( await customEncryption.encrypt(
Buffer.from(key).toString('base64'), Buffer.from(key).toString('base64'),
Buffer.from(nonce).toString('base64'), Buffer.from(nonce).toString('base64'),
Buffer.from(this.buffer).toString('base64') Buffer.from(this.buffer).toString('base64')
), ),
'base64' 'base64'
) )
); );
} else { } else {
const aad = ui32_to_be_ui8_array(this.buffer.length + 40); const aad = ui32_to_be_ui8_array(this.buffer.length + 40);
this.buffer = new XChaCha20Poly1305(key).seal(nonce, this.buffer, aad); this.buffer = new XChaCha20Poly1305(key).seal(nonce, this.buffer, aad);
this.buffer = new Uint8Array([ this.buffer = new Uint8Array([
...this.buffer.slice(this.buffer.length - 16), ...this.buffer.slice(this.buffer.length - 16),
...this.buffer.slice(0, this.buffer.length - 16), ...this.buffer.slice(0, this.buffer.length - 16),
]); ]);
this.push_buffer(nonce); this.push_buffer(nonce);
} }
return this.buffer; return this.buffer;
} catch (e) { } catch (e) {
return Promise.reject(e); return Promise.reject(e);
} }
}; };
push_buffer = (buffer) => { push_buffer = (buffer) => {
if (!(buffer instanceof Uint8Array)) { if (!(buffer instanceof Uint8Array)) {
throw new Error('Buffer must be of type Uint8Array'); throw new Error('Buffer must be of type Uint8Array');
} }
this.buffer = this.buffer this.buffer = this.buffer
? new Uint8Array([...buffer, ...this.buffer]) ? new Uint8Array([...buffer, ...this.buffer])
: buffer; : buffer;
}; };
} }

File diff suppressed because it is too large Load Diff

View File

@@ -1,112 +1,112 @@
export const is_big_endian_system = export const is_big_endian_system =
new Uint8Array(new Uint32Array([0x12345678]).buffer)[0] === 0x12; new Uint8Array(new Uint32Array([0x12345678]).buffer)[0] === 0x12;
export const is_little_endian_system = export const is_little_endian_system =
new Uint8Array(new Uint32Array([0x12345678]).buffer)[0] === 0x78; new Uint8Array(new Uint32Array([0x12345678]).buffer)[0] === 0x78;
export const i8_to_ui8_array = (num) => { export const i8_to_ui8_array = (num) => {
if (typeof num === 'string' || num instanceof String) { if (typeof num === 'string' || num instanceof String) {
num = parseInt(num, 10); num = parseInt(num, 10);
} }
const buffer = Buffer.alloc(1); const buffer = Buffer.alloc(1);
buffer.writeInt8(num); buffer.writeInt8(num);
return new Uint8Array(buffer); return new Uint8Array(buffer);
}; };
export const ui8_array_to_i8 = (ar, offset) => { export const ui8_array_to_i8 = (ar, offset) => {
const buffer = Buffer.alloc(1); const buffer = Buffer.alloc(1);
buffer[0] = ar[offset]; buffer[0] = ar[offset];
return buffer.readInt8(0); return buffer.readInt8(0);
}; };
export const ui8_to_ui8_array = (num) => { export const ui8_to_ui8_array = (num) => {
if (typeof num === 'string' || num instanceof String) { if (typeof num === 'string' || num instanceof String) {
num = parseInt(num, 10); num = parseInt(num, 10);
} }
const buffer = Buffer.alloc(1); const buffer = Buffer.alloc(1);
buffer.writeUInt8(num); buffer.writeUInt8(num);
return new Uint8Array(buffer); return new Uint8Array(buffer);
}; };
export const ui8_array_to_ui8 = (ar, offset) => { export const ui8_array_to_ui8 = (ar, offset) => {
const buffer = Buffer.alloc(1); const buffer = Buffer.alloc(1);
buffer[0] = ar[offset]; buffer[0] = ar[offset];
return buffer.readUInt8(0); return buffer.readUInt8(0);
}; };
export const i16_to_be_ui8_array = (num) => { export const i16_to_be_ui8_array = (num) => {
if (typeof num === 'string' || num instanceof String) { if (typeof num === 'string' || num instanceof String) {
num = parseInt(num, 10); num = parseInt(num, 10);
} }
const buffer = Buffer.alloc(2); const buffer = Buffer.alloc(2);
buffer.writeInt16BE(num); buffer.writeInt16BE(num);
return new Uint8Array(buffer); return new Uint8Array(buffer);
}; };
export const be_ui8_array_to_i16 = (ar, offset) => { export const be_ui8_array_to_i16 = (ar, offset) => {
const buffer = Buffer.alloc(2); const buffer = Buffer.alloc(2);
for (let i = offset; i < buffer.length + offset; i++) { for (let i = offset; i < buffer.length + offset; i++) {
buffer[i - offset] = ar[i]; buffer[i - offset] = ar[i];
} }
return buffer.readInt16BE(0); return buffer.readInt16BE(0);
}; };
export const ui16_to_be_ui8_array = (num) => { export const ui16_to_be_ui8_array = (num) => {
if (typeof num === 'string' || num instanceof String) { if (typeof num === 'string' || num instanceof String) {
num = parseInt(num, 10); num = parseInt(num, 10);
} }
const buffer = Buffer.alloc(2); const buffer = Buffer.alloc(2);
buffer.writeUInt16BE(num); buffer.writeUInt16BE(num);
return new Uint8Array(buffer); return new Uint8Array(buffer);
}; };
export const be_ui8_array_to_ui16 = (ar, offset) => { export const be_ui8_array_to_ui16 = (ar, offset) => {
const buffer = Buffer.alloc(2); const buffer = Buffer.alloc(2);
for (let i = offset; i < buffer.length + offset; i++) { for (let i = offset; i < buffer.length + offset; i++) {
buffer[i - offset] = ar[i]; buffer[i - offset] = ar[i];
} }
return buffer.readUInt16BE(0); return buffer.readUInt16BE(0);
}; };
export const i32_to_be_ui8_array = (num) => { export const i32_to_be_ui8_array = (num) => {
if (typeof num === 'string' || num instanceof String) { if (typeof num === 'string' || num instanceof String) {
num = parseInt(num, 10); num = parseInt(num, 10);
} }
const buffer = Buffer.alloc(4); const buffer = Buffer.alloc(4);
buffer.writeInt32BE(num); buffer.writeInt32BE(num);
return new Uint8Array(buffer); return new Uint8Array(buffer);
}; };
export const be_ui8_array_to_i32 = (ar, offset) => { export const be_ui8_array_to_i32 = (ar, offset) => {
const buffer = Buffer.alloc(4); const buffer = Buffer.alloc(4);
for (let i = offset; i < buffer.length + offset; i++) { for (let i = offset; i < buffer.length + offset; i++) {
buffer[i - offset] = ar[i]; buffer[i - offset] = ar[i];
} }
return buffer.readInt32BE(0); return buffer.readInt32BE(0);
}; };
export const ui32_to_be_ui8_array = (num) => { export const ui32_to_be_ui8_array = (num) => {
if (typeof num === 'string' || num instanceof String) { if (typeof num === 'string' || num instanceof String) {
num = parseInt(num, 10); num = parseInt(num, 10);
} }
const buffer = Buffer.alloc(4); const buffer = Buffer.alloc(4);
buffer.writeUInt32BE(num); buffer.writeUInt32BE(num);
return new Uint8Array(buffer); return new Uint8Array(buffer);
}; };
export const be_ui8_array_to_ui32 = (ar, offset) => { export const be_ui8_array_to_ui32 = (ar, offset) => {
const buffer = Buffer.alloc(4); const buffer = Buffer.alloc(4);
for (let i = offset; i < buffer.length + offset; i++) { for (let i = offset; i < buffer.length + offset; i++) {
buffer[i - offset] = ar[i]; buffer[i - offset] = ar[i];
} }
return buffer.readUInt32BE(0); return buffer.readUInt32BE(0);
}; };

View File

@@ -1,16 +1,16 @@
import { v4 as uuidv4 } from 'uuid'; import { v4 as uuidv4 } from 'uuid';
import _package_json from '../../package.json'; import _package_json from '../../package.json';
let customEncryption; let customEncryption;
export const getCustomEncryption = () => { export const getCustomEncryption = () => {
return customEncryption; return customEncryption;
}; };
export const setCustomEncryption = (ce) => { export const setCustomEncryption = (ce) => {
customEncryption = ce; customEncryption = ce;
}; };
export const instance_id = uuidv4(); export const instance_id = uuidv4();
export const package_json = _package_json; export const package_json = _package_json;
export const get_version = () => _package_json.version; export const get_version = () => _package_json.version;