@mherod/get-cookie 1.1.5 → 1.2.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.
Files changed (5) hide show
  1. package/README.md +2 -0
  2. package/cli.js +48 -0
  3. package/index.js +160 -225
  4. package/package.json +7 -3
  5. package/utils.js +175 -0
package/README.md CHANGED
@@ -11,6 +11,8 @@ To install get-cookie, run the following command:
11
11
 
12
12
  $ npm install @mherod/get-cookie --global
13
13
 
14
+ **Note: Currently only macOS is supported. Windows support is planned for a future release.**
15
+
14
16
  ## How do I use it?
15
17
 
16
18
  To use get-cookie, run the following command:
package/cli.js ADDED
@@ -0,0 +1,48 @@
1
+ #!/usr/bin/env node
2
+
3
+ const {getCookie} = require("./index");
4
+
5
+ if (process.argv) {
6
+ if (process.argv.length > 2) {
7
+ const name = process.argv[2];
8
+
9
+ let domain;
10
+ if (process.argv[3] != null && process.argv[3].indexOf(".") > -1) {
11
+ domain = process.argv[3];
12
+ } else {
13
+ domain = '%';
14
+ }
15
+
16
+ if (process.argv.includes("--require-jwt")) {
17
+ process.env.REQUIRE_JWT = "true";
18
+ }
19
+ if (process.argv.includes('--verbose')) {
20
+ process.env.VERBOSE = "true";
21
+ }
22
+ if (process.argv.includes("--chrome-only")) {
23
+ process.env.CHROME_ONLY = "true";
24
+ }
25
+ if (process.argv.includes("--firefox-only")) {
26
+ process.env.FIREFOX_ONLY = "true";
27
+ }
28
+ if (process.argv.includes("--ignore-expired")) {
29
+ process.env.IGNORE_EXPIRED = "true";
30
+ }
31
+
32
+ if (process.env.VERBOSE) {
33
+ console.log("Verbose mode", process.argv);
34
+ }
35
+
36
+ getCookie({
37
+ name: name,
38
+ domain: domain,
39
+ requireJwt: (process.env.REQUIRE_JWT === "true"),
40
+ }).then(cookie => {
41
+ if (typeof cookie === "string") {
42
+ console.log(cookie);
43
+ }
44
+ }).catch(err => {
45
+ console.error(err);
46
+ });
47
+ }
48
+ }
package/index.js CHANGED
@@ -1,11 +1,27 @@
1
1
  #!/usr/bin/env node
2
2
 
3
+ if (process.platform !== 'darwin') {
4
+ throw new Error('This script only works on macOS');
5
+ }
6
+
3
7
  const crypto = require('crypto');
4
- const {exec} = require("child_process");
5
8
  const fs = require("fs");
9
+ const {execSimple, toStringOrNull, doSqliteQuery1, printStringValue, toStringValue} = require("./utils");
10
+ const jsonwebtoken = require('jsonwebtoken');
6
11
 
7
- if (process.platform !== 'darwin') {
8
- throw new Error('This script only works on macOS');
12
+ function isValidJwt(token) {
13
+ if (typeof token !== 'string') {
14
+ return false;
15
+ }
16
+ try {
17
+ const result = jsonwebtoken.decode(token, {complete: true});
18
+ if (process.env.VERBOSE) {
19
+ console.log(result);
20
+ }
21
+ return true;
22
+ } catch (err) {
23
+ return false;
24
+ }
9
25
  }
10
26
 
11
27
  /**
@@ -13,24 +29,74 @@ if (process.platform !== 'darwin') {
13
29
  * @returns {Promise<string>}
14
30
  */
15
31
  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
- });
32
+ return await execSimple("security find-generic-password -w -s \"Chrome Safe Storage\"");
30
33
  }
31
34
 
