@hesed/mysql 0.5.2 → 0.6.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/README.md CHANGED
@@ -26,7 +26,7 @@ $ npm install -g @hesed/mysql
26
26
  $ mq COMMAND
27
27
  running command...
28
28
  $ mq (--version)
29
- @hesed/mysql/0.5.2 linux-x64 node-v24.17.0
29
+ @hesed/mysql/0.6.0 linux-x64 node-v24.18.0
30
30
  $ mq --help [COMMAND]
31
31
  USAGE
32
32
  $ mq COMMAND
@@ -57,16 +57,18 @@ Add MySQL authentication
57
57
  ```
58
58
  USAGE
59
59
  $ mq mysql auth add -p <value> --host <value> --port <value> -u <value> --password <value> -d <value> --ssl
60
- [--json]
60
+ --maxConcurrentQueries <value> --queryQueueTimeoutMs <value> [--json]
61
61
 
62
62
  FLAGS
63
- -d, --database=<value> (required) Database name
64
- -p, --profile=<value> (required) Profile name
65
- -u, --user=<value> (required) Username
66
- --host=<value> (required) MySQL host
67
- --password=<value> (required) Password
68
- --port=<value> (required) MySQL port
69
- --ssl (required) Use SSL
63
+ -d, --database=<value> (required) Database name
64
+ -p, --profile=<value> (required) Profile name
65
+ -u, --user=<value> (required) Username
66
+ --host=<value> (required) MySQL host
67
+ --maxConcurrentQueries=<value> (required) Max concurrent queries for this profile
68
+ --password=<value> (required) Password
69
+ --port=<value> (required) MySQL port
70
+ --queryQueueTimeoutMs=<value> (required) Milliseconds a query may wait for a free query slot before failing
71
+ --ssl (required) Use SSL
70
72
 
71
73
  GLOBAL FLAGS
72
74
  --json Format output as json.
@@ -80,7 +82,7 @@ EXAMPLES
80
82
  $ mq mysql auth add -p prod
81
83
  ```
82
84
 
