grimoire-wizard 0.3.1 → 0.5.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/README.md +185 -18
- package/dist/cli.js +602 -109
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +207 -3
- package/dist/index.js +921 -371
- package/dist/index.js.map +1 -1
- package/examples/handlers/setup-project.ts +9 -0
- package/examples/json/all-features.json +66 -0
- package/examples/json/appstore-screenshot-wizard.json +362 -0
- package/examples/json/appstore-upload.json +104 -0
- package/examples/json/basic.json +72 -0
- package/examples/json/batch-generate.json +186 -0
- package/examples/json/brief-builder.json +519 -0
- package/examples/json/conditional.json +155 -0
- package/examples/json/cost-analyzer.json +83 -0
- package/examples/json/demo.json +130 -0
- package/examples/json/scraper-selector.json +63 -0
- package/examples/json/themed-catppuccin.json +39 -0
- package/examples/json/themed.json +103 -0
- package/examples/json/with-actions.json +61 -0
- package/examples/json/with-checks.json +47 -0
- package/examples/json/with-oncomplete.json +45 -0
- package/examples/yaml/appstore-screenshot-wizard.yaml +321 -0
- package/examples/yaml/appstore-upload.yaml +84 -0
- package/examples/yaml/batch-generate.yaml +156 -0
- package/examples/yaml/brief-builder.yaml +429 -0
- package/examples/yaml/cost-analyzer.yaml +69 -0
- package/examples/yaml/pipeline.yaml +35 -0
- package/examples/yaml/scraper-selector.yaml +52 -0
- package/examples/yaml/themed-catppuccin.yaml +31 -0
- package/examples/yaml/with-actions.yaml +45 -0
- package/examples/yaml/with-oncomplete.yaml +35 -0
- package/package.json +1 -1
- /package/examples/{all-features.yaml → yaml/all-features.yaml} +0 -0
- /package/examples/{base.yaml → yaml/base.yaml} +0 -0
- /package/examples/{basic.yaml → yaml/basic.yaml} +0 -0
- /package/examples/{conditional.yaml → yaml/conditional.yaml} +0 -0
- /package/examples/{demo.yaml → yaml/demo.yaml} +0 -0
- /package/examples/{ebay-mcp-setup.yaml → yaml/ebay-mcp-setup.yaml} +0 -0
- /package/examples/{extended.yaml → yaml/extended.yaml} +0 -0
- /package/examples/{themed.yaml → yaml/themed.yaml} +0 -0
- /package/examples/{with-checks.yaml → yaml/with-checks.yaml} +0 -0
package/dist/index.js
CHANGED
|
@@ -1,283 +1,15 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
3
|
+
var __esm = (fn, res) => function __init() {
|
|
4
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
5
|
+
};
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
|
|
1
11
|
// src/schema.ts
|
|
2
12
|
import { z } from "zod";
|
|
3
|
-
var conditionSchema = z.lazy(() => {
|
|
4
|
-
const fieldEquals = z.object({ field: z.string(), equals: z.unknown() }).strict();
|
|
5
|
-
const fieldNotEquals = z.object({ field: z.string(), notEquals: z.unknown() }).strict();
|
|
6
|
-
const fieldIncludes = z.object({ field: z.string(), includes: z.unknown() }).strict();
|
|
7
|
-
const fieldNotIncludes = z.object({ field: z.string(), notIncludes: z.unknown() }).strict();
|
|
8
|
-
const fieldGreaterThan = z.object({ field: z.string(), greaterThan: z.number() }).strict();
|
|
9
|
-
const fieldLessThan = z.object({ field: z.string(), lessThan: z.number() }).strict();
|
|
10
|
-
const fieldIsEmpty = z.object({ field: z.string(), isEmpty: z.literal(true) }).strict();
|
|
11
|
-
const fieldIsNotEmpty = z.object({ field: z.string(), isNotEmpty: z.literal(true) }).strict();
|
|
12
|
-
const allCondition = z.object({ all: z.array(conditionSchema) }).strict();
|
|
13
|
-
const anyCondition = z.object({ any: z.array(conditionSchema) }).strict();
|
|
14
|
-
const notCondition = z.object({ not: conditionSchema }).strict();
|
|
15
|
-
return z.union([
|
|
16
|
-
fieldEquals,
|
|
17
|
-
fieldNotEquals,
|
|
18
|
-
fieldIncludes,
|
|
19
|
-
fieldNotIncludes,
|
|
20
|
-
fieldGreaterThan,
|
|
21
|
-
fieldLessThan,
|
|
22
|
-
fieldIsEmpty,
|
|
23
|
-
fieldIsNotEmpty,
|
|
24
|
-
allCondition,
|
|
25
|
-
anyCondition,
|
|
26
|
-
notCondition
|
|
27
|
-
]);
|
|
28
|
-
});
|
|
29
|
-
var validationRuleSchema = z.discriminatedUnion("rule", [
|
|
30
|
-
z.object({ rule: z.literal("required"), message: z.string().optional() }),
|
|
31
|
-
z.object({ rule: z.literal("minLength"), value: z.number(), message: z.string().optional() }),
|
|
32
|
-
z.object({ rule: z.literal("maxLength"), value: z.number(), message: z.string().optional() }),
|
|
33
|
-
z.object({ rule: z.literal("pattern"), value: z.string(), message: z.string().optional() }),
|
|
34
|
-
z.object({ rule: z.literal("min"), value: z.number(), message: z.string().optional() }),
|
|
35
|
-
z.object({ rule: z.literal("max"), value: z.number(), message: z.string().optional() })
|
|
36
|
-
]);
|
|
37
|
-
var selectOptionSchema = z.object({
|
|
38
|
-
value: z.string(),
|
|
39
|
-
label: z.string(),
|
|
40
|
-
hint: z.string().optional(),
|
|
41
|
-
disabled: z.union([z.boolean(), z.string()]).optional()
|
|
42
|
-
});
|
|
43
|
-
var separatorOptionSchema = z.object({
|
|
44
|
-
separator: z.string()
|
|
45
|
-
});
|
|
46
|
-
var selectChoiceSchema = z.union([selectOptionSchema, separatorOptionSchema]);
|
|
47
|
-
var baseStepFields = {
|
|
48
|
-
id: z.string(),
|
|
49
|
-
message: z.string(),
|
|
50
|
-
description: z.string().optional(),
|
|
51
|
-
next: z.string().optional(),
|
|
52
|
-
when: conditionSchema.optional(),
|
|
53
|
-
keepValuesOnPrevious: z.boolean().optional(),
|
|
54
|
-
required: z.boolean().optional(),
|
|
55
|
-
group: z.string().optional()
|
|
56
|
-
};
|
|
57
|
-
var textStepSchema = z.object({
|
|
58
|
-
...baseStepFields,
|
|
59
|
-
type: z.literal("text"),
|
|
60
|
-
placeholder: z.string().optional(),
|
|
61
|
-
default: z.string().optional(),
|
|
62
|
-
validate: z.array(validationRuleSchema).optional()
|
|
63
|
-
});
|
|
64
|
-
var selectStepSchema = z.object({
|
|
65
|
-
...baseStepFields,
|
|
66
|
-
type: z.literal("select"),
|
|
67
|
-
options: z.array(selectChoiceSchema).min(1).optional(),
|
|
68
|
-
optionsFrom: z.string().optional(),
|
|
69
|
-
default: z.string().optional(),
|
|
70
|
-
routes: z.record(z.string(), z.string()).optional(),
|
|
71
|
-
pageSize: z.number().int().positive().optional(),
|
|
72
|
-
loop: z.boolean().optional()
|
|
73
|
-
});
|
|
74
|
-
var multiSelectStepSchema = z.object({
|
|
75
|
-
...baseStepFields,
|
|
76
|
-
type: z.literal("multiselect"),
|
|
77
|
-
options: z.array(selectChoiceSchema).min(1).optional(),
|
|
78
|
-
optionsFrom: z.string().optional(),
|
|
79
|
-
default: z.array(z.string()).optional(),
|
|
80
|
-
min: z.number().int().nonnegative().optional(),
|
|
81
|
-
max: z.number().int().positive().optional(),
|
|
82
|
-
pageSize: z.number().int().positive().optional(),
|
|
83
|
-
loop: z.boolean().optional()
|
|
84
|
-
});
|
|
85
|
-
var confirmStepSchema = z.object({
|
|
86
|
-
...baseStepFields,
|
|
87
|
-
type: z.literal("confirm"),
|
|
88
|
-
default: z.boolean().optional()
|
|
89
|
-
});
|
|
90
|
-
var passwordStepSchema = z.object({
|
|
91
|
-
...baseStepFields,
|
|
92
|
-
type: z.literal("password"),
|
|
93
|
-
validate: z.array(validationRuleSchema).optional()
|
|
94
|
-
});
|
|
95
|
-
var numberStepSchema = z.object({
|
|
96
|
-
...baseStepFields,
|
|
97
|
-
type: z.literal("number"),
|
|
98
|
-
default: z.number().optional(),
|
|
99
|
-
min: z.number().optional(),
|
|
100
|
-
max: z.number().optional(),
|
|
101
|
-
step: z.number().positive().optional()
|
|
102
|
-
});
|
|
103
|
-
var searchStepSchema = z.object({
|
|
104
|
-
...baseStepFields,
|
|
105
|
-
type: z.literal("search"),
|
|
106
|
-
options: z.array(selectChoiceSchema).min(1).optional(),
|
|
107
|
-
optionsFrom: z.string().optional(),
|
|
108
|
-
default: z.string().optional(),
|
|
109
|
-
placeholder: z.string().optional(),
|
|
110
|
-
pageSize: z.number().int().positive().optional(),
|
|
111
|
-
loop: z.boolean().optional()
|
|
112
|
-
});
|
|
113
|
-
var editorStepSchema = z.object({
|
|
114
|
-
...baseStepFields,
|
|
115
|
-
type: z.literal("editor"),
|
|
116
|
-
default: z.string().optional(),
|
|
117
|
-
validate: z.array(validationRuleSchema).optional()
|
|
118
|
-
});
|
|
119
|
-
var pathStepSchema = z.object({
|
|
120
|
-
...baseStepFields,
|
|
121
|
-
type: z.literal("path"),
|
|
122
|
-
default: z.string().optional(),
|
|
123
|
-
placeholder: z.string().optional(),
|
|
124
|
-
validate: z.array(validationRuleSchema).optional()
|
|
125
|
-
});
|
|
126
|
-
var toggleStepSchema = z.object({
|
|
127
|
-
...baseStepFields,
|
|
128
|
-
type: z.literal("toggle"),
|
|
129
|
-
default: z.boolean().optional(),
|
|
130
|
-
active: z.string().optional(),
|
|
131
|
-
inactive: z.string().optional()
|
|
132
|
-
});
|
|
133
|
-
var messageStepSchema = z.object({
|
|
134
|
-
...baseStepFields,
|
|
135
|
-
type: z.literal("message")
|
|
136
|
-
});
|
|
137
|
-
var stepConfigSchema = z.discriminatedUnion("type", [
|
|
138
|
-
textStepSchema,
|
|
139
|
-
selectStepSchema,
|
|
140
|
-
multiSelectStepSchema,
|
|
141
|
-
confirmStepSchema,
|
|
142
|
-
passwordStepSchema,
|
|
143
|
-
numberStepSchema,
|
|
144
|
-
searchStepSchema,
|
|
145
|
-
editorStepSchema,
|
|
146
|
-
pathStepSchema,
|
|
147
|
-
toggleStepSchema,
|
|
148
|
-
messageStepSchema
|
|
149
|
-
]);
|
|
150
|
-
var hexColorSchema = z.string().regex(
|
|
151
|
-
/^#[0-9a-fA-F]{6}$/,
|
|
152
|
-
"Must be a 6-digit hex color (e.g., #FF0000)"
|
|
153
|
-
);
|
|
154
|
-
var themeConfigSchema = z.object({
|
|
155
|
-
tokens: z.object({
|
|
156
|
-
primary: hexColorSchema.optional(),
|
|
157
|
-
success: hexColorSchema.optional(),
|
|
158
|
-
error: hexColorSchema.optional(),
|
|
159
|
-
warning: hexColorSchema.optional(),
|
|
160
|
-
info: hexColorSchema.optional(),
|
|
161
|
-
muted: hexColorSchema.optional(),
|
|
162
|
-
accent: hexColorSchema.optional()
|
|
163
|
-
}).optional(),
|
|
164
|
-
icons: z.object({
|
|
165
|
-
step: z.string().optional(),
|
|
166
|
-
stepDone: z.string().optional(),
|
|
167
|
-
stepPending: z.string().optional(),
|
|
168
|
-
pointer: z.string().optional()
|
|
169
|
-
}).optional()
|
|
170
|
-
});
|
|
171
|
-
var preFlightCheckSchema = z.object({
|
|
172
|
-
name: z.string(),
|
|
173
|
-
run: z.string(),
|
|
174
|
-
message: z.string()
|
|
175
|
-
});
|
|
176
|
-
var actionConfigSchema = z.object({
|
|
177
|
-
name: z.string().optional(),
|
|
178
|
-
run: z.string(),
|
|
179
|
-
when: conditionSchema.optional()
|
|
180
|
-
});
|
|
181
|
-
var wizardConfigSchema = z.object({
|
|
182
|
-
meta: z.object({
|
|
183
|
-
name: z.string(),
|
|
184
|
-
version: z.string().optional(),
|
|
185
|
-
description: z.string().optional()
|
|
186
|
-
}),
|
|
187
|
-
theme: themeConfigSchema.optional(),
|
|
188
|
-
steps: z.array(stepConfigSchema).min(1),
|
|
189
|
-
output: z.object({
|
|
190
|
-
format: z.enum(["json", "env", "yaml"]),
|
|
191
|
-
path: z.string().optional()
|
|
192
|
-
}).optional(),
|
|
193
|
-
extends: z.string().optional(),
|
|
194
|
-
checks: z.array(preFlightCheckSchema).optional(),
|
|
195
|
-
actions: z.array(actionConfigSchema).optional()
|
|
196
|
-
}).superRefine((config, ctx) => {
|
|
197
|
-
const stepIds = /* @__PURE__ */ new Set();
|
|
198
|
-
for (const step of config.steps) {
|
|
199
|
-
if (stepIds.has(step.id)) {
|
|
200
|
-
ctx.addIssue({
|
|
201
|
-
code: z.ZodIssueCode.custom,
|
|
202
|
-
message: `Duplicate step ID: "${step.id}"`,
|
|
203
|
-
path: ["steps"]
|
|
204
|
-
});
|
|
205
|
-
}
|
|
206
|
-
stepIds.add(step.id);
|
|
207
|
-
}
|
|
208
|
-
config.steps.forEach((step, i) => {
|
|
209
|
-
if (step.next && step.next !== "__done__" && !stepIds.has(step.next)) {
|
|
210
|
-
ctx.addIssue({
|
|
211
|
-
code: z.ZodIssueCode.custom,
|
|
212
|
-
message: `Step "${step.id}" references unknown next step: "${step.next}"`,
|
|
213
|
-
path: ["steps", i, "next"]
|
|
214
|
-
});
|
|
215
|
-
}
|
|
216
|
-
if (step.type === "select" && step.routes) {
|
|
217
|
-
for (const [key, target] of Object.entries(step.routes)) {
|
|
218
|
-
if (target !== "__done__" && !stepIds.has(target)) {
|
|
219
|
-
ctx.addIssue({
|
|
220
|
-
code: z.ZodIssueCode.custom,
|
|
221
|
-
message: `Step "${step.id}" route "${key}" references unknown step: "${target}"`,
|
|
222
|
-
path: ["steps", i, "routes", key]
|
|
223
|
-
});
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
if (step.when) {
|
|
228
|
-
collectConditionFieldIssues(step.when, stepIds, ctx, ["steps", i, "when"]);
|
|
229
|
-
}
|
|
230
|
-
if (step.type === "select" && step.routes && step.options) {
|
|
231
|
-
const optionValues = /* @__PURE__ */ new Set();
|
|
232
|
-
for (const o of step.options) {
|
|
233
|
-
if ("value" in o) {
|
|
234
|
-
optionValues.add(o.value);
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
for (const routeKey of Object.keys(step.routes)) {
|
|
238
|
-
if (!optionValues.has(routeKey)) {
|
|
239
|
-
ctx.addIssue({
|
|
240
|
-
code: z.ZodIssueCode.custom,
|
|
241
|
-
message: `Step "${step.id}" route key "${routeKey}" does not match any option value`,
|
|
242
|
-
path: ["steps", i, "routes", routeKey]
|
|
243
|
-
});
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
}
|
|
247
|
-
if (step.type === "select" || step.type === "multiselect" || step.type === "search") {
|
|
248
|
-
const hasOptions = step.options !== void 0;
|
|
249
|
-
const hasOptionsFrom = step.optionsFrom !== void 0;
|
|
250
|
-
if (hasOptions && hasOptionsFrom) {
|
|
251
|
-
ctx.addIssue({
|
|
252
|
-
code: z.ZodIssueCode.custom,
|
|
253
|
-
message: `Step "${step.id}" has both "options" and "optionsFrom" \u2014 only one is allowed`,
|
|
254
|
-
path: ["steps", i]
|
|
255
|
-
});
|
|
256
|
-
}
|
|
257
|
-
if (!hasOptions && !hasOptionsFrom) {
|
|
258
|
-
ctx.addIssue({
|
|
259
|
-
code: z.ZodIssueCode.custom,
|
|
260
|
-
message: `Step "${step.id}" must have either "options" or "optionsFrom"`,
|
|
261
|
-
path: ["steps", i]
|
|
262
|
-
});
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
if ((step.type === "number" || step.type === "multiselect") && step.min !== void 0 && step.max !== void 0 && step.min > step.max) {
|
|
266
|
-
ctx.addIssue({
|
|
267
|
-
code: z.ZodIssueCode.custom,
|
|
268
|
-
message: `Step "${step.id}" has min (${String(step.min)}) greater than max (${String(step.max)})`,
|
|
269
|
-
path: ["steps", i]
|
|
270
|
-
});
|
|
271
|
-
}
|
|
272
|
-
});
|
|
273
|
-
if (config.actions) {
|
|
274
|
-
config.actions.forEach((action, i) => {
|
|
275
|
-
if (action.when) {
|
|
276
|
-
collectConditionFieldIssues(action.when, stepIds, ctx, ["actions", i, "when"]);
|
|
277
|
-
}
|
|
278
|
-
});
|
|
279
|
-
}
|
|
280
|
-
});
|
|
281
13
|
function collectConditionFieldIssues(condition, validIds, ctx, path) {
|
|
282
14
|
if ("field" in condition) {
|
|
283
15
|
const fieldRoot = condition.field.split(".")[0];
|
|
@@ -310,13 +42,316 @@ function parseWizardConfig(raw) {
|
|
|
310
42
|
const result = wizardConfigSchema.parse(raw);
|
|
311
43
|
return result;
|
|
312
44
|
}
|
|
45
|
+
var conditionSchema, validationRuleSchema, selectOptionSchema, separatorOptionSchema, selectChoiceSchema, baseStepFields, textStepSchema, selectStepSchema, multiSelectStepSchema, confirmStepSchema, passwordStepSchema, numberStepSchema, searchStepSchema, editorStepSchema, pathStepSchema, toggleStepSchema, messageStepSchema, noteStepSchema, stepConfigSchema, hexColorSchema, themeConfigSchema, preFlightCheckSchema, actionConfigSchema, wizardConfigSchema;
|
|
46
|
+
var init_schema = __esm({
|
|
47
|
+
"src/schema.ts"() {
|
|
48
|
+
"use strict";
|
|
49
|
+
conditionSchema = z.lazy(() => {
|
|
50
|
+
const fieldEquals = z.object({ field: z.string(), equals: z.unknown() }).strict();
|
|
51
|
+
const fieldNotEquals = z.object({ field: z.string(), notEquals: z.unknown() }).strict();
|
|
52
|
+
const fieldIncludes = z.object({ field: z.string(), includes: z.unknown() }).strict();
|
|
53
|
+
const fieldNotIncludes = z.object({ field: z.string(), notIncludes: z.unknown() }).strict();
|
|
54
|
+
const fieldGreaterThan = z.object({ field: z.string(), greaterThan: z.number() }).strict();
|
|
55
|
+
const fieldLessThan = z.object({ field: z.string(), lessThan: z.number() }).strict();
|
|
56
|
+
const fieldIsEmpty = z.object({ field: z.string(), isEmpty: z.literal(true) }).strict();
|
|
57
|
+
const fieldIsNotEmpty = z.object({ field: z.string(), isNotEmpty: z.literal(true) }).strict();
|
|
58
|
+
const allCondition = z.object({ all: z.array(conditionSchema) }).strict();
|
|
59
|
+
const anyCondition = z.object({ any: z.array(conditionSchema) }).strict();
|
|
60
|
+
const notCondition = z.object({ not: conditionSchema }).strict();
|
|
61
|
+
return z.union([
|
|
62
|
+
fieldEquals,
|
|
63
|
+
fieldNotEquals,
|
|
64
|
+
fieldIncludes,
|
|
65
|
+
fieldNotIncludes,
|
|
66
|
+
fieldGreaterThan,
|
|
67
|
+
fieldLessThan,
|
|
68
|
+
fieldIsEmpty,
|
|
69
|
+
fieldIsNotEmpty,
|
|
70
|
+
allCondition,
|
|
71
|
+
anyCondition,
|
|
72
|
+
notCondition
|
|
73
|
+
]);
|
|
74
|
+
});
|
|
75
|
+
validationRuleSchema = z.discriminatedUnion("rule", [
|
|
76
|
+
z.object({ rule: z.literal("required"), message: z.string().optional() }),
|
|
77
|
+
z.object({ rule: z.literal("minLength"), value: z.number(), message: z.string().optional() }),
|
|
78
|
+
z.object({ rule: z.literal("maxLength"), value: z.number(), message: z.string().optional() }),
|
|
79
|
+
z.object({ rule: z.literal("pattern"), value: z.string(), message: z.string().optional() }),
|
|
80
|
+
z.object({ rule: z.literal("min"), value: z.number(), message: z.string().optional() }),
|
|
81
|
+
z.object({ rule: z.literal("max"), value: z.number(), message: z.string().optional() })
|
|
82
|
+
]);
|
|
83
|
+
selectOptionSchema = z.object({
|
|
84
|
+
value: z.string(),
|
|
85
|
+
label: z.string(),
|
|
86
|
+
hint: z.string().optional(),
|
|
87
|
+
disabled: z.union([z.boolean(), z.string()]).optional()
|
|
88
|
+
});
|
|
89
|
+
separatorOptionSchema = z.object({
|
|
90
|
+
separator: z.string()
|
|
91
|
+
});
|
|
92
|
+
selectChoiceSchema = z.union([selectOptionSchema, separatorOptionSchema]);
|
|
93
|
+
baseStepFields = {
|
|
94
|
+
id: z.string(),
|
|
95
|
+
message: z.string(),
|
|
96
|
+
description: z.string().optional(),
|
|
97
|
+
next: z.string().optional(),
|
|
98
|
+
when: conditionSchema.optional(),
|
|
99
|
+
keepValuesOnPrevious: z.boolean().optional(),
|
|
100
|
+
required: z.boolean().optional(),
|
|
101
|
+
group: z.string().optional()
|
|
102
|
+
};
|
|
103
|
+
textStepSchema = z.object({
|
|
104
|
+
...baseStepFields,
|
|
105
|
+
type: z.literal("text"),
|
|
106
|
+
placeholder: z.string().optional(),
|
|
107
|
+
default: z.string().optional(),
|
|
108
|
+
validate: z.array(validationRuleSchema).optional()
|
|
109
|
+
});
|
|
110
|
+
selectStepSchema = z.object({
|
|
111
|
+
...baseStepFields,
|
|
112
|
+
type: z.literal("select"),
|
|
113
|
+
options: z.array(selectChoiceSchema).min(1).optional(),
|
|
114
|
+
optionsFrom: z.string().optional(),
|
|
115
|
+
default: z.string().optional(),
|
|
116
|
+
routes: z.record(z.string(), z.string()).optional(),
|
|
117
|
+
pageSize: z.number().int().positive().optional(),
|
|
118
|
+
loop: z.boolean().optional()
|
|
119
|
+
});
|
|
120
|
+
multiSelectStepSchema = z.object({
|
|
121
|
+
...baseStepFields,
|
|
122
|
+
type: z.literal("multiselect"),
|
|
123
|
+
options: z.array(selectChoiceSchema).min(1).optional(),
|
|
124
|
+
optionsFrom: z.string().optional(),
|
|
125
|
+
default: z.array(z.string()).optional(),
|
|
126
|
+
min: z.number().int().nonnegative().optional(),
|
|
127
|
+
max: z.number().int().positive().optional(),
|
|
128
|
+
pageSize: z.number().int().positive().optional(),
|
|
129
|
+
loop: z.boolean().optional()
|
|
130
|
+
});
|
|
131
|
+
confirmStepSchema = z.object({
|
|
132
|
+
...baseStepFields,
|
|
133
|
+
type: z.literal("confirm"),
|
|
134
|
+
default: z.boolean().optional()
|
|
135
|
+
});
|
|
136
|
+
passwordStepSchema = z.object({
|
|
137
|
+
...baseStepFields,
|
|
138
|
+
type: z.literal("password"),
|
|
139
|
+
validate: z.array(validationRuleSchema).optional()
|
|
140
|
+
});
|
|
141
|
+
numberStepSchema = z.object({
|
|
142
|
+
...baseStepFields,
|
|
143
|
+
type: z.literal("number"),
|
|
144
|
+
default: z.number().optional(),
|
|
145
|
+
min: z.number().optional(),
|
|
146
|
+
max: z.number().optional(),
|
|
147
|
+
step: z.number().positive().optional()
|
|
148
|
+
});
|
|
149
|
+
searchStepSchema = z.object({
|
|
150
|
+
...baseStepFields,
|
|
151
|
+
type: z.literal("search"),
|
|
152
|
+
options: z.array(selectChoiceSchema).min(1).optional(),
|
|
153
|
+
optionsFrom: z.string().optional(),
|
|
154
|
+
default: z.string().optional(),
|
|
155
|
+
placeholder: z.string().optional(),
|
|
156
|
+
pageSize: z.number().int().positive().optional(),
|
|
157
|
+
loop: z.boolean().optional()
|
|
158
|
+
});
|
|
159
|
+
editorStepSchema = z.object({
|
|
160
|
+
...baseStepFields,
|
|
161
|
+
type: z.literal("editor"),
|
|
162
|
+
default: z.string().optional(),
|
|
163
|
+
validate: z.array(validationRuleSchema).optional()
|
|
164
|
+
});
|
|
165
|
+
pathStepSchema = z.object({
|
|
166
|
+
...baseStepFields,
|
|
167
|
+
type: z.literal("path"),
|
|
168
|
+
default: z.string().optional(),
|
|
169
|
+
placeholder: z.string().optional(),
|
|
170
|
+
validate: z.array(validationRuleSchema).optional()
|
|
171
|
+
});
|
|
172
|
+
toggleStepSchema = z.object({
|
|
173
|
+
...baseStepFields,
|
|
174
|
+
type: z.literal("toggle"),
|
|
175
|
+
default: z.boolean().optional(),
|
|
176
|
+
active: z.string().optional(),
|
|
177
|
+
inactive: z.string().optional()
|
|
178
|
+
});
|
|
179
|
+
messageStepSchema = z.object({
|
|
180
|
+
...baseStepFields,
|
|
181
|
+
type: z.literal("message")
|
|
182
|
+
});
|
|
183
|
+
noteStepSchema = z.object({
|
|
184
|
+
...baseStepFields,
|
|
185
|
+
type: z.literal("note")
|
|
186
|
+
});
|
|
187
|
+
stepConfigSchema = z.discriminatedUnion("type", [
|
|
188
|
+
textStepSchema,
|
|
189
|
+
selectStepSchema,
|
|
190
|
+
multiSelectStepSchema,
|
|
191
|
+
confirmStepSchema,
|
|
192
|
+
passwordStepSchema,
|
|
193
|
+
numberStepSchema,
|
|
194
|
+
searchStepSchema,
|
|
195
|
+
editorStepSchema,
|
|
196
|
+
pathStepSchema,
|
|
197
|
+
toggleStepSchema,
|
|
198
|
+
messageStepSchema,
|
|
199
|
+
noteStepSchema
|
|
200
|
+
]);
|
|
201
|
+
hexColorSchema = z.string().regex(
|
|
202
|
+
/^#[0-9a-fA-F]{6}$/,
|
|
203
|
+
"Must be a 6-digit hex color (e.g., #FF0000)"
|
|
204
|
+
);
|
|
205
|
+
themeConfigSchema = z.object({
|
|
206
|
+
preset: z.enum(["default", "catppuccin", "dracula", "nord", "tokyonight", "monokai"]).optional(),
|
|
207
|
+
tokens: z.object({
|
|
208
|
+
primary: hexColorSchema.optional(),
|
|
209
|
+
success: hexColorSchema.optional(),
|
|
210
|
+
error: hexColorSchema.optional(),
|
|
211
|
+
warning: hexColorSchema.optional(),
|
|
212
|
+
info: hexColorSchema.optional(),
|
|
213
|
+
muted: hexColorSchema.optional(),
|
|
214
|
+
accent: hexColorSchema.optional()
|
|
215
|
+
}).optional(),
|
|
216
|
+
icons: z.object({
|
|
217
|
+
step: z.string().optional(),
|
|
218
|
+
stepDone: z.string().optional(),
|
|
219
|
+
stepPending: z.string().optional(),
|
|
220
|
+
pointer: z.string().optional()
|
|
221
|
+
}).optional(),
|
|
222
|
+
spinner: z.union([
|
|
223
|
+
z.string(),
|
|
224
|
+
z.object({
|
|
225
|
+
frames: z.array(z.string()).min(1),
|
|
226
|
+
interval: z.number().positive().optional()
|
|
227
|
+
})
|
|
228
|
+
]).optional()
|
|
229
|
+
});
|
|
230
|
+
preFlightCheckSchema = z.object({
|
|
231
|
+
name: z.string(),
|
|
232
|
+
run: z.string(),
|
|
233
|
+
message: z.string()
|
|
234
|
+
});
|
|
235
|
+
actionConfigSchema = z.object({
|
|
236
|
+
name: z.string().optional(),
|
|
237
|
+
run: z.string(),
|
|
238
|
+
when: conditionSchema.optional()
|
|
239
|
+
});
|
|
240
|
+
wizardConfigSchema = z.object({
|
|
241
|
+
meta: z.object({
|
|
242
|
+
name: z.string(),
|
|
243
|
+
version: z.string().optional(),
|
|
244
|
+
description: z.string().optional(),
|
|
245
|
+
review: z.boolean().optional()
|
|
246
|
+
}),
|
|
247
|
+
theme: themeConfigSchema.optional(),
|
|
248
|
+
steps: z.array(stepConfigSchema).min(1),
|
|
249
|
+
output: z.object({
|
|
250
|
+
format: z.enum(["json", "env", "yaml"]),
|
|
251
|
+
path: z.string().optional()
|
|
252
|
+
}).optional(),
|
|
253
|
+
extends: z.string().optional(),
|
|
254
|
+
checks: z.array(preFlightCheckSchema).optional(),
|
|
255
|
+
actions: z.array(actionConfigSchema).optional(),
|
|
256
|
+
onComplete: z.string().optional()
|
|
257
|
+
}).superRefine((config, ctx) => {
|
|
258
|
+
const stepIds = /* @__PURE__ */ new Set();
|
|
259
|
+
for (const step of config.steps) {
|
|
260
|
+
if (stepIds.has(step.id)) {
|
|
261
|
+
ctx.addIssue({
|
|
262
|
+
code: z.ZodIssueCode.custom,
|
|
263
|
+
message: `Duplicate step ID: "${step.id}"`,
|
|
264
|
+
path: ["steps"]
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
stepIds.add(step.id);
|
|
268
|
+
}
|
|
269
|
+
config.steps.forEach((step, i) => {
|
|
270
|
+
if (step.next && step.next !== "__done__" && !stepIds.has(step.next)) {
|
|
271
|
+
ctx.addIssue({
|
|
272
|
+
code: z.ZodIssueCode.custom,
|
|
273
|
+
message: `Step "${step.id}" references unknown next step: "${step.next}"`,
|
|
274
|
+
path: ["steps", i, "next"]
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
if (step.type === "select" && step.routes) {
|
|
278
|
+
for (const [key, target] of Object.entries(step.routes)) {
|
|
279
|
+
if (target !== "__done__" && !stepIds.has(target)) {
|
|
280
|
+
ctx.addIssue({
|
|
281
|
+
code: z.ZodIssueCode.custom,
|
|
282
|
+
message: `Step "${step.id}" route "${key}" references unknown step: "${target}"`,
|
|
283
|
+
path: ["steps", i, "routes", key]
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
if (step.when) {
|
|
289
|
+
collectConditionFieldIssues(step.when, stepIds, ctx, ["steps", i, "when"]);
|
|
290
|
+
}
|
|
291
|
+
if (step.type === "select" && step.routes && step.options) {
|
|
292
|
+
const optionValues = /* @__PURE__ */ new Set();
|
|
293
|
+
for (const o of step.options) {
|
|
294
|
+
if ("value" in o) {
|
|
295
|
+
optionValues.add(o.value);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
for (const routeKey of Object.keys(step.routes)) {
|
|
299
|
+
if (!optionValues.has(routeKey)) {
|
|
300
|
+
ctx.addIssue({
|
|
301
|
+
code: z.ZodIssueCode.custom,
|
|
302
|
+
message: `Step "${step.id}" route key "${routeKey}" does not match any option value`,
|
|
303
|
+
path: ["steps", i, "routes", routeKey]
|
|
304
|
+
});
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
if (step.type === "select" || step.type === "multiselect" || step.type === "search") {
|
|
309
|
+
const hasOptions = step.options !== void 0;
|
|
310
|
+
const hasOptionsFrom = step.optionsFrom !== void 0;
|
|
311
|
+
if (hasOptions && hasOptionsFrom) {
|
|
312
|
+
ctx.addIssue({
|
|
313
|
+
code: z.ZodIssueCode.custom,
|
|
314
|
+
message: `Step "${step.id}" has both "options" and "optionsFrom" \u2014 only one is allowed`,
|
|
315
|
+
path: ["steps", i]
|
|
316
|
+
});
|
|
317
|
+
}
|
|
318
|
+
if (!hasOptions && !hasOptionsFrom) {
|
|
319
|
+
ctx.addIssue({
|
|
320
|
+
code: z.ZodIssueCode.custom,
|
|
321
|
+
message: `Step "${step.id}" must have either "options" or "optionsFrom"`,
|
|
322
|
+
path: ["steps", i]
|
|
323
|
+
});
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
if ((step.type === "number" || step.type === "multiselect") && step.min !== void 0 && step.max !== void 0 && step.min > step.max) {
|
|
327
|
+
ctx.addIssue({
|
|
328
|
+
code: z.ZodIssueCode.custom,
|
|
329
|
+
message: `Step "${step.id}" has min (${String(step.min)}) greater than max (${String(step.max)})`,
|
|
330
|
+
path: ["steps", i]
|
|
331
|
+
});
|
|
332
|
+
}
|
|
333
|
+
});
|
|
334
|
+
if (config.actions) {
|
|
335
|
+
config.actions.forEach((action, i) => {
|
|
336
|
+
if (action.when) {
|
|
337
|
+
collectConditionFieldIssues(action.when, stepIds, ctx, ["actions", i, "when"]);
|
|
338
|
+
}
|
|
339
|
+
});
|
|
340
|
+
}
|
|
341
|
+
});
|
|
342
|
+
}
|
|
343
|
+
});
|
|
313
344
|
|
|
314
345
|
// src/parser.ts
|
|
346
|
+
var parser_exports = {};
|
|
347
|
+
__export(parser_exports, {
|
|
348
|
+
loadWizardConfig: () => loadWizardConfig,
|
|
349
|
+
parseWizardYAML: () => parseWizardYAML
|
|
350
|
+
});
|
|
315
351
|
import { cosmiconfig } from "cosmiconfig";
|
|
316
352
|
import { readFileSync } from "fs";
|
|
317
353
|
import { dirname, resolve, isAbsolute } from "path";
|
|
318
354
|
import { parse as parseYAML } from "yaml";
|
|
319
|
-
var DONE_SENTINEL = "__done__";
|
|
320
355
|
function buildStepGraph(steps) {
|
|
321
356
|
const graph = /* @__PURE__ */ new Map();
|
|
322
357
|
for (let i = 0; i < steps.length; i++) {
|
|
@@ -395,7 +430,6 @@ function mergeConfigs(parent, child) {
|
|
|
395
430
|
actions: child.actions ?? parent.actions
|
|
396
431
|
};
|
|
397
432
|
}
|
|
398
|
-
var OPTION_STEP_TYPES = /* @__PURE__ */ new Set(["select", "multiselect", "search"]);
|
|
399
433
|
function resolveOptionsFromSteps(raw, configDir) {
|
|
400
434
|
const steps = raw["steps"];
|
|
401
435
|
if (!Array.isArray(steps)) return;
|
|
@@ -491,6 +525,19 @@ function parseWizardYAML(yamlString) {
|
|
|
491
525
|
detectCycles(config);
|
|
492
526
|
return config;
|
|
493
527
|
}
|
|
528
|
+
var DONE_SENTINEL, OPTION_STEP_TYPES;
|
|
529
|
+
var init_parser = __esm({
|
|
530
|
+
"src/parser.ts"() {
|
|
531
|
+
"use strict";
|
|
532
|
+
init_schema();
|
|
533
|
+
DONE_SENTINEL = "__done__";
|
|
534
|
+
OPTION_STEP_TYPES = /* @__PURE__ */ new Set(["select", "multiselect", "search"]);
|
|
535
|
+
}
|
|
536
|
+
});
|
|
537
|
+
|
|
538
|
+
// src/index.ts
|
|
539
|
+
init_schema();
|
|
540
|
+
init_parser();
|
|
494
541
|
|
|
495
542
|
// src/conditions.ts
|
|
496
543
|
function isRecord(value) {
|
|
@@ -770,6 +817,102 @@ function applyValidationRule(rule, value) {
|
|
|
770
817
|
|
|
771
818
|
// src/theme.ts
|
|
772
819
|
import chalk from "chalk";
|
|
820
|
+
|
|
821
|
+
// src/themes/presets.ts
|
|
822
|
+
var THEME_PRESETS = {
|
|
823
|
+
default: {
|
|
824
|
+
primary: "#7C3AED",
|
|
825
|
+
success: "#10B981",
|
|
826
|
+
error: "#EF4444",
|
|
827
|
+
warning: "#F59E0B",
|
|
828
|
+
info: "#3B82F6",
|
|
829
|
+
muted: "#6B7280",
|
|
830
|
+
accent: "#8B5CF6"
|
|
831
|
+
},
|
|
832
|
+
catppuccin: {
|
|
833
|
+
primary: "#cba6f7",
|
|
834
|
+
success: "#a6e3a1",
|
|
835
|
+
error: "#f38ba8",
|
|
836
|
+
warning: "#fab387",
|
|
837
|
+
info: "#74c7ec",
|
|
838
|
+
muted: "#6c7086",
|
|
839
|
+
accent: "#f5c2e7"
|
|
840
|
+
},
|
|
841
|
+
dracula: {
|
|
842
|
+
primary: "#bd93f9",
|
|
843
|
+
success: "#50fa7b",
|
|
844
|
+
error: "#ff5555",
|
|
845
|
+
warning: "#ffb86c",
|
|
846
|
+
info: "#8be9fd",
|
|
847
|
+
muted: "#6272a4",
|
|
848
|
+
accent: "#ff79c6"
|
|
849
|
+
},
|
|
850
|
+
nord: {
|
|
851
|
+
primary: "#88c0d0",
|
|
852
|
+
success: "#a3be8c",
|
|
853
|
+
error: "#bf616a",
|
|
854
|
+
warning: "#ebcb8b",
|
|
855
|
+
info: "#81a1c1",
|
|
856
|
+
muted: "#4c566a",
|
|
857
|
+
accent: "#b48ead"
|
|
858
|
+
},
|
|
859
|
+
tokyonight: {
|
|
860
|
+
primary: "#7aa2f7",
|
|
861
|
+
success: "#9ece6a",
|
|
862
|
+
error: "#f7768e",
|
|
863
|
+
warning: "#e0af68",
|
|
864
|
+
info: "#7dcfff",
|
|
865
|
+
muted: "#565f89",
|
|
866
|
+
accent: "#bb9af7"
|
|
867
|
+
},
|
|
868
|
+
monokai: {
|
|
869
|
+
primary: "#ab9df2",
|
|
870
|
+
success: "#a9dc76",
|
|
871
|
+
error: "#ff6188",
|
|
872
|
+
warning: "#ffd866",
|
|
873
|
+
info: "#78dce8",
|
|
874
|
+
muted: "#727072",
|
|
875
|
+
accent: "#fc9867"
|
|
876
|
+
}
|
|
877
|
+
};
|
|
878
|
+
var PRESET_NAMES = Object.keys(THEME_PRESETS);
|
|
879
|
+
|
|
880
|
+
// src/spinners.ts
|
|
881
|
+
var spinners = {
|
|
882
|
+
dots: { interval: 80, frames: ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"] },
|
|
883
|
+
dots2: { interval: 80, frames: ["\u28FE", "\u28FD", "\u28FB", "\u28BF", "\u287F", "\u28DF", "\u28EF", "\u28F7"] },
|
|
884
|
+
line: { interval: 130, frames: ["-", "\\", "|", "/"] },
|
|
885
|
+
arc: { interval: 100, frames: ["\u25DC", "\u25E0", "\u25DD", "\u25DE", "\u25E1", "\u25DF"] },
|
|
886
|
+
circle: { interval: 80, frames: ["\u25D2", "\u25D0", "\u25D3", "\u25D1"] },
|
|
887
|
+
circleHalves: { interval: 50, frames: ["\u25D0", "\u25D3", "\u25D1", "\u25D2"] },
|
|
888
|
+
triangle: { interval: 50, frames: ["\u25E2", "\u25E3", "\u25E4", "\u25E5"] },
|
|
889
|
+
pipe: { interval: 100, frames: ["\u2524", "\u2518", "\u2534", "\u2514", "\u251C", "\u250C", "\u252C", "\u2510"] },
|
|
890
|
+
arrow: { interval: 100, frames: ["\u2190", "\u2196", "\u2191", "\u2197", "\u2192", "\u2198", "\u2193", "\u2199"] },
|
|
891
|
+
arrow3: { interval: 120, frames: ["\u25B9\u25B9\u25B9\u25B9\u25B9", "\u25B8\u25B9\u25B9\u25B9\u25B9", "\u25B9\u25B8\u25B9\u25B9\u25B9", "\u25B9\u25B9\u25B8\u25B9\u25B9", "\u25B9\u25B9\u25B9\u25B8\u25B9", "\u25B9\u25B9\u25B9\u25B9\u25B8"] },
|
|
892
|
+
bouncingBar: { interval: 80, frames: ["[ ]", "[= ]", "[== ]", "[=== ]", "[====]", "[ ===]", "[ ==]", "[ =]", "[ ]", "[ =]", "[ ==]", "[ ===]", "[====]", "[=== ]", "[== ]", "[= ]"] },
|
|
893
|
+
bouncingBall: { interval: 80, frames: ["( \u25CF )", "( \u25CF )", "( \u25CF )", "( \u25CF )", "( \u25CF)", "( \u25CF )", "( \u25CF )", "( \u25CF )", "( \u25CF )", "(\u25CF )"] },
|
|
894
|
+
simpleDots: { interval: 400, frames: [". ", ".. ", "...", " "] },
|
|
895
|
+
aesthetic: { interval: 80, frames: ["\u25B0\u25B1\u25B1\u25B1\u25B1\u25B1\u25B1", "\u25B0\u25B0\u25B1\u25B1\u25B1\u25B1\u25B1", "\u25B0\u25B0\u25B0\u25B1\u25B1\u25B1\u25B1", "\u25B0\u25B0\u25B0\u25B0\u25B1\u25B1\u25B1", "\u25B0\u25B0\u25B0\u25B0\u25B0\u25B1\u25B1", "\u25B0\u25B0\u25B0\u25B0\u25B0\u25B0\u25B1", "\u25B0\u25B0\u25B0\u25B0\u25B0\u25B0\u25B0", "\u25B0\u25B1\u25B1\u25B1\u25B1\u25B1\u25B1"] },
|
|
896
|
+
star: { interval: 70, frames: ["\u2736", "\u2738", "\u2739", "\u273A", "\u2739", "\u2737"] }
|
|
897
|
+
};
|
|
898
|
+
var DEFAULT_SPINNER = "circle";
|
|
899
|
+
function resolveSpinner(config) {
|
|
900
|
+
if (!config) {
|
|
901
|
+
return spinners[DEFAULT_SPINNER];
|
|
902
|
+
}
|
|
903
|
+
if (typeof config === "string") {
|
|
904
|
+
if (config in spinners) {
|
|
905
|
+
return spinners[config];
|
|
906
|
+
}
|
|
907
|
+
throw new Error(`Unknown spinner preset: "${config}". Available: ${Object.keys(spinners).join(", ")}`);
|
|
908
|
+
}
|
|
909
|
+
return {
|
|
910
|
+
frames: config.frames,
|
|
911
|
+
interval: config.interval ?? 80
|
|
912
|
+
};
|
|
913
|
+
}
|
|
914
|
+
|
|
915
|
+
// src/theme.ts
|
|
773
916
|
var DEFAULT_TOKENS = {
|
|
774
917
|
primary: "#5B9BD5",
|
|
775
918
|
success: "#6BCB77",
|
|
@@ -786,7 +929,8 @@ var DEFAULT_ICONS = {
|
|
|
786
929
|
pointer: "\u203A"
|
|
787
930
|
};
|
|
788
931
|
function resolveTheme(themeConfig) {
|
|
789
|
-
const
|
|
932
|
+
const presetTokens = themeConfig?.preset ? THEME_PRESETS[themeConfig.preset] : void 0;
|
|
933
|
+
const tokens = { ...DEFAULT_TOKENS, ...presetTokens, ...themeConfig?.tokens };
|
|
790
934
|
const icons = { ...DEFAULT_ICONS, ...themeConfig?.icons };
|
|
791
935
|
return {
|
|
792
936
|
primary: chalk.hex(tokens.primary),
|
|
@@ -797,7 +941,8 @@ function resolveTheme(themeConfig) {
|
|
|
797
941
|
muted: chalk.hex(tokens.muted),
|
|
798
942
|
accent: chalk.hex(tokens.accent),
|
|
799
943
|
bold: chalk.bold,
|
|
800
|
-
icons
|
|
944
|
+
icons,
|
|
945
|
+
spinner: resolveSpinner(themeConfig?.spinner)
|
|
801
946
|
};
|
|
802
947
|
}
|
|
803
948
|
|
|
@@ -824,6 +969,8 @@ function resolveEnvDefaultBoolean(value) {
|
|
|
824
969
|
|
|
825
970
|
// src/runner.ts
|
|
826
971
|
import { execSync } from "child_process";
|
|
972
|
+
import { resolve as resolve2, dirname as dirname2 } from "path";
|
|
973
|
+
import { pathToFileURL } from "url";
|
|
827
974
|
|
|
828
975
|
// src/renderers/inquirer.ts
|
|
829
976
|
import {
|
|
@@ -1024,6 +1171,17 @@ function resolveTemplate(template, answers) {
|
|
|
1024
1171
|
return _match;
|
|
1025
1172
|
});
|
|
1026
1173
|
}
|
|
1174
|
+
function resolveTemplateStrict(template, answers) {
|
|
1175
|
+
return template.replace(/\{\{([^}]+)\}\}/g, (_match, key) => {
|
|
1176
|
+
const trimmedKey = key.trim();
|
|
1177
|
+
if (!(trimmedKey in answers)) {
|
|
1178
|
+
throw new Error(`Action references unknown step "${trimmedKey}"`);
|
|
1179
|
+
}
|
|
1180
|
+
const value = answers[trimmedKey];
|
|
1181
|
+
if (Array.isArray(value)) return value.join(", ");
|
|
1182
|
+
return String(value);
|
|
1183
|
+
});
|
|
1184
|
+
}
|
|
1027
1185
|
|
|
1028
1186
|
// src/banner.ts
|
|
1029
1187
|
import figlet from "figlet";
|
|
@@ -1131,14 +1289,66 @@ function clearCache(wizardName, customDir) {
|
|
|
1131
1289
|
}
|
|
1132
1290
|
}
|
|
1133
1291
|
|
|
1134
|
-
// src/
|
|
1135
|
-
import { readFileSync as readFileSync3, writeFileSync as writeFileSync2,
|
|
1292
|
+
// src/progress.ts
|
|
1293
|
+
import { mkdirSync as mkdirSync2, readFileSync as readFileSync3, writeFileSync as writeFileSync2, unlinkSync as unlinkSync2 } from "fs";
|
|
1136
1294
|
import { join as join2 } from "path";
|
|
1137
1295
|
import { homedir as homedir2 } from "os";
|
|
1138
|
-
var
|
|
1296
|
+
var DEFAULT_PROGRESS_DIR = join2(homedir2(), ".config", "grimoire", "progress");
|
|
1297
|
+
function getProgressFilePath(wizardName, customDir) {
|
|
1298
|
+
const dir = customDir ?? DEFAULT_PROGRESS_DIR;
|
|
1299
|
+
return join2(dir, `${slugify(wizardName)}.json`);
|
|
1300
|
+
}
|
|
1301
|
+
function saveProgress(wizardName, state, customDir, excludeStepIds) {
|
|
1302
|
+
try {
|
|
1303
|
+
const dir = customDir ?? DEFAULT_PROGRESS_DIR;
|
|
1304
|
+
mkdirSync2(dir, { recursive: true });
|
|
1305
|
+
const excludeSet = new Set(excludeStepIds ?? []);
|
|
1306
|
+
const filteredAnswers = {};
|
|
1307
|
+
for (const [key, value] of Object.entries(state.answers)) {
|
|
1308
|
+
if (!excludeSet.has(key)) {
|
|
1309
|
+
filteredAnswers[key] = value;
|
|
1310
|
+
}
|
|
1311
|
+
}
|
|
1312
|
+
const progress = {
|
|
1313
|
+
currentStepId: state.currentStepId,
|
|
1314
|
+
answers: filteredAnswers,
|
|
1315
|
+
history: state.history,
|
|
1316
|
+
savedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1317
|
+
};
|
|
1318
|
+
const filePath = getProgressFilePath(wizardName, customDir);
|
|
1319
|
+
writeFileSync2(filePath, JSON.stringify(progress, null, 2) + "\n", "utf-8");
|
|
1320
|
+
} catch {
|
|
1321
|
+
}
|
|
1322
|
+
}
|
|
1323
|
+
function loadProgress(wizardName, customDir) {
|
|
1324
|
+
try {
|
|
1325
|
+
const filePath = getProgressFilePath(wizardName, customDir);
|
|
1326
|
+
const raw = readFileSync3(filePath, "utf-8");
|
|
1327
|
+
const parsed = JSON.parse(raw);
|
|
1328
|
+
if (typeof parsed === "object" && parsed !== null && !Array.isArray(parsed) && "currentStepId" in parsed && "answers" in parsed && "history" in parsed && "savedAt" in parsed) {
|
|
1329
|
+
return parsed;
|
|
1330
|
+
}
|
|
1331
|
+
return null;
|
|
1332
|
+
} catch {
|
|
1333
|
+
return null;
|
|
1334
|
+
}
|
|
1335
|
+
}
|
|
1336
|
+
function clearProgress(wizardName, customDir) {
|
|
1337
|
+
try {
|
|
1338
|
+
const filePath = getProgressFilePath(wizardName, customDir);
|
|
1339
|
+
unlinkSync2(filePath);
|
|
1340
|
+
} catch {
|
|
1341
|
+
}
|
|
1342
|
+
}
|
|
1343
|
+
|
|
1344
|
+
// src/mru.ts
|
|
1345
|
+
import { readFileSync as readFileSync4, writeFileSync as writeFileSync3, mkdirSync as mkdirSync3, existsSync } from "fs";
|
|
1346
|
+
import { join as join3 } from "path";
|
|
1347
|
+
import { homedir as homedir3 } from "os";
|
|
1348
|
+
var MRU_DIR = join3(homedir3(), ".config", "grimoire", "mru");
|
|
1139
1349
|
function getMruFilePath(wizardName) {
|
|
1140
1350
|
const safeName = wizardName.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
1141
|
-
return
|
|
1351
|
+
return join3(MRU_DIR, `${safeName}.json`);
|
|
1142
1352
|
}
|
|
1143
1353
|
function loadMruData(wizardName) {
|
|
1144
1354
|
const filePath = getMruFilePath(wizardName);
|
|
@@ -1146,7 +1356,7 @@ function loadMruData(wizardName) {
|
|
|
1146
1356
|
if (!existsSync(filePath)) {
|
|
1147
1357
|
return {};
|
|
1148
1358
|
}
|
|
1149
|
-
const raw =
|
|
1359
|
+
const raw = readFileSync4(filePath, "utf-8");
|
|
1150
1360
|
const parsed = JSON.parse(raw);
|
|
1151
1361
|
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
1152
1362
|
return {};
|
|
@@ -1159,8 +1369,8 @@ function loadMruData(wizardName) {
|
|
|
1159
1369
|
function saveMruData(wizardName, data) {
|
|
1160
1370
|
const filePath = getMruFilePath(wizardName);
|
|
1161
1371
|
try {
|
|
1162
|
-
|
|
1163
|
-
|
|
1372
|
+
mkdirSync3(MRU_DIR, { recursive: true });
|
|
1373
|
+
writeFileSync3(filePath, JSON.stringify(data, null, 2) + "\n", "utf-8");
|
|
1164
1374
|
} catch {
|
|
1165
1375
|
}
|
|
1166
1376
|
}
|
|
@@ -1216,20 +1426,28 @@ function clearMruData(wizardName) {
|
|
|
1216
1426
|
const filePath = getMruFilePath(wizardName);
|
|
1217
1427
|
try {
|
|
1218
1428
|
if (existsSync(filePath)) {
|
|
1219
|
-
|
|
1429
|
+
writeFileSync3(filePath, "{}", "utf-8");
|
|
1220
1430
|
}
|
|
1221
1431
|
} catch {
|
|
1222
1432
|
}
|
|
1223
1433
|
}
|
|
1224
1434
|
|
|
1225
1435
|
// src/runner.ts
|
|
1226
|
-
function
|
|
1436
|
+
function emitEvent(renderer, event, theme) {
|
|
1437
|
+
if (renderer.onEvent) {
|
|
1438
|
+
renderer.onEvent(event, theme);
|
|
1439
|
+
}
|
|
1440
|
+
}
|
|
1441
|
+
function runPreFlightChecks(checks, theme, renderer) {
|
|
1442
|
+
if (renderer) emitEvent(renderer, { type: "checks:start", checks }, theme);
|
|
1227
1443
|
for (const check of checks) {
|
|
1228
1444
|
try {
|
|
1229
1445
|
execSync(check.run, { stdio: "pipe" });
|
|
1230
1446
|
console.log(` ${theme.success("\u2713")} ${check.name}`);
|
|
1447
|
+
if (renderer) emitEvent(renderer, { type: "check:pass", name: check.name }, theme);
|
|
1231
1448
|
} catch {
|
|
1232
1449
|
console.log(` ${theme.error("\u2717")} ${check.name}: ${check.message}`);
|
|
1450
|
+
if (renderer) emitEvent(renderer, { type: "check:fail", name: check.name, message: check.message }, theme);
|
|
1233
1451
|
throw new Error(`Pre-flight check failed: ${check.name} \u2014 ${check.message}`);
|
|
1234
1452
|
}
|
|
1235
1453
|
}
|
|
@@ -1239,7 +1457,7 @@ function getMockValue(step, mockAnswers) {
|
|
|
1239
1457
|
if (step.id in mockAnswers) {
|
|
1240
1458
|
return mockAnswers[step.id];
|
|
1241
1459
|
}
|
|
1242
|
-
if (step.type === "message") {
|
|
1460
|
+
if (step.type === "message" || step.type === "note") {
|
|
1243
1461
|
return true;
|
|
1244
1462
|
}
|
|
1245
1463
|
const defaultValue = getStepDefault(step);
|
|
@@ -1267,6 +1485,7 @@ function getStepDefault(step) {
|
|
|
1267
1485
|
return step.default;
|
|
1268
1486
|
case "password":
|
|
1269
1487
|
case "message":
|
|
1488
|
+
case "note":
|
|
1270
1489
|
return void 0;
|
|
1271
1490
|
}
|
|
1272
1491
|
}
|
|
@@ -1280,6 +1499,21 @@ async function runWizard(config, options) {
|
|
|
1280
1499
|
const cacheDir = typeof options?.cache === "object" ? options.cache.dir : void 0;
|
|
1281
1500
|
const mruEnabled = !isMock && options?.mru !== false;
|
|
1282
1501
|
let state = createWizardState(config);
|
|
1502
|
+
const resumeEnabled = !isMock && options?.resume !== false;
|
|
1503
|
+
if (resumeEnabled) {
|
|
1504
|
+
const saved = loadProgress(config.meta.name);
|
|
1505
|
+
if (saved) {
|
|
1506
|
+
const stepExists = config.steps.some((s) => s.id === saved.currentStepId);
|
|
1507
|
+
if (stepExists) {
|
|
1508
|
+
state = {
|
|
1509
|
+
...state,
|
|
1510
|
+
currentStepId: saved.currentStepId,
|
|
1511
|
+
answers: { ...state.answers, ...saved.answers },
|
|
1512
|
+
history: saved.history
|
|
1513
|
+
};
|
|
1514
|
+
}
|
|
1515
|
+
}
|
|
1516
|
+
}
|
|
1283
1517
|
const cachedAnswers = cacheEnabled ? loadCachedAnswers(config.meta.name, cacheDir) : void 0;
|
|
1284
1518
|
const userPlugins = options?.plugins;
|
|
1285
1519
|
if (userPlugins) {
|
|
@@ -1289,103 +1523,174 @@ async function runWizard(config, options) {
|
|
|
1289
1523
|
}
|
|
1290
1524
|
try {
|
|
1291
1525
|
if (!isMock && config.checks && config.checks.length > 0) {
|
|
1292
|
-
runPreFlightChecks(config.checks, theme);
|
|
1526
|
+
runPreFlightChecks(config.checks, theme, renderer);
|
|
1293
1527
|
}
|
|
1294
1528
|
if (!quiet) {
|
|
1295
1529
|
printWizardHeader(config, theme, options?.plain);
|
|
1296
1530
|
}
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1531
|
+
const visibleStepsForCount = getVisibleSteps(config, state.answers);
|
|
1532
|
+
emitEvent(renderer, { type: "session:start", wizard: config.meta.name, description: config.meta.description, totalSteps: visibleStepsForCount.length }, theme);
|
|
1533
|
+
let needsReview = true;
|
|
1534
|
+
while (needsReview) {
|
|
1535
|
+
let previousGroup;
|
|
1536
|
+
while (state.status === "running") {
|
|
1537
|
+
const visibleSteps = getVisibleSteps(config, state.answers);
|
|
1538
|
+
const currentStep = config.steps.find((s) => s.id === state.currentStepId);
|
|
1539
|
+
if (!currentStep) {
|
|
1540
|
+
throw new Error(`Current step not found: "${state.currentStepId}"`);
|
|
1541
|
+
}
|
|
1305
1542
|
if (currentStep.group !== void 0 && currentStep.group !== previousGroup) {
|
|
1306
1543
|
const resolvedGroup = resolveTemplate(currentStep.group, state.answers);
|
|
1307
|
-
|
|
1544
|
+
if (!isMock) {
|
|
1545
|
+
renderer.renderGroupHeader(resolvedGroup, theme);
|
|
1546
|
+
}
|
|
1547
|
+
emitEvent(renderer, { type: "group:start", group: resolvedGroup }, theme);
|
|
1308
1548
|
}
|
|
1309
1549
|
previousGroup = currentStep.group;
|
|
1310
1550
|
const stepIndex = visibleSteps.findIndex((s) => s.id === state.currentStepId);
|
|
1311
1551
|
const resolvedMessage = resolveTemplate(currentStep.message, state.answers);
|
|
1312
1552
|
const resolvedDescription = currentStep.description ? resolveTemplate(currentStep.description, state.answers) : void 0;
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
const
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1553
|
+
if (!isMock) {
|
|
1554
|
+
renderer.renderStepHeader(stepIndex, visibleSteps.length, resolvedMessage, theme, resolvedDescription);
|
|
1555
|
+
}
|
|
1556
|
+
emitEvent(renderer, { type: "step:start", stepId: currentStep.id, stepIndex, totalVisible: visibleSteps.length, step: currentStep }, theme);
|
|
1557
|
+
if (currentStep.type === "note") {
|
|
1558
|
+
emitEvent(renderer, { type: "note", title: resolvedMessage, body: resolvedDescription ?? "" }, theme);
|
|
1559
|
+
}
|
|
1560
|
+
if (options?.onBeforeStep) {
|
|
1561
|
+
await options.onBeforeStep(currentStep.id, currentStep, state);
|
|
1562
|
+
}
|
|
1563
|
+
const pluginStep = getPluginStep(currentStep.type);
|
|
1564
|
+
const resolvedStep = pluginStep ? currentStep : resolveStepDefaults(currentStep, cachedAnswers);
|
|
1565
|
+
const withTemplate = options?.templateAnswers ? applyTemplateDefaults(resolvedStep, options.templateAnswers) : resolvedStep;
|
|
1566
|
+
const templatedStep = resolveStepTemplates(withTemplate, state.answers);
|
|
1567
|
+
const mruStep = mruEnabled ? applyMruOrdering(templatedStep, config.meta.name) : templatedStep;
|
|
1568
|
+
try {
|
|
1569
|
+
const value = isMock ? getMockValue(mruStep, mockAnswers) : pluginStep ? await pluginStep.render(toStepRecord(mruStep), state, theme) : await renderStep(renderer, mruStep, state, theme);
|
|
1570
|
+
if (pluginStep?.validate) {
|
|
1571
|
+
const pluginError = pluginStep.validate(value, toStepRecord(templatedStep));
|
|
1572
|
+
if (pluginError) {
|
|
1573
|
+
if (isMock) {
|
|
1574
|
+
throw new Error(
|
|
1575
|
+
`Mock mode: validation failed for step "${currentStep.id}": ${pluginError}`
|
|
1576
|
+
);
|
|
1577
|
+
}
|
|
1578
|
+
console.log(theme.error(`
|
|
1579
|
+
${pluginError}
|
|
1580
|
+
`));
|
|
1581
|
+
continue;
|
|
1582
|
+
}
|
|
1583
|
+
}
|
|
1584
|
+
const nextState = wizardReducer(state, { type: "NEXT", value }, config);
|
|
1585
|
+
if (nextState.errors[currentStep.id]) {
|
|
1586
|
+
const errorMsg = resolveTemplate(nextState.errors[currentStep.id] ?? "", state.answers);
|
|
1587
|
+
emitEvent(renderer, { type: "step:error", stepId: currentStep.id, error: errorMsg }, theme);
|
|
1328
1588
|
if (isMock) {
|
|
1329
1589
|
throw new Error(
|
|
1330
|
-
`Mock mode: validation failed for step "${currentStep.id}": ${
|
|
1590
|
+
`Mock mode: validation failed for step "${currentStep.id}": ${errorMsg}`
|
|
1331
1591
|
);
|
|
1332
1592
|
}
|
|
1333
1593
|
console.log(theme.error(`
|
|
1334
|
-
${
|
|
1594
|
+
${errorMsg}
|
|
1335
1595
|
`));
|
|
1596
|
+
state = { ...nextState, errors: {} };
|
|
1336
1597
|
continue;
|
|
1337
1598
|
}
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
if (isMock) {
|
|
1343
|
-
throw new Error(
|
|
1344
|
-
`Mock mode: validation failed for step "${currentStep.id}": ${errorMsg}`
|
|
1345
|
-
);
|
|
1346
|
-
}
|
|
1347
|
-
console.log(theme.error(`
|
|
1348
|
-
${errorMsg}
|
|
1349
|
-
`));
|
|
1350
|
-
state = { ...nextState, errors: {} };
|
|
1351
|
-
continue;
|
|
1352
|
-
}
|
|
1353
|
-
if (!isMock && options?.asyncValidate) {
|
|
1354
|
-
const asyncError = await options.asyncValidate(currentStep.id, value, nextState.answers);
|
|
1355
|
-
if (asyncError !== null) {
|
|
1356
|
-
console.log(theme.error(`
|
|
1599
|
+
if (!isMock && options?.asyncValidate) {
|
|
1600
|
+
const asyncError = await options.asyncValidate(currentStep.id, value, nextState.answers);
|
|
1601
|
+
if (asyncError !== null) {
|
|
1602
|
+
console.log(theme.error(`
|
|
1357
1603
|
${asyncError}
|
|
1358
1604
|
`));
|
|
1359
|
-
|
|
1360
|
-
|
|
1605
|
+
state = { ...nextState, errors: {} };
|
|
1606
|
+
continue;
|
|
1607
|
+
}
|
|
1361
1608
|
}
|
|
1609
|
+
if (options?.onAfterStep) {
|
|
1610
|
+
await options.onAfterStep(currentStep.id, value, nextState);
|
|
1611
|
+
}
|
|
1612
|
+
state = nextState;
|
|
1613
|
+
emitEvent(renderer, { type: "step:complete", stepId: currentStep.id, value, step: currentStep }, theme);
|
|
1614
|
+
if (mruEnabled && isSelectLikeStep(currentStep.type)) {
|
|
1615
|
+
recordSelection(config.meta.name, currentStep.id, value);
|
|
1616
|
+
}
|
|
1617
|
+
options?.onStepComplete?.(currentStep.id, value, state);
|
|
1618
|
+
} catch (error) {
|
|
1619
|
+
if (!isMock && isUserCancel(error)) {
|
|
1620
|
+
state = wizardReducer(state, { type: "CANCEL" }, config);
|
|
1621
|
+
options?.onCancel?.(state);
|
|
1622
|
+
const passwordStepIds = config.steps.filter((s) => s.type === "password").map((s) => s.id);
|
|
1623
|
+
saveProgress(config.meta.name, {
|
|
1624
|
+
currentStepId: state.currentStepId,
|
|
1625
|
+
answers: state.answers,
|
|
1626
|
+
history: state.history
|
|
1627
|
+
}, void 0, passwordStepIds);
|
|
1628
|
+
emitEvent(renderer, { type: "session:end", answers: state.answers, cancelled: true }, theme);
|
|
1629
|
+
if (!quiet) {
|
|
1630
|
+
console.log(theme.warning("\n Wizard cancelled.\n"));
|
|
1631
|
+
}
|
|
1632
|
+
return state.answers;
|
|
1633
|
+
}
|
|
1634
|
+
throw error;
|
|
1362
1635
|
}
|
|
1363
|
-
|
|
1364
|
-
|
|
1636
|
+
}
|
|
1637
|
+
if (config.meta.review && !isMock && state.status === "done") {
|
|
1638
|
+
const reviewLines = [];
|
|
1639
|
+
for (const step of config.steps) {
|
|
1640
|
+
const answer = state.answers[step.id];
|
|
1641
|
+
if (answer === void 0) continue;
|
|
1642
|
+
const display = step.type === "password" ? "****" : Array.isArray(answer) ? answer.map(String).join(", ") : String(answer);
|
|
1643
|
+
reviewLines.push(`${step.id}: ${display}`);
|
|
1365
1644
|
}
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1645
|
+
emitEvent(renderer, { type: "note", title: "Review your answers", body: reviewLines.join("\n") }, theme);
|
|
1646
|
+
console.log(`
|
|
1647
|
+
${theme.bold("Review your answers:")}
|
|
1648
|
+
`);
|
|
1649
|
+
for (const line of reviewLines) {
|
|
1650
|
+
console.log(` ${line}`);
|
|
1369
1651
|
}
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1652
|
+
console.log();
|
|
1653
|
+
const { confirm: confirmPrompt } = await import("@inquirer/prompts");
|
|
1654
|
+
const ok = await confirmPrompt({
|
|
1655
|
+
message: "Everything look right?",
|
|
1656
|
+
default: true
|
|
1657
|
+
});
|
|
1658
|
+
if (ok) {
|
|
1659
|
+
needsReview = false;
|
|
1660
|
+
} else {
|
|
1661
|
+
const { select: selectPrompt } = await import("@inquirer/prompts");
|
|
1662
|
+
const stepsWithAnswers = config.steps.filter(
|
|
1663
|
+
(s) => state.answers[s.id] !== void 0 && s.type !== "note" && s.type !== "message"
|
|
1664
|
+
);
|
|
1665
|
+
const stepToRevisit = await selectPrompt({
|
|
1666
|
+
message: "Which step would you like to change?",
|
|
1667
|
+
choices: stepsWithAnswers.map((s) => ({
|
|
1668
|
+
name: `${s.id}: ${s.type === "password" ? "****" : String(state.answers[s.id] ?? "")}`,
|
|
1669
|
+
value: s.id
|
|
1670
|
+
}))
|
|
1671
|
+
});
|
|
1672
|
+
state = {
|
|
1673
|
+
...state,
|
|
1674
|
+
currentStepId: stepToRevisit,
|
|
1675
|
+
status: "running"
|
|
1676
|
+
};
|
|
1379
1677
|
}
|
|
1380
|
-
|
|
1678
|
+
} else {
|
|
1679
|
+
needsReview = false;
|
|
1381
1680
|
}
|
|
1382
1681
|
}
|
|
1383
1682
|
if (state.status === "done" && !quiet) {
|
|
1384
1683
|
renderer.renderSummary(state.answers, config.steps, theme);
|
|
1385
1684
|
}
|
|
1386
|
-
if (state.status === "done" &&
|
|
1387
|
-
|
|
1685
|
+
if (state.status === "done" && !isMock) {
|
|
1686
|
+
if (config.onComplete) {
|
|
1687
|
+
await executeOnComplete(config.onComplete, options?.configFilePath, state.answers, config, theme, renderer);
|
|
1688
|
+
}
|
|
1689
|
+
if (config.actions && config.actions.length > 0) {
|
|
1690
|
+
await executeActions(config.actions, state.answers, theme, renderer);
|
|
1691
|
+
}
|
|
1388
1692
|
}
|
|
1693
|
+
emitEvent(renderer, { type: "session:end", answers: state.answers, cancelled: state.status === "cancelled" }, theme);
|
|
1389
1694
|
if (state.status === "done" && cacheEnabled) {
|
|
1390
1695
|
const passwordStepIds = new Set(
|
|
1391
1696
|
config.steps.filter((s) => s.type === "password").map((s) => s.id)
|
|
@@ -1398,6 +1703,9 @@ async function runWizard(config, options) {
|
|
|
1398
1703
|
}
|
|
1399
1704
|
saveCachedAnswers(config.meta.name, answersToCache, cacheDir);
|
|
1400
1705
|
}
|
|
1706
|
+
if (state.status === "done") {
|
|
1707
|
+
clearProgress(config.meta.name);
|
|
1708
|
+
}
|
|
1401
1709
|
return state.answers;
|
|
1402
1710
|
} finally {
|
|
1403
1711
|
if (userPlugins) {
|
|
@@ -1437,6 +1745,8 @@ function renderStep(renderer, step, state, theme) {
|
|
|
1437
1745
|
case "message":
|
|
1438
1746
|
renderer.renderMessage(step, state, theme);
|
|
1439
1747
|
return Promise.resolve(true);
|
|
1748
|
+
case "note":
|
|
1749
|
+
return Promise.resolve(true);
|
|
1440
1750
|
}
|
|
1441
1751
|
}
|
|
1442
1752
|
function resolveStepDefaults(step, cachedAnswers) {
|
|
@@ -1487,6 +1797,7 @@ function resolveStepDefaults(step, cachedAnswers) {
|
|
|
1487
1797
|
}
|
|
1488
1798
|
case "password":
|
|
1489
1799
|
case "message":
|
|
1800
|
+
case "note":
|
|
1490
1801
|
return step;
|
|
1491
1802
|
}
|
|
1492
1803
|
}
|
|
@@ -1496,7 +1807,7 @@ function getCachedDefault(stepId, cachedAnswers) {
|
|
|
1496
1807
|
}
|
|
1497
1808
|
function applyTemplateDefaults(step, templateAnswers) {
|
|
1498
1809
|
if (!(step.id in templateAnswers)) return step;
|
|
1499
|
-
if (step.type === "password" || step.type === "message") return step;
|
|
1810
|
+
if (step.type === "password" || step.type === "message" || step.type === "note") return step;
|
|
1500
1811
|
const value = templateAnswers[step.id];
|
|
1501
1812
|
switch (step.type) {
|
|
1502
1813
|
case "text":
|
|
@@ -1585,13 +1896,34 @@ function resolveStepTemplates(step, answers) {
|
|
|
1585
1896
|
case "confirm":
|
|
1586
1897
|
case "toggle":
|
|
1587
1898
|
case "message":
|
|
1899
|
+
case "note":
|
|
1588
1900
|
return {
|
|
1589
1901
|
...step,
|
|
1590
1902
|
description: step.description ? resolveTemplate(step.description, answers) : void 0
|
|
1591
1903
|
};
|
|
1592
1904
|
}
|
|
1593
1905
|
}
|
|
1594
|
-
async function
|
|
1906
|
+
async function executeOnComplete(handlerPath, configFilePath, answers, config, theme, renderer) {
|
|
1907
|
+
if (renderer) emitEvent(renderer, { type: "oncomplete:start" }, theme);
|
|
1908
|
+
const resolvedPath = configFilePath ? resolve2(dirname2(configFilePath), handlerPath) : resolve2(handlerPath);
|
|
1909
|
+
try {
|
|
1910
|
+
const mod = await import(pathToFileURL(resolvedPath).href);
|
|
1911
|
+
if (typeof mod.default !== "function") {
|
|
1912
|
+
throw new Error(`onComplete handler "${handlerPath}" must export a default function`);
|
|
1913
|
+
}
|
|
1914
|
+
await mod.default({ answers, config });
|
|
1915
|
+
if (renderer) emitEvent(renderer, { type: "oncomplete:pass" }, theme);
|
|
1916
|
+
} catch (error) {
|
|
1917
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1918
|
+
if (renderer) emitEvent(renderer, { type: "oncomplete:fail", error: message }, theme);
|
|
1919
|
+
console.log(`
|
|
1920
|
+
${theme.error("\u2717")} onComplete handler failed: ${message}
|
|
1921
|
+
`);
|
|
1922
|
+
throw error;
|
|
1923
|
+
}
|
|
1924
|
+
}
|
|
1925
|
+
async function executeActions(actions, answers, theme, renderer) {
|
|
1926
|
+
if (renderer) emitEvent(renderer, { type: "actions:start" }, theme);
|
|
1595
1927
|
console.log(`
|
|
1596
1928
|
${theme.bold("Running actions...")}
|
|
1597
1929
|
`);
|
|
@@ -1599,14 +1931,16 @@ async function executeActions(actions, answers, theme) {
|
|
|
1599
1931
|
if (action.when && !evaluateCondition(action.when, answers)) {
|
|
1600
1932
|
continue;
|
|
1601
1933
|
}
|
|
1602
|
-
const resolvedCommand =
|
|
1603
|
-
const resolvedName = action.name ?
|
|
1934
|
+
const resolvedCommand = resolveTemplateStrict(action.run, answers);
|
|
1935
|
+
const resolvedName = action.name ? resolveTemplateStrict(action.name, answers) : void 0;
|
|
1604
1936
|
const label = resolvedName ?? resolvedCommand;
|
|
1605
1937
|
try {
|
|
1606
1938
|
execSync(resolvedCommand, { stdio: "pipe" });
|
|
1607
1939
|
console.log(` ${theme.success("\u2713")} ${label}`);
|
|
1940
|
+
if (renderer) emitEvent(renderer, { type: "action:pass", name: label }, theme);
|
|
1608
1941
|
} catch {
|
|
1609
1942
|
console.log(` ${theme.error("\u2717")} ${label}`);
|
|
1943
|
+
if (renderer) emitEvent(renderer, { type: "action:fail", name: label }, theme);
|
|
1610
1944
|
throw new Error(`Action failed: ${label}`);
|
|
1611
1945
|
}
|
|
1612
1946
|
}
|
|
@@ -1827,21 +2161,201 @@ var InkRenderer = class {
|
|
|
1827
2161
|
}
|
|
1828
2162
|
};
|
|
1829
2163
|
|
|
2164
|
+
// src/renderers/clack.ts
|
|
2165
|
+
import chalk2 from "chalk";
|
|
2166
|
+
|
|
2167
|
+
// src/renderers/symbols.ts
|
|
2168
|
+
function isUnicodeSupported() {
|
|
2169
|
+
if (process.platform === "win32") {
|
|
2170
|
+
return Boolean(process.env["WT_SESSION"]) || process.env["TERM_PROGRAM"] === "vscode";
|
|
2171
|
+
}
|
|
2172
|
+
return process.env["TERM"] !== "linux";
|
|
2173
|
+
}
|
|
2174
|
+
var unicode = isUnicodeSupported();
|
|
2175
|
+
var u = (unicodeChar, fallback) => unicode ? unicodeChar : fallback;
|
|
2176
|
+
var S_BAR_START = u("\u250C", "T");
|
|
2177
|
+
var S_BAR = u("\u2502", "|");
|
|
2178
|
+
var S_BAR_END = u("\u2514", "\u2014");
|
|
2179
|
+
var S_STEP_ACTIVE = u("\u25C6", "*");
|
|
2180
|
+
var S_STEP_SUBMIT = u("\u25C7", "o");
|
|
2181
|
+
var S_STEP_CANCEL = u("\u25A0", "x");
|
|
2182
|
+
var S_STEP_ERROR = u("\u25B2", "x");
|
|
2183
|
+
var S_CORNER_TR = u("\u256E", "+");
|
|
2184
|
+
var S_CORNER_BR = u("\u256F", "+");
|
|
2185
|
+
var S_BAR_H = u("\u2500", "-");
|
|
2186
|
+
|
|
2187
|
+
// src/renderers/clack.ts
|
|
2188
|
+
var ClackRenderer = class extends InquirerRenderer {
|
|
2189
|
+
spinnerInterval;
|
|
2190
|
+
spinnerFrameIndex = 0;
|
|
2191
|
+
renderStepHeader() {
|
|
2192
|
+
}
|
|
2193
|
+
renderGroupHeader() {
|
|
2194
|
+
}
|
|
2195
|
+
renderSummary(answers, steps, theme) {
|
|
2196
|
+
const entries = [];
|
|
2197
|
+
for (const step of steps) {
|
|
2198
|
+
const answer = answers[step.id];
|
|
2199
|
+
if (answer === void 0) continue;
|
|
2200
|
+
const display = Array.isArray(answer) ? answer.map(String).join(", ") : String(answer);
|
|
2201
|
+
entries.push({ id: step.id, display });
|
|
2202
|
+
}
|
|
2203
|
+
if (entries.length === 0) return;
|
|
2204
|
+
const title = "Summary";
|
|
2205
|
+
const lines = entries.map((e) => `${e.id}: ${e.display}`);
|
|
2206
|
+
this.writeNoteBox(title, lines, theme);
|
|
2207
|
+
}
|
|
2208
|
+
onEvent(event, theme) {
|
|
2209
|
+
switch (event.type) {
|
|
2210
|
+
case "session:start":
|
|
2211
|
+
this.handleSessionStart(event, theme);
|
|
2212
|
+
break;
|
|
2213
|
+
case "session:end":
|
|
2214
|
+
this.handleSessionEnd(event, theme);
|
|
2215
|
+
break;
|
|
2216
|
+
case "step:start":
|
|
2217
|
+
process.stdout.write(`${chalk2.gray(S_BAR)}
|
|
2218
|
+
`);
|
|
2219
|
+
break;
|
|
2220
|
+
case "step:complete":
|
|
2221
|
+
this.handleStepComplete(event, theme);
|
|
2222
|
+
break;
|
|
2223
|
+
case "step:error":
|
|
2224
|
+
process.stdout.write(`${chalk2.gray(S_BAR)} ${theme.error(`${S_STEP_ERROR} ${event.error}`)}
|
|
2225
|
+
`);
|
|
2226
|
+
break;
|
|
2227
|
+
case "step:back":
|
|
2228
|
+
process.stdout.write(`${chalk2.gray(S_BAR)} ${theme.muted("\u21A9 Back")}
|
|
2229
|
+
`);
|
|
2230
|
+
break;
|
|
2231
|
+
case "group:start":
|
|
2232
|
+
process.stdout.write(`${chalk2.gray(S_BAR)}
|
|
2233
|
+
`);
|
|
2234
|
+
process.stdout.write(`${chalk2.gray(S_BAR)} ${theme.accent(event.group)}
|
|
2235
|
+
`);
|
|
2236
|
+
break;
|
|
2237
|
+
case "note":
|
|
2238
|
+
this.writeNoteBox(event.title, event.body.split("\n"), theme);
|
|
2239
|
+
break;
|
|
2240
|
+
case "spinner:start":
|
|
2241
|
+
this.startSpinner(event.message, theme);
|
|
2242
|
+
break;
|
|
2243
|
+
case "spinner:stop":
|
|
2244
|
+
this.stopSpinner(event.message, theme);
|
|
2245
|
+
break;
|
|
2246
|
+
case "checks:start":
|
|
2247
|
+
process.stdout.write(`${chalk2.gray(S_BAR)}
|
|
2248
|
+
`);
|
|
2249
|
+
process.stdout.write(`${chalk2.gray(S_BAR)} ${theme.bold("Running checks...")}
|
|
2250
|
+
`);
|
|
2251
|
+
break;
|
|
2252
|
+
case "check:pass":
|
|
2253
|
+
process.stdout.write(`${chalk2.gray(S_BAR)} ${theme.success(S_STEP_SUBMIT)} ${event.name}
|
|
2254
|
+
`);
|
|
2255
|
+
break;
|
|
2256
|
+
case "check:fail":
|
|
2257
|
+
process.stdout.write(`${chalk2.gray(S_BAR)} ${theme.error(S_STEP_ERROR)} ${event.name}: ${event.message}
|
|
2258
|
+
`);
|
|
2259
|
+
break;
|
|
2260
|
+
case "actions:start":
|
|
2261
|
+
process.stdout.write(`${chalk2.gray(S_BAR)}
|
|
2262
|
+
`);
|
|
2263
|
+
process.stdout.write(`${chalk2.gray(S_BAR)} ${theme.bold("Running actions...")}
|
|
2264
|
+
`);
|
|
2265
|
+
break;
|
|
2266
|
+
case "action:pass":
|
|
2267
|
+
process.stdout.write(`${chalk2.gray(S_BAR)} ${theme.success(S_STEP_SUBMIT)} ${event.name}
|
|
2268
|
+
`);
|
|
2269
|
+
break;
|
|
2270
|
+
case "action:fail":
|
|
2271
|
+
process.stdout.write(`${chalk2.gray(S_BAR)} ${theme.error(S_STEP_ERROR)} ${event.name}
|
|
2272
|
+
`);
|
|
2273
|
+
break;
|
|
2274
|
+
}
|
|
2275
|
+
}
|
|
2276
|
+
handleSessionStart(event, theme) {
|
|
2277
|
+
process.stdout.write(`${chalk2.gray(S_BAR_START)} ${theme.bold(event.wizard)}
|
|
2278
|
+
`);
|
|
2279
|
+
if (event.description) {
|
|
2280
|
+
process.stdout.write(`${chalk2.gray(S_BAR)} ${theme.muted(event.description)}
|
|
2281
|
+
`);
|
|
2282
|
+
}
|
|
2283
|
+
process.stdout.write(`${chalk2.gray(S_BAR)}
|
|
2284
|
+
`);
|
|
2285
|
+
}
|
|
2286
|
+
handleSessionEnd(event, theme) {
|
|
2287
|
+
if (event.cancelled) {
|
|
2288
|
+
process.stdout.write(`${theme.warning(S_STEP_CANCEL)} Cancelled
|
|
2289
|
+
`);
|
|
2290
|
+
} else {
|
|
2291
|
+
process.stdout.write(`${chalk2.gray(S_BAR_END)} ${theme.success("You're all set!")}
|
|
2292
|
+
`);
|
|
2293
|
+
}
|
|
2294
|
+
}
|
|
2295
|
+
handleStepComplete(event, theme) {
|
|
2296
|
+
const displayValue = event.step.type === "password" ? "****" : this.formatValue(event.value);
|
|
2297
|
+
process.stdout.write(
|
|
2298
|
+
`${chalk2.gray(S_STEP_SUBMIT)} ${event.step.message} ${chalk2.gray("\xB7")} ${theme.muted(displayValue)}
|
|
2299
|
+
`
|
|
2300
|
+
);
|
|
2301
|
+
}
|
|
2302
|
+
formatValue(value) {
|
|
2303
|
+
if (Array.isArray(value)) return value.map(String).join(", ");
|
|
2304
|
+
if (typeof value === "boolean") return value ? "Yes" : "No";
|
|
2305
|
+
return String(value);
|
|
2306
|
+
}
|
|
2307
|
+
writeNoteBox(title, lines, _theme) {
|
|
2308
|
+
const maxLen = Math.max(title.length, ...lines.map((l) => l.length));
|
|
2309
|
+
const padded = maxLen + 2;
|
|
2310
|
+
const topLine = `${S_BAR_H.repeat(padded - title.length - 1)}${S_CORNER_TR}`;
|
|
2311
|
+
const bottomLine = `${S_BAR_H.repeat(padded)}${S_CORNER_BR}`;
|
|
2312
|
+
process.stdout.write(`${chalk2.gray(S_BAR)}
|
|
2313
|
+
`);
|
|
2314
|
+
process.stdout.write(`${chalk2.gray(S_BAR)} ${chalk2.gray(`\u256D${S_BAR_H} ${title} ${topLine}`)}
|
|
2315
|
+
`);
|
|
2316
|
+
for (const line of lines) {
|
|
2317
|
+
const pad = " ".repeat(maxLen - line.length);
|
|
2318
|
+
process.stdout.write(`${chalk2.gray(S_BAR)} ${chalk2.gray(S_BAR)} ${line}${pad} ${chalk2.gray(S_BAR)}
|
|
2319
|
+
`);
|
|
2320
|
+
}
|
|
2321
|
+
process.stdout.write(`${chalk2.gray(S_BAR)} ${chalk2.gray(`\u256E${bottomLine}`)}
|
|
2322
|
+
`);
|
|
2323
|
+
}
|
|
2324
|
+
startSpinner(message, theme) {
|
|
2325
|
+
this.spinnerFrameIndex = 0;
|
|
2326
|
+
const { frames, interval } = theme.spinner;
|
|
2327
|
+
this.spinnerInterval = setInterval(() => {
|
|
2328
|
+
const frame = frames[this.spinnerFrameIndex % frames.length];
|
|
2329
|
+
process.stdout.write(`\r${chalk2.gray(S_BAR)} ${chalk2.cyan(frame ?? "")} ${message}`);
|
|
2330
|
+
this.spinnerFrameIndex++;
|
|
2331
|
+
}, interval);
|
|
2332
|
+
}
|
|
2333
|
+
stopSpinner(message, theme) {
|
|
2334
|
+
if (this.spinnerInterval) {
|
|
2335
|
+
clearInterval(this.spinnerInterval);
|
|
2336
|
+
this.spinnerInterval = void 0;
|
|
2337
|
+
}
|
|
2338
|
+
const finalMessage = message ?? "Done";
|
|
2339
|
+
process.stdout.write(`\r${chalk2.gray(S_BAR)} ${theme.success(S_STEP_SUBMIT)} ${finalMessage}
|
|
2340
|
+
`);
|
|
2341
|
+
}
|
|
2342
|
+
};
|
|
2343
|
+
|
|
1830
2344
|
// src/templates.ts
|
|
1831
|
-
import { mkdirSync as
|
|
1832
|
-
import { join as
|
|
1833
|
-
import { homedir as
|
|
1834
|
-
var TEMPLATES_DIR =
|
|
2345
|
+
import { mkdirSync as mkdirSync4, readFileSync as readFileSync5, writeFileSync as writeFileSync4, unlinkSync as unlinkSync3, readdirSync as readdirSync2, existsSync as existsSync2 } from "fs";
|
|
2346
|
+
import { join as join4 } from "path";
|
|
2347
|
+
import { homedir as homedir4 } from "os";
|
|
2348
|
+
var TEMPLATES_DIR = join4(homedir4(), ".config", "grimoire", "templates");
|
|
1835
2349
|
function getWizardTemplateDir(wizardName) {
|
|
1836
|
-
return
|
|
2350
|
+
return join4(TEMPLATES_DIR, slugify(wizardName));
|
|
1837
2351
|
}
|
|
1838
2352
|
function getTemplateFilePath(wizardName, templateName) {
|
|
1839
|
-
return
|
|
2353
|
+
return join4(getWizardTemplateDir(wizardName), `${slugify(templateName)}.json`);
|
|
1840
2354
|
}
|
|
1841
2355
|
function saveTemplate(wizardName, templateName, answers, excludeKeys) {
|
|
1842
2356
|
try {
|
|
1843
2357
|
const dir = getWizardTemplateDir(wizardName);
|
|
1844
|
-
|
|
2358
|
+
mkdirSync4(dir, { recursive: true });
|
|
1845
2359
|
const filePath = getTemplateFilePath(wizardName, templateName);
|
|
1846
2360
|
let answersToSave = answers;
|
|
1847
2361
|
if (excludeKeys && excludeKeys.length > 0) {
|
|
@@ -1850,14 +2364,14 @@ function saveTemplate(wizardName, templateName, answers, excludeKeys) {
|
|
|
1850
2364
|
Object.entries(answers).filter(([k]) => !excluded.has(k))
|
|
1851
2365
|
);
|
|
1852
2366
|
}
|
|
1853
|
-
|
|
2367
|
+
writeFileSync4(filePath, JSON.stringify(answersToSave, null, 2) + "\n", "utf-8");
|
|
1854
2368
|
} catch {
|
|
1855
2369
|
}
|
|
1856
2370
|
}
|
|
1857
2371
|
function loadTemplate(wizardName, templateName) {
|
|
1858
2372
|
try {
|
|
1859
2373
|
const filePath = getTemplateFilePath(wizardName, templateName);
|
|
1860
|
-
const raw =
|
|
2374
|
+
const raw = readFileSync5(filePath, "utf-8");
|
|
1861
2375
|
const parsed = JSON.parse(raw);
|
|
1862
2376
|
if (typeof parsed === "object" && parsed !== null && !Array.isArray(parsed)) {
|
|
1863
2377
|
return parsed;
|
|
@@ -1879,16 +2393,46 @@ function listTemplates(wizardName) {
|
|
|
1879
2393
|
function deleteTemplate(wizardName, templateName) {
|
|
1880
2394
|
try {
|
|
1881
2395
|
const filePath = getTemplateFilePath(wizardName, templateName);
|
|
1882
|
-
|
|
2396
|
+
unlinkSync3(filePath);
|
|
1883
2397
|
} catch {
|
|
1884
2398
|
}
|
|
1885
2399
|
}
|
|
2400
|
+
|
|
2401
|
+
// src/pipeline.ts
|
|
2402
|
+
async function runPipeline(steps, globalOptions) {
|
|
2403
|
+
const results = {};
|
|
2404
|
+
let accumulated = {};
|
|
2405
|
+
for (const step of steps) {
|
|
2406
|
+
let config;
|
|
2407
|
+
if (typeof step.config === "string") {
|
|
2408
|
+
const { loadWizardConfig: loadWizardConfig2 } = await Promise.resolve().then(() => (init_parser(), parser_exports));
|
|
2409
|
+
config = await loadWizardConfig2(step.config);
|
|
2410
|
+
} else {
|
|
2411
|
+
config = step.config;
|
|
2412
|
+
}
|
|
2413
|
+
if (step.when && !evaluateCondition(step.when, accumulated)) {
|
|
2414
|
+
continue;
|
|
2415
|
+
}
|
|
2416
|
+
const answers = await runWizard(config, {
|
|
2417
|
+
...globalOptions,
|
|
2418
|
+
...step.options,
|
|
2419
|
+
mockAnswers: step.mockAnswers,
|
|
2420
|
+
templateAnswers: accumulated
|
|
2421
|
+
});
|
|
2422
|
+
results[config.meta.name] = answers;
|
|
2423
|
+
accumulated = { ...accumulated, ...answers };
|
|
2424
|
+
}
|
|
2425
|
+
return results;
|
|
2426
|
+
}
|
|
1886
2427
|
export {
|
|
2428
|
+
ClackRenderer,
|
|
2429
|
+
DEFAULT_SPINNER,
|
|
1887
2430
|
InkRenderer,
|
|
1888
2431
|
InquirerRenderer,
|
|
1889
2432
|
clearCache,
|
|
1890
2433
|
clearMruData,
|
|
1891
2434
|
clearPlugins,
|
|
2435
|
+
clearProgress,
|
|
1892
2436
|
createWizardState,
|
|
1893
2437
|
defineWizard,
|
|
1894
2438
|
deleteTemplate,
|
|
@@ -1900,6 +2444,7 @@ export {
|
|
|
1900
2444
|
isStepVisible,
|
|
1901
2445
|
listTemplates,
|
|
1902
2446
|
loadCachedAnswers,
|
|
2447
|
+
loadProgress,
|
|
1903
2448
|
loadTemplate,
|
|
1904
2449
|
loadWizardConfig,
|
|
1905
2450
|
parseWizardConfig,
|
|
@@ -1909,13 +2454,18 @@ export {
|
|
|
1909
2454
|
renderBanner,
|
|
1910
2455
|
resolveEnvDefault,
|
|
1911
2456
|
resolveNextStep,
|
|
2457
|
+
resolveSpinner,
|
|
1912
2458
|
resolveTemplate,
|
|
2459
|
+
resolveTemplateStrict,
|
|
1913
2460
|
resolveTheme,
|
|
2461
|
+
runPipeline,
|
|
1914
2462
|
runPreFlightChecks,
|
|
1915
2463
|
runWizard,
|
|
1916
2464
|
saveCachedAnswers,
|
|
2465
|
+
saveProgress,
|
|
1917
2466
|
saveTemplate,
|
|
1918
2467
|
slugify,
|
|
2468
|
+
spinners,
|
|
1919
2469
|
validateStepAnswer,
|
|
1920
2470
|
wizardReducer
|
|
1921
2471
|
};
|