hfs 0.26.9 → 0.29.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +17 -3
- package/admin/assets/index-cbb42a0e.js +415 -0
- package/admin/assets/index-f8049da8.css +1 -0
- package/{frontend/assets/sha512.6af42937.js → admin/assets/sha512-3273321f.js} +2 -2
- package/admin/index.html +2 -2
- package/frontend/assets/index-72e96bb2.js +85 -0
- package/frontend/assets/index-cbcc6ac5.css +1 -0
- package/{admin/assets/sha512.9dfe82e1.js → frontend/assets/sha512-2c2fa926.js} +2 -2
- package/frontend/index.html +3 -3
- package/package.json +8 -10
- package/plugins/vhosting/plugin.js +23 -20
- package/src/QuickZipStream.js +2 -25
- package/src/ThrottledStream.js +1 -1
- package/src/adminApis.js +6 -8
- package/src/api.accounts.js +10 -10
- package/src/api.auth.js +21 -17
- package/src/api.file_list.js +13 -6
- package/src/api.helpers.js +6 -6
- package/src/api.monitor.js +2 -0
- package/src/api.plugins.js +1 -0
- package/src/api.vfs.js +17 -19
- package/src/apiMiddleware.js +16 -9
- package/src/block.js +1 -0
- package/src/commands.js +1 -0
- package/src/config.js +3 -2
- package/src/connections.js +1 -1
- package/src/const.js +19 -7
- package/src/crypt.js +1 -1
- package/src/debounceAsync.js +1 -0
- package/src/events.js +1 -1
- package/src/frontEndApis.js +23 -2
- package/src/github.js +5 -1
- package/src/index.js +5 -3
- package/src/listen.js +6 -3
- package/src/log.js +6 -6
- package/src/middlewares.js +32 -26
- package/src/misc.js +27 -2
- package/src/perm.js +31 -29
- package/src/plugins.js +6 -8
- package/src/serveFile.js +15 -13
- package/src/serveGuiFiles.js +5 -28
- package/src/sse.js +3 -2
- package/src/throttler.js +1 -1
- package/src/update.js +3 -2
- package/src/upload.js +92 -0
- package/src/util-files.js +19 -13
- package/src/util-generators.js +1 -0
- package/src/util-http.js +3 -1
- package/src/util-os.js +41 -0
- package/src/vfs.js +44 -37
- package/src/watchLoad.js +1 -1
- package/src/zip.js +9 -6
- package/admin/assets/index.bb5198ec.js +0 -281
- package/admin/assets/index.dcc78777.css +0 -1
- package/frontend/assets/index.27a78796.js +0 -85
- package/frontend/assets/index.93366732.css +0 -1
package/src/api.monitor.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
// This file is part of HFS - Copyright 2021-2023, Massimo Melina <a@rejetto.com> - License https://www.gnu.org/licenses/gpl-3.0.txt
|
|
2
3
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
4
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
5
|
};
|
|
@@ -23,6 +24,7 @@ const apis = {
|
|
|
23
24
|
const list = new apiMiddleware_1.SendListReadable({ addAtStart: (0, connections_1.getConnections)().map(c => serializeConnection(c)) });
|
|
24
25
|
const throttledUpdate = lodash_1.default.throttle(update, 1000 / 20); // try to avoid clogging with updates
|
|
25
26
|
const state = Symbol('state'); // undefined=added, Timeout=add-pending, false=removed
|
|
27
|
+
list.props({ you: ctx.ip });
|
|
26
28
|
return list.events(ctx, {
|
|
27
29
|
connection(conn) {
|
|
28
30
|
conn[state] = setTimeout(() => add(conn), 100);
|
package/src/api.plugins.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
// This file is part of HFS - Copyright 2021-2023, Massimo Melina <a@rejetto.com> - License https://www.gnu.org/licenses/gpl-3.0.txt
|
|
2
3
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
4
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
5
|
};
|
package/src/api.vfs.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
// This file is part of HFS - Copyright 2021-
|
|
2
|
+
// This file is part of HFS - Copyright 2021-2023, Massimo Melina <a@rejetto.com> - License https://www.gnu.org/licenses/gpl-3.0.txt
|
|
3
3
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
4
4
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
5
5
|
};
|
|
@@ -10,10 +10,9 @@ const promises_1 = require("fs/promises");
|
|
|
10
10
|
const apiMiddleware_1 = require("./apiMiddleware");
|
|
11
11
|
const path_1 = require("path");
|
|
12
12
|
const misc_1 = require("./misc");
|
|
13
|
-
const child_process_1 = require("child_process");
|
|
14
|
-
const util_1 = require("util");
|
|
15
13
|
const const_1 = require("./const");
|
|
16
14
|
const micromatch_1 = require("micromatch");
|
|
15
|
+
const util_os_1 = require("./util-os");
|
|
17
16
|
// to manipulate the tree we need the original node
|
|
18
17
|
async function urlToNodeOriginal(uri) {
|
|
19
18
|
const n = await (0, vfs_1.urlToNode)(uri);
|
|
@@ -21,7 +20,10 @@ async function urlToNodeOriginal(uri) {
|
|
|
21
20
|
}
|
|
22
21
|
const apis = {
|
|
23
22
|
async get_vfs() {
|
|
24
|
-
return {
|
|
23
|
+
return {
|
|
24
|
+
root: vfs_1.vfs && await recur(vfs_1.vfs),
|
|
25
|
+
defaultPerms: vfs_1.defaultPerms,
|
|
26
|
+
};
|
|
25
27
|
async function recur(node) {
|
|
26
28
|
const dir = await (0, vfs_1.nodeIsDirectory)(node);
|
|
27
29
|
const stats = {};
|
|
@@ -49,8 +51,8 @@ const apis = {
|
|
|
49
51
|
async set_vfs({ uri, props }) {
|
|
50
52
|
const n = await urlToNodeOriginal(uri);
|
|
51
53
|
if (!n)
|
|
52
|
-
return new apiMiddleware_1.ApiError(
|
|
53
|
-
props = pickProps(props, ['name', 'source', '
|
|
54
|
+
return new apiMiddleware_1.ApiError(const_1.HTTP_NOT_FOUND, 'path not found');
|
|
55
|
+
props = pickProps(props, ['name', 'source', 'masks', 'default', ...Object.keys(vfs_1.defaultPerms)]);
|
|
54
56
|
props = (0, misc_1.objSameKeys)(props, v => v === null ? undefined : v); // null is a way to serialize undefined, that will restore default values
|
|
55
57
|
if (props.masks && typeof props.masks !== 'object')
|
|
56
58
|
delete props.masks;
|
|
@@ -63,35 +65,35 @@ const apis = {
|
|
|
63
65
|
async add_vfs({ under, source, name }) {
|
|
64
66
|
const n = under ? await urlToNodeOriginal(under) : vfs_1.vfs;
|
|
65
67
|
if (!n)
|
|
66
|
-
return new apiMiddleware_1.ApiError(
|
|
68
|
+
return new apiMiddleware_1.ApiError(const_1.HTTP_NOT_FOUND, 'invalid under');
|
|
67
69
|
if (n.isTemp || !await (0, vfs_1.nodeIsDirectory)(n))
|
|
68
|
-
return new apiMiddleware_1.ApiError(const_1.
|
|
70
|
+
return new apiMiddleware_1.ApiError(const_1.HTTP_NOT_ACCEPTABLE, 'invalid under');
|
|
69
71
|
if ((0, misc_1.isWindowsDrive)(source))
|
|
70
72
|
source += '\\'; // slash must be included, otherwise it will refer to the cwd of that drive
|
|
71
73
|
const a = n.children || (n.children = []);
|
|
72
74
|
if (source && a.find(x => x.source === source))
|
|
73
|
-
return new apiMiddleware_1.ApiError(
|
|
75
|
+
return new apiMiddleware_1.ApiError(const_1.HTTP_CONFLICT, 'already present');
|
|
74
76
|
a.unshift({ source, name });
|
|
75
77
|
await (0, vfs_1.saveVfs)();
|
|
76
78
|
return {};
|
|
77
79
|
},
|
|
78
80
|
async del_vfs({ uris }) {
|
|
79
81
|
if (!uris || !Array.isArray(uris))
|
|
80
|
-
return new apiMiddleware_1.ApiError(
|
|
82
|
+
return new apiMiddleware_1.ApiError(const_1.HTTP_BAD_REQUEST, 'invalid uris');
|
|
81
83
|
return {
|
|
82
84
|
errors: await Promise.all(uris.map(async (uri) => {
|
|
83
85
|
if (typeof uri !== 'string')
|
|
84
|
-
return
|
|
86
|
+
return const_1.HTTP_BAD_REQUEST;
|
|
85
87
|
const node = await urlToNodeOriginal(uri);
|
|
86
88
|
if (!node)
|
|
87
|
-
return
|
|
89
|
+
return const_1.HTTP_NOT_FOUND;
|
|
88
90
|
const parent = (0, path_1.dirname)(uri);
|
|
89
91
|
const parentNode = await urlToNodeOriginal(parent);
|
|
90
92
|
if (!parentNode)
|
|
91
|
-
return const_1.
|
|
93
|
+
return const_1.HTTP_NOT_ACCEPTABLE;
|
|
92
94
|
const { children } = parentNode;
|
|
93
95
|
if (!children) // shouldn't happen
|
|
94
|
-
return
|
|
96
|
+
return const_1.HTTP_SERVER_ERROR;
|
|
95
97
|
const idx = children.indexOf(node);
|
|
96
98
|
children.splice(idx, 1);
|
|
97
99
|
(0, vfs_1.saveVfs)();
|
|
@@ -112,7 +114,7 @@ const apis = {
|
|
|
112
114
|
async *ls({ path, files = true, fileMask }, ctx) {
|
|
113
115
|
if (!path && const_1.IS_WINDOWS) {
|
|
114
116
|
try {
|
|
115
|
-
for (const n of await getDrives())
|
|
117
|
+
for (const n of await (0, util_os_1.getDrives)())
|
|
116
118
|
yield { add: { n, k: 'd' } };
|
|
117
119
|
}
|
|
118
120
|
catch (error) {
|
|
@@ -158,7 +160,3 @@ function pickProps(o, keys) {
|
|
|
158
160
|
ret[k] = o[k] === null || o[k] === '' ? undefined : o[k];
|
|
159
161
|
return ret;
|
|
160
162
|
}
|
|
161
|
-
async function getDrives() {
|
|
162
|
-
const { stdout } = await (0, util_1.promisify)(child_process_1.exec)('wmic logicaldisk get name');
|
|
163
|
-
return stdout.split('\n').slice(1).map(x => x.trim()).filter(Boolean);
|
|
164
|
-
}
|
package/src/apiMiddleware.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
// This file is part of HFS - Copyright 2021-
|
|
2
|
+
// This file is part of HFS - Copyright 2021-2023, Massimo Melina <a@rejetto.com> - License https://www.gnu.org/licenses/gpl-3.0.txt
|
|
3
3
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
4
4
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
5
5
|
};
|
|
@@ -13,7 +13,7 @@ const const_1 = require("./const");
|
|
|
13
13
|
const lodash_1 = __importDefault(require("lodash"));
|
|
14
14
|
class ApiError extends Error {
|
|
15
15
|
constructor(status, message) {
|
|
16
|
-
super(typeof message === 'string' ? message : message
|
|
16
|
+
super(typeof message === 'string' ? message : message && message instanceof Error ? message.message : JSON.stringify(message));
|
|
17
17
|
this.status = status;
|
|
18
18
|
}
|
|
19
19
|
}
|
|
@@ -22,21 +22,25 @@ function apiMiddleware(apis) {
|
|
|
22
22
|
return async (ctx) => {
|
|
23
23
|
const { params } = ctx;
|
|
24
24
|
console.debug('API', ctx.method, ctx.path, { ...params });
|
|
25
|
-
|
|
25
|
+
const apiFun = apis.hasOwnProperty(ctx.path) && apis[ctx.path];
|
|
26
|
+
if (!apiFun) {
|
|
26
27
|
ctx.body = 'invalid api';
|
|
27
|
-
return ctx.status =
|
|
28
|
+
return ctx.status = const_1.HTTP_NOT_FOUND;
|
|
28
29
|
}
|
|
30
|
+
ctx.params = ctx.method === 'POST' ? (0, misc_1.tryJson)(await (0, misc_1.stream2string)(ctx.req))
|
|
31
|
+
: (0, misc_1.objSameKeys)(ctx.query, x => Array.isArray(x) ? x : (0, misc_1.tryJson)(x));
|
|
32
|
+
console.debug('API', ctx.method, ctx.path, { ...ctx.params });
|
|
29
33
|
const csrf = ctx.cookies.get('csrf');
|
|
30
34
|
// we don't rely on SameSite cookie option because it's https-only
|
|
31
|
-
let res = csrf && csrf !== params.csrf ? new ApiError(const_1.
|
|
32
|
-
: await
|
|
35
|
+
let res = csrf && csrf !== ctx.params.csrf ? new ApiError(const_1.HTTP_UNAUTHORIZED, 'csrf')
|
|
36
|
+
: await apiFun(ctx.params || {}, ctx);
|
|
33
37
|
if (isAsyncGenerator(res))
|
|
34
38
|
res = (0, misc_1.asyncGeneratorToReadable)(res);
|
|
35
39
|
if (res instanceof stream_1.Readable) { // Readable, we'll go SSE-mode
|
|
36
40
|
res.pipe((0, sse_1.default)(ctx));
|
|
37
|
-
const
|
|
41
|
+
const resAsReadable = res; // satisfy ts
|
|
38
42
|
ctx.req.on('close', () => // by closing the generated stream, creator of the stream will know the request is over without having to access anything else
|
|
39
|
-
|
|
43
|
+
resAsReadable.destroy());
|
|
40
44
|
return;
|
|
41
45
|
}
|
|
42
46
|
if (res instanceof ApiError) {
|
|
@@ -45,7 +49,7 @@ function apiMiddleware(apis) {
|
|
|
45
49
|
}
|
|
46
50
|
if (res instanceof Error) { // generic exception
|
|
47
51
|
ctx.body = String(res);
|
|
48
|
-
return ctx.status =
|
|
52
|
+
return ctx.status = const_1.HTTP_BAD_REQUEST;
|
|
49
53
|
}
|
|
50
54
|
ctx.body = res;
|
|
51
55
|
};
|
|
@@ -95,6 +99,9 @@ class SendListReadable extends stream_1.Readable {
|
|
|
95
99
|
custom(data) {
|
|
96
100
|
this._push(data);
|
|
97
101
|
}
|
|
102
|
+
props(props) {
|
|
103
|
+
this._push({ props });
|
|
104
|
+
}
|
|
98
105
|
error(msg, close = false) {
|
|
99
106
|
this._push({ error: msg });
|
|
100
107
|
this.lastError = msg;
|
package/src/block.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
// This file is part of HFS - Copyright 2021-2023, Massimo Melina <a@rejetto.com> - License https://www.gnu.org/licenses/gpl-3.0.txt
|
|
2
3
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
4
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
5
|
};
|
package/src/commands.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
// This file is part of HFS - Copyright 2021-2023, Massimo Melina <a@rejetto.com> - License https://www.gnu.org/licenses/gpl-3.0.txt
|
|
2
3
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
4
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
5
|
};
|
package/src/config.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
// This file is part of HFS - Copyright 2021-
|
|
2
|
+
// This file is part of HFS - Copyright 2021-2023, Massimo Melina <a@rejetto.com> - License https://www.gnu.org/licenses/gpl-3.0.txt
|
|
3
3
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
4
4
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
5
5
|
};
|
|
@@ -147,7 +147,8 @@ function setConfig1(k, newV, saveChanges = true) {
|
|
|
147
147
|
var _a;
|
|
148
148
|
if (lodash_1.default.isPlainObject(newV))
|
|
149
149
|
newV = lodash_1.default.pickBy(newV, x => x !== undefined);
|
|
150
|
-
|
|
150
|
+
const def = (_a = configProps[k]) === null || _a === void 0 ? void 0 : _a.defaultValue;
|
|
151
|
+
if ((0, misc_1.same)(newV !== null && newV !== void 0 ? newV : null, def !== null && def !== void 0 ? def : null))
|
|
151
152
|
newV = undefined;
|
|
152
153
|
if (started && (0, misc_1.same)(newV, state[k]))
|
|
153
154
|
return; // no change
|
package/src/connections.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
// This file is part of HFS - Copyright 2021-
|
|
2
|
+
// This file is part of HFS - Copyright 2021-2023, Massimo Melina <a@rejetto.com> - License https://www.gnu.org/licenses/gpl-3.0.txt
|
|
3
3
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
4
4
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
5
5
|
};
|
package/src/const.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
// This file is part of HFS - Copyright 2021-
|
|
2
|
+
// This file is part of HFS - Copyright 2021-2023, Massimo Melina <a@rejetto.com> - License https://www.gnu.org/licenses/gpl-3.0.txt
|
|
3
3
|
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
4
4
|
if (k2 === undefined) k2 = k;
|
|
5
5
|
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
@@ -27,7 +27,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
27
27
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
28
28
|
};
|
|
29
29
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
30
|
-
exports.APP_PATH = exports.IS_WINDOWS = exports.
|
|
30
|
+
exports.APP_PATH = exports.IS_WINDOWS = exports.HTTP_SERVER_ERROR = exports.HTTP_FOOL = exports.HTTP_RANGE_NOT_SATISFIABLE = exports.HTTP_PAYLOAD_TOO_LARGE = exports.HTTP_CONFLICT = exports.HTTP_NOT_ACCEPTABLE = exports.HTTP_METHOD_NOT_ALLOWED = exports.HTTP_NOT_FOUND = exports.HTTP_FORBIDDEN = exports.HTTP_UNAUTHORIZED = exports.HTTP_BAD_REQUEST = exports.HTTP_NOT_MODIFIED = exports.HTTP_TEMPORARY_REDIRECT = exports.HTTP_PARTIAL_CONTENT = exports.HTTP_NO_CONTENT = exports.HTTP_OK = exports.PLUGINS_PUB_URI = exports.API_URI = exports.ADMIN_URI = exports.FRONTEND_URI = exports.SPECIAL_URI = exports.COMPATIBLE_API_VERSION = exports.API_VERSION = exports.SESSION_DURATION = exports.DAY = exports.VERSION = exports.BUILD_TIMESTAMP = exports.HFS_STARTED = exports.ORIGINAL_CWD = exports.DEV = exports.argv = void 0;
|
|
31
31
|
const minimist_1 = __importDefault(require("minimist"));
|
|
32
32
|
const fs = __importStar(require("fs"));
|
|
33
33
|
const os_1 = require("os");
|
|
@@ -50,10 +50,22 @@ exports.FRONTEND_URI = exports.SPECIAL_URI + 'frontend/';
|
|
|
50
50
|
exports.ADMIN_URI = exports.SPECIAL_URI + 'admin/';
|
|
51
51
|
exports.API_URI = exports.SPECIAL_URI + 'api/';
|
|
52
52
|
exports.PLUGINS_PUB_URI = exports.SPECIAL_URI + 'plugins/';
|
|
53
|
-
exports.
|
|
54
|
-
exports.
|
|
55
|
-
exports.
|
|
56
|
-
exports.
|
|
53
|
+
exports.HTTP_OK = 200;
|
|
54
|
+
exports.HTTP_NO_CONTENT = 204;
|
|
55
|
+
exports.HTTP_PARTIAL_CONTENT = 206;
|
|
56
|
+
exports.HTTP_TEMPORARY_REDIRECT = 302;
|
|
57
|
+
exports.HTTP_NOT_MODIFIED = 304;
|
|
58
|
+
exports.HTTP_BAD_REQUEST = 400;
|
|
59
|
+
exports.HTTP_UNAUTHORIZED = 401;
|
|
60
|
+
exports.HTTP_FORBIDDEN = 403;
|
|
61
|
+
exports.HTTP_NOT_FOUND = 404;
|
|
62
|
+
exports.HTTP_METHOD_NOT_ALLOWED = 405;
|
|
63
|
+
exports.HTTP_NOT_ACCEPTABLE = 406;
|
|
64
|
+
exports.HTTP_CONFLICT = 409;
|
|
65
|
+
exports.HTTP_PAYLOAD_TOO_LARGE = 413;
|
|
66
|
+
exports.HTTP_RANGE_NOT_SATISFIABLE = 416;
|
|
67
|
+
exports.HTTP_FOOL = 418;
|
|
68
|
+
exports.HTTP_SERVER_ERROR = 500;
|
|
57
69
|
exports.IS_WINDOWS = process.platform === 'win32';
|
|
58
70
|
const IS_BINARY = !(0, path_1.basename)(process.argv0).includes('node'); // this won't be node if pkg was used
|
|
59
71
|
exports.APP_PATH = (0, path_1.dirname)(IS_BINARY ? process.argv0 : __dirname);
|
|
@@ -62,7 +74,7 @@ if (exports.DEV)
|
|
|
62
74
|
console.clear();
|
|
63
75
|
else
|
|
64
76
|
console.debug = () => { };
|
|
65
|
-
console.log(`HFS ~ HTTP File Server - Copyright 2021-
|
|
77
|
+
console.log(`HFS ~ HTTP File Server - Copyright 2021-2023, Massimo Melina <a@rejetto.com>`);
|
|
66
78
|
console.log(`License https://www.gnu.org/licenses/gpl-3.0.txt`);
|
|
67
79
|
console.log('started', exports.HFS_STARTED.toLocaleString(), exports.DEV);
|
|
68
80
|
console.log('version', exports.VERSION || '-');
|
package/src/crypt.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
// This file is part of HFS - Copyright 2021-
|
|
2
|
+
// This file is part of HFS - Copyright 2021-2023, Massimo Melina <a@rejetto.com> - License https://www.gnu.org/licenses/gpl-3.0.txt
|
|
3
3
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
4
4
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
5
5
|
};
|
package/src/debounceAsync.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
// This file is part of HFS - Copyright 2021-2023, Massimo Melina <a@rejetto.com> - License https://www.gnu.org/licenses/gpl-3.0.txt
|
|
2
3
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
4
|
// like lodash.debounce, but also avoids async invocations to overlap
|
|
4
5
|
function debounceAsync(callback, wait = 100, { leading = false, maxWait = Infinity } = {}) {
|
package/src/events.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
// This file is part of HFS - Copyright 2021-
|
|
2
|
+
// This file is part of HFS - Copyright 2021-2023, Massimo Melina <a@rejetto.com> - License https://www.gnu.org/licenses/gpl-3.0.txt
|
|
3
3
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
4
4
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
5
5
|
};
|
package/src/frontEndApis.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
// This file is part of HFS - Copyright 2021-
|
|
2
|
+
// This file is part of HFS - Copyright 2021-2023, Massimo Melina <a@rejetto.com> - License https://www.gnu.org/licenses/gpl-3.0.txt
|
|
3
3
|
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
4
4
|
if (k2 === undefined) k2 = k;
|
|
5
5
|
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
@@ -23,16 +23,37 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
|
|
23
23
|
__setModuleDefault(result, mod);
|
|
24
24
|
return result;
|
|
25
25
|
};
|
|
26
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
27
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
28
|
+
};
|
|
26
29
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
27
|
-
exports.frontEndApis = void 0;
|
|
30
|
+
exports.notifyClient = exports.frontEndApis = void 0;
|
|
31
|
+
const apiMiddleware_1 = require("./apiMiddleware");
|
|
28
32
|
const api_file_list_1 = require("./api.file_list");
|
|
29
33
|
const api_auth = __importStar(require("./api.auth"));
|
|
30
34
|
const config_1 = require("./config");
|
|
35
|
+
const events_1 = __importDefault(require("./events"));
|
|
31
36
|
const customHeader = (0, config_1.defineConfig)('custom_header');
|
|
32
37
|
exports.frontEndApis = {
|
|
33
38
|
file_list: api_file_list_1.file_list,
|
|
34
39
|
...api_auth,
|
|
35
40
|
config() {
|
|
36
41
|
return Object.fromEntries([customHeader].map(x => [x.key(), x.get()]));
|
|
42
|
+
},
|
|
43
|
+
get_notifications({ channel }, ctx) {
|
|
44
|
+
const list = new apiMiddleware_1.SendListReadable();
|
|
45
|
+
list.ready(); // on chrome109 EventSource doesn't emit 'open' until something is sent
|
|
46
|
+
return list.events(ctx, {
|
|
47
|
+
[NOTIFICATION_PREFIX + channel](name, data) {
|
|
48
|
+
list.custom({ name, data });
|
|
49
|
+
}
|
|
50
|
+
});
|
|
37
51
|
}
|
|
38
52
|
};
|
|
53
|
+
function notifyClient(ctx, name, data) {
|
|
54
|
+
const { notificationChannel } = ctx.query;
|
|
55
|
+
if (notificationChannel)
|
|
56
|
+
events_1.default.emit(NOTIFICATION_PREFIX + notificationChannel, name, data);
|
|
57
|
+
}
|
|
58
|
+
exports.notifyClient = notifyClient;
|
|
59
|
+
const NOTIFICATION_PREFIX = 'notificationChannel:';
|
package/src/github.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
// This file is part of HFS - Copyright 2021-2023, Massimo Melina <a@rejetto.com> - License https://www.gnu.org/licenses/gpl-3.0.txt
|
|
2
3
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
4
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
5
|
};
|
|
@@ -9,6 +10,7 @@ const misc_1 = require("./misc");
|
|
|
9
10
|
const plugins_1 = require("./plugins");
|
|
10
11
|
const apiMiddleware_1 = require("./apiMiddleware");
|
|
11
12
|
const lodash_1 = __importDefault(require("lodash"));
|
|
13
|
+
const const_1 = require("./const");
|
|
12
14
|
const DIST_ROOT = 'dist/';
|
|
13
15
|
const downloading = {};
|
|
14
16
|
function downloadProgress(id, status) {
|
|
@@ -20,12 +22,14 @@ function downloadProgress(id, status) {
|
|
|
20
22
|
}
|
|
21
23
|
async function downloadPlugin(repo, branch = '', overwrite) {
|
|
22
24
|
if (downloading[repo])
|
|
23
|
-
return new apiMiddleware_1.ApiError(
|
|
25
|
+
return new apiMiddleware_1.ApiError(const_1.HTTP_CONFLICT, "already downloading");
|
|
24
26
|
downloadProgress(repo, true);
|
|
25
27
|
const rec = await getRepoInfo(repo);
|
|
26
28
|
if (!branch)
|
|
27
29
|
branch = rec.default_branch;
|
|
28
30
|
const short = repo.split('/')[1]; // second part, repo without the owner
|
|
31
|
+
if (!short)
|
|
32
|
+
return new apiMiddleware_1.ApiError(const_1.HTTP_BAD_REQUEST, "bad repo");
|
|
29
33
|
const folder2repo = getFolder2repo();
|
|
30
34
|
const folder = overwrite ? lodash_1.default.findKey(folder2repo, x => x === repo) // use existing folder
|
|
31
35
|
: short in folder2repo ? repo.replace('/', '-') // longer form only if another plugin is using short form
|
package/src/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
"use strict";
|
|
3
|
-
// This file is part of HFS - Copyright 2021-
|
|
3
|
+
// This file is part of HFS - Copyright 2021-2023, Massimo Melina <a@rejetto.com> - License https://www.gnu.org/licenses/gpl-3.0.txt
|
|
4
4
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
5
5
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
6
6
|
};
|
|
@@ -23,6 +23,7 @@ const config_1 = require("./config");
|
|
|
23
23
|
const assert_1 = require("assert");
|
|
24
24
|
const lodash_1 = __importDefault(require("lodash"));
|
|
25
25
|
const misc_1 = require("./misc");
|
|
26
|
+
//import body from 'koa-better-body'
|
|
26
27
|
(0, assert_1.ok)(lodash_1.default.intersection(Object.keys(frontEndApis_1.frontEndApis), Object.keys(adminApis_1.adminApis)).length === 0); // they share same endpoints
|
|
27
28
|
const keys = ((_a = process.env.COOKIE_SIGN_KEYS) === null || _a === void 0 ? void 0 : _a.split(',')) || [(0, misc_1.randomId)(30)];
|
|
28
29
|
exports.app = new koa_1.default({ keys });
|
|
@@ -33,16 +34,17 @@ exports.app.use(middlewares_1.someSecurity)
|
|
|
33
34
|
.use((0, log_1.log)())
|
|
34
35
|
.use(throttler_1.throttler)
|
|
35
36
|
.use(middlewares_1.gzipper)
|
|
36
|
-
.use(middlewares_1.paramsDecoder)
|
|
37
37
|
.use((0, plugins_1.pluginsMiddleware)())
|
|
38
38
|
.use((0, koa_mount_1.default)(const_1.API_URI, (0, apiMiddleware_1.apiMiddleware)({ ...frontEndApis_1.frontEndApis, ...adminApis_1.adminApis })))
|
|
39
|
+
//.use(body({ multipart: false }))
|
|
39
40
|
.use(middlewares_1.serveGuiAndSharedFiles)
|
|
40
41
|
.on('error', errorHandler);
|
|
41
42
|
function errorHandler(err) {
|
|
42
43
|
const { code } = err;
|
|
43
44
|
if (const_1.DEV && code === 'ENOENT' && err.path.endsWith('sockjs-node'))
|
|
44
45
|
return; // spam out dev stuff
|
|
45
|
-
if (code === 'ECANCELED' || code === 'ECONNRESET' || code === 'ECONNABORTED' || code === 'EPIPE'
|
|
46
|
+
if (code === 'ECANCELED' || code === 'ECONNRESET' || code === 'ECONNABORTED' || code === 'EPIPE'
|
|
47
|
+
|| code === 'HPE_INVALID_EOF_STATE')
|
|
46
48
|
return; // someone interrupted, don't care
|
|
47
49
|
console.error('server error', err);
|
|
48
50
|
}
|
package/src/listen.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
// This file is part of HFS - Copyright 2021-
|
|
2
|
+
// This file is part of HFS - Copyright 2021-2023, Massimo Melina <a@rejetto.com> - License https://www.gnu.org/licenses/gpl-3.0.txt
|
|
3
3
|
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
4
4
|
if (k2 === undefined) k2 = k;
|
|
5
5
|
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
@@ -132,6 +132,8 @@ function startServer(srv, { port, host }) {
|
|
|
132
132
|
try {
|
|
133
133
|
if (port < 0 || !host && !await testIpV4()) // !host means ipV4+6, and if v4 port alone is busy we won't be notified of the failure, so we'll first test it on its own
|
|
134
134
|
return resolve(0);
|
|
135
|
+
// from a few tests, this seems enough to support the expect-100 http/1.1 mechanism, at least with curl -T, not used by chrome|firefox anyway
|
|
136
|
+
srv.on('checkContinue', (req, res) => srv.emit('request', req, res));
|
|
135
137
|
port = await listen(host);
|
|
136
138
|
if (port)
|
|
137
139
|
console.log(srv.name, "serving on", host || "any network", ':', port);
|
|
@@ -223,9 +225,10 @@ function printUrls(port, proto) {
|
|
|
223
225
|
if (!nets || ignore.test(name))
|
|
224
226
|
continue;
|
|
225
227
|
lodash_1.default.remove(nets, 'internal');
|
|
226
|
-
|
|
228
|
+
const first = nets[0];
|
|
229
|
+
if (!first)
|
|
227
230
|
continue;
|
|
228
|
-
const best = lodash_1.default.find(nets, { family: 'IPv4' }) ||
|
|
231
|
+
const best = lodash_1.default.find(nets, { family: 'IPv4' }) || first;
|
|
229
232
|
const appendPort = port === (proto === 'https' ? 443 : 80) ? '' : ':' + port;
|
|
230
233
|
let { address } = best;
|
|
231
234
|
if (address.includes(':'))
|
package/src/log.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
// This file is part of HFS - Copyright 2021-
|
|
2
|
+
// This file is part of HFS - Copyright 2021-2023, Massimo Melina <a@rejetto.com> - License https://www.gnu.org/licenses/gpl-3.0.txt
|
|
3
3
|
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
4
4
|
if (k2 === undefined) k2 = k;
|
|
5
5
|
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
@@ -35,7 +35,7 @@ const promises_1 = require("fs/promises");
|
|
|
35
35
|
const const_1 = require("./const");
|
|
36
36
|
const events_1 = __importDefault(require("./events"));
|
|
37
37
|
const lodash_1 = __importDefault(require("lodash"));
|
|
38
|
-
const
|
|
38
|
+
const util_files_1 = require("./util-files");
|
|
39
39
|
const perm_1 = require("./perm");
|
|
40
40
|
class Logger {
|
|
41
41
|
constructor(name) {
|
|
@@ -54,8 +54,8 @@ class Logger {
|
|
|
54
54
|
this.last = stats.mtime || stats.ctime;
|
|
55
55
|
}
|
|
56
56
|
catch (_b) {
|
|
57
|
-
|
|
58
|
-
|
|
57
|
+
if (await (0, util_files_1.prepareFolder)(path) === false)
|
|
58
|
+
console.log("cannot create folder for", path);
|
|
59
59
|
}
|
|
60
60
|
this.reopen();
|
|
61
61
|
}
|
|
@@ -81,7 +81,7 @@ const logRotation = (0, config_1.defineConfig)('log_rotation', 'weekly');
|
|
|
81
81
|
function log() {
|
|
82
82
|
const debounce = lodash_1.default.debounce(cb => cb(), 1000);
|
|
83
83
|
return async (ctx, next) => {
|
|
84
|
-
var _a;
|
|
84
|
+
var _a, _b;
|
|
85
85
|
await next();
|
|
86
86
|
const isError = ctx.status >= 400;
|
|
87
87
|
const logger = isError && accessErrorLog || accessLogger;
|
|
@@ -112,7 +112,7 @@ function log() {
|
|
|
112
112
|
}
|
|
113
113
|
}
|
|
114
114
|
const format = '%s - %s [%s] "%s %s HTTP/%s" %d %s\n'; // Apache's Common Log Format
|
|
115
|
-
const date = a[2] + '/' + a[1] + '/' + a[3] + ':' + a[4] + ' ' + a[5].slice(3);
|
|
115
|
+
const date = a[2] + '/' + a[1] + '/' + a[3] + ':' + a[4] + ' ' + ((_b = a[5]) === null || _b === void 0 ? void 0 : _b.slice(3));
|
|
116
116
|
const user = (0, perm_1.getCurrentUsername)(ctx);
|
|
117
117
|
events_1.default.emit(logger.name, Object.assign(lodash_1.default.pick(ctx, ['ip', 'method', 'status', 'length']), { user, ts: now, uri: ctx.path }));
|
|
118
118
|
console.debug(ctx.status, ctx.method, ctx.path);
|
package/src/middlewares.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
// This file is part of HFS - Copyright 2021-
|
|
2
|
+
// This file is part of HFS - Copyright 2021-2023, Massimo Melina <a@rejetto.com> - License https://www.gnu.org/licenses/gpl-3.0.txt
|
|
3
3
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
4
4
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
5
5
|
};
|
|
6
6
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
-
exports.
|
|
7
|
+
exports.prepareState = exports.getProxyDetected = exports.someSecurity = exports.serveGuiAndSharedFiles = exports.sessions = exports.headRequests = exports.gzipper = void 0;
|
|
8
8
|
const koa_compress_1 = __importDefault(require("koa-compress"));
|
|
9
9
|
const koa_session_1 = __importDefault(require("koa-session"));
|
|
10
10
|
const const_1 = require("./const");
|
|
@@ -22,6 +22,10 @@ const connections_1 = require("./connections");
|
|
|
22
22
|
const basic_auth_1 = __importDefault(require("basic-auth"));
|
|
23
23
|
const tssrp6a_1 = require("tssrp6a");
|
|
24
24
|
const api_auth_1 = require("./api.auth");
|
|
25
|
+
const path_1 = require("path");
|
|
26
|
+
const promises_1 = require("stream/promises");
|
|
27
|
+
const formidable_1 = __importDefault(require("formidable"));
|
|
28
|
+
const upload_1 = require("./upload");
|
|
25
29
|
exports.gzipper = (0, koa_compress_1.default)({
|
|
26
30
|
threshold: 2048,
|
|
27
31
|
gzip: { flush: require('zlib').constants.Z_SYNC_FLUSH },
|
|
@@ -66,9 +70,33 @@ const serveGuiAndSharedFiles = async (ctx, next) => {
|
|
|
66
70
|
return ctx.redirect(const_1.ADMIN_URI);
|
|
67
71
|
if (path.startsWith(const_1.ADMIN_URI))
|
|
68
72
|
return serveAdminPrefixed(ctx, next);
|
|
73
|
+
if (ctx.method === 'PUT') { // curl -T file url/
|
|
74
|
+
const decPath = decodeURI(path);
|
|
75
|
+
let rest = (0, path_1.basename)(decPath);
|
|
76
|
+
const folder = await (0, vfs_1.urlToNode)((0, path_1.dirname)(decPath), ctx, vfs_1.vfs, v => rest = v + '/' + rest);
|
|
77
|
+
if (!folder)
|
|
78
|
+
return ctx.status = const_1.HTTP_NOT_FOUND;
|
|
79
|
+
const dest = (0, upload_1.uploadWriter)(folder, rest, ctx);
|
|
80
|
+
if (dest) {
|
|
81
|
+
await (0, promises_1.pipeline)(ctx.req, dest);
|
|
82
|
+
ctx.body = {};
|
|
83
|
+
}
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
69
86
|
const node = await (0, vfs_1.urlToNode)(path, ctx);
|
|
70
87
|
if (!node)
|
|
71
|
-
return ctx.status =
|
|
88
|
+
return ctx.status = const_1.HTTP_NOT_FOUND;
|
|
89
|
+
if (ctx.method === 'POST') { // curl -F upload=@file url/
|
|
90
|
+
ctx.body = {};
|
|
91
|
+
const form = (0, formidable_1.default)({
|
|
92
|
+
maxFileSize: Infinity,
|
|
93
|
+
//@ts-ignore wrong in the .d.ts file
|
|
94
|
+
fileWriteStreamHandler: f => (0, upload_1.uploadWriter)(node, f.originalFilename, ctx)
|
|
95
|
+
});
|
|
96
|
+
form.parse(ctx.req);
|
|
97
|
+
await (0, stream_1.once)(form, 'end').catch(() => { });
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
72
100
|
const canRead = (0, vfs_1.hasPermission)(node, 'can_read', ctx);
|
|
73
101
|
const isFolder = await (0, vfs_1.nodeIsDirectory)(node);
|
|
74
102
|
if (isFolder && !path.endsWith('/'))
|
|
@@ -78,7 +106,7 @@ const serveGuiAndSharedFiles = async (ctx, next) => {
|
|
|
78
106
|
: next();
|
|
79
107
|
if (!canRead) {
|
|
80
108
|
ctx.status = (0, vfs_1.cantReadStatusCode)(node);
|
|
81
|
-
if (ctx.status === const_1.
|
|
109
|
+
if (ctx.status === const_1.HTTP_FORBIDDEN)
|
|
82
110
|
return;
|
|
83
111
|
const browserDetected = ctx.get('Upgrade-Insecure-Requests') || ctx.get('Sec-Fetch-Mode'); // ugh, heuristics
|
|
84
112
|
if (!browserDetected) // we don't want to trigger basic authentication on browsers, it's meant for download managers only
|
|
@@ -151,25 +179,3 @@ async function srpCheck(username, password) {
|
|
|
151
179
|
const clientRes2 = await clientRes1.step2(BigInt(salt), BigInt(pubKey));
|
|
152
180
|
return await step1.step2(clientRes2.A, clientRes2.M1).then(() => true, () => false);
|
|
153
181
|
}
|
|
154
|
-
// unify get/post parameters, with JSON decoding to not be limited to strings
|
|
155
|
-
const paramsDecoder = async (ctx, next) => {
|
|
156
|
-
ctx.params = ctx.method === 'POST' ? (0, misc_1.tryJson)(await getReqData(ctx.req))
|
|
157
|
-
: (0, misc_1.objSameKeys)(ctx.query, x => Array.isArray(x) ? x : (0, misc_1.tryJson)(x));
|
|
158
|
-
await next();
|
|
159
|
-
};
|
|
160
|
-
exports.paramsDecoder = paramsDecoder;
|
|
161
|
-
async function getReqData(req) {
|
|
162
|
-
return new Promise((resolve, reject) => {
|
|
163
|
-
let data = '';
|
|
164
|
-
req.on('data', chunk => data += chunk);
|
|
165
|
-
req.on('error', reject);
|
|
166
|
-
req.on('end', () => {
|
|
167
|
-
try {
|
|
168
|
-
resolve(data);
|
|
169
|
-
}
|
|
170
|
-
catch (e) {
|
|
171
|
-
reject(e);
|
|
172
|
-
}
|
|
173
|
-
});
|
|
174
|
-
});
|
|
175
|
-
}
|
package/src/misc.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
// This file is part of HFS - Copyright 2021-
|
|
2
|
+
// This file is part of HFS - Copyright 2021-2023, Massimo Melina <a@rejetto.com> - License https://www.gnu.org/licenses/gpl-3.0.txt
|
|
3
3
|
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
4
4
|
if (k2 === undefined) k2 = k;
|
|
5
5
|
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
@@ -18,7 +18,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
18
18
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
19
19
|
};
|
|
20
20
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
21
|
-
exports.tryJson = exports.same = exports.isLocalHost = exports.with_ = exports.typedKeys = exports.objRenameKey = exports.onOff = exports.pendingPromise = exports.onlyTruthy = exports.truthy = exports.pattern2filter = exports.onFirstEvent = exports.onProcessExit = exports.randomId = exports.getOrSet = exports.wantArray = exports.wait = exports.objSameKeys = exports.setHidden = exports.prefix = exports.enforceFinal = exports.debounceAsync = void 0;
|
|
21
|
+
exports.try_ = exports.stream2string = exports.tryJson = exports.same = exports.isLocalHost = exports.with_ = exports.typedKeys = exports.objRenameKey = exports.onOff = exports.pendingPromise = exports.onlyTruthy = exports.truthy = exports.pattern2filter = exports.onFirstEvent = exports.onProcessExit = exports.randomId = exports.getOrSet = exports.wantArray = exports.wait = exports.objSameKeys = exports.setHidden = exports.prefix = exports.enforceFinal = exports.debounceAsync = void 0;
|
|
22
22
|
const path_1 = require("path");
|
|
23
23
|
const lodash_1 = __importDefault(require("lodash"));
|
|
24
24
|
const assert_1 = __importDefault(require("assert"));
|
|
@@ -158,3 +158,28 @@ function tryJson(s) {
|
|
|
158
158
|
catch (_a) { }
|
|
159
159
|
}
|
|
160
160
|
exports.tryJson = tryJson;
|
|
161
|
+
async function stream2string(stream) {
|
|
162
|
+
return new Promise((resolve, reject) => {
|
|
163
|
+
let data = '';
|
|
164
|
+
stream.on('data', chunk => data += chunk);
|
|
165
|
+
stream.on('error', reject);
|
|
166
|
+
stream.on('end', () => {
|
|
167
|
+
try {
|
|
168
|
+
resolve(data);
|
|
169
|
+
}
|
|
170
|
+
catch (e) {
|
|
171
|
+
reject(e);
|
|
172
|
+
}
|
|
173
|
+
});
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
exports.stream2string = stream2string;
|
|
177
|
+
function try_(cb, onException) {
|
|
178
|
+
try {
|
|
179
|
+
return cb();
|
|
180
|
+
}
|
|
181
|
+
catch (e) {
|
|
182
|
+
return onException === null || onException === void 0 ? void 0 : onException(e);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
exports.try_ = try_;
|