@jskit-ai/jskit-cli 0.2.26 → 0.2.28

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.
Files changed (59) hide show
  1. package/package.json +3 -2
  2. package/src/server/cliRuntime/appState.js +226 -0
  3. package/src/server/cliRuntime/capabilitySupport.js +194 -0
  4. package/src/server/cliRuntime/descriptorValidation.js +150 -0
  5. package/src/server/cliRuntime/ioAndMigrations.js +381 -0
  6. package/src/server/cliRuntime/localPackageSupport.js +390 -0
  7. package/src/server/cliRuntime/mutationApplication.js +9 -0
  8. package/src/server/cliRuntime/mutationWhen.js +285 -0
  9. package/src/server/cliRuntime/mutations/fileMutations.js +247 -0
  10. package/src/server/cliRuntime/mutations/installMigrationMutation.js +213 -0
  11. package/src/server/cliRuntime/mutations/mutationPathUtils.js +12 -0
  12. package/src/server/cliRuntime/mutations/surfaceTargets.js +155 -0
  13. package/src/server/cliRuntime/mutations/templateContext.js +171 -0
  14. package/src/server/cliRuntime/mutations/textMutations.js +250 -0
  15. package/src/server/cliRuntime/packageInstallFlow.js +489 -0
  16. package/src/server/cliRuntime/packageIntrospection/exportEntries.js +259 -0
  17. package/src/server/cliRuntime/packageIntrospection/exportedSymbols.js +216 -0
  18. package/src/server/cliRuntime/packageIntrospection/placementNormalization.js +98 -0
  19. package/src/server/cliRuntime/packageIntrospection/providerBindingIntrospection.js +377 -0
  20. package/src/server/cliRuntime/packageIntrospection.js +137 -0
  21. package/src/server/cliRuntime/packageOptions.js +299 -0
  22. package/src/server/cliRuntime/packageRegistries.js +343 -0
  23. package/src/server/cliRuntime/packageTemplateResolution.js +131 -0
  24. package/src/server/cliRuntime/viteProxy.js +356 -0
  25. package/src/server/commandHandlers/health.js +292 -0
  26. package/src/server/commandHandlers/list.js +292 -0
  27. package/src/server/commandHandlers/package.js +23 -0
  28. package/src/server/commandHandlers/packageCommands/add.js +282 -0
  29. package/src/server/commandHandlers/packageCommands/create.js +155 -0
  30. package/src/server/commandHandlers/packageCommands/generate.js +116 -0
  31. package/src/server/commandHandlers/packageCommands/migrations.js +155 -0
  32. package/src/server/commandHandlers/packageCommands/position.js +103 -0
  33. package/src/server/commandHandlers/packageCommands/remove.js +181 -0
  34. package/src/server/commandHandlers/packageCommands/update.js +40 -0
  35. package/src/server/commandHandlers/shared.js +314 -0
  36. package/src/server/commandHandlers/show/payloads.js +92 -0
  37. package/src/server/commandHandlers/show/renderBundleText.js +16 -0
  38. package/src/server/commandHandlers/show/renderHelpers.js +82 -0
  39. package/src/server/commandHandlers/show/renderPackageCapabilities.js +124 -0
  40. package/src/server/commandHandlers/show/renderPackageExports.js +203 -0
  41. package/src/server/commandHandlers/show/renderPackageText.js +332 -0
  42. package/src/server/commandHandlers/show.js +114 -0
  43. package/src/server/core/argParser.js +144 -0
  44. package/src/server/{runtimeDeps.js → core/buildCommandDeps.js} +2 -1
  45. package/src/server/core/commandCatalog.js +47 -0
  46. package/src/server/core/createCliRunner.js +150 -0
  47. package/src/server/core/createCommandHandlers.js +43 -0
  48. package/src/server/{runCli.js → core/dispatchCli.js} +14 -1
  49. package/src/server/core/usageHelp.js +344 -0
  50. package/src/server/index.js +1 -1
  51. package/src/server/{optionInterpolation.js → shared/optionInterpolation.js} +12 -1
  52. package/src/server/{pathResolution.js → shared/pathResolution.js} +1 -1
  53. package/src/server/argParser.js +0 -206
  54. package/src/server/cliRuntime.js +0 -4853
  55. package/src/server/commandHandlers.js +0 -2109
  56. /package/src/server/{cliError.js → shared/cliError.js} +0 -0
  57. /package/src/server/{collectionUtils.js → shared/collectionUtils.js} +0 -0
  58. /package/src/server/{outputFormatting.js → shared/outputFormatting.js} +0 -0
  59. /package/src/server/{packageIdHelpers.js → shared/packageIdHelpers.js} +0 -0