32
- async function getCookie(name, domain) {
33
- throw new Error('Not implemented');
35
+ /**
36
+ *
37
+ * @param params
38
+ * @returns {Promise<string>}
39
+ */
40
+ async function getCookie(params) {
41
+ if (process.env.CHROME_ONLY) {
42
+ if (process.env.VERBOSE) {
43
+ console.log('chrome only');
44
+ }
45
+ return await getChromeCookie(params)
46
+ .then(cookie => {
47
+ if (cookie && cookie.length > 0) {
48
+ return toStringValue(cookie);
49
+ }
50
+ })
51
+ .catch(err => {
52
+ if (process.env.VERBOSE) {
53
+ console.error(err);
54
+ }
55
+ });
56
+ } else if (process.env.FIREFOX_ONLY) {
57
+ if (process.env.VERBOSE) {
58
+ console.log('firefox only');
59
+ }
60
+ return await getFirefoxCookie(params)
61
+ .then(b => b.toString('utf8'))
62
+ .then(cookie => {
63
+ if (cookie && cookie.length > 0) {
64
+ return toStringValue(cookie);
65
+ } else {
66
+ console.log('No cookie found');
67
+ }
68
+ })
69
+ .catch(err => {
70
+ if (process.env.VERBOSE) {
71
+ console.error("Error getting Firefox cookie", err);
72
+ }
73
+ });
74
+ } else {
75
+ return await getChromeCookie(params)
76
+ .catch(err => {
77
+ if (process.env.VERBOSE) {
78
+ console.error("Error getting Chrome cookie", err);
79
+ }
80
+ })
81
+ .then(r => {
82
+ if (typeof r === 'string' && r.trim().length > 0) {
83
+ return r;
84
+ } else {
85
+ return getFirefoxCookie(params)
86
+ .catch(err => {
87
+ if (process.env.VERBOSE) {
88
+ console.error("Error getting Firefox cookie", err);
89
+ }
90
+ });
91
+ }
92
+ })
93
+ .catch((e) => {
94
+ if (process.env.VERBOSE) {
95
+ console.error("Error getting Chrome or Firefox cookie", e);
96
+ }
97
+ })
98
+ .then(toStringValue);
99
+ }
34
100
  }
35
101
 
36
102
  /**
@@ -46,8 +112,11 @@ async function getFirefoxCookie({name, domain}) {
46
112
  if (domain && typeof domain !== 'string') {
47
113
  throw new Error('domain must be a string');
48
114
  }
49
- const file = await findFile(`${process.env.HOME}/Library/Application Support/Firefox/Profiles`, "cookies.sqlite")
50
- if (!fs.existsSync(file)) {
115
+ const [file] = await findAllFiles({
116
+ path: `${process.env.HOME}/Library/Application Support/Firefox/Profiles`,
117
+ name: "cookies.sqlite"
118
+ })
119
+ if (file && !fs.existsSync(file)) {
51
120
  throw new Error(`File ${file} does not exist`);
52
121
  }
53
122
  if (process.env.VERBOSE) {
@@ -67,7 +136,9 @@ async function getFirefoxCookie({name, domain}) {
67
136
  sql += `host LIKE '${domain}';`;
68
137
  }
69
138
  }
70
- return await doSqliteQuery1(file, sql);
139
+ return await doSqliteQuery1(file, sql).then(rows => {
140
+ return rows.map(toStringOrNull).find(v => v !== null);
141
+ });
71
142
  }
72
143
 
73
144
  const defaultChromeRoot = `${process.env.HOME}/Library/Application Support/Google/Chrome`;
@@ -87,7 +158,7 @@ async function getEncryptedChromeCookie({name, domain, file = defaultChromeCooki
87
158
  throw new Error(`File ${file} does not exist`);
88
159
  }
89
160
  if (process.env.VERBOSE) {
90
- console.log(`Trying Chrome (at ${file}) cookie ${name} for domain ${domain}`);
161
+ console.log(`Trying Chrome (at ${file.split('/').slice(-3).join('/')}) cookie ${name} for domain ${domain}`);
91
162
  }
92
163
  let sql;
93
164
  sql = `SELECT encrypted_value FROM cookies`;
@@ -106,75 +177,32 @@ async function getEncryptedChromeCookie({name, domain, file = defaultChromeCooki
106
177
  return await doSqliteQuery1(file, sql);
107
178
  }
108
179
 
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
180
  /**
165
181
  *
166
182
  * @param {string} password
167
- * @param encryptedData
183
+ * @param {Buffer} encryptedData
168
184
  * @returns {Promise<string>}
169
185
  */
170
186
  async function decrypt(password, encryptedData) {
171
187
  if (typeof password !== 'string') {
172
188
  throw new Error('password must be a string: ' + password);
173
189
  }
174
- const encryptedData1 = encryptedData;
190
+ let encryptedData1;
191
+ encryptedData1 = encryptedData;
175
192
  if (encryptedData1 == null || typeof encryptedData1 !== 'object') {
176
193
  throw new Error('encryptedData must be a object: ' + encryptedData1);
177
194
  }
195
+ if (!(encryptedData1 instanceof Buffer)) {
196
+ if (Array.isArray(encryptedData1) && encryptedData1[0] instanceof Buffer) {
197
+ [encryptedData1] = encryptedData1;
198
+ if (process.env.VERBOSE) {
199
+ console.log(`encryptedData is an array of buffers, selected first: ${encryptedData1}`);
200
+ }
201
+ } else {
202
+ throw new Error('encryptedData must be a Buffer: ' + encryptedData1);
203
+ }
204
+ encryptedData1 = Buffer.from(encryptedData1);
205
+ }
178
206
  if (process.env.VERBOSE) {
179
207
  console.log(`Trying to decrypt with password ${password}`);
180
208
  }
@@ -202,7 +230,7 @@ async function decrypt(password, encryptedData) {
202
230
  decipher.setAutoPadding(false);
203
231
 
204
232
  if (encryptedData1 && encryptedData1.slice) {
205
- encryptedData = encryptedData1.slice(3);
233
+ encryptedData1 = encryptedData1.slice(3);
206
234
  }
207
235
 
208
236
  if (encryptedData1.length % 16 !== 0) {
@@ -213,8 +241,7 @@ async function decrypt(password, encryptedData) {
213
241
  return;
214
242
  }
215
243
 
216
- let decoded = decipher.update(encryptedData1, 'binary', 'utf8');
217
- // let decoded = decipher.update(encryptedData);
244
+ let decoded = decipher.update(encryptedData1);
218
245
  try {
219
246
  decipher.final('utf-8');
220
247
  } catch (e) {
@@ -242,55 +269,81 @@ async function decrypt(password, encryptedData) {
242
269
  /**
243
270
  *
244
271
  * @param {string|undefined} name
245
- * @param {string|undefined} domain
272
+ * @param {string} domain
273
+ * @param {boolean} requireJwt
246
274
  * @returns {Promise<string>}
247
275
  */
248
- async function getChromeCookie({name, domain}) {
276
+ async function getChromeCookie({name, domain = '%', requireJwt = false}) {
249
277
  if (name && typeof name !== 'string') {
250
278
  throw new Error('name must be a string');
251
279
  }
252
- if (domain && typeof domain !== 'string') {
280
+ if (typeof domain !== 'string') {
253
281
  throw new Error('domain must be a string');
254
282
  }
255
- const chromePasswordPromise = getChromePassword();
256
- const [encryptedData] = await findAllFiles({
283
+ const encryptedDataItems = await findAllFiles({
257
284
  path: defaultChromeRoot,
258
285
  name: 'Cookies'
259
286
  }).then(files => {
260
- const promise = Promise.all(files.map(file => {
287
+ const promises = files.map(file => {
261
288
  return getEncryptedChromeCookie({
262
289
  name: name,
263
290
  domain: domain,
264
291
  file: file
292
+ }).catch(e => {
293
+ if (process.env.VERBOSE) {
294
+ console.log("Error getting encrypted cookie", e);
295
+ }
296
+ return [];
265
297
  });
266
- }));
267
- if (process.env.VERBOSE) {
268
- console.log('promise', promise);
269
- }
270
- return promise;
298
+ });
299
+ return Promise.all(promises).then(results => results.flat()).then(results => {
300
+ if (process.env.VERBOSE) {
301
+ console.log("getEncryptedChromeCookie results", results);
302
+ }
303
+ return results.filter(result => {
304
+ return result;
305
+ });
306
+ });
271
307
  }).catch(error => {
272
308
  if (process.env.VERBOSE) {
273
309
  console.log('error', error);
274
310
  }
275
311
  return [];
276
312
  });
277
- const password = await chromePasswordPromise;
313
+ const password = await getChromePassword();
278
314
  if (process.env.VERBOSE) {
279
- console.log("Received encrypted", encryptedData);
315
+ console.log('encryptedDataItems', encryptedDataItems);
280
316
  }
281
- let s;
282
- try {
283
- s = await decrypt(password, encryptedData);
284
- } catch (e) {
317
+ const decrypted = encryptedDataItems.filter(encryptedData => {
318
+ return encryptedData != null && encryptedData.length > 0;
319
+ }).map(async encryptedData => {
285
320
  if (process.env.VERBOSE) {
286
- console.error("Error decrypting", e);
321
+ console.log("Received encrypted", encryptedData);
287
322
  }
288
- throw new Error('Failed to decrypt');
289
- }
323
+ let decrypted;
324
+ try {
325
+ decrypted = await decrypt(password, encryptedData);
326
+ } catch (e) {
327
+ if (process.env.VERBOSE) {
328
+ console.log("Error decrypting cookie", e);
329
+ }
330
+ return null;
331
+ }
332
+ if (decrypted) {
333
+ if (process.env.VERBOSE) {
334
+ console.log("Decrypted", decrypted);
335
+ }
336
+ return decrypted;
337
+ }
338
+ return null;
339
+ });
340
+ const results = await Promise.all(decrypted);
290
341
  if (process.env.VERBOSE) {
291
- console.log("Decrypted", s);
342
+ console.log('results', results);
292
343
  }
293
- return s;
344
+ return results.map(toStringOrNull).find(result => {
345
+ return typeof result === 'string' && result.length > 0 && (requireJwt === false || isValidJwt(result));
346
+ });
294
347
  }
295
348
 
296
349
  /**
@@ -361,129 +414,11 @@ async function findAllFiles({path, name, rootSegments = path.split('/').length,
361
414
  return files;
362
415
  }
363
416
 
364
- /**
365
- *
366
- * @param path
367
- * @param name
368
- * @returns {Promise<string>}
369
- */
370
- async function findFile(path, name) {
371
- return await new Promise((resolve, reject) => {
372
- for (const file of fs.readdirSync(path)) {
373
- const filePath = path + '/' + file;
374
- const stat = fs.statSync(filePath);
375
- if (stat.isDirectory()) {
376
- findFile(filePath, name).then(resolve).catch(reject);
377
- } else if (file === name) {
378
- resolve(filePath);
379
- }
380
- }
381
- });
382
- }
383
-
384
- function printStringValue(r) {
385
- if (process.env.VERBOSE) {
386
- console.log("Printing value", r);
387
- }
388
- if (r) {
389
- if (typeof r === 'string') {
390
- console.log(r);
391
- } else if (r.toString) {
392
- // noinspection JSCheckFunctionSignatures
393
- console.log(r.toString('utf8'));
394
- }
395
- }
396
- }
397
-
398
417
  // noinspection JSUnusedGlobalSymbols
399
418
  module.exports = {
400
419
  getDecryptedCookie: getChromeCookie,
401
420
  getChromeCookie,
402
421
  getFirefoxCookie,
403
- getCookie
422
+ getCookie,
423
+ decrypt
404
424
  };
405
-
406
- if (process.argv) {
407
- if (process.argv.length > 2) {
408
- const name = process.argv[2];
409
- const domain = process.argv[3] ?? '%';
410
-
411
- if (process.argv.includes('--verbose')) {
412
- process.env.VERBOSE = "true";
413
- }
414
- if (process.argv.includes("--chrome-only")) {
415
- process.env.CHROME_ONLY = "true";
416
- }
417
- if (process.argv.includes("--firefox-only")) {
418
- process.env.FIREFOX_ONLY = "true";
419
- }
420
- if (process.argv.includes("--ignore-expired")) {
421
- process.env.IGNORE_EXPIRED = "true";
422
- }
423
-
424
- if (process.env.VERBOSE) {
425
- console.log("Verbose mode", process.argv);
426
- }
427
-
428
- if (process.env.CHROME_ONLY) {
429
- if (process.env.VERBOSE) {
430
- console.log('chrome only');
431
- }
432
- getChromeCookie({name, domain})
433
- .then(cookie => {
434
- if (cookie && cookie.length > 0) {
435
- printStringValue(cookie);
436
- } else {
437
- console.log('No cookie found');
438
- }
439
- })
440
- .catch(err => {
441
- if (process.env.VERBOSE) {
442
- console.error(err);
443
- }
444
- });
445
- } else if (process.env.FIREFOX_ONLY) {
446
- if (process.env.VERBOSE) {
447
- console.log('firefox only');
448
- }
449
- getFirefoxCookie({name, domain})
450
- .then(cookie => {
451
- if (cookie && cookie.length > 0) {
452
- printStringValue(cookie);
453
- } else {
454
- console.log('No cookie found');
455
- }
456
- })
457
- .catch(err => {
458
- if (process.env.VERBOSE) {
459
- console.error("Error getting Firefox cookie", err);
460
- }
461
- });
462
- } else {
463
- getChromeCookie({name, domain})
464
- .catch(err => {
465
- if (process.env.VERBOSE) {
466
- console.error("Error getting Chrome cookie", err);
467
- }
468
- })
469
- .then(r => {
470
- if (typeof r === 'string' && r.trim().length > 0) {
471
- return r;
472
- } else {
473
- return getFirefoxCookie({name, domain})
474
- .catch(err => {
475
- if (process.env.VERBOSE) {
476
- console.error("Error getting Firefox cookie", err);
477
- }
478
- });
479
- }
480
- })
481
- .catch((e) => {
482
- if (process.env.VERBOSE) {
483
- console.error("Error getting Chrome or Firefox cookie", e);
484
- }
485
- })
486
- .then(printStringValue);
487
- }
488
- }
489
- }
package/package.json CHANGED
@@ -1,20 +1,24 @@
1
1
  {
2
2
  "name": "@mherod/get-cookie",
3
- "version": "1.1.5",
3
+ "version": "1.2.0",
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": [],
15
15
  "author": "Matthew Herod",
16
16
  "license": "ISC",
17
17
  "dependencies": {
18
+ "jsonwebtoken": "^8.5.1",
18
19
  "sqlite3": "^5.0.8"
20
+ },
21
+ "devDependencies": {
22
+ "jest": "^28.1.0"
19
23
  }
20
24
  }
package/utils.js ADDED
@@ -0,0 +1,175 @@
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 toStringValue(r) {
60
+ if (process.env.VERBOSE) {
61
+ console.log("Printing value", r);
62
+ }
63
+ if (r) {
64
+ if (typeof r === 'string') {
65
+ return r;
66
+ } else if (r.toString) {
67
+ // noinspection JSCheckFunctionSignatures
68
+ return r.toString('utf8');
69
+ }
70
+ }
71
+ }
72
+
73
+ function printStringValue(r) {
74
+ if (process.env.VERBOSE) {
75
+ console.log("Printing value", r);
76
+ }
77
+ if (r) {
78
+ if (typeof r === 'string') {
79
+ console.log(r);
80
+ } else if (r.toString) {
81
+ // noinspection JSCheckFunctionSignatures
82
+ console.log(r.toString('utf8'));
83
+ }
84
+ }
85
+ }
86
+
87
+ /**
88
+ *
89
+ * @param result
90
+ * @returns {string|null}
91
+ */
92
+ function toStringOrNull(result) {
93
+ if (result == null) {
94
+ return null;
95
+ }
96
+ if (process.env.VERBOSE) {
97
+ console.log('result', result);
98
+ }
99
+ if (typeof result === 'string' && result.length > 0) {
100
+ return result;
101
+ }
102
+ if (result.slice && result.toString) {
103
+ // noinspection JSCheckFunctionSignatures
104
+ return result.toString('utf8');
105
+ }
106
+ return null;
107
+ }
108
+
109
+ /**
110
+ *
111
+ * @param {string} file
112
+ * @param {string} sql
113
+ * @returns {Promise<Buffer[]>}
114
+ */
115
+ async function doSqliteQuery1(file, sql) {
116
+ if (typeof sql !== 'string') {
117
+ throw new TypeError('doSqliteQuery1: sql must be a string');
118
+ }
119
+ if (typeof file !== 'string') {
120
+ throw new TypeError('doSqliteQuery1: file must be a string');
121
+ }
122
+ if (!fs.existsSync(file)) {
123
+ throw new Error(`doSqliteQuery1: file ${file} does not exist`);
124
+ }
125
+ if (process.env.VERBOSE) {
126
+ console.log(`doSqliteQuery1: file ${file}`);
127
+ console.log(`doSqliteQuery1: sql ${sql}`);
128
+ }
129
+ const sqlite3 = require('sqlite3');
130
+ const db = new sqlite3.Database(file);
131
+ return new Promise((resolve, reject) => {
132
+ db.all(sql, (err, rows) => {
133
+ if (err) {
134
+ if (process.env.VERBOSE) {
135
+ console.log(`doSqliteQuery1: error ${err}`);
136
+ }
137
+ reject(err);
138
+ return;
139
+ }
140
+ const rows1 = rows;
141
+ if (rows1 == null || rows1.length === 0) {
142
+ if (process.env.VERBOSE) {
143
+ console.log(`doSqliteQuery1: no rows`);
144
+ }
145
+ resolve([]);
146
+ return;
147
+ }
148
+ if (Array.isArray(rows1)) {
149
+ if (process.env.VERBOSE) {
150
+ console.log(`doSqliteQuery1: ${rows1.length} rows`);
151
+ }
152
+ // noinspection JSCheckFunctionSignatures
153
+ const buffers = rows1.flatMap(row => Object.values(row)).map(v => Buffer.from(v, 'binary'));
154
+ if (process.env.VERBOSE) {
155
+ console.log(`doSqliteQuery1: ${buffers.length} buffers`);
156
+ }
157
+ resolve(buffers);
158
+ return;
159
+ }
160
+ if (process.env.VERBOSE) {
161
+ console.log(`doSqliteQuery1: rows ${JSON.stringify(rows1)}`);
162
+ }
163
+ resolve([rows1]);
164
+ });
165
+ });
166
+ }
167
+
168
+ module.exports = {
169
+ execSimple: execSimple,
170
+ execAsBuffer: execAsBuffer,
171
+ printStringValue: printStringValue,
172
+ toStringValue,
173
+ toStringOrNull: toStringOrNull,
174
+ doSqliteQuery1
175
+ };