@jskit-ai/jskit-cli 0.2.74 → 0.2.76
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 +4 -4
- package/src/server/cliRuntime/completion.js +40 -4
- package/src/server/cliRuntime/mutations/textMutations.js +8 -2
- package/src/server/cliRuntime/packageIntrospection/placementNormalization.js +19 -5
- package/src/server/commandHandlers/health.js +10 -6
- package/src/server/commandHandlers/list.js +420 -17
- package/src/server/commandHandlers/mobile.js +178 -277
- package/src/server/commandHandlers/mobileCommandCatalog.js +34 -39
- package/src/server/commandHandlers/mobileShellSupport.js +30 -7
- package/src/server/core/argParser.js +6 -0
- package/src/server/core/buildCommandDeps.js +3 -1
- package/src/server/core/commandCatalog.js +18 -12
- package/src/server/core/createCliRunner.js +8 -2
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.76",
|
|
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.
|
|
24
|
-
"@jskit-ai/kernel": "0.1.
|
|
25
|
-
"@jskit-ai/shell-web": "0.1.
|
|
23
|
+
"@jskit-ai/jskit-catalog": "0.1.75",
|
|
24
|
+
"@jskit-ai/kernel": "0.1.67",
|
|
25
|
+
"@jskit-ai/shell-web": "0.1.66"
|
|
26
26
|
},
|
|
27
27
|
"engines": {
|
|
28
28
|
"node": "20.x"
|
|
@@ -2,6 +2,10 @@ import path from "node:path";
|
|
|
2
2
|
import { pathToFileURL } from "node:url";
|
|
3
3
|
import { access, readdir, readFile } from "node:fs/promises";
|
|
4
4
|
import { buildCrudFieldContractMap } from "@jskit-ai/kernel/shared/support/crudFieldContract";
|
|
5
|
+
import {
|
|
6
|
+
discoverPlacementTopologyFromApp,
|
|
7
|
+
discoverShellOutletTargetsFromApp
|
|
8
|
+
} from "@jskit-ai/kernel/server/support";
|
|
5
9
|
import {
|
|
6
10
|
buildAppCommandOptionMeta,
|
|
7
11
|
listAppCommandDefinitions
|
|
@@ -370,6 +374,37 @@ async function discoverPlacementTargets(appRoot) {
|
|
|
370
374
|
return uniqueSorted(placementValues);
|
|
371
375
|
}
|
|
372
376
|
|
|
377
|
+
async function discoverSemanticPlacementTargets(appRoot) {
|
|
378
|
+
try {
|
|
379
|
+
const topology = await discoverPlacementTopologyFromApp({ appRoot });
|
|
380
|
+
return uniqueSorted(
|
|
381
|
+
(Array.isArray(topology?.placements) ? topology.placements : [])
|
|
382
|
+
.map((placement) => normalizeText(placement?.id))
|
|
383
|
+
.filter(Boolean)
|
|
384
|
+
);
|
|
385
|
+
} catch {
|
|
386
|
+
const topologyPath = path.join(appRoot, "src", "placementTopology.js");
|
|
387
|
+
if (!(await pathExists(topologyPath))) {
|
|
388
|
+
return [];
|
|
389
|
+
}
|
|
390
|
+
const source = await readFile(topologyPath, "utf8");
|
|
391
|
+
return uniqueSorted(extractMatches(source, [/\bid\s*:\s*["']([^"':]+(?:\.[^"':]+)+)["']/g]));
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
async function discoverConcretePlacementTargets(appRoot) {
|
|
396
|
+
try {
|
|
397
|
+
const discovered = await discoverShellOutletTargetsFromApp({ appRoot });
|
|
398
|
+
return uniqueSorted(
|
|
399
|
+
(Array.isArray(discovered?.targets) ? discovered.targets : [])
|
|
400
|
+
.map((target) => normalizeText(target?.id || target?.target))
|
|
401
|
+
.filter(Boolean)
|
|
402
|
+
);
|
|
403
|
+
} catch {
|
|
404
|
+
return discoverPlacementTargets(appRoot);
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
|
|
373
408
|
async function discoverComponentTokens(appRoot) {
|
|
374
409
|
const tokens = [];
|
|
375
410
|
for (const filePath of [
|
|
@@ -391,7 +426,6 @@ async function discoverComponentTokens(appRoot) {
|
|
|
391
426
|
const source = await readFile(filePath, "utf8");
|
|
392
427
|
tokens.push(...extractMatches(source, [
|
|
393
428
|
/\bcomponentToken\s*:\s*["']([^"']+)["']/g,
|
|
394
|
-
/\bdefault-link-component-token\s*=\s*["']([^"']+)["']/g,
|
|
395
429
|
/registerMainClientComponent\(\s*["']([^"']+)["']/g
|
|
396
430
|
]));
|
|
397
431
|
}
|
|
@@ -590,9 +624,11 @@ async function completeOptionValue({
|
|
|
590
624
|
|
|
591
625
|
if (normalizedOptionName === "resource-file") {
|
|
592
626
|
suggestions = await discoverResourceFiles(appRoot);
|
|
593
|
-
} else if (["link-placement", "placement"
|
|
594
|
-
suggestions = await
|
|
595
|
-
} else if (normalizedOptionName === "
|
|
627
|
+
} else if (["link-placement", "placement"].includes(normalizedOptionName)) {
|
|
628
|
+
suggestions = await discoverSemanticPlacementTargets(appRoot);
|
|
629
|
+
} else if (normalizedOptionName === "target") {
|
|
630
|
+
suggestions = await discoverConcretePlacementTargets(appRoot);
|
|
631
|
+
} else if (normalizedOptionName === "link-renderer") {
|
|
596
632
|
suggestions = await discoverComponentTokens(appRoot);
|
|
597
633
|
} else if (normalizedOptionName === "surface") {
|
|
598
634
|
suggestions = await discoverSurfaces(appRoot);
|
|
@@ -113,6 +113,13 @@ async function applyTextMutations(
|
|
|
113
113
|
const previous = await readFileBufferIfExists(absoluteFile);
|
|
114
114
|
const previousContent = previous.exists ? previous.buffer.toString("utf8") : "";
|
|
115
115
|
const mutationId = String(mutation?.id || "").trim() || "append-text";
|
|
116
|
+
const interpolatedSkipChecks = normalizeSkipChecks(mutation?.skipIfContains)
|
|
117
|
+
.map((entry) => interpolateOptionValue(entry, options, packageEntry.packageId, `${mutationId}.skipIfContains`))
|
|
118
|
+
.filter((entry) => String(entry || "").trim().length > 0);
|
|
119
|
+
if (interpolatedSkipChecks.some((pattern) => previousContent.includes(String(pattern)))) {
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
122
|
+
|
|
116
123
|
const resolvedSnippet = interpolateOptionValue(snippet, options, packageEntry.packageId, mutationId);
|
|
117
124
|
const templateContextReplacements = await resolveTemplateContextReplacementsForMutation({
|
|
118
125
|
packageEntry,
|
|
@@ -126,8 +133,7 @@ async function applyTextMutations(
|
|
|
126
133
|
const renderedSnippet = templateContextReplacements
|
|
127
134
|
? applyTemplateContextReplacements(resolvedSnippet, templateContextReplacements)
|
|
128
135
|
: resolvedSnippet;
|
|
129
|
-
const skipChecks =
|
|
130
|
-
.map((entry) => interpolateOptionValue(entry, options, packageEntry.packageId, `${mutationId}.skipIfContains`))
|
|
136
|
+
const skipChecks = interpolatedSkipChecks
|
|
131
137
|
.map((entry) => {
|
|
132
138
|
if (!templateContextReplacements) {
|
|
133
139
|
return entry;
|
|
@@ -3,7 +3,11 @@ import {
|
|
|
3
3
|
ensureObject
|
|
4
4
|
} from "../../shared/collectionUtils.js";
|
|
5
5
|
import {
|
|
6
|
-
|
|
6
|
+
normalizePlacementKind,
|
|
7
|
+
normalizePlacementOwnerId,
|
|
8
|
+
normalizePlacementTopologyDefinition,
|
|
9
|
+
normalizeShellOutletTargetId,
|
|
10
|
+
resolvePlacementTargetReference
|
|
7
11
|
} from "@jskit-ai/kernel/shared/support/shellLayoutTargets";
|
|
8
12
|
|
|
9
13
|
function normalizePlacementOutlets(value) {
|
|
@@ -41,22 +45,27 @@ function normalizePlacementContributions(value) {
|
|
|
41
45
|
for (const entry of ensureArray(value)) {
|
|
42
46
|
const record = ensureObject(entry);
|
|
43
47
|
const id = String(record.id || "").trim();
|
|
44
|
-
const
|
|
45
|
-
if (!id || !
|
|
48
|
+
const targetReference = resolvePlacementTargetReference(record.target);
|
|
49
|
+
if (!id || !targetReference?.id) {
|
|
46
50
|
continue;
|
|
47
51
|
}
|
|
48
52
|
|
|
49
53
|
const surfaces = [...new Set(ensureArray(record.surfaces).map((item) => String(item || "").trim()).filter(Boolean))];
|
|
54
|
+
const kind = normalizePlacementKind(record.kind) || (String(record.componentToken || "").trim() ? "component" : "link");
|
|
50
55
|
const componentToken = String(record.componentToken || "").trim();
|
|
51
56
|
const when = String(record.when || "").trim();
|
|
52
57
|
const description = String(record.description || "").trim();
|
|
53
58
|
const source = String(record.source || "").trim();
|
|
59
|
+
const owner = normalizePlacementOwnerId(record.owner);
|
|
54
60
|
const parsedOrder = Number(record.order);
|
|
55
61
|
const order = Number.isFinite(parsedOrder) ? Math.trunc(parsedOrder) : null;
|
|
56
62
|
contributions.push(
|
|
57
63
|
Object.freeze({
|
|
58
64
|
id,
|
|
59
|
-
target,
|
|
65
|
+
target: targetReference.id,
|
|
66
|
+
targetType: targetReference.type,
|
|
67
|
+
owner,
|
|
68
|
+
kind,
|
|
60
69
|
surfaces: Object.freeze(surfaces),
|
|
61
70
|
order,
|
|
62
71
|
componentToken,
|
|
@@ -83,7 +92,12 @@ function normalizePlacementContributions(value) {
|
|
|
83
92
|
);
|
|
84
93
|
}
|
|
85
94
|
|
|
95
|
+
function normalizePlacementTopology(value, { context = "package placement topology" } = {}) {
|
|
96
|
+
return normalizePlacementTopologyDefinition(value, { context }).placements;
|
|
97
|
+
}
|
|
98
|
+
|
|
86
99
|
export {
|
|
87
100
|
normalizePlacementContributions,
|
|
88
|
-
normalizePlacementOutlets
|
|
101
|
+
normalizePlacementOutlets,
|
|
102
|
+
normalizePlacementTopology
|
|
89
103
|
};
|
|
@@ -1825,8 +1825,12 @@ function createHealthCommands(ctx = {}) {
|
|
|
1825
1825
|
return false;
|
|
1826
1826
|
}
|
|
1827
1827
|
|
|
1828
|
-
function
|
|
1829
|
-
|
|
1828
|
+
function isListFiltersImportSource(sourcePath = "") {
|
|
1829
|
+
const normalizedSource = String(sourcePath || "").trim();
|
|
1830
|
+
return (
|
|
1831
|
+
/(^|\/)shared\/[^/'"]*ListFilters(?:\.[A-Za-z0-9]+)?$/u.test(normalizedSource) ||
|
|
1832
|
+
/^\.\/listFilters\.[A-Za-z0-9]+$/u.test(normalizedSource)
|
|
1833
|
+
);
|
|
1830
1834
|
}
|
|
1831
1835
|
|
|
1832
1836
|
function findCallSites(sourceText = "", calleeName = "") {
|
|
@@ -1872,22 +1876,22 @@ function createHealthCommands(ctx = {}) {
|
|
|
1872
1876
|
|
|
1873
1877
|
if (!firstArgument || firstArgument.startsWith("{")) {
|
|
1874
1878
|
issues.push(
|
|
1875
|
-
`${relativePath}:${lineNumber}: [filters:shared-definition] do not inline structured filter definitions in ${calleeName}(...). Put them in packages/<crud>/src/shared/<crud>ListFilters.js and import that
|
|
1879
|
+
`${relativePath}:${lineNumber}: [filters:shared-definition] do not inline structured filter definitions in ${calleeName}(...). Put them in listFilters.js or packages/<crud>/src/shared/<crud>ListFilters.js and import that module.`
|
|
1876
1880
|
);
|
|
1877
1881
|
continue;
|
|
1878
1882
|
}
|
|
1879
1883
|
|
|
1880
1884
|
if (!/^[A-Za-z_$][\w$]*$/u.test(firstArgument)) {
|
|
1881
1885
|
issues.push(
|
|
1882
|
-
`${relativePath}:${lineNumber}: [filters:shared-definition] ${calleeName}(...) must receive a definitions symbol imported from a CRUD shared *ListFilters module, not an ad-hoc expression.`
|
|
1886
|
+
`${relativePath}:${lineNumber}: [filters:shared-definition] ${calleeName}(...) must receive a definitions symbol imported from listFilters.js or a CRUD shared *ListFilters module, not an ad-hoc expression.`
|
|
1883
1887
|
);
|
|
1884
1888
|
continue;
|
|
1885
1889
|
}
|
|
1886
1890
|
|
|
1887
1891
|
const importSource = importBindings.get(firstArgument) || "";
|
|
1888
|
-
if (!
|
|
1892
|
+
if (!isListFiltersImportSource(importSource)) {
|
|
1889
1893
|
issues.push(
|
|
1890
|
-
`${relativePath}:${lineNumber}: [filters:shared-definition] ${calleeName}(${firstArgument}, ...) must use definitions imported from a CRUD shared *ListFilters module. Found ${importSource ? `import source "${importSource}"` : "a local symbol"} instead.`
|
|
1894
|
+
`${relativePath}:${lineNumber}: [filters:shared-definition] ${calleeName}(${firstArgument}, ...) must use definitions imported from listFilters.js or a CRUD shared *ListFilters module. Found ${importSource ? `import source "${importSource}"` : "a local symbol"} instead.`
|
|
1891
1895
|
);
|
|
1892
1896
|
}
|
|
1893
1897
|
}
|