@jskit-ai/jskit-cli 0.2.135 → 0.2.136

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.135",
3
+ "version": "0.2.136",
4
4
  "description": "Bundle and package orchestration CLI for JSKIT apps.",
5
5
  "type": "module",
6
6
  "files": [
@@ -20,9 +20,9 @@
20
20
  "test": "node --test"
21
21
  },
22
22
  "dependencies": {
23
- "@jskit-ai/jskit-catalog": "0.1.130",
24
- "@jskit-ai/kernel": "0.1.120",
25
- "@jskit-ai/shell-web": "0.1.119",
23
+ "@jskit-ai/jskit-catalog": "0.1.131",
24
+ "@jskit-ai/kernel": "0.1.121",
25
+ "@jskit-ai/shell-web": "0.1.120",
26
26
  "@vue/compiler-sfc": "^3.5.29",
27
27
  "ts-morph": "^28.0.0",
28
28
  "yaml": "^2.8.3"
@@ -20,6 +20,7 @@ import {
20
20
  loadMutationWhenConfigContext,
21
21
  resolveAppRelativePathWithinRoot
22
22
  } from "../ioAndMigrations.js";
23
+ import { normalizeRelativePosixPath } from "../localPackageSupport.js";
23
24
  import {
24
25
  interpolateFileMutationRecord,
25
26
  renderTemplateFile,
@@ -39,7 +40,7 @@ async function prepareFileMutations(
39
40
  const existingManagedFilesByPath = new Map();
40
41
  for (const managedFileValue of ensureArray(existingManagedFiles)) {
41
42
  const managedFile = ensureObject(managedFileValue);
42
- const managedPath = String(managedFile.path || "").trim();
43
+ const managedPath = normalizeRelativePosixPath(managedFile.path);
43
44
  if (!managedPath) {
44
45
  continue;
45
46
  }
@@ -205,7 +206,7 @@ async function applyFileMutations(
205
206
  const existingManagedFilesByPath = new Map();
206
207
  for (const managedFileValue of ensureArray(existingManagedFiles)) {
207
208
  const managedFile = ensureObject(managedFileValue);
208
- const managedPath = String(managedFile.path || "").trim();
209
+ const managedPath = normalizeRelativePosixPath(managedFile.path);
209
210
  if (!managedPath) {
210
211
  continue;
211
212
  }
@@ -260,6 +261,7 @@ async function applyFileMutations(
260
261
  managedFiles.push({
261
262
  ...existingManaged,
262
263
  path: relativeTargetPath,
264
+ ownership: mutation.ownership,
263
265
  preserveOnRemove: mutation.preserveOnRemove,
264
266
  reason: mutation.reason || String(existingManaged.reason || ""),
265
267
  category: mutation.category || String(existingManaged.category || ""),
@@ -271,6 +273,7 @@ async function applyFileMutations(
271
273
  if (mutation.ownership === "app" && previous.exists && hashBuffer(previous.buffer) === renderedSourceHash) {
272
274
  managedFiles.push({
273
275
  path: relativeTargetPath,
276
+ ownership: mutation.ownership,
274
277
  hash: renderedSourceHash,
275
278
  hadPrevious: true,
276
279
  previousContentBase64: previous.buffer.toString("base64"),
@@ -289,6 +292,7 @@ async function applyFileMutations(
289
292
 
290
293
  managedFiles.push({
291
294
  path: relativeTargetPath,
295
+ ownership: mutation.ownership,
292
296
  hash: renderedSourceHash,
293
297
  hadPrevious: previous.exists,
294
298
  previousContentBase64: previous.exists ? previous.buffer.toString("base64") : "",
@@ -90,7 +90,8 @@ const APP_COMMAND_DEFINITIONS = Object.freeze({
90
90
  defaults: Object.freeze([
91
91
  "Root runtime, development, optional, and peer dependencies are installed at their exact latest registry versions.",
92
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."
93
+ "Installed packages whose descriptor versions changed are reapplied with their saved options before managed migrations and the composed CI workflow are refreshed.",
94
+ "App-owned files retain local edits through the normal package-update ownership rules."
94
95
  ])
95
96
  }),
96
97
  "sync-ci": Object.freeze({
@@ -5,6 +5,10 @@ import {
5
5
  runExternalCommandAsync,
6
6
  runLocalJskitAsync
7
7
  } from "./shared.js";
8
+ import {
9
+ ensureObject,
10
+ sortStrings
11
+ } from "../../shared/collectionUtils.js";
8
12
 
9
13
  const DEPENDENCY_SECTIONS = Object.freeze([
10
14
  Object.freeze({
@@ -32,9 +36,10 @@ const JSKIT_PACKAGE_PATTERN = /^@jskit-ai\/[a-z0-9._-]+$/iu;
32
36
  const PROGRESS_INTERVAL_MS = 5_000;
33
37
 
34
38
  function collectJskitPackageNames(packageMap = {}) {
35
- return Object.keys(packageMap && typeof packageMap === "object" ? packageMap : {})
36
- .filter((name) => JSKIT_PACKAGE_PATTERN.test(String(name || "")))
37
- .sort((left, right) => left.localeCompare(right));
39
+ return sortStrings(
40
+ Object.keys(packageMap && typeof packageMap === "object" ? packageMap : {})
41
+ .filter((name) => JSKIT_PACKAGE_PATTERN.test(String(name || "")))
42
+ );
38
43
  }
39
44
 
40
45
  function collectManifestJskitPackageNames(packageJson = {}) {
@@ -44,7 +49,7 @@ function collectManifestJskitPackageNames(packageJson = {}) {
44
49
  packageNames.add(packageName);
45
50
  }
46
51
  }
47
- return [...packageNames].sort((left, right) => left.localeCompare(right));
52
+ return sortStrings([...packageNames]);
48
53
  }
49
54
 
50
55
  function resolveExactVersion(packageName = "", rawVersion = "", createCliError) {
@@ -74,6 +79,70 @@ function resolveInstallSpecs(packageNames = [], latestVersions = new Map()) {
74
79
  return packageNames.map((packageName) => `${packageName}@${latestVersions.get(packageName)}`);
75
80
  }
76
81
 
82
+ function collectChangedInstalledPackageIds(lock = {}, latestVersions = new Map()) {
83
+ const installedPackages = ensureObject(lock.installedPackages);
84
+ return sortStrings(
85
+ [...latestVersions.entries()]
86
+ .filter(([packageId, targetVersion]) => {
87
+ if (!Object.prototype.hasOwnProperty.call(installedPackages, packageId)) {
88
+ return false;
89
+ }
90
+ const installedPackage = ensureObject(installedPackages[packageId]);
91
+ return String(installedPackage.version || "").trim() !== targetVersion;
92
+ })
93
+ .map(([packageId]) => packageId)
94
+ );
95
+ }
96
+
97
+ async function reapplyChangedInstalledPackages({
98
+ appRoot,
99
+ createCliError,
100
+ dryRun,
101
+ latestVersions,
102
+ loadLockFile,
103
+ stderr,
104
+ stdout
105
+ }) {
106
+ const { lock } = await loadLockFile(appRoot);
107
+ const packageIds = collectChangedInstalledPackageIds(lock, latestVersions);
108
+ if (packageIds.length < 1) {
109
+ stdout?.write("[jskit:update] managed package state is already current.\n");
110
+ return;
111
+ }
112
+
113
+ stdout?.write(`[jskit:update] managed packages requiring reapply: ${packageIds.join(", ")}.\n`);
114
+ if (dryRun) {
115
+ return;
116
+ }
117
+
118
+ for (const [index, packageId] of packageIds.entries()) {
119
+ const { lock: currentLock } = await loadLockFile(appRoot);
120
+ const currentVersion = String(
121
+ ensureObject(ensureObject(currentLock.installedPackages)[packageId]).version || ""
122
+ ).trim();
123
+ if (currentVersion === latestVersions.get(packageId)) {
124
+ continue;
125
+ }
126
+ stdout?.write(
127
+ `[jskit:update] reapplying managed package ${index + 1}/${packageIds.length}: ${packageId}.\n`
128
+ );
129
+ await runLocalJskitAsync(appRoot, ["update", "package", packageId], {
130
+ stdout,
131
+ stderr,
132
+ createCliError
133
+ });
134
+ }
135
+
136
+ const { lock: updatedLock } = await loadLockFile(appRoot);
137
+ const remainingPackageIds = collectChangedInstalledPackageIds(updatedLock, latestVersions);
138
+ if (remainingPackageIds.length > 0) {
139
+ throw createCliError(
140
+ `Managed package reapply finished with stale lock versions: ${remainingPackageIds.join(", ")}.`
141
+ );
142
+ }
143
+ stdout?.write("[jskit:update] managed package state is current.\n");
144
+ }
145
+
77
146
  function formatElapsedTime(elapsedMilliseconds = 0) {
78
147
  const elapsedSeconds = Math.max(0, Math.floor(Number(elapsedMilliseconds) / 1000));
79
148
  if (elapsedSeconds < 1) {
@@ -429,6 +498,7 @@ async function runAppUpdatePackagesCommand(ctx = {}, { appRoot = "", options = {
429
498
  const {
430
499
  createCliError,
431
500
  loadAppPackageJson,
501
+ loadLockFile,
432
502
  assertAppManagedCiWorkflowUnmodified
433
503
  } = ctx;
434
504
 
@@ -455,7 +525,19 @@ async function runAppUpdatePackagesCommand(ctx = {}, { appRoot = "", options = {
455
525
  stderr,
456
526
  stdout
457
527
  });
458
- if (!updatedRootPackages || dryRun) {
528
+ if (!updatedRootPackages) {
529
+ return;
530
+ }
531
+ await reapplyChangedInstalledPackages({
532
+ appRoot,
533
+ createCliError,
534
+ dryRun,
535
+ latestVersions,
536
+ loadLockFile,
537
+ stderr,
538
+ stdout
539
+ });
540
+ if (dryRun) {
459
541
  return;
460
542
  }
461
543
  stdout?.write("[jskit:update] generating managed migrations for changed packages.\n");
@@ -538,7 +620,9 @@ async function runAppUpdatePackagesCommand(ctx = {}, { appRoot = "", options = {
538
620
  }
539
621
 
540
622
  export {
623
+ collectChangedInstalledPackageIds,
541
624
  formatElapsedTime,
625
+ reapplyChangedInstalledPackages,
542
626
  runAppUpdatePackagesCommand,
543
627
  runWithProgress
544
628
  };
@@ -30,6 +30,7 @@ function createHealthCommands(ctx = {}) {
30
30
  inspectPackageOfferings,
31
31
  fileExists,
32
32
  normalizeRelativePath,
33
+ normalizeRelativePosixPath,
33
34
  path
34
35
  } = ctx;
35
36
 
@@ -1321,7 +1322,12 @@ function createHealthCommands(ctx = {}) {
1321
1322
  }
1322
1323
  }
1323
1324
 
1324
- async function collectMainPackageFeatureLaneWarnings({ appRoot, appLocalRegistry, warnings }) {
1325
+ async function collectMainPackageFeatureLaneWarnings({
1326
+ appRoot,
1327
+ appLocalRegistry,
1328
+ managedAppOwnedFilePaths,
1329
+ warnings
1330
+ }) {
1325
1331
  const mainPackageEntry = [...appLocalRegistry.values()].find((packageEntry) => {
1326
1332
  const packageId = String(packageEntry?.packageId || "").trim();
1327
1333
  const relativeDir = String(packageEntry?.relativeDir || "").trim();
@@ -1343,7 +1349,8 @@ function createHealthCommands(ctx = {}) {
1343
1349
  const extraDomainFiles = relativeServerFiles.filter(
1344
1350
  (relativePath) =>
1345
1351
  !MAIN_SERVER_BASELINE_RELATIVE_PATHS.has(relativePath) &&
1346
- MAIN_SERVER_DOMAIN_FILE_PATTERN.test(relativePath)
1352
+ MAIN_SERVER_DOMAIN_FILE_PATTERN.test(relativePath) &&
1353
+ !managedAppOwnedFilePaths.has(normalizeRelativePath(appRoot, path.join(rootDir, relativePath)))
1347
1354
  );
1348
1355
  if (extraDomainFiles.length > 0) {
1349
1356
  reasons.push(`extra server domain files: ${extraDomainFiles.join(", ")}`);
@@ -1438,7 +1445,13 @@ function createHealthCommands(ctx = {}) {
1438
1445
  }
1439
1446
  }
1440
1447
 
1441
- async function collectFeatureLaneDoctorIssues({ appRoot, appLocalRegistry, issues, warnings }) {
1448
+ async function collectFeatureLaneDoctorIssues({
1449
+ appRoot,
1450
+ appLocalRegistry,
1451
+ managedAppOwnedFilePaths,
1452
+ issues,
1453
+ warnings
1454
+ }) {
1442
1455
  const packageEntries = sortStrings([...appLocalRegistry.keys()])
1443
1456
  .map((packageId) => appLocalRegistry.get(packageId))
1444
1457
  .filter(Boolean);
@@ -1454,6 +1467,7 @@ function createHealthCommands(ctx = {}) {
1454
1467
  await collectMainPackageFeatureLaneWarnings({
1455
1468
  appRoot,
1456
1469
  appLocalRegistry,
1470
+ managedAppOwnedFilePaths,
1457
1471
  warnings
1458
1472
  });
1459
1473
  await collectHandmadeFeatureLaneWarnings({
@@ -2125,6 +2139,7 @@ function createHealthCommands(ctx = {}) {
2125
2139
  const issues = [];
2126
2140
  const warnings = [];
2127
2141
  const installed = ensureObject(lock.installedPackages);
2142
+ const managedAppOwnedFilePaths = new Set();
2128
2143
  await hydratePackageRegistryFromInstalledNodeModules({
2129
2144
  appRoot,
2130
2145
  packageRegistry: combinedPackageRegistry,
@@ -2148,7 +2163,10 @@ function createHealthCommands(ctx = {}) {
2148
2163
  const managed = ensureObject(lockEntry.managed);
2149
2164
  for (const fileChange of ensureArray(managed.files)) {
2150
2165
  const changeRecord = ensureObject(fileChange);
2151
- const relativePath = String(changeRecord.path || "").trim();
2166
+ const relativePath = normalizeRelativePosixPath(changeRecord.path);
2167
+ if (changeRecord.ownership === "app" && relativePath) {
2168
+ managedAppOwnedFilePaths.add(relativePath);
2169
+ }
2152
2170
  const absolutePath = path.join(appRoot, relativePath);
2153
2171
  if (!(await fileExists(absolutePath))) {
2154
2172
  issues.push(`${packageId}: managed file missing: ${relativePath}`);
@@ -2172,6 +2190,7 @@ function createHealthCommands(ctx = {}) {
2172
2190
  await collectFeatureLaneDoctorIssues({
2173
2191
  appRoot,
2174
2192
  appLocalRegistry,
2193
+ managedAppOwnedFilePaths,
2175
2194
  issues,
2176
2195
  warnings
2177
2196
  });