83
- _See code: [src/commands/mysql/auth/add.ts](https://github.com/hesedcasa/mysql/blob/v0.5.2/src/commands/mysql/auth/add.ts)_
85
+ _See code: [src/commands/mysql/auth/add.ts](https://github.com/hesedcasa/mysql/blob/v0.6.0/src/commands/mysql/auth/add.ts)_
84
86
 
85
87
  ## `mq mysql auth delete`
86
88
 
@@ -105,7 +107,7 @@ EXAMPLES
105
107
  $ mq mysql auth delete -p prod
106
108
  ```
107
109
 
108
- _See code: [src/commands/mysql/auth/delete.ts](https://github.com/hesedcasa/mysql/blob/v0.5.2/src/commands/mysql/auth/delete.ts)_
110
+ _See code: [src/commands/mysql/auth/delete.ts](https://github.com/hesedcasa/mysql/blob/v0.6.0/src/commands/mysql/auth/delete.ts)_
109
111
 
110
112
  ## `mq mysql auth list`
111
113
 
@@ -125,7 +127,7 @@ EXAMPLES
125
127
  $ mq mysql auth list
126
128
  ```
127
129
 
128
- _See code: [src/commands/mysql/auth/list.ts](https://github.com/hesedcasa/mysql/blob/v0.5.2/src/commands/mysql/auth/list.ts)_
130
+ _See code: [src/commands/mysql/auth/list.ts](https://github.com/hesedcasa/mysql/blob/v0.6.0/src/commands/mysql/auth/list.ts)_
129
131
 
130
132
  ## `mq mysql auth profile`
131
133
 
@@ -150,7 +152,7 @@ EXAMPLES
150
152
  $ mq mysql auth profile --default test
151
153
  ```
152
154
 
153
- _See code: [src/commands/mysql/auth/profile.ts](https://github.com/hesedcasa/mysql/blob/v0.5.2/src/commands/mysql/auth/profile.ts)_
155
+ _See code: [src/commands/mysql/auth/profile.ts](https://github.com/hesedcasa/mysql/blob/v0.6.0/src/commands/mysql/auth/profile.ts)_
154
156
 
155
157
  ## `mq mysql auth test`
156
158
 
@@ -175,7 +177,7 @@ EXAMPLES
175
177
  $ mq mysql auth test -p prod
176
178
  ```
177
179
 
178
- _See code: [src/commands/mysql/auth/test.ts](https://github.com/hesedcasa/mysql/blob/v0.5.2/src/commands/mysql/auth/test.ts)_
180
+ _See code: [src/commands/mysql/auth/test.ts](https://github.com/hesedcasa/mysql/blob/v0.6.0/src/commands/mysql/auth/test.ts)_
179
181
 
180
182
  ## `mq mysql auth update`
181
183
 
@@ -184,16 +186,18 @@ Update MySQL authentication
184
186
  ```
185
187
  USAGE
186
188
  $ mq mysql auth update -p <value> --host <value> --port <value> -u <value> --password <value> -d <value> --ssl
187
- [--json]
189
+ --maxConcurrentQueries <value> --queryQueueTimeoutMs <value> [--json]
188
190
 
189
191
  FLAGS
190
- -d, --database=<value> (required) Database name
191
- -p, --profile=<value> (required) Profile name
192
- -u, --user=<value> (required) Username
193
- --host=<value> (required) MySQL host
194
- --password=<value> (required) Password
195
- --port=<value> (required) MySQL port
196
- --ssl (required) Use SSL
192
+ -d, --database=<value> (required) Database name
193
+ -p, --profile=<value> (required) Profile name
194
+ -u, --user=<value> (required) Username
195
+ --host=<value> (required) MySQL host
196
+ --maxConcurrentQueries=<value> (required) Max concurrent queries for this profile
197
+ --password=<value> (required) Password
198
+ --port=<value> (required) MySQL port
199
+ --queryQueueTimeoutMs=<value> (required) Milliseconds a query may wait for a free query slot before failing
200
+ --ssl (required) Use SSL
197
201
 
198
202
  GLOBAL FLAGS
199
203
  --json Format output as json.
@@ -207,7 +211,7 @@ EXAMPLES
207
211
  $ mq mysql auth update -p test
208
212
  ```
209
213
 
210
- _See code: [src/commands/mysql/auth/update.ts](https://github.com/hesedcasa/mysql/blob/v0.5.2/src/commands/mysql/auth/update.ts)_
214
+ _See code: [src/commands/mysql/auth/update.ts](https://github.com/hesedcasa/mysql/blob/v0.6.0/src/commands/mysql/auth/update.ts)_
211
215
 
212
216
  ## `mq mysql databases`
213
217
 
@@ -232,7 +236,7 @@ EXAMPLES
232
236
  $ mq mysql databases -p staging
233
237
  ```
234
238
 
235
- _See code: [src/commands/mysql/databases.ts](https://github.com/hesedcasa/mysql/blob/v0.5.2/src/commands/mysql/databases.ts)_
239
+ _See code: [src/commands/mysql/databases.ts](https://github.com/hesedcasa/mysql/blob/v0.6.0/src/commands/mysql/databases.ts)_
236
240
 
237
241
  ## `mq mysql describe-table TABLE`
238
242
 
@@ -261,7 +265,7 @@ EXAMPLES
261
265
  $ mq mysql describe-table orders --toon -p prod
262
266
  ```
263
267
 
264
- _See code: [src/commands/mysql/describe-table.ts](https://github.com/hesedcasa/mysql/blob/v0.5.2/src/commands/mysql/describe-table.ts)_
268
+ _See code: [src/commands/mysql/describe-table.ts](https://github.com/hesedcasa/mysql/blob/v0.6.0/src/commands/mysql/describe-table.ts)_
265
269
 
266
270
  ## `mq mysql explain QUERY`
267
271
 
@@ -290,7 +294,7 @@ EXAMPLES
290
294
  $ mq mysql explain "SELECT * FROM orders JOIN users ON orders.user_id = users.id" --json
291
295
  ```
292
296
 
293
- _See code: [src/commands/mysql/explain.ts](https://github.com/hesedcasa/mysql/blob/v0.5.2/src/commands/mysql/explain.ts)_
297
+ _See code: [src/commands/mysql/explain.ts](https://github.com/hesedcasa/mysql/blob/v0.6.0/src/commands/mysql/explain.ts)_
294
298
 
295
299
  ## `mq mysql indexes TABLE`
296
300
 
@@ -319,7 +323,7 @@ EXAMPLES
319
323
  $ mq mysql indexes orders --json -p prod
320
324
  ```
321
325
 
322
- _See code: [src/commands/mysql/indexes.ts](https://github.com/hesedcasa/mysql/blob/v0.5.2/src/commands/mysql/indexes.ts)_
326
+ _See code: [src/commands/mysql/indexes.ts](https://github.com/hesedcasa/mysql/blob/v0.6.0/src/commands/mysql/indexes.ts)_
323
327
 
324
328
  ## `mq mysql query QUERY`
325
329
 
@@ -351,7 +355,7 @@ EXAMPLES
351
355
  $ mq mysql query "DELETE FROM sessions" -p prod --skip-confirmation
352
356
  ```
353
357
 
354
- _See code: [src/commands/mysql/query.ts](https://github.com/hesedcasa/mysql/blob/v0.5.2/src/commands/mysql/query.ts)_
358
+ _See code: [src/commands/mysql/query.ts](https://github.com/hesedcasa/mysql/blob/v0.6.0/src/commands/mysql/query.ts)_
355
359
 
356
360
  ## `mq mysql tables`
357
361
 
@@ -376,5 +380,5 @@ EXAMPLES
376
380
  $ mq mysql tables -p local
377
381
  ```
378
382
 
379
- _See code: [src/commands/mysql/tables.ts](https://github.com/hesedcasa/mysql/blob/v0.5.2/src/commands/mysql/tables.ts)_
383
+ _See code: [src/commands/mysql/tables.ts](https://github.com/hesedcasa/mysql/blob/v0.6.0/src/commands/mysql/tables.ts)_
380
384
  <!-- commandsstop -->
@@ -7,6 +7,20 @@ const fields = [
7
7
  { description: 'Password', name: 'password', type: 'string' },
8
8
  { char: 'd', description: 'Database name', name: 'database', type: 'string' },
9
9
  { default: false, description: 'Use SSL', name: 'ssl', required: false, type: 'boolean' },
10
+ {
11
+ default: 5,
12
+ description: 'Max concurrent queries for this profile',
13
+ name: 'maxConcurrentQueries',
14
+ required: false,
15
+ type: 'number',
16
+ },
17
+ {
18
+ default: 60_000,
19
+ description: 'Milliseconds a query may wait for a free query slot before failing',
20
+ name: 'queryQueueTimeoutMs',
21
+ required: false,
22
+ type: 'number',
23
+ },
10
24
  ];
11
25
  export default createAuthAddCommand({
12
26
  clearClients: closeConnections,
@@ -7,6 +7,20 @@ const fields = [
7
7
  { description: 'Password', name: 'password', type: 'string' },
8
8
  { char: 'd', description: 'Database name', name: 'database', type: 'string' },
9
9
  { default: false, description: 'Use SSL', name: 'ssl', required: false, type: 'boolean' },
10
+ {
11
+ default: 5,
12
+ description: 'Max concurrent queries for this profile',
13
+ name: 'maxConcurrentQueries',
14
+ required: false,
15
+ type: 'number',
16
+ },
17
+ {
18
+ default: 60_000,
19
+ description: 'Milliseconds a query may wait for a free query slot before failing',
20
+ name: 'queryQueueTimeoutMs',
21
+ required: false,
22
+ type: 'number',
23
+ },
10
24
  ];
11
25
  export default createAuthUpdateCommand({
12
26
  clearClients: closeConnections,
@@ -2,14 +2,18 @@ import type { ConnectionOptions as MySQL2ConnectionOptions } from 'mysql2/promis
2
2
  export interface DatabaseProfile {
3
3
  database: string;
4
4
  host: string;
5
+ maxConcurrentQueries?: number;
5
6
  password: string;
6
7
  port: number;
8
+ queryQueueTimeoutMs?: number;
7
9
  ssl?: boolean;
8
10
  user: string;
9
11
  }
10
12
  interface SafetyConfig {
11
13
  blacklistedOperations: string[];
12
14
  defaultLimit: number;
15
+ maxConcurrentQueries?: number;
16
+ queryQueueTimeoutMs?: number;
13
17
  requireConfirmationFor: string[];
14
18
  }
15
19
  export interface MySQLConfig {
@@ -5,6 +5,8 @@ let cachedConfig = null;
5
5
  const DEFAULT_SAFETY_CONFIG = {
6
6
  blacklistedOperations: ['DROP DATABASE'],
7
7
  defaultLimit: 100,
8
+ maxConcurrentQueries: 5,
9
+ queryQueueTimeoutMs: 60_000,
8
10
  requireConfirmationFor: ['DELETE', 'UPDATE', 'DROP', 'TRUNCATE', 'ALTER'],
9
11
  };
10
12
  async function initMySQL(config) {
@@ -2,7 +2,8 @@ import type { MySQLConfig } from './config-loader.js';
2
2
  import type { ConnectionTestResult, DatabaseListResult, DatabaseUtil, ExplainResult, IndexResult, OutputFormat, QueryResult, TableListResult, TableStructureResult } from './database.js';
3
3
  export declare class MySQLUtil implements DatabaseUtil {
4
4
  private config;
5
- private connections;
5
+ private pools;
6
+ private querySlots;
6
7
  constructor(config: MySQLConfig);
7
8
  closeAll(): Promise<void>;
8
9
  describeTable(profileName: string, table: string, format?: 'json' | 'table' | 'toon'): Promise<TableStructureResult>;
@@ -12,8 +13,11 @@ export declare class MySQLUtil implements DatabaseUtil {
12
13
  listTables(profileName: string): Promise<TableListResult>;
13
14
  showIndexes(profileName: string, table: string, format?: 'json' | 'table' | 'toon'): Promise<IndexResult>;
14
15
  testConnection(profileName: string): Promise<ConnectionTestResult>;
16
+ private acquireQuerySlot;
15
17
  private formatReadResult;
16
18
  private formatRows;
17
19
  private formatWriteResult;
18
- private getConnection;
20
+ private getPool;
21
+ private getQueryLimit;
22
+ private runQuery;
19
23
  }
@@ -2,22 +2,32 @@ import mysql from 'mysql2/promise';
2
2
  import { getMySQLConnectionOptions } from './config-loader.js';
3
3
  import { FORMATTERS } from './formatters.js';
4
4
  import { analyzeQuery, applyDefaultLimit, checkBlacklist, getQueryType, requiresConfirmation } from './query-validator.js';
5
+ const DEFAULT_MAX_CONCURRENT_QUERIES = 5;
6
+ const DEFAULT_QUEUE_TIMEOUT_MS = 60_000;
5
7
  export class MySQLUtil {
6
8
  config;
7
- connections;
9
+ pools;
10
+ querySlots;
8
11
  constructor(config) {
9
12
  this.config = config;
10
- this.connections = new Map();
13
+ this.pools = new Map();
14
+ this.querySlots = new Map();
11
15
  }
12
16
  async closeAll() {
13
- const entries = [...this.connections.values()];
14
- this.connections.clear();
15
- await Promise.allSettled(entries.map(async (connPromise) => (await connPromise).end()));
17
+ // Reject queued queries first so nothing waits forever on a closed util.
18
+ for (const slot of this.querySlots.values()) {
19
+ for (const waiter of slot.waiting.splice(0)) {
20
+ waiter.reject(new Error('Connections were closed while the query was waiting for a free slot'));
21
+ }
22
+ }
23
+ this.querySlots.clear();
24
+ const pools = [...this.pools.values()];
25
+ this.pools.clear();
26
+ await Promise.allSettled(pools.map((pool) => pool.end()));
16
27
  }
17
28
  async describeTable(profileName, table, format = 'table') {
18
29
  try {
19
- const connection = await this.getConnection(profileName);
20
- const [rows, fields] = await connection.query(`DESCRIBE ${table}`);
30
+ const [rows, fields] = await this.runQuery(profileName, `DESCRIBE ${table}`);
21
31
  return {
22
32
  data: {
23
33
  result: this.formatRows(rows, fields, format),
@@ -72,8 +82,7 @@ export class MySQLUtil {
72
82
  }
73
83
  }
74
84
  try {
75
- const connection = await this.getConnection(profileName);
76
- const [rows, fields] = await connection.query(finalQuery);
85
+ const [rows, fields] = await this.runQuery(profileName, finalQuery);
77
86
  const isRead = queryType === 'SELECT' || queryType === 'SHOW' || queryType === 'DESCRIBE' || queryType === 'EXPLAIN';
78
87
  let data = isRead
79
88
  ? this.formatReadResult(rows, fields, format, notices)
@@ -102,8 +111,7 @@ export class MySQLUtil {
102
111
  }
103
112
  async explainQuery(profileName, query, format = 'table') {
104
113
  try {
105
- const connection = await this.getConnection(profileName);
106
- const [rows, fields] = await connection.query(`EXPLAIN ${query}`);
114
+ const [rows, fields] = await this.runQuery(profileName, `EXPLAIN ${query}`);
107
115
  return {
108
116
  data: {
109
117
  plan: rows,
@@ -122,8 +130,7 @@ export class MySQLUtil {
122
130
  }
123
131
  async listDatabases(profileName) {
124
132
  try {
125
- const connection = await this.getConnection(profileName);
126
- const [rows] = await connection.query('SHOW DATABASES');
133
+ const [rows] = await this.runQuery(profileName, 'SHOW DATABASES');
127
134
  const databases = rows.map((row) => row.Database);
128
135
  return {
129
136
  data: {
@@ -143,8 +150,7 @@ export class MySQLUtil {
143
150
  }
144
151
  async listTables(profileName) {
145
152
  try {
146
- const connection = await this.getConnection(profileName);
147
- const [rows] = await connection.query('SHOW TABLES');
153
+ const [rows] = await this.runQuery(profileName, 'SHOW TABLES');
148
154
  const rowsArray = rows;
149
155
  const tableKey = Object.keys(rowsArray[0])[0];
150
156
  const tables = rowsArray.map((row) => row[tableKey]);
@@ -166,8 +172,7 @@ export class MySQLUtil {
166
172
  }
167
173
  async showIndexes(profileName, table, format = 'table') {
168
174
  try {
169
- const connection = await this.getConnection(profileName);
170
- const [rows, fields] = await connection.query(`SHOW INDEXES FROM ${table}`);
175
+ const [rows, fields] = await this.runQuery(profileName, `SHOW INDEXES FROM ${table}`);
171
176
  return {
172
177
  data: {
173
178
  indexes: rows,
@@ -186,8 +191,7 @@ export class MySQLUtil {
186
191
  }
187
192
  async testConnection(profileName) {
188
193
  try {
189
- const connection = await this.getConnection(profileName);
190
- const [rows] = await connection.query('SELECT VERSION() as version, DATABASE() as current_database');
194
+ const [rows] = await this.runQuery(profileName, 'SELECT VERSION() as version, DATABASE() as current_database');
191
195
  const info = rows[0];
192
196
  return {
193
197
  data: {
@@ -206,6 +210,56 @@ export class MySQLUtil {
206
210
  };
207
211
  }
208
212
  }
213
+ // Grants a query slot for the profile, or waits until one frees up. The
214
+ // returned release callback must be invoked exactly once per acquisition.
215
+ acquireQuerySlot(profileName) {
216
+ const limit = this.getQueryLimit(profileName);
217
+ let slot = this.querySlots.get(profileName);
218
+ if (!slot) {
219
+ slot = { active: 0, waiting: [] };
220
+ this.querySlots.set(profileName, slot);
221
+ }
222
+ const state = slot;
223
+ const release = () => {
224
+ const next = state.waiting.shift();
225
+ if (next) {
226
+ next.grant();
227
+ }
228
+ else {
229
+ state.active -= 1;
230
+ }
231
+ };
232
+ if (state.active < limit) {
233
+ state.active += 1;
234
+ return Promise.resolve(release);
235
+ }
236
+ const timeoutMs = this.config.profiles[profileName]?.queryQueueTimeoutMs ??
237
+ this.config.safety.queryQueueTimeoutMs ??
238
+ DEFAULT_QUEUE_TIMEOUT_MS;
239
+ process.stderr.write(`Waiting for a free query slot (${limit}/${limit} in use for profile "${profileName}")...\n`);
240
+ return new Promise((resolve, reject) => {
241
+ const waiter = {
242
+ grant() {
243
+ clearTimeout(timer);
244
+ resolve(release);
245
+ },
246
+ reject(error) {
247
+ clearTimeout(timer);
248
+ reject(error);
249
+ },
250
+ };
251
+ const timer = setTimeout(() => {
252
+ const index = state.waiting.indexOf(waiter);
253
+ if (index !== -1)
254
+ state.waiting.splice(index, 1);
255
+ reject(new Error(`Timed out after ${timeoutMs / 1000}s waiting for a free query slot ` +
256
+ `(limit: ${limit} concurrent queries for profile "${profileName}")`));
257
+ }, timeoutMs);
258
+ // Don't let a pending queue timer keep the CLI process alive.
259
+ timer.unref?.();
260
+ state.waiting.push(waiter);
261
+ });
262
+ }
209
263
  formatReadResult(rows, fields, format, notices) {
210
264
  const rowCount = Array.isArray(rows) ? rows.length : 0;
211
265
  notices.push(`Query executed successfully. Rows returned: ${rowCount}`);
@@ -231,26 +285,37 @@ export class MySQLUtil {
231
285
  data += `Insert ID: ${insertId}\n`;
232
286
  return data;
233
287
  }
234
- async getConnection(profileName) {
235
- const existing = this.connections.get(profileName);
236
- if (existing) {
237
- try {
238
- const conn = await existing;
239
- await conn.ping();
240
- return conn;
241
- }
242
- catch {
243
- this.connections.delete(profileName);
244
- }
245
- }
246
- const connPromise = mysql.createConnection(getMySQLConnectionOptions(this.config, profileName));
247
- this.connections.set(profileName, connPromise);
288
+ // The pool is sized to the profile's query limit so slot holders each get a
289
+ // real physical connection — a single Connection would serialize commands on
290
+ // the wire and make the concurrency limit meaningless.
291
+ getPool(profileName) {
292
+ const existing = this.pools.get(profileName);
293
+ if (existing)
294
+ return existing;
295
+ const pool = mysql.createPool({
296
+ ...getMySQLConnectionOptions(this.config, profileName),
297
+ connectionLimit: this.getQueryLimit(profileName),
298
+ waitForConnections: true,
299
+ });
300
+ this.pools.set(profileName, pool);
301
+ return pool;
302
+ }
303
+ getQueryLimit(profileName) {
304
+ const configuredLimit = this.config.profiles[profileName]?.maxConcurrentQueries ??
305
+ this.config.safety.maxConcurrentQueries ??
306
+ DEFAULT_MAX_CONCURRENT_QUERIES;
307
+ // A limit below 1 would leave every query waiting forever.
308
+ return Math.max(1, configuredLimit);
309
+ }
310
+ // All queries go through here so concurrent load on the same profile is
311
+ // capped at maxConcurrentQueries; excess queries wait for a free slot.
312
+ async runQuery(profileName, sql) {
313
+ const release = await this.acquireQuerySlot(profileName);
248
314
  try {
249
- return await connPromise;
315
+ return (await this.getPool(profileName).query(sql));
250
316
  }
251
- catch (error) {
252
- this.connections.delete(profileName);
253
- throw error;
317
+ finally {
318
+ release();
254
319
  }
255
320
  }
256
321
  }
@@ -384,6 +384,22 @@
384
384
  "required": true,
385
385
  "allowNo": false,
386
386
  "type": "boolean"
387
+ },
388
+ "maxConcurrentQueries": {
389
+ "description": "Max concurrent queries for this profile",
390
+ "name": "maxConcurrentQueries",
391
+ "required": true,
392
+ "hasDynamicHelp": false,
393
+ "multiple": false,
394
+ "type": "option"
395
+ },
396
+ "queryQueueTimeoutMs": {
397
+ "description": "Milliseconds a query may wait for a free query slot before failing",
398
+ "name": "queryQueueTimeoutMs",
399
+ "required": true,
400
+ "hasDynamicHelp": false,
401
+ "multiple": false,
402
+ "type": "option"
387
403
  }
388
404
  },
389
405
  "hasDynamicHelp": false,
@@ -637,6 +653,22 @@
637
653
  "required": true,
638
654
  "allowNo": false,
639
655
  "type": "boolean"
656
+ },
657
+ "maxConcurrentQueries": {
658
+ "description": "Max concurrent queries for this profile",
659
+ "name": "maxConcurrentQueries",
660
+ "required": true,
661
+ "hasDynamicHelp": false,
662
+ "multiple": false,
663
+ "type": "option"
664
+ },
665
+ "queryQueueTimeoutMs": {
666
+ "description": "Milliseconds a query may wait for a free query slot before failing",
667
+ "name": "queryQueueTimeoutMs",
668
+ "required": true,
669
+ "hasDynamicHelp": false,
670
+ "multiple": false,
671
+ "type": "option"
640
672
  }
641
673
  },
642
674
  "hasDynamicHelp": false,
@@ -657,5 +689,5 @@
657
689
  ]
658
690
  }
659
691
  },
660
- "version": "0.5.2"
692
+ "version": "0.6.0"
661
693
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@hesed/mysql",
3
3
  "description": "CLI for MySQL database interaction",
4
- "version": "0.5.2",
4
+ "version": "0.6.0",
5
5
  "author": "Hesed",
6
6
  "bin": {
7
7
  "mq": "./bin/run.js"