bajo-extra 0.3.0 → 0.3.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/bajo/config.json CHANGED
@@ -2,18 +2,5 @@
2
2
  "alias": "x",
3
3
  "fetch": {
4
4
  "agent": {}
5
- },
6
- "stream": {
7
- "export": {
8
- "maxBatch": 1000,
9
- "stringify": {
10
- "open": "[\n",
11
- "sep": ",\n",
12
- "close": "\n]\n"
13
- }
14
- },
15
- "import": {
16
- "maxBatch": 1000
17
- }
18
5
  }
19
6
  }
@@ -51,13 +51,11 @@ async function handler (rec, bulk) {
51
51
  }
52
52
 
53
53
  async function fetchAndSave ({ url, bulk, save = {}, opts = {} } = {}) {
54
- const { getConfig, importModule } = this.bajo.helper
54
+ const { startPlugin } = this.bajo.helper
55
55
  const { fetchBulk } = this.bajoExtra.helper
56
56
  const { merge } = this.bajo.helper._
57
57
  merge(bulk, { handler, save })
58
- const cfgDb = getConfig('bajoDb', { full: true })
59
- const start = await importModule(`${cfgDb.dir.pkg}/bajo/start.js`)
60
- await start.call(this, 'all')
58
+ await startPlugin('bajoDb')
61
59
 
62
60
  await fetchBulk(url, bulk, opts)
63
61
  }
package/bajoCli/tool.js CHANGED
@@ -1,8 +1 @@
1
- async function tool ({ path, args = [] }) {
2
- const { currentLoc } = this.bajo.helper
3
- const { runToolMethod } = this.bajoCli.helper
4
- const options = { demonize: ['shell'] }
5
- await runToolMethod({ path, args, dir: `${currentLoc(import.meta).dir}/tool`, options })
6
- }
7
-
8
- export default tool
1
+ export default 'defCliHandler'
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bajo-extra",
3
- "version": "0.3.0",
3
+ "version": "0.3.1",
4
4
  "description": "Extra package for Bajo Framework",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -26,21 +26,13 @@
26
26
  },
27
27
  "homepage": "https://github.com/ardhi/bajo-extra#readme",
28
28
  "dependencies": {
29
- "@atomictech/xlsx-write-stream": "^2.0.2",
30
29
  "async": "^3.2.4",
31
30
  "axios": "^1.4.0",
32
31
  "bcrypt": "^5.1.1",
33
32
  "email-addresses": "^5.0.0",
34
- "fast-csv": "^5.0.1",
35
- "fast-jwt": "^3.2.0",
36
- "fast-xml-parser": "^4.3.2",
37
- "ndjson": "^2.0.0",
33
+ "fast-xml-parser": "^4.3.6",
38
34
  "numbro": "^2.5.0",
39
35
  "performant-array-to-tree": "^1.11.0",
40
- "query-string": "^8.1.0",
41
- "scramjet": "^4.36.9",
42
- "stream-chain": "^2.2.5",
43
- "stream-json": "^1.8.0",
44
- "xlsx-parse-stream": "^1.1.0"
36
+ "query-string": "^8.1.0"
45
37
  }
46
38
  }