@@ -0,0 +1,377 @@
1
+ import path from "node:path";
2
+ import { ensureObject } from "../../shared/collectionUtils.js";
3
+
4
+ function escapeRegexLiteral(value) {
5
+ return String(value || "").replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
6
+ }
7
+
8
+ function parseQuotedStringLiteral(value) {
9
+ const source = String(value || "").trim();
10
+ if (source.length < 2) {
11
+ return null;
12
+ }
13
+
14
+ const quote = source[0];
15
+ if ((quote !== "\"" && quote !== "'") || source[source.length - 1] !== quote) {
16
+ return null;
17
+ }
18
+
19
+ if (quote === "\"") {
20
+ try {
21
+ return JSON.parse(source);
22
+ } catch {
23
+ return null;
24
+ }
25
+ }
26
+
27
+ return source
28
+ .slice(1, -1)
29
+ .replace(/\\\\/g, "\\")
30
+ .replace(/\\'/g, "'")
31
+ .replace(/\\\"/g, "\"")
32
+ .replace(/\\n/g, "\n")
33
+ .replace(/\\r/g, "\r")
34
+ .replace(/\\t/g, "\t");
35
+ }
36
+
37
+ function resolveLineNumberAtIndex(source, index) {
38
+ const text = String(source || "");
39
+ const maxIndex = Math.max(0, Math.min(Number(index) || 0, text.length));
40
+ let line = 1;
41
+ for (let cursor = 0; cursor < maxIndex; cursor += 1) {
42
+ if (text[cursor] === "\n") {
43
+ line += 1;
44
+ }
45
+ }
46
+ return line;
47
+ }
48
+
49
+ function findMatchingBraceIndex(source, openBraceIndex) {
50
+ const text = String(source || "");
51
+ const startIndex = Number(openBraceIndex);
52
+ if (!Number.isInteger(startIndex) || startIndex < 0 || startIndex >= text.length || text[startIndex] !== "{") {
53
+ return -1;
54
+ }
55
+
56
+ let depth = 0;
57
+ let inSingleQuote = false;
58
+ let inDoubleQuote = false;
59
+ let inTemplateQuote = false;
60
+ let inLineComment = false;
61
+ let inBlockComment = false;
62
+
63
+ for (let cursor = startIndex; cursor < text.length; cursor += 1) {
64
+ const current = text[cursor];
65
+ const next = text[cursor + 1] || "";
66
+
67
+ if (inLineComment) {
68
+ if (current === "\n") {
69
+ inLineComment = false;
70
+ }
71
+ continue;
72
+ }
73
+
74
+ if (inBlockComment) {
75
+ if (current === "*" && next === "/") {
76
+ inBlockComment = false;
77
+ cursor += 1;
78
+ }
79
+ continue;
80
+ }
81
+
82
+ if (inSingleQuote) {
83
+ if (current === "\\") {
84
+ cursor += 1;
85
+ continue;
86
+ }
87
+ if (current === "'") {
88
+ inSingleQuote = false;
89
+ }
90
+ continue;
91
+ }
92
+
93
+ if (inDoubleQuote) {
94
+ if (current === "\\") {
95
+ cursor += 1;
96
+ continue;
97
+ }
98
+ if (current === "\"") {
99
+ inDoubleQuote = false;
100
+ }
101
+ continue;
102
+ }
103
+
104
+ if (inTemplateQuote) {
105
+ if (current === "\\") {
106
+ cursor += 1;
107
+ continue;
108
+ }
109
+ if (current === "`") {
110
+ inTemplateQuote = false;
111
+ }
112
+ continue;
113
+ }
114
+
115
+ if (current === "/" && next === "/") {
116
+ inLineComment = true;
117
+ cursor += 1;
118
+ continue;
119
+ }
120
+ if (current === "/" && next === "*") {
121
+ inBlockComment = true;
122
+ cursor += 1;
123
+ continue;
124
+ }
125
+ if (current === "'") {
126
+ inSingleQuote = true;
127
+ continue;
128
+ }
129
+ if (current === "\"") {
130
+ inDoubleQuote = true;
131
+ continue;
132
+ }
133
+ if (current === "`") {
134
+ inTemplateQuote = true;
135
+ continue;
136
+ }
137
+
138
+ if (current === "{") {
139
+ depth += 1;
140
+ continue;
141
+ }
142
+ if (current === "}") {
143
+ depth -= 1;
144
+ if (depth === 0) {
145
+ return cursor;
146
+ }
147
+ }
148
+ }
149
+
150
+ return -1;
151
+ }
152
+
153
+ function extractProviderLifecycleMethodRanges(source, providerExportName) {
154
+ const text = String(source || "");
155
+ const providerName = String(providerExportName || "").trim();
156
+ if (!text) {
157
+ return [];
158
+ }
159
+
160
+ const fallback = [
161
+ {
162
+ lifecycle: "unknown",
163
+ start: 0,
164
+ end: text.length
165
+ }
166
+ ];
167
+ if (!providerName) {
168
+ return fallback;
169
+ }
170
+
171
+ const classPattern = new RegExp(`\\bclass\\s+${escapeRegexLiteral(providerName)}\\b`);
172
+ const classMatch = classPattern.exec(text);
173
+ if (!classMatch) {
174
+ return fallback;
175
+ }
176
+
177
+ const classOpenBraceIndex = text.indexOf("{", classMatch.index + classMatch[0].length);
178
+ if (classOpenBraceIndex < 0) {
179
+ return fallback;
180
+ }
181
+ const classCloseBraceIndex = findMatchingBraceIndex(text, classOpenBraceIndex);
182
+ if (classCloseBraceIndex < 0) {
183
+ return fallback;
184
+ }
185
+
186
+ const classBody = text.slice(classOpenBraceIndex + 1, classCloseBraceIndex);
187
+ const methodPattern = /\b(?:async\s+)?(register|boot)\s*\([^)]*\)\s*\{/g;
188
+ const ranges = [];
189
+ let methodMatch = methodPattern.exec(classBody);
190
+ while (methodMatch) {
191
+ const lifecycle = String(methodMatch[1] || "").trim() || "unknown";
192
+ const methodOpenOffset = methodMatch[0].lastIndexOf("{");
193
+ if (methodOpenOffset < 0) {
194
+ methodMatch = methodPattern.exec(classBody);
195
+ continue;
196
+ }
197
+ const methodOpenIndex = classOpenBraceIndex + 1 + methodMatch.index + methodOpenOffset;
198
+ const methodCloseIndex = findMatchingBraceIndex(text, methodOpenIndex);
199
+ if (methodCloseIndex < 0) {
200
+ methodMatch = methodPattern.exec(classBody);
201
+ continue;
202
+ }
203
+ ranges.push({
204
+ lifecycle,
205
+ start: methodOpenIndex + 1,
206
+ end: methodCloseIndex
207
+ });
208
+ methodMatch = methodPattern.exec(classBody);
209
+ }
210
+
211
+ if (ranges.length > 0) {
212
+ return ranges;
213
+ }
214
+ return [
215
+ {
216
+ lifecycle: "class",
217
+ start: classOpenBraceIndex + 1,
218
+ end: classCloseBraceIndex
219
+ }
220
+ ];
221
+ }
222
+
223
+ function collectConstTokenAssignments(source) {
224
+ const text = String(source || "");
225
+ const assignments = new Map();
226
+ const pattern = /^\s*const\s+([A-Za-z_$][A-Za-z0-9_$]*)\s*=\s*([^;]+);\s*$/gm;
227
+ let match = pattern.exec(text);
228
+ while (match) {
229
+ const identifier = String(match[1] || "").trim();
230
+ const expression = String(match[2] || "").trim();
231
+ if (identifier && expression) {
232
+ assignments.set(identifier, expression);
233
+ }
234
+ match = pattern.exec(text);
235
+ }
236
+ return assignments;
237
+ }
238
+
239
+ function resolveTokenFromExpression(expression, constAssignments, visited = new Set()) {
240
+ let normalized = String(expression || "").trim();
241
+ if (!normalized) {
242
+ return {
243
+ token: "",
244
+ resolved: false,
245
+ kind: "empty"
246
+ };
247
+ }
248
+
249
+ while (normalized.startsWith("(") && normalized.endsWith(")")) {
250
+ normalized = normalized.slice(1, -1).trim();
251
+ }
252
+
253
+ const quoted = parseQuotedStringLiteral(normalized);
254
+ if (quoted !== null) {
255
+ return {
256
+ token: quoted,
257
+ resolved: true,
258
+ kind: "string"
259
+ };
260
+ }
261
+
262
+ const symbolMatch = /^Symbol\.for\(\s*(['"])(.*?)\1\s*\)$/.exec(normalized);
263
+ if (symbolMatch) {
264
+ return {
265
+ token: `Symbol.for(${symbolMatch[2]})`,
266
+ resolved: true,
267
+ kind: "symbol"
268
+ };
269
+ }
270
+
271
+ if (/^[A-Za-z_$][A-Za-z0-9_$]*(?:\.[A-Za-z_$][A-Za-z0-9_$]*)+$/.test(normalized)) {
272
+ return {
273
+ token: normalized,
274
+ resolved: true,
275
+ kind: "member"
276
+ };
277
+ }
278
+
279
+ if (/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(normalized)) {
280
+ const identifier = normalized;
281
+ if (visited.has(identifier)) {
282
+ return {
283
+ token: identifier,
284
+ resolved: false,
285
+ kind: "cyclic-identifier"
286
+ };
287
+ }
288
+ const nextExpression = constAssignments.get(identifier);
289
+ if (nextExpression) {
290
+ return resolveTokenFromExpression(nextExpression, constAssignments, new Set([...visited, identifier]));
291
+ }
292
+ return {
293
+ token: identifier,
294
+ resolved: false,
295
+ kind: "identifier"
296
+ };
297
+ }
298
+
299
+ return {
300
+ token: normalized,
301
+ resolved: false,
302
+ kind: "expression"
303
+ };
304
+ }
305
+
306
+ function collectContainerBindingsFromProviderSource({ source, providerLabel, entrypoint, providerExportName }) {
307
+ const text = String(source || "");
308
+ if (!text) {
309
+ return [];
310
+ }
311
+
312
+ const constAssignments = collectConstTokenAssignments(text);
313
+ const methodRanges = extractProviderLifecycleMethodRanges(text, providerExportName);
314
+ const records = [];
315
+
316
+ for (const range of methodRanges) {
317
+ const lifecycle = String(range?.lifecycle || "unknown").trim() || "unknown";
318
+ const start = Number(range?.start) || 0;
319
+ const end = Number(range?.end) || text.length;
320
+ const slice = text.slice(start, end);
321
+ const bindingPattern = /\bapp\.(singleton|bind|scoped|instance)\s*\(\s*([\s\S]*?)\s*,/g;
322
+ let match = bindingPattern.exec(slice);
323
+ while (match) {
324
+ const binding = String(match[1] || "").trim();
325
+ const tokenExpression = String(match[2] || "")
326
+ .replace(/\s+/g, " ")
327
+ .trim();
328
+ if (!tokenExpression) {
329
+ match = bindingPattern.exec(slice);
330
+ continue;
331
+ }
332
+ const tokenResolution = resolveTokenFromExpression(tokenExpression, constAssignments);
333
+ const line = resolveLineNumberAtIndex(text, start + match.index);
334
+ records.push({
335
+ provider: providerLabel,
336
+ entrypoint: String(entrypoint || "").trim(),
337
+ exportName: String(providerExportName || "").trim(),
338
+ lifecycle,
339
+ binding,
340
+ token: String(tokenResolution.token || "").trim(),
341
+ tokenExpression,
342
+ tokenResolved: Boolean(tokenResolution.resolved),
343
+ tokenKind: String(tokenResolution.kind || "").trim(),
344
+ location: `${String(entrypoint || "").trim()}:${line}`,
345
+ line
346
+ });
347
+ match = bindingPattern.exec(slice);
348
+ }
349
+ }
350
+
351
+ return records;
352
+ }
353
+
354
+ function deriveProviderDisplayName(bindingRecord) {
355
+ const binding = ensureObject(bindingRecord);
356
+ const providerLabel = String(binding.provider || "").trim();
357
+ if (!providerLabel) {
358
+ return "";
359
+ }
360
+
361
+ const hashIndex = providerLabel.lastIndexOf("#");
362
+ if (hashIndex > -1 && hashIndex < providerLabel.length - 1) {
363
+ return providerLabel.slice(hashIndex + 1);
364
+ }
365
+
366
+ const entrypoint = String(binding.entrypoint || "").trim();
367
+ if (!entrypoint) {
368
+ return providerLabel;
369
+ }
370
+ const basename = path.posix.basename(entrypoint);
371
+ return basename.replace(/\.(?:c|m)?js$/i, "") || providerLabel;
372
+ }
373
+
374
+ export {
375
+ collectContainerBindingsFromProviderSource,
376
+ deriveProviderDisplayName
377
+ };
@@ -0,0 +1,137 @@
1
+ import { readFile } from "node:fs/promises";
2
+ import path from "node:path";
3
+ import {
4
+ ensureArray,
5
+ ensureObject
6
+ } from "../shared/collectionUtils.js";
7
+ import { fileExists } from "./ioAndMigrations.js";
8
+ import {
9
+ collectContainerBindingsFromProviderSource,
10
+ deriveProviderDisplayName
11
+ } from "./packageIntrospection/providerBindingIntrospection.js";
12
+ import {
13
+ describePackageExports,
14
+ formatPackageSubpathImport,
15
+ shouldShowPackageExportTarget
16
+ } from "./packageIntrospection/exportEntries.js";
17
+ import {
18
+ classifyExportedSymbols,
19
+ collectExportFileSymbolSummaries
20
+ } from "./packageIntrospection/exportedSymbols.js";
21
+ import {
22
+ normalizePlacementContributions,
23
+ normalizePlacementOutlets
24
+ } from "./packageIntrospection/placementNormalization.js";
25
+
26
+ async function inspectPackageOfferings({ packageEntry }) {
27
+ const rootDir = String(packageEntry?.rootDir || "").trim();
28
+ const notes = [];
29
+ const details = {
30
+ available: Boolean(rootDir),
31
+ notes,
32
+ packageExports: [],
33
+ containerBindings: {
34
+ server: [],
35
+ client: []
36
+ },
37
+ exportedSymbols: []
38
+ };
39
+
40
+ if (!rootDir) {
41
+ notes.push("Source files are unavailable for static introspection (catalog metadata only).");
42
+ return details;
43
+ }
44
+
45
+ const packageJson = ensureObject(packageEntry?.packageJson);
46
+ details.packageExports = await describePackageExports({
47
+ packageRoot: rootDir,
48
+ packageJson
49
+ });
50
+
51
+ const runtime = ensureObject(packageEntry?.descriptor?.runtime);
52
+ const runtimeSides = [
53
+ {
54
+ side: "server",
55
+ providers: ensureArray(ensureObject(runtime.server).providers)
56
+ },
57
+ {
58
+ side: "client",
59
+ providers: ensureArray(ensureObject(runtime.client).providers)
60
+ }
61
+ ];
62
+
63
+ for (const runtimeSide of runtimeSides) {
64
+ const side = String(runtimeSide.side || "").trim();
65
+ if (!side) {
66
+ continue;
67
+ }
68
+ const bindings = [];
69
+ for (const provider of runtimeSide.providers) {
70
+ const record = ensureObject(provider);
71
+ const entrypoint = String(record.entrypoint || "").trim();
72
+ const exportName = String(record.export || "").trim();
73
+ if (!entrypoint) {
74
+ continue;
75
+ }
76
+
77
+ const providerLabel = exportName ? `${entrypoint}#${exportName}` : entrypoint;
78
+ if (entrypoint.includes("*")) {
79
+ notes.push(`Skipped wildcard provider entrypoint during introspection: ${providerLabel}`);
80
+ continue;
81
+ }
82
+
83
+ const providerPath = path.resolve(rootDir, entrypoint);
84
+ if (!(await fileExists(providerPath))) {
85
+ notes.push(`Provider file missing during introspection: ${providerLabel}`);
86
+ continue;
87
+ }
88
+
89
+ let source = "";
90
+ try {
91
+ source = await readFile(providerPath, "utf8");
92
+ } catch (error) {
93
+ notes.push(`Failed reading provider ${providerLabel}: ${String(error?.message || error || "unknown error")}`);
94
+ continue;
95
+ }
96
+
97
+ bindings.push(
98
+ ...collectContainerBindingsFromProviderSource({
99
+ source,
100
+ providerLabel,
101
+ entrypoint,
102
+ providerExportName: exportName
103
+ })
104
+ );
105
+ }
106
+
107
+ details.containerBindings[side] = bindings.sort((left, right) => {
108
+ const tokenComparison = String(left?.token || "").localeCompare(String(right?.token || ""));
109
+ if (tokenComparison !== 0) {
110
+ return tokenComparison;
111
+ }
112
+ const providerComparison = String(left?.provider || "").localeCompare(String(right?.provider || ""));
113
+ if (providerComparison !== 0) {
114
+ return providerComparison;
115
+ }
116
+ return Number(left?.line || 0) - Number(right?.line || 0);
117
+ });
118
+ }
119
+
120
+ details.exportedSymbols = await collectExportFileSymbolSummaries({
121
+ packageRoot: rootDir,
122
+ packageExports: details.packageExports,
123
+ notes
124
+ });
125
+
126
+ return details;
127
+ }
128
+
129
+ export {
130
+ classifyExportedSymbols,
131
+ deriveProviderDisplayName,
132
+ formatPackageSubpathImport,
133
+ inspectPackageOfferings,
134
+ normalizePlacementContributions,
135
+ normalizePlacementOutlets,
136
+ shouldShowPackageExportTarget
137
+ };