heroku 10.0.3-alpha.3 → 10.0.3-beta.0

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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "heroku",
3
3
  "description": "CLI to interact with Heroku",
4
- "version": "10.0.3-alpha.3",
4
+ "version": "10.0.3-beta.0",
5
5
  "author": "Heroku",
6
6
  "bin": "./bin/run",
7
7
  "bugs": "https://github.com/heroku/cli/issues",
@@ -46,7 +46,7 @@
46
46
  "execa": "5.1.1",
47
47
  "filesize": "^10.1.2",
48
48
  "foreman": "^3.0.1",
49
- "fs-extra": "^9.1",
49
+ "fs-extra": "^11.3.0",
50
50
  "github-url-to-object": "^4.0.4",
51
51
  "glob": "^10.3.10",
52
52
  "got": "^11.8.6",
@@ -78,8 +78,7 @@
78
78
  "urijs": "^1.19.11",
79
79
  "validator": "^13.7.0",
80
80
  "word-wrap": "^1.2.5",
81
- "ws": "^6.2.2",
82
- "yargs-parser": "18.1.3"
81
+ "ws": "^6.2.2"
83
82
  },
84
83
  "devDependencies": {
85
84
  "@heroku-cli/schema": "^1.0.25",
@@ -89,7 +88,7 @@
89
88
  "@types/chai": "^4.3.14",
90
89
  "@types/chai-as-promised": "^7.1.8",
91
90
  "@types/debug": "^4.1.2",
92
- "@types/fs-extra": "^7.0.0",
91
+ "@types/fs-extra": "^11.0.4",
93
92
  "@types/glob": "^7.1.1",
94
93
  "@types/inquirer": "^8.2.10",
95
94
  "@types/lodash": "^4.14.123",
@@ -102,6 +101,7 @@
102
101
  "@types/redis-parser": "^3.0.3",
103
102
  "@types/shell-escape": "^0.2.0",
104
103
  "@types/shell-quote": "^1.7.5",
104
+ "@types/sinon": "^17.0.3",
105
105
  "@types/ssh2": "^1.15.0",
106
106
  "@types/std-mocks": "^1.0.4",
107
107
  "@types/strftime": "^0.9.8",
@@ -126,7 +126,7 @@
126
126
  "qqjs": "0.3.11",
127
127
  "read-pkg": "^4.0.1",
128
128
  "rimraf": "5.0.5",
129
- "sinon": "^7.2.4",
129
+ "sinon": "^19.0.2",
130
130
  "std-mocks": "^2.0.0",
131
131
  "strip-ansi": "6.0.1",
132
132
  "tmp": "^0.2.3",
@@ -391,5 +391,5 @@
391
391
  "version": "oclif readme --multi && git add README.md ../../docs"
392
392
  },
393
393
  "types": "lib/index.d.ts",
394
- "gitHead": "ca78edeb011baec69aa41adac799c1de35264dfb"
394
+ "gitHead": "a9b1f3fa165a6a37e0681031eabfb1589dbf4e2e"
395
395
  }