@@ -1,106 +0,0 @@
1
- import path from 'path'
2
- import scramjet from 'scramjet'
3
- import format from '../../lib/ndjson-csv-xlsx.js'
4
- import { createGzip } from 'zlib'
5
-
6
- const { json, ndjson, csv, xlsx } = format
7
- const { DataStream } = scramjet
8
-
9
- const supportedExt = ['.json', '.jsonl', '.ndjson', '.csv', '.xlsx', '.tsv']
10
-
11
- async function getFile (dest, ensureDir) {
12
- const { fs, importPkg, error, getPluginDataDir } = this.bajo.helper
13
- const increment = await importPkg('add-filename-increment')
14
- let file
15
- if (path.isAbsolute(dest)) file = dest
16
- else {
17
- file = `${getPluginDataDir('bajoExtra')}/export/${dest}`
18
- fs.ensureDirSync(path.dirname(file))
19
- }
20
- file = increment(file, { fs: true })
21
- const dir = path.dirname(file)
22
- if (!fs.existsSync(dir)) {
23
- if (ensureDir) fs.ensureDirSync(dir)
24
- else throw error('Directory \'%s\' doesn\'t exist', dir)
25
- }
26
- let compress = false
27
- let ext = path.extname(file)
28
- if (ext === '.gz') {
29
- compress = true
30
- ext = path.extname(path.basename(file).replace('.gz', ''))
31
- // file = file.slice(0, file.length - 3)
32
- }
33
- if (!supportedExt.includes(ext)) throw error('Unsupported format \'%s\'', ext.slice(1))
34
- return { file, ext, compress }
35
- }
36
-
37
- async function getData ({ source, filter, count, stream, progressFn }) {
38
- let cnt = count ?? 0
39
- const { recordFind } = this.bajoDb.helper
40
- for (;;) {
41
- const batchStart = new Date()
42
- const { data, page } = await recordFind(source, filter, { dataOnly: false })
43
- if (data.length === 0) break
44
- cnt += data.length
45
- await stream.pull(data)
46
- if (progressFn) await progressFn.call(this, { batchNo: page, data, batchStart, batchEnd: new Date() })
47
- filter.page++
48
- }
49
- await stream.end()
50
- return cnt
51
- }
52
-
53
- function exportTo (source, dest, { filter = {}, ensureDir, useHeader = true, batch = 500, progressFn } = {}, opts = {}) {
54
- const { fs, error, getConfig } = this.bajo.helper
55
- const cfg = getConfig('bajoExtra')
56
- if (!this.bajoDb) throw error('Bajo DB isn\'t loaded')
57
- filter.page = 1
58
- batch = parseInt(batch) ?? 500
59
- if (batch > cfg.stream.export.maxBatch) batch = cfg.stream.export.maxBatch
60
- if (batch < 0) batch = 1
61
- filter.limit = batch
62
- const { merge } = this.bajo.helper._
63
-
64
- return new Promise((resolve, reject) => {
65
- const { getInfo } = this.bajoDb.helper
66
- let count = 0
67
- let file
68
- let ext
69
- let stream
70
- let compress
71
- let writer
72
- getInfo(source)
73
- .then(res => {
74
- return getFile.call(this, dest, ensureDir)
75
- })
76
- .then(res => {
77
- file = res.file
78
- ext = res.ext
79
- compress = res.compress
80
- writer = fs.createWriteStream(file)
81
- writer.on('error', err => {
82
- reject(err)
83
- })
84
- writer.on('finish', () => {
85
- resolve({ file, count })
86
- })
87
- stream = new DataStream()
88
- stream = stream.flatMap(items => (items))
89
- const pipes = []
90
- if (ext === '.json') pipes.push(json.stringify(opts))
91
- else if (['.ndjson', '.jsonl'].includes(ext)) pipes.push(ndjson.stringify(opts))
92
- else if (ext === '.csv') pipes.push(csv.stringify(merge({}, { headers: useHeader }, opts)))
93
- else if (ext === '.tsv') pipes.push(csv.stringify(merge({}, { headers: useHeader }, merge({}, opts, { delimiter: '\t' }))))
94
- else if (ext === '.xlsx') pipes.push(xlsx.stringify(merge({}, { header: useHeader }, opts)))
95
- if (compress) pipes.push(createGzip())
96
- DataStream.pipeline(stream, ...pipes).pipe(writer)
97
- return getData.call(this, { source, filter, count, stream, progressFn })
98
- })
99
- .then(cnt => {
100
- count = cnt
101
- })
102
- .catch(reject)
103
- })
104
- }
105
-
106
- export default exportTo
@@ -1,69 +0,0 @@
1
- import path from 'path'
2
- import scramjet from 'scramjet'
3
- import format from '../../lib/ndjson-csv-xlsx.js'
4
- import { createGunzip } from 'zlib'
5
-
6
- const { json, ndjson, csv, xlsx } = format
7
- const { DataStream } = scramjet
8
- const supportedExt = ['.json', '.jsonl', '.ndjson', '.csv', '.xlsx', '.tsv']
9
-
10
- async function importFrom (source, dest, { trashOld = true, batch = 1, progressFn, converterFn, useHeader = true, fileType, createOpts = {} } = {}, opts = {}) {
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
- const cfg = getConfig('bajoExtra')
18
-
19
- let file
20
- if (path.isAbsolute(source)) file = source
21
- else {
22
- file = `${getPluginDataDir('bajoExtra')}/import/${source}`
23
- fs.ensureDirSync(path.dirname(file))
24
- }
25
- if (!fs.existsSync(file)) throw error('Source file \'%s\' doesn\'t exist', file)
26
- let ext = fileType ? `.${fileType}` : path.extname(file)
27
- let decompress = false
28
- if (ext === '.gz') {
29
- ext = path.extname(path.basename(file, '.gz'))
30
- decompress = true
31
- }
32
- if (!supportedExt.includes(ext)) throw error('Unsupported format \'%s\'', ext.slice(1))
33
- if (trashOld && dest !== false) await this.bajoDb.helper.recordClear(dest)
34
- const reader = fs.createReadStream(file)
35
- batch = parseInt(batch) || 100
36
- if (batch > cfg.stream.import.maxBatch) batch = cfg.stream.import.maxBatch
37
- if (batch < 0) batch = 1
38
- let count = 0
39
- const pipes = [reader]
40
- if (decompress) pipes.push(createGunzip())
41
- if (ext === '.json') pipes.push(json.parse(opts))
42
- else if (['.ndjson', '.jsonl'].includes(ext)) pipes.push(ndjson.parse(opts))
43
- else if (ext === '.csv') pipes.push(csv.parse(merge({}, { headers: useHeader }, opts)))
44
- else if (ext === '.tsv') pipes.push(csv.parse(merge({}, { headers: useHeader }, merge({}, opts, { delimiter: '\t' }))))
45
- else if (ext === '.xlsx') pipes.push(xlsx.parse(merge({}, { header: useHeader }, opts)))
46
-
47
- const stream = DataStream.pipeline(...pipes)
48
- let batchNo = 1
49
- const data = []
50
- await stream
51
- .batch(batch)
52
- .map(async items => {
53
- if (items.length === 0) return null
54
- const batchStart = new Date()
55
- for (let item of items) {
56
- count++
57
- item = converterFn ? await converterFn.call(this, item) : item
58
- if (dest !== false) await this.bajoDb.helper.recordCreate(dest, item, createOpts)
59
- else data.push(item)
60
- }
61
- if (progressFn) await progressFn.call(this, { batchNo, data: items, batchStart, batchEnd: new Date() })
62
- batchNo++
63
- })
64
- .run()
65
-
66
- return dest === false ? data : { file, count }
67
- }
68
-
69
- export default importFrom
@@ -1,57 +0,0 @@
1
- import Path from 'path'
2
-
3
- const batch = 100
4
-
5
- function makeProgress (spin) {
6
- return async function ({ batchNo, data, batchStart, batchEnd } = {}) {
7
- const { secToHms } = this.bajo.helper
8
- if (data.length === 0) return
9
- spin.setText('Batch #%d (%s)', batchNo, secToHms(batchEnd.toTime() - batchStart.toTime(), true))
10
- }
11
- }
12
-
13
- async function exportTo ({ path, args }) {
14
- const { importPkg, print, dayjs, getConfig, importModule, spinner } = this.bajo.helper
15
- const { isEmpty, map } = this.bajo.helper._
16
- const [input, select] = await importPkg('bajoCli:@inquirer/input',
17
- 'bajo-cli:@inquirer/select')
18
- const config = getConfig()
19
- if (!this.bajoDb) return print.fail('Bajo DB isn\'t loaded', { exit: config.tool })
20
- const schemas = map(this.bajoDb.schemas, 'name')
21
- if (isEmpty(schemas)) return print.fail('No schema found!', { exit: config.tool })
22
- let [coll, dest, query] = args
23
- if (isEmpty(coll)) {
24
- coll = await select({
25
- message: print.__('Please choose collection:'),
26
- choices: map(schemas, s => ({ value: s }))
27
- })
28
- }
29
- if (isEmpty(dest)) {
30
- dest = await input({
31
- message: print.__('Please enter destination file:'),
32
- default: `${coll}-${dayjs().format('YYYYMMDD')}.ndjson`,
33
- validate: (item) => !isEmpty(item)
34
- })
35
- }
36
- if (isEmpty(query)) {
37
- query = await input({
38
- message: print.__('Please enter a query (if any):')
39
- })
40
- }
41
- const spin = spinner().start('Exporting...')
42
- const progressFn = makeProgress.call(this, spin)
43
- const cfg = getConfig('bajoDb', { full: true })
44
- const start = await importModule(`${cfg.dir.pkg}/bajo/start.js`)
45
- const { connection } = await this.bajoDb.helper.getInfo(coll)
46
- await start.call(this, connection.name)
47
- try {
48
- const filter = { query }
49
- const result = await this.bajoExtra.helper.exportTo(coll, dest, { filter, batch, progressFn })
50
- spin.succeed('%d records successfully exported to \'%s\'', result.count, Path.resolve(result.file))
51
- } catch (err) {
52
- console.log(err)
53
- spin.fail('Error: %s', err.message, { exit: config.tool })
54
- }
55
- }
56
-
57
- export default exportTo
@@ -1,53 +0,0 @@
1
- import Path from 'path'
2
-
3
- const batch = 100
4
-
5
- function makeProgress (spin) {
6
- const { secToHms } = this.bajo.helper
7
- return async function ({ batchNo, data, batchStart, batchEnd } = {}) {
8
- spin.setText('Batch #%d (%s)', batchNo, secToHms(batchEnd.toTime() - batchStart.toTime(), true))
9
- }
10
- }
11
-
12
- async function importFrom ({ path, args }) {
13
- const { importPkg, print, importModule, getConfig, spinner } = this.bajo.helper
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
- const config = getConfig()
18
- if (!this.bajoDb) return print.fail('Bajo DB isn\'t loaded', { exit: config.tool })
19
- const schemas = map(this.bajoDb.schemas, 'name')
20
- if (isEmpty(schemas)) return print.fail('No schema found!', { exit: config.tool })
21
- let [dest, coll] = args
22
- if (isEmpty(dest)) {
23
- dest = await input({
24
- message: print.__('Please enter source file:'),
25
- validate: (item) => !isEmpty(item)
26
- })
27
- }
28
- if (isEmpty(coll)) {
29
- coll = await select({
30
- message: print.__('Please choose collection:'),
31
- choices: map(schemas, s => ({ value: s }))
32
- })
33
- }
34
- const answer = await confirm({
35
- message: print.__('You\'re about to replace ALL records with the new ones. Are you really sure?'),
36
- default: false
37
- })
38
- if (!answer) return print.fail('Aborted!', { exit: config.tool })
39
- const spin = spinner({ showCounter: true }).start('Importing...')
40
- const progressFn = makeProgress.call(this, spin)
41
- const cfg = getConfig('bajoDb', { full: true })
42
- const start = await importModule(`${cfg.dir.pkg}/bajo/start.js`)
43
- const { connection } = await this.bajoDb.helper.getInfo(coll)
44
- await start.call(this, connection.name)
45
- try {
46
- const result = await this.bajoExtra.helper.importFrom(dest, coll, { batch, progressFn })
47
- spin.succeed('%d records successfully imported from \'%s\'', result.count, Path.resolve(result.file))
48
- } catch (err) {
49
- spin.fail('Error: %s', err.message, { exit: config.tool })
50
- }
51
- }
52
-
53
- export default importFrom
@@ -1,35 +0,0 @@
1
- // Borrowed from: https://github.com/fanlia/ndjson-csv-xlsx/blob/main/index.js
2
-
3
- import ndjson from 'ndjson'
4
- import csv from 'fast-csv'
5
- import xlsxparse from 'xlsx-parse-stream'
6
- import XLSXWriteStream from '@atomictech/xlsx-write-stream'
7
- import StreamArray from 'stream-json/streamers/StreamArray.js'
8
- import stringer from 'stream-json/Stringer.js'
9
- import disassembler from 'stream-json/Disassembler.js'
10
- import chain from 'stream-chain'
11
-
12
- export default {
13
- ndjson: {
14
- parse: (...args) => ndjson.parse(...args),
15
- stringify: (...args) => ndjson.stringify(...args)
16
- },
17
- csv: {
18
- parse: (...args) => csv.parse(...args),
19
- stringify: (...args) => csv.format(...args)
20
- },
21
- xlsx: {
22
- parse: (...args) => xlsxparse(...args),
23
- stringify: (...args) => new XLSXWriteStream(...args)
24
- },
25
- json: {
26
- parse: (...args) => chain([
27
- StreamArray.withParser(...args),
28
- data => data.value
29
- ]),
30
- stringify: (options, ...args) => chain([
31
- disassembler(),
32
- stringer({ ...options, makeArray: true })
33
- ])
34
- }
35
- }