commander
Advanced tools
Comparing version 5.0.0 to 5.1.0
@@ -10,2 +10,23 @@ # Changelog | ||
## [5.1.0] (2020-04-25) | ||
### Added | ||
- support for multiple command aliases, the first of which is shown in the auto-generated help ([#531], [#1236]) | ||
- configuration support in `addCommand()` for `hidden` and `isDefault` ([#1232]) | ||
### Fixed | ||
- omit masked help flags from the displayed help ([#645], [#1247]) | ||
- remove old short help flag when change help flags using `helpOption` ([#1248]) | ||
### Changed | ||
- remove use of `arguments` to improve auto-generated help in editors ([#1235]) | ||
- rename `.command()` configuration `noHelp` to `hidden` (but not remove old support) ([#1232]) | ||
- improvements to documentation | ||
- update dependencies | ||
- update tested versions of node | ||
- eliminate lint errors in TypeScript ([#1208]) | ||
## [5.0.0] (2020-03-14) | ||
@@ -278,4 +299,6 @@ | ||
[#512]: https://github.com/tj/commander.js/issues/512 | ||
[#531]: https://github.com/tj/commander.js/issues/531 | ||
[#599]: https://github.com/tj/commander.js/issues/599 | ||
[#611]: https://github.com/tj/commander.js/issues/611 | ||
[#645]: https://github.com/tj/commander.js/issues/645 | ||
[#697]: https://github.com/tj/commander.js/issues/697 | ||
@@ -340,4 +363,11 @@ [#742]: https://github.com/tj/commander.js/issues/742 | ||
[#1195]: https://github.com/tj/commander.js/pull/1195 | ||
[#1208]: https://github.com/tj/commander.js/pull/1208 | ||
[#1232]: https://github.com/tj/commander.js/pull/1232 | ||
[#1235]: https://github.com/tj/commander.js/pull/1235 | ||
[#1236]: https://github.com/tj/commander.js/pull/1236 | ||
[#1247]: https://github.com/tj/commander.js/pull/1247 | ||
[#1248]: https://github.com/tj/commander.js/pull/1248 | ||
[Unreleased]: https://github.com/tj/commander.js/compare/master...develop | ||
[5.1.0]: https://github.com/tj/commander.js/compare/v5.0.0..v5.1.0 | ||
[5.0.0]: https://github.com/tj/commander.js/compare/v4.1.1..v5.0.0 | ||
@@ -344,0 +374,0 @@ [5.0.0-4]: https://github.com/tj/commander.js/compare/v5.0.0-3..v5.0.0-4 |
144
index.js
@@ -120,5 +120,5 @@ /** | ||
this._exitCallback = null; | ||
this._alias = null; | ||
this._aliases = []; | ||
this._noHelp = false; | ||
this._hidden = false; | ||
this._helpFlags = '-h, --help'; | ||
@@ -157,3 +157,3 @@ this._helpDescription = 'display help for command'; | ||
* @param {Object} [execOpts] - configuration options (for executable) | ||
* @return {Command} returns new command for action handler, or top-level command for executable command | ||
* @return {Command} returns new command for action handler, or `this` for executable command | ||
* @api public | ||
@@ -179,3 +179,3 @@ */ | ||
cmd._noHelp = !!opts.noHelp; | ||
cmd._hidden = !!(opts.noHelp || opts.hidden); | ||
cmd._helpFlags = this._helpFlags; | ||
@@ -222,7 +222,8 @@ cmd._helpDescription = this._helpDescription; | ||
* @param {Command} cmd - new subcommand | ||
* @return {Command} parent command for chaining | ||
* @param {Object} [opts] - configuration options | ||
* @return {Command} `this` command for chaining | ||
* @api public | ||
*/ | ||
addCommand(cmd) { | ||
addCommand(cmd, opts) { | ||
if (!cmd._name) throw new Error('Command passed to .addCommand() must have a name'); | ||
@@ -242,2 +243,6 @@ | ||
opts = opts || {}; | ||
if (opts.isDefault) this._defaultCommandName = cmd._name; | ||
if (opts.noHelp || opts.hidden) cmd._hidden = true; // modifying passed command due to existing implementation | ||
this.commands.push(cmd); | ||
@@ -249,3 +254,3 @@ cmd.parent = this; | ||
/** | ||
* Define argument syntax for the top-level command. | ||
* Define argument syntax for the command. | ||
* | ||
@@ -266,3 +271,3 @@ * @api public | ||
* | ||
* @return {Command} for chaining | ||
* @return {Command} `this` command for chaining | ||
* @api public | ||
@@ -303,3 +308,3 @@ */ | ||
* @param {Array} args | ||
* @return {Command} for chaining | ||
* @return {Command} `this` command for chaining | ||
* @api private | ||
@@ -347,3 +352,3 @@ */ | ||
* @param {Function} [fn] optional callback which will be passed a CommanderError, defaults to throwing | ||
* @return {Command} for chaining | ||
* @return {Command} `this` command for chaining | ||
* @api public | ||
@@ -398,3 +403,3 @@ */ | ||
* @param {Function} fn | ||
* @return {Command} for chaining | ||
* @return {Command} `this` command for chaining | ||
* @api public | ||
@@ -438,3 +443,3 @@ */ | ||
* @param {*} [defaultValue] | ||
* @return {Command} for chaining | ||
* @return {Command} `this` command for chaining | ||
* @api private | ||
@@ -557,3 +562,3 @@ */ | ||
* @param {*} [defaultValue] | ||
* @return {Command} for chaining | ||
* @return {Command} `this` command for chaining | ||
* @api public | ||
@@ -576,3 +581,3 @@ */ | ||
* @param {*} [defaultValue] | ||
* @return {Command} for chaining | ||
* @return {Command} `this` command for chaining | ||
* @api public | ||
@@ -593,3 +598,3 @@ */ | ||
allowUnknownOption(arg) { | ||
this._allowUnknownOption = arguments.length === 0 || arg; | ||
this._allowUnknownOption = (arg === undefined) || arg; | ||
return this; | ||
@@ -603,3 +608,3 @@ }; | ||
* @param {boolean} value | ||
* @return {Command} Command for chaining | ||
* @return {Command} `this` command for chaining | ||
* @api public | ||
@@ -621,3 +626,3 @@ */ | ||
* @param {boolean} value | ||
* @return {Command} Command for chaining | ||
* @return {Command} `this` command for chaining | ||
* @api public | ||
@@ -677,3 +682,3 @@ */ | ||
* @param {string} [parseOptions.from] - where the args are from: 'node', 'user', 'electron' | ||
* @return {Command} for chaining | ||
* @return {Command} `this` command for chaining | ||
* @api public | ||
@@ -952,3 +957,3 @@ */ | ||
if (!name) return undefined; | ||
return this.commands.find(cmd => cmd._name === name || cmd._alias === name); | ||
return this.commands.find(cmd => cmd._name === name || cmd._aliases.includes(name)); | ||
}; | ||
@@ -1193,3 +1198,3 @@ | ||
* @param {string} [description] | ||
* @return {Command | string} this for chaining | ||
* @return {this | string} `this` command for chaining, or version string if no arguments | ||
* @api public | ||
@@ -1199,3 +1204,3 @@ */ | ||
version(str, flags, description) { | ||
if (arguments.length === 0) return this._version; | ||
if (str === undefined) return this._version; | ||
this._version = str; | ||
@@ -1219,3 +1224,3 @@ flags = flags || '-V, --version'; | ||
* @param {Object} [argsDescription] | ||
* @return {String|Command} | ||
* @return {string|Command} | ||
* @api public | ||
@@ -1225,3 +1230,3 @@ */ | ||
description(str, argsDescription) { | ||
if (arguments.length === 0) return this._description; | ||
if (str === undefined && argsDescription === undefined) return this._description; | ||
this._description = str; | ||
@@ -1233,6 +1238,8 @@ this._argsDescription = argsDescription; | ||
/** | ||
* Set an alias for the command | ||
* Set an alias for the command. | ||
* | ||
* @param {string} alias | ||
* @return {String|Command} | ||
* You may call more than once to add multiple aliases. Only the first alias is shown in the auto-generated help. | ||
* | ||
* @param {string} [alias] | ||
* @return {string|Command} | ||
* @api public | ||
@@ -1242,12 +1249,13 @@ */ | ||
alias(alias) { | ||
if (alias === undefined) return this._aliases[0]; // just return first, for backwards compatibility | ||
let command = this; | ||
if (this.commands.length !== 0) { | ||
if (this.commands.length !== 0 && this.commands[this.commands.length - 1]._executableHandler) { | ||
// assume adding alias for last added executable subcommand, rather than this | ||
command = this.commands[this.commands.length - 1]; | ||
} | ||
if (arguments.length === 0) return command._alias; | ||
if (alias === command._name) throw new Error('Command alias can\'t be the same as its name'); | ||
command._alias = alias; | ||
command._aliases.push(alias); | ||
return this; | ||
@@ -1257,2 +1265,20 @@ }; | ||
/** | ||
* Set aliases for the command. | ||
* | ||
* Only the first alias is shown in the auto-generated help. | ||
* | ||
* @param {string[]} [aliases] | ||
* @return {string[]|Command} | ||
* @api public | ||
*/ | ||
aliases(aliases) { | ||
// Getter for the array of aliases is the main reason for having aliases() in addition to alias(). | ||
if (aliases === undefined) return this._aliases; | ||
aliases.forEach((alias) => this.alias(alias)); | ||
return this; | ||
}; | ||
/** | ||
* Set / get the command usage `str`. | ||
@@ -1266,13 +1292,14 @@ * | ||
usage(str) { | ||
const args = this._args.map((arg) => { | ||
return humanReadableArgName(arg); | ||
}); | ||
if (str === undefined) { | ||
if (this._usage) return this._usage; | ||
const usage = '[options]' + | ||
(this.commands.length ? ' [command]' : '') + | ||
(this._args.length ? ' ' + args.join(' ') : ''); | ||
const args = this._args.map((arg) => { | ||
return humanReadableArgName(arg); | ||
}); | ||
return '[options]' + | ||
(this.commands.length ? ' [command]' : '') + | ||
(this._args.length ? ' ' + args.join(' ') : ''); | ||
} | ||
if (arguments.length === 0) return this._usage || usage; | ||
this._usage = str; | ||
return this; | ||
@@ -1290,3 +1317,3 @@ }; | ||
name(str) { | ||
if (arguments.length === 0) return this._name; | ||
if (str === undefined) return this._name; | ||
this._name = str; | ||
@@ -1305,3 +1332,3 @@ return this; | ||
const commandDetails = this.commands.filter((cmd) => { | ||
return !cmd._noHelp; | ||
return !cmd._hidden; | ||
}).map((cmd) => { | ||
@@ -1314,3 +1341,3 @@ const args = cmd._args.map((arg) => { | ||
cmd._name + | ||
(cmd._alias ? '|' + cmd._alias : '') + | ||
(cmd._aliases[0] ? '|' + cmd._aliases[0] : '') + | ||
(cmd.options.length ? ' [options]' : '') + | ||
@@ -1406,13 +1433,29 @@ (args ? ' ' + args : ''), | ||
const width = this.padWidth(); | ||
const columns = process.stdout.columns || 80; | ||
const descriptionWidth = columns - width - 4; | ||
function padOptionDetails(flags, description) { | ||
return pad(flags, width) + ' ' + optionalWrap(description, descriptionWidth, width + 2); | ||
}; | ||
// Append the help information | ||
return this.options.map((option) => { | ||
// Explicit options (including version) | ||
const help = this.options.map((option) => { | ||
const fullDesc = option.description + | ||
((!option.negate && option.defaultValue !== undefined) ? ' (default: ' + JSON.stringify(option.defaultValue) + ')' : ''); | ||
return pad(option.flags, width) + ' ' + optionalWrap(fullDesc, descriptionWidth, width + 2); | ||
}).concat([pad(this._helpFlags, width) + ' ' + optionalWrap(this._helpDescription, descriptionWidth, width + 2)]) | ||
.join('\n'); | ||
return padOptionDetails(option.flags, fullDesc); | ||
}); | ||
// Implicit help | ||
const showShortHelpFlag = this._helpShortFlag && !this._findOption(this._helpShortFlag); | ||
const showLongHelpFlag = !this._findOption(this._helpLongFlag); | ||
if (showShortHelpFlag || showLongHelpFlag) { | ||
let helpFlags = this._helpFlags; | ||
if (!showShortHelpFlag) { | ||
helpFlags = this._helpLongFlag; | ||
} else if (!showLongHelpFlag) { | ||
helpFlags = this._helpShortFlag; | ||
} | ||
help.push(padOptionDetails(helpFlags, this._helpDescription)); | ||
} | ||
return help.join('\n'); | ||
}; | ||
@@ -1476,4 +1519,4 @@ | ||
let cmdName = this._name; | ||
if (this._alias) { | ||
cmdName = cmdName + '|' + this._alias; | ||
if (this._aliases[0]) { | ||
cmdName = cmdName + '|' + this._aliases[0]; | ||
} | ||
@@ -1535,3 +1578,3 @@ let parentCmdNames = ''; | ||
* @param {string} [description] | ||
* @return {Command} | ||
* @return {Command} `this` command for chaining | ||
* @api public | ||
@@ -1546,2 +1589,3 @@ */ | ||
this._helpShortFlag = undefined; | ||
if (splitFlags.length > 1) this._helpShortFlag = splitFlags.shift(); | ||
@@ -1548,0 +1592,0 @@ |
{ | ||
"name": "commander", | ||
"version": "5.0.0", | ||
"version": "5.1.0", | ||
"description": "the complete solution for node.js command-line programs", | ||
@@ -34,10 +34,10 @@ "keywords": [ | ||
"devDependencies": { | ||
"@types/jest": "^25.1.4", | ||
"@types/node": "^12.12.26", | ||
"@typescript-eslint/eslint-plugin": "^2.23.0", | ||
"@types/jest": "^25.2.1", | ||
"@types/node": "^12.12.36", | ||
"@typescript-eslint/eslint-plugin": "^2.29.0", | ||
"eslint": "^6.8.0", | ||
"eslint-config-standard-with-typescript": "^14.0.0", | ||
"eslint-config-standard-with-typescript": "^15.0.1", | ||
"eslint-plugin-jest": "^23.8.2", | ||
"jest": "^25.1.0", | ||
"standard": "^14.3.1", | ||
"jest": "^25.4.0", | ||
"standard": "^14.3.3", | ||
"typescript": "^3.7.5" | ||
@@ -44,0 +44,0 @@ }, |
@@ -303,3 +303,3 @@ # Commander.js | ||
You can specify (sub)commands for your top-level command using `.command()` or `.addCommand()`. There are two ways these can be implemented: using an action handler attached to the command, or as a stand-alone executable file (described in more detail later). The subcommands may be nested ([example](./examples/nestedCommands.js)). | ||
You can specify (sub)commands using `.command()` or `.addCommand()`. There are two ways these can be implemented: using an action handler attached to the command, or as a stand-alone executable file (described in more detail later). The subcommands may be nested ([example](./examples/nestedCommands.js)). | ||
@@ -323,3 +323,3 @@ In the first parameter to `.command()` you specify the command name and any command arguments. The arguments may be `<required>` or `[optional]`, and the last argument may also be `variadic...`. | ||
// Command implemented using stand-alone executable file (description is second parameter to `.command`) | ||
// Returns top-level command for adding more commands. | ||
// Returns `this` for adding more commands. | ||
program | ||
@@ -330,3 +330,3 @@ .command('start <service>', 'start named service') | ||
// Command prepared separately. | ||
// Returns top-level command for adding more commands. | ||
// Returns `this` for adding more commands. | ||
program | ||
@@ -336,7 +336,7 @@ .addCommand(build.makeBuildCommand()); | ||
Configuration options can be passed with the call to `.command()`. Specifying `true` for `opts.noHelp` will remove the command from the generated help output. Specifying `true` for `opts.isDefault` will run the subcommand if no other subcommand is specified ([example](./examples/defaultCommand.js)). | ||
Configuration options can be passed with the call to `.command()` and `.addCommand()`. Specifying `true` for `opts.hidden` will remove the command from the generated help output. Specifying `true` for `opts.isDefault` will run the subcommand if no other subcommand is specified ([example](./examples/defaultCommand.js)). | ||
### Specify the argument syntax | ||
You use `.arguments` to specify the arguments for the top-level command, and for subcommands they are included in the `.command` call. Angled brackets (e.g. `<required>`) indicate required input. Square brackets (e.g. `[optional]`) indicate optional input. | ||
You use `.arguments` to specify the arguments for the top-level command, and for subcommands they are usually included in the `.command` call. Angled brackets (e.g. `<required>`) indicate required input. Square brackets (e.g. `[optional]`) indicate optional input. | ||
@@ -343,0 +343,0 @@ ```js |
@@ -12,3 +12,3 @@ // Type definitions for commander | ||
} | ||
type CommanderErrorConstructor = { new (exitCode: number, code: string, message: string): CommanderError }; | ||
type CommanderErrorConstructor = new (exitCode: number, code: string, message: string) => CommanderError; | ||
@@ -25,3 +25,3 @@ interface Option { | ||
} | ||
type OptionConstructor = { new (flags: string, description?: string): Option }; | ||
type OptionConstructor = new (flags: string, description?: string) => Option; | ||
@@ -40,7 +40,7 @@ interface ParseOptions { | ||
/** | ||
* Set the program version to `str`. | ||
* Set the program version to `str`. | ||
* | ||
* This method auto-registers the "-V, --version" flag | ||
* which will print the version number when passed. | ||
* | ||
* | ||
* You can optionally supply the flags and description to override the defaults. | ||
@@ -52,6 +52,6 @@ */ | ||
* Define a command, implemented using an action handler. | ||
* | ||
* | ||
* @remarks | ||
* The command description is supplied using `.description`, not as a parameter to `.command`. | ||
* | ||
* | ||
* @example | ||
@@ -66,3 +66,3 @@ * ```ts | ||
* ``` | ||
* | ||
* | ||
* @param nameAndArgs - command name and arguments, args are `<required>` or `[optional]` and last may also be `variadic...` | ||
@@ -75,6 +75,6 @@ * @param opts - configuration options | ||
* Define a command, implemented in a separate executable file. | ||
* | ||
* | ||
* @remarks | ||
* The command description is supplied as the second parameter to `.command`. | ||
* | ||
* | ||
* @example | ||
@@ -86,9 +86,9 @@ * ```ts | ||
* ``` | ||
* | ||
* | ||
* @param nameAndArgs - command name and arguments, args are `<required>` or `[optional]` and last may also be `variadic...` | ||
* @param description - description of executable command | ||
* @param opts - configuration options | ||
* @returns top level command for chaining more command definitions | ||
* @returns `this` command for chaining | ||
*/ | ||
command(nameAndArgs: string, description: string, opts?: commander.CommandOptions): this; | ||
command(nameAndArgs: string, description: string, opts?: commander.ExecutableCommandOptions): this; | ||
@@ -102,16 +102,16 @@ /** | ||
createCommand(name?: string): Command; | ||
/** | ||
* Add a prepared subcommand. | ||
* | ||
* | ||
* See .command() for creating an attached subcommand which inherits settings from its parent. | ||
* | ||
* @returns parent command for chaining | ||
* | ||
* @returns `this` command for chaining | ||
*/ | ||
addCommand(cmd: Command): this; | ||
addCommand(cmd: Command, opts?: CommandOptions): this; | ||
/** | ||
* Define argument syntax for the top-level command. | ||
* Define argument syntax for command. | ||
* | ||
* @returns Command for chaining | ||
* @returns `this` command for chaining | ||
*/ | ||
@@ -136,3 +136,3 @@ arguments(desc: string): this; | ||
* | ||
* @returns Command for chaining | ||
* @returns `this` command for chaining | ||
*/ | ||
@@ -181,3 +181,3 @@ action(fn: (...args: any[]) => void | Promise<void>): this; | ||
* | ||
* @returns Command for chaining | ||
* @returns `this` command for chaining | ||
*/ | ||
@@ -198,3 +198,2 @@ option(flags: string, description?: string, defaultValue?: string | boolean): this; | ||
/** | ||
@@ -204,3 +203,3 @@ * Whether to store option values as properties on command object, | ||
* | ||
* @return Command for chaining | ||
* @returns `this` command for chaining | ||
*/ | ||
@@ -212,4 +211,4 @@ storeOptionsAsProperties(value?: boolean): this; | ||
* or just the options (specify false). | ||
* | ||
* @return Command for chaining | ||
* | ||
* @returns `this` command for chaining | ||
*/ | ||
@@ -222,3 +221,3 @@ passCommandToAction(value?: boolean): this; | ||
* @param [arg] if `true` or omitted, no error will be thrown for unknown options. | ||
* @returns Command for chaining | ||
* @returns `this` command for chaining | ||
*/ | ||
@@ -229,3 +228,3 @@ allowUnknownOption(arg?: boolean): this; | ||
* Parse `argv`, setting options and invoking commands when defined. | ||
* | ||
* | ||
* The default expectation is that the arguments are from node and have the application as argv[0] | ||
@@ -239,4 +238,4 @@ * and the script being run in argv[1], with user parameters after that. | ||
* program.parse(my-args, { from: 'user' }); // just user supplied arguments, nothing special about argv[0] | ||
* | ||
* @returns Command for chaining | ||
* | ||
* @returns `this` command for chaining | ||
*/ | ||
@@ -247,5 +246,5 @@ parse(argv?: string[], options?: ParseOptions): this; | ||
* Parse `argv`, setting options and invoking commands when defined. | ||
* | ||
* | ||
* Use parseAsync instead of parse if any of your action handlers are async. Returns a Promise. | ||
* | ||
* | ||
* The default expectation is that the arguments are from node and have the application as argv[0] | ||
@@ -284,4 +283,4 @@ * and the script being run in argv[1], with user parameters after that. | ||
* Set the description. | ||
* | ||
* @returns Command for chaining | ||
* | ||
* @returns `this` command for chaining | ||
*/ | ||
@@ -296,4 +295,6 @@ description(str: string, argsDescription?: {[argName: string]: string}): this; | ||
* Set an alias for the command. | ||
* | ||
* @returns Command for chaining | ||
* | ||
* You may call more than once to add multiple aliases. Only the first alias is shown in the auto-generated help. | ||
* | ||
* @returns `this` command for chaining | ||
*/ | ||
@@ -307,5 +308,18 @@ alias(alias: string): this; | ||
/** | ||
* Set aliases for the command. | ||
* | ||
* Only the first alias is shown in the auto-generated help. | ||
* | ||
* @returns `this` command for chaining | ||
*/ | ||
aliases(aliases: string[]): this; | ||
/** | ||
* Get aliases for the command. | ||
*/ | ||
aliases(): string[]; | ||
/** | ||
* Set the command usage. | ||
* | ||
* @returns Command for chaining | ||
* | ||
* @returns `this` command for chaining | ||
*/ | ||
@@ -320,4 +334,4 @@ usage(str: string): this; | ||
* Set the name of the command. | ||
* | ||
* @returns Command for chaining | ||
* | ||
* @returns `this` command for chaining | ||
*/ | ||
@@ -342,3 +356,3 @@ name(str: string): this; | ||
helpInformation(): string; | ||
/** | ||
@@ -350,3 +364,3 @@ * You can pass in flags and a description to override the help | ||
/** | ||
/** | ||
* Output help information and exit. | ||
@@ -358,3 +372,3 @@ */ | ||
* Add a listener (callback) for when events occur. (Implemented using EventEmitter.) | ||
* | ||
* | ||
* @example | ||
@@ -368,26 +382,30 @@ * program | ||
} | ||
type CommandConstructor = { new (name?: string): Command }; | ||
type CommandConstructor = new (name?: string) => Command; | ||
interface CommandOptions { | ||
noHelp?: boolean; // old name for hidden | ||
hidden?: boolean; | ||
isDefault?: boolean; | ||
} | ||
interface ExecutableCommandOptions extends CommandOptions { | ||
executableFile?: string; | ||
} | ||
interface CommandOptions { | ||
noHelp?: boolean; | ||
isDefault?: boolean; | ||
executableFile?: string; | ||
} | ||
interface ParseOptionsResult { | ||
operands: string[]; | ||
unknown: string[]; | ||
} | ||
interface ParseOptionsResult { | ||
operands: string[]; | ||
unknown: string[]; | ||
} | ||
interface CommanderStatic extends Command { | ||
program: Command; | ||
Command: CommandConstructor; | ||
Option: OptionConstructor; | ||
CommanderError: CommanderErrorConstructor; | ||
} | ||
interface CommanderStatic extends Command { | ||
program: Command; | ||
Command: CommandConstructor; | ||
Option: OptionConstructor; | ||
CommanderError:CommanderErrorConstructor; | ||
} | ||
} | ||
// Declaring namespace AND global | ||
// eslint-disable-next-line no-redeclare | ||
declare const commander: commander.CommanderStatic; | ||
export = commander; |
106839
1900