bajo-extra 0.2.16 → 0.3.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/bajo/config.json +3 -0
- package/bajo/helper/count-file-lines.js +23 -0
- package/bajo/helper/download.js +45 -0
- package/bajo/helper/export-to.js +4 -13
- package/bajo/helper/fetch-and-save.js +3 -4
- package/bajo/helper/fetch-bulk.js +4 -4
- package/bajo/helper/fetch.js +9 -2
- package/bajo/helper/format/byte.js +11 -0
- package/bajo/helper/format/float.js +9 -0
- package/bajo/helper/format/integer.js +9 -0
- package/bajo/helper/format/percentage.js +10 -0
- package/bajo/helper/gzip.js +13 -16
- package/bajo/helper/import-from.js +11 -9
- package/bajoCli/tool/download.js +17 -0
- package/bajoCli/tool/export-to.js +2 -2
- package/bajoCli/tool/import-from.js +3 -3
- package/package.json +2 -1
package/bajo/config.json
CHANGED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
// taken from: https://stackoverflow.com/a/41439945
|
|
2
|
+
import fs from 'fs'
|
|
3
|
+
|
|
4
|
+
function countFileLines (file) {
|
|
5
|
+
return new Promise((resolve, reject) => {
|
|
6
|
+
let lineCount = 0
|
|
7
|
+
fs.createReadStream(file)
|
|
8
|
+
.on('data', (buffer) => {
|
|
9
|
+
let idx = -1
|
|
10
|
+
lineCount--
|
|
11
|
+
do {
|
|
12
|
+
idx = buffer.indexOf(10, idx + 1)
|
|
13
|
+
lineCount++
|
|
14
|
+
} while (idx !== -1)
|
|
15
|
+
})
|
|
16
|
+
.on('end', () => {
|
|
17
|
+
resolve(lineCount)
|
|
18
|
+
})
|
|
19
|
+
.on('error', reject)
|
|
20
|
+
})
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export default countFileLines
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import path from 'path'
|
|
2
|
+
|
|
3
|
+
async function download (url, opts = {}) {
|
|
4
|
+
const { fs, getPluginDataDir, importPkg, error, generateId } = this.bajo.helper
|
|
5
|
+
const { fetch, formatByte, formatPercentage } = this.bajoExtra.helper
|
|
6
|
+
const { isFunction } = this.bajo.helper._
|
|
7
|
+
if (typeof opts === 'string') opts = { dir: opts }
|
|
8
|
+
const increment = await importPkg('add-filename-increment')
|
|
9
|
+
if (!opts.dir) {
|
|
10
|
+
opts.dir = `${getPluginDataDir('bajoExtra')}/download`
|
|
11
|
+
fs.ensureDirSync(opts.dir)
|
|
12
|
+
}
|
|
13
|
+
const fetchOpts = opts.fetchOpts ?? {}
|
|
14
|
+
if (!fs.existsSync(opts.dir)) throw error('Download dir \'%s\' doesn\'t exists', opts.dir)
|
|
15
|
+
if (opts.randomFileName) {
|
|
16
|
+
const ext = path.extname(url)
|
|
17
|
+
opts.fileName = `${generateId()}${ext}`
|
|
18
|
+
}
|
|
19
|
+
if (!opts.fileName) opts.fileName = path.basename(url)
|
|
20
|
+
const file = path.resolve(increment(`${opts.dir}/${opts.fileName}`, { fs: true }))
|
|
21
|
+
const writer = fs.createWriteStream(file)
|
|
22
|
+
fetchOpts.responseType = 'stream'
|
|
23
|
+
const { headers, data } = await fetch(url, fetchOpts, { rawResponse: true })
|
|
24
|
+
const total = headers['content-length'] ?? 0
|
|
25
|
+
let length = 0
|
|
26
|
+
data.on('data', chunk => {
|
|
27
|
+
length += chunk.length
|
|
28
|
+
if (isFunction(opts.progressFn)) opts.progressFn.call(this, length, total)
|
|
29
|
+
else if (opts.spin) {
|
|
30
|
+
opts.spinText = opts.spinText ?? 'Downloading...'
|
|
31
|
+
if (total === 0) opts.spin.setText(`${opts.spinText} %s`, formatByte(length))
|
|
32
|
+
else opts.spin.setText(`${opts.spinText} %s of %s (%s)`, formatByte(length), formatByte(total), formatPercentage(length / total))
|
|
33
|
+
}
|
|
34
|
+
})
|
|
35
|
+
data.pipe(writer)
|
|
36
|
+
|
|
37
|
+
return new Promise((resolve, reject) => {
|
|
38
|
+
writer.on('error', reject)
|
|
39
|
+
writer.on('finish', () => {
|
|
40
|
+
resolve(file)
|
|
41
|
+
})
|
|
42
|
+
})
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export default download
|
package/bajo/helper/export-to.js
CHANGED
|
@@ -9,8 +9,8 @@ const { DataStream } = scramjet
|
|
|
9
9
|
const supportedExt = ['.json', '.jsonl', '.ndjson', '.csv', '.xlsx', '.tsv']
|
|
10
10
|
|
|
11
11
|
async function getFile (dest, ensureDir) {
|
|
12
|
-
const { importPkg, error, getPluginDataDir } = this.bajo.helper
|
|
13
|
-
const
|
|
12
|
+
const { fs, importPkg, error, getPluginDataDir } = this.bajo.helper
|
|
13
|
+
const increment = await importPkg('add-filename-increment')
|
|
14
14
|
let file
|
|
15
15
|
if (path.isAbsolute(dest)) file = dest
|
|
16
16
|
else {
|
|
@@ -51,7 +51,7 @@ async function getData ({ source, filter, count, stream, progressFn }) {
|
|
|
51
51
|
}
|
|
52
52
|
|
|
53
53
|
function exportTo (source, dest, { filter = {}, ensureDir, useHeader = true, batch = 500, progressFn } = {}, opts = {}) {
|
|
54
|
-
const {
|
|
54
|
+
const { fs, error, getConfig } = this.bajo.helper
|
|
55
55
|
const cfg = getConfig('bajoExtra')
|
|
56
56
|
if (!this.bajoDb) throw error('Bajo DB isn\'t loaded')
|
|
57
57
|
filter.page = 1
|
|
@@ -59,27 +59,18 @@ function exportTo (source, dest, { filter = {}, ensureDir, useHeader = true, bat
|
|
|
59
59
|
if (batch > cfg.stream.export.maxBatch) batch = cfg.stream.export.maxBatch
|
|
60
60
|
if (batch < 0) batch = 1
|
|
61
61
|
filter.limit = batch
|
|
62
|
+
const { merge } = this.bajo.helper._
|
|
62
63
|
|
|
63
64
|
return new Promise((resolve, reject) => {
|
|
64
65
|
const { getInfo } = this.bajoDb.helper
|
|
65
66
|
let count = 0
|
|
66
|
-
let fs
|
|
67
|
-
let merge
|
|
68
67
|
let file
|
|
69
68
|
let ext
|
|
70
69
|
let stream
|
|
71
70
|
let compress
|
|
72
71
|
let writer
|
|
73
72
|
getInfo(source)
|
|
74
|
-
.then(() => {
|
|
75
|
-
return importPkg('lodash-es')
|
|
76
|
-
})
|
|
77
|
-
.then(l => {
|
|
78
|
-
merge = l.merge
|
|
79
|
-
return importPkg('fs-extra')
|
|
80
|
-
})
|
|
81
73
|
.then(res => {
|
|
82
|
-
fs = res
|
|
83
74
|
return getFile.call(this, dest, ensureDir)
|
|
84
75
|
})
|
|
85
76
|
.then(res => {
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
async function handler (rec, bulk) {
|
|
2
|
-
const {
|
|
3
|
-
const { isFunction, set } = await importPkg('lodash-es')
|
|
2
|
+
const { isFunction, set } = this.bajo.helper._
|
|
4
3
|
const { recordCreate, recordFind, recordUpdate } = this.bajoDb.helper
|
|
5
4
|
const save = bulk.save ?? {}
|
|
6
5
|
const current = save.current ?? {}
|
|
@@ -52,9 +51,9 @@ async function handler (rec, bulk) {
|
|
|
52
51
|
}
|
|
53
52
|
|
|
54
53
|
async function fetchAndSave ({ url, bulk, save = {}, opts = {} } = {}) {
|
|
55
|
-
const {
|
|
54
|
+
const { getConfig, importModule } = this.bajo.helper
|
|
56
55
|
const { fetchBulk } = this.bajoExtra.helper
|
|
57
|
-
const { merge } =
|
|
56
|
+
const { merge } = this.bajo.helper._
|
|
58
57
|
merge(bulk, { handler, save })
|
|
59
58
|
const cfgDb = getConfig('bajoDb', { full: true })
|
|
60
59
|
const start = await importModule(`${cfgDb.dir.pkg}/bajo/start.js`)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
async function fetching ({ url, opts, bulk, spin }) {
|
|
2
|
-
const { setImmediate,
|
|
3
|
-
const { isEmpty, isFunction, has } =
|
|
2
|
+
const { setImmediate, print } = this.bajo.helper
|
|
3
|
+
const { isEmpty, isFunction, has } = this.bajo.helper._
|
|
4
4
|
const { validationErrorMessage } = this.bajoDb.helper
|
|
5
5
|
const { fetch } = this.bajoExtra.helper
|
|
6
6
|
const resp = await fetch(url, opts ?? {})
|
|
@@ -49,8 +49,8 @@ async function fetching ({ url, opts, bulk, spin }) {
|
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
async function fetchBulk (url, bulk = {}, opts = {}) {
|
|
52
|
-
const { print, spinner,
|
|
53
|
-
const { isFunction } =
|
|
52
|
+
const { print, spinner, error } = this.bajo.helper
|
|
53
|
+
const { isFunction } = this.bajo.helper._
|
|
54
54
|
opts.params = opts.params ?? {}
|
|
55
55
|
bulk.maxStep = bulk.maxStep ?? 0
|
|
56
56
|
if (!isFunction(bulk.handler)) throw error('A function handler must be provided')
|
package/bajo/helper/fetch.js
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import axios from 'axios'
|
|
2
|
+
import http from 'http'
|
|
3
|
+
import https from 'https'
|
|
2
4
|
|
|
3
5
|
async function fetch (url, opts = {}, ext = {}) {
|
|
4
|
-
const {
|
|
5
|
-
const { has, isPlainObject, cloneDeep } =
|
|
6
|
+
const { getConfig } = this.bajo.helper
|
|
7
|
+
const { has, isPlainObject, cloneDeep, isEmpty } = this.bajo.helper._
|
|
8
|
+
const cfg = getConfig('bajoExtra')
|
|
6
9
|
if (isPlainObject(url)) {
|
|
7
10
|
ext = cloneDeep(opts)
|
|
8
11
|
opts = cloneDeep(url)
|
|
@@ -10,6 +13,10 @@ async function fetch (url, opts = {}, ext = {}) {
|
|
|
10
13
|
opts.params = opts.params ?? {}
|
|
11
14
|
if (!has(ext, 'cacheBuster')) ext.cacheBuster = true
|
|
12
15
|
if (ext.cacheBuster) opts.params[ext.cacheBusterKey ?? '_'] = Date.now()
|
|
16
|
+
if (!isEmpty(cfg.fetch.agent)) {
|
|
17
|
+
opts.httpAgent = opts.httpAgent ?? new http.Agent(cfg.fetch.agent)
|
|
18
|
+
opts.httpsAgent = opts.httpsAgent ?? new https.Agent(cfg.fetch.agent)
|
|
19
|
+
}
|
|
13
20
|
const resp = await axios(opts)
|
|
14
21
|
if (ext.rawResponse) return resp
|
|
15
22
|
return resp.data
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import numbro from 'numbro'
|
|
2
|
+
|
|
3
|
+
function byte (value, opts = {}) {
|
|
4
|
+
opts.output = 'byte'
|
|
5
|
+
opts.base = 'binary'
|
|
6
|
+
opts.mantissa = opts.mantissa ?? opts.scale ?? 2
|
|
7
|
+
opts.spaceSeparated = opts.spaceSeparated ?? true
|
|
8
|
+
return numbro(value).format(opts)
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export default byte
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import numbro from 'numbro'
|
|
2
|
+
|
|
3
|
+
function percentage (value, opts = {}) {
|
|
4
|
+
opts.output = 'percent'
|
|
5
|
+
opts.mantissa = opts.mantissa ?? opts.scale ?? 2
|
|
6
|
+
opts.spaceSeparated = opts.spaceSeparated ?? true
|
|
7
|
+
return numbro(value).format(opts)
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export default percentage
|
package/bajo/helper/gzip.js
CHANGED
|
@@ -1,22 +1,19 @@
|
|
|
1
1
|
import { createGzip, createGunzip } from 'zlib'
|
|
2
2
|
|
|
3
|
-
function gzip (file, deleteOld,
|
|
3
|
+
function gzip (file, deleteOld, expand) {
|
|
4
|
+
const { fs } = this.bajo.helper
|
|
4
5
|
return new Promise((resolve, reject) => {
|
|
5
|
-
const
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
if (deleteOld) fs.unlinkSync(file)
|
|
17
|
-
resolve()
|
|
18
|
-
})
|
|
19
|
-
})
|
|
6
|
+
const newFile = expand ? file.slice(0, file.length - 3) : (file + '.gz')
|
|
7
|
+
const reader = fs.createReadStream(file)
|
|
8
|
+
const writer = fs.createWriteStream(newFile)
|
|
9
|
+
const method = expand ? createGunzip() : createGzip()
|
|
10
|
+
reader.pipe(method).pipe(writer)
|
|
11
|
+
writer.on('error', reject)
|
|
12
|
+
writer.on('finish', err => {
|
|
13
|
+
if (err) return reject(err)
|
|
14
|
+
if (deleteOld) fs.unlinkSync(file)
|
|
15
|
+
resolve()
|
|
16
|
+
})
|
|
20
17
|
})
|
|
21
18
|
}
|
|
22
19
|
|
|
@@ -8,12 +8,12 @@ const { DataStream } = scramjet
|
|
|
8
8
|
const supportedExt = ['.json', '.jsonl', '.ndjson', '.csv', '.xlsx', '.tsv']
|
|
9
9
|
|
|
10
10
|
async function importFrom (source, dest, { trashOld = true, batch = 1, progressFn, converterFn, useHeader = true, fileType, createOpts = {} } = {}, opts = {}) {
|
|
11
|
-
const {
|
|
12
|
-
if (
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
const
|
|
11
|
+
const { fs, error, getConfig, getPluginDataDir } = this.bajo.helper
|
|
12
|
+
if (dest !== false) {
|
|
13
|
+
if (!this.bajoDb) throw error('Bajo DB isn\'t loaded')
|
|
14
|
+
await this.bajoDb.helper.getInfo(dest)
|
|
15
|
+
}
|
|
16
|
+
const { merge } = this.bajo.helper._
|
|
17
17
|
const cfg = getConfig('bajoExtra')
|
|
18
18
|
|
|
19
19
|
let file
|
|
@@ -30,7 +30,7 @@ async function importFrom (source, dest, { trashOld = true, batch = 1, progressF
|
|
|
30
30
|
decompress = true
|
|
31
31
|
}
|
|
32
32
|
if (!supportedExt.includes(ext)) throw error('Unsupported format \'%s\'', ext.slice(1))
|
|
33
|
-
if (trashOld) await recordClear(dest)
|
|
33
|
+
if (trashOld && dest !== false) await this.bajoDb.helper.recordClear(dest)
|
|
34
34
|
const reader = fs.createReadStream(file)
|
|
35
35
|
batch = parseInt(batch) || 100
|
|
36
36
|
if (batch > cfg.stream.import.maxBatch) batch = cfg.stream.import.maxBatch
|
|
@@ -46,6 +46,7 @@ async function importFrom (source, dest, { trashOld = true, batch = 1, progressF
|
|
|
46
46
|
|
|
47
47
|
const stream = DataStream.pipeline(...pipes)
|
|
48
48
|
let batchNo = 1
|
|
49
|
+
const data = []
|
|
49
50
|
await stream
|
|
50
51
|
.batch(batch)
|
|
51
52
|
.map(async items => {
|
|
@@ -54,14 +55,15 @@ async function importFrom (source, dest, { trashOld = true, batch = 1, progressF
|
|
|
54
55
|
for (let item of items) {
|
|
55
56
|
count++
|
|
56
57
|
item = converterFn ? await converterFn.call(this, item) : item
|
|
57
|
-
await recordCreate(dest, item, createOpts)
|
|
58
|
+
if (dest !== false) await this.bajoDb.helper.recordCreate(dest, item, createOpts)
|
|
59
|
+
else data.push(item)
|
|
58
60
|
}
|
|
59
61
|
if (progressFn) await progressFn.call(this, { batchNo, data: items, batchStart, batchEnd: new Date() })
|
|
60
62
|
batchNo++
|
|
61
63
|
})
|
|
62
64
|
.run()
|
|
63
65
|
|
|
64
|
-
return { file, count }
|
|
66
|
+
return dest === false ? data : { file, count }
|
|
65
67
|
}
|
|
66
68
|
|
|
67
69
|
export default importFrom
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
async function download ({ path, args }) {
|
|
2
|
+
const { spinner } = this.bajo.helper
|
|
3
|
+
const { download } = this.bajoExtra.helper
|
|
4
|
+
const url = args[0]
|
|
5
|
+
const spinText = 'Downloading file...'
|
|
6
|
+
const spin = spinner({ showCounter: true }).start(spinText)
|
|
7
|
+
|
|
8
|
+
let dest
|
|
9
|
+
try {
|
|
10
|
+
dest = await download(url, { spin, spinText })
|
|
11
|
+
} catch (err) {
|
|
12
|
+
spin.fatal('Error: %s', err.message)
|
|
13
|
+
}
|
|
14
|
+
spin.succeed('File saved as \'%s\'', dest)
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export default download
|
|
@@ -12,8 +12,8 @@ function makeProgress (spin) {
|
|
|
12
12
|
|
|
13
13
|
async function exportTo ({ path, args }) {
|
|
14
14
|
const { importPkg, print, dayjs, getConfig, importModule, spinner } = this.bajo.helper
|
|
15
|
-
const { isEmpty, map } =
|
|
16
|
-
const [input, select] = await importPkg('
|
|
15
|
+
const { isEmpty, map } = this.bajo.helper._
|
|
16
|
+
const [input, select] = await importPkg('bajoCli:@inquirer/input',
|
|
17
17
|
'bajo-cli:@inquirer/select')
|
|
18
18
|
const config = getConfig()
|
|
19
19
|
if (!this.bajoDb) return print.fail('Bajo DB isn\'t loaded', { exit: config.tool })
|
|
@@ -11,9 +11,9 @@ function makeProgress (spin) {
|
|
|
11
11
|
|
|
12
12
|
async function importFrom ({ path, args }) {
|
|
13
13
|
const { importPkg, print, importModule, getConfig, spinner } = this.bajo.helper
|
|
14
|
-
const { isEmpty, map } =
|
|
15
|
-
const [input, select, confirm] = await importPkg('
|
|
16
|
-
'
|
|
14
|
+
const { isEmpty, map } = this.bajo.helper._
|
|
15
|
+
const [input, select, confirm] = await importPkg('bajoCli:@inquirer/input',
|
|
16
|
+
'bajoCli:@inquirer/select', 'bajoCli:@inquirer/confirm')
|
|
17
17
|
const config = getConfig()
|
|
18
18
|
if (!this.bajoDb) return print.fail('Bajo DB isn\'t loaded', { exit: config.tool })
|
|
19
19
|
const schemas = map(this.bajoDb.schemas, 'name')
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bajo-extra",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Extra package for Bajo Framework",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -35,6 +35,7 @@
|
|
|
35
35
|
"fast-jwt": "^3.2.0",
|
|
36
36
|
"fast-xml-parser": "^4.3.2",
|
|
37
37
|
"ndjson": "^2.0.0",
|
|
38
|
+
"numbro": "^2.5.0",
|
|
38
39
|
"performant-array-to-tree": "^1.11.0",
|
|
39
40
|
"query-string": "^8.1.0",
|
|
40
41
|
"scramjet": "^4.36.9",
|