@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/src/index.ts CHANGED
@@ -1,6 +1,5 @@
1
- /* eslint complexity: 0, camelcase: 0, no-nested-ternary: 0 */
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: () => undefined | {
23
- is_atlas: boolean;
24
- is_data_federation: boolean;
25
- server_version: string;
26
- },
27
- apiVersionInfo: () => { version: string, strict: boolean } | undefined;
28
- getCollectionCompletionsForCurrentDb: (collName: string) => string[] | Promise<string[]>;
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(params: AutocompleteParameters, line: string): Promise<[string[], string, 'exclusive'] | [string[], string]> {
78
- const SHELL_COMPLETIONS = shellSignatures.ShellApi.attributes as TypeSignatureAttributes;
79
- const COLL_COMPLETIONS = shellSignatures.Collection.attributes as TypeSignatureAttributes;
80
- const DB_COMPLETIONS = shellSignatures.Database.attributes as TypeSignatureAttributes;
81
- const AGG_CURSOR_COMPLETIONS = shellSignatures.AggregationCursor.attributes as TypeSignatureAttributes;
82
- const COLL_CURSOR_COMPLETIONS = shellSignatures.Cursor.attributes as TypeSignatureAttributes;
83
- const RS_COMPLETIONS = shellSignatures.ReplicaSet.attributes as TypeSignatureAttributes;
84
- const CONFIG_COMPLETIONS = shellSignatures.ShellConfig.attributes as TypeSignatureAttributes;
85
- const SHARD_COMPLETE = shellSignatures.Shard.attributes as TypeSignatureAttributes;
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 = await completer(params, splitLineWhitespace.map(item => item.trim())) || [];
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 => [...splitLineWhitespace.slice(0, -1), hit].join(''));
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 (firstLineEl.match(/\bdb\b/) && splitLine.length === 2) {
126
- if (elToComplete.match(/aggregate\s*\(\s*\[\s*\{\s*/)) {
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 ? line.split(prefix).shift() as string : line;
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
- ? DB_AGGREGATE_COMPLETIONS
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(params, DB_COMPLETIONS, elToComplete, splitLine);
147
- const colls = await params.getCollectionCompletionsForCurrentDb(elToComplete.trim());
148
- hits.push(...colls.map(coll => `${splitLine[0]}.${coll}`));
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 (firstLineEl.match(/\bdb\b/) && splitLine.length > 2) {
151
- if (!splitLine[1].match(/^\s*\w+\s*$/) && !splitLine[1].match(/\bgetCollection\b/)) {
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].match(/\baggregate\b/)) {
190
+ if (/\baggregate\b/.exec(splitLine[2])) {
162
191
  // aggregation cursor completions
163
192
  const hits = filterShellAPI(
164
- params, AGG_CURSOR_COMPLETIONS, elToComplete, splitLine);
193
+ params,
194
+ AGG_CURSOR_COMPLETIONS,
195
+ elToComplete,
196
+ splitLine
197
+ );
165
198
  return [hits.length ? hits : [], line];
166
- } else if (splitLine[2].match(/\bfind\b/)) {
199
+ } else if (/\bfind\b/.exec(splitLine[2])) {
167
200
  // collection cursor completions
168
201
  const hits = filterShellAPI(
169
- params, COLL_CURSOR_COMPLETIONS, elToComplete, splitLine);
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].match(/\baggregate\b/)) {
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 ? line.split(prefix).shift() as string : line;
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, COLL_COMPLETIONS, elToComplete, splitLine);
237
+ params,
238
+ COLL_COMPLETIONS,
239
+ elToComplete,
240
+ splitLine
241
+ );
199
242
  return [hits.length ? hits : [], line];
200
- } else if (firstLineEl.match(/\bsh\b/) && splitLine.length === 2) {
243
+ } else if (/\bsh\b/.exec(firstLineEl) && splitLine.length === 2) {
201
244
  const hits = filterShellAPI(
202
- params, SHARD_COMPLETE, elToComplete, splitLine);
245
+ params,
246
+ SHARD_COMPLETE,
247
+ elToComplete,
248
+ splitLine
249
+ );
203
250
  return [hits.length ? hits : [], line];
204
- } else if (firstLineEl.match(/\brs\b/) && splitLine.length === 2) {
251
+ } else if (/\brs\b/.exec(firstLineEl) && splitLine.length === 2) {
205
252
  const hits = filterShellAPI(
206
- params, RS_COMPLETIONS, elToComplete, splitLine);
253
+ params,
254
+ RS_COMPLETIONS,
255
+ elToComplete,
256
+ splitLine
257
+ );
207
258
  return [hits.length ? hits : [], line];
208
- } else if (firstLineEl.match(/\bconfig\b/) && splitLine.length === 2) {
259
+ } else if (/\bconfig\b/.exec(firstLineEl) && splitLine.length === 2) {
209
260
  const hits = filterShellAPI(
210
- params, CONFIG_COMPLETIONS, elToComplete, splitLine);
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: { version?: string; projectVersion?: string; env?: string[]; apiVersions?: number[] },
220
- versionKey: 'version' | 'projectVersion') {
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 ? entry.env.includes(ADL) :
237
- connectionInfo.is_atlas ? entry.env.includes(ATLAS) :
238
- entry.env.includes(ON_PREM));
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(params: AutocompleteParameters, completions: any, prefix: string, split: string): string[] {
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 e.name && e.name.startsWith(prefix) && isAcceptable(params, e, 'version');
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[]): 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 (apiVersionInfo?.strict && (acceptableApiVersions = completions[c].apiVersions)) {
348
+ if (
349
+ apiVersionInfo?.strict &&
350
+ (acceptableApiVersions = completions[c].apiVersions)
351
+ ) {
278
352
  isAcceptableVersion =
279
- +apiVersionInfo.version >= acceptableApiVersions[0] &&
280
- +apiVersionInfo.version <= acceptableApiVersions[1];
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
- semver.lte(serverVersion, acceptableVersions[1]));
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;
@@ -0,0 +1,5 @@
1
+ {
2
+ "extends": "./tsconfig.json",
3
+ "include": ["**/*"],
4
+ "exclude": ["node_modules", "dist", "lib"]
5
+ }
package/tsconfig.json CHANGED
@@ -1,13 +1,9 @@
1
1
  {
2
- "extends": "../../config/tsconfig.base.json",
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
- "./src/**/*"
9
- ],
10
- "exclude": [
11
- "./src/**/*.spec.*"
12
- ]
7
+ "include": ["src/**/*"],
8
+ "exclude": ["./src/**/*.spec.*"]
13
9
  }
@@ -1,8 +0,0 @@
1
- {
2
- "extends": "./tsconfig.json",
3
- "include": [
4
- "./src/**/*",
5
- "./test/**/*"
6
- ],
7
- "exclude": []
8
- }