@seamapi/cli 0.16.0 → 0.17.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/bin/cli.js +9 -5
- package/bin/cli.js.map +1 -1
- package/lib/interact-for-action-attempt-poll.js +3 -5
- package/lib/interact-for-action-attempt-poll.js.map +1 -1
- package/lib/interact-for-array.js +11 -18
- package/lib/interact-for-array.js.map +1 -1
- package/lib/interact-for-blueprint-object.js +24 -34
- package/lib/interact-for-blueprint-object.js.map +1 -1
- package/lib/interact-for-command-selection.js +6 -11
- package/lib/interact-for-command-selection.js.map +1 -1
- package/lib/interact-for-custom-metadata.js +11 -22
- package/lib/interact-for-custom-metadata.js.map +1 -1
- package/lib/interact-for-login.js +3 -5
- package/lib/interact-for-login.js.map +1 -1
- package/lib/interact-for-resource.js +6 -6
- package/lib/interact-for-resource.js.map +1 -1
- package/lib/interact-for-server-selection.js +9 -17
- package/lib/interact-for-server-selection.js.map +1 -1
- package/lib/interact-for-timestamp.d.ts +1 -1
- package/lib/interact-for-timestamp.js +14 -5
- package/lib/interact-for-timestamp.js.map +1 -1
- package/lib/interact-for-use-remote-api-defs.js +14 -18
- package/lib/interact-for-use-remote-api-defs.js.map +1 -1
- package/lib/interact-for-workspace-id.d.ts +1 -1
- package/lib/interact-for-workspace-id.js +8 -13
- package/lib/interact-for-workspace-id.js.map +1 -1
- package/lib/util/prompt.d.ts +42 -13
- package/lib/util/prompt.js +79 -28
- package/lib/util/prompt.js.map +1 -1
- package/lib/version.d.ts +1 -1
- package/lib/version.js +1 -1
- package/package.json +2 -3
- package/src/bin/cli.ts +14 -5
- package/src/lib/interact-for-action-attempt-poll.ts +3 -5
- package/src/lib/interact-for-array.ts +11 -19
- package/src/lib/interact-for-blueprint-object.ts +43 -49
- package/src/lib/interact-for-command-selection.ts +6 -12
- package/src/lib/interact-for-custom-metadata.ts +11 -23
- package/src/lib/interact-for-login.ts +3 -5
- package/src/lib/interact-for-resource.ts +6 -7
- package/src/lib/interact-for-server-selection.ts +10 -18
- package/src/lib/interact-for-timestamp.ts +14 -5
- package/src/lib/interact-for-use-remote-api-defs.ts +14 -18
- package/src/lib/interact-for-workspace-id.ts +8 -14
- package/src/lib/util/prompt.ts +138 -38
- package/src/lib/version.ts +1 -1
package/src/lib/util/prompt.ts
CHANGED
|
@@ -1,4 +1,12 @@
|
|
|
1
|
-
import
|
|
1
|
+
import {
|
|
2
|
+
autocomplete,
|
|
3
|
+
autocompleteMultiselect,
|
|
4
|
+
confirm,
|
|
5
|
+
isCancel,
|
|
6
|
+
type Option,
|
|
7
|
+
select,
|
|
8
|
+
text,
|
|
9
|
+
} from '@clack/prompts'
|
|
2
10
|
|
|
3
11
|
import { NonInteractiveError } from './cli-args.js'
|
|
4
12
|
|
|
@@ -13,62 +21,154 @@ import { NonInteractiveError } from './cli-args.js'
|
|
|
13
21
|
export const canPrompt = (): boolean =>
|
|
14
22
|
process.stdin.isTTY === true && process.stderr.isTTY === true
|
|
15
23
|
|
|
16
|
-
/**
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
24
|
+
/** The user dismissed a prompt with ctrl-c or escape instead of answering. */
|
|
25
|
+
export class PromptCancelledError extends Error {
|
|
26
|
+
constructor() {
|
|
27
|
+
super('Cancelled')
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface PromptChoice<Value> {
|
|
32
|
+
label: string
|
|
33
|
+
value: Value
|
|
34
|
+
hint?: string | undefined
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const ensureInteractive = (): void => {
|
|
26
38
|
if (!canPrompt()) {
|
|
27
39
|
throw new NonInteractiveError(
|
|
28
40
|
'Cannot prompt without a terminal: pass the missing arguments, or pipe them in as JSON',
|
|
29
41
|
)
|
|
30
42
|
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const unwrap = <Value>(value: Value | symbol): Value => {
|
|
46
|
+
if (isCancel(value)) throw new PromptCancelledError()
|
|
47
|
+
return value as Value
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Prompts are rendered to stderr: a selection is not a command result,
|
|
51
|
+
// so it must not end up in stdout when the CLI is piped.
|
|
52
|
+
const output = process.stderr
|
|
53
|
+
|
|
54
|
+
const toOptions = <Value>(
|
|
55
|
+
choices: Array<PromptChoice<Value>>,
|
|
56
|
+
): Array<Option<Value>> =>
|
|
57
|
+
choices.map(
|
|
58
|
+
({ label, value, hint }) =>
|
|
59
|
+
(hint === undefined
|
|
60
|
+
? { label, value }
|
|
61
|
+
: { label, value, hint }) as Option<Value>,
|
|
62
|
+
)
|
|
31
63
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
64
|
+
export const promptText = async (options: {
|
|
65
|
+
message: string
|
|
66
|
+
placeholder?: string
|
|
67
|
+
defaultValue?: string
|
|
68
|
+
validate?: (value: string | undefined) => string | undefined
|
|
69
|
+
}): Promise<string> => {
|
|
70
|
+
ensureInteractive()
|
|
71
|
+
return unwrap(await text({ ...options, output }))
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export const promptNumber = async (options: {
|
|
75
|
+
message: string
|
|
76
|
+
validate?: (value: number) => string | undefined
|
|
77
|
+
}): Promise<number> => {
|
|
78
|
+
ensureInteractive()
|
|
79
|
+
const value = unwrap(
|
|
80
|
+
await text({
|
|
81
|
+
message: options.message,
|
|
82
|
+
validate: (value) => {
|
|
83
|
+
if (value == null || value.trim() === '') return 'Enter a number'
|
|
84
|
+
const parsed = Number(value)
|
|
85
|
+
if (Number.isNaN(parsed)) return 'Enter a number'
|
|
86
|
+
return options.validate?.(parsed)
|
|
87
|
+
},
|
|
88
|
+
output,
|
|
89
|
+
}),
|
|
90
|
+
)
|
|
91
|
+
return Number(value)
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export const promptConfirm = async (options: {
|
|
95
|
+
message: string
|
|
96
|
+
initialValue?: boolean
|
|
97
|
+
active?: string
|
|
98
|
+
inactive?: string
|
|
99
|
+
}): Promise<boolean> => {
|
|
100
|
+
ensureInteractive()
|
|
101
|
+
return unwrap(await confirm({ ...options, output }))
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export const promptSelect = async <Value>(options: {
|
|
105
|
+
message: string
|
|
106
|
+
choices: Array<PromptChoice<Value>>
|
|
107
|
+
}): Promise<Value> => {
|
|
108
|
+
ensureInteractive()
|
|
109
|
+
return unwrap(
|
|
110
|
+
await select<Value>({
|
|
111
|
+
message: options.message,
|
|
112
|
+
options: toOptions(options.choices),
|
|
113
|
+
output,
|
|
114
|
+
}),
|
|
115
|
+
)
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export const promptAutocomplete = async <Value>(options: {
|
|
119
|
+
message: string
|
|
120
|
+
choices: Array<PromptChoice<Value>>
|
|
121
|
+
}): Promise<Value> => {
|
|
122
|
+
ensureInteractive()
|
|
123
|
+
return unwrap(
|
|
124
|
+
await autocomplete<Value>({
|
|
125
|
+
message: options.message,
|
|
126
|
+
options: toOptions(options.choices),
|
|
127
|
+
// Search a list by any part of a name or hint, rather than only by
|
|
128
|
+
// the label, which is all clack matches for itself.
|
|
129
|
+
filter: searchChoices,
|
|
130
|
+
output,
|
|
131
|
+
}),
|
|
132
|
+
)
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export const promptAutocompleteMultiselect = async <Value>(options: {
|
|
136
|
+
message: string
|
|
137
|
+
choices: Array<PromptChoice<Value>>
|
|
138
|
+
}): Promise<Value[]> => {
|
|
139
|
+
ensureInteractive()
|
|
140
|
+
return unwrap(
|
|
141
|
+
await autocompleteMultiselect<Value>({
|
|
142
|
+
message: options.message,
|
|
143
|
+
options: toOptions(options.choices),
|
|
144
|
+
filter: searchChoices,
|
|
145
|
+
output,
|
|
146
|
+
}),
|
|
45
147
|
)
|
|
46
148
|
}
|
|
47
149
|
|
|
48
150
|
export interface SearchableChoice {
|
|
49
|
-
|
|
50
|
-
|
|
151
|
+
label?: string | undefined
|
|
152
|
+
hint?: string | undefined
|
|
51
153
|
}
|
|
52
154
|
|
|
53
155
|
/**
|
|
54
|
-
*
|
|
55
|
-
* case insensitively against the
|
|
156
|
+
* Match a choice by every whitespace separated term of the input, matched
|
|
157
|
+
* case insensitively against the label and the hint.
|
|
56
158
|
*/
|
|
57
|
-
export const searchChoices =
|
|
159
|
+
export const searchChoices = (
|
|
58
160
|
input: string,
|
|
59
|
-
|
|
60
|
-
):
|
|
161
|
+
choice: SearchableChoice,
|
|
162
|
+
): boolean => {
|
|
61
163
|
const terms = input
|
|
62
164
|
.toLowerCase()
|
|
63
165
|
.split(/\s+/)
|
|
64
166
|
.filter((term) => term.length > 0)
|
|
65
167
|
|
|
66
|
-
if (terms.length === 0) return
|
|
168
|
+
if (terms.length === 0) return true
|
|
67
169
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
return terms.every((term) => searchable.includes(term))
|
|
73
|
-
})
|
|
170
|
+
const searchable = `${choice.label ?? ''} ${choice.hint ?? ''}`
|
|
171
|
+
.toLowerCase()
|
|
172
|
+
.trim()
|
|
173
|
+
return terms.every((term) => searchable.includes(term))
|
|
74
174
|
}
|
package/src/lib/version.ts
CHANGED