@mherod/get-cookie 1.1.4 → 1.1.7

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.
Files changed (5) hide show
  1. package/README.md +7 -6
  2. package/cli.js +95 -0
  3. package/index.js +109 -219
  4. package/package.json +6 -3
  5. package/utils.js +160 -0
package/README.md CHANGED
@@ -2,27 +2,28 @@
2
2
 
3
3
  ## What is it?
4
4
 
5
- get-cookie is a command line utility that allows you to get the value of a cookie from your locally installed browser. It is useful for testing web pages that require authentication.
5
+ get-cookie is a command line utility that allows you to get the value of a cookie from your locally installed browser.
6
+ It is useful for testing web pages that require authentication.
6
7
 
7
8
  ## Installation
8
9
 
9
- To install get-cookie, run the following command:
10
+ To install get-cookie, run the following command:
10
11
 
11
12
  $ npm install @mherod/get-cookie --global
12
13
 
13
14
  ## How do I use it?
14
15
 
15
- To use get-cookie, run the following command:
16
+ To use get-cookie, run the following command:
16
17
 
17
18
  $ get-cookie <cookie-name> <domain>
18
19
 
19
- For example, to get the value of the `auth` cookie on the `www.example.com` domain, run the following command:
20
+ For example, to get the value of the `auth` cookie on the `www.example.com` domain, run the following command:
20
21
 
21
22
  $ get-cookie auth www.example.com
22
23
 
23
- The output of the command will be the value of the cookie.
24
+ The output of the command will be the value of the cookie.
24
25
 
25
- The library can also be used as a module.
26
+ The library can also be used as a module.
26
27
 
