xcraft-core-utils 3.1.1 → 4.1.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/index.js CHANGED
@@ -16,10 +16,9 @@ exports.propTypes = require('./lib/prop-types.js');
16
16
  exports.reflect = require('./lib/reflect.js');
17
17
  exports.regex = require('./lib/regex.js');
18
18
  exports.string = require('./lib/string.js');
19
+ exports.whereIs = require('./lib/whereIs.js');
19
20
  exports.yaml = require('./lib/yaml.js');
20
21
 
21
- exports.SQLite = require('./lib/sqlite.js');
22
22
  exports.RankedCache = require('./lib/ranked-cache.js');
23
23
  exports.JobQueue = require('./lib/job-queue.js');
24
- exports.PersistantJobQueue = require('./lib/persistant-job-queue.js');
25
24
  exports.CursorPump = require('./lib/cursorPump.js');
package/lib/.babelrc ADDED
@@ -0,0 +1,8 @@
1
+ {
2
+ "plugins": [
3
+ "@babel/proposal-class-properties",
4
+ "@babel/proposal-object-rest-spread",
5
+ "@babel/proposal-function-bind",
6
+ "@babel/transform-modules-commonjs"
7
+ ]
8
+ }
@@ -1,12 +1,13 @@
1
+ 'use strict';
2
+
1
3
  const {throttle} = require('lodash');
2
4
  const watt = require('gigawatts');
3
5
 
