@jskit-ai/jskit-cli 0.2.134 → 0.2.135
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.135",
|
|
4
4
|
"description": "Bundle and package orchestration CLI for JSKIT apps.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
"test": "node --test"
|
|
21
21
|
},
|
|
22
22
|
"dependencies": {
|
|
23
|
-
"@jskit-ai/jskit-catalog": "0.1.
|
|
23
|
+
"@jskit-ai/jskit-catalog": "0.1.130",
|
|
24
24
|
"@jskit-ai/kernel": "0.1.120",
|
|
25
25
|
"@jskit-ai/shell-web": "0.1.119",
|
|
26
26
|
"@vue/compiler-sfc": "^3.5.29",
|
|
@@ -75,7 +75,7 @@ const APP_COMMAND_DEFINITIONS = Object.freeze({
|
|
|
75
75
|
}),
|
|
76
76
|
"update-packages": Object.freeze({
|
|
77
77
|
name: "update-packages",
|
|
78
|
-
summary: "Update
|
|
78
|
+
summary: "Update @jskit-ai dependencies across the app and its npm workspaces.",
|
|
79
79
|
usage: "jskit app update-packages [--registry <url>] [--dry-run]",
|
|
80
80
|
options: Object.freeze([
|
|
81
81
|
Object.freeze({
|
|
@@ -88,9 +88,9 @@ const APP_COMMAND_DEFINITIONS = Object.freeze({
|
|
|
88
88
|
})
|
|
89
89
|
]),
|
|
90
90
|
defaults: Object.freeze([
|
|
91
|
-
"
|
|
92
|
-
"
|
|
93
|
-
"
|
|
91
|
+
"Root runtime, development, optional, and peer dependencies are installed at their exact latest registry versions.",
|
|
92
|
+
"JSKIT ranges in npm workspace manifests and package descriptors are aligned with the latest major release, then workspace resolutions are refreshed in the lockfile.",
|
|
93
|
+
"The command reports elapsed progress and refreshes managed migrations and the composed CI workflow unless --dry-run is used."
|
|
94
94
|
])
|
|
95
95
|
}),
|
|
96
96
|
"sync-ci": Object.freeze({
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { spawnSync } from "node:child_process";
|
|
1
|
+
import { spawn, spawnSync } from "node:child_process";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { access, mkdir, readFile, readdir, rm, symlink } from "node:fs/promises";
|
|
4
4
|
|
|
@@ -80,6 +80,94 @@ function runExternalCommand(
|
|
|
80
80
|
});
|
|
81
81
|
}
|
|
82
82
|
|
|
83
|
+
function runExternalCommandAsync(
|
|
84
|
+
command,
|
|
85
|
+
args = [],
|
|
86
|
+
{
|
|
87
|
+
cwd = "",
|
|
88
|
+
env = {},
|
|
89
|
+
stdout,
|
|
90
|
+
stderr,
|
|
91
|
+
quiet = false,
|
|
92
|
+
createCliError
|
|
93
|
+
} = {}
|
|
94
|
+
) {
|
|
95
|
+
return new Promise((resolve, reject) => {
|
|
96
|
+
let capturedStdout = "";
|
|
97
|
+
let capturedStderr = "";
|
|
98
|
+
let settled = false;
|
|
99
|
+
let child;
|
|
100
|
+
|
|
101
|
+
const finish = (result) => {
|
|
102
|
+
if (settled) {
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
settled = true;
|
|
106
|
+
try {
|
|
107
|
+
resolve(ensureCommandSucceeded(result, command, {
|
|
108
|
+
createCliError,
|
|
109
|
+
cwd,
|
|
110
|
+
stdout,
|
|
111
|
+
stderr,
|
|
112
|
+
quiet: true
|
|
113
|
+
}));
|
|
114
|
+
} catch (error) {
|
|
115
|
+
reject(error);
|
|
116
|
+
}
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
try {
|
|
120
|
+
child = spawn(command, Array.isArray(args) ? args : [], {
|
|
121
|
+
cwd: cwd || process.cwd(),
|
|
122
|
+
env: {
|
|
123
|
+
...process.env,
|
|
124
|
+
...env
|
|
125
|
+
},
|
|
126
|
+
shell: process.platform === "win32"
|
|
127
|
+
});
|
|
128
|
+
} catch (error) {
|
|
129
|
+
finish({
|
|
130
|
+
error,
|
|
131
|
+
status: null,
|
|
132
|
+
stdout: capturedStdout,
|
|
133
|
+
stderr: capturedStderr
|
|
134
|
+
});
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
child.stdout?.setEncoding("utf8");
|
|
139
|
+
child.stderr?.setEncoding("utf8");
|
|
140
|
+
child.stdout?.on("data", (chunk) => {
|
|
141
|
+
capturedStdout += chunk;
|
|
142
|
+
if (!quiet) {
|
|
143
|
+
stdout?.write(chunk);
|
|
144
|
+
}
|
|
145
|
+
});
|
|
146
|
+
child.stderr?.on("data", (chunk) => {
|
|
147
|
+
capturedStderr += chunk;
|
|
148
|
+
if (!quiet) {
|
|
149
|
+
stderr?.write(chunk);
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
child.once("error", (error) => {
|
|
153
|
+
finish({
|
|
154
|
+
error,
|
|
155
|
+
status: null,
|
|
156
|
+
stdout: capturedStdout,
|
|
157
|
+
stderr: capturedStderr
|
|
158
|
+
});
|
|
159
|
+
});
|
|
160
|
+
child.once("close", (status, signal) => {
|
|
161
|
+
finish({
|
|
162
|
+
error: signal ? new Error(`${command} terminated by signal ${signal}.`) : null,
|
|
163
|
+
status,
|
|
164
|
+
stdout: capturedStdout,
|
|
165
|
+
stderr: capturedStderr
|
|
166
|
+
});
|
|
167
|
+
});
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
|
|
83
171
|
function runExternalShellCommand(
|
|
84
172
|
commandText,
|
|
85
173
|
{
|
|
@@ -155,6 +243,32 @@ async function runLocalJskit(
|
|
|
155
243
|
});
|
|
156
244
|
}
|
|
157
245
|
|
|
246
|
+
async function runLocalJskitAsync(
|
|
247
|
+
appRoot,
|
|
248
|
+
args = [],
|
|
249
|
+
{
|
|
250
|
+
stdout,
|
|
251
|
+
stderr,
|
|
252
|
+
createCliError,
|
|
253
|
+
quiet = false
|
|
254
|
+
} = {}
|
|
255
|
+
) {
|
|
256
|
+
const localJskitBin = resolveLocalJskitBin(appRoot);
|
|
257
|
+
if (!(await fileExists(localJskitBin))) {
|
|
258
|
+
throw createCliError(`Local jskit binary not found at ${path.relative(appRoot, localJskitBin)}. Run npm install first.`, {
|
|
259
|
+
exitCode: 1
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
return runExternalCommandAsync(localJskitBin, args, {
|
|
264
|
+
cwd: appRoot,
|
|
265
|
+
stdout,
|
|
266
|
+
stderr,
|
|
267
|
+
quiet,
|
|
268
|
+
createCliError
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
|
|
158
272
|
async function resolveLocalRepoRoot({ appRoot = "", explicitRepoRoot = "" } = {}) {
|
|
159
273
|
const explicit = normalizeText(explicitRepoRoot);
|
|
160
274
|
if (explicit) {
|
|
@@ -295,10 +409,12 @@ export {
|
|
|
295
409
|
normalizeText,
|
|
296
410
|
isTruthyFlag,
|
|
297
411
|
runExternalCommand,
|
|
412
|
+
runExternalCommandAsync,
|
|
298
413
|
runExternalShellCommand,
|
|
299
414
|
formatUtcReleaseTimestamp,
|
|
300
415
|
resolveLocalJskitBin,
|
|
301
416
|
runLocalJskit,
|
|
417
|
+
runLocalJskitAsync,
|
|
302
418
|
resolveLocalRepoRoot,
|
|
303
419
|
discoverLocalPackageMap,
|
|
304
420
|
linkPackageBinEntries,
|
|
@@ -1,20 +1,65 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { readFile, writeFile } from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
runExternalCommandAsync,
|
|
6
|
+
runLocalJskitAsync
|
|
7
|
+
} from "./shared.js";
|
|
8
|
+
|
|
9
|
+
const DEPENDENCY_SECTIONS = Object.freeze([
|
|
10
|
+
Object.freeze({
|
|
11
|
+
installArgs: Object.freeze(["--save-exact"]),
|
|
12
|
+
label: "runtime",
|
|
13
|
+
name: "dependencies"
|
|
14
|
+
}),
|
|
15
|
+
Object.freeze({
|
|
16
|
+
installArgs: Object.freeze(["--save-dev", "--save-exact"]),
|
|
17
|
+
label: "development",
|
|
18
|
+
name: "devDependencies"
|
|
19
|
+
}),
|
|
20
|
+
Object.freeze({
|
|
21
|
+
installArgs: Object.freeze(["--save-optional", "--save-exact"]),
|
|
22
|
+
label: "optional",
|
|
23
|
+
name: "optionalDependencies"
|
|
24
|
+
}),
|
|
25
|
+
Object.freeze({
|
|
26
|
+
installArgs: Object.freeze(["--save-peer", "--save-exact"]),
|
|
27
|
+
label: "peer",
|
|
28
|
+
name: "peerDependencies"
|
|
29
|
+
})
|
|
30
|
+
]);
|
|
31
|
+
const JSKIT_PACKAGE_PATTERN = /^@jskit-ai\/[a-z0-9._-]+$/iu;
|
|
32
|
+
const PROGRESS_INTERVAL_MS = 5_000;
|
|
2
33
|
|
|
3
34
|
function collectJskitPackageNames(packageMap = {}) {
|
|
4
35
|
return Object.keys(packageMap && typeof packageMap === "object" ? packageMap : {})
|
|
5
|
-
.filter((name) => String(name || "")
|
|
36
|
+
.filter((name) => JSKIT_PACKAGE_PATTERN.test(String(name || "")))
|
|
6
37
|
.sort((left, right) => left.localeCompare(right));
|
|
7
38
|
}
|
|
8
39
|
|
|
9
|
-
function
|
|
40
|
+
function collectManifestJskitPackageNames(packageJson = {}) {
|
|
41
|
+
const packageNames = new Set();
|
|
42
|
+
for (const section of DEPENDENCY_SECTIONS) {
|
|
43
|
+
for (const packageName of collectJskitPackageNames(packageJson?.[section.name])) {
|
|
44
|
+
packageNames.add(packageName);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return [...packageNames].sort((left, right) => left.localeCompare(right));
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function resolveExactVersion(packageName = "", rawVersion = "", createCliError) {
|
|
10
51
|
const normalizedVersion = String(rawVersion || "").trim();
|
|
11
|
-
|
|
12
|
-
if (!match) {
|
|
52
|
+
if (!/^\d+\.\d+\.\d+(?:[.+-][0-9A-Za-z.-]+)?$/u.test(normalizedVersion)) {
|
|
13
53
|
throw createCliError(`Invalid latest version for ${packageName}: ${normalizedVersion || "<empty>"}.`, {
|
|
14
54
|
exitCode: 1
|
|
15
55
|
});
|
|
16
56
|
}
|
|
17
|
-
return
|
|
57
|
+
return normalizedVersion;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function resolveMajorRange(packageName = "", version = "", createCliError) {
|
|
61
|
+
const exactVersion = resolveExactVersion(packageName, version, createCliError);
|
|
62
|
+
return `${exactVersion.slice(0, exactVersion.indexOf("."))}.x`;
|
|
18
63
|
}
|
|
19
64
|
|
|
20
65
|
function resolveRegistryArgs(registryUrl = "") {
|
|
@@ -25,8 +70,359 @@ function resolveRegistryArgs(registryUrl = "") {
|
|
|
25
70
|
return ["--registry", normalizedRegistryUrl];
|
|
26
71
|
}
|
|
27
72
|
|
|
28
|
-
function resolveInstallSpecs(packageNames = [],
|
|
29
|
-
return packageNames.map((packageName) => `${packageName}@${
|
|
73
|
+
function resolveInstallSpecs(packageNames = [], latestVersions = new Map()) {
|
|
74
|
+
return packageNames.map((packageName) => `${packageName}@${latestVersions.get(packageName)}`);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function formatElapsedTime(elapsedMilliseconds = 0) {
|
|
78
|
+
const elapsedSeconds = Math.max(0, Math.floor(Number(elapsedMilliseconds) / 1000));
|
|
79
|
+
if (elapsedSeconds < 1) {
|
|
80
|
+
return "under 1s";
|
|
81
|
+
}
|
|
82
|
+
if (elapsedSeconds < 60) {
|
|
83
|
+
return `${elapsedSeconds}s`;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const minutes = Math.floor(elapsedSeconds / 60);
|
|
87
|
+
const seconds = elapsedSeconds % 60;
|
|
88
|
+
return seconds > 0 ? `${minutes}m ${seconds}s` : `${minutes}m`;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
async function runWithProgress(task, {
|
|
92
|
+
activity,
|
|
93
|
+
progressIntervalMs = PROGRESS_INTERVAL_MS,
|
|
94
|
+
stdout,
|
|
95
|
+
step
|
|
96
|
+
} = {}) {
|
|
97
|
+
const normalizedActivity = String(activity || "running update").trim();
|
|
98
|
+
const normalizedStep = String(step || "Update").trim();
|
|
99
|
+
const startedAt = Date.now();
|
|
100
|
+
stdout?.write(`[jskit:update] ${normalizedStep}: ${normalizedActivity}.\n`);
|
|
101
|
+
|
|
102
|
+
const progressTimer = setInterval(() => {
|
|
103
|
+
stdout?.write(
|
|
104
|
+
`[jskit:update] ${normalizedStep} is still running (${formatElapsedTime(Date.now() - startedAt)} elapsed): ${normalizedActivity}.\n`
|
|
105
|
+
);
|
|
106
|
+
}, progressIntervalMs);
|
|
107
|
+
progressTimer.unref();
|
|
108
|
+
|
|
109
|
+
try {
|
|
110
|
+
const result = await task();
|
|
111
|
+
stdout?.write(
|
|
112
|
+
`[jskit:update] ${normalizedStep} complete in ${formatElapsedTime(Date.now() - startedAt)}.\n`
|
|
113
|
+
);
|
|
114
|
+
return result;
|
|
115
|
+
} finally {
|
|
116
|
+
clearInterval(progressTimer);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function hasNpmWorkspaces(packageJson = {}) {
|
|
121
|
+
const workspaces = Array.isArray(packageJson?.workspaces)
|
|
122
|
+
? packageJson.workspaces
|
|
123
|
+
: packageJson?.workspaces?.packages;
|
|
124
|
+
return Array.isArray(workspaces) && workspaces.some((workspace) => String(workspace || "").trim());
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
async function readJson(filePath) {
|
|
128
|
+
return JSON.parse(await readFile(filePath, "utf8"));
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
async function readOptionalFile(filePath) {
|
|
132
|
+
try {
|
|
133
|
+
return await readFile(filePath, "utf8");
|
|
134
|
+
} catch (error) {
|
|
135
|
+
if (error?.code === "ENOENT") {
|
|
136
|
+
return "";
|
|
137
|
+
}
|
|
138
|
+
throw error;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
async function resolveWorkspaceDirectories({
|
|
143
|
+
appRoot,
|
|
144
|
+
createCliError,
|
|
145
|
+
packageJson,
|
|
146
|
+
stderr,
|
|
147
|
+
stdout
|
|
148
|
+
}) {
|
|
149
|
+
if (!hasNpmWorkspaces(packageJson)) {
|
|
150
|
+
return [];
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const result = await runExternalCommandAsync("npm", ["query", ".workspace", "--json"], {
|
|
154
|
+
cwd: appRoot,
|
|
155
|
+
stdout,
|
|
156
|
+
stderr,
|
|
157
|
+
quiet: true,
|
|
158
|
+
createCliError
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
let entries;
|
|
162
|
+
try {
|
|
163
|
+
entries = JSON.parse(String(result.stdout || "[]"));
|
|
164
|
+
} catch (error) {
|
|
165
|
+
throw createCliError(`npm returned invalid workspace metadata: ${error instanceof Error ? error.message : String(error)}.`, {
|
|
166
|
+
exitCode: 1
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
if (!Array.isArray(entries)) {
|
|
170
|
+
throw createCliError("npm returned invalid workspace metadata: expected a JSON array.", {
|
|
171
|
+
exitCode: 1
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
const normalizedAppRoot = path.resolve(appRoot);
|
|
176
|
+
const workspaceDirectories = new Set();
|
|
177
|
+
for (const entry of entries) {
|
|
178
|
+
const location = String(entry?.location || "").trim();
|
|
179
|
+
if (!location) {
|
|
180
|
+
continue;
|
|
181
|
+
}
|
|
182
|
+
const workspaceDirectory = path.resolve(normalizedAppRoot, location);
|
|
183
|
+
if (
|
|
184
|
+
workspaceDirectory === normalizedAppRoot ||
|
|
185
|
+
!workspaceDirectory.startsWith(`${normalizedAppRoot}${path.sep}`)
|
|
186
|
+
) {
|
|
187
|
+
throw createCliError(`npm returned a workspace outside the app root: ${location}.`, {
|
|
188
|
+
exitCode: 1
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
workspaceDirectories.add(workspaceDirectory);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
return [...workspaceDirectories].sort((left, right) => left.localeCompare(right));
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
function descriptorJskitPackageNames(source = "") {
|
|
198
|
+
const packageNames = new Set();
|
|
199
|
+
const patterns = [
|
|
200
|
+
/(["'])(@jskit-ai\/[a-z0-9._-]+)\1\s*:\s*(["'])[^"']+\3/giu,
|
|
201
|
+
/(["'])(@jskit-ai\/[a-z0-9._-]+)\1\s*:\s*\{[^{}]*?(?:["'](?:version|value)["']|version|value)\s*:\s*(["'])[^"']+\3/giu
|
|
202
|
+
];
|
|
203
|
+
for (const pattern of patterns) {
|
|
204
|
+
for (const match of String(source || "").matchAll(pattern)) {
|
|
205
|
+
packageNames.add(match[2]);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
return [...packageNames].sort((left, right) => left.localeCompare(right));
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
async function loadWorkspacePackages(workspaceDirectories = []) {
|
|
212
|
+
const workspacePackages = [];
|
|
213
|
+
for (const directory of workspaceDirectories) {
|
|
214
|
+
const packageJsonPath = path.join(directory, "package.json");
|
|
215
|
+
const descriptorPath = path.join(directory, "package.descriptor.mjs");
|
|
216
|
+
workspacePackages.push({
|
|
217
|
+
descriptorPath,
|
|
218
|
+
descriptorSource: await readOptionalFile(descriptorPath),
|
|
219
|
+
directory,
|
|
220
|
+
packageJson: await readJson(packageJsonPath),
|
|
221
|
+
packageJsonPath
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
return workspacePackages;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
async function resolveLatestVersions(packageNames = [], latestVersions = new Map(), {
|
|
228
|
+
appRoot,
|
|
229
|
+
createCliError,
|
|
230
|
+
registryArgs,
|
|
231
|
+
stderr,
|
|
232
|
+
stdout
|
|
233
|
+
}) {
|
|
234
|
+
const unresolvedPackageNames = packageNames
|
|
235
|
+
.filter((packageName) => !latestVersions.has(packageName))
|
|
236
|
+
.sort((left, right) => left.localeCompare(right));
|
|
237
|
+
|
|
238
|
+
for (const [index, packageName] of unresolvedPackageNames.entries()) {
|
|
239
|
+
stdout?.write(
|
|
240
|
+
`[jskit:update] resolving latest package ${index + 1}/${unresolvedPackageNames.length}: ${packageName}.\n`
|
|
241
|
+
);
|
|
242
|
+
const result = await runExternalCommandAsync(
|
|
243
|
+
"npm",
|
|
244
|
+
["view", ...registryArgs, packageName, "version"],
|
|
245
|
+
{
|
|
246
|
+
cwd: appRoot,
|
|
247
|
+
stdout,
|
|
248
|
+
stderr,
|
|
249
|
+
quiet: true,
|
|
250
|
+
createCliError
|
|
251
|
+
}
|
|
252
|
+
);
|
|
253
|
+
const version = resolveExactVersion(packageName, result.stdout, createCliError);
|
|
254
|
+
latestVersions.set(packageName, version);
|
|
255
|
+
stdout?.write(`[jskit:update] resolved ${packageName}@${version}.\n`);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
return latestVersions;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
function updateWorkspaceManifest(packageJson = {}, latestVersions = new Map(), createCliError) {
|
|
262
|
+
const updates = [];
|
|
263
|
+
for (const section of DEPENDENCY_SECTIONS) {
|
|
264
|
+
const packageMap = packageJson?.[section.name];
|
|
265
|
+
for (const packageName of collectJskitPackageNames(packageMap)) {
|
|
266
|
+
const targetRange = resolveMajorRange(packageName, latestVersions.get(packageName), createCliError);
|
|
267
|
+
if (packageMap[packageName] === targetRange) {
|
|
268
|
+
continue;
|
|
269
|
+
}
|
|
270
|
+
packageMap[packageName] = targetRange;
|
|
271
|
+
updates.push(`${packageName}@${targetRange}`);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
return updates;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
function updateWorkspaceDescriptor(source = "", latestVersions = new Map(), createCliError) {
|
|
278
|
+
const updates = [];
|
|
279
|
+
const replaceVersion = (match, keyQuote, packageName, separator, valueQuote, currentVersion) => {
|
|
280
|
+
const targetRange = resolveMajorRange(packageName, latestVersions.get(packageName), createCliError);
|
|
281
|
+
if (currentVersion === targetRange) {
|
|
282
|
+
return match;
|
|
283
|
+
}
|
|
284
|
+
updates.push(`${packageName}@${targetRange}`);
|
|
285
|
+
return `${keyQuote}${packageName}${keyQuote}${separator}${valueQuote}${targetRange}${valueQuote}`;
|
|
286
|
+
};
|
|
287
|
+
let nextSource = String(source || "").replace(
|
|
288
|
+
/(["'])(@jskit-ai\/[a-z0-9._-]+)\1(\s*:\s*)(["'])([^"']+)\4/giu,
|
|
289
|
+
replaceVersion
|
|
290
|
+
);
|
|
291
|
+
nextSource = nextSource.replace(
|
|
292
|
+
/(["'])(@jskit-ai\/[a-z0-9._-]+)\1(\s*:\s*\{[^{}]*?(?:["'](?:version|value)["']|version|value)\s*:\s*)(["'])([^"']+)\4/giu,
|
|
293
|
+
replaceVersion
|
|
294
|
+
);
|
|
295
|
+
return {
|
|
296
|
+
nextSource,
|
|
297
|
+
updates
|
|
298
|
+
};
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
async function synchronizeWorkspacePackageSpecs({
|
|
302
|
+
appRoot,
|
|
303
|
+
createCliError,
|
|
304
|
+
dryRun,
|
|
305
|
+
latestVersions,
|
|
306
|
+
packageJson,
|
|
307
|
+
registryArgs,
|
|
308
|
+
stderr,
|
|
309
|
+
stdout
|
|
310
|
+
}) {
|
|
311
|
+
const workspaceDirectories = await resolveWorkspaceDirectories({
|
|
312
|
+
appRoot,
|
|
313
|
+
createCliError,
|
|
314
|
+
packageJson,
|
|
315
|
+
stderr,
|
|
316
|
+
stdout
|
|
317
|
+
});
|
|
318
|
+
const workspacePackages = await loadWorkspacePackages(workspaceDirectories);
|
|
319
|
+
const workspaceJskitPackages = new Set();
|
|
320
|
+
for (const workspacePackage of workspacePackages) {
|
|
321
|
+
for (const packageName of collectManifestJskitPackageNames(workspacePackage.packageJson)) {
|
|
322
|
+
workspaceJskitPackages.add(packageName);
|
|
323
|
+
}
|
|
324
|
+
for (const packageName of descriptorJskitPackageNames(workspacePackage.descriptorSource)) {
|
|
325
|
+
workspaceJskitPackages.add(packageName);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
const packageNames = [...workspaceJskitPackages].sort((left, right) => left.localeCompare(right));
|
|
330
|
+
await resolveLatestVersions(packageNames, latestVersions, {
|
|
331
|
+
appRoot,
|
|
332
|
+
createCliError,
|
|
333
|
+
registryArgs,
|
|
334
|
+
stderr,
|
|
335
|
+
stdout
|
|
336
|
+
});
|
|
337
|
+
|
|
338
|
+
const changedFiles = [];
|
|
339
|
+
for (const workspacePackage of workspacePackages) {
|
|
340
|
+
const manifestUpdates = updateWorkspaceManifest(
|
|
341
|
+
workspacePackage.packageJson,
|
|
342
|
+
latestVersions,
|
|
343
|
+
createCliError
|
|
344
|
+
);
|
|
345
|
+
if (manifestUpdates.length > 0) {
|
|
346
|
+
const relativePath = path.relative(appRoot, workspacePackage.packageJsonPath).replaceAll(path.sep, "/");
|
|
347
|
+
changedFiles.push(relativePath);
|
|
348
|
+
stdout?.write(`[jskit:update] workspace manifest ${relativePath} -> ${manifestUpdates.join(", ")}\n`);
|
|
349
|
+
if (!dryRun) {
|
|
350
|
+
await writeFile(
|
|
351
|
+
workspacePackage.packageJsonPath,
|
|
352
|
+
`${JSON.stringify(workspacePackage.packageJson, null, 2)}\n`,
|
|
353
|
+
"utf8"
|
|
354
|
+
);
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
if (!workspacePackage.descriptorSource) {
|
|
359
|
+
continue;
|
|
360
|
+
}
|
|
361
|
+
const descriptorResult = updateWorkspaceDescriptor(
|
|
362
|
+
workspacePackage.descriptorSource,
|
|
363
|
+
latestVersions,
|
|
364
|
+
createCliError
|
|
365
|
+
);
|
|
366
|
+
if (descriptorResult.updates.length < 1) {
|
|
367
|
+
continue;
|
|
368
|
+
}
|
|
369
|
+
const relativePath = path.relative(appRoot, workspacePackage.descriptorPath).replaceAll(path.sep, "/");
|
|
370
|
+
changedFiles.push(relativePath);
|
|
371
|
+
stdout?.write(`[jskit:update] workspace descriptor ${relativePath} -> ${descriptorResult.updates.join(", ")}\n`);
|
|
372
|
+
if (!dryRun) {
|
|
373
|
+
await writeFile(workspacePackage.descriptorPath, descriptorResult.nextSource, "utf8");
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
return {
|
|
378
|
+
changedFiles,
|
|
379
|
+
packageNames
|
|
380
|
+
};
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
async function updateRootPackages({
|
|
384
|
+
appRoot,
|
|
385
|
+
createCliError,
|
|
386
|
+
dryRun,
|
|
387
|
+
latestVersions,
|
|
388
|
+
packageJson,
|
|
389
|
+
registryArgs,
|
|
390
|
+
stderr,
|
|
391
|
+
stdout
|
|
392
|
+
}) {
|
|
393
|
+
const rootPackageNames = collectManifestJskitPackageNames(packageJson);
|
|
394
|
+
if (rootPackageNames.length < 1) {
|
|
395
|
+
stdout?.write("[jskit:update] no root @jskit-ai packages found.\n");
|
|
396
|
+
return false;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
await resolveLatestVersions(rootPackageNames, latestVersions, {
|
|
400
|
+
appRoot,
|
|
401
|
+
createCliError,
|
|
402
|
+
registryArgs,
|
|
403
|
+
stderr,
|
|
404
|
+
stdout
|
|
405
|
+
});
|
|
406
|
+
const dryRunArgs = dryRun ? ["--dry-run"] : [];
|
|
407
|
+
for (const section of DEPENDENCY_SECTIONS) {
|
|
408
|
+
const packageNames = collectJskitPackageNames(packageJson?.[section.name]);
|
|
409
|
+
if (packageNames.length < 1) {
|
|
410
|
+
continue;
|
|
411
|
+
}
|
|
412
|
+
const installSpecs = resolveInstallSpecs(packageNames, latestVersions);
|
|
413
|
+
stdout?.write(`[jskit:update] updating ${section.label} packages: ${installSpecs.join(" ")}\n`);
|
|
414
|
+
await runExternalCommandAsync(
|
|
415
|
+
"npm",
|
|
416
|
+
["install", ...section.installArgs, ...registryArgs, ...dryRunArgs, ...installSpecs],
|
|
417
|
+
{
|
|
418
|
+
cwd: appRoot,
|
|
419
|
+
stdout,
|
|
420
|
+
stderr,
|
|
421
|
+
createCliError
|
|
422
|
+
}
|
|
423
|
+
);
|
|
424
|
+
}
|
|
425
|
+
return true;
|
|
30
426
|
}
|
|
31
427
|
|
|
32
428
|
async function runAppUpdatePackagesCommand(ctx = {}, { appRoot = "", options = {}, stdout, stderr }) {
|
|
@@ -38,73 +434,111 @@ async function runAppUpdatePackagesCommand(ctx = {}, { appRoot = "", options = {
|
|
|
38
434
|
|
|
39
435
|
await assertAppManagedCiWorkflowUnmodified({ appRoot });
|
|
40
436
|
const { packageJson } = await loadAppPackageJson(appRoot);
|
|
41
|
-
const runtimePackages = collectJskitPackageNames(packageJson?.dependencies);
|
|
42
|
-
const devPackages = collectJskitPackageNames(packageJson?.devDependencies);
|
|
43
437
|
const registryUrl = String(options?.inlineOptions?.registry || "").trim();
|
|
44
438
|
const registryArgs = resolveRegistryArgs(registryUrl);
|
|
45
439
|
const dryRun = options?.dryRun === true;
|
|
46
|
-
|
|
47
|
-
if (runtimePackages.length < 1 && devPackages.length < 1) {
|
|
48
|
-
stdout.write("[jskit:update] no @jskit-ai packages found in dependencies.\n");
|
|
49
|
-
return 0;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
const resolveMajorRange = (packageName) => {
|
|
53
|
-
const result = runExternalCommand("npm", ["view", ...registryArgs, packageName, "version"], {
|
|
54
|
-
cwd: appRoot,
|
|
55
|
-
stdout,
|
|
56
|
-
stderr,
|
|
57
|
-
quiet: true,
|
|
58
|
-
createCliError
|
|
59
|
-
});
|
|
60
|
-
return resolveMajorRangeFromVersion(packageName, result.stdout, createCliError);
|
|
61
|
-
};
|
|
62
|
-
|
|
63
|
-
const runtimeSpecs = resolveInstallSpecs(runtimePackages, resolveMajorRange);
|
|
64
|
-
const devSpecs = resolveInstallSpecs(devPackages, resolveMajorRange);
|
|
65
|
-
const dryRunArgs = dryRun ? ["--dry-run"] : [];
|
|
440
|
+
const latestVersions = new Map();
|
|
66
441
|
|
|
67
442
|
if (dryRun) {
|
|
68
|
-
stdout
|
|
443
|
+
stdout?.write("[jskit:update] dry-run mode enabled.\n");
|
|
69
444
|
}
|
|
70
445
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
446
|
+
await runWithProgress(
|
|
447
|
+
async () => {
|
|
448
|
+
const updatedRootPackages = await updateRootPackages({
|
|
449
|
+
appRoot,
|
|
450
|
+
createCliError,
|
|
451
|
+
dryRun,
|
|
452
|
+
latestVersions,
|
|
453
|
+
packageJson,
|
|
454
|
+
registryArgs,
|
|
455
|
+
stderr,
|
|
456
|
+
stdout
|
|
457
|
+
});
|
|
458
|
+
if (!updatedRootPackages || dryRun) {
|
|
459
|
+
return;
|
|
460
|
+
}
|
|
461
|
+
stdout?.write("[jskit:update] generating managed migrations for changed packages.\n");
|
|
462
|
+
await runLocalJskitAsync(appRoot, ["migrations", "changed"], {
|
|
463
|
+
stdout,
|
|
464
|
+
stderr,
|
|
465
|
+
createCliError
|
|
466
|
+
});
|
|
467
|
+
stdout?.write("[jskit:update] synchronizing the managed CI workflow.\n");
|
|
468
|
+
await runLocalJskitAsync(appRoot, ["app", "sync-ci"], {
|
|
469
|
+
stdout,
|
|
470
|
+
stderr,
|
|
471
|
+
createCliError
|
|
472
|
+
});
|
|
473
|
+
},
|
|
474
|
+
{
|
|
475
|
+
activity: dryRun
|
|
476
|
+
? "checking root JSKIT package updates"
|
|
477
|
+
: "updating root JSKIT packages and managed artifacts",
|
|
75
478
|
stdout,
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
}
|
|
479
|
+
step: "Step 1/3"
|
|
480
|
+
}
|
|
481
|
+
);
|
|
80
482
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
483
|
+
let workspaceResult = {
|
|
484
|
+
changedFiles: [],
|
|
485
|
+
packageNames: []
|
|
486
|
+
};
|
|
487
|
+
await runWithProgress(
|
|
488
|
+
async () => {
|
|
489
|
+
workspaceResult = await synchronizeWorkspacePackageSpecs({
|
|
490
|
+
appRoot,
|
|
491
|
+
createCliError,
|
|
492
|
+
dryRun,
|
|
493
|
+
latestVersions,
|
|
494
|
+
packageJson,
|
|
495
|
+
registryArgs,
|
|
496
|
+
stderr,
|
|
497
|
+
stdout
|
|
498
|
+
});
|
|
499
|
+
},
|
|
500
|
+
{
|
|
501
|
+
activity: "aligning JSKIT ranges in workspace manifests and descriptors",
|
|
85
502
|
stdout,
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
503
|
+
step: "Step 2/3"
|
|
504
|
+
}
|
|
505
|
+
);
|
|
506
|
+
stdout?.write(
|
|
507
|
+
`[jskit:update] Step 2/3 summary: ${workspaceResult.changedFiles.length} workspace files ${dryRun ? "would change" : "changed"}.\n`
|
|
508
|
+
);
|
|
90
509
|
|
|
91
|
-
if (
|
|
92
|
-
stdout
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
510
|
+
if (dryRun) {
|
|
511
|
+
stdout?.write(
|
|
512
|
+
`[jskit:update] Step 3/3 skipped in dry-run mode: ${workspaceResult.packageNames.length} workspace JSKIT packages would be refreshed.\n`
|
|
513
|
+
);
|
|
514
|
+
} else if (workspaceResult.packageNames.length > 0) {
|
|
515
|
+
await runWithProgress(
|
|
516
|
+
() => runExternalCommandAsync(
|
|
517
|
+
"npm",
|
|
518
|
+
["update", ...registryArgs, "--workspaces", ...workspaceResult.packageNames],
|
|
519
|
+
{
|
|
520
|
+
cwd: appRoot,
|
|
521
|
+
stdout,
|
|
522
|
+
stderr,
|
|
523
|
+
createCliError
|
|
524
|
+
}
|
|
525
|
+
),
|
|
526
|
+
{
|
|
527
|
+
activity: `refreshing ${workspaceResult.packageNames.length} workspace JSKIT packages and updating the lockfile`,
|
|
528
|
+
stdout,
|
|
529
|
+
step: "Step 3/3"
|
|
530
|
+
}
|
|
531
|
+
);
|
|
532
|
+
} else {
|
|
533
|
+
stdout?.write("[jskit:update] Step 3/3 skipped: no workspace JSKIT packages were found.\n");
|
|
104
534
|
}
|
|
105
535
|
|
|
106
|
-
stdout
|
|
536
|
+
stdout?.write("[jskit:update] done.\n");
|
|
107
537
|
return 0;
|
|
108
538
|
}
|
|
109
539
|
|
|
110
|
-
export {
|
|
540
|
+
export {
|
|
541
|
+
formatElapsedTime,
|
|
542
|
+
runAppUpdatePackagesCommand,
|
|
543
|
+
runWithProgress
|
|
544
|
+
};
|