27
28
  ```javascript
28
29
  const {getCookie} = require('@mherod/get-cookie');
package/cli.js ADDED
@@ -0,0 +1,95 @@
1
+ #!/usr/bin/env node
2
+
3
+ const {getFirefoxCookie, getChromeCookie} = require("./index");
4
+ const {printStringValue} = require("./utils");
5
+
6
+ if (process.argv) {
7
+ if (process.argv.length > 2) {
8
+ const name = process.argv[2];
9
+
10
+ let domain;
11
+ if (process.argv[3] != null && process.argv[3].indexOf(".") > -1) {
12
+ domain = process.argv[3];
13
+ } else {
14
+ domain = '%';
15
+ }
16
+
17
+ if (process.argv.includes('--verbose')) {
18
+ process.env.VERBOSE = "true";
19
+ }
20
+ if (process.argv.includes("--chrome-only")) {
21
+ process.env.CHROME_ONLY = "true";
22
+ }
23
+ if (process.argv.includes("--firefox-only")) {
24
+ process.env.FIREFOX_ONLY = "true";
25
+ }
26
+ if (process.argv.includes("--ignore-expired")) {
27
+ process.env.IGNORE_EXPIRED = "true";
28
+ }
29
+
30
+ if (process.env.VERBOSE) {
31
+ console.log("Verbose mode", process.argv);
32
+ }
33
+
34
+ if (process.env.CHROME_ONLY) {
35
+ if (process.env.VERBOSE) {
36
+ console.log('chrome only');
37
+ }
38
+ getChromeCookie({name, domain})
39
+ .then(cookie => {
40
+ if (cookie && cookie.length > 0) {
41
+ printStringValue(cookie);
42
+ } else {
43
+ console.log('No cookie found');
44
+ }
45
+ })
46
+ .catch(err => {
47
+ if (process.env.VERBOSE) {
48
+ console.error(err);
49
+ }
50
+ });
51
+ } else if (process.env.FIREFOX_ONLY) {
52
+ if (process.env.VERBOSE) {
53
+ console.log('firefox only');
54
+ }
55
+ getFirefoxCookie({name, domain})
56
+ .then(cookie => {
57
+ if (cookie && cookie.length > 0) {
58
+ printStringValue(cookie);
59
+ } else {
60
+ console.log('No cookie found');
61
+ }
62
+ })
63
+ .catch(err => {
64
+ if (process.env.VERBOSE) {
65
+ console.error("Error getting Firefox cookie", err);
66
+ }
67
+ });
68
+ } else {
69
+ getChromeCookie({name, domain})
70
+ .catch(err => {
71
+ if (process.env.VERBOSE) {
72
+ console.error("Error getting Chrome cookie", err);
73
+ }
74
+ })
75
+ .then(r => {
76
+ if (typeof r === 'string' && r.trim().length > 0) {
77
+ return r;
78
+ } else {
79
+ return getFirefoxCookie({name, domain})
80
+ .catch(err => {
81
+ if (process.env.VERBOSE) {
82
+ console.error("Error getting Firefox cookie", err);
83
+ }
84
+ });
85
+ }
86
+ })
87
+ .catch((e) => {
88
+ if (process.env.VERBOSE) {
89
+ console.error("Error getting Chrome or Firefox cookie", e);
90
+ }
91
+ })
92
+ .then(printStringValue);
93
+ }
94
+ }
95
+ }
package/index.js CHANGED
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  const crypto = require('crypto');
4
- const {exec} = require("child_process");
5
4
  const fs = require("fs");
5
+ const {execSimple, toStringOrNull, doSqliteQuery1} = require("./utils");
6
6
 
7
7
  if (process.platform !== 'darwin') {
8
8
  throw new Error('This script only works on macOS');
@@ -13,20 +13,7 @@ if (process.platform !== 'darwin') {
13
13
  * @returns {Promise<string>}
14
14
  */
15
15
  async function getChromePassword() {
16
- return await new Promise((resolve, reject) => {
17
- exec("security find-generic-password -w -s \"Chrome Safe Storage\"", (error, stdout, stderr) => {
18
- if (error) {
19
- reject(error);
20
- return;
21
- }
22
- if (stderr) {
23
- reject(error);
24
- return;
25
- }
26
- let s = stdout.toString().trim();
27
- resolve(s);
28
- });
29
- });
16
+ return await execSimple("security find-generic-password -w -s \"Chrome Safe Storage\"");
30
17
  }
31
18
 
32
19
  async function getCookie(name, domain) {
@@ -46,8 +33,11 @@ async function getFirefoxCookie({name, domain}) {
46
33
  if (domain && typeof domain !== 'string') {
47
34
  throw new Error('domain must be a string');
48
35
  }
49
- const file = await findFile(`${process.env.HOME}/Library/Application Support/Firefox/Profiles`, "cookies.sqlite")
50
- if (!fs.existsSync(file)) {
36
+ const [file] = await findAllFiles({
37
+ path: `${process.env.HOME}/Library/Application Support/Firefox/Profiles`,
38
+ name: "cookies.sqlite"
39
+ })
40
+ if (file && !fs.existsSync(file)) {
51
41
  throw new Error(`File ${file} does not exist`);
52
42
  }
53
43
  if (process.env.VERBOSE) {
@@ -67,13 +57,15 @@ async function getFirefoxCookie({name, domain}) {
67
57
  sql += `host LIKE '${domain}';`;
68
58
  }
69
59
  }
70
- return await doSqliteQuery1(file, sql);
60
+ return await doSqliteQuery1(file, sql).then(rows => {
61
+ return rows.map(toStringOrNull).find(v => v !== null);
62
+ });
71
63
  }
72
64
 
73
65
  const defaultChromeRoot = `${process.env.HOME}/Library/Application Support/Google/Chrome`;
74
66
  const defaultChromeCookies = `${defaultChromeRoot}/Default/Cookies`;
75
67
 
76
- async function getEncryptedChromeCookie(name, domain, file = defaultChromeCookies) {
68
+ async function getEncryptedChromeCookie({name, domain, file = defaultChromeCookies}) {
77
69
  if (name && typeof name !== 'string') {
78
70
  throw new Error('name must be a string');
79
71
  }
@@ -87,7 +79,7 @@ async function getEncryptedChromeCookie(name, domain, file = defaultChromeCookie
87
79
  throw new Error(`File ${file} does not exist`);
88
80
  }
89
81
  if (process.env.VERBOSE) {
90
- console.log(`Trying Chrome (at ${file}) cookie ${name} for domain ${domain}`);
82
+ console.log(`Trying Chrome (at ${file.split('/').slice(-3).join('/')}) cookie ${name} for domain ${domain}`);
91
83
  }
92
84
  let sql;
93
85
  sql = `SELECT encrypted_value FROM cookies`;
@@ -106,75 +98,32 @@ async function getEncryptedChromeCookie(name, domain, file = defaultChromeCookie
106
98
  return await doSqliteQuery1(file, sql);
107
99
  }
108
100
 
109
- async function doSqliteQuery1(file, sql) {
110
- const sqlite3 = require('sqlite3');
111
- const db = new sqlite3.Database(file);
112
- return new Promise((resolve, reject) => {
113
- db.all(sql, (err, rows) => {
114
- if (err) {
115
- reject(err);
116
- return;
117
- }
118
- const rows1 = rows;
119
- if (rows1.length === 0) {
120
- if (process.env.VERBOSE) {
121
- console.log(`No rows found`);
122
- }
123
- resolve(null);
124
- return;
125
- }
126
- if (Array.isArray(rows1)) {
127
- // noinspection JSCheckFunctionSignatures
128
- const [value] = rows1.flatMap(row => Object.values(row));
129
- resolve(Buffer.from(value));
130
- return;
131
- }
132
- resolve(rows1);
133
- });
134
- });
135
- }
136
-
137
- async function doSqliteQuery(file, sql) {
138
- const command = `sqlite3 "${file}" "${sql}"`;
139
- if (process.env.VERBOSE) {
140
- console.log(command);
141
- }
142
- return await new Promise((resolve, reject) => {
143
- exec(command, {encoding: 'binary', maxBuffer: 5 * 1024}, (error, stdout, stderr) => {
144
- if (error) {
145
- reject(error);
146
- return;
147
- }
148
- if (stderr) {
149
- reject(error);
150
- return;
151
- }
152
- let stdoutAsBuffer = stdout;
153
- if (typeof stdoutAsBuffer === 'string' && stdoutAsBuffer.length > 0) {
154
- // noinspection JSCheckFunctionSignatures
155
- stdoutAsBuffer = Buffer.from(stdoutAsBuffer, 'binary').slice(0, -1);
156
- }
157
- if (stdoutAsBuffer && stdoutAsBuffer.length > 0) {
158
- resolve(stdoutAsBuffer);
159
- }
160
- });
161
- });
162
- }
163
-
164
101
  /**
165
102
  *
166
103
  * @param {string} password
167
- * @param encryptedData
104
+ * @param {Buffer} encryptedData
168
105
  * @returns {Promise<string>}
169
106
  */
170
107
  async function decrypt(password, encryptedData) {
171
108
  if (typeof password !== 'string') {
172
109
  throw new Error('password must be a string: ' + password);
173
110
  }
174
- const encryptedData1 = encryptedData;
111
+ let encryptedData1;
112
+ encryptedData1 = encryptedData;
175
113
  if (encryptedData1 == null || typeof encryptedData1 !== 'object') {
176
114
  throw new Error('encryptedData must be a object: ' + encryptedData1);
177
115
  }
116
+ if (!(encryptedData1 instanceof Buffer)) {
117
+ if (Array.isArray(encryptedData1) && encryptedData1[0] instanceof Buffer) {
118
+ [encryptedData1] = encryptedData1;
119
+ if (process.env.VERBOSE) {
120
+ console.log(`encryptedData is an array of buffers, selected first: ${encryptedData1}`);
121
+ }
122
+ } else {
123
+ throw new Error('encryptedData must be a Buffer: ' + encryptedData1);
124
+ }
125
+ encryptedData1 = Buffer.from(encryptedData1);
126
+ }
178
127
  if (process.env.VERBOSE) {
179
128
  console.log(`Trying to decrypt with password ${password}`);
180
129
  }
@@ -202,7 +151,7 @@ async function decrypt(password, encryptedData) {
202
151
  decipher.setAutoPadding(false);
203
152
 
204
153
  if (encryptedData1 && encryptedData1.slice) {
205
- encryptedData = encryptedData1.slice(3);
154
+ encryptedData1 = encryptedData1.slice(3);
206
155
  }
207
156
 
208
157
  if (encryptedData1.length % 16 !== 0) {
@@ -213,8 +162,7 @@ async function decrypt(password, encryptedData) {
213
162
  return;
214
163
  }
215
164
 
216
- let decoded = decipher.update(encryptedData1, 'binary', 'utf8');
217
- // let decoded = decipher.update(encryptedData);
165
+ let decoded = decipher.update(encryptedData1);
218
166
  try {
219
167
  decipher.final('utf-8');
220
168
  } catch (e) {
@@ -242,37 +190,80 @@ async function decrypt(password, encryptedData) {
242
190
  /**
243
191
  *
244
192
  * @param {string|undefined} name
245
- * @param {string|undefined} domain
193
+ * @param {string} domain
246
194
  * @returns {Promise<string>}
247
195
  */
248
- async function getChromeCookie({name, domain}) {
196
+ async function getChromeCookie({name, domain = '%'}) {
249
197
  if (name && typeof name !== 'string') {
250
198
  throw new Error('name must be a string');
251
199
  }
252
- if (domain && typeof domain !== 'string') {
200
+ if (typeof domain !== 'string') {
253
201
  throw new Error('domain must be a string');
254
202
  }
255
- const chromePasswordPromise = getChromePassword();
256
- const [encryptedData] = await findAllFiles({path: defaultChromeRoot, name: 'Cookies'}).then(files => {
257
- return Promise.all(files.map(file => getEncryptedChromeCookie(name, domain, file)));
203
+ const encryptedDataItems = await findAllFiles({
204
+ path: defaultChromeRoot,
205
+ name: 'Cookies'
206
+ }).then(files => {
207
+ const promises = files.map(file => {
208
+ return getEncryptedChromeCookie({
209
+ name: name,
210
+ domain: domain,
211
+ file: file
212
+ }).catch(e => {
213
+ if (process.env.VERBOSE) {
214
+ console.log("Error getting encrypted cookie", e);
215
+ }
216
+ return [];
217
+ });
218
+ });
219
+ return Promise.all(promises).then(results => results.flat()).then(results => {
220
+ if (process.env.VERBOSE) {
221
+ console.log("getEncryptedChromeCookie results", results);
222
+ }
223
+ return results.filter(result => {
224
+ return result;
225
+ });
226
+ });
227
+ }).catch(error => {
228
+ if (process.env.VERBOSE) {
229
+ console.log('error', error);
230
+ }
231
+ return [];
258
232
  });
259
- const password = await chromePasswordPromise;
233
+ const password = await getChromePassword();
260
234
  if (process.env.VERBOSE) {
261
- console.log("Received encrypted", encryptedData);
235
+ console.log('encryptedDataItems', encryptedDataItems);
262
236
  }
263
- let s;
264
- try {
265
- s = await decrypt(password, encryptedData);
266
- } catch (e) {
237
+ const decrypted = encryptedDataItems.filter(encryptedData => {
238
+ return encryptedData != null && encryptedData.length > 0;
239
+ }).map(async encryptedData => {
267
240
  if (process.env.VERBOSE) {
268
- console.error("Error decrypting", e);
241
+ console.log("Received encrypted", encryptedData);
269
242
  }
270
- throw new Error('Failed to decrypt');
271
- }
243
+ let decrypted;
244
+ try {
245
+ decrypted = await decrypt(password, encryptedData);
246
+ } catch (e) {
247
+ if (process.env.VERBOSE) {
248
+ console.log("Error decrypting cookie", e);
249
+ }
250
+ return null;
251
+ }
252
+ if (decrypted) {
253
+ if (process.env.VERBOSE) {
254
+ console.log("Decrypted", decrypted);
255
+ }
256
+ return decrypted;
257
+ }
258
+ return null;
259
+ });
260
+ const results = await Promise.all(decrypted);
272
261
  if (process.env.VERBOSE) {
273
- console.log("Decrypted", s);
262
+ console.log('results', results);
274
263
  }
275
- return s;
264
+ return results.map(toStringOrNull).find(result => {
265
+ return typeof result === 'string' && result.length > 0;
266
+ });
276
267
  }
277
268
 
278
269
  /**
@@ -294,9 +285,26 @@ async function findAllFiles({path, name, rootSegments = path.split('/').length,
294
285
  console.log(`Searching for ${name} in ${path}`);
295
286
  }
296
287
  const files = [];
297
- for (const file of fs.readdirSync(path)) {
288
+ let readdirSync;
289
+ try {
290
+ readdirSync = fs.readdirSync(path);
291
+ } catch (e) {
292
+ if (process.env.VERBOSE) {
293
+ console.log(`Error reading ${path}`, e);
294
+ }
295
+ return files;
296
+ }
297
+ for (const file of readdirSync) {
298
298
  const filePath = path + '/' + file;
299
- const stat = fs.statSync(filePath);
299
+ let stat;
300
+ try {
301
+ stat = fs.statSync(filePath);
302
+ } catch (e) {
303
+ if (process.env.VERBOSE) {
304
+ console.error(`Error getting stat for ${filePath}`, e);
305
+ }
306
+ continue;
307
+ }
300
308
  if (stat.isDirectory()) {
301
309
  if (filePath.split('/').length < rootSegments + maxDepth) {
302
310
  try {
@@ -326,129 +334,11 @@ async function findAllFiles({path, name, rootSegments = path.split('/').length,
326
334
  return files;
327
335
  }
328
336
 
329
- /**
330
- *
331
- * @param path
332
- * @param name
333
- * @returns {Promise<string>}
334
- */
335
- async function findFile(path, name) {
336
- return await new Promise((resolve, reject) => {
337
- for (const file of fs.readdirSync(path)) {
338
- const filePath = path + '/' + file;
339
- const stat = fs.statSync(filePath);
340
- if (stat.isDirectory()) {
341
- findFile(filePath, name).then(resolve).catch(reject);
342
- } else if (file === name) {
343
- resolve(filePath);
344
- }
345
- }
346
- });
347
- }
348
-
349
- function printStringValue(r) {
350
- if (process.env.VERBOSE) {
351
- console.log("Printing value", r);
352
- }
353
- if (r) {
354
- if (typeof r === 'string') {
355
- console.log(r);
356
- } else if (r.toString) {
357
- // noinspection JSCheckFunctionSignatures
358
- console.log(r.toString('utf8'));
359
- }
360
- }
361
- }
362
-
363
337
  // noinspection JSUnusedGlobalSymbols
364
338
  module.exports = {
365
339
  getDecryptedCookie: getChromeCookie,
366
340
  getChromeCookie,
367
341
  getFirefoxCookie,
368
- getCookie
342
+ getCookie,
343
+ decrypt
369
344
  };
370
-
371
- if (process.argv) {
372
- if (process.argv.length > 2) {
373
- const name = process.argv[2];
374
- const domain = process.argv[3] ?? '%';
375
-
376
- if (process.argv.includes('--verbose')) {
377
- process.env.VERBOSE = "true";
378
- }
379
- if (process.argv.includes("--chrome-only")) {
380
- process.env.CHROME_ONLY = "true";
381
- }
382
- if (process.argv.includes("--firefox-only")) {
383
- process.env.FIREFOX_ONLY = "true";
384
- }
385
- if (process.argv.includes("--ignore-expired")) {
386
- process.env.IGNORE_EXPIRED = "true";
387
- }
388
-
389
- if (process.env.VERBOSE) {
390
- console.log("Verbose mode", process.argv);
391
- }
392
-
393
- if (process.env.CHROME_ONLY) {
394
- if (process.env.VERBOSE) {
395
- console.log('chrome only');
396
- }
397
- getChromeCookie({name, domain})
398
- .then(cookie => {
399
- if (cookie && cookie.length > 0) {
400
- printStringValue(cookie);
401
- } else {
402
- console.log('No cookie found');
403
- }
404
- })
405
- .catch(err => {
406
- if (process.env.VERBOSE) {
407
- console.error(err);
408
- }
409
- });
410
- } else if (process.env.FIREFOX_ONLY) {
411
- if (process.env.VERBOSE) {
412
- console.log('firefox only');
413
- }
414
- getFirefoxCookie({name, domain})
415
- .then(cookie => {
416
- if (cookie && cookie.length > 0) {
417
- printStringValue(cookie);
418
- } else {
419
- console.log('No cookie found');
420
- }
421
- })
422
- .catch(err => {
423
- if (process.env.VERBOSE) {
424
- console.error("Error getting Firefox cookie", err);
425
- }
426
- });
427
- } else {
428
- getChromeCookie({name, domain})
429
- .catch(err => {
430
- if (process.env.VERBOSE) {
431
- console.error("Error getting Chrome cookie", err);
432
- }
433
- })
434
- .then(r => {
435
- if (typeof r === 'string' && r.trim().length > 0) {
436
- return r;
437
- } else {
438
- return getFirefoxCookie({name, domain})
439
- .catch(err => {
440
- if (process.env.VERBOSE) {
441
- console.error("Error getting Firefox cookie", err);
442
- }
443
- });
444
- }
445
- })
446
- .catch((e) => {
447
- if (process.env.VERBOSE) {
448
- console.error("Error getting Chrome or Firefox cookie", e);
449
- }
450
- })
451
- .then(printStringValue);
452
- }
453
- }
454
- }
package/package.json CHANGED
@@ -1,14 +1,14 @@
1
1
  {
2
2
  "name": "@mherod/get-cookie",
3
- "version": "1.1.4",
3
+ "version": "1.1.7",
4
4
  "description": "Node.js module for querying a local user's Chrome cookie",
5
5
  "main": "index.js",
6
- "bin": "index.js",
6
+ "bin": "cli.js",
7
7
  "publishConfig": {
8
8
  "access": "public"
9
9
  },
10
10
  "scripts": {
11
- "test": "echo \"Error: no test specified\" && exit 1",
11
+ "test": "jest",
12
12
  "installGlobal": "rm -rf /usr/local/bin/get-cookie ; npm install --force --global ."
13
13
  },
14
14
  "keywords": [],
@@ -16,5 +16,8 @@
16
16
  "license": "ISC",
17
17
  "dependencies": {
18
18
  "sqlite3": "^5.0.8"
19
+ },
20
+ "devDependencies": {
21
+ "jest": "^28.1.0"
19
22
  }
20
23
  }
package/utils.js ADDED
@@ -0,0 +1,160 @@
1
+ // noinspection JSUnusedGlobalSymbols
2
+
3
+ const {exec} = require('child_process');
4
+ const fs = require("fs");
5
+
6
+ async function execSimple(command) {
7
+ if (typeof command !== 'string') {
8
+ throw new TypeError('execSimple: command must be a string');
9
+ }
10
+ if (process.env.VERBOSE) {
11
+ console.log(command);
12
+ }
13
+ return await new Promise((resolve, reject) => {
14
+ exec(command, {encoding: 'binary', maxBuffer: 5 * 1024}, (error, stdout, stderr) => {
15
+ if (error) {
16
+ reject(error);
17
+ return;
18
+ }
19
+ if (stderr) {
20
+ reject(error);
21
+ return;
22
+ }
23
+ if (stdout) {
24
+ resolve(stdout.toString('utf8').trim());
25
+ }
26
+ });
27
+ });
28
+ }
29
+
30
+ async function execAsBuffer(command) {
31
+ if (typeof command !== 'string') {
32
+ throw new TypeError('execAsBuffer: command must be a string');
33
+ }
34
+ if (process.env.VERBOSE) {
35
+ console.log(command);
36
+ }
37
+ return await new Promise((resolve, reject) => {
38
+ exec(command, {encoding: 'binary', maxBuffer: 5 * 1024}, (error, stdout, stderr) => {
39
+ if (error) {
40
+ reject(error);
41
+ return;
42
+ }
43
+ if (stderr) {
44
+ reject(error);
45
+ return;
46
+ }
47
+ let stdoutAsBuffer = stdout;
48
+ if (typeof stdoutAsBuffer === 'string' && stdoutAsBuffer.length > 0) {
49
+ // noinspection JSCheckFunctionSignatures
50
+ stdoutAsBuffer = Buffer.from(stdoutAsBuffer, 'binary').slice(0, -1);
51
+ }
52
+ if (stdoutAsBuffer && stdoutAsBuffer.length > 0) {
53
+ resolve(stdoutAsBuffer);
54
+ }
55
+ });
56
+ });
57
+ }
58
+
59
+ function printStringValue(r) {
60
+ if (process.env.VERBOSE) {
61
+ console.log("Printing value", r);
62
+ }
63
+ if (r) {
64
+ if (typeof r === 'string') {
65
+ console.log(r);
66
+ } else if (r.toString) {
67
+ // noinspection JSCheckFunctionSignatures
68
+ console.log(r.toString('utf8'));
69
+ }
70
+ }
71
+ }
72
+
73
+ /**
74
+ *
75
+ * @param result
76
+ * @returns {string|null}
77
+ */
78
+ function toStringOrNull(result) {
79
+ if (result == null) {
80
+ return null;
81
+ }
82
+ if (process.env.VERBOSE) {
83
+ console.log('result', result);
84
+ }
85
+ if (typeof result === 'string' && result.length > 0) {
86
+ return result;
87
+ }
88
+ if (result.slice && result.toString) {
89
+ // noinspection JSCheckFunctionSignatures
90
+ return result.toString('utf8');
91
+ }
92
+ return null;
93
+ }
94
+
95
+ /**
96
+ *
97
+ * @param {string} file
98
+ * @param {string} sql
99
+ * @returns {Promise<Buffer[]>}
100
+ */
101
+ async function doSqliteQuery1(file, sql) {
102
+ if (typeof sql !== 'string') {
103
+ throw new TypeError('doSqliteQuery1: sql must be a string');
104
+ }
105
+ if (typeof file !== 'string') {
106
+ throw new TypeError('doSqliteQuery1: file must be a string');
107
+ }
108
+ if (!fs.existsSync(file)) {
109
+ throw new Error(`doSqliteQuery1: file ${file} does not exist`);
110
+ }
111
+ if (process.env.VERBOSE) {
112
+ console.log(`doSqliteQuery1: file ${file}`);
113
+ console.log(`doSqliteQuery1: sql ${sql}`);
114
+ }
115
+ const sqlite3 = require('sqlite3');
116
+ const db = new sqlite3.Database(file);
117
+ return new Promise((resolve, reject) => {
118
+ db.all(sql, (err, rows) => {
119
+ if (err) {
120
+ if (process.env.VERBOSE) {
121
+ console.log(`doSqliteQuery1: error ${err}`);
122
+ }
123
+ reject(err);
124
+ return;
125
+ }
126
+ const rows1 = rows;
127
+ if (rows1 == null || rows1.length === 0) {
128
+ if (process.env.VERBOSE) {
129
+ console.log(`doSqliteQuery1: no rows`);
130
+ }
131
+ resolve([]);
132
+ return;
133
+ }
134
+ if (Array.isArray(rows1)) {
135
+ if (process.env.VERBOSE) {
136
+ console.log(`doSqliteQuery1: ${rows1.length} rows`);
137
+ }
138
+ // noinspection JSCheckFunctionSignatures
139
+ const buffers = rows1.flatMap(row => Object.values(row)).map(v => Buffer.from(v, 'binary'));
140
+ if (process.env.VERBOSE) {
141
+ console.log(`doSqliteQuery1: ${buffers.length} buffers`);
142
+ }
143
+ resolve(buffers);
144
+ return;
145
+ }
146
+ if (process.env.VERBOSE) {
147
+ console.log(`doSqliteQuery1: rows ${JSON.stringify(rows1)}`);
148
+ }
149
+ resolve([rows1]);
150
+ });
151
+ });
152
+ }
153
+
154
+ module.exports = {
155
+ execSimple: execSimple,
156
+ execAsBuffer: execAsBuffer,
157
+ printStringValue: printStringValue,
158
+ toStringOrNull: toStringOrNull,
159
+ doSqliteQuery1
160
+ };