4
6
  class ArrayCollector {
5
- constructor(wait = 20, onCollect, leading = true) {
7
+ constructor(resp, wait = 20, onCollect, leading = true) {
6
8
  this.onCollect = watt(onCollect);
7
9
  this.entries = {};
8
- const busClient = require('xcraft-core-busclient').getGlobal();
9
- this.resp = busClient.newResponse('collector', 'token');
10
+ this.resp = resp;
10
11
  this.release = throttle(this._release, wait, {leading});
11
12
  }
12
13
 
package/lib/async.js ADDED
@@ -0,0 +1,34 @@
1
+ 'use strict';
2
+
3
+ const watt = require('gigawatts');
4
+
5
+ class Async {
6
+ constructor() {
7
+ watt.wrapAll(this);
8
+ }
9
+
10
+ /**
11
+ * Reduce an array to a map with an async iteration.
12
+ *
13
+ * It's useful in the case where the main event loop must not
14
+ * be blocked and the array is very large. The purpose of this
15
+ * helper is only for task which be done with an usual sync
16
+ * map reduce.
17
+ *
18
+ * @param {*} keyFunc - Key for the item in the map.
19
+ * @param {*} valueFunc - Value for the item in the map.
20
+ * @param {*} list - The input array.
21
+ * @returns {Object} the map.
22
+ */
23
+ *mapReduce(keyFunc, valueFunc, list) {
24
+ const state = {};
25
+ for (const item of list) {
26
+ state[keyFunc(item)] = yield new Promise((resolve) =>
27
+ resolve(valueFunc(item))
28
+ );
29
+ }
30
+ return state;
31
+ }
32
+ }
33
+
34
+ module.exports = new Async();
package/lib/batch.js CHANGED
@@ -9,7 +9,7 @@ exports.run = function (filter, location, callbackAction) {
9
9
 
10
10
  files.forEach(function (file) {
11
11
  var fullPath = path.join(location, file);
12
- var st = fs.statSync(fullPath);
12
+ var st = fs.lstatSync(fullPath);
13
13
 
14
14
  if (st.isDirectory()) {
15
15
  exports.run(filter, fullPath, callbackAction);
package/lib/crypto.js CHANGED
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  var crypto = require('crypto');
4
- const uuidV4 = require('uuid/v4');
4
+ const {v4: uuidV4} = require('uuid');
5
5
 
6
6
  exports.md5 = function (data) {
7
7
  var md5 = crypto.createHash('md5');
package/lib/cursorPump.js CHANGED
@@ -1,3 +1,5 @@
1
+ 'use strict';
2
+
1
3
  const watt = require('gigawatts');
2
4
 
3
5
  class CursorPump {
@@ -1,19 +1,20 @@
1
+ 'use strict';
2
+
1
3
  const {debounce} = require('lodash');
2
4
 
3
5
  class EventDebouncer {
4
- constructor(wait = 1000) {
6
+ constructor(resp, wait = 1000) {
5
7
  this.debouncers = {};
6
8
  this.wait = wait;
7
- const busClient = require('xcraft-core-busclient').getGlobal();
8
- this.resp = busClient.newResponse('event-debouncer', 'token');
9
+ this.resp = resp;
9
10
  }
10
11
 
11
- publish(topic) {
12
+ publish(topic, data) {
12
13
  if (!this.debouncers[topic]) {
13
- const send = (topic) => this.resp.events.send(topic);
14
+ const send = (topic, data) => this.resp.events.send(topic, data);
14
15
  this.debouncers[topic] = debounce(send, this.wait);
15
16
  }
16
- this.debouncers[topic](topic);
17
+ this.debouncers[topic](topic, data);
17
18
  }
18
19
  }
19
20
 
package/lib/files.js CHANGED
@@ -1,4 +1,5 @@
1
1
  'use strict';
2
+
2
3
  const path = require('path');
3
4
 
4
5
  const exts = {
package/lib/job-queue.js CHANGED
@@ -1,35 +1,64 @@
1
1
  'use strict';
2
- var clc = require('cli-color');
3
2
 
4
3
  const moduleName = 'job-queue';
4
+ const clc = require('cli-color');
5
5
  const watt = require('gigawatts');
6
6
  const {locks} = require('xcraft-core-utils');
7
7
  const runner = require('./runnerInstance.js');
8
8
  const throttle = require('lodash/throttle');
9
+ const defaultOptions = {
10
+ priorityGroup: 'default',
11
+ waitOn: [], //groups to wait
12
+ waitDelay: 50,
13
+ maxAttempt: 100,
14
+ useLogger: false,
15
+ };
9
16
  class JobQueue {
10
- constructor(name, runner, parallelLimit, useLogger = true) {
11
- this.log = useLogger && require('xcraft-core-log')(`${moduleName}`, null);
17
+ constructor(name, runner, parallelLimit, options) {
18
+ //override default options
19
+ if (!options) {
20
+ options = defaultOptions;
21
+ } else {
22
+ options = {...defaultOptions, ...options};
23
+ }
24
+
25
+ this.log =
26
+ options.useLogger && require('xcraft-core-log')(`${moduleName}`, null);
12
27
  this.name = name;
28
+ this.priorityGroup = options.priorityGroup;
29
+ this.waitOn = options.waitOn;
30
+ this.maxAttempt = options.maxAttempt;
31
+ this.attempt = 0;
32
+ this.waitDelay = options.waitDelay;
13
33
  this.channel = name.toLowerCase().replace(/\.|\[|\//g, '-');
14
- this.runner = watt(runner);
34
+ this.runner = watt(function* (...args) {
35
+ this.running++;
36
+ this.runningSamples++;
37
+ yield* runner(...args);
38
+ });
15
39
  this.parallelLimit = parallelLimit;
16
40
  this.waiting = new Map();
17
41
  this.running = 0;
42
+ this.runningSamples = 0;
18
43
  this._mutex = new locks.Mutex();
19
44
  const busClient = require('xcraft-core-busclient').getGlobal();
20
45
  this.resp = busClient.newResponse('job-queue', 'token');
21
46
  watt.wrapAll(this);
22
- this.notify = throttle(this._notify, 500);
47
+ this.notify = throttle(this._notify, 500).bind(this);
23
48
  }
24
49
 
25
- _notify() {
26
- const n = this.waiting.size;
50
+ get maxAttemptReached() {
51
+ return this.attempt >= this.maxAttempt;
52
+ }
53
+
54
+ _notify(runningSamples) {
27
55
  this.resp.events.send('<job-queue.sampled>', {
28
56
  channel: this.channel,
29
- sample: n,
57
+ sample: runningSamples,
30
58
  current: 0,
31
59
  total: 0,
32
60
  });
61
+ this.runningSamples = 0;
33
62
  }
34
63
 
35
64
  _log() {
@@ -37,11 +66,10 @@ class JobQueue {
37
66
  return;
38
67
  }
39
68
 
40
- this.log.info(
41
- `«${clc.blackBright.bold(this.name)}» waiting:${
42
- this.waiting.size
43
- } running:${this.running}`
44
- );
69
+ const msg = `«${clc.blackBright.bold(this.name)}» waiting:${
70
+ this.waiting.size
71
+ } running:${this.running}`;
72
+ this.log.info(msg);
45
73
  }
46
74
 
47
75
  _dbg(debugMessage) {
@@ -49,19 +77,34 @@ class JobQueue {
49
77
  return;
50
78
  }
51
79
 
52
- this.log.dbg(`«${clc.blackBright.bold(this.name)}» ${debugMessage}`);
80
+ const msg = `«${clc.blackBright.bold(this.name)}» ${debugMessage}`;
81
+ this.log.dbg(msg);
82
+ }
83
+
84
+ err(ex) {
85
+ const msg = `«${clc.blackBright.bold(this.name)}» ${
86
+ ex.stack || ex.message || ex
87
+ }`;
88
+
89
+ if (!this.log) {
90
+ console.error(msg);
91
+ return;
92
+ }
93
+
94
+ this.log.err(msg);
53
95
  }
54
96
 
55
97
  push(job) {
56
98
  try {
57
99
  this.waiting.set(job.id, job);
58
100
  } finally {
59
- setImmediate(this.notify.bind(this));
60
- setImmediate(runner.instance.run, this);
101
+ //launch macro-task
102
+ setTimeout(runner.instance.run, 0, this);
61
103
  }
62
104
  }
63
105
 
64
106
  dispose() {
107
+ this.notify(0);
65
108
  this.resp.events.send('<job-queue.disposed>', {
66
109
  channel: this.channel,
67
110
  });
package/lib/log.js CHANGED
@@ -64,13 +64,13 @@ exports.decorate = function (mode, prefix, mod, log, maxWidth, stripBegin) {
64
64
 
65
65
  var beginLength = begin.replace(ansiRegex(), '').length;
66
66
  var availableSpace = maxWidth - beginLength - 1;
67
- if (availableSpace <= 0) {
67
+ if (availableSpace <= 1) {
68
68
  return util.format('%s%s', begin, text);
69
69
  }
70
70
 
71
71
  let beginEmbedded = '';
72
72
  let textNoColor = text.replace(ansiRegex(), '');
73
- const embedded = /^[a-zA-Z]+ \[[a-zA-Z/.\-_]+\] (?:Verb|Info|Warn|Err):/.test(
73
+ const embedded = /^[a-zA-Z]+ \[[a-zA-Z/.\-_]+\] (?:Verb|Info|Warn|Err|Dbg):/.test(
74
74
  textNoColor
75
75
  );
76
76
 
@@ -90,16 +90,17 @@ exports.decorate = function (mode, prefix, mod, log, maxWidth, stripBegin) {
90
90
  }
91
91
 
92
92
  var output = '';
93
+ let _output = '';
93
94
  spaces = new Array(beginLength + 1).join(' ');
94
95
 
95
96
  if (embedded) {
96
97
  const beginEmbeddedNoCol = beginEmbedded.replace(ansiRegex(), '');
97
98
  const beginEmbeddedLength = beginEmbeddedNoCol.length;
98
- const isSmall = /Err:$/.test(beginEmbeddedNoCol); // only 3 chars
99
+ const isSmall = /(Err|Dbg):$/.test(beginEmbeddedNoCol); // only 3 chars
99
100
  const length = beginLength - beginEmbeddedLength + (isSmall ? -1 : 0);
100
101
  const padding = length > 0 ? new Array(length).join(' ') : '';
101
102
  if (!stripBegin) {
102
- output += `${begin}...\n`;
103
+ _output += `${begin}...\n`;
103
104
  }
104
105
  begin = `${padding}${beginEmbedded}` + (isSmall ? ' ' : '');
105
106
  }
@@ -141,7 +142,7 @@ exports.decorate = function (mode, prefix, mod, log, maxWidth, stripBegin) {
141
142
  begin = spaces;
142
143
  });
143
144
 
144
- return output;
145
+ return _output + output;
145
146
  };
146
147
 
147
148
  exports.graffiti = function (text, callback) {
@@ -1,3 +1,5 @@
1
+ 'use strict';
2
+
1
3
  // create a unique, global symbol name
2
4
  // -----------------------------------
3
5
  const RUNNER_INSTANCE_KEY = Symbol.for('xcraft-core-utils.runnerInstance');
@@ -6,47 +8,99 @@ const watt = require('gigawatts');
6
8
  class Runner {
7
9
  constructor() {
8
10
  this.totalRunning = 0;
11
+ this.runningsGroups = [];
12
+ this.runnings = {};
13
+ this.run = this.run.bind(this);
9
14
  watt.wrapAll(this);
10
15
  }
11
16
 
12
- *run(jobQueue, next) {
17
+ run(jobQueue) {
18
+ //must be postponed?
19
+ if (jobQueue.waitOn.length > 0) {
20
+ const mustWait = this.runningsGroups.some((q) =>
21
+ jobQueue.waitOn.includes(q)
22
+ );
23
+ if (mustWait) {
24
+ if (!jobQueue.maxAttemptReached) {
25
+ jobQueue.attempt++;
26
+ setTimeout(this.run, jobQueue.waitDelay, jobQueue);
27
+ return;
28
+ } else {
29
+ jobQueue.attempt = 0;
30
+ }
31
+ }
32
+ }
33
+
34
+ //queue is draining?
13
35
  if (jobQueue.running > 0 && jobQueue.waiting.size === 0) {
14
36
  return;
15
37
  }
16
- yield jobQueue._mutex.lock();
17
- for (let x = jobQueue.running; x < jobQueue.parallelLimit; x++) {
18
- setTimeout(jobQueue._log.bind(jobQueue), 1);
19
38
 
39
+ //jobqueue releasing
40
+ for (let x = jobQueue.running; x < jobQueue.parallelLimit; x++) {
20
41
  const nextEntry = jobQueue.waiting.entries().next();
21
42
  if (nextEntry.done) {
43
+ //we can leave, nothing is waiting us
22
44
  break;
23
45
  }
46
+
47
+ //remove the jobEntry from queue
24
48
  const jobEntry = Object.assign({}, nextEntry.value);
25
49
  jobQueue.waiting.delete(jobEntry[0]);
26
50
 
51
+ //log for journal inspections
27
52
  if (this.totalRunning === 0 && jobQueue.parallelLimit > 1) {
28
- jobQueue._dbg(`this queue is now running new jobs`);
53
+ jobQueue._dbg(`system are running new jobs`);
54
+ }
55
+
56
+ //priority tracking
57
+ if (jobQueue.priorityGroup !== 'default' && jobQueue.running === 0) {
58
+ if (!this.runnings[jobQueue.priorityGroup]) {
59
+ this.runnings[jobQueue.priorityGroup] = {};
60
+ }
61
+ this.runnings[jobQueue.priorityGroup][jobQueue.name] = true;
62
+ //update runnings group
63
+ this.runningsGroups = Object.entries(this.runnings)
64
+ .filter((e) => Object.values(e[1]).some((r) => r === true))
65
+ .map((e) => e[0]);
29
66
  }
30
- jobQueue.running++;
67
+
68
+ //update counters
31
69
  this.totalRunning++;
32
- setImmediate(() =>
33
- jobQueue.runner(jobEntry[1], () => {
34
- jobQueue.running--;
35
- this.totalRunning--;
36
- if (this.totalRunning === 0 && jobQueue.waiting.size === 0) {
37
- jobQueue._dbg(
38
- `this queue is now empty, and there is no more jobs running`
39
- );
40
- }
41
- setTimeout(jobQueue._log.bind(jobQueue), 1);
42
- if (jobQueue.waiting.size > 0) {
43
- setImmediate(this.run, jobQueue);
44
- }
45
- })
46
- );
47
- yield setTimeout(next, this.totalRunning * 10);
70
+
71
+ //trigger job start on queue runner
72
+ jobQueue.runner(jobEntry[1], (err) => {
73
+ if (err) {
74
+ jobQueue.err(err);
75
+ }
76
+ //end callback
77
+
78
+ jobQueue.notify(jobQueue.runningSamples);
79
+
80
+ //update counter
81
+ jobQueue.running--;
82
+ this.totalRunning--;
83
+
84
+ //priority tracking
85
+ if (jobQueue.priorityGroup !== 'default' && jobQueue.running === 0) {
86
+ this.runnings[jobQueue.priorityGroup][jobQueue.name] = false;
87
+ //update runnings group
88
+ this.runningsGroups = Object.entries(this.runnings)
89
+ .filter((e) => Object.values(e[1]).some((r) => r === true))
90
+ .map((e) => e[0]);
91
+ }
92
+
93
+ //log
94
+ if (this.totalRunning === 0 && jobQueue.waiting.size === 0) {
95
+ jobQueue._dbg(`no more jobs running`);
96
+ }
97
+
98
+ //schedule a new run for this queue if needed
99
+ if (jobQueue.waiting.size > 0) {
100
+ setTimeout(this.run, 0, jobQueue);
101
+ }
102
+ });
48
103
  }
49
- jobQueue._mutex.unlock();
50
104
  }
51
105
  }
52
106
 
package/lib/whereIs.js ADDED
@@ -0,0 +1,13 @@
1
+ const path = require('path');
2
+ const fse = require('fs-extra');
3
+
4
+ module.exports = function whereIs(bin) {
5
+ var fullLocation = null;
6
+
7
+ var exists = process.env.PATH.split(path.delimiter).some(function (location) {
8
+ fullLocation = path.join(location, bin);
9
+ return fse.existsSync(fullLocation);
10
+ });
11
+
12
+ return exists ? fullLocation : null;
13
+ };
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "xcraft-core-utils",
3
- "version": "3.1.1",
3
+ "version": "4.1.0",
4
4
  "description": "Xcraft utils",
5
5
  "main": "index.js",
6
6
  "engines": {
7
- "node": ">=6"
7
+ "node": ">=14"
8
8
  },
9
9
  "scripts": {
10
10
  "test": "echo \"Error: no test specified\" && exit 1"
@@ -23,26 +23,28 @@
23
23
  "dependencies": {
24
24
  "ansi-regex": "^2.1.1",
25
25
  "cli-color": "^1.2.0",
26
- "escape-regex": "^1.0.7",
26
+ "escape-regexp": "0.0.1",
27
27
  "escape-string-regexp": "^1.0.5",
28
28
  "figlet": "^1.2.1",
29
+ "fs-extra": "^9.1.0",
29
30
  "gigawatts": "^4.0.1",
30
31
  "js-yaml": "^3.9.0",
31
32
  "linked-list": "^1.0.4",
32
33
  "lodash": "^4.17.20",
33
34
  "locks": "^0.2.2",
34
35
  "prop-types": "^15.7.2",
35
- "uuid": "^3.3.2",
36
- "xcraft-core-fs": "^2.0.0"
37
- },
38
- "optionalDependencies": {
39
- "better-sqlite3": "^5.2.1"
36
+ "uuid": "^8.3.2",
37
+ "xcraft-core-busclient": "^5.0.0",
38
+ "xcraft-core-fs": "^2.0.0",
39
+ "xcraft-core-log": "^2.0.0",
40
+ "xcraft-core-utils": "^4.0.0"
40
41
  },
41
42
  "bugs": {
42
43
  "url": "https://github.com/Xcraft-Inc/xcraft-core-utils/issues"
43
44
  },
44
45
  "homepage": "https://github.com/Xcraft-Inc/xcraft-core-utils#readme",
45
46
  "devDependencies": {
47
+ "should": "^11.2.1",
46
48
  "prettier": "2.0.4",
47
49
  "xcraft-dev-prettier": "^2.0.0",
48
50
  "xcraft-dev-rules": "^2.0.0"
@@ -1,144 +0,0 @@
1
- 'use strict';
2
-
3
- var clc = require('cli-color');
4
-
5
- const moduleName = 'job-queue';
6
- const watt = require('gigawatts');
7
- const locks = require('xcraft-core-utils/lib/locks.js');
8
-
9
- class PersistantJobQueue {
10
- constructor(
11
- path,
12
- name,
13
- runner,
14
- parallelLimit,
15
- useLogger = true,
16
- options = {}
17
- ) {
18
- // Status db
19
- const Database = require('better-sqlite3');
20
- this.db = new Database(path, options);
21
-
22
- this.db.exec(`
23
- CREATE TABLE IF NOT EXISTS JobQueue (
24
- jobId TEXT PRIMARY KEY,
25
- topic TEXT,
26
- seq NUMBER,
27
- status TEXT,
28
- work TEXT
29
- );
30
- `);
31
-
32
- this.insertJob = this.db.prepare(`
33
- INSERT OR REPLACE INTO JobQueue (jobId,topic,seq,status, work)
34
- VALUES ($jobId, $topic, $seq, $status, $work);
35
- `);
36
-
37
- this.getJob = this.db.prepare(`
38
- SELECT * FROM JobQueue WHERE status='waiting' ORDER BY seq LIMIT 0,1;
39
- `);
40
-
41
- this.changeJobStatus = this.db.prepare(`
42
- UPDATE JobQueue
43
- SET status=$status
44
- WHERE jobId=$jobId;
45
- `);
46
-
47
- this.deleteJobDone = this.db.prepare(`
48
- DELETE FROM JobQueue WHERE jobId=$jobId;
49
- `);
50
-
51
- this.getJobsCount = () => {
52
- const row = this.db.prepare(
53
- `SELECT COUNT(*) as count FROM JobQueue WHERE status='waiting';`
54
- );
55
-
56
- const r = row.get();
57
- return parseInt(r.count);
58
- };
59
-
60
- this.count = this.getJobsCount();
61
-
62
- this.log = useLogger && require('xcraft-core-log')(`${moduleName}`, null);
63
- this.name = name;
64
- this.runner = runner;
65
- this.parallelLimit = parallelLimit;
66
- this.running = 0;
67
- this.paused = false;
68
- this._mutex = new locks.Mutex();
69
- watt.wrapAll(this);
70
- }
71
-
72
- _log() {
73
- if (!this.log) {
74
- return;
75
- }
76
-
77
- const count = this.getJobsCount();
78
-
79
- /* FIXME: change dbg to info when it's no longer necessary */
80
- this.log.dbg(
81
- `«${clc.blackBright.bold(this.name)}» waiting:${count} running:${
82
- this.running
83
- }`
84
- );
85
- }
86
-
87
- run() {
88
- if (this.paused) {
89
- return;
90
- }
91
- if (this.getJobsCount() === 0) {
92
- this._log();
93
- return;
94
- }
95
-
96
- if (this.running >= this.parallelLimit) {
97
- return;
98
- }
99
-
100
- // Change status running
101
- const job = this.getJob.get();
102
- this.changeJobStatus.run({jobId: job.jobId, status: 'running'});
103
- this.running++;
104
- this._log();
105
- job.work = JSON.parse(job.work);
106
- setImmediate(this.runner, job, () => {
107
- this.deleteJobDone.run({jobId: job.jobId});
108
- this.running--;
109
- this.run();
110
- });
111
-
112
- this.run();
113
- }
114
-
115
- *push(job) {
116
- yield this._mutex.lock();
117
- try {
118
- this.insertJob.run({
119
- jobId: job.id,
120
- topic: job.topic || this.name,
121
- seq: this.count++,
122
- status: 'waiting',
123
- work: JSON.stringify(job.work || {}),
124
- });
125
- } catch (ex) {
126
- throw ex;
127
- } finally {
128
- this._log();
129
- this._mutex.unlock();
130
- this.run();
131
- }
132
- }
133
-
134
- resume() {
135
- this.paused = false;
136
- this.run();
137
- }
138
-
139
- pause() {
140
- this.paused = true;
141
- }
142
- }
143
-
144
- module.exports = PersistantJobQueue;
package/lib/sqlite.js DELETED
@@ -1,141 +0,0 @@
1
- 'use strict';
2
-
3
- const path = require('path');
4
- const xFs = require('xcraft-core-fs');
5
-
6
- class SQLite {
7
- constructor(location, skip) {
8
- this._stmts = {};
9
- this._db = {};
10
- this._dir = location;
11
- SQLite.prototype._init.call(this, skip);
12
- }
13
-
14
- _init(skip) {
15
- try {
16
- this.Database = skip ? null : require('better-sqlite3');
17
- } catch (ex) {
18
- /* ... */
19
- }
20
- }
21
-
22
- _path(dbName) {
23
- return path.join(this._dir, `${dbName}.db`);
24
- }
25
-
26
- _onError(resp) {
27
- resp.log.info('sqlite3 is not supported on this platform');
28
- }
29
-
30
- _prepare(dbName, query, sql) {
31
- this._stmts[dbName][query] = this._db[dbName].prepare(sql);
32
- }
33
-
34
- stmts(dbName) {
35
- return this._stmts[dbName];
36
- }
37
-
38
- getLocation() {
39
- return this._dir;
40
- }
41
-
42
- setEnable(en) {
43
- SQLite.prototype._init.call(this, !en);
44
-
45
- if (!en) {
46
- Object.keys(this._db).forEach((db) => this.close(db));
47
- }
48
- }
49
-
50
- /**
51
- * Check if SQLite is usable.
52
- *
53
- * @return {Boolean} true if SQLite is available.
54
- */
55
- usable() {
56
- return !!this.Database;
57
- }
58
-
59
- /**
60
- *
61
- * @param {Object} resp - Response object provided by busClient.
62
- * @returns {Boolean} true if usable.
63
- */
64
- tryToUse(resp) {
65
- if (!this.usable()) {
66
- SQLite.prototype._onError.call(this, resp);
67
- return false;
68
- }
69
- return true;
70
- }
71
-
72
- timestamp() {
73
- return new Date().toISOString();
74
- }
75
-
76
- /**
77
- * Open (and create if necessary) a SQLite database.
78
- *
79
- * @param {string} dbName - Database name used for the database file.
80
- * @param {string} tables - Main queries for creating the tables.
81
- * @param {Object} queries - Raw queries to prepare.
82
- * @param {function} onOpen - Callback just after opening the database.
83
- * @param {function} onMigrate - Callback for migrations.
84
- * @return {Boolean} false if SQLite is not available.
85
- */
86
- open(dbName, tables, queries, onOpen, onMigrate) {
87
- if (!this.usable()) {
88
- return false;
89
- }
90
-
91
- if (this._db[dbName]) {
92
- return true;
93
- }
94
-
95
- xFs.mkdir(this._dir);
96
-
97
- const dbPath = this._path(dbName);
98
- this._db[dbName] = new this.Database(dbPath, {timeout: 30000});
99
- this._stmts[dbName] = {};
100
-
101
- if (onOpen) {
102
- onOpen();
103
- }
104
-
105
- this._db[dbName].exec(tables);
106
-
107
- if (onMigrate) {
108
- onMigrate();
109
- }
110
-
111
- for (const query in queries) {
112
- SQLite.prototype._prepare.call(this, dbName, query, queries[query]);
113
- }
114
-
115
- return true;
116
- }
117
-
118
- close(dbName) {
119
- if (!this._db[dbName]) {
120
- return;
121
- }
122
- this._db[dbName].close();
123
- delete this._db[dbName];
124
- }
125
-
126
- exec(dbName, query) {
127
- if (!this.usable() || !this._db[dbName]) {
128
- return false;
129
- }
130
- this._db[dbName].exec(query);
131
- }
132
-
133
- pragma(dbName, pragma) {
134
- if (!this.usable() || !this._db[dbName]) {
135
- return false;
136
- }
137
- return this._db[dbName].pragma(pragma, {simple: true});
138
- }
139
- }
140
-
141
- module.exports = SQLite;