@jskit-ai/jskit-cli 0.2.24 → 0.2.26
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 +2 -2
- package/src/server/argParser.js +23 -4
- package/src/server/cliRuntime.js +410 -121
- package/src/server/commandHandlers.js +462 -13
- package/src/server/pathResolution.js +0 -64
- package/src/server/runCli.js +16 -0
- package/src/server/runtimeDeps.js +1 -0
|
@@ -28,6 +28,7 @@ function createCommandHandlers(deps) {
|
|
|
28
28
|
validatePlannedCapabilityClosure,
|
|
29
29
|
resolvePackageOptions,
|
|
30
30
|
applyPackageInstall,
|
|
31
|
+
applyPackageMigrationsOnly,
|
|
31
32
|
applyPackagePositioning,
|
|
32
33
|
adoptAppLocalPackageDependencies,
|
|
33
34
|
loadAppPackageJson,
|
|
@@ -123,6 +124,18 @@ function createCommandHandlers(deps) {
|
|
|
123
124
|
return sortStrings(dependents);
|
|
124
125
|
}
|
|
125
126
|
|
|
127
|
+
function resolvePackageKind(packageEntry) {
|
|
128
|
+
const descriptor = ensureObject(packageEntry?.descriptor);
|
|
129
|
+
const normalizedKind = String(descriptor.kind || "").trim().toLowerCase();
|
|
130
|
+
if (normalizedKind === "runtime" || normalizedKind === "generator") {
|
|
131
|
+
return normalizedKind;
|
|
132
|
+
}
|
|
133
|
+
const packageId = String(packageEntry?.packageId || descriptor.packageId || "unknown-package").trim();
|
|
134
|
+
throw createCliError(
|
|
135
|
+
`Invalid package descriptor for ${packageId}: missing/invalid kind (expected runtime or generator).`
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
|
|
126
139
|
function resolvePackageOptionNames(packageEntry) {
|
|
127
140
|
const optionSchemas = ensureObject(packageEntry?.descriptor?.options);
|
|
128
141
|
return Object.keys(optionSchemas);
|
|
@@ -176,6 +189,155 @@ function createCommandHandlers(deps) {
|
|
|
176
189
|
: " This bundle does not accept inline options.";
|
|
177
190
|
throw createCliError(`Unknown option(s) for bundle ${bundleId}: ${sortedUnknown.join(", ")}.${suffix}`);
|
|
178
191
|
}
|
|
192
|
+
|
|
193
|
+
function collectDescriptorContainerTokens({ packageId, side, values, issues }) {
|
|
194
|
+
const declaredTokens = new Set();
|
|
195
|
+
const duplicateTokens = new Set();
|
|
196
|
+
let invalidCount = 0;
|
|
197
|
+
|
|
198
|
+
for (const rawValue of ensureArray(values)) {
|
|
199
|
+
if (typeof rawValue !== "string") {
|
|
200
|
+
invalidCount += 1;
|
|
201
|
+
continue;
|
|
202
|
+
}
|
|
203
|
+
const token = rawValue.trim();
|
|
204
|
+
if (!token) {
|
|
205
|
+
invalidCount += 1;
|
|
206
|
+
continue;
|
|
207
|
+
}
|
|
208
|
+
if (declaredTokens.has(token)) {
|
|
209
|
+
duplicateTokens.add(token);
|
|
210
|
+
continue;
|
|
211
|
+
}
|
|
212
|
+
declaredTokens.add(token);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
if (invalidCount > 0) {
|
|
216
|
+
issues.push({
|
|
217
|
+
packageId,
|
|
218
|
+
side,
|
|
219
|
+
code: "descriptor-token-invalid",
|
|
220
|
+
message: `${packageId} (${side}): metadata.apiSummary.containerTokens includes ${invalidCount} non-string or empty token value(s).`
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
for (const token of sortStrings([...duplicateTokens])) {
|
|
224
|
+
issues.push({
|
|
225
|
+
packageId,
|
|
226
|
+
side,
|
|
227
|
+
code: "descriptor-token-duplicate",
|
|
228
|
+
token,
|
|
229
|
+
message: `${packageId} (${side}): descriptor token is declared more than once: ${token}.`
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
return declaredTokens;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
function collectUsedContainerTokens({ packageId, side, bindings, issues }) {
|
|
237
|
+
const usedTokens = new Set();
|
|
238
|
+
for (const rawBinding of ensureArray(bindings)) {
|
|
239
|
+
const binding = ensureObject(rawBinding);
|
|
240
|
+
const tokenExpression = String(binding.tokenExpression || "").trim();
|
|
241
|
+
const token = String(binding.token || "").trim();
|
|
242
|
+
const location = String(binding.location || "").trim();
|
|
243
|
+
if (binding.tokenResolved !== true || !token) {
|
|
244
|
+
const expressionLabel = tokenExpression || "<empty>";
|
|
245
|
+
const locationSuffix = location ? ` at ${location}` : "";
|
|
246
|
+
issues.push({
|
|
247
|
+
packageId,
|
|
248
|
+
side,
|
|
249
|
+
code: "binding-token-unresolved",
|
|
250
|
+
tokenExpression: expressionLabel,
|
|
251
|
+
location,
|
|
252
|
+
message: `${packageId} (${side}): unresolved DI token expression "${expressionLabel}"${locationSuffix}.`
|
|
253
|
+
});
|
|
254
|
+
continue;
|
|
255
|
+
}
|
|
256
|
+
usedTokens.add(token);
|
|
257
|
+
}
|
|
258
|
+
return usedTokens;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
function collectProviderIntrospectionIssues({ packageId, packageInsights, issues }) {
|
|
262
|
+
const introspection = ensureObject(packageInsights);
|
|
263
|
+
if (!introspection.available) {
|
|
264
|
+
issues.push({
|
|
265
|
+
packageId,
|
|
266
|
+
side: "",
|
|
267
|
+
code: "provider-introspection-unavailable",
|
|
268
|
+
message: `${packageId}: provider source introspection is unavailable, so DI token parity cannot be verified.`
|
|
269
|
+
});
|
|
270
|
+
return;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
const notes = ensureArray(introspection.notes).map((value) => String(value || "").trim()).filter(Boolean);
|
|
274
|
+
for (const note of notes) {
|
|
275
|
+
if (
|
|
276
|
+
note.startsWith("Skipped wildcard provider entrypoint during introspection:") ||
|
|
277
|
+
note.startsWith("Provider file missing during introspection:") ||
|
|
278
|
+
note.startsWith("Failed reading provider ")
|
|
279
|
+
) {
|
|
280
|
+
issues.push({
|
|
281
|
+
packageId,
|
|
282
|
+
side: "",
|
|
283
|
+
code: "provider-introspection-incomplete",
|
|
284
|
+
message: `${packageId}: ${note}`
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
function collectDiLabelParityIssuesForPackage({ packageEntry, packageInsights }) {
|
|
291
|
+
const packageId = String(packageEntry?.packageId || "").trim();
|
|
292
|
+
const descriptor = ensureObject(packageEntry?.descriptor);
|
|
293
|
+
const metadataApiSummary = ensureObject(ensureObject(descriptor.metadata).apiSummary);
|
|
294
|
+
const descriptorTokenSummary = ensureObject(metadataApiSummary.containerTokens);
|
|
295
|
+
const bindingSections = ensureObject(ensureObject(packageInsights).containerBindings);
|
|
296
|
+
const issues = [];
|
|
297
|
+
const sides = ["server", "client"];
|
|
298
|
+
|
|
299
|
+
collectProviderIntrospectionIssues({ packageId, packageInsights, issues });
|
|
300
|
+
|
|
301
|
+
for (const side of sides) {
|
|
302
|
+
const declaredTokens = collectDescriptorContainerTokens({
|
|
303
|
+
packageId,
|
|
304
|
+
side,
|
|
305
|
+
values: descriptorTokenSummary[side],
|
|
306
|
+
issues
|
|
307
|
+
});
|
|
308
|
+
const usedTokens = collectUsedContainerTokens({
|
|
309
|
+
packageId,
|
|
310
|
+
side,
|
|
311
|
+
bindings: bindingSections[side],
|
|
312
|
+
issues
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
for (const token of sortStrings([...usedTokens])) {
|
|
316
|
+
if (!declaredTokens.has(token)) {
|
|
317
|
+
issues.push({
|
|
318
|
+
packageId,
|
|
319
|
+
side,
|
|
320
|
+
code: "binding-token-undeclared",
|
|
321
|
+
token,
|
|
322
|
+
message: `${packageId} (${side}): token is used by providers but missing from metadata.apiSummary.containerTokens.${side}: ${token}.`
|
|
323
|
+
});
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
for (const token of sortStrings([...declaredTokens])) {
|
|
327
|
+
if (!usedTokens.has(token)) {
|
|
328
|
+
issues.push({
|
|
329
|
+
packageId,
|
|
330
|
+
side,
|
|
331
|
+
code: "descriptor-token-unused",
|
|
332
|
+
token,
|
|
333
|
+
message: `${packageId} (${side}): token is declared in metadata.apiSummary.containerTokens.${side} but never bound by providers: ${token}.`
|
|
334
|
+
});
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
return issues;
|
|
340
|
+
}
|
|
179
341
|
|
|
180
342
|
async function commandList({ positional, options, cwd, stdout }) {
|
|
181
343
|
const packageRegistry = await loadPackageRegistry();
|
|
@@ -206,8 +368,9 @@ function createCommandHandlers(deps) {
|
|
|
206
368
|
const mode = String(positional[0] || "").trim();
|
|
207
369
|
const shouldListBundles = !mode || mode === "bundles";
|
|
208
370
|
const shouldListPackages = !mode || mode === "packages";
|
|
209
|
-
|
|
210
|
-
|
|
371
|
+
const shouldListGenerators = !mode || mode === "generators";
|
|
372
|
+
|
|
373
|
+
if (!shouldListBundles && !shouldListPackages && !shouldListGenerators) {
|
|
211
374
|
throw createCliError(`Unknown list mode: ${mode}`, { showUsage: true });
|
|
212
375
|
}
|
|
213
376
|
|
|
@@ -237,8 +400,11 @@ function createCommandHandlers(deps) {
|
|
|
237
400
|
if (lines.length > 0) {
|
|
238
401
|
lines.push("");
|
|
239
402
|
}
|
|
240
|
-
lines.push(color.heading("Available packages:"));
|
|
241
|
-
const packageIds = sortStrings([...packageRegistry.keys()])
|
|
403
|
+
lines.push(color.heading("Available runtime packages:"));
|
|
404
|
+
const packageIds = sortStrings([...packageRegistry.keys()].filter((packageId) => {
|
|
405
|
+
const packageEntry = packageRegistry.get(packageId);
|
|
406
|
+
return resolvePackageKind(packageEntry) === "runtime";
|
|
407
|
+
}));
|
|
242
408
|
for (const packageId of packageIds) {
|
|
243
409
|
const packageEntry = packageRegistry.get(packageId);
|
|
244
410
|
const installedLabel = installedPackages.has(packageId) ? " (installed)" : "";
|
|
@@ -280,6 +446,24 @@ function createCommandHandlers(deps) {
|
|
|
280
446
|
}
|
|
281
447
|
}
|
|
282
448
|
}
|
|
449
|
+
|
|
450
|
+
if (shouldListGenerators) {
|
|
451
|
+
if (lines.length > 0) {
|
|
452
|
+
lines.push("");
|
|
453
|
+
}
|
|
454
|
+
lines.push(color.heading("Available generators:"));
|
|
455
|
+
const packageIds = sortStrings([...packageRegistry.keys()].filter((packageId) => {
|
|
456
|
+
const packageEntry = packageRegistry.get(packageId);
|
|
457
|
+
return resolvePackageKind(packageEntry) === "generator";
|
|
458
|
+
}));
|
|
459
|
+
for (const packageId of packageIds) {
|
|
460
|
+
const packageEntry = packageRegistry.get(packageId);
|
|
461
|
+
const installedLabel = installedPackages.has(packageId) ? " (installed)" : "";
|
|
462
|
+
lines.push(
|
|
463
|
+
`- ${color.item(packageId)} ${color.version(`(${packageEntry.version})`)}${installedLabel ? color.installed(installedLabel) : ""}`
|
|
464
|
+
);
|
|
465
|
+
}
|
|
466
|
+
}
|
|
283
467
|
|
|
284
468
|
if (options.json) {
|
|
285
469
|
const payload = {
|
|
@@ -298,14 +482,42 @@ function createCommandHandlers(deps) {
|
|
|
298
482
|
})
|
|
299
483
|
: [],
|
|
300
484
|
packages: shouldListPackages
|
|
485
|
+
? sortStrings([...packageRegistry.keys()])
|
|
486
|
+
.filter((packageId) => resolvePackageKind(packageRegistry.get(packageId)) === "runtime")
|
|
487
|
+
.map((packageId) => {
|
|
488
|
+
const packageEntry = packageRegistry.get(packageId);
|
|
489
|
+
return {
|
|
490
|
+
packageId,
|
|
491
|
+
version: packageEntry.version,
|
|
492
|
+
installed: installedPackages.has(packageId)
|
|
493
|
+
};
|
|
494
|
+
})
|
|
495
|
+
: [],
|
|
496
|
+
runtimePackages: shouldListPackages
|
|
301
497
|
? sortStrings([...packageRegistry.keys()]).map((packageId) => {
|
|
302
498
|
const packageEntry = packageRegistry.get(packageId);
|
|
499
|
+
if (resolvePackageKind(packageEntry) !== "runtime") {
|
|
500
|
+
return null;
|
|
501
|
+
}
|
|
303
502
|
return {
|
|
304
503
|
packageId,
|
|
305
504
|
version: packageEntry.version,
|
|
306
505
|
installed: installedPackages.has(packageId)
|
|
307
506
|
};
|
|
308
|
-
})
|
|
507
|
+
}).filter(Boolean)
|
|
508
|
+
: [],
|
|
509
|
+
generators: shouldListGenerators
|
|
510
|
+
? sortStrings([...packageRegistry.keys()]).map((packageId) => {
|
|
511
|
+
const packageEntry = packageRegistry.get(packageId);
|
|
512
|
+
if (resolvePackageKind(packageEntry) !== "generator") {
|
|
513
|
+
return null;
|
|
514
|
+
}
|
|
515
|
+
return {
|
|
516
|
+
packageId,
|
|
517
|
+
version: packageEntry.version,
|
|
518
|
+
installed: installedPackages.has(packageId)
|
|
519
|
+
};
|
|
520
|
+
}).filter(Boolean)
|
|
309
521
|
: [],
|
|
310
522
|
installedLocalPackages: shouldListPackages
|
|
311
523
|
? installedLocalPackageIds.map((packageId) => {
|
|
@@ -1102,10 +1314,16 @@ function createCommandHandlers(deps) {
|
|
|
1102
1314
|
}
|
|
1103
1315
|
|
|
1104
1316
|
async function commandAdd({ positional, options, cwd, io }) {
|
|
1317
|
+
const invocationMode = options?.commandMode === "generate" ? "generate" : "add";
|
|
1105
1318
|
const targetType = String(positional[0] || "").trim();
|
|
1106
1319
|
const targetId = String(positional[1] || "").trim();
|
|
1107
1320
|
|
|
1108
1321
|
if (!targetType || !targetId) {
|
|
1322
|
+
if (invocationMode === "generate") {
|
|
1323
|
+
throw createCliError("generate requires a package id (generate <packageId>).", {
|
|
1324
|
+
showUsage: true
|
|
1325
|
+
});
|
|
1326
|
+
}
|
|
1109
1327
|
throw createCliError("add requires target type and id (add bundle <id> | add package <id>).", {
|
|
1110
1328
|
showUsage: true
|
|
1111
1329
|
});
|
|
@@ -1113,6 +1331,11 @@ function createCommandHandlers(deps) {
|
|
|
1113
1331
|
if (targetType !== "bundle" && targetType !== "package") {
|
|
1114
1332
|
throw createCliError(`Unsupported add target type: ${targetType}`, { showUsage: true });
|
|
1115
1333
|
}
|
|
1334
|
+
if (invocationMode === "generate" && targetType !== "package") {
|
|
1335
|
+
throw createCliError("generate requires a package id (generate <packageId>).", {
|
|
1336
|
+
showUsage: true
|
|
1337
|
+
});
|
|
1338
|
+
}
|
|
1116
1339
|
|
|
1117
1340
|
const appRoot = await resolveAppRootFromCwd(cwd);
|
|
1118
1341
|
const packageRegistry = await loadPackageRegistry();
|
|
@@ -1122,10 +1345,11 @@ function createCommandHandlers(deps) {
|
|
|
1122
1345
|
const { packageJsonPath, packageJson } = await loadAppPackageJson(appRoot);
|
|
1123
1346
|
const { lockPath, lock } = await loadLockFile(appRoot);
|
|
1124
1347
|
let resolvedTargetPackageId = targetType === "package" ? resolvePackageIdInput(targetId, combinedPackageRegistry) : "";
|
|
1125
|
-
if (targetType === "package"
|
|
1348
|
+
if (targetType === "package") {
|
|
1349
|
+
const packageIdForNodeModulesLookup = resolvedTargetPackageId || targetId;
|
|
1126
1350
|
const installedNodeModuleEntry = await resolveInstalledNodeModulePackageEntry({
|
|
1127
1351
|
appRoot,
|
|
1128
|
-
packageId:
|
|
1352
|
+
packageId: packageIdForNodeModulesLookup
|
|
1129
1353
|
});
|
|
1130
1354
|
if (installedNodeModuleEntry) {
|
|
1131
1355
|
combinedPackageRegistry.set(installedNodeModuleEntry.packageId, installedNodeModuleEntry);
|
|
@@ -1156,6 +1380,17 @@ function createCommandHandlers(deps) {
|
|
|
1156
1380
|
if (!targetPackageEntry) {
|
|
1157
1381
|
throw createCliError(`Unknown package: ${targetId}`);
|
|
1158
1382
|
}
|
|
1383
|
+
const packageKind = resolvePackageKind(targetPackageEntry);
|
|
1384
|
+
if (invocationMode === "add" && packageKind === "generator") {
|
|
1385
|
+
throw createCliError(
|
|
1386
|
+
`Package ${resolvedTargetPackageId} is a generator. Use: jskit generate ${resolvedTargetPackageId}`
|
|
1387
|
+
);
|
|
1388
|
+
}
|
|
1389
|
+
if (invocationMode === "generate" && packageKind !== "generator") {
|
|
1390
|
+
throw createCliError(
|
|
1391
|
+
`Package ${resolvedTargetPackageId} is a runtime package. Use: jskit add package ${resolvedTargetPackageId}`
|
|
1392
|
+
);
|
|
1393
|
+
}
|
|
1159
1394
|
validateInlineOptionsForPackage(targetPackageEntry, options.inlineOptions);
|
|
1160
1395
|
}
|
|
1161
1396
|
|
|
@@ -1163,6 +1398,17 @@ function createCommandHandlers(deps) {
|
|
|
1163
1398
|
targetPackageIds,
|
|
1164
1399
|
combinedPackageRegistry
|
|
1165
1400
|
);
|
|
1401
|
+
if (invocationMode === "add" && targetType === "bundle") {
|
|
1402
|
+
const bundledGenerators = resolvedPackageIds.filter((packageId) => {
|
|
1403
|
+
const packageEntry = combinedPackageRegistry.get(packageId);
|
|
1404
|
+
return resolvePackageKind(packageEntry) === "generator";
|
|
1405
|
+
});
|
|
1406
|
+
if (bundledGenerators.length > 0) {
|
|
1407
|
+
throw createCliError(
|
|
1408
|
+
`Bundle ${targetId} includes generator package(s): ${bundledGenerators.join(", ")}. Use: jskit generate <packageId>`
|
|
1409
|
+
);
|
|
1410
|
+
}
|
|
1411
|
+
}
|
|
1166
1412
|
const plannedInstalledPackageIds = sortStrings([
|
|
1167
1413
|
...new Set([
|
|
1168
1414
|
...Object.keys(ensureObject(lock.installedPackages)).map((value) => String(value || "").trim()).filter(Boolean),
|
|
@@ -1172,7 +1418,7 @@ function createCommandHandlers(deps) {
|
|
|
1172
1418
|
validatePlannedCapabilityClosure(
|
|
1173
1419
|
plannedInstalledPackageIds,
|
|
1174
1420
|
combinedPackageRegistry,
|
|
1175
|
-
|
|
1421
|
+
`${invocationMode} ${targetType} ${targetId}`
|
|
1176
1422
|
);
|
|
1177
1423
|
|
|
1178
1424
|
if (targetType === "bundle") {
|
|
@@ -1251,14 +1497,18 @@ function createCommandHandlers(deps) {
|
|
|
1251
1497
|
validatePlannedCapabilityClosure(
|
|
1252
1498
|
postInstallPackageIds,
|
|
1253
1499
|
combinedPackageRegistry,
|
|
1254
|
-
|
|
1500
|
+
`${invocationMode} ${targetType} ${targetId}`
|
|
1255
1501
|
);
|
|
1256
1502
|
}
|
|
1257
1503
|
|
|
1258
1504
|
const finalResolvedPackageIds = sortStrings([...resolvedPackageIds, ...adoptedPackageIds]);
|
|
1259
1505
|
|
|
1260
1506
|
const touchedFileList = sortStrings([...touchedFiles]);
|
|
1261
|
-
const successLabel =
|
|
1507
|
+
const successLabel = invocationMode === "generate"
|
|
1508
|
+
? "Generated with"
|
|
1509
|
+
: targetType === "bundle"
|
|
1510
|
+
? "Added bundle"
|
|
1511
|
+
: "Added package";
|
|
1262
1512
|
const installWarnings = installedPackageRecords
|
|
1263
1513
|
.flatMap((record) => ensureArray(ensureObject(record).warnings))
|
|
1264
1514
|
.map((value) => String(value || "").trim())
|
|
@@ -1274,7 +1524,7 @@ function createCommandHandlers(deps) {
|
|
|
1274
1524
|
|
|
1275
1525
|
if (options.json) {
|
|
1276
1526
|
io.stdout.write(`${JSON.stringify({
|
|
1277
|
-
targetType,
|
|
1527
|
+
targetType: invocationMode === "generate" ? "generator" : targetType,
|
|
1278
1528
|
targetId,
|
|
1279
1529
|
resolvedPackages: finalResolvedPackageIds,
|
|
1280
1530
|
touchedFiles: touchedFileList,
|
|
@@ -1309,6 +1559,32 @@ function createCommandHandlers(deps) {
|
|
|
1309
1559
|
|
|
1310
1560
|
return 0;
|
|
1311
1561
|
}
|
|
1562
|
+
|
|
1563
|
+
async function commandGenerate({ positional, options, cwd, io }) {
|
|
1564
|
+
const firstToken = String(positional[0] || "").trim();
|
|
1565
|
+
const secondToken = String(positional[1] || "").trim();
|
|
1566
|
+
if (firstToken === "bundle") {
|
|
1567
|
+
throw createCliError("generate supports packages only (generate <packageId>).", {
|
|
1568
|
+
showUsage: true
|
|
1569
|
+
});
|
|
1570
|
+
}
|
|
1571
|
+
const targetId = firstToken === "package" ? secondToken : firstToken;
|
|
1572
|
+
if (!targetId) {
|
|
1573
|
+
throw createCliError("generate requires a package id (generate <packageId>).", {
|
|
1574
|
+
showUsage: true
|
|
1575
|
+
});
|
|
1576
|
+
}
|
|
1577
|
+
|
|
1578
|
+
return commandAdd({
|
|
1579
|
+
positional: ["package", targetId],
|
|
1580
|
+
options: {
|
|
1581
|
+
...options,
|
|
1582
|
+
commandMode: "generate"
|
|
1583
|
+
},
|
|
1584
|
+
cwd,
|
|
1585
|
+
io
|
|
1586
|
+
});
|
|
1587
|
+
}
|
|
1312
1588
|
|
|
1313
1589
|
async function commandUpdate({ positional, options, cwd, io }) {
|
|
1314
1590
|
const targetType = String(positional[0] || "").trim();
|
|
@@ -1336,6 +1612,138 @@ function createCommandHandlers(deps) {
|
|
|
1336
1612
|
});
|
|
1337
1613
|
}
|
|
1338
1614
|
|
|
1615
|
+
async function commandMigrations({ positional, options, cwd, io }) {
|
|
1616
|
+
const scope = String(positional[0] || "").trim().toLowerCase();
|
|
1617
|
+
const targetId = String(positional[1] || "").trim();
|
|
1618
|
+
if (!scope || (scope !== "all" && scope !== "changed" && scope !== "package")) {
|
|
1619
|
+
throw createCliError("migrations requires: migrations <all|changed|package <packageId>>", {
|
|
1620
|
+
showUsage: true
|
|
1621
|
+
});
|
|
1622
|
+
}
|
|
1623
|
+
if (scope === "package" && !targetId) {
|
|
1624
|
+
throw createCliError("migrations package requires a package id.", {
|
|
1625
|
+
showUsage: true
|
|
1626
|
+
});
|
|
1627
|
+
}
|
|
1628
|
+
if (scope !== "package" && Object.keys(ensureObject(options.inlineOptions)).length > 0) {
|
|
1629
|
+
throw createCliError("Inline options are only supported with: migrations package <packageId>.");
|
|
1630
|
+
}
|
|
1631
|
+
|
|
1632
|
+
const appRoot = await resolveAppRootFromCwd(cwd);
|
|
1633
|
+
const packageRegistry = await loadPackageRegistry();
|
|
1634
|
+
const appLocalRegistry = await loadAppLocalPackageRegistry(appRoot);
|
|
1635
|
+
const combinedPackageRegistry = mergePackageRegistries(packageRegistry, appLocalRegistry);
|
|
1636
|
+
const { lockPath, lock } = await loadLockFile(appRoot);
|
|
1637
|
+
const installedPackages = ensureObject(lock.installedPackages);
|
|
1638
|
+
const installedPackageIds = sortStrings(Object.keys(installedPackages));
|
|
1639
|
+
await hydratePackageRegistryFromInstalledNodeModules({
|
|
1640
|
+
appRoot,
|
|
1641
|
+
packageRegistry: combinedPackageRegistry,
|
|
1642
|
+
seedPackageIds: installedPackageIds
|
|
1643
|
+
});
|
|
1644
|
+
|
|
1645
|
+
let requestedPackageIds = [];
|
|
1646
|
+
if (scope === "all") {
|
|
1647
|
+
requestedPackageIds = installedPackageIds;
|
|
1648
|
+
} else if (scope === "package") {
|
|
1649
|
+
const resolvedTargetId = resolveInstalledPackageIdInput(targetId, installedPackages);
|
|
1650
|
+
if (!resolvedTargetId) {
|
|
1651
|
+
throw createCliError(`Package is not installed: ${targetId}`);
|
|
1652
|
+
}
|
|
1653
|
+
requestedPackageIds = [resolvedTargetId];
|
|
1654
|
+
} else {
|
|
1655
|
+
requestedPackageIds = installedPackageIds.filter((packageId) => {
|
|
1656
|
+
const packageEntry = combinedPackageRegistry.get(packageId);
|
|
1657
|
+
if (!packageEntry) {
|
|
1658
|
+
return true;
|
|
1659
|
+
}
|
|
1660
|
+
const lockEntry = ensureObject(installedPackages[packageId]);
|
|
1661
|
+
const migrationSyncVersion = String(lockEntry.migrationSyncVersion || "").trim();
|
|
1662
|
+
return migrationSyncVersion !== String(packageEntry.version || "").trim();
|
|
1663
|
+
});
|
|
1664
|
+
}
|
|
1665
|
+
|
|
1666
|
+
const touchedFiles = new Set();
|
|
1667
|
+
const migratedRecords = [];
|
|
1668
|
+
const migrationWarnings = [];
|
|
1669
|
+
for (const packageId of requestedPackageIds) {
|
|
1670
|
+
const packageEntry = combinedPackageRegistry.get(packageId);
|
|
1671
|
+
if (!packageEntry) {
|
|
1672
|
+
throw createCliError(
|
|
1673
|
+
`Installed package descriptor not found: ${packageId}. Ensure it is available in catalog, app packages/, or node_modules.`
|
|
1674
|
+
);
|
|
1675
|
+
}
|
|
1676
|
+
validateInlineOptionsForPackage(packageEntry, options.inlineOptions);
|
|
1677
|
+
const installedRecord = ensureObject(installedPackages[packageId]);
|
|
1678
|
+
const mergedInlineOptions = scope === "package" ? ensureObject(options.inlineOptions) : {};
|
|
1679
|
+
const resolvedOptions = await resolvePackageOptions(
|
|
1680
|
+
packageEntry,
|
|
1681
|
+
{
|
|
1682
|
+
...ensureObject(installedRecord.options),
|
|
1683
|
+
...mergedInlineOptions
|
|
1684
|
+
},
|
|
1685
|
+
io,
|
|
1686
|
+
{ appRoot }
|
|
1687
|
+
);
|
|
1688
|
+
|
|
1689
|
+
const managedRecord = await applyPackageMigrationsOnly({
|
|
1690
|
+
packageEntry,
|
|
1691
|
+
packageOptions: resolvedOptions,
|
|
1692
|
+
appRoot,
|
|
1693
|
+
lock,
|
|
1694
|
+
touchedFiles
|
|
1695
|
+
});
|
|
1696
|
+
migratedRecords.push(managedRecord);
|
|
1697
|
+
for (const warning of ensureArray(ensureObject(managedRecord).warnings)) {
|
|
1698
|
+
const normalizedWarning = String(warning || "").trim();
|
|
1699
|
+
if (!normalizedWarning) {
|
|
1700
|
+
continue;
|
|
1701
|
+
}
|
|
1702
|
+
migrationWarnings.push(normalizedWarning);
|
|
1703
|
+
}
|
|
1704
|
+
}
|
|
1705
|
+
|
|
1706
|
+
const touchedFileList = sortStrings([...touchedFiles]);
|
|
1707
|
+
if (!options.dryRun) {
|
|
1708
|
+
await writeJsonFile(lockPath, lock);
|
|
1709
|
+
}
|
|
1710
|
+
|
|
1711
|
+
if (options.json) {
|
|
1712
|
+
io.stdout.write(`${JSON.stringify({
|
|
1713
|
+
targetType: "migrations",
|
|
1714
|
+
scope,
|
|
1715
|
+
requestedPackages: requestedPackageIds,
|
|
1716
|
+
touchedFiles: touchedFileList,
|
|
1717
|
+
lockPath: normalizeRelativePath(appRoot, lockPath),
|
|
1718
|
+
dryRun: options.dryRun,
|
|
1719
|
+
migrated: migratedRecords,
|
|
1720
|
+
warnings: migrationWarnings
|
|
1721
|
+
}, null, 2)}\n`);
|
|
1722
|
+
} else {
|
|
1723
|
+
io.stdout.write(`Generated migrations (${scope}).\n`);
|
|
1724
|
+
io.stdout.write(`Resolved packages (${requestedPackageIds.length}):\n`);
|
|
1725
|
+
for (const packageId of requestedPackageIds) {
|
|
1726
|
+
io.stdout.write(`- ${packageId}\n`);
|
|
1727
|
+
}
|
|
1728
|
+
io.stdout.write(`Touched files (${touchedFileList.length}):\n`);
|
|
1729
|
+
for (const touchedFile of touchedFileList) {
|
|
1730
|
+
io.stdout.write(`- ${touchedFile}\n`);
|
|
1731
|
+
}
|
|
1732
|
+
io.stdout.write(`Lock file: ${normalizeRelativePath(appRoot, lockPath)}\n`);
|
|
1733
|
+
if (options.verbose && migrationWarnings.length > 0) {
|
|
1734
|
+
io.stdout.write(`Warnings (${migrationWarnings.length}):\n`);
|
|
1735
|
+
for (const warning of migrationWarnings) {
|
|
1736
|
+
io.stdout.write(`- ${warning}\n`);
|
|
1737
|
+
}
|
|
1738
|
+
}
|
|
1739
|
+
if (options.dryRun) {
|
|
1740
|
+
io.stdout.write("Dry run enabled: no files were written.\n");
|
|
1741
|
+
}
|
|
1742
|
+
}
|
|
1743
|
+
|
|
1744
|
+
return 0;
|
|
1745
|
+
}
|
|
1746
|
+
|
|
1339
1747
|
async function commandPosition({ positional, options, cwd, io }) {
|
|
1340
1748
|
const targetType = String(positional[0] || "").trim();
|
|
1341
1749
|
const targetId = String(positional[1] || "").trim();
|
|
@@ -1627,19 +2035,58 @@ function createCommandHandlers(deps) {
|
|
|
1627
2035
|
async function commandLintDescriptors({ options, stdout }) {
|
|
1628
2036
|
const packageRegistry = await loadPackageRegistry();
|
|
1629
2037
|
const bundleRegistry = await loadBundleRegistry();
|
|
2038
|
+
const shouldCheckDiLabels = options.checkDiLabels === true;
|
|
2039
|
+
let diLabelIssues = [];
|
|
2040
|
+
if (shouldCheckDiLabels) {
|
|
2041
|
+
const issues = [];
|
|
2042
|
+
for (const packageId of sortStrings([...packageRegistry.keys()])) {
|
|
2043
|
+
const packageEntry = packageRegistry.get(packageId);
|
|
2044
|
+
if (!packageEntry) {
|
|
2045
|
+
continue;
|
|
2046
|
+
}
|
|
2047
|
+
const packageInsights = await inspectPackageOfferings({ packageEntry });
|
|
2048
|
+
issues.push(...collectDiLabelParityIssuesForPackage({ packageEntry, packageInsights }));
|
|
2049
|
+
}
|
|
2050
|
+
diLabelIssues = issues;
|
|
2051
|
+
}
|
|
1630
2052
|
const payload = {
|
|
1631
2053
|
packageCount: packageRegistry.size,
|
|
1632
2054
|
bundleCount: bundleRegistry.size,
|
|
1633
2055
|
packages: sortStrings([...packageRegistry.keys()]),
|
|
1634
|
-
bundles: sortStrings([...bundleRegistry.keys()])
|
|
2056
|
+
bundles: sortStrings([...bundleRegistry.keys()]),
|
|
2057
|
+
diLabelCheck: shouldCheckDiLabels
|
|
2058
|
+
? {
|
|
2059
|
+
enabled: true,
|
|
2060
|
+
issueCount: diLabelIssues.length,
|
|
2061
|
+
issues: diLabelIssues
|
|
2062
|
+
}
|
|
2063
|
+
: {
|
|
2064
|
+
enabled: false
|
|
2065
|
+
}
|
|
1635
2066
|
};
|
|
1636
2067
|
|
|
1637
2068
|
if (options.json) {
|
|
1638
2069
|
stdout.write(`${JSON.stringify(payload, null, 2)}\n`);
|
|
1639
2070
|
} else {
|
|
1640
|
-
|
|
2071
|
+
const descriptorStatus = shouldCheckDiLabels && diLabelIssues.length > 0 ? "failed" : "passed";
|
|
2072
|
+
stdout.write(`Descriptor lint ${descriptorStatus}.\n`);
|
|
1641
2073
|
stdout.write(`Packages: ${payload.packageCount}\n`);
|
|
1642
2074
|
stdout.write(`Bundles: ${payload.bundleCount}\n`);
|
|
2075
|
+
if (shouldCheckDiLabels) {
|
|
2076
|
+
if (diLabelIssues.length === 0) {
|
|
2077
|
+
stdout.write("DI label parity check passed.\n");
|
|
2078
|
+
} else {
|
|
2079
|
+
stdout.write(`DI label parity check failed (${diLabelIssues.length} issue(s)).\n`);
|
|
2080
|
+
for (const issue of diLabelIssues) {
|
|
2081
|
+
const code = String(issue?.code || "").trim();
|
|
2082
|
+
const codeLabel = code ? `[${code}] ` : "";
|
|
2083
|
+
stdout.write(`- ${codeLabel}${String(issue?.message || "").trim()}\n`);
|
|
2084
|
+
}
|
|
2085
|
+
}
|
|
2086
|
+
}
|
|
2087
|
+
}
|
|
2088
|
+
if (shouldCheckDiLabels && diLabelIssues.length > 0) {
|
|
2089
|
+
return 1;
|
|
1643
2090
|
}
|
|
1644
2091
|
return 0;
|
|
1645
2092
|
}
|
|
@@ -1649,6 +2096,8 @@ function createCommandHandlers(deps) {
|
|
|
1649
2096
|
commandShow,
|
|
1650
2097
|
commandCreate,
|
|
1651
2098
|
commandAdd,
|
|
2099
|
+
commandGenerate,
|
|
2100
|
+
commandMigrations,
|
|
1652
2101
|
commandPosition,
|
|
1653
2102
|
commandUpdate,
|
|
1654
2103
|
commandRemove,
|