@mohamed1_1ibrahim/dcli 1.0.0 → 1.1.1

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/src/utils/help.js CHANGED
@@ -11,7 +11,7 @@ const COMMANDS = [
11
11
  {
12
12
  name: 'export',
13
13
  args: '<uri>',
14
- desc: 'Export a MongoDB database to JSON file(s)',
14
+ desc: 'Export a MongoDB database to JSON file(s) (supports friendly name)',
15
15
  options: [
16
16
  ['-o, --output <name>', 'Output name (default: data-<timestamp>-<db>)'],
17
17
  ['--format <type>', 'Output format: file, split, all (default: file)'],
@@ -33,7 +33,7 @@ const COMMANDS = [
33
33
  {
34
34
  name: 'import',
35
35
  args: '<uri>',
36
- desc: 'Import a database or collection(s) from JSON file(s)',
36
+ desc: 'Import a database or collection(s) from JSON file(s) (supports friendly name)',
37
37
  options: [
38
38
  ['-f, --file <path>', 'File (.json), collection file, or directory of .json files (required)'],
39
39
  ],
@@ -59,7 +59,7 @@ const COMMANDS = [
59
59
  {
60
60
  name: 'info',
61
61
  args: '<uri>',
62
- desc: 'Show database collections and document counts',
62
+ desc: 'Show database collections and document counts (supports friendly name)',
63
63
  options: [],
64
64
  examples: [
65
65
  '$ dcli info "mongodb://localhost:27017/mydb"',
@@ -90,15 +90,112 @@ const COMMANDS = [
90
90
  {
91
91
  name: 'auto-ping',
92
92
  args: '',
93
- desc: 'Schedule "dcli ping" to run on logon (may need admin)',
93
+ desc: 'Schedule "dcli ping" to run automatically (may need admin)',
94
94
  options: [
95
95
  ['--remove', 'Remove the scheduled task'],
96
+ ['--schedule <type>', 'ONLOGON, DAILY, HOURLY, ONCE (default: ONLOGON)'],
97
+ ['--at <time>', 'Time for DAILY/ONCE (24h, e.g. 09:00)'],
98
+ ['--every <n>', 'Hourly interval in hours (default: 1)'],
99
+ ['--delay <n>', 'ONLOGON delay in minutes (default: 5)'],
96
100
  ],
97
101
  examples: [
98
102
  '$ dcli auto-ping',
103
+ '$ dcli auto-ping --schedule DAILY --at 10:00',
104
+ '$ dcli auto-ping --schedule HOURLY --every 2',
105
+ '$ dcli auto-ping --schedule ONLOGON --delay 10',
99
106
  '$ dcli auto-ping --remove',
100
107
  ],
101
108
  },
109
+ {
110
+ name: 'clone-add',
111
+ args: '<uri>',
112
+ desc: 'Add a database URI to the auto-clone list',
113
+ options: [
114
+ ['-n, --name <name>', 'Friendly name for this database'],
115
+ ],
116
+ examples: [
117
+ '$ dcli clone-add "mongodb://user:pass@cluster.mongodb.net/mydb"',
118
+ '$ dcli clone-add "mongodb://..." -n dbName',
119
+ ],
120
+ },
121
+ {
122
+ name: 'clone-remove',
123
+ args: '<uri|name>',
124
+ desc: 'Remove a database by URI or friendly name from the clone list',
125
+ options: [],
126
+ examples: [
127
+ '$ dcli clone-remove "mongodb://user:pass@cluster.mongodb.net/mydb"',
128
+ '$ dcli clone-remove dbName',
129
+ ],
130
+ },
131
+ {
132
+ name: 'clone',
133
+ args: '<name>',
134
+ desc: 'Clone a single database by its friendly name from the clone list',
135
+ options: [
136
+ ['-o, --output <dir>', 'Output directory (default: current directory)'],
137
+ ],
138
+ examples: [
139
+ '$ dcli clone kinderride',
140
+ '$ dcli clone kinderride -o ./backups',
141
+ ],
142
+ },
143
+ {
144
+ name: 'auto-clone',
145
+ args: '',
146
+ desc: 'Clone all databases in the clone list to JSON files',
147
+ options: [
148
+ ['-o, --output <dir>', 'Output directory (default: current directory)'],
149
+ ],
150
+ examples: [
151
+ '$ dcli auto-clone',
152
+ '$ dcli auto-clone -o ./backups',
153
+ ],
154
+ },
155
+ {
156
+ name: 'view',
157
+ args: '<uri> [collection]',
158
+ desc: 'Browse collections and documents in a styled table',
159
+ options: [
160
+ ['--limit <n>', 'Maximum documents to show (default: 10)'],
161
+ ['--fields <f1,f2>', 'Comma-separated fields to display'],
162
+ ['--sort <field>', 'Sort by field (ascending)'],
163
+ ['--all', 'Show all documents (no limit)'],
164
+ ['--json', 'Output raw JSON instead of a table'],
165
+ ],
166
+ examples: [
167
+ '$ dcli view myapp',
168
+ '$ dcli view myapp users',
169
+ '$ dcli view myapp users --limit 5',
170
+ '$ dcli view myapp users --fields name,email',
171
+ '$ dcli view myapp users --sort createdAt --all',
172
+ '$ dcli view myapp users --json',
173
+ ],
174
+ },
175
+ {
176
+ name: 'show',
177
+ args: '',
178
+ desc: 'Show the auto-ping or auto-clone database list',
179
+ options: [
180
+ ['--clone', 'Show the auto-clone list instead of the clone list'],
181
+ ],
182
+ examples: [
183
+ '$ dcli show',
184
+ '$ dcli show --clone',
185
+ ],
186
+ },
187
+ {
188
+ name: 'gui',
189
+ args: '',
190
+ desc: 'Open the dcli graphical interface in your browser',
191
+ options: [
192
+ ['-p, --port <number>', 'Port to run the GUI on (default: 3456)'],
193
+ ],
194
+ examples: [
195
+ '$ dcli gui',
196
+ '$ dcli gui -p 8080',
197
+ ],
198
+ },
102
199
  {
103
200
  name: 'help',
104
201
  args: '[command]',
@@ -1,6 +1,16 @@
1
+ import { setServers } from 'node:dns';
2
+ import { resolveSrv } from 'node:dns/promises';
1
3
  import { MongoClient } from 'mongodb';
2
4
 
3
5
  export async function connect(uri) {
6
+ if (uri.startsWith('mongodb+srv://')) {
7
+ const hostname = new URL(uri).hostname;
8
+ try {
9
+ await resolveSrv(`_mongodb._tcp.${hostname}`);
10
+ } catch {
11
+ setServers(['8.8.8.8', '1.1.1.1']);
12
+ }
13
+ }
4
14
  const client = new MongoClient(uri);
5
15
  await client.connect();
6
16
  return client;
@@ -0,0 +1,14 @@
1
+ import { readConfig } from './config.js';
2
+ import { readCloneConfig } from './cloneConfig.js';
3
+
4
+ export async function resolveName(input) {
5
+ const config = await readConfig();
6
+ const entry = config.databases.find(e => e.name === input);
7
+ if (entry) return entry.uri;
8
+
9
+ const cloneConfig = await readCloneConfig();
10
+ const cloneEntry = cloneConfig.databases.find(e => e.name === input);
11
+ if (cloneEntry) return cloneEntry.uri;
12
+
13
+ return input;
14
+ }
@@ -0,0 +1,74 @@
1
+ const RESET = '\x1b[0m';
2
+ const CYAN = '\x1b[36m';
3
+ const BOLD = '\x1b[1m';
4
+ const DIM = '\x1b[2m';
5
+
6
+ function visualLen(str) {
7
+ return str.replace(/\x1b\[[0-9;]*m/g, '').length;
8
+ }
9
+
10
+ function truncate(str, maxLen) {
11
+ if (!str) return '';
12
+ const s = String(str);
13
+ const len = visualLen(s);
14
+ if (len <= maxLen) return s;
15
+ const stripped = s.replace(/\x1b\[[0-9;]*m/g, '');
16
+ return stripped.slice(0, maxLen - 1) + '…';
17
+ }
18
+
19
+ function padAnsi(str, width) {
20
+ const len = visualLen(str);
21
+ const padding = Math.max(0, width - len);
22
+ return str + ' '.repeat(padding);
23
+ }
24
+
25
+ function repeatChar(char, count) {
26
+ return char.repeat(Math.max(0, count));
27
+ }
28
+
29
+ export function renderTable(rows, { maxColWidth = 50 } = {}) {
30
+ if (!rows || rows.length === 0) return '';
31
+
32
+ const keys = Object.keys(rows[0]);
33
+ const colWidths = keys.map(k => {
34
+ const headerLen = visualLen(k);
35
+ let max = headerLen;
36
+ for (const row of rows) {
37
+ const val = row[k] == null ? '' : String(row[k]);
38
+ const len = Math.min(visualLen(val), maxColWidth);
39
+ if (len > max) max = len;
40
+ }
41
+ return Math.min(max, maxColWidth);
42
+ });
43
+
44
+ const border = (left, sep, right, fill) => {
45
+ const parts = keys.map((k, i) => repeatChar(fill, colWidths[i] + 2));
46
+ return `${CYAN}${left}${parts.join(sep)}${right}${RESET}`;
47
+ };
48
+
49
+ const dataRow = (row) => {
50
+ const cells = keys.map((k, i) => {
51
+ const val = row[k] == null ? '' : truncate(String(row[k]), colWidths[i]);
52
+ return ` ${padAnsi(val, colWidths[i])} `;
53
+ });
54
+ return `${CYAN}│${RESET}${cells.join(`${CYAN}│${RESET}`)}${CYAN}│${RESET}`;
55
+ };
56
+
57
+ const headerRow = () => {
58
+ const cells = keys.map((k, i) => {
59
+ return ` ${CYAN}${BOLD}${padAnsi(k, colWidths[i])}${RESET} `;
60
+ });
61
+ return `${CYAN}│${RESET}${cells.join(`${CYAN}│${RESET}`)}${CYAN}│${RESET}`;
62
+ };
63
+
64
+ const lines = [];
65
+ lines.push(border('┌', '┬', '┐', '─'));
66
+ lines.push(headerRow());
67
+ lines.push(border('├', '┼', '┤', '─'));
68
+ for (const row of rows) {
69
+ lines.push(dataRow(row));
70
+ }
71
+ lines.push(border('└', '┴', '┘', '─'));
72
+
73
+ return lines.join('\n');
74
+ }