@jskit-ai/jskit-cli 0.2.34 → 0.2.37

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.34",
3
+ "version": "0.2.37",
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.34",
24
- "@jskit-ai/kernel": "0.1.26"
23
+ "@jskit-ai/jskit-catalog": "0.1.37",
24
+ "@jskit-ai/kernel": "0.1.29"
25
25
  },
26
26
  "engines": {
27
27
  "node": "20.x"
@@ -1,4 +1,9 @@
1
- import { readdir } from "node:fs/promises";
1
+ import { spawn } from "node:child_process";
2
+ import {
3
+ mkdir,
4
+ readdir,
5
+ writeFile
6
+ } from "node:fs/promises";
2
7
  import path from "node:path";
3
8
  import { createCliError } from "../shared/cliError.js";
4
9
  import { CLI_PACKAGE_ROOT } from "../shared/pathResolution.js";
@@ -8,8 +13,150 @@ import {
8
13
  } from "./ioAndMigrations.js";
9
14
 
10
15
  const LOCAL_WORKSPACE_PACKAGE_ROOTS = new Map();
16
+ const MATERIALIZED_PACKAGE_ROOTS = new Map();
11
17
  let LOCAL_WORKSPACE_PACKAGE_ID_INDEX = null;
12
18
 
19
+ function isInternalCatalogPackageEntry(packageEntry = {}) {
20
+ return (
21
+ String(packageEntry?.sourceType || "").trim() === "catalog" &&
22
+ String(packageEntry?.packageId || "").trim().startsWith("@jskit-ai/")
23
+ );
24
+ }
25
+
26
+ function encodePackageCacheSegment(value = "") {
27
+ return encodeURIComponent(String(value || "").trim() || "unknown");
28
+ }
29
+
30
+ function buildMaterializedCacheKey(packageEntry = {}) {
31
+ const packageId = String(packageEntry?.packageId || "").trim();
32
+ const version = String(packageEntry?.version || "").trim() || "latest";
33
+ return `${packageId}@${version}`;
34
+ }
35
+
36
+ function buildMaterializedInstallRoot({ appRoot, packageEntry }) {
37
+ const packageId = String(packageEntry?.packageId || "").trim();
38
+ const version = String(packageEntry?.version || "").trim() || "latest";
39
+ return path.resolve(
40
+ String(appRoot || "").trim(),
41
+ ".jskit",
42
+ "cache",
43
+ "package-sources",
44
+ encodePackageCacheSegment(packageId),
45
+ encodePackageCacheSegment(version)
46
+ );
47
+ }
48
+
49
+ async function ensureMaterializedInstallWorkspace(installRoot) {
50
+ await mkdir(installRoot, { recursive: true });
51
+ const packageJsonPath = path.join(installRoot, "package.json");
52
+ if (await fileExists(packageJsonPath)) {
53
+ return;
54
+ }
55
+
56
+ await writeFile(
57
+ packageJsonPath,
58
+ `${JSON.stringify(
59
+ {
60
+ name: "jskit-package-source-cache",
61
+ private: true,
62
+ type: "module"
63
+ },
64
+ null,
65
+ 2
66
+ )}\n`,
67
+ "utf8"
68
+ );
69
+ }
70
+
71
+ async function installCatalogPackageIntoCache({ installRoot, packageEntry }) {
72
+ const packageId = String(packageEntry?.packageId || "").trim();
73
+ const version = String(packageEntry?.version || "").trim();
74
+ const packageSpec = version ? `${packageId}@${version}` : packageId;
75
+ await ensureMaterializedInstallWorkspace(installRoot);
76
+
77
+ await new Promise((resolve, reject) => {
78
+ const child = spawn(
79
+ "npm",
80
+ [
81
+ "install",
82
+ "--no-save",
83
+ "--ignore-scripts",
84
+ "--package-lock=false",
85
+ "--no-audit",
86
+ "--no-fund",
87
+ packageSpec
88
+ ],
89
+ {
90
+ cwd: installRoot,
91
+ stdio: ["ignore", "pipe", "pipe"]
92
+ }
93
+ );
94
+
95
+ let stderr = "";
96
+ child.stderr.on("data", (chunk) => {
97
+ stderr += String(chunk || "");
98
+ });
99
+
100
+ child.on("error", (error) => {
101
+ reject(
102
+ createCliError(
103
+ `Unable to materialize template source for ${packageId}: ${String(error?.message || error || "unknown error")}`
104
+ )
105
+ );
106
+ });
107
+ child.on("exit", (code) => {
108
+ if (code === 0) {
109
+ resolve();
110
+ return;
111
+ }
112
+
113
+ const detail = stderr.trim();
114
+ reject(
115
+ createCliError(
116
+ `Unable to materialize template source for ${packageId}. npm install failed` +
117
+ `${detail ? `: ${detail}` : "."}`
118
+ )
119
+ );
120
+ });
121
+ });
122
+ }
123
+
124
+ async function materializeCatalogPackageRoot({
125
+ packageEntry,
126
+ appRoot,
127
+ installCatalogPackage = installCatalogPackageIntoCache
128
+ } = {}) {
129
+ if (!isInternalCatalogPackageEntry(packageEntry)) {
130
+ return "";
131
+ }
132
+
133
+ const packageId = String(packageEntry?.packageId || "").trim();
134
+ const cacheKey = buildMaterializedCacheKey(packageEntry);
135
+ if (MATERIALIZED_PACKAGE_ROOTS.has(cacheKey)) {
136
+ return MATERIALIZED_PACKAGE_ROOTS.get(cacheKey);
137
+ }
138
+
139
+ const installRoot = buildMaterializedInstallRoot({ appRoot, packageEntry });
140
+ const candidateRoot = path.join(installRoot, "node_modules", ...packageId.split("/"));
141
+ const descriptorPath = path.join(candidateRoot, "package.descriptor.mjs");
142
+
143
+ if (!(await fileExists(descriptorPath))) {
144
+ await installCatalogPackage({
145
+ installRoot,
146
+ packageEntry
147
+ });
148
+ }
149
+
150
+ if (!(await fileExists(descriptorPath))) {
151
+ throw createCliError(
152
+ `Unable to resolve template source for ${packageId} after materialization. Missing package.descriptor.mjs in cache.`
153
+ );
154
+ }
155
+
156
+ MATERIALIZED_PACKAGE_ROOTS.set(cacheKey, candidateRoot);
157
+ return candidateRoot;
158
+ }
159
+
13
160
  async function resolvePackageRootFromNodeModules({ appRoot, packageId }) {
14
161
  const normalizedAppRoot = path.resolve(String(appRoot || "").trim());
15
162
  const normalizedPackageId = String(packageId || "").trim();
@@ -93,7 +240,11 @@ async function resolvePackageRootFromLocalWorkspace({ packageId }) {
93
240
  return packageRoot;
94
241
  }
95
242
 
96
- async function resolvePackageTemplateRoot({ packageEntry, appRoot }) {
243
+ async function resolvePackageTemplateRoot({
244
+ packageEntry,
245
+ appRoot,
246
+ materializeCatalogRoot = materializeCatalogPackageRoot
247
+ } = {}) {
97
248
  const packageRoot = String(packageEntry?.rootDir || "").trim();
98
249
  if (packageRoot) {
99
250
  return packageRoot;
@@ -114,6 +265,14 @@ async function resolvePackageTemplateRoot({ packageEntry, appRoot }) {
114
265
  return localWorkspacePackageRoot;
115
266
  }
116
267
 
268
+ const materializedCatalogPackageRoot = await materializeCatalogRoot({
269
+ packageEntry,
270
+ appRoot
271
+ });
272
+ if (materializedCatalogPackageRoot) {
273
+ return materializedCatalogPackageRoot;
274
+ }
275
+
117
276
  throw createCliError(
118
277
  `Unable to resolve local template source for ${String(packageEntry?.packageId || "unknown package")}. ` +
119
278
  "Install it in node_modules or ensure it exists in the local jskit-ai workspace."
@@ -122,10 +281,12 @@ async function resolvePackageTemplateRoot({ packageEntry, appRoot }) {
122
281
 
123
282
  async function cleanupMaterializedPackageRoots() {
124
283
  LOCAL_WORKSPACE_PACKAGE_ROOTS.clear();
284
+ MATERIALIZED_PACKAGE_ROOTS.clear();
125
285
  LOCAL_WORKSPACE_PACKAGE_ID_INDEX = null;
126
286
  }
127
287
 
128
288
  export {
129
289
  cleanupMaterializedPackageRoots,
290
+ materializeCatalogPackageRoot,
130
291
  resolvePackageTemplateRoot
131
292
  };
@@ -75,6 +75,7 @@ async function runPackageGenerateCommand(
75
75
  mergePackageRegistries,
76
76
  resolvePackageIdFromRegistryOrNodeModules,
77
77
  hydratePackageRegistryFromInstalledNodeModules,
78
+ resolvePackageTemplateRoot,
78
79
  resolvePackageKind,
79
80
  resolveGeneratorPrimarySubcommand,
80
81
  hasGeneratorSubcommandDefinition,
@@ -231,8 +232,20 @@ async function runPackageGenerateCommand(
231
232
  });
232
233
  }
233
234
 
234
- return runGeneratorSubcommand({
235
+ const templateRoot = await resolvePackageTemplateRoot({
235
236
  packageEntry,
237
+ appRoot
238
+ });
239
+ const executablePackageEntry =
240
+ templateRoot === packageEntry.rootDir
241
+ ? packageEntry
242
+ : {
243
+ ...packageEntry,
244
+ rootDir: templateRoot
245
+ };
246
+
247
+ return runGeneratorSubcommand({
248
+ packageEntry: executablePackageEntry,
236
249
  subcommandName,
237
250
  subcommandArgs,
238
251
  inlineOptions: options.inlineOptions,
@@ -16,6 +16,7 @@ function createCommandHandlerDeps(deps = {}) {
16
16
  resolveInstalledPackageIdInput: deps.resolveInstalledPackageIdInput,
17
17
  resolveInstalledNodeModulePackageEntry: deps.resolveInstalledNodeModulePackageEntry,
18
18
  hydratePackageRegistryFromInstalledNodeModules: deps.hydratePackageRegistryFromInstalledNodeModules,
19
+ resolvePackageTemplateRoot: deps.resolvePackageTemplateRoot,
19
20
  validateInlineOptionsForPackage: deps.validateInlineOptionsForPackage,
20
21
  resolveLocalDependencyOrder: deps.resolveLocalDependencyOrder,
21
22
  validatePlannedCapabilityClosure: deps.validatePlannedCapabilityClosure,
@@ -70,6 +70,7 @@ import {
70
70
  validateInlineOptionsForPackage
71
71
  } from "../cliRuntime/packageOptions.js";
72
72
  import {
73
+ resolvePackageTemplateRoot,
73
74
  cleanupMaterializedPackageRoots
74
75
  } from "../cliRuntime/packageTemplateResolution.js";
75
76
  import {
@@ -100,6 +101,7 @@ const commandHandlers = createCommandHandlers(
100
101
  resolveInstalledPackageIdInput,
101
102
  resolveInstalledNodeModulePackageEntry,
102
103
  hydratePackageRegistryFromInstalledNodeModules,
104
+ resolvePackageTemplateRoot,
103
105
  validateInlineOptionsForPackage,
104
106
  resolveLocalDependencyOrder,
105
107
  validatePlannedCapabilityClosure,