@jskit-ai/jskit-cli 0.2.152 → 0.2.154
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jskit-ai/jskit-cli",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.154",
|
|
4
4
|
"description": "Bundle and package orchestration CLI for JSKIT apps.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|
|
@@ -21,10 +21,11 @@
|
|
|
21
21
|
"test": "node --test"
|
|
22
22
|
},
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"@jskit-ai/jskit-catalog": "0.1.
|
|
25
|
-
"@jskit-ai/kernel": "0.1.
|
|
26
|
-
"@jskit-ai/shell-web": "0.1.
|
|
24
|
+
"@jskit-ai/jskit-catalog": "0.1.148",
|
|
25
|
+
"@jskit-ai/kernel": "0.1.133",
|
|
26
|
+
"@jskit-ai/shell-web": "0.1.134",
|
|
27
27
|
"@vue/compiler-sfc": "^3.5.29",
|
|
28
|
+
"semver": "^7.7.4",
|
|
28
29
|
"ts-morph": "^28.0.0",
|
|
29
30
|
"yaml": "^2.8.3"
|
|
30
31
|
},
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { readFile, writeFile } from "node:fs/promises";
|
|
2
2
|
import path from "node:path";
|
|
3
|
+
import semver from "semver";
|
|
3
4
|
|
|
4
5
|
import {
|
|
5
6
|
runExternalCommandAsync,
|
|
@@ -81,6 +82,144 @@ function resolveInstallSpecs(packageNames = [], latestVersions = new Map()) {
|
|
|
81
82
|
return packageNames.map((packageName) => `${packageName}@${latestVersions.get(packageName)}`);
|
|
82
83
|
}
|
|
83
84
|
|
|
85
|
+
function parseRegistryPackageManifest(rawValue, packageName, createCliError) {
|
|
86
|
+
let parsed;
|
|
87
|
+
try {
|
|
88
|
+
parsed = JSON.parse(String(rawValue || ""));
|
|
89
|
+
} catch (error) {
|
|
90
|
+
throw createCliError(
|
|
91
|
+
`npm returned invalid metadata for ${packageName}: ${error instanceof Error ? error.message : String(error)}.`,
|
|
92
|
+
{ exitCode: 1 }
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
96
|
+
throw createCliError(`npm returned invalid metadata for ${packageName}: expected an object.`, {
|
|
97
|
+
exitCode: 1
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
return parsed;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
async function resolveRegistryPackageManifests(packageNames = [], latestVersions = new Map(), {
|
|
104
|
+
appRoot,
|
|
105
|
+
createCliError,
|
|
106
|
+
registryArgs,
|
|
107
|
+
stderr,
|
|
108
|
+
stdout
|
|
109
|
+
}) {
|
|
110
|
+
const manifests = new Map();
|
|
111
|
+
for (const packageName of packageNames) {
|
|
112
|
+
const version = latestVersions.get(packageName);
|
|
113
|
+
const result = await runExternalCommandAsync(
|
|
114
|
+
"npm",
|
|
115
|
+
["view", ...registryArgs, `${packageName}@${version}`, "--json"],
|
|
116
|
+
{
|
|
117
|
+
cwd: appRoot,
|
|
118
|
+
stdout,
|
|
119
|
+
stderr,
|
|
120
|
+
quiet: true,
|
|
121
|
+
createCliError
|
|
122
|
+
}
|
|
123
|
+
);
|
|
124
|
+
manifests.set(
|
|
125
|
+
packageName,
|
|
126
|
+
parseRegistryPackageManifest(result.stdout, packageName, createCliError)
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
return manifests;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function resolveDeclaredDependencySection(packageJson = {}, packageName = "") {
|
|
133
|
+
return DEPENDENCY_SECTIONS.find((section) =>
|
|
134
|
+
Object.prototype.hasOwnProperty.call(packageJson?.[section.name] || {}, packageName)
|
|
135
|
+
) || null;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function findRangeIntersectionVersion(ranges = []) {
|
|
139
|
+
const normalizedRanges = ranges
|
|
140
|
+
.map((range) => semver.validRange(String(range || "").trim()))
|
|
141
|
+
.filter(Boolean);
|
|
142
|
+
if (normalizedRanges.length !== ranges.length) {
|
|
143
|
+
return null;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const candidates = normalizedRanges
|
|
147
|
+
.map((range) => semver.minVersion(range))
|
|
148
|
+
.filter(Boolean)
|
|
149
|
+
.sort(semver.compare);
|
|
150
|
+
return candidates.find((version) =>
|
|
151
|
+
normalizedRanges.every((range) => semver.satisfies(version, range))
|
|
152
|
+
) || null;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function resolveRequiredDirectPeerUpdates({
|
|
156
|
+
createCliError,
|
|
157
|
+
packageJson = {},
|
|
158
|
+
packageManifests = new Map()
|
|
159
|
+
} = {}) {
|
|
160
|
+
const requirementsByPeer = new Map();
|
|
161
|
+
for (const [packageName, manifest] of packageManifests.entries()) {
|
|
162
|
+
const peerDependencies = ensureObject(manifest?.peerDependencies);
|
|
163
|
+
const peerDependenciesMeta = ensureObject(manifest?.peerDependenciesMeta);
|
|
164
|
+
for (const [peerName, rawRange] of Object.entries(peerDependencies)) {
|
|
165
|
+
if (ensureObject(peerDependenciesMeta[peerName]).optional === true) {
|
|
166
|
+
continue;
|
|
167
|
+
}
|
|
168
|
+
const section = resolveDeclaredDependencySection(packageJson, peerName);
|
|
169
|
+
if (!section) {
|
|
170
|
+
continue;
|
|
171
|
+
}
|
|
172
|
+
const range = String(rawRange || "").trim();
|
|
173
|
+
if (!semver.validRange(range)) {
|
|
174
|
+
throw createCliError(
|
|
175
|
+
`${packageName} declares an invalid required peer range for ${peerName}: ${range || "<empty>"}.`,
|
|
176
|
+
{ exitCode: 1 }
|
|
177
|
+
);
|
|
178
|
+
}
|
|
179
|
+
const requirement = requirementsByPeer.get(peerName) || {
|
|
180
|
+
packageNames: [],
|
|
181
|
+
ranges: [],
|
|
182
|
+
section
|
|
183
|
+
};
|
|
184
|
+
requirement.packageNames.push(packageName);
|
|
185
|
+
requirement.ranges.push(range);
|
|
186
|
+
requirementsByPeer.set(peerName, requirement);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const updates = [];
|
|
191
|
+
for (const [peerName, requirement] of [...requirementsByPeer.entries()].sort(([left], [right]) =>
|
|
192
|
+
left.localeCompare(right)
|
|
193
|
+
)) {
|
|
194
|
+
const uniqueRanges = [...new Set(requirement.ranges)];
|
|
195
|
+
const targetVersion = findRangeIntersectionVersion(uniqueRanges);
|
|
196
|
+
if (!targetVersion) {
|
|
197
|
+
throw createCliError(
|
|
198
|
+
`Updated JSKIT packages require incompatible ${peerName} peers: ${uniqueRanges.join(", ")}.`,
|
|
199
|
+
{ exitCode: 1 }
|
|
200
|
+
);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
const currentRange = String(packageJson?.[requirement.section.name]?.[peerName] || "").trim();
|
|
204
|
+
if (findRangeIntersectionVersion([currentRange, ...uniqueRanges])) {
|
|
205
|
+
continue;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
const subsetRange = uniqueRanges.find((candidateRange) =>
|
|
209
|
+
uniqueRanges.every((otherRange) => semver.subset(candidateRange, otherRange))
|
|
210
|
+
);
|
|
211
|
+
updates.push(Object.freeze({
|
|
212
|
+
name: peerName,
|
|
213
|
+
packageNames: Object.freeze([...new Set(requirement.packageNames)].sort()),
|
|
214
|
+
previousRange: currentRange,
|
|
215
|
+
section: requirement.section,
|
|
216
|
+
targetRange: subsetRange || targetVersion.version
|
|
217
|
+
}));
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
return Object.freeze(updates);
|
|
221
|
+
}
|
|
222
|
+
|
|
84
223
|
function collectChangedInstalledPackageIds(lock = {}, latestVersions = new Map()) {
|
|
85
224
|
const installedPackages = ensureObject(lock.installedPackages);
|
|
86
225
|
return sortStrings(
|
|
@@ -474,13 +613,53 @@ async function updateRootPackages({
|
|
|
474
613
|
stderr,
|
|
475
614
|
stdout
|
|
476
615
|
});
|
|
616
|
+
const packageManifests = await resolveRegistryPackageManifests(
|
|
617
|
+
rootPackageNames,
|
|
618
|
+
latestVersions,
|
|
619
|
+
{
|
|
620
|
+
appRoot,
|
|
621
|
+
createCliError,
|
|
622
|
+
registryArgs,
|
|
623
|
+
stderr,
|
|
624
|
+
stdout
|
|
625
|
+
}
|
|
626
|
+
);
|
|
627
|
+
const peerUpdates = resolveRequiredDirectPeerUpdates({
|
|
628
|
+
createCliError,
|
|
629
|
+
packageJson,
|
|
630
|
+
packageManifests
|
|
631
|
+
});
|
|
632
|
+
for (const peerUpdate of peerUpdates) {
|
|
633
|
+
stdout?.write(
|
|
634
|
+
`[jskit:update] reconciling required direct peer ${peerUpdate.name}: ` +
|
|
635
|
+
`${peerUpdate.previousRange} -> ${peerUpdate.targetRange} ` +
|
|
636
|
+
`(required by ${peerUpdate.packageNames.join(", ")}).\n`
|
|
637
|
+
);
|
|
638
|
+
packageJson[peerUpdate.section.name][peerUpdate.name] = peerUpdate.targetRange;
|
|
639
|
+
}
|
|
640
|
+
if (!dryRun && peerUpdates.length > 0) {
|
|
641
|
+
await writeFile(
|
|
642
|
+
path.join(appRoot, "package.json"),
|
|
643
|
+
`${JSON.stringify(packageJson, null, 2)}\n`,
|
|
644
|
+
"utf8"
|
|
645
|
+
);
|
|
646
|
+
}
|
|
647
|
+
|
|
477
648
|
const dryRunArgs = dryRun ? ["--dry-run"] : [];
|
|
478
649
|
for (const section of DEPENDENCY_SECTIONS) {
|
|
479
650
|
const packageNames = collectJskitPackageNames(packageJson?.[section.name]);
|
|
480
|
-
|
|
651
|
+
const dryRunPeerSpecs = dryRun
|
|
652
|
+
? peerUpdates
|
|
653
|
+
.filter((peerUpdate) => peerUpdate.section.name === section.name)
|
|
654
|
+
.map((peerUpdate) => `${peerUpdate.name}@${peerUpdate.targetRange}`)
|
|
655
|
+
: [];
|
|
656
|
+
if (packageNames.length < 1 && dryRunPeerSpecs.length < 1) {
|
|
481
657
|
continue;
|
|
482
658
|
}
|
|
483
|
-
const installSpecs =
|
|
659
|
+
const installSpecs = [
|
|
660
|
+
...resolveInstallSpecs(packageNames, latestVersions),
|
|
661
|
+
...dryRunPeerSpecs
|
|
662
|
+
];
|
|
484
663
|
stdout?.write(`[jskit:update] updating ${section.label} packages: ${installSpecs.join(" ")}\n`);
|
|
485
664
|
await runExternalCommandAsync(
|
|
486
665
|
"npm",
|
|
@@ -685,7 +864,9 @@ async function runAppUpdatePackagesCommand(ctx = {}, { appRoot = "", options = {
|
|
|
685
864
|
|
|
686
865
|
export {
|
|
687
866
|
collectChangedInstalledPackageIds,
|
|
867
|
+
findRangeIntersectionVersion,
|
|
688
868
|
formatElapsedTime,
|
|
869
|
+
resolveRequiredDirectPeerUpdates,
|
|
689
870
|
reapplyChangedInstalledPackages,
|
|
690
871
|
runAppUpdatePackagesCommand,
|
|
691
872
|
runWithProgress
|