utilitas 1995.2.7 → 1995.2.9

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/lib/dbio.mjs CHANGED
@@ -1,8 +1,11 @@
1
- import { assertSet, ensureArray, log as _log, need } from './utilitas.mjs';
1
+ import {
2
+ assertSet, ensureArray, ensureString, log as _log, need,
3
+ } from './utilitas.mjs';
4
+
2
5
  import { isPrimary } from './callosum.mjs';
3
6
 
4
- const _NEED = ['mysql2'];
5
- const defaultPort = 3306;
7
+ const _NEED = ['mysql2', 'pg'];
8
+ const [mysqlDefaultPort, postgresqlDefaultPort] = [3306, 5432];
6
9
  const orders = { '+': 'ASC', '-': 'DESC' };
7
10
  const [fieldId, fieldAny] = ['id', '*'];
8
11
  const fieldCount = `COUNT(${fieldAny})`;
@@ -12,16 +15,47 @@ const log = (content) => _log(content, import.meta.url);
12
15
  const defaultKey = (options) => options && options.key ? options.key : fieldId;
13
16
  const queryOne = async (...args) => (await query(...args))[0];
14
17
  const assertForce = op => assert(op?.force, "Option 'force' is required.", 500);
18
+ const [MYSQL, POSTGRESQL] = ['MYSQL', 'POSTGRESQL'];
19
+ const quote = name => `${bracket}${name}${bracket}`;
20
+ const join = elements => elements.join(', ');
15
21
 
16
- let pool;
22
+ let [provider, pool, actExecute, bracket, actDuplicate] = [];
17
23
 
