@miso.ai/server-commons 0.5.0-beta.1
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/package.json +17 -0
- package/src/date.js +96 -0
- package/src/functions.js +3 -0
- package/src/index.js +5 -0
- package/src/object.js +25 -0
- package/src/stream.js +43 -0
- package/src/task-queue.js +60 -0
package/package.json
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@miso.ai/server-commons",
|
|
3
|
+
"description": "Miso Utility Functions",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"main": "src/index.js",
|
|
6
|
+
"publishConfig": {
|
|
7
|
+
"access": "public"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {},
|
|
10
|
+
"repository": "MisoAI/miso-server-js-sdk",
|
|
11
|
+
"license": "MIT",
|
|
12
|
+
"contributors": [
|
|
13
|
+
"simonpai <simon.pai@askmiso.com>"
|
|
14
|
+
],
|
|
15
|
+
"dependencies": {},
|
|
16
|
+
"version": "0.5.0-beta.1"
|
|
17
|
+
}
|
package/src/date.js
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
const DURATION_EXPR = /^(\d+)([s|m|h|d])$/;
|
|
2
|
+
const TS_PER_UNIT = {
|
|
3
|
+
ms: 1,
|
|
4
|
+
s: 1000,
|
|
5
|
+
};
|
|
6
|
+
TS_PER_UNIT.m = TS_PER_UNIT.s * 60;
|
|
7
|
+
TS_PER_UNIT.h = TS_PER_UNIT.m * 60;
|
|
8
|
+
TS_PER_UNIT.d = TS_PER_UNIT.h * 24;
|
|
9
|
+
//TS_PER_UNIT.M = TS_PER_UNIT.d * 31;
|
|
10
|
+
//TS_PER_UNIT.y = TS_PER_UNIT.d * 366;
|
|
11
|
+
|
|
12
|
+
export function parseDuration(expr, unit) {
|
|
13
|
+
if (expr === undefined || typeof expr === 'number') {
|
|
14
|
+
return expr;
|
|
15
|
+
}
|
|
16
|
+
let value = expr;
|
|
17
|
+
if (typeof unit !== 'string') {
|
|
18
|
+
const m = expr.match(DURATION_EXPR);
|
|
19
|
+
if (!m) {
|
|
20
|
+
throw new Error(`Unrecognized expression: ${expr}`);
|
|
21
|
+
}
|
|
22
|
+
[value, unit] = m.slice(1);
|
|
23
|
+
}
|
|
24
|
+
const ts = TS_PER_UNIT[unit.toLowerCase()];
|
|
25
|
+
if (!ts) {
|
|
26
|
+
throw new Error(`Unrecognized time unit: ${expr}`);
|
|
27
|
+
}
|
|
28
|
+
return value * ts;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function startOfDate(expr) {
|
|
32
|
+
if (expr === undefined) {
|
|
33
|
+
return undefined;
|
|
34
|
+
}
|
|
35
|
+
if (typeof expr === 'number') {
|
|
36
|
+
if (expr < 10000) {
|
|
37
|
+
expr = startOfYearByYearNum(expr);
|
|
38
|
+
}
|
|
39
|
+
return expr;
|
|
40
|
+
}
|
|
41
|
+
const ts = Date.parse(expr);
|
|
42
|
+
if (isNaN(ts)) {
|
|
43
|
+
throw new Error(`Unrecognized date: ${expr}`);
|
|
44
|
+
}
|
|
45
|
+
return ts;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const RE_YEAR = /^\d{4}$/g;
|
|
49
|
+
const RE_MONTH = /^\d{4}-\d{2}$/g;
|
|
50
|
+
const RE_DATE = /^\d{4}-\d{2}-\d{2}$/g;
|
|
51
|
+
|
|
52
|
+
// TODO: support end of hour, minute
|
|
53
|
+
|
|
54
|
+
export function endOfDate(expr) {
|
|
55
|
+
if (expr === undefined) {
|
|
56
|
+
return undefined;
|
|
57
|
+
}
|
|
58
|
+
if (typeof expr === 'number') {
|
|
59
|
+
if (expr < 10000) {
|
|
60
|
+
expr = endOfYearByYearNum(expr);
|
|
61
|
+
}
|
|
62
|
+
return expr;
|
|
63
|
+
}
|
|
64
|
+
let ts = Date.parse(expr);
|
|
65
|
+
if (isNaN(ts)) {
|
|
66
|
+
throw new Error(`Unrecognized date: ${expr}`);
|
|
67
|
+
}
|
|
68
|
+
return expr.match(RE_YEAR) ? endOfYear(ts) :
|
|
69
|
+
expr.match(RE_MONTH) ? endOfMonth(ts) :
|
|
70
|
+
expr.match(RE_DATE) ? endOfDay(ts) : ts;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function endOfYear(ts) {
|
|
74
|
+
return endOfYearByYearNum(new Date(ts).getFullYear());
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function startOfYearByYearNum(fullYear) {
|
|
78
|
+
return new Date(fullYear, 0, 1).getTime();
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function endOfYearByYearNum(fullYear) {
|
|
82
|
+
return new Date(fullYear + 1, 0, 1).getTime() - 1000;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function endOfMonth(ts) {
|
|
86
|
+
const date = new Date(ts);
|
|
87
|
+
const year = date.getFullYear();
|
|
88
|
+
const month = date.getMonth();
|
|
89
|
+
return (month == 11 ? new Date(year + 1, 0, 1) : new Date(year, month + 1, 1)).getTime() - 1000;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const MS_PER_DAY = 1000 * 60 * 60 * 24;
|
|
93
|
+
|
|
94
|
+
function endOfDay(ts) {
|
|
95
|
+
return (Math.floor(ts / MS_PER_DAY) + 1) * MS_PER_DAY - 1000;
|
|
96
|
+
}
|
package/src/functions.js
ADDED
package/src/index.js
ADDED
package/src/object.js
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Remove object properties with undefined values and return the object itself.
|
|
3
|
+
*/
|
|
4
|
+
export function trimObj(obj) {
|
|
5
|
+
if (typeof obj !== 'object') {
|
|
6
|
+
return obj;
|
|
7
|
+
}
|
|
8
|
+
for (const k in obj) {
|
|
9
|
+
if (Object.prototype.hasOwnProperty.call(obj, k) && obj[k] === undefined) {
|
|
10
|
+
delete obj[k];
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
return obj;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function asArray(value) {
|
|
17
|
+
return Array.isArray(value) ? value : value === undefined ? [] : [value];
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function asMap(objects, {key = 'id', target = {}} = {}) {
|
|
21
|
+
return objects.reduce((acc, c) => {
|
|
22
|
+
acc[c[key]] = c;
|
|
23
|
+
return acc;
|
|
24
|
+
}, target);
|
|
25
|
+
}
|
package/src/stream.js
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { Transform, pipeline as _pipeline } from 'stream';
|
|
2
|
+
|
|
3
|
+
export function stringify() {
|
|
4
|
+
return new Transform({
|
|
5
|
+
transform(chunk, _, callback) {
|
|
6
|
+
callback(null, JSON.stringify(chunk) + '\n');
|
|
7
|
+
},
|
|
8
|
+
writableObjectMode: true,
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export async function pipelineToStdout(...streams) {
|
|
13
|
+
return new Promise((resolve, reject) =>
|
|
14
|
+
_pipeline(...streams, err => err ? reject(err) : resolve())
|
|
15
|
+
.pipe(process.stdout, { end: false })
|
|
16
|
+
);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export async function collectStream(stream) {
|
|
20
|
+
const records = [];
|
|
21
|
+
for await (const record of stream) {
|
|
22
|
+
records.push(record);
|
|
23
|
+
}
|
|
24
|
+
return records;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// from https://github.com/maxogden/concat-stream/issues/66
|
|
28
|
+
export async function * concatStreams(...streams) {
|
|
29
|
+
for (const stream of streams) yield * stream
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function transform(fn, { transform: _, ...options } = {}) {
|
|
33
|
+
return new Transform({
|
|
34
|
+
...options,
|
|
35
|
+
transform(chunk, encoding, next) {
|
|
36
|
+
try {
|
|
37
|
+
next(undefined, fn(chunk, encoding));
|
|
38
|
+
} catch (error) {
|
|
39
|
+
next(error);
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
});
|
|
43
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
export default class TaskQueue {
|
|
2
|
+
|
|
3
|
+
constructor() {
|
|
4
|
+
this._index = 0;
|
|
5
|
+
this._finished = -1;
|
|
6
|
+
this._promises = {};
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
request() {
|
|
10
|
+
return this._index++;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
async push(index, fn) {
|
|
14
|
+
if (typeof index === 'function') {
|
|
15
|
+
fn = index;
|
|
16
|
+
index = this.request();
|
|
17
|
+
}
|
|
18
|
+
if (index > this._finished) {
|
|
19
|
+
await this._waitFor(index - 1);
|
|
20
|
+
}
|
|
21
|
+
try {
|
|
22
|
+
const value = await fn();
|
|
23
|
+
this._resolve(index, value);
|
|
24
|
+
} catch (error) {
|
|
25
|
+
this._reject(index, error);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
_resolve(index, value) {
|
|
30
|
+
const promises = this._promises;
|
|
31
|
+
if (promises[index]) {
|
|
32
|
+
promises[index].resolve(value);
|
|
33
|
+
delete promises[index];
|
|
34
|
+
}
|
|
35
|
+
this._finished = index;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
_reject(index, error) {
|
|
39
|
+
const promises = this._promises;
|
|
40
|
+
if (promises[index]) {
|
|
41
|
+
promises[index].reject(error);
|
|
42
|
+
delete promises[index];
|
|
43
|
+
}
|
|
44
|
+
this._finished = index;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
_waitFor(index) {
|
|
48
|
+
if (this._finished >= index) {
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
const promises = this._promises;
|
|
52
|
+
if (promises[index]) {
|
|
53
|
+
throw new Error();
|
|
54
|
+
}
|
|
55
|
+
return new Promise((resolve, reject) => {
|
|
56
|
+
promises[index] = { resolve, reject };
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
}
|