@mongosh/autocomplete 1.10.1 → 1.10.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/.depcheckrc +11 -0
- package/.eslintrc.js +10 -1
- package/.prettierignore +6 -0
- package/.prettierrc.json +1 -0
- package/README.md +7 -5
- package/lib/index.d.ts +1 -1
- package/lib/index.js +51 -38
- package/lib/index.js.map +1 -1
- package/package.json +19 -9
- package/src/index.spec.ts +393 -290
- package/src/index.ts +146 -73
- package/tsconfig-lint.json +5 -0
- package/tsconfig.json +3 -7
- package/tsconfig.lint.json +0 -8
package/src/index.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
import { signatures as shellSignatures, Topologies, TypeSignature } from '@mongosh/shell-api';
|
|
1
|
+
import type { Topologies, TypeSignature } from '@mongosh/shell-api';
|
|
2
|
+
import { signatures as shellSignatures } from '@mongosh/shell-api';
|
|
4
3
|
import semver from 'semver';
|
|
5
4
|
import {
|
|
6
5
|
CONVERSION_OPERATORS,
|
|
@@ -12,30 +11,34 @@ import {
|
|
|
12
11
|
ATLAS,
|
|
13
12
|
ADL,
|
|
14
13
|
ON_PREM,
|
|
15
|
-
DATABASE
|
|
14
|
+
DATABASE,
|
|
16
15
|
} from '@mongodb-js/mongodb-constants';
|
|
17
16
|
|
|
18
17
|
type TypeSignatureAttributes = { [key: string]: TypeSignature };
|
|
19
18
|
|
|
20
19
|
export interface AutocompleteParameters {
|
|
21
20
|
topology: () => Topologies;
|
|
22
|
-
connectionInfo: () =>
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
21
|
+
connectionInfo: () =>
|
|
22
|
+
| undefined
|
|
23
|
+
| {
|
|
24
|
+
is_atlas: boolean;
|
|
25
|
+
is_data_federation: boolean;
|
|
26
|
+
server_version: string;
|
|
27
|
+
};
|
|
28
|
+
apiVersionInfo: () => { version: string; strict: boolean } | undefined;
|
|
29
|
+
getCollectionCompletionsForCurrentDb: (
|
|
30
|
+
collName: string
|
|
31
|
+
) => string[] | Promise<string[]>;
|
|
29
32
|
getDatabaseCompletions: (dbName: string) => string[] | Promise<string[]>;
|
|
30
33
|
}
|
|
31
34
|
|
|
32
35
|
type AnyCompletions = readonly (
|
|
33
|
-
| typeof CONVERSION_OPERATORS[number]
|
|
34
|
-
| typeof EXPRESSION_OPERATORS[number]
|
|
35
|
-
| typeof STAGE_OPERATORS[number]
|
|
36
|
-
| typeof QUERY_OPERATORS[number]
|
|
37
|
-
| typeof ACCUMULATORS[number]
|
|
38
|
-
| typeof BSON_TYPES[number]
|
|
36
|
+
| (typeof CONVERSION_OPERATORS)[number]
|
|
37
|
+
| (typeof EXPRESSION_OPERATORS)[number]
|
|
38
|
+
| (typeof STAGE_OPERATORS)[number]
|
|
39
|
+
| (typeof QUERY_OPERATORS)[number]
|
|
40
|
+
| (typeof ACCUMULATORS)[number]
|
|
41
|
+
| (typeof BSON_TYPES)[number]
|
|
39
42
|
)[];
|
|
40
43
|
|
|
41
44
|
export const BASE_COMPLETIONS = ([] as AnyCompletions).concat(
|
|
@@ -74,15 +77,26 @@ const GROUP = '$group';
|
|
|
74
77
|
*
|
|
75
78
|
* @returns {array} Matching Completions, Current User Input.
|
|
76
79
|
*/
|
|
77
|
-
async function completer(
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
const
|
|
82
|
-
|
|
83
|
-
const
|
|
84
|
-
|
|
85
|
-
const
|
|
80
|
+
async function completer(
|
|
81
|
+
params: AutocompleteParameters,
|
|
82
|
+
line: string
|
|
83
|
+
): Promise<[string[], string, 'exclusive'] | [string[], string]> {
|
|
84
|
+
const SHELL_COMPLETIONS = shellSignatures.ShellApi
|
|
85
|
+
.attributes as TypeSignatureAttributes;
|
|
86
|
+
const COLL_COMPLETIONS = shellSignatures.Collection
|
|
87
|
+
.attributes as TypeSignatureAttributes;
|
|
88
|
+
const DB_COMPLETIONS = shellSignatures.Database
|
|
89
|
+
.attributes as TypeSignatureAttributes;
|
|
90
|
+
const AGG_CURSOR_COMPLETIONS = shellSignatures.AggregationCursor
|
|
91
|
+
.attributes as TypeSignatureAttributes;
|
|
92
|
+
const COLL_CURSOR_COMPLETIONS = shellSignatures.Cursor
|
|
93
|
+
.attributes as TypeSignatureAttributes;
|
|
94
|
+
const RS_COMPLETIONS = shellSignatures.ReplicaSet
|
|
95
|
+
.attributes as TypeSignatureAttributes;
|
|
96
|
+
const CONFIG_COMPLETIONS = shellSignatures.ShellConfig
|
|
97
|
+
.attributes as TypeSignatureAttributes;
|
|
98
|
+
const SHARD_COMPLETE = shellSignatures.Shard
|
|
99
|
+
.attributes as TypeSignatureAttributes;
|
|
86
100
|
|
|
87
101
|
// Split at space-to-non-space transitions when looking at this as a command,
|
|
88
102
|
// because multiple spaces (e.g. 'show collections') are valid in commands.
|
|
@@ -104,10 +118,16 @@ async function completer(params: AutocompleteParameters, line: string): Promise<
|
|
|
104
118
|
// Complete the first argument after the command.
|
|
105
119
|
splitLineWhitespace.push('');
|
|
106
120
|
}
|
|
107
|
-
const hits =
|
|
121
|
+
const hits =
|
|
122
|
+
(await completer(
|
|
123
|
+
params,
|
|
124
|
+
splitLineWhitespace.map((item) => item.trim())
|
|
125
|
+
)) || [];
|
|
108
126
|
// Adjust to full input, because `completer` only completed the last item
|
|
109
127
|
// in the line, e.g. ['profile'] -> ['show profile']
|
|
110
|
-
const fullLineHits = hits.map(hit =>
|
|
128
|
+
const fullLineHits = hits.map((hit) =>
|
|
129
|
+
[...splitLineWhitespace.slice(0, -1), hit].join('')
|
|
130
|
+
);
|
|
111
131
|
return [fullLineHits, line, 'exclusive'];
|
|
112
132
|
}
|
|
113
133
|
return [[line], line, 'exclusive'];
|
|
@@ -122,20 +142,19 @@ async function completer(params: AutocompleteParameters, line: string): Promise<
|
|
|
122
142
|
if (splitLine.length <= 1) {
|
|
123
143
|
const hits = filterShellAPI(params, SHELL_COMPLETIONS, elToComplete);
|
|
124
144
|
return [hits.length ? hits : [], line];
|
|
125
|
-
} else if (
|
|
126
|
-
if (
|
|
145
|
+
} else if (/\bdb\b/.exec(firstLineEl) && splitLine.length === 2) {
|
|
146
|
+
if (/aggregate\s*\(\s*\[\s*\{\s*/.exec(elToComplete)) {
|
|
127
147
|
const splitQuery = line.split('{');
|
|
128
148
|
const prefix = splitQuery.pop()?.trim() || '';
|
|
129
|
-
const command: string = prefix
|
|
149
|
+
const command: string = prefix
|
|
150
|
+
? (line.split(prefix).shift() as string)
|
|
151
|
+
: line;
|
|
130
152
|
const suggestFirstStage = splitQuery.length <= 2;
|
|
131
153
|
|
|
132
154
|
const expressions = suggestFirstStage
|
|
133
|
-
// First stage in `db.aggregate` form can only be 'db' namespaced stages
|
|
134
|
-
|
|
135
|
-
: [
|
|
136
|
-
...BASE_COMPLETIONS,
|
|
137
|
-
...getStageAccumulators(params, elToComplete)
|
|
138
|
-
];
|
|
155
|
+
? // First stage in `db.aggregate` form can only be 'db' namespaced stages
|
|
156
|
+
DB_AGGREGATE_COMPLETIONS
|
|
157
|
+
: [...BASE_COMPLETIONS, ...getStageAccumulators(params, elToComplete)];
|
|
139
158
|
|
|
140
159
|
const hits = filterQueries(params, expressions, prefix, command);
|
|
141
160
|
return [hits.length ? hits : [], line];
|
|
@@ -143,12 +162,22 @@ async function completer(params: AutocompleteParameters, line: string): Promise<
|
|
|
143
162
|
// We're seeing something like 'db.foo' and expand that to all methods on
|
|
144
163
|
// db which start with 'foo' and all collections on the current db that
|
|
145
164
|
// start with 'foo'.
|
|
146
|
-
const hits = filterShellAPI(
|
|
147
|
-
|
|
148
|
-
|
|
165
|
+
const hits = filterShellAPI(
|
|
166
|
+
params,
|
|
167
|
+
DB_COMPLETIONS,
|
|
168
|
+
elToComplete,
|
|
169
|
+
splitLine
|
|
170
|
+
);
|
|
171
|
+
const colls = await params.getCollectionCompletionsForCurrentDb(
|
|
172
|
+
elToComplete.trim()
|
|
173
|
+
);
|
|
174
|
+
hits.push(...colls.map((coll) => `${splitLine[0]}.${coll}`));
|
|
149
175
|
return [hits.length ? hits : [], line];
|
|
150
|
-
} else if (
|
|
151
|
-
if (
|
|
176
|
+
} else if (/\bdb\b/.exec(firstLineEl) && splitLine.length > 2) {
|
|
177
|
+
if (
|
|
178
|
+
!/^\s*\w+\s*$/.exec(splitLine[1]) &&
|
|
179
|
+
!/\bgetCollection\b/.exec(splitLine[1])
|
|
180
|
+
) {
|
|
152
181
|
// The collection name contains something that is not whitespace or an
|
|
153
182
|
// alphanumeric character. This could be a function call, for example.
|
|
154
183
|
// In any case, we can't currently provide reasonable autocompletion
|
|
@@ -158,15 +187,23 @@ async function completer(params: AutocompleteParameters, line: string): Promise<
|
|
|
158
187
|
|
|
159
188
|
if (splitLine.length > 3) {
|
|
160
189
|
// We're seeing something like db.coll.find().xyz or db.coll.aggregate().xyz
|
|
161
|
-
if (splitLine[2]
|
|
190
|
+
if (/\baggregate\b/.exec(splitLine[2])) {
|
|
162
191
|
// aggregation cursor completions
|
|
163
192
|
const hits = filterShellAPI(
|
|
164
|
-
params,
|
|
193
|
+
params,
|
|
194
|
+
AGG_CURSOR_COMPLETIONS,
|
|
195
|
+
elToComplete,
|
|
196
|
+
splitLine
|
|
197
|
+
);
|
|
165
198
|
return [hits.length ? hits : [], line];
|
|
166
|
-
} else if (splitLine[2]
|
|
199
|
+
} else if (/\bfind\b/.exec(splitLine[2])) {
|
|
167
200
|
// collection cursor completions
|
|
168
201
|
const hits = filterShellAPI(
|
|
169
|
-
params,
|
|
202
|
+
params,
|
|
203
|
+
COLL_CURSOR_COMPLETIONS,
|
|
204
|
+
elToComplete,
|
|
205
|
+
splitLine
|
|
206
|
+
);
|
|
170
207
|
return [hits.length ? hits : [], line];
|
|
171
208
|
}
|
|
172
209
|
// This is something else, and we currently don't know what this is.
|
|
@@ -176,11 +213,11 @@ async function completer(params: AutocompleteParameters, line: string): Promise<
|
|
|
176
213
|
// complete aggregation and collection queries/stages
|
|
177
214
|
if (splitLine[2].includes('([') || splitLine[2].includes('({')) {
|
|
178
215
|
let expressions;
|
|
179
|
-
if (splitLine[2]
|
|
216
|
+
if (/\baggregate\b/.exec(splitLine[2])) {
|
|
180
217
|
// aggregation needs extra accumulators to autocomplete properly
|
|
181
218
|
expressions = [
|
|
182
219
|
...BASE_COMPLETIONS,
|
|
183
|
-
...getStageAccumulators(params, elToComplete)
|
|
220
|
+
...getStageAccumulators(params, elToComplete),
|
|
184
221
|
];
|
|
185
222
|
} else {
|
|
186
223
|
// collection querying just needs MATCH COMPLETIONS
|
|
@@ -189,25 +226,43 @@ async function completer(params: AutocompleteParameters, line: string): Promise<
|
|
|
189
226
|
// split on {, as a stage/query will always follow an open curly brace
|
|
190
227
|
const splitQuery = line.split('{');
|
|
191
228
|
const prefix = splitQuery.pop()?.trim();
|
|
192
|
-
const command: string = prefix
|
|
229
|
+
const command: string = prefix
|
|
230
|
+
? (line.split(prefix).shift() as string)
|
|
231
|
+
: line;
|
|
193
232
|
const hits = filterQueries(params, expressions, prefix || '', command);
|
|
194
233
|
return [hits.length ? hits : [], line];
|
|
195
234
|
}
|
|
196
235
|
|
|
197
236
|
const hits = filterShellAPI(
|
|
198
|
-
params,
|
|
237
|
+
params,
|
|
238
|
+
COLL_COMPLETIONS,
|
|
239
|
+
elToComplete,
|
|
240
|
+
splitLine
|
|
241
|
+
);
|
|
199
242
|
return [hits.length ? hits : [], line];
|
|
200
|
-
} else if (
|
|
243
|
+
} else if (/\bsh\b/.exec(firstLineEl) && splitLine.length === 2) {
|
|
201
244
|
const hits = filterShellAPI(
|
|
202
|
-
params,
|
|
245
|
+
params,
|
|
246
|
+
SHARD_COMPLETE,
|
|
247
|
+
elToComplete,
|
|
248
|
+
splitLine
|
|
249
|
+
);
|
|
203
250
|
return [hits.length ? hits : [], line];
|
|
204
|
-
} else if (
|
|
251
|
+
} else if (/\brs\b/.exec(firstLineEl) && splitLine.length === 2) {
|
|
205
252
|
const hits = filterShellAPI(
|
|
206
|
-
params,
|
|
253
|
+
params,
|
|
254
|
+
RS_COMPLETIONS,
|
|
255
|
+
elToComplete,
|
|
256
|
+
splitLine
|
|
257
|
+
);
|
|
207
258
|
return [hits.length ? hits : [], line];
|
|
208
|
-
} else if (
|
|
259
|
+
} else if (/\bconfig\b/.exec(firstLineEl) && splitLine.length === 2) {
|
|
209
260
|
const hits = filterShellAPI(
|
|
210
|
-
params,
|
|
261
|
+
params,
|
|
262
|
+
CONFIG_COMPLETIONS,
|
|
263
|
+
elToComplete,
|
|
264
|
+
splitLine
|
|
265
|
+
);
|
|
211
266
|
return [hits.length ? hits : [], line];
|
|
212
267
|
}
|
|
213
268
|
|
|
@@ -216,8 +271,14 @@ async function completer(params: AutocompleteParameters, line: string): Promise<
|
|
|
216
271
|
|
|
217
272
|
function isAcceptable(
|
|
218
273
|
params: AutocompleteParameters,
|
|
219
|
-
entry: {
|
|
220
|
-
|
|
274
|
+
entry: {
|
|
275
|
+
version?: string;
|
|
276
|
+
projectVersion?: string;
|
|
277
|
+
env?: string[];
|
|
278
|
+
apiVersions?: number[];
|
|
279
|
+
},
|
|
280
|
+
versionKey: 'version' | 'projectVersion'
|
|
281
|
+
) {
|
|
221
282
|
const connectionInfo = params.connectionInfo();
|
|
222
283
|
const apiVersionInfo = params.apiVersionInfo();
|
|
223
284
|
let isAcceptableVersion;
|
|
@@ -233,9 +294,11 @@ function isAcceptable(
|
|
|
233
294
|
const isAcceptableEnvironment =
|
|
234
295
|
!entry.env ||
|
|
235
296
|
!connectionInfo ||
|
|
236
|
-
(connectionInfo.is_data_federation
|
|
237
|
-
|
|
238
|
-
|
|
297
|
+
(connectionInfo.is_data_federation
|
|
298
|
+
? entry.env.includes(ADL)
|
|
299
|
+
: connectionInfo.is_atlas
|
|
300
|
+
? entry.env.includes(ATLAS)
|
|
301
|
+
: entry.env.includes(ON_PREM));
|
|
239
302
|
return isAcceptableVersion && isAcceptableEnvironment;
|
|
240
303
|
}
|
|
241
304
|
|
|
@@ -243,7 +306,7 @@ function isAcceptable(
|
|
|
243
306
|
function getStageAccumulators(
|
|
244
307
|
params: AutocompleteParameters,
|
|
245
308
|
stage: string
|
|
246
|
-
): readonly typeof ACCUMULATORS[number][] {
|
|
309
|
+
): readonly (typeof ACCUMULATORS)[number][] {
|
|
247
310
|
if (stage.includes(PROJECT)) {
|
|
248
311
|
return ACCUMULATORS.filter((acc) => {
|
|
249
312
|
return isAcceptable(params, acc, 'projectVersion');
|
|
@@ -254,19 +317,27 @@ function getStageAccumulators(
|
|
|
254
317
|
return [];
|
|
255
318
|
}
|
|
256
319
|
|
|
257
|
-
function filterQueries(
|
|
320
|
+
function filterQueries(
|
|
321
|
+
params: AutocompleteParameters,
|
|
322
|
+
completions: any,
|
|
323
|
+
prefix: string,
|
|
324
|
+
split: string
|
|
325
|
+
): string[] {
|
|
258
326
|
const hits: any[] = completions.filter((e: any) => {
|
|
259
|
-
return
|
|
327
|
+
return (
|
|
328
|
+
e.name && e.name.startsWith(prefix) && isAcceptable(params, e, 'version')
|
|
329
|
+
);
|
|
260
330
|
});
|
|
261
331
|
|
|
262
|
-
return hits.map(h => `${split}${h.name}`);
|
|
332
|
+
return hits.map((h) => `${split}${h.name}`);
|
|
263
333
|
}
|
|
264
334
|
|
|
265
335
|
function filterShellAPI(
|
|
266
336
|
params: AutocompleteParameters,
|
|
267
337
|
completions: { [key: string]: TypeSignature },
|
|
268
338
|
prefix: string,
|
|
269
|
-
split?: string[]
|
|
339
|
+
split?: string[]
|
|
340
|
+
): string[] {
|
|
270
341
|
const hits: string[] = Object.keys(completions).filter((c: string) => {
|
|
271
342
|
if (!c.toLowerCase().startsWith(prefix.toLowerCase())) return false;
|
|
272
343
|
if (completions[c].deprecated) return false;
|
|
@@ -274,10 +345,13 @@ function filterShellAPI(
|
|
|
274
345
|
const apiVersionInfo = params.apiVersionInfo();
|
|
275
346
|
let isAcceptableVersion;
|
|
276
347
|
let acceptableApiVersions;
|
|
277
|
-
if (
|
|
348
|
+
if (
|
|
349
|
+
apiVersionInfo?.strict &&
|
|
350
|
+
(acceptableApiVersions = completions[c].apiVersions)
|
|
351
|
+
) {
|
|
278
352
|
isAcceptableVersion =
|
|
279
|
-
|
|
280
|
-
|
|
353
|
+
+apiVersionInfo.version >= acceptableApiVersions[0] &&
|
|
354
|
+
+apiVersionInfo.version <= acceptableApiVersions[1];
|
|
281
355
|
} else {
|
|
282
356
|
const serverVersion = params.connectionInfo()?.server_version;
|
|
283
357
|
if (!serverVersion) return true;
|
|
@@ -286,19 +360,18 @@ function filterShellAPI(
|
|
|
286
360
|
isAcceptableVersion =
|
|
287
361
|
!acceptableVersions ||
|
|
288
362
|
(semver.gte(serverVersion, acceptableVersions[0]) &&
|
|
289
|
-
|
|
363
|
+
semver.lte(serverVersion, acceptableVersions[1]));
|
|
290
364
|
}
|
|
291
365
|
|
|
292
366
|
const acceptableTopologies = completions[c].topologies;
|
|
293
367
|
const isAcceptableTopology =
|
|
294
|
-
!acceptableTopologies ||
|
|
295
|
-
acceptableTopologies.includes(params.topology());
|
|
368
|
+
!acceptableTopologies || acceptableTopologies.includes(params.topology());
|
|
296
369
|
|
|
297
370
|
return isAcceptableVersion && isAcceptableTopology;
|
|
298
371
|
});
|
|
299
372
|
|
|
300
373
|
if (split) {
|
|
301
|
-
return hits.map(h => `${split.slice(0, -1).join('.')}.${h}`);
|
|
374
|
+
return hits.map((h) => `${split.slice(0, -1).join('.')}.${h}`);
|
|
302
375
|
}
|
|
303
376
|
|
|
304
377
|
return hits;
|
package/tsconfig.json
CHANGED
|
@@ -1,13 +1,9 @@
|
|
|
1
1
|
{
|
|
2
|
-
"extends": "
|
|
2
|
+
"extends": "@mongodb-js/tsconfig-mongosh/tsconfig.common.json",
|
|
3
3
|
"compilerOptions": {
|
|
4
4
|
"outDir": "./lib",
|
|
5
5
|
"allowJs": true
|
|
6
6
|
},
|
|
7
|
-
"include": [
|
|
8
|
-
|
|
9
|
-
],
|
|
10
|
-
"exclude": [
|
|
11
|
-
"./src/**/*.spec.*"
|
|
12
|
-
]
|
|
7
|
+
"include": ["src/**/*"],
|
|
8
|
+
"exclude": ["./src/**/*.spec.*"]
|
|
13
9
|
}
|