@jskit-ai/jskit-cli 0.2.39 → 0.2.40
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 +3 -3
- package/src/server/cliRuntime/mutations/fileMutations.js +4 -3
- package/src/server/cliRuntime/packageOptions.js +160 -7
- package/src/server/commandHandlers/packageCommands/discoverabilityHelp.js +253 -99
- package/src/server/commandHandlers/packageCommands/generate.js +246 -47
- package/src/server/core/argParser.js +29 -10
- package/src/server/core/buildCommandDeps.js +2 -0
- package/src/server/core/createCliRunner.js +5 -1
- package/src/server/core/dispatchCli.js +3 -1
- package/src/server/core/usageHelp.js +94 -27
- package/src/server/shared/cliError.js +2 -1
- package/src/server/shared/outputFormatting.js +164 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jskit-ai/jskit-cli",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.40",
|
|
4
4
|
"description": "Bundle and package orchestration CLI for JSKIT apps.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|
|
@@ -20,8 +20,8 @@
|
|
|
20
20
|
"test": "node --test"
|
|
21
21
|
},
|
|
22
22
|
"dependencies": {
|
|
23
|
-
"@jskit-ai/jskit-catalog": "0.1.
|
|
24
|
-
"@jskit-ai/kernel": "0.1.
|
|
23
|
+
"@jskit-ai/jskit-catalog": "0.1.40",
|
|
24
|
+
"@jskit-ai/kernel": "0.1.32"
|
|
25
25
|
},
|
|
26
26
|
"engines": {
|
|
27
27
|
"node": "20.x"
|
|
@@ -16,7 +16,8 @@ import {
|
|
|
16
16
|
hashBuffer,
|
|
17
17
|
fileExists,
|
|
18
18
|
readFileBufferIfExists,
|
|
19
|
-
loadMutationWhenConfigContext
|
|
19
|
+
loadMutationWhenConfigContext,
|
|
20
|
+
resolveAppRelativePathWithinRoot
|
|
20
21
|
} from "../ioAndMigrations.js";
|
|
21
22
|
import {
|
|
22
23
|
copyTemplateFile,
|
|
@@ -113,7 +114,7 @@ async function applyFileMutations(
|
|
|
113
114
|
mutation,
|
|
114
115
|
configContext
|
|
115
116
|
})
|
|
116
|
-
: [
|
|
117
|
+
: [resolveAppRelativePathWithinRoot(appRoot, to, `${packageEntry.packageId} files mutation.to`).absolutePath];
|
|
117
118
|
const hasPrecomputedTemplateContext =
|
|
118
119
|
precomputedTemplateContextByMutationIndex instanceof Map &&
|
|
119
120
|
precomputedTemplateContextByMutationIndex.has(mutationIndex);
|
|
@@ -224,7 +225,7 @@ async function preflightFileMutationTemplateContexts(
|
|
|
224
225
|
mutation,
|
|
225
226
|
configContext
|
|
226
227
|
})
|
|
227
|
-
: [
|
|
228
|
+
: [resolveAppRelativePathWithinRoot(appRoot, to, `${packageEntry.packageId} files mutation.to`).absolutePath]
|
|
228
229
|
: [path.join(appRoot, mutation.toDir || "migrations")];
|
|
229
230
|
const replacements = await resolveTemplateContextReplacementsForMutation({
|
|
230
231
|
packageEntry,
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { normalizePagesRelativeTargetRoot } from "@jskit-ai/kernel/server/support";
|
|
1
2
|
import { createCliError } from "../shared/cliError.js";
|
|
2
3
|
import {
|
|
3
4
|
ensureObject,
|
|
@@ -15,6 +16,8 @@ import { loadMutationWhenConfigContext } from "./ioAndMigrations.js";
|
|
|
15
16
|
|
|
16
17
|
const WORKSPACE_VISIBILITY_LEVELS = Object.freeze(["workspace", "workspace_user"]);
|
|
17
18
|
const WORKSPACE_VISIBILITY_SET = new Set(WORKSPACE_VISIBILITY_LEVELS);
|
|
19
|
+
const OPTION_VALIDATION_ENABLED_SURFACE_ID = "enabled-surface-id";
|
|
20
|
+
const OPTION_NORMALIZATION_PAGES_RELATIVE_TARGET_ROOT = "pages-relative-target-root";
|
|
18
21
|
|
|
19
22
|
function normalizeSurfaceIdForMutation(value = "") {
|
|
20
23
|
return String(value || "").trim().toLowerCase();
|
|
@@ -100,6 +103,95 @@ function normalizeResolvedOptionValue(value = "") {
|
|
|
100
103
|
return String(value || "").trim().toLowerCase();
|
|
101
104
|
}
|
|
102
105
|
|
|
106
|
+
function normalizeResolvedOptionSchemaValue({
|
|
107
|
+
packageEntry,
|
|
108
|
+
optionName = "",
|
|
109
|
+
schema = {},
|
|
110
|
+
value = ""
|
|
111
|
+
} = {}) {
|
|
112
|
+
const normalizedValue = String(value || "").trim();
|
|
113
|
+
if (!normalizedValue) {
|
|
114
|
+
return "";
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const normalizationType = normalizeResolvedOptionValue(schema?.normalizationType);
|
|
118
|
+
if (!normalizationType) {
|
|
119
|
+
return normalizedValue;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (normalizationType === OPTION_NORMALIZATION_PAGES_RELATIVE_TARGET_ROOT) {
|
|
123
|
+
return normalizePagesRelativeTargetRoot(normalizedValue, {
|
|
124
|
+
context: `package ${String(packageEntry?.packageId || "unknown-package")}`,
|
|
125
|
+
label: `option "${String(optionName || "").trim() || "unknown"}"`
|
|
126
|
+
}).slice("src/pages/".length);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
throw createCliError(
|
|
130
|
+
`Invalid option normalization type in package ${String(packageEntry?.packageId || "unknown-package")}: ${String(schema?.normalizationType || "").trim()}.`
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function resolveSchemaValidatedOptionNames(packageEntry = {}, validationType = "", { optionNames = null } = {}) {
|
|
135
|
+
const normalizedValidationType = String(validationType || "").trim().toLowerCase();
|
|
136
|
+
if (!normalizedValidationType) {
|
|
137
|
+
return [];
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const optionSchemas = ensureObject(packageEntry?.descriptor?.options);
|
|
141
|
+
const candidateOptionNames = Array.isArray(optionNames) && optionNames.length > 0
|
|
142
|
+
? optionNames
|
|
143
|
+
: Object.keys(optionSchemas);
|
|
144
|
+
|
|
145
|
+
return [
|
|
146
|
+
...new Set(
|
|
147
|
+
candidateOptionNames.filter((optionName) => {
|
|
148
|
+
const schema = ensureObject(optionSchemas[optionName]);
|
|
149
|
+
return normalizeResolvedOptionValue(schema.validationType) === normalizedValidationType;
|
|
150
|
+
})
|
|
151
|
+
)
|
|
152
|
+
];
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function validateEnabledSurfaceOptionValues({
|
|
156
|
+
packageEntry,
|
|
157
|
+
resolvedOptions = {},
|
|
158
|
+
optionNames = null,
|
|
159
|
+
configContext = {}
|
|
160
|
+
} = {}) {
|
|
161
|
+
const validatedOptionNames = resolveSchemaValidatedOptionNames(
|
|
162
|
+
packageEntry,
|
|
163
|
+
OPTION_VALIDATION_ENABLED_SURFACE_ID,
|
|
164
|
+
{ optionNames }
|
|
165
|
+
);
|
|
166
|
+
if (validatedOptionNames.length < 1) {
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const providedSurfaceOptionNames = validatedOptionNames.filter((optionName) => {
|
|
171
|
+
return normalizeSurfaceIdForMutation(resolvedOptions?.[optionName]).length > 0;
|
|
172
|
+
});
|
|
173
|
+
if (providedSurfaceOptionNames.length < 1) {
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
const packageId = String(packageEntry?.packageId || "").trim() || "unknown-package";
|
|
178
|
+
const surfaceDefinitions = resolveSurfaceDefinitionsForOptionPolicy(configContext);
|
|
179
|
+
for (const optionName of providedSurfaceOptionNames) {
|
|
180
|
+
const surfaceId = normalizeSurfaceIdForMutation(resolvedOptions?.[optionName]);
|
|
181
|
+
const surfaceDefinition = surfaceDefinitions[surfaceId];
|
|
182
|
+
if (!surfaceDefinition) {
|
|
183
|
+
throw createCliError(
|
|
184
|
+
`Invalid option for package ${packageId}: --${optionName} references unknown surface "${surfaceId}" in config/public.js.`
|
|
185
|
+
);
|
|
186
|
+
}
|
|
187
|
+
if (surfaceDefinition.enabled !== true) {
|
|
188
|
+
throw createCliError(
|
|
189
|
+
`Invalid option for package ${packageId}: --${optionName} references disabled surface "${surfaceId}" in config/public.js.`
|
|
190
|
+
);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
103
195
|
function validateSurfaceVisibilityOptionPolicy({
|
|
104
196
|
packageEntry,
|
|
105
197
|
resolvedOptions = {},
|
|
@@ -159,6 +251,34 @@ async function validateResolvedOptionPolicies({
|
|
|
159
251
|
});
|
|
160
252
|
}
|
|
161
253
|
|
|
254
|
+
async function validateOptionValuesForPackage({
|
|
255
|
+
packageEntry,
|
|
256
|
+
resolvedOptions = {},
|
|
257
|
+
appRoot = "",
|
|
258
|
+
optionNames = null
|
|
259
|
+
} = {}) {
|
|
260
|
+
if (!appRoot) {
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
const validatedOptionNames = resolveSchemaValidatedOptionNames(
|
|
265
|
+
packageEntry,
|
|
266
|
+
OPTION_VALIDATION_ENABLED_SURFACE_ID,
|
|
267
|
+
{ optionNames }
|
|
268
|
+
);
|
|
269
|
+
if (validatedOptionNames.length < 1) {
|
|
270
|
+
return;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
const configContext = await loadMutationWhenConfigContext(appRoot);
|
|
274
|
+
validateEnabledSurfaceOptionValues({
|
|
275
|
+
packageEntry,
|
|
276
|
+
resolvedOptions,
|
|
277
|
+
optionNames: validatedOptionNames,
|
|
278
|
+
configContext
|
|
279
|
+
});
|
|
280
|
+
}
|
|
281
|
+
|
|
162
282
|
async function resolvePackageOptions(packageEntry, inlineOptions, io, { appRoot = "" } = {}) {
|
|
163
283
|
const optionSchemas = ensureObject(packageEntry.descriptor.options);
|
|
164
284
|
const optionNames = Object.keys(optionSchemas);
|
|
@@ -211,10 +331,23 @@ async function resolvePackageOptions(packageEntry, inlineOptions, io, { appRoot
|
|
|
211
331
|
for (const optionName of optionNames) {
|
|
212
332
|
const schema = ensureObject(optionSchemas[optionName]);
|
|
213
333
|
const allowEmpty = schema.allowEmpty === true;
|
|
334
|
+
const assignResolvedOption = (rawValue = "") => {
|
|
335
|
+
const normalizedOptionValue = normalizeResolvedOptionSchemaValue({
|
|
336
|
+
packageEntry,
|
|
337
|
+
optionName,
|
|
338
|
+
schema,
|
|
339
|
+
value: rawValue
|
|
340
|
+
});
|
|
341
|
+
if (normalizedOptionValue || allowEmpty) {
|
|
342
|
+
resolved[optionName] = normalizedOptionValue;
|
|
343
|
+
return true;
|
|
344
|
+
}
|
|
345
|
+
return false;
|
|
346
|
+
};
|
|
214
347
|
if (hasInlineOption(optionName)) {
|
|
215
348
|
const inlineValue = String(inlineOptionValues[optionName] || "").trim();
|
|
216
349
|
if (inlineValue || allowEmpty) {
|
|
217
|
-
|
|
350
|
+
assignResolvedOption(inlineValue);
|
|
218
351
|
continue;
|
|
219
352
|
}
|
|
220
353
|
if (schema.required) {
|
|
@@ -226,7 +359,7 @@ async function resolvePackageOptions(packageEntry, inlineOptions, io, { appRoot
|
|
|
226
359
|
if (defaultFromConfigPath) {
|
|
227
360
|
const defaultFromConfigValue = await resolveOptionDefaultFromConfig(defaultFromConfigPath);
|
|
228
361
|
if (defaultFromConfigValue || allowEmpty) {
|
|
229
|
-
|
|
362
|
+
assignResolvedOption(defaultFromConfigValue);
|
|
230
363
|
continue;
|
|
231
364
|
}
|
|
232
365
|
}
|
|
@@ -235,31 +368,37 @@ async function resolvePackageOptions(packageEntry, inlineOptions, io, { appRoot
|
|
|
235
368
|
if (defaultFromOptionTemplate) {
|
|
236
369
|
const derivedOptionValue = resolveOptionDefaultFromTemplate(defaultFromOptionTemplate, optionName);
|
|
237
370
|
if (derivedOptionValue || allowEmpty) {
|
|
238
|
-
|
|
371
|
+
assignResolvedOption(derivedOptionValue);
|
|
239
372
|
continue;
|
|
240
373
|
}
|
|
241
374
|
}
|
|
242
375
|
|
|
243
376
|
if (typeof schema.defaultValue === "string" && schema.defaultValue.trim()) {
|
|
244
|
-
|
|
377
|
+
assignResolvedOption(schema.defaultValue.trim());
|
|
245
378
|
continue;
|
|
246
379
|
}
|
|
247
380
|
|
|
248
381
|
if (schema.required) {
|
|
249
|
-
|
|
382
|
+
assignResolvedOption(await promptForRequiredOption({
|
|
250
383
|
ownerType: "package",
|
|
251
384
|
ownerId: packageEntry.packageId,
|
|
252
385
|
optionName,
|
|
253
386
|
optionSchema: schema,
|
|
254
387
|
stdin: io.stdin,
|
|
255
388
|
stdout: io.stdout
|
|
256
|
-
});
|
|
389
|
+
}));
|
|
257
390
|
continue;
|
|
258
391
|
}
|
|
259
392
|
|
|
260
393
|
resolved[optionName] = "";
|
|
261
394
|
}
|
|
262
395
|
|
|
396
|
+
await validateOptionValuesForPackage({
|
|
397
|
+
packageEntry,
|
|
398
|
+
resolvedOptions: resolved,
|
|
399
|
+
appRoot
|
|
400
|
+
});
|
|
401
|
+
|
|
263
402
|
await validateResolvedOptionPolicies({
|
|
264
403
|
packageEntry,
|
|
265
404
|
resolvedOptions: resolved,
|
|
@@ -291,9 +430,23 @@ function validateInlineOptionsForPackage(packageEntry, inlineOptions) {
|
|
|
291
430
|
);
|
|
292
431
|
}
|
|
293
432
|
|
|
433
|
+
async function validateInlineOptionValuesForPackage(
|
|
434
|
+
packageEntry,
|
|
435
|
+
inlineOptions,
|
|
436
|
+
{ appRoot = "", optionNames = null } = {}
|
|
437
|
+
) {
|
|
438
|
+
await validateOptionValuesForPackage({
|
|
439
|
+
packageEntry,
|
|
440
|
+
resolvedOptions: ensureObject(inlineOptions),
|
|
441
|
+
appRoot,
|
|
442
|
+
optionNames
|
|
443
|
+
});
|
|
444
|
+
}
|
|
445
|
+
|
|
294
446
|
export {
|
|
295
447
|
normalizeSurfaceIdForMutation,
|
|
296
448
|
parseSurfaceIdListForMutation,
|
|
297
449
|
resolvePackageOptions,
|
|
298
|
-
validateInlineOptionsForPackage
|
|
450
|
+
validateInlineOptionsForPackage,
|
|
451
|
+
validateInlineOptionValuesForPackage
|
|
299
452
|
};
|