18
24
  const init = async (options) => {
19
25
  if (options) {
20
- const mysql = await need('mysql2/promise');
21
- pool = mysql.createPool(options);
26
+ const _provider = ensureString(options?.provider, { case: 'UP' });
27
+ let port;
28
+ switch (_provider) {
29
+ case MYSQL:
30
+ case 'MARIA':
31
+ case 'MARIADB':
32
+ const mysql = await need('mysql2/promise');
33
+ delete options.provider;
34
+ [provider, pool, port, actExecute, bracket, actDuplicate] = [
35
+ MYSQL, mysql.createPool(options), mysqlDefaultPort,
36
+ 'execute', '`', 'ON DUPLICATE KEY',
37
+ ];
38
+ break;
39
+ case 'PG':
40
+ case 'POSTGRE':
41
+ case 'PSQL':
42
+ case POSTGRESQL:
43
+ // https://node-postgres.com/apis/pool
44
+ const { Pool } = await need('pg');
45
+ [provider, pool, port, actExecute, bracket, actDuplicate] = [
46
+ POSTGRESQL, new Pool(options), postgresqlDefaultPort,
47
+ 'query', '', 'ON CONFLICT DO',
48
+ ];
49
+ break;
50
+ default:
51
+ assert(
52
+ !options?.provider,
53
+ `Invalid database provider: '${options.provider}'.`, 400
54
+ );
55
+ }
22
56
  isPrimary && !options.silent && log(
23
- `Initialized: mysql://${options.user}@${options.host}`
24
- + `:${options.port || defaultPort}/${options.database}`
57
+ `Initialized: ${_provider.toLowerCase()}://${options.user}@`
58
+ + `${options.host}:${options.port || port}/${options.database}`
25
59
  );
26
60
  }
27
61
  assert(pool, 'Database has not been initialized.', 501);
@@ -34,8 +68,9 @@ const end = async (options) => {
34
68
  };
35
69
 
36
70
  const logCommand = (args) => {
37
- if (~~globalThis.debug > 0 && args && args[0]) { log(`SQL: ${args[0]}`); }
38
- if (~~globalThis.debug > 1 && args && args[1]) { console.log(args[1]); };
71
+ const _args = ensureArray(args);
72
+ if (~~globalThis.debug > 0 && _args && _args[0]) { log(`SQL: ${_args[0]}`); }
73
+ if (~~globalThis.debug > 1 && _args && _args[1]) { console.log(_args[1]); };
39
74
  };
40
75
 
41
76
  const getComparison = (val) => Object.isObject(val)
@@ -61,16 +96,28 @@ const rawQuery = async (...args) => {
61
96
 
62
97
  const rawExecute = async (...args) => {
63
98
  const conn = await init();
99
+ console.log(args);
64
100
  logCommand(...args);
65
- return await conn.execute(...args);
101
+ return await conn[actExecute](...args);
66
102
  };
67
103
 
68
- const query = async (...args) => {
69
- return (await rawQuery(...distillArguments(...args)))[0];
104
+ const handleResult = result => {
105
+ switch (provider) {
106
+ case MYSQL: return result[0];
107
+ case POSTGRESQL: return result.rows;
108
+ }
70
109
  };
71
110
 
111
+ const query = async (...args) => handleResult(
112
+ await rawQuery(...distillArguments(...args))
113
+ );
114
+
72
115
  const execute = async (...args) => {
73
- return (await rawExecute(...distillArguments(...args)))[0];
116
+ const resp = await rawExecute(...distillArguments(...args));
117
+ switch (provider) {
118
+ case MYSQL: return resp[0];
119
+ case POSTGRESQL: return resp;
120
+ }
74
121
  };
75
122
 
76
123
  const assertTable = (table, message, status) =>
@@ -92,10 +139,10 @@ const assembleQuery = (table, options) => {
92
139
  const fields = [];
93
140
  ensureArray(options.fields).map((field) => {
94
141
  fields.push(fieldNoQuote.includes(field) || options.noQuote
95
- ? field : `\`${field}\``);
142
+ ? field : quote(field));
96
143
  });
97
144
  if (!fields.length) { fields.push(fieldAny); }
98
- return `SELECT ${fields.join(', ')} FROM \`${table}\``;
145
+ return `SELECT ${join(fields)} FROM ${quote(table)}`;
99
146
  };
100
147
 
101
148
  const rawAssembleKeyValue = (key, value, options) => {
@@ -106,7 +153,7 @@ const rawAssembleKeyValue = (key, value, options) => {
106
153
  assert(value.length, 'Invalid array value.', 500);
107
154
  express = 'IN (?)';
108
155
  } else if (value === null) { express = 'IS ?'; }
109
- return `${options.prefix || ''}\`${key}\` ${express}`;
156
+ return `${options.prefix || ''}${quote(key)} ${express}`;
110
157
  };
111
158
 
112
159
  const assembleKeyValue = (key, value, options) => {
@@ -124,22 +171,26 @@ const assembleSet = (data, options) => {
124
171
  const [isArray, result] = [options.asArray || Array.isArray(data), []];
125
172
  ensureArray(data).map((item) => {
126
173
  assert(Object.keys(item).length, 'Fields are required.', 500);
127
- let [sql, values, dupSql] = [[], [], []];
174
+ let [keys, vals, values, dupSql] = [[], [], [], []];
128
175
  for (let k in item) {
129
- sql.push(`\`${k}\` = ?`);
176
+ keys.push(k);
177
+ switch (provider) {
178
+ case MYSQL: vals.push('?'); break;
179
+ case POSTGRESQL: vals.push(`$${vals.length + 1}`); break;
180
+ }
130
181
  pushValue(values, item[k]);
131
182
  }
132
183
  if (options.upsert) {
133
184
  for (let k in item) {
134
- dupSql.push(`\`${k}\` = ?`);
185
+ dupSql.push(`${quote(k)} = ?`);
135
186
  pushValue(values, item[k]);
136
187
  }
137
- dupSql = ` ON DUPLICATE KEY UPDATE ${dupSql.join(', ')}`;
188
+ dupSql = ` ${actDuplicate} UPDATE ${join(dupSql)}`;
138
189
  } else { dupSql = ''; }
139
- result.push({
140
- sql: `${options.prefix || ''}SET ${sql.join(', ')}${dupSql}`,
141
- values, object: item,
142
- });
190
+ const sql = `${options.prefix || ''}`
191
+ + `(${join(keys.map(quote))}) VALUES (${join(vals)})${dupSql}`
192
+ + `${options.subfix || ''}`
193
+ result.push({ sql, values, object: item });
143
194
  });
144
195
  return isArray ? result : result[0];
145
196
  };
@@ -147,20 +198,20 @@ const assembleSet = (data, options) => {
147
198
  const assembleInsert = (table, data, options) => {
148
199
  options = options || {};
149
200
  assertTable(table);
150
- options.prefix = `INSERT INTO \`${table}\` `;
201
+ options.prefix = `INSERT INTO ${quote(table)} `;
151
202
  return assembleSet(data, options);
152
203
  };
153
204
 
154
205
  const assembleUpdate = (table, data, options) => {
155
206
  options = options || {};
156
207
  assertTable(table);
157
- options.prefix = `UPDATE \`${table}\` `;
208
+ options.prefix = `UPDATE ${quote(table)} `;
158
209
  return assembleSet(data, options);
159
210
  };
160
211
 
161
212
  const assembleDelete = (table) => {
162
213
  assertTable(table);
163
- return `DELETE FROM \`${table}\``;
214
+ return `DELETE FROM ${quote(table)}`;
164
215
  };
165
216
 
166
217
  const assembleTail = (options) => {
@@ -168,15 +219,28 @@ const assembleTail = (options) => {
168
219
  ensureArray(options?.order || []).map?.(x => {
169
220
  const ord = Object.isObject(x) ? x : { [x]: '+' };
170
221
  const key = Object.keys(ord)[0];
171
- sort.push(`\`${key}\` ${getOrder(ord[key])}`);
222
+ sort.push(`${quote(key)} ${getOrder(ord[key])}`);
172
223
  });
173
- return (sort.length ? ` ORDER BY ${sort.join(', ')}` : '')
224
+ return (sort.length ? ` ORDER BY ${join(sort)}` : '')
174
225
  + (~~options?.limit ? ` LIMIT ${~~options.limit}` : '');
175
226
  };
176
227
 
177
228
  const tables = async (options) => {
178
- const resp = await query(`SHOW TABLES`);
179
- return options?.raw ? resp : resp.map(x => Object.values(x)[0]);
229
+ let sql = '';
230
+ switch (provider) {
231
+ case MYSQL:
232
+ sql = 'SHOW TABLES';
233
+ break;
234
+ case POSTGRESQL:
235
+ sql = "SELECT * FROM information_schema.tables WHERE table_schema = 'public'";
236
+ }
237
+ const resp = await query(sql);
238
+ return options?.raw ? resp : resp.map(x => {
239
+ switch (provider) {
240
+ case MYSQL: return Object.values(x)[0];
241
+ case POSTGRESQL: return x.table_name;
242
+ }
243
+ });
180
244
  };
181
245
 
182
246
  const desc = async (table, options) => {
@@ -198,7 +262,7 @@ const indexes = async (table, options) => {
198
262
  const drop = async (table, options) => {
199
263
  assertTable(table);
200
264
  assertForce(options);
201
- return await query(`DROP TABLE IF EXISTS ??`, [table]);
265
+ return await query('DROP TABLE IF EXISTS ??', [table]);
202
266
  };
203
267
 
204
268
  const queryAll = (table, options) =>
@@ -223,20 +287,32 @@ const insert = async (table, fields, options) => {
223
287
  options = options || {};
224
288
  let [isArray, key, ids, error, result]
225
289
  = [Array.isArray(fields), defaultKey(options), [], [], []];
290
+ const subfix = provider === POSTGRESQL ? (options.skipEcho ? (
291
+ options.key ? ` RETURNING ${key}` : ''
292
+ ) : ` RETURNING *`) : '';
226
293
  for (let item of assembleInsert(
227
- table, fields, { ...options, asArray: true }
228
- )) {
294
+ table, fields, { ...options, asArray: true, subfix })
295
+ ) {
229
296
  try {
230
297
  const resp = await execute(item.sql, item.values);
298
+ if (provider === POSTGRESQL) {
299
+ resp.affectedRows = resp.rowCount;
300
+ }
231
301
  resp.key = key;
232
- resp.insertId = !resp.insertId && item.object[key]
233
- ? item.object[key] : resp.insertId;
302
+ !resp.insertId && item.object[key]
303
+ && (resp.insertId = item.object[key]);
234
304
  result.push(resp);
235
305
  ids.push(resp.insertId);
236
306
  } catch (err) { error.push(err); }
237
307
  }
238
308
  if (!options.skipEcho && ids.length) {
239
- result = await queryById(table, ids, options);
309
+ switch (provider) {
310
+ case MYSQL:
311
+ result = await queryById(table, ids, options);
312
+ break;
313
+ case POSTGRESQL:
314
+ result = result.map(x => x.rows).flat();
315
+ }
240
316
  }
241
317
  if (!isArray) {
242
318
  if (error.length) { throw error[0]; }
package/lib/manifest.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  const manifest = {
2
2
  "name": "utilitas",
3
3
  "description": "Just another common utility for JavaScript.",
4
- "version": "1995.2.7",
4
+ "version": "1995.2.9",
5
5
  "private": false,
6
6
  "homepage": "https://github.com/Leask/utilitas",
7
7
  "main": "index.mjs",
@@ -28,7 +28,7 @@ const manifest = {
28
28
  "@google-cloud/text-to-speech": "^5.0.1",
29
29
  "@google-cloud/vision": "^4.0.2",
30
30
  "@mozilla/readability": "^0.4.4",
31
- "@sentry/node": "^7.80.1",
31
+ "@sentry/node": "^7.83.0",
32
32
  "@waylaidwanderer/chatgpt-api": "^1.37.3",
33
33
  "acme-client": "^5.0.0",
34
34
  "browserify-fs": "^1.0.0",
@@ -37,24 +37,26 @@ const manifest = {
37
37
  "fluent-ffmpeg": "^2.1.2",
38
38
  "form-data": "^4.0.0",
39
39
  "ioredis": "^5.3.2",
40
- "jsdom": "^22.1.0",
40
+ "jsdom": "^23.0.0",
41
41
  "lorem-ipsum": "^2.0.8",
42
42
  "mailgun.js": "^9.3.0",
43
43
  "mime-types": "^2.1.35",
44
- "mysql2": "^3.6.3",
45
- "node-mailjet": "^6.0.4",
44
+ "mysql2": "^3.6.5",
45
+ "node-mailjet": "^6.0.5",
46
46
  "node-polyfill-webpack-plugin": "^2.0.1",
47
47
  "office-text-extractor": "^3.0.2",
48
- "openai": "^4.19.0",
48
+ "openai": "^4.20.1",
49
+ "pdfjs-dist": "^4.0.269",
50
+ "pg": "^8.11.3",
49
51
  "ping": "^0.4.4",
50
52
  "say": "^0.16.0",
51
- "telegraf": "^4.15.0",
53
+ "telegraf": "^4.15.2",
52
54
  "telesignsdk": "^2.2.3",
53
55
  "tesseract.js": "^5.0.3",
54
56
  "twilio": "^4.19.0",
55
57
  "url": "github:Leask/node-url",
56
58
  "webpack-cli": "^5.1.4",
57
- "whisper-node": "^0.2.12",
59
+ "whisper-node": "^1.1.1",
58
60
  "youtube-transcript": "^1.0.6"
59
61
  }
60
62
  };
package/lib/shot.mjs CHANGED
@@ -122,14 +122,12 @@ const get = async (url, options) => {
122
122
  case _JSON:
123
123
  content = parseJson(buf2utf(buffer), null);
124
124
  break;
125
- case 'TEXT':
126
- content = buf2utf(buffer);
127
- break;
128
125
  case _PARSED:
129
126
  content = await distillHtml(buf2utf(buffer));
130
127
  break;
131
128
  default:
132
129
  assert(!options.encode, 'Invalid encoding.', 400);
130
+ case 'TEXT':
133
131
  content = buf2utf(buffer);
134
132
  }
135
133
  }
package/lib/vision.mjs CHANGED
@@ -1,6 +1,3 @@
1
- import { getApiKeyCredentials } from './encryption.mjs';
2
- import get from './shot.mjs';
3
-
4
1
  import {
5
2
  convert, deleteOnCloud, downloadFromCloud, getIdByGs, uploadToCloud,
6
3
  } from './storage.mjs';
@@ -9,7 +6,9 @@ import {
9
6
  ensureArray, ignoreErrFunc, log as _log, need, throwError, trim,
10
7
  } from './utilitas.mjs';
11
8
 
12
- const _NEED = ['@google-cloud/vision', 'tesseract.js'];
9
+ import { getApiKeyCredentials } from './encryption.mjs';
10
+
11
+ const _NEED = ['@google-cloud/vision', 'pdfjs-dist', 'tesseract.js'];
13
12
  const [BASE64, BUFFER, FILE, DEFAULT_LANG] = ['BASE64', 'BUFFER', 'FILE', 'eng'];
14
13
  const ceil = num => num.toFixed(4);
15
14
  const errorMessage = 'Invalid image data.';
@@ -128,6 +127,12 @@ const see = async (image, options) => {
128
127
 
129
128
  const read = async (image, options) => {
130
129
  assert(client, 'Vision API has not been initialized.', 500);
130
+ if (options?.allPages) {
131
+ assert(options?.input === FILE, 'Only file input is supported.', 400);
132
+ if ((await getPdfInfo(image)).numPages > pages.length) {
133
+ return await readAll(image, options);
134
+ }
135
+ }
131
136
  const content = await convert(image, {
132
137
  input: options?.input, expected: BASE64, errorMessage,
133
138
  });
@@ -163,9 +168,45 @@ const readAll = async (image, options) => {
163
168
  ).flat();
164
169
  };
165
170
 
171
+ const getPdfPage = async (doc, pageNum) => {
172
+ const page = await doc.getPage(pageNum);
173
+ const viewport = page.getViewport({ scale: 1.0 });
174
+ const result = {
175
+ pageNum: pageNum,
176
+ width: viewport.width,
177
+ height: viewport.height,
178
+ content: (await page.getTextContent()).items.map(x => x.str).join(' '),
179
+ };
180
+ page.cleanup();
181
+ return result
182
+ };
183
+
184
+ const getPdfPages = async (doc) => {
185
+ const result = [];
186
+ for (let i = 1; i <= doc.numPages; i++) { result.push(getPdfPage(doc, i)); }
187
+ return await Promise.all(result);
188
+ };
189
+
190
+ // https://github.com/mozilla/pdf.js/blob/master/examples/node/getinfo.mjs
191
+ const getPdfInfo = async (file, options) => {
192
+ const { getDocument } = await need('pdfjs-dist');
193
+ const doc = await getDocument(file).promise;
194
+ const data = await doc.getMetadata();
195
+ const result = {
196
+ numPages: doc.numPages,
197
+ info: data.info,
198
+ metadata: { ...data.metadata.getAll() },
199
+ pages: options?.withPages ? await getPdfPages(doc) : null,
200
+ };
201
+ return result;
202
+ };
203
+
166
204
  export {
167
205
  _NEED,
168
206
  annotateImage,
207
+ getPdfInfo,
208
+ getPdfPage,
209
+ getPdfPages,
169
210
  init,
170
211
  ocrImage,
171
212
  ocrImageGoogle,
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "utilitas",
3
3
  "description": "Just another common utility for JavaScript.",
4
- "version": "1995.2.7",
4
+ "version": "1995.2.9",
5
5
  "private": false,
6
6
  "homepage": "https://github.com/Leask/utilitas",
7
7
  "main": "index.mjs",
@@ -39,7 +39,7 @@
39
39
  "@google-cloud/text-to-speech": "^5.0.1",
40
40
  "@google-cloud/vision": "^4.0.2",
41
41
  "@mozilla/readability": "^0.4.4",
42
- "@sentry/node": "^7.80.1",
42
+ "@sentry/node": "^7.83.0",
43
43
  "@waylaidwanderer/chatgpt-api": "^1.37.3",
44
44
  "acme-client": "^5.0.0",
45
45
  "browserify-fs": "^1.0.0",
@@ -48,24 +48,26 @@
48
48
  "fluent-ffmpeg": "^2.1.2",
49
49
  "form-data": "^4.0.0",
50
50
  "ioredis": "^5.3.2",
51
- "jsdom": "^22.1.0",
51
+ "jsdom": "^23.0.0",
52
52
  "lorem-ipsum": "^2.0.8",
53
53
  "mailgun.js": "^9.3.0",
54
54
  "mime-types": "^2.1.35",
55
- "mysql2": "^3.6.3",
56
- "node-mailjet": "^6.0.4",
55
+ "mysql2": "^3.6.5",
56
+ "node-mailjet": "^6.0.5",
57
57
  "node-polyfill-webpack-plugin": "^2.0.1",
58
58
  "office-text-extractor": "^3.0.2",
59
- "openai": "^4.19.0",
59
+ "openai": "^4.20.1",
60
+ "pdfjs-dist": "^4.0.269",
61
+ "pg": "^8.11.3",
60
62
  "ping": "^0.4.4",
61
63
  "say": "^0.16.0",
62
- "telegraf": "^4.15.0",
64
+ "telegraf": "^4.15.2",
63
65
  "telesignsdk": "^2.2.3",
64
66
  "tesseract.js": "^5.0.3",
65
67
  "twilio": "^4.19.0",
66
68
  "url": "github:Leask/node-url",
67
69
  "webpack-cli": "^5.1.4",
68
- "whisper-node": "^0.2.12",
70
+ "whisper-node": "^1.1.1",
69
71
  "youtube-transcript": "^1.0.6"
70
72
  }
71
73
  }