@hesed/mysql 0.5.1 → 0.5.2

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.1 linux-x64 node-v24.17.0
29
+ @hesed/mysql/0.5.2 linux-x64 node-v24.17.0
30
30
  $ mq --help [COMMAND]
31
31
  USAGE
32
32
  $ mq COMMAND
@@ -45,7 +45,7 @@ USAGE
45
45
  * [`mq mysql auth update`](#mq-mysql-auth-update)
46
46
  * [`mq mysql databases`](#mq-mysql-databases)
47
47
  * [`mq mysql describe-table TABLE`](#mq-mysql-describe-table-table)
48
- * [`mq mysql explain-query QUERY`](#mq-mysql-explain-query-query)
48
+ * [`mq mysql explain QUERY`](#mq-mysql-explain-query)
49
49
  * [`mq mysql indexes TABLE`](#mq-mysql-indexes-table)
50
50
  * [`mq mysql query QUERY`](#mq-mysql-query-query)
51
51
  * [`mq mysql tables`](#mq-mysql-tables)
@@ -80,7 +80,7 @@ EXAMPLES
80
80
  $ mq mysql auth add -p prod
81
81
  ```
82
82
 
83
- _See code: [src/commands/mysql/auth/add.ts](https://github.com/hesedcasa/mysql/blob/v0.5.1/src/commands/mysql/auth/add.ts)_
83
+ _See code: [src/commands/mysql/auth/add.ts](https://github.com/hesedcasa/mysql/blob/v0.5.2/src/commands/mysql/auth/add.ts)_
84
84
 
85
85
  ## `mq mysql auth delete`
86
86
 
@@ -105,7 +105,7 @@ EXAMPLES
105
105
  $ mq mysql auth delete -p prod
106
106
  ```
107
107
 
108
- _See code: [src/commands/mysql/auth/delete.ts](https://github.com/hesedcasa/mysql/blob/v0.5.1/src/commands/mysql/auth/delete.ts)_
108
+ _See code: [src/commands/mysql/auth/delete.ts](https://github.com/hesedcasa/mysql/blob/v0.5.2/src/commands/mysql/auth/delete.ts)_
109
109
 
110
110
  ## `mq mysql auth list`
111
111
 
@@ -125,7 +125,7 @@ EXAMPLES
125
125
  $ mq mysql auth list
126
126
  ```
127
127
 
128
- _See code: [src/commands/mysql/auth/list.ts](https://github.com/hesedcasa/mysql/blob/v0.5.1/src/commands/mysql/auth/list.ts)_
128
+ _See code: [src/commands/mysql/auth/list.ts](https://github.com/hesedcasa/mysql/blob/v0.5.2/src/commands/mysql/auth/list.ts)_
129
129
 
130
130
  ## `mq mysql auth profile`
131
131
 
@@ -150,7 +150,7 @@ EXAMPLES
150
150
  $ mq mysql auth profile --default test
151
151
  ```
152
152
 
153
- _See code: [src/commands/mysql/auth/profile.ts](https://github.com/hesedcasa/mysql/blob/v0.5.1/src/commands/mysql/auth/profile.ts)_
153
+ _See code: [src/commands/mysql/auth/profile.ts](https://github.com/hesedcasa/mysql/blob/v0.5.2/src/commands/mysql/auth/profile.ts)_
154
154
 
155
155
  ## `mq mysql auth test`
156
156
 
@@ -175,7 +175,7 @@ EXAMPLES
175
175
  $ mq mysql auth test -p prod
176
176
  ```
177
177
 
178
- _See code: [src/commands/mysql/auth/test.ts](https://github.com/hesedcasa/mysql/blob/v0.5.1/src/commands/mysql/auth/test.ts)_
178
+ _See code: [src/commands/mysql/auth/test.ts](https://github.com/hesedcasa/mysql/blob/v0.5.2/src/commands/mysql/auth/test.ts)_
179
179
 
180
180
  ## `mq mysql auth update`
181
181
 
@@ -207,7 +207,7 @@ EXAMPLES
207
207
  $ mq mysql auth update -p test
208
208
  ```
209
209
 
210
- _See code: [src/commands/mysql/auth/update.ts](https://github.com/hesedcasa/mysql/blob/v0.5.1/src/commands/mysql/auth/update.ts)_
210
+ _See code: [src/commands/mysql/auth/update.ts](https://github.com/hesedcasa/mysql/blob/v0.5.2/src/commands/mysql/auth/update.ts)_
211
211
 
212
212
  ## `mq mysql databases`
213
213
 
@@ -232,7 +232,7 @@ EXAMPLES
232
232
  $ mq mysql databases -p staging
233
233
  ```
234
234
 
235
- _See code: [src/commands/mysql/databases.ts](https://github.com/hesedcasa/mysql/blob/v0.5.1/src/commands/mysql/databases.ts)_
235
+ _See code: [src/commands/mysql/databases.ts](https://github.com/hesedcasa/mysql/blob/v0.5.2/src/commands/mysql/databases.ts)_
236
236
 
237
237
  ## `mq mysql describe-table TABLE`
238
238
 
@@ -240,15 +240,14 @@ Describe the structure of a MySQL table
240
240
 
241
241
  ```
242
242
  USAGE
243
- $ mq mysql describe-table TABLE [--json] [--format table|json|toon] [-p <value>]
243
+ $ mq mysql describe-table TABLE [--json] [-p <value>] [--toon]
244
244
 
245
245
  ARGUMENTS
246
246
  TABLE Table name to describe
247
247
 
248
248
  FLAGS
249
249
  -p, --profile=<value> Database profile name from config
250
- --format=<option> [default: table] Output format
251
- <options: table|json|toon>
250
+ --toon Format output as toon
252
251
 
253
252
  GLOBAL FLAGS
254
253
  --json Format output as json.
@@ -259,26 +258,25 @@ DESCRIPTION
259
258
  EXAMPLES
260
259
  $ mq mysql describe-table users
261
260
 
262
- $ mq mysql describe-table orders --format json -p prod
261
+ $ mq mysql describe-table orders --toon -p prod
263
262
  ```
264
263
 
265
- _See code: [src/commands/mysql/describe-table.ts](https://github.com/hesedcasa/mysql/blob/v0.5.1/src/commands/mysql/describe-table.ts)_
264
+ _See code: [src/commands/mysql/describe-table.ts](https://github.com/hesedcasa/mysql/blob/v0.5.2/src/commands/mysql/describe-table.ts)_
266
265
 
267
- ## `mq mysql explain-query QUERY`
266
+ ## `mq mysql explain QUERY`
268
267
 
269
268
  Show the execution plan for a MySQL query
270
269
 
271
270
  ```
272
271
  USAGE
273
- $ mq mysql explain-query QUERY [--json] [--format table|json|toon] [-p <value>]
272
+ $ mq mysql explain QUERY [--json] [-p <value>] [--toon]
274
273
 
275
274
  ARGUMENTS
276
275
  QUERY SQL query to explain
277
276
 
278
277
  FLAGS
279
278
  -p, --profile=<value> Database profile name from config
280
- --format=<option> [default: table] Output format
281
- <options: table|json|toon>
279
+ --toon Format output as toon
282
280
 
283
281
  GLOBAL FLAGS
284
282
  --json Format output as json.
@@ -287,12 +285,12 @@ DESCRIPTION
287
285
  Show the execution plan for a MySQL query
288
286
 
289
287
  EXAMPLES
290
- $ mq mysql explain-query "SELECT * FROM users WHERE id = 1"
288
+ $ mq mysql explain "SELECT * FROM users WHERE id = 1"
291
289
 
292
- $ mq mysql explain-query "SELECT * FROM orders JOIN users ON orders.user_id = users.id" --format json
290
+ $ mq mysql explain "SELECT * FROM orders JOIN users ON orders.user_id = users.id" --json
293
291
  ```
294
292
 
295
- _See code: [src/commands/mysql/explain-query.ts](https://github.com/hesedcasa/mysql/blob/v0.5.1/src/commands/mysql/explain-query.ts)_
293
+ _See code: [src/commands/mysql/explain.ts](https://github.com/hesedcasa/mysql/blob/v0.5.2/src/commands/mysql/explain.ts)_
296
294
 
297
295
  ## `mq mysql indexes TABLE`
298
296
 
@@ -300,15 +298,14 @@ Show indexes for a MySQL table
300
298
 
301
299
  ```
302
300
  USAGE
303
- $ mq mysql indexes TABLE [--json] [--format table|json|toon] [-p <value>]
301
+ $ mq mysql indexes TABLE [--json] [-p <value>] [--toon]
304
302
 
305
303
  ARGUMENTS
306
304
  TABLE Table name to show indexes for
307
305
 
308
306
  FLAGS
309
307
  -p, --profile=<value> Database profile name from config
310
- --format=<option> [default: table] Output format
311
- <options: table|json|toon>
308
+ --toon Format output as toon
312
309
 
313
310
  GLOBAL FLAGS
314
311
  --json Format output as json.
@@ -319,10 +316,10 @@ DESCRIPTION
319
316
  EXAMPLES
320
317
  $ mq mysql indexes users
321
318
 
322
- $ mq mysql indexes orders --format json -p prod
319
+ $ mq mysql indexes orders --json -p prod
323
320
  ```
324
321
 
325
- _See code: [src/commands/mysql/indexes.ts](https://github.com/hesedcasa/mysql/blob/v0.5.1/src/commands/mysql/indexes.ts)_
322
+ _See code: [src/commands/mysql/indexes.ts](https://github.com/hesedcasa/mysql/blob/v0.5.2/src/commands/mysql/indexes.ts)_
326
323
 
327
324
  ## `mq mysql query QUERY`
328
325
 
@@ -330,16 +327,15 @@ Execute a SQL query against a MySQL database
330
327
 
331
328
  ```
332
329
  USAGE
333
- $ mq mysql query QUERY [--json] [--format table|json|csv|toon] [-p <value>] [--skip-confirmation]
330
+ $ mq mysql query QUERY [--json] [-p <value>] [--skip-confirmation] [--toon]
334
331
 
335
332
  ARGUMENTS
336
333
  QUERY SQL query to execute
337
334
 
338
335
  FLAGS
339
336
  -p, --profile=<value> Database profile name from config
340
- --format=<option> [default: table] Output format
341
- <options: table|json|csv|toon>
342
337
  --skip-confirmation Skip confirmation prompt for destructive operations
338
+ --toon Format output as toon
343
339
 
344
340
  GLOBAL FLAGS
345
341
  --json Format output as json.
@@ -348,14 +344,14 @@ DESCRIPTION
348
344
  Execute a SQL query against a MySQL database
349
345
 
350
346
  EXAMPLES
351
- $ mq mysql query "SELECT * FROM users LIMIT 10"
347
+ $ mq mysql query "SELECT * FROM users LIMIT 10" --json
352
348
 
353
- $ mq mysql query "UPDATE users SET email = 'user@email.com' WHERE id = 999" --format json
349
+ $ mq mysql query "UPDATE users SET email = 'user@email.com' WHERE id = 999"
354
350
 
355
351
  $ mq mysql query "DELETE FROM sessions" -p prod --skip-confirmation
356
352
  ```
357
353
 
358
- _See code: [src/commands/mysql/query.ts](https://github.com/hesedcasa/mysql/blob/v0.5.1/src/commands/mysql/query.ts)_
354
+ _See code: [src/commands/mysql/query.ts](https://github.com/hesedcasa/mysql/blob/v0.5.2/src/commands/mysql/query.ts)_
359
355
 
360
356
  ## `mq mysql tables`
361
357
 
@@ -363,11 +359,14 @@ List all tables in the current MySQL database
363
359
 
364
360
  ```
365
361
  USAGE
366
- $ mq mysql tables [-p <value>]
362
+ $ mq mysql tables [--json] [-p <value>]
367
363
 
368
364
  FLAGS
369
365
  -p, --profile=<value> Database profile name from config
370
366
 
367
+ GLOBAL FLAGS
368
+ --json Format output as json.
369
+
371
370
  DESCRIPTION
372
371
  List all tables in the current MySQL database
373
372
 
@@ -377,5 +376,5 @@ EXAMPLES
377
376
  $ mq mysql tables -p local
378
377
  ```
379
378
 
380
- _See code: [src/commands/mysql/tables.ts](https://github.com/hesedcasa/mysql/blob/v0.5.1/src/commands/mysql/tables.ts)_
379
+ _See code: [src/commands/mysql/tables.ts](https://github.com/hesedcasa/mysql/blob/v0.5.2/src/commands/mysql/tables.ts)_
381
380
  <!-- commandsstop -->
@@ -7,6 +7,4 @@ export declare abstract class BaseCommand extends Command {
7
7
  protected toErrorJson(err: unknown): {
8
8
  error: string;
9
9
  };
10
- private formatFlagValue;
11
- private hasFormatFlag;
12
10
  }
@@ -4,8 +4,6 @@ export class BaseCommand extends Command {
4
4
  jsonEnabled() {
5
5
  const separatorIndex = this.argv.indexOf('--');
6
6
  const flagArgs = separatorIndex === -1 ? this.argv : this.argv.slice(0, separatorIndex);
7
- if (this.hasFormatFlag(flagArgs))
8
- return this.formatFlagValue(flagArgs) === 'json';
9
7
  return flagArgs.includes('--json');
10
8
  }
11
9
  // oclif sets this.parsed=true only after Parser.parse() returns successfully.
@@ -36,15 +34,4 @@ export class BaseCommand extends Command {
36
34
  const message = err instanceof Error ? err.message : String(err);
37
35
  return { error: message };
38
36
  }
39
- formatFlagValue(flagArgs) {
40
- for (const [index, arg] of flagArgs.entries()) {
41
- if (arg === '--format')
42
- return flagArgs[index + 1];
43
- if (arg.startsWith('--format='))
44
- return arg.slice('--format='.length);
45
- }
46
- }
47
- hasFormatFlag(flagArgs) {
48
- return flagArgs.some((arg) => arg === '--format' || arg.startsWith('--format='));
49
- }
50
37
  }
@@ -1,11 +1,10 @@
1
- import { Command } from '@oclif/core';
2
- export default class MySQLDatabases extends Command {
1
+ import type { ApiResult } from '@hesed/plugin-lib';
2
+ import { BaseCommand } from '../../base-command.js';
3
+ export default class MySQLDatabases extends BaseCommand {
3
4
  static description: string;
4
- static enableJsonFlag: boolean;
5
5
  static examples: string[];
6
6
  static flags: {
7
7
  profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
8
8
  };
9
- jsonEnabled(): boolean;
10
- run(): Promise<string[]>;
9
+ run(): Promise<ApiResult>;
11
10
  }
@@ -1,22 +1,21 @@
1
- import { Command, Flags } from '@oclif/core';
1
+ import { Flags } from '@oclif/core';
2
+ import { BaseCommand } from '../../base-command.js';
2
3
  import { closeConnections, listDatabases } from '../../mysql/index.js';
3
- export default class MySQLDatabases extends Command {
4
+ export default class MySQLDatabases extends BaseCommand {
4
5
  static description = 'List all databases accessible on the MySQL server';
5
- static enableJsonFlag = true;
6
6
  static examples = ['<%= config.bin %> <%= command.id %>', '<%= config.bin %> <%= command.id %> -p staging'];
7
7
  static flags = {
8
8
  profile: Flags.string({ char: 'p', description: 'Database profile name from config', required: false }),
9
9
  };
10
- jsonEnabled() {
11
- return true;
12
- }
13
10
  async run() {
14
11
  const { flags } = await this.parse(MySQLDatabases);
15
12
  const result = await listDatabases(this.config, flags.profile);
16
13
  await closeConnections();
17
14
  if (result.success) {
18
- return result.databases ?? [];
15
+ this.log(result.data?.result ?? '');
16
+ delete result.data.result;
17
+ return result;
19
18
  }
20
- this.error(result.error ?? 'Failed to list databases');
19
+ this.error(String(result.error ?? 'Failed to list databases'));
21
20
  }
22
21
  }
@@ -1,3 +1,4 @@
1
+ import { ApiResult } from '@hesed/plugin-lib';
1
2
  import { BaseCommand } from '../../base-command.js';
2
3
  export default class MySQLDescribeTable extends BaseCommand {
3
4
  static args: {
@@ -6,8 +7,8 @@ export default class MySQLDescribeTable extends BaseCommand {
6
7
  static description: string;
7
8
  static examples: string[];
8
9
  static flags: {
9
- format: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
10
10
  profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
11
+ toon: import("@oclif/core/interfaces").BooleanFlag<boolean>;
11
12
  };
12
- run(): Promise<unknown>;
13
+ run(): Promise<ApiResult>;
13
14
  }
@@ -8,24 +8,21 @@ export default class MySQLDescribeTable extends BaseCommand {
8
8
  static description = 'Describe the structure of a MySQL table';
9
9
  static examples = [
10
10
  '<%= config.bin %> <%= command.id %> users',
11
- '<%= config.bin %> <%= command.id %> orders --format json -p prod',
11
+ '<%= config.bin %> <%= command.id %> orders --toon -p prod',
12
12
  ];
13
13
  static flags = {
14
- format: Flags.string({
15
- default: 'table',
16
- description: 'Output format',
17
- options: ['table', 'json', 'toon'],
18
- }),
19
14
  profile: Flags.string({ char: 'p', description: 'Database profile name from config', required: false }),
15
+ toon: Flags.boolean({ description: 'Format output as toon', required: false }),
20
16
  };
21
17
  async run() {
22
18
  const { args, flags } = await this.parse(MySQLDescribeTable);
23
- const result = await describeTable(this.config, args.table, flags.profile, flags.format);
19
+ const result = await describeTable(this.config, args.table, flags.profile, flags.toon ? 'toon' : flags.json ? 'json' : 'table');
24
20
  await closeConnections();
25
21
  if (result.success) {
26
- this.log(result.result ?? '');
22
+ this.log(result.data?.result ?? '');
23
+ delete result.data.result;
27
24
  return result;
28
25
  }
29
- this.error(result.error ?? 'Failed to describe table');
26
+ this.error(String(result.error ?? 'Failed to describe table'));
30
27
  }
31
28
  }
@@ -1,3 +1,4 @@
1
+ import { ApiResult } from '@hesed/plugin-lib';
1
2
  import { BaseCommand } from '../../base-command.js';
2
3
  export default class MySQLExplain extends BaseCommand {
3
4
  static args: {
@@ -6,8 +7,8 @@ export default class MySQLExplain extends BaseCommand {
6
7
  static description: string;
7
8
  static examples: string[];
8
9
  static flags: {
9
- format: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
10
10
  profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
11
+ toon: import("@oclif/core/interfaces").BooleanFlag<boolean>;
11
12
  };
12
- run(): Promise<unknown>;
13
+ run(): Promise<ApiResult>;
13
14
  }
@@ -8,24 +8,21 @@ export default class MySQLExplain extends BaseCommand {
8
8
  static description = 'Show the execution plan for a MySQL query';
9
9
  static examples = [
10
10
  '<%= config.bin %> <%= command.id %> "SELECT * FROM users WHERE id = 1"',
11
- '<%= config.bin %> <%= command.id %> "SELECT * FROM orders JOIN users ON orders.user_id = users.id" --format json',
11
+ '<%= config.bin %> <%= command.id %> "SELECT * FROM orders JOIN users ON orders.user_id = users.id" --json',
12
12
  ];
13
13
  static flags = {
14
- format: Flags.string({
15
- default: 'table',
16
- description: 'Output format',
17
- options: ['table', 'json', 'toon'],
18
- }),
19
14
  profile: Flags.string({ char: 'p', description: 'Database profile name from config', required: false }),
15
+ toon: Flags.boolean({ description: 'Format output as toon', required: false }),
20
16
  };
21
17
  async run() {
22
18
  const { args, flags } = await this.parse(MySQLExplain);
23
- const result = await explainQuery(this.config, args.query, flags.profile, flags.format);
19
+ const result = await explainQuery(this.config, args.query, flags.profile, flags.toon ? 'toon' : flags.json ? 'json' : 'table');
24
20
  await closeConnections();
25
21
  if (result.success) {
26
- this.log(result.result ?? '');
22
+ this.log(result.data?.result ?? '');
23
+ delete result.data.result;
27
24
  return result;
28
25
  }
29
- this.error(result.error ?? 'Failed to explain query');
26
+ this.error(String(result.error ?? 'Failed to explain query'));
30
27
  }
31
28
  }
@@ -1,3 +1,4 @@
1
+ import { ApiResult } from '@hesed/plugin-lib';
1
2
  import { BaseCommand } from '../../base-command.js';
2
3
  export default class MySQLIndexes extends BaseCommand {
3
4
  static args: {
@@ -6,8 +7,8 @@ export default class MySQLIndexes extends BaseCommand {
6
7
  static description: string;
7
8
  static examples: string[];
8
9
  static flags: {
9
- format: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
10
10
  profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
11
+ toon: import("@oclif/core/interfaces").BooleanFlag<boolean>;
11
12
  };
12
- run(): Promise<unknown>;
13
+ run(): Promise<ApiResult>;
13
14
  }
@@ -8,24 +8,21 @@ export default class MySQLIndexes extends BaseCommand {
8
8
  static description = 'Show indexes for a MySQL table';
9
9
  static examples = [
10
10
  '<%= config.bin %> <%= command.id %> users',
11
- '<%= config.bin %> <%= command.id %> orders --format json -p prod',
11
+ '<%= config.bin %> <%= command.id %> orders --json -p prod',
12
12
  ];
13
13
  static flags = {
14
- format: Flags.string({
15
- default: 'table',
16
- description: 'Output format',
17
- options: ['table', 'json', 'toon'],
18
- }),
19
14
  profile: Flags.string({ char: 'p', description: 'Database profile name from config', required: false }),
15
+ toon: Flags.boolean({ description: 'Format output as toon', required: false }),
20
16
  };
21
17
  async run() {
22
18
  const { args, flags } = await this.parse(MySQLIndexes);
23
- const result = await showIndexes(this.config, args.table, flags.profile, flags.format);
19
+ const result = await showIndexes(this.config, args.table, flags.profile, flags.toon ? 'toon' : flags.json ? 'json' : 'table');
24
20
  await closeConnections();
25
21
  if (result.success) {
26
- this.log(result.result ?? '');
22
+ this.log(result.data?.result ?? '');
23
+ delete result.data.result;
27
24
  return result;
28
25
  }
29
- this.error(result.error ?? 'Failed to show indexes');
26
+ this.error(String(result.error ?? 'Failed to show indexes'));
30
27
  }
31
28
  }
@@ -1,3 +1,4 @@
1
+ import { ApiResult } from '@hesed/plugin-lib';
1
2
  import { BaseCommand } from '../../base-command.js';
2
3
  export default class MySQLQuery extends BaseCommand {
3
4
  static args: {
@@ -6,9 +7,9 @@ export default class MySQLQuery extends BaseCommand {
6
7
  static description: string;
7
8
  static examples: string[];
8
9
  static flags: {
9
- format: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
10
10
  profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
11
11
  'skip-confirmation': import("@oclif/core/interfaces").BooleanFlag<boolean>;
12
+ toon: import("@oclif/core/interfaces").BooleanFlag<boolean>;
12
13
  };
13
- run(): Promise<unknown>;
14
+ run(): Promise<ApiResult>;
14
15
  }
@@ -7,40 +7,37 @@ export default class MySQLQuery extends BaseCommand {
7
7
  };
8
8
  static description = 'Execute a SQL query against a MySQL database';
9
9
  static examples = [
10
- '<%= config.bin %> <%= command.id %> "SELECT * FROM users LIMIT 10"',
11
- '<%= config.bin %> <%= command.id %> "UPDATE users SET email = \'user@email.com\' WHERE id = 999" --format json',
10
+ '<%= config.bin %> <%= command.id %> "SELECT * FROM users LIMIT 10" --json',
11
+ '<%= config.bin %> <%= command.id %> "UPDATE users SET email = \'user@email.com\' WHERE id = 999"',
12
12
  '<%= config.bin %> <%= command.id %> "DELETE FROM sessions" -p prod --skip-confirmation',
13
13
  ];
14
14
  static flags = {
15
- format: Flags.string({
16
- default: 'table',
17
- description: 'Output format',
18
- options: ['table', 'json', 'csv', 'toon'],
19
- }),
20
15
  profile: Flags.string({ char: 'p', description: 'Database profile name from config', required: false }),
21
16
  'skip-confirmation': Flags.boolean({
22
17
  default: false,
23
18
  description: 'Skip confirmation prompt for destructive operations',
24
19
  }),
20
+ toon: Flags.boolean({ description: 'Format output as toon', required: false }),
25
21
  };
26
22
  async run() {
27
23
  const { args, flags } = await this.parse(MySQLQuery);
28
- const result = await executeQuery(this.config, args.query, flags.profile, flags.format, flags['skip-confirmation']);
24
+ const result = await executeQuery(this.config, args.query, flags.profile, flags.toon ? 'toon' : flags.json ? 'json' : 'table', flags['skip-confirmation']);
29
25
  await closeConnections();
30
26
  if (result.success) {
31
27
  // Notices (warnings, row counts) go to stderr so machine-readable formats
32
28
  // leave stdout as clean, parseable data.
33
- if (result.notices)
34
- this.logToStderr(result.notices);
35
- if (this.jsonEnabled())
36
- return this.parseJsonOutput(result.result);
37
- this.log(result.result ?? '');
29
+ if (result.data?.notices)
30
+ this.logToStderr(result.data.notices);
31
+ // result is a formatted string for human/csv/toon output; for --json it
32
+ // holds the parsed payload, but jsonEnabled() suppresses log() in that case.
33
+ this.log(typeof result.data?.result === 'string' ? result.data.result : '');
34
+ delete result.data.notices;
38
35
  return result;
39
36
  }
40
- if (result.requiresConfirmation) {
41
- this.log(`${result.message ?? 'Destructive operation requires confirmation.'}\nRe-run with --skip-confirmation to proceed.`);
37
+ if (result.data?.requiresConfirmation) {
38
+ this.log(`${result.data?.message ?? 'Destructive operation requires confirmation.'}\nRe-run with --skip-confirmation to proceed.`);
42
39
  return result;
43
40
  }
44
- this.error(result.error ?? 'Query failed');
41
+ this.error(String(result.error ?? 'Query failed'));
45
42
  }
46
43
  }
@@ -1,10 +1,10 @@
1
- import { Command } from '@oclif/core';
2
- export default class MySQLTables extends Command {
1
+ import type { ApiResult } from '@hesed/plugin-lib';
2
+ import { BaseCommand } from '../../base-command.js';
3
+ export default class MySQLTables extends BaseCommand {
3
4
  static description: string;
4
5
  static examples: string[];
5
6
  static flags: {
6
7
  profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
7
8
  };
8
- jsonEnabled(): boolean;
9
- run(): Promise<string[]>;
9
+ run(): Promise<ApiResult>;
10
10
  }
@@ -1,21 +1,21 @@
1
- import { Command, Flags } from '@oclif/core';
1
+ import { Flags } from '@oclif/core';
2
+ import { BaseCommand } from '../../base-command.js';
2
3
  import { closeConnections, listTables } from '../../mysql/index.js';
3
- export default class MySQLTables extends Command {
4
+ export default class MySQLTables extends BaseCommand {
4
5
  static description = 'List all tables in the current MySQL database';
5
6
  static examples = ['<%= config.bin %> <%= command.id %>', '<%= config.bin %> <%= command.id %> -p local'];
6
7
  static flags = {
7
8
  profile: Flags.string({ char: 'p', description: 'Database profile name from config', required: false }),
8
9
  };
9
- jsonEnabled() {
10
- return true;
11
- }
12
10
  async run() {
13
11
  const { flags } = await this.parse(MySQLTables);
14
12
  const result = await listTables(this.config, flags.profile);
15
13
  await closeConnections();
16
14
  if (result.success) {
17
- return result.tables ?? [];
15
+ this.log(result.data?.result ?? '');
16
+ delete result.data.result;
17
+ return result;
18
18
  }
19
- this.error(result.error ?? 'Failed to list tables');
19
+ this.error(String(result.error ?? 'Failed to list tables'));
20
20
  }
21
21
  }
@@ -1,49 +1,57 @@
1
- export interface QueryResult {
2
- error?: string;
1
+ import type { ApiResult } from '@hesed/plugin-lib';
2
+ export type OutputFormat = 'csv' | 'json' | 'table' | 'toon';
3
+ export interface QueryData {
3
4
  message?: string;
4
5
  notices?: string;
5
6
  requiresConfirmation?: boolean;
6
- result?: string;
7
- success: boolean;
7
+ result?: unknown;
8
8
  }
9
- export interface DatabaseListResult {
10
- databases?: string[];
11
- error?: string;
9
+ export interface DatabaseListData {
10
+ databases: string[];
12
11
  result?: string;
13
- success: boolean;
14
12
  }
15
- export interface TableListResult {
16
- error?: string;
13
+ export interface TableListData {
17
14
  result?: string;
18
- success: boolean;
19
- tables?: string[];
15
+ tables: string[];
20
16
  }
21
- export interface TableStructureResult {
22
- error?: string;
17
+ export interface TableStructureData {
23
18
  result?: string;
24
- structure?: Record<string, unknown>[];
25
- success: boolean;
19
+ structure: Record<string, unknown>[];
26
20
  }
27
- export interface IndexResult {
28
- error?: string;
29
- indexes?: Record<string, unknown>[];
21
+ export interface IndexData {
22
+ indexes: Record<string, unknown>[];
30
23
  result?: string;
31
- success: boolean;
32
24
  }
33
- export interface ExplainResult {
34
- error?: string;
35
- plan?: Record<string, unknown>[];
25
+ export interface ExplainData {
26
+ plan: Record<string, unknown>[];
36
27
  result?: string;
37
- success: boolean;
38
28
  }
39
- export interface ConnectionTestResult {
40
- database?: string;
41
- error?: string;
29
+ interface ConnectionTestData {
30
+ database: string;
42
31
  result?: string;
43
- success: boolean;
44
- version?: string;
32
+ version: string;
45
33
  }
46
- export type OutputFormat = 'csv' | 'json' | 'table' | 'toon';
34
+ export type QueryResult = ApiResult & {
35
+ data?: QueryData;
36
+ };
37
+ export type DatabaseListResult = ApiResult & {
38
+ data?: DatabaseListData;
39
+ };
40
+ export type TableListResult = ApiResult & {
41
+ data?: TableListData;
42
+ };
43
+ export type TableStructureResult = ApiResult & {
44
+ data?: TableStructureData;
45
+ };
46
+ export type IndexResult = ApiResult & {
47
+ data?: IndexData;
48
+ };
49
+ export type ExplainResult = ApiResult & {
50
+ data?: ExplainData;
51
+ };
52
+ export type ConnectionTestResult = ApiResult & {
53
+ data?: ConnectionTestData;
54
+ };
47
55
  export interface DatabaseUtil {
48
56
  closeAll(): Promise<void>;
49
57
  describeTable(profileName: string, table: string, format?: OutputFormat): Promise<TableStructureResult>;
@@ -53,3 +61,4 @@ export interface DatabaseUtil {
53
61
  listTables(profileName: string): Promise<TableListResult>;
54
62
  showIndexes(profileName: string, table: string, format?: OutputFormat): Promise<IndexResult>;
55
63
  }
64
+ export {};
@@ -19,8 +19,10 @@ export class MySQLUtil {
19
19
  const connection = await this.getConnection(profileName);
20
20
  const [rows, fields] = await connection.query(`DESCRIBE ${table}`);
21
21
  return {
22
- result: this.formatRows(rows, fields, format),
23
- structure: rows,
22
+ data: {
23
+ result: this.formatRows(rows, fields, format),
24
+ structure: rows,
25
+ },
24
26
  success: true,
25
27
  };
26
28
  }
@@ -44,8 +46,10 @@ export class MySQLUtil {
44
46
  const confirmationCheck = requiresConfirmation(query, this.config.safety.requireConfirmationFor);
45
47
  if (confirmationCheck.required) {
46
48
  return {
47
- message: `${confirmationCheck.message}\nQuery: ${query}`,
48
- requiresConfirmation: true,
49
+ data: {
50
+ message: `${confirmationCheck.message}\nQuery: ${query}`,
51
+ requiresConfirmation: true,
52
+ },
49
53
  success: false,
50
54
  };
51
55
  }
@@ -71,15 +75,20 @@ export class MySQLUtil {
71
75
  const connection = await this.getConnection(profileName);
72
76
  const [rows, fields] = await connection.query(finalQuery);
73
77
  const isRead = queryType === 'SELECT' || queryType === 'SHOW' || queryType === 'DESCRIBE' || queryType === 'EXPLAIN';
74
- const data = isRead
78
+ let data = isRead
75
79
  ? this.formatReadResult(rows, fields, format, notices)
76
- : this.formatWriteResult(rows, notices);
80
+ : this.formatWriteResult(rows, notices, format);
81
+ if (format === 'json') {
82
+ data = JSON.parse(data);
83
+ }
77
84
  const notice = notices.join('\n\n');
78
85
  // For human (table) output everything stays on stdout, exactly as before.
79
86
  // For machine formats the data is returned alone and notices go to stderr.
80
87
  return {
81
- notices: machineFormat ? notice : undefined,
82
- result: machineFormat ? data : `${notice}\n\n${data}`,
88
+ data: {
89
+ notices: machineFormat ? notice : undefined,
90
+ result: machineFormat ? data : `${notice}\n\n${data}`,
91
+ },
83
92
  success: true,
84
93
  };
85
94
  }
@@ -96,8 +105,10 @@ export class MySQLUtil {
96
105
  const connection = await this.getConnection(profileName);
97
106
  const [rows, fields] = await connection.query(`EXPLAIN ${query}`);
98
107
  return {
99
- plan: rows,
100
- result: this.formatRows(rows, fields, format),
108
+ data: {
109
+ plan: rows,
110
+ result: this.formatRows(rows, fields, format),
111
+ },
101
112
  success: true,
102
113
  };
103
114
  }
@@ -115,8 +126,10 @@ export class MySQLUtil {
115
126
  const [rows] = await connection.query('SHOW DATABASES');
116
127
  const databases = rows.map((row) => row.Database);
117
128
  return {
118
- databases,
119
- result: `Databases:\n${databases.map((db) => ` • ${db}`).join('\n')}`,
129
+ data: {
130
+ databases,
131
+ result: `Databases:\n${databases.map((db) => ` • ${db}`).join('\n')}`,
132
+ },
120
133
  success: true,
121
134
  };
122
135
  }
@@ -136,9 +149,11 @@ export class MySQLUtil {
136
149
  const tableKey = Object.keys(rowsArray[0])[0];
137
150
  const tables = rowsArray.map((row) => row[tableKey]);
138
151
  return {
139
- result: `Tables in database:\n${tables.map((table) => ` • ${table}`).join('\n')}`,
152
+ data: {
153
+ result: `Tables in database:\n${tables.map((table) => ` • ${table}`).join('\n')}`,
154
+ tables,
155
+ },
140
156
  success: true,
141
- tables,
142
157
  };
143
158
  }
144
159
  catch (error) {
@@ -154,8 +169,10 @@ export class MySQLUtil {
154
169
  const connection = await this.getConnection(profileName);
155
170
  const [rows, fields] = await connection.query(`SHOW INDEXES FROM ${table}`);
156
171
  return {
157
- indexes: rows,
158
- result: this.formatRows(rows, fields, format),
172
+ data: {
173
+ indexes: rows,
174
+ result: this.formatRows(rows, fields, format),
175
+ },
159
176
  success: true,
160
177
  };
161
178
  }
@@ -173,10 +190,12 @@ export class MySQLUtil {
173
190
  const [rows] = await connection.query('SELECT VERSION() as version, DATABASE() as current_database');
174
191
  const info = rows[0];
175
192
  return {
176
- database: info.current_database,
177
- result: `Connection successful!\n\nProfile: ${profileName}\nMySQL Version: ${info.version}\nCurrent Database: ${info.current_database}`,
193
+ data: {
194
+ database: info.current_database,
195
+ result: `Connection successful!\n\nProfile: ${profileName}\nMySQL Version: ${info.version}\nCurrent Database: ${info.current_database}`,
196
+ version: info.version,
197
+ },
178
198
  success: true,
179
- version: info.version,
180
199
  };
181
200
  }
182
201
  catch (error) {
@@ -195,10 +214,18 @@ export class MySQLUtil {
195
214
  formatRows(rows, fields, format) {
196
215
  return FORMATTERS[format](rows, fields);
197
216
  }
198
- formatWriteResult(rows, notices) {
217
+ formatWriteResult(rows, notices, format) {
199
218
  const affectedRows = rows.affectedRows ?? 0;
200
219
  const insertId = rows.insertId ?? null;
201
220
  notices.push('Query executed successfully.');
221
+ // The caller JSON.parses the result for json output, so emit valid JSON
222
+ // here rather than the human-readable string (which would throw on parse).
223
+ if (format === 'json') {
224
+ const payload = { affectedRows };
225
+ if (insertId)
226
+ payload.insertId = insertId;
227
+ return JSON.stringify(payload, null, 2);
228
+ }
202
229
  let data = `Affected rows: ${affectedRows}\n`;
203
230
  if (insertId)
204
231
  data += `Insert ID: ${insertId}\n`;
@@ -54,7 +54,7 @@
54
54
  "description": "Describe the structure of a MySQL table",
55
55
  "examples": [
56
56
  "<%= config.bin %> <%= command.id %> users",
57
- "<%= config.bin %> <%= command.id %> orders --format json -p prod"
57
+ "<%= config.bin %> <%= command.id %> orders --toon -p prod"
58
58
  ],
59
59
  "flags": {
60
60
  "json": {
@@ -64,19 +64,6 @@
64
64
  "allowNo": false,
65
65
  "type": "boolean"
66
66
  },
67
- "format": {
68
- "description": "Output format",
69
- "name": "format",
70
- "default": "table",
71
- "hasDynamicHelp": false,
72
- "multiple": false,
73
- "options": [
74
- "table",
75
- "json",
76
- "toon"
77
- ],
78
- "type": "option"
79
- },
80
67
  "profile": {
81
68
  "char": "p",
82
69
  "description": "Database profile name from config",
@@ -85,6 +72,13 @@
85
72
  "hasDynamicHelp": false,
86
73
  "multiple": false,
87
74
  "type": "option"
75
+ },
76
+ "toon": {
77
+ "description": "Format output as toon",
78
+ "name": "toon",
79
+ "required": false,
80
+ "allowNo": false,
81
+ "type": "boolean"
88
82
  }
89
83
  },
90
84
  "hasDynamicHelp": false,
@@ -103,7 +97,7 @@
103
97
  "describe-table.js"
104
98
  ]
105
99
  },
106
- "mysql:explain-query": {
100
+ "mysql:explain": {
107
101
  "aliases": [],
108
102
  "args": {
109
103
  "query": {
@@ -115,7 +109,7 @@
115
109
  "description": "Show the execution plan for a MySQL query",
116
110
  "examples": [
117
111
  "<%= config.bin %> <%= command.id %> \"SELECT * FROM users WHERE id = 1\"",
118
- "<%= config.bin %> <%= command.id %> \"SELECT * FROM orders JOIN users ON orders.user_id = users.id\" --format json"
112
+ "<%= config.bin %> <%= command.id %> \"SELECT * FROM orders JOIN users ON orders.user_id = users.id\" --json"
119
113
  ],
120
114
  "flags": {
121
115
  "json": {
@@ -125,19 +119,6 @@
125
119
  "allowNo": false,
126
120
  "type": "boolean"
127
121
  },
128
- "format": {
129
- "description": "Output format",
130
- "name": "format",
131
- "default": "table",
132
- "hasDynamicHelp": false,
133
- "multiple": false,
134
- "options": [
135
- "table",
136
- "json",
137
- "toon"
138
- ],
139
- "type": "option"
140
- },
141
122
  "profile": {
142
123
  "char": "p",
143
124
  "description": "Database profile name from config",
@@ -146,11 +127,18 @@
146
127
  "hasDynamicHelp": false,
147
128
  "multiple": false,
148
129
  "type": "option"
130
+ },
131
+ "toon": {
132
+ "description": "Format output as toon",
133
+ "name": "toon",
134
+ "required": false,
135
+ "allowNo": false,
136
+ "type": "boolean"
149
137
  }
150
138
  },
151
139
  "hasDynamicHelp": false,
152
140
  "hiddenAliases": [],
153
- "id": "mysql:explain-query",
141
+ "id": "mysql:explain",
154
142
  "pluginAlias": "@hesed/mysql",
155
143
  "pluginName": "@hesed/mysql",
156
144
  "pluginType": "core",
@@ -161,7 +149,7 @@
161
149
  "dist",
162
150
  "commands",
163
151
  "mysql",
164
- "explain-query.js"
152
+ "explain.js"
165
153
  ]
166
154
  },
167
155
  "mysql:indexes": {
@@ -176,7 +164,7 @@
176
164
  "description": "Show indexes for a MySQL table",
177
165
  "examples": [
178
166
  "<%= config.bin %> <%= command.id %> users",
179
- "<%= config.bin %> <%= command.id %> orders --format json -p prod"
167
+ "<%= config.bin %> <%= command.id %> orders --json -p prod"
180
168
  ],
181
169
  "flags": {
182
170
  "json": {
@@ -186,19 +174,6 @@
186
174
  "allowNo": false,
187
175
  "type": "boolean"
188
176
  },
189
- "format": {
190
- "description": "Output format",
191
- "name": "format",
192
- "default": "table",
193
- "hasDynamicHelp": false,
194
- "multiple": false,
195
- "options": [
196
- "table",
197
- "json",
198
- "toon"
199
- ],
200
- "type": "option"
201
- },
202
177
  "profile": {
203
178
  "char": "p",
204
179
  "description": "Database profile name from config",
@@ -207,6 +182,13 @@
207
182
  "hasDynamicHelp": false,
208
183
  "multiple": false,
209
184
  "type": "option"
185
+ },
186
+ "toon": {
187
+ "description": "Format output as toon",
188
+ "name": "toon",
189
+ "required": false,
190
+ "allowNo": false,
191
+ "type": "boolean"
210
192
  }
211
193
  },
212
194
  "hasDynamicHelp": false,
@@ -236,8 +218,8 @@
236
218
  },
237
219
  "description": "Execute a SQL query against a MySQL database",
238
220
  "examples": [
239
- "<%= config.bin %> <%= command.id %> \"SELECT * FROM users LIMIT 10\"",
240
- "<%= config.bin %> <%= command.id %> \"UPDATE users SET email = 'user@email.com' WHERE id = 999\" --format json",
221
+ "<%= config.bin %> <%= command.id %> \"SELECT * FROM users LIMIT 10\" --json",
222
+ "<%= config.bin %> <%= command.id %> \"UPDATE users SET email = 'user@email.com' WHERE id = 999\"",
241
223
  "<%= config.bin %> <%= command.id %> \"DELETE FROM sessions\" -p prod --skip-confirmation"
242
224
  ],
243
225
  "flags": {
@@ -248,20 +230,6 @@
248
230
  "allowNo": false,
249
231
  "type": "boolean"
250
232
  },
251
- "format": {
252
- "description": "Output format",
253
- "name": "format",
254
- "default": "table",
255
- "hasDynamicHelp": false,
256
- "multiple": false,
257
- "options": [
258
- "table",
259
- "json",
260
- "csv",
261
- "toon"
262
- ],
263
- "type": "option"
264
- },
265
233
  "profile": {
266
234
  "char": "p",
267
235
  "description": "Database profile name from config",
@@ -276,6 +244,13 @@
276
244
  "name": "skip-confirmation",
277
245
  "allowNo": false,
278
246
  "type": "boolean"
247
+ },
248
+ "toon": {
249
+ "description": "Format output as toon",
250
+ "name": "toon",
251
+ "required": false,
252
+ "allowNo": false,
253
+ "type": "boolean"
279
254
  }
280
255
  },
281
256
  "hasDynamicHelp": false,
@@ -303,6 +278,13 @@
303
278
  "<%= config.bin %> <%= command.id %> -p local"
304
279
  ],
305
280
  "flags": {
281
+ "json": {
282
+ "description": "Format output as json.",
283
+ "helpGroup": "GLOBAL",
284
+ "name": "json",
285
+ "allowNo": false,
286
+ "type": "boolean"
287
+ },
306
288
  "profile": {
307
289
  "char": "p",
308
290
  "description": "Database profile name from config",
@@ -320,7 +302,7 @@
320
302
  "pluginName": "@hesed/mysql",
321
303
  "pluginType": "core",
322
304
  "strict": true,
323
- "enableJsonFlag": false,
305
+ "enableJsonFlag": true,
324
306
  "isESM": true,
325
307
  "relativePath": [
326
308
  "dist",
@@ -675,5 +657,5 @@
675
657
  ]
676
658
  }
677
659
  },
678
- "version": "0.5.1"
660
+ "version": "0.5.2"
679
661
  }
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.1",
4
+ "version": "0.5.2",
5
5
  "author": "Hesed",
6
6
  "bin": {
7
7
  "mq": "./bin/run.js"
@@ -49,7 +49,7 @@
49
49
  ],
50
50
  "homepage": "https://github.com/hesedcasa/mysql",
51
51
  "keywords": [
52
- "oclif"
52
+ "mysql"
53
53
  ],
54
54
  "license": "Apache-2.0",
55
55
  "main": "dist/index.js",