@vbarbarosh/node-helpers 1.1.3 → 2.0.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 +1 -1
- package/package.json +1 -1
- package/src/array_chunk.js +1 -1
- package/src/array_chunk.test.js +2 -2
- package/src/array_gcd.js +1 -1
- package/src/array_gcd.test.js +2 -2
- package/src/array_group.js +2 -2
- package/src/array_group_map.js +1 -1
- package/src/array_index.js +1 -1
- package/src/array_index.test.js +2 -2
- package/src/array_max.js +1 -1
- package/src/array_max.test.js +2 -2
- package/src/array_min.js +1 -1
- package/src/array_min.test.js +2 -2
- package/src/array_shuffle.js +24 -0
- package/src/array_sort.js +2 -2
- package/src/array_sort.test.js +2 -2
- package/src/array_sort_other.js +2 -2
- package/src/array_sort_other.test.js +2 -2
- package/src/array_sum.js +1 -1
- package/src/array_sum.test.js +2 -2
- package/src/array_unique_last.js +1 -1
- package/src/cli.js +8 -25
- package/src/crypto_decrypt_aes256.js +2 -2
- package/src/crypto_encrypt_aes256.js +2 -2
- package/src/crypto_encrypt_aes256.test.js +3 -3
- package/src/crypto_hash_md5.js +2 -2
- package/src/crypto_hash_sha256.js +2 -2
- package/src/crypto_hash_sha256.test.js +2 -2
- package/src/crypto_hmac_sha256.js +2 -2
- package/src/fcmp_dates.js +1 -1
- package/src/fcmp_numbers.js +1 -1
- package/src/fcmp_strings.js +1 -1
- package/src/format_thousands.js +1 -1
- package/src/fs_append.js +3 -3
- package/src/fs_append_json.js +3 -3
- package/src/fs_assert_file_exists.js +2 -2
- package/src/fs_assert_file_readable.js +2 -2
- package/src/fs_copy.js +3 -3
- package/src/fs_exists.js +2 -2
- package/src/fs_fclose.js +3 -3
- package/src/fs_fi.js +6 -6
- package/src/fs_find.js +5 -5
- package/src/fs_fopen.js +3 -3
- package/src/fs_fopen_update.js +2 -2
- package/src/fs_fread.js +3 -3
- package/src/fs_fsize.js +2 -2
- package/src/fs_fstat.js +3 -3
- package/src/fs_fwrite.js +3 -3
- package/src/fs_ls.js +5 -5
- package/src/fs_lsr.js +5 -5
- package/src/fs_lstat.js +3 -3
- package/src/fs_mkdir.js +3 -3
- package/src/fs_mkdirp.js +17 -11
- package/src/fs_mkdirp.test.js +15 -0
- package/src/fs_path_basename.js +2 -2
- package/src/fs_path_dirname.js +2 -2
- package/src/fs_path_join.js +2 -2
- package/src/fs_path_resolve.js +2 -2
- package/src/fs_path_sanitize.js +2 -2
- package/src/fs_path_tempdir.js +2 -2
- package/src/fs_read.js +3 -3
- package/src/fs_read_json.js +2 -2
- package/src/fs_read_stream.js +3 -1
- package/src/fs_read_utf8.js +2 -2
- package/src/fs_readdir.js +3 -3
- package/src/fs_readlink.js +3 -3
- package/src/fs_rename.js +3 -3
- package/src/fs_rm.js +3 -3
- package/src/fs_rmdir.js +3 -3
- package/src/fs_rmrf.js +7 -7
- package/src/fs_size.js +2 -2
- package/src/fs_tempdir.js +6 -6
- package/src/fs_write.js +3 -3
- package/src/fs_write_stream.js +3 -1
- package/src/http_delete.js +2 -2
- package/src/http_get_blob.js +2 -2
- package/src/http_get_buffer.js +2 -2
- package/src/http_get_file.js +6 -6
- package/src/http_get_json.js +2 -2
- package/src/http_patch_json.js +2 -2
- package/src/http_post_json.js +2 -2
- package/src/http_post_multipart.js +3 -3
- package/src/http_post_urlencoded.js +3 -3
- package/src/http_put_file.js +7 -7
- package/src/http_put_json.js +2 -2
- package/src/identity.js +1 -1
- package/src/ignore.js +1 -1
- package/src/json_stringify_stable.js +2 -2
- package/src/now_atom.js +1 -1
- package/src/random_int.js +6 -0
- package/src/redis_connect.js +21 -0
- package/src/redis_lshift.js +8 -0
- package/src/redis_poll.js +106 -0
- package/src/redis_poll_zshift_callback_rpush.js +141 -0
- package/src/redis_poll_zshift_spawn_rpush.js +159 -0
- package/src/redis_zshift.js +9 -0
- package/src/stream_data_ln.js +1 -1
- package/src/stream_data_ln.test.js +4 -4
- package/src/stream_promise.js +2 -2
- package/src/stream_promise.test.js +3 -3
- package/src/throttle.js +1 -1
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
const Promise = require('bluebird');
|
|
2
|
+
const random_int = require('./random_int');
|
|
3
|
+
const redis_zshift = require('./redis_zshift');
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Poll redis for input messages, await for callback, rpush results and user friendly statuses.
|
|
7
|
+
* @param options
|
|
8
|
+
* @returns {Promise<void>}
|
|
9
|
+
*
|
|
10
|
+
* const options = {
|
|
11
|
+
* log: logger_create(),
|
|
12
|
+
* redis: redis_connect2('redis://127.0.0.1'),
|
|
13
|
+
* redis_input_queue: 'mp4gif_input',
|
|
14
|
+
* redis_output_queue: 'mp4gif_output',
|
|
15
|
+
* callback: function (message) { ... },
|
|
16
|
+
* log_waiter_begin: 'mp4gif_waiter_begin',
|
|
17
|
+
* log_waiter_end: 'mp4gif_waiter_end',
|
|
18
|
+
* log_waiter_sleep: 'mp4gif_waiter_sleep',
|
|
19
|
+
* log_waiter_rpush: 'mp4gif_waiter_rpush',
|
|
20
|
+
* log_waiter_error: 'mp4gif_waiter_error',
|
|
21
|
+
* log_waiter_error_parse: 'mp4gif_waiter_error_parse',
|
|
22
|
+
* log_waiter_error_no_uid: 'mp4gif_waiter_error_no_uid',
|
|
23
|
+
* log_worker_begin: 'mp4gif_worker_begin',
|
|
24
|
+
* log_worker_end_ok: 'mp4gif_worker_end_ok',
|
|
25
|
+
* log_worker_end_error: 'mp4gif_worker_end_error',
|
|
26
|
+
* log_worker_stdout: 'mp4gif_worker_stdout',
|
|
27
|
+
* log_worker_stderr: 'mp4gif_worker_stderr',
|
|
28
|
+
* log_worker_user_friendly_status: 'mp4gif_worker_user_friendly_status',
|
|
29
|
+
* };
|
|
30
|
+
* await redis_poll_zshift_callback_rpush(options);
|
|
31
|
+
*/
|
|
32
|
+
async function redis_poll_zshift_callback_rpush(options)
|
|
33
|
+
{
|
|
34
|
+
const {log, redis, redis_input_queue, redis_output_queue, version} = options;
|
|
35
|
+
const {
|
|
36
|
+
log_waiter_begin,
|
|
37
|
+
log_waiter_end,
|
|
38
|
+
log_waiter_sleep,
|
|
39
|
+
log_waiter_rpush,
|
|
40
|
+
log_waiter_error,
|
|
41
|
+
log_waiter_error_parse,
|
|
42
|
+
log_waiter_error_no_uid,
|
|
43
|
+
} = options;
|
|
44
|
+
|
|
45
|
+
log(`[${log_waiter_begin}] v${version}`);
|
|
46
|
+
try {
|
|
47
|
+
for (let iter = 1; iter <= 24*60*60; ++iter) {
|
|
48
|
+
|
|
49
|
+
// const message_string = await redis.lpop_p(redis_input_queue);
|
|
50
|
+
const [message_string] = await redis_zshift(redis, redis_input_queue);
|
|
51
|
+
if (!message_string) {
|
|
52
|
+
const ms = random_int(500, 1500);
|
|
53
|
+
log(`[${log_waiter_sleep}] ${ms}ms`);
|
|
54
|
+
await Promise.delay(ms);
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
console.log(message_string);
|
|
58
|
+
|
|
59
|
+
let message = null;
|
|
60
|
+
try {
|
|
61
|
+
message = JSON.parse(message_string);
|
|
62
|
+
}
|
|
63
|
+
catch (error) {
|
|
64
|
+
log(`[${log_waiter_error_parse}] ${error.message}`);
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
try {
|
|
69
|
+
const uid = message.uid;
|
|
70
|
+
|
|
71
|
+
if (!uid) {
|
|
72
|
+
log(`[${log_waiter_error_no_uid}] uid is missed; skipped`);
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
if (!message.expires_at) {
|
|
76
|
+
throw new Error('expires_at is missed');
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// This is necessary because we use signed urls which has limited lifetime.
|
|
80
|
+
// Basically, `expires_at` tell when signed url will be expired.
|
|
81
|
+
if (new Date(message.expires_at).getTime() <= new Date().getTime()) {
|
|
82
|
+
throw new Error('Expired');
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const value = await worker(log.spawn(), message, options);
|
|
86
|
+
const response = JSON.stringify({uid: message.uid, version, type: 'resolve', value});
|
|
87
|
+
await redis.rpush_p(redis_output_queue, response);
|
|
88
|
+
|
|
89
|
+
log(`[${log_waiter_rpush}]`);
|
|
90
|
+
}
|
|
91
|
+
catch (error) {
|
|
92
|
+
const response = JSON.stringify({uid: message.uid, version, type: 'reject', value: `${(error && (error.stack || error.message)) || 'Error N/A'}`});
|
|
93
|
+
log(`[${log_waiter_error}] ${response}`);
|
|
94
|
+
await redis.rpush_p(redis_output_queue, response);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
finally {
|
|
99
|
+
log(`[${log_waiter_end}]`);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
async function worker(log, message, options)
|
|
104
|
+
{
|
|
105
|
+
const {redis, redis_output_queue, spawn_command, version} = options;
|
|
106
|
+
const {
|
|
107
|
+
callback,
|
|
108
|
+
log_worker_begin,
|
|
109
|
+
log_worker_end_ok,
|
|
110
|
+
log_worker_end_error,
|
|
111
|
+
log_worker_stdout,
|
|
112
|
+
log_worker_stderr,
|
|
113
|
+
log_worker_user_friendly_status,
|
|
114
|
+
} = options;
|
|
115
|
+
|
|
116
|
+
log(`[${log_worker_begin}] ${message.uid}`);
|
|
117
|
+
|
|
118
|
+
const uid = message.uid;
|
|
119
|
+
|
|
120
|
+
try {
|
|
121
|
+
// GOTCHA
|
|
122
|
+
// Promise won't update its status until at least one message would
|
|
123
|
+
// be taken from output queue. Pushing generic "Started...." message
|
|
124
|
+
// will tell the promise to refresh its status.
|
|
125
|
+
await user_friendly_status('Started...');
|
|
126
|
+
const out = await callback({message, log: log.spawn(), user_friendly_status});
|
|
127
|
+
log(`[${log_worker_end_ok}]`);
|
|
128
|
+
return out;
|
|
129
|
+
}
|
|
130
|
+
catch (error) {
|
|
131
|
+
log(`[${log_worker_end_error}] ${error.message}`);
|
|
132
|
+
throw error;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
async function user_friendly_status(value) {
|
|
136
|
+
log(`[${log_worker_user_friendly_status}] ${value}`);
|
|
137
|
+
await redis.rpush_p(redis_output_queue, JSON.stringify({uid, version, type: 'user_friendly_status', value}));
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
module.exports = redis_poll_zshift_callback_rpush;
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
const Promise = require('bluebird');
|
|
2
|
+
const child_process = require('child_process');
|
|
3
|
+
const fs_tempdir = require('./fs_mkdirp');
|
|
4
|
+
const random_int = require('./random_int');
|
|
5
|
+
const redis_zshift = require('./redis_zshift');
|
|
6
|
+
const stream_data_ln = require('./stream_data_ln');
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Poll redis for input messages, spawn shell script, rpush results and user friendly statuses.
|
|
10
|
+
* @param options
|
|
11
|
+
* @returns {Promise<void>}
|
|
12
|
+
*
|
|
13
|
+
* const options = {
|
|
14
|
+
* log: logger_create(),
|
|
15
|
+
* redis: redis_connect2('redis://127.0.0.1'),
|
|
16
|
+
* redis_input_queue: 'mp4gif_input',
|
|
17
|
+
* redis_output_queue: 'mp4gif_output',
|
|
18
|
+
* spawn_command: 'banner-export-mp4gif',
|
|
19
|
+
* log_waiter_begin: 'mp4gif_waiter_begin',
|
|
20
|
+
* log_waiter_end: 'mp4gif_waiter_end',
|
|
21
|
+
* log_waiter_sleep: 'mp4gif_waiter_sleep',
|
|
22
|
+
* log_waiter_rpush: 'mp4gif_waiter_rpush',
|
|
23
|
+
* log_waiter_error: 'mp4gif_waiter_error',
|
|
24
|
+
* log_waiter_error_parse: 'mp4gif_waiter_error_parse',
|
|
25
|
+
* log_waiter_error_no_uid: 'mp4gif_waiter_error_no_uid',
|
|
26
|
+
* log_worker_begin: 'mp4gif_worker_begin',
|
|
27
|
+
* log_worker_end_ok: 'mp4gif_worker_end_ok',
|
|
28
|
+
* log_worker_end_error: 'mp4gif_worker_end_error',
|
|
29
|
+
* log_worker_stdout: 'mp4gif_worker_stdout',
|
|
30
|
+
* log_worker_stderr: 'mp4gif_worker_stderr',
|
|
31
|
+
* log_worker_user_friendly_status: 'mp4gif_worker_user_friendly_status',
|
|
32
|
+
* };
|
|
33
|
+
* await redis_poll_zshift_spawn_rpush(options);
|
|
34
|
+
*/
|
|
35
|
+
async function redis_poll_zshift_spawn_rpush(options)
|
|
36
|
+
{
|
|
37
|
+
const {log, redis, redis_input_queue, redis_output_queue, version} = options;
|
|
38
|
+
const {
|
|
39
|
+
log_waiter_begin,
|
|
40
|
+
log_waiter_end,
|
|
41
|
+
log_waiter_sleep,
|
|
42
|
+
log_waiter_rpush,
|
|
43
|
+
log_waiter_error,
|
|
44
|
+
log_waiter_error_parse,
|
|
45
|
+
log_waiter_error_no_uid,
|
|
46
|
+
} = options;
|
|
47
|
+
|
|
48
|
+
log(`[${log_waiter_begin}] v${version}`);
|
|
49
|
+
try {
|
|
50
|
+
for (let iter = 1; iter <= 24*60*60; ++iter) {
|
|
51
|
+
|
|
52
|
+
// const message_string = await redis.lpop_p(redis_input_queue);
|
|
53
|
+
const [message_string] = await redis_zshift(redis, redis_input_queue);
|
|
54
|
+
if (!message_string) {
|
|
55
|
+
const ms = random_int(500, 1500);
|
|
56
|
+
log(`[${log_waiter_sleep}] ${ms}ms`);
|
|
57
|
+
await Promise.delay(ms);
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
let message = null;
|
|
62
|
+
try {
|
|
63
|
+
message = JSON.parse(message_string);
|
|
64
|
+
}
|
|
65
|
+
catch (error) {
|
|
66
|
+
log(`[${log_waiter_error_parse}] ${error.message}`);
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
try {
|
|
71
|
+
const uid = message.uid;
|
|
72
|
+
|
|
73
|
+
if (!uid) {
|
|
74
|
+
log(`[${log_waiter_error_no_uid}] uid is missed; skipped`);
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
if (!message.expires_at) {
|
|
78
|
+
throw new Error('expires_at is missed');
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// This is necessary because we use signed urls which has limited lifetime.
|
|
82
|
+
// Basically, `expires_at` tell when signed url will be expired.
|
|
83
|
+
if (new Date(message.expires_at).getTime() <= new Date().getTime()) {
|
|
84
|
+
throw new Error('Expired');
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
await worker(log.spawn(), message, options);
|
|
88
|
+
const response = JSON.stringify({uid: message.uid, version, type: 'resolve', value: null});
|
|
89
|
+
await redis.rpush_p(redis_output_queue, response);
|
|
90
|
+
|
|
91
|
+
log(`[${log_waiter_rpush}]`);
|
|
92
|
+
}
|
|
93
|
+
catch (error) {
|
|
94
|
+
const response = JSON.stringify({uid: message.uid, version, type: 'error', value: `${(error && (error.stack || error.message)) || 'Error N/A'}`});
|
|
95
|
+
log(`[${log_waiter_error}] ${response}`);
|
|
96
|
+
await redis.rpush_p(redis_output_queue, response);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
finally {
|
|
101
|
+
log(`[${log_waiter_end}]`);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
async function worker(log, message, options)
|
|
106
|
+
{
|
|
107
|
+
const {redis, redis_output_queue, spawn_command, version} = options;
|
|
108
|
+
const {
|
|
109
|
+
log_worker_begin,
|
|
110
|
+
log_worker_end_ok,
|
|
111
|
+
log_worker_end_error,
|
|
112
|
+
log_worker_stdout,
|
|
113
|
+
log_worker_stderr,
|
|
114
|
+
log_worker_user_friendly_status,
|
|
115
|
+
} = options;
|
|
116
|
+
|
|
117
|
+
log(`[${log_worker_begin}] ${message.uid}`);
|
|
118
|
+
|
|
119
|
+
try {
|
|
120
|
+
const uid = message.uid;
|
|
121
|
+
const env = {...process.env, COLUMNS: 80, PATH: `${__dirname}:${process.env.PATH}`};
|
|
122
|
+
Object.keys(message).forEach(key => env[`BN_${key.toUpperCase()}`] = message[key]);
|
|
123
|
+
// GOTCHA
|
|
124
|
+
// Promise won't update its status until at least one message would
|
|
125
|
+
// be taken from output queue. Pushing generic "Started...." message
|
|
126
|
+
// will tell the promise to refresh its status.
|
|
127
|
+
await redis.rpush_p(redis_output_queue, JSON.stringify({uid, version, type: 'user_friendly_status', value: 'Started...'}));
|
|
128
|
+
await fs_tempdir(async function (d) {
|
|
129
|
+
const proc = child_process.spawn(spawn_command, [], {cwd: d, env, stdio: ['pipe', 'pipe', 'pipe', 'pipe']});
|
|
130
|
+
let end_stdout = function () {};
|
|
131
|
+
let end_stderr = function () {};
|
|
132
|
+
let end_user_friendly_status = function () {};
|
|
133
|
+
try {
|
|
134
|
+
await new Promise(function (resolve, reject) {
|
|
135
|
+
proc.once('error', reject);
|
|
136
|
+
proc.once('exit', code => code ? reject(new Error(`Process terminated with code ${code}`)) : resolve());
|
|
137
|
+
end_stdout = stream_data_ln(proc.stdout, line => log(`[${log_worker_stdout}] ${line}`));
|
|
138
|
+
end_stderr = stream_data_ln(proc.stderr, line => log(`[${log_worker_stderr}] ${line}`));
|
|
139
|
+
end_user_friendly_status = stream_data_ln(proc.stdio[3], function (line) {
|
|
140
|
+
log(`[${log_worker_user_friendly_status}] ${line}`);
|
|
141
|
+
redis.rpush_p(redis_output_queue, JSON.stringify({uid, version, type: 'user_friendly_status', value: line}));
|
|
142
|
+
});
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
finally {
|
|
146
|
+
end_stdout();
|
|
147
|
+
end_stderr();
|
|
148
|
+
end_user_friendly_status();
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
catch (error) {
|
|
153
|
+
log(`[${log_worker_end_error}] ${error.message}`);
|
|
154
|
+
throw error;
|
|
155
|
+
}
|
|
156
|
+
log(`[${log_worker_end_ok}]`);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
module.exports = redis_poll_zshift_spawn_rpush;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
async function redis_zshift(redis, queue, limit = 1)
|
|
2
|
+
{
|
|
3
|
+
const last = limit - 1;
|
|
4
|
+
// noinspection JSUnresolvedFunction
|
|
5
|
+
const [items] = await redis.multi().zrange(queue, 0, last).zremrangebyrank(queue, 0, last).exec_p();
|
|
6
|
+
return items;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
module.exports = redis_zshift;
|
package/src/stream_data_ln.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
const assert = require('assert');
|
|
2
|
+
const fs = require('fs');
|
|
3
|
+
const stream_data_ln = require('./stream_data_ln');
|
|
4
|
+
const stream_promise = require('./stream_promise');
|
|
5
5
|
|
|
6
6
|
describe('stream_data_ln', function () {
|
|
7
7
|
it('should handle basic input', async function () {
|
package/src/stream_promise.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
const {finished} = require('stream/promises');
|
|
2
2
|
|
|
3
3
|
// /**
|
|
4
4
|
// * Wait for a stream to finish with `await stream_promise(stream)`.
|
|
@@ -59,4 +59,4 @@ function stream_promise(stream)
|
|
|
59
59
|
return finished(stream);
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
-
|
|
62
|
+
module.exports = stream_promise;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const ignore = require('./ignore');
|
|
3
|
+
const stream_promise = require('./stream_promise');
|
|
4
4
|
|
|
5
5
|
describe('stream_promise', function () {
|
|
6
6
|
it('should handle ReadableStream', async function () {
|
package/src/throttle.js
CHANGED