@@ -1,235 +0,0 @@
1
- const fs = require('node:fs')
2
- const inquirer = require('inquirer')
3
-
4
- function choicesPrompt(description, choices, required, defaultValue) {
5
- return inquirer.prompt([{
6
- type: 'list',
7
- name: 'choices',
8
- message: description,
9
- choices,
10
- default: defaultValue,
11
- validate(input) {
12
- if (!required || input) {
13
- return true
14
- }
15
-
16
- return `${description} is required`
17
- },
18
- }])
19
- }
20
-
21
- function prompt(description, required) {
22
- return inquirer.prompt([{
23
- type: 'input',
24
- name: 'input',
25
- message: description,
26
- validate(input) {
27
- if (!required || input.trim()) {
28
- return true
29
- }
30
-
31
- return `${description} is required`
32
- },
33
- }])
34
- }
35
-
36
- function filePrompt(description, defaultPath) {
37
- return inquirer.prompt([{
38
- type: 'input',
39
- name: 'path',
40
- message: description,
41
- default: defaultPath,
42
- validate(input) {
43
- if (fs.existsSync(input)) {
44
- return true
45
- }
46
-
47
- return 'File does not exist. Please enter a valid file path.'
48
- },
49
- }])
50
- }
51
-
52
- const showBooleanPrompt = async (commandFlag, userInputMap, defaultOption) => {
53
- const {description, default: defaultValue, name: flagOrArgName} = commandFlag
54
- const choice = await choicesPrompt(description, [
55
- {name: 'yes', value: true},
56
- {name: 'no', value: false},
57
- ], defaultOption)
58
-
59
- // user cancelled
60
- if (choice === undefined || choice === 'Cancel') {
61
- return true
62
- }
63
-
64
- if (choice === 'Yes') {
65
- userInputMap.set(flagOrArgName, defaultValue)
66
- }
67
-
68
- return false
69
- }
70
-
71
- const showOtherDialog = async (commandFlagOrArg, userInputMap) => {
72
- const {description, default: defaultValue, options, required, name: flagOrArgName} = commandFlagOrArg
73
-
74
- let input
75
- const isFileInput = description?.includes('absolute path')
76
- if (isFileInput) {
77
- input = await filePrompt(description, '')
78
- } else if (options) {
79
- const choices = options.map(option => ({name: option, value: option}))
80
- input = await choicesPrompt(`Select the ${description}`, choices, required, defaultValue)
81
- } else {
82
- input = await prompt(`${description.slice(0, 1).toUpperCase()}${description.slice(1)} (${required ? 'required' : 'optional - press "Enter" to bypass'})`, required)
83
- }
84
-
85
- if (input === undefined) {
86
- return true
87
- }
88
-
89
- if (input !== '') {
90
- userInputMap.set(flagOrArgName, input)
91
- }
92
-
93
- return false
94
- }
95
-
96
- function collectInputsFromManifest(flagsOrArgsManifest, omitOptional) {
97
- const requiredInputs = []
98
- const optionalInputs = []
99
-
100
- // Prioritize options over booleans to
101
- // prevent the user from yo-yo back and
102
- // forth between the different input dialogs
103
- const keysByType = Object.keys(flagsOrArgsManifest).sort((a, b) => {
104
- const {type: aType} = flagsOrArgsManifest[a]
105
- const {type: bType} = flagsOrArgsManifest[b]
106
- if (aType === bType) {
107
- return 0
108
- }
109
-
110
- if (aType === 'option') {
111
- return -1
112
- }
113
-
114
- if (bType === 'option') {
115
- return 1
116
- }
117
-
118
- return 0
119
- })
120
-
121
- keysByType.forEach(key => {
122
- const isRequired = Reflect.get(flagsOrArgsManifest[key], 'required');
123
- (isRequired ? requiredInputs : optionalInputs).push(key)
124
- })
125
- // Prioritize required inputs
126
- // over optional inputs when
127
- // prompting the user.
128
- // required inputs are sorted
129
- // alphabetically. optional
130
- // inputs are sorted alphabetically
131
- // and then pushed to the end of
132
- // the list.
133
- requiredInputs.sort((a, b) => {
134
- if (a < b) {
135
- return -1
136
- }
137
-
138
- if (a > b) {
139
- return 1
140
- }
141
-
142
- return 0
143
- })
144
- // Include optional only when not explicitly omitted
145
- return omitOptional ? requiredInputs : [...requiredInputs, ...optionalInputs]
146
- }
147
-
148
- async function getInput(flagsOrArgsManifest, userInputMap, omitOptional) {
149
- const flagsOrArgs = collectInputsFromManifest(flagsOrArgsManifest, omitOptional)
150
-
151
- for (const flagOrArg of flagsOrArgs) {
152
- const {name, description, type, hidden} = flagsOrArgsManifest[flagOrArg]
153
- if (userInputMap.has(name)) {
154
- continue
155
- }
156
-
157
- // hidden args and flags may be exposed later
158
- // based on the user type. For now, skip them.
159
- if (!description || hidden) {
160
- continue
161
- }
162
-
163
- const cancelled = await (type === 'boolean' ? showBooleanPrompt : showOtherDialog)(flagsOrArgsManifest[flagOrArg], userInputMap)
164
- if (cancelled) {
165
- return true
166
- }
167
- }
168
-
169
- return false
170
- }
171
-
172
- async function promptForInputs(commandName, commandManifest, userArgs, userFlags) {
173
- const {args, flags} = commandManifest
174
-
175
- const userInputByArg = new Map()
176
- Object.keys(args).forEach((argKey, index) => {
177
- if (userArgs[index]) {
178
- userInputByArg.set(argKey, userArgs[index])
179
- }
180
- })
181
-
182
- let cancelled = await getInput(args, userInputByArg)
183
- if (cancelled) {
184
- return {userInputByArg}
185
- }
186
-
187
- const userInputByFlag = new Map()
188
- Object.keys(flags).forEach(flagKey => {
189
- const {name, char} = flags[flagKey]
190
- if (userFlags[name] || userFlags[char]) {
191
- userInputByFlag.set(flagKey, userFlags[flagKey])
192
- }
193
- })
194
- cancelled = await getInput(flags, userInputByFlag)
195
- if (cancelled) {
196
- return
197
- }
198
-
199
- return {userInputByArg, userInputByFlag}
200
- }
201
-
202
- module.exports.promptUser = async (config, commandName, args, flags) => {
203
- const commandMeta = config.findCommand(commandName)
204
- if (!commandMeta) {
205
- process.stderr.write(`"${commandName}" not a valid command\n$ `)
206
- return
207
- }
208
-
209
- const {userInputByArg, userInputByFlag} = await promptForInputs(commandName, commandMeta, args, flags)
210
-
211
- try {
212
- for (const [, {input: argValue}] of userInputByArg) {
213
- if (argValue) {
214
- args.push(argValue)
215
- }
216
- }
217
-
218
- for (const [flagName, {input: flagValue}] of userInputByFlag) {
219
- if (!flagValue) {
220
- continue
221
- }
222
-
223
- if (flagValue === true) {
224
- args.push(`--${flagName}`)
225
- continue
226
- }
227
-
228
- args.push(`--${flagName}`, flagValue)
229
- }
230
-
231
- return args
232
- } catch (error) {
233
- process.stderr.write(error.message)
234
- }
235
- }
@@ -1,48 +0,0 @@
1
- const {Config} = require('@oclif/core')
2
- const root = require.resolve('../package.json')
3
- const config = new Config({root})
4
- const flagsByName = new Map()
5
- async function * commandGenerator() {
6
- while (true) {
7
- const argv = await new Promise(resolve => {
8
- process.stdin.once('data', resolve)
9
- })
10
- yield argv.toString().trim().split(' ')
11
- }
12
- }
13
-
14
- module.exports.herokuRepl = async function (config) {
15
- process.stderr.write('Welcome to the Heroku Terminal!\n$ ')
16
-
17
- for await (const input of commandGenerator()) {
18
- const [command, ...argv] = input
19
- if (command === '.exit') {
20
- process.exit(0)
21
- }
22
-
23
- if (command.startsWith('set')) {
24
- flagsByName.set(argv[0], argv[1])
25
- process.stderr.write(`setting --app to "${argv[1]}"\n$ `)
26
- continue
27
- }
28
-
29
- const commandMeta = config.findCommand(command)
30
- if (!commandMeta) {
31
- process.stderr.write(`"${command}" not a valid command\n$ `)
32
- continue
33
- }
34
-
35
- try {
36
- const {flags} = commandMeta
37
- if (flags.app && flagsByName.has('app') && !argv?.includes('--app')) {
38
- argv.push('--app', flagsByName.get('app'))
39
- }
40
-
41
- await config.runCommand(command, argv)
42
- } catch (error) {
43
- process.stderr.write(error.message)
44
- }
45
-
46
- process.stderr.write('\n$ ')
47
- }
48
- }