@jay-framework/plugin-validator 0.24.2 → 0.24.3
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/dist/index.d.ts +1 -1
- package/dist/index.js +129 -25
- package/package.json +5 -5
package/dist/index.d.ts
CHANGED
|
@@ -18,7 +18,7 @@ interface ValidationResult {
|
|
|
18
18
|
typesGenerated?: number;
|
|
19
19
|
}
|
|
20
20
|
interface ValidationError {
|
|
21
|
-
type: 'schema' | 'file-missing' | 'export-mismatch' | 'contract-invalid' | 'component-contract-mismatch' | 'type-generation-failed' | 'add-menu-catalog';
|
|
21
|
+
type: 'schema' | 'file-missing' | 'export-mismatch' | 'contract-invalid' | 'component-contract-mismatch' | 'type-generation-failed' | 'add-menu-catalog' | 'compiler-leak';
|
|
22
22
|
message: string;
|
|
23
23
|
location?: string;
|
|
24
24
|
suggestion?: string;
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import fs from "fs";
|
|
2
2
|
import path from "path";
|
|
3
3
|
import YAML from "yaml";
|
|
4
|
-
import { loadPluginManifest } from "@jay-framework/compiler-shared";
|
|
4
|
+
import { loadPluginManifest, normalizeActionEntry } from "@jay-framework/compiler-shared";
|
|
5
5
|
import { parseContract } from "@jay-framework/compiler-jay-html";
|
|
6
6
|
import { ts } from "@jay-framework/typescript-bridge";
|
|
7
7
|
const FRAMEWORK_PROP_TYPES = /* @__PURE__ */ new Set(["PageProps", "RequestQuery"]);
|
|
@@ -1225,6 +1225,7 @@ async function validatePluginPackage(pluginPath, options) {
|
|
|
1225
1225
|
}
|
|
1226
1226
|
await validateAddMenuCatalog(context, result);
|
|
1227
1227
|
await validateAiditorSettings(context, result);
|
|
1228
|
+
validateNoCompilerLeak(context, result);
|
|
1228
1229
|
result.valid = result.errors.length === 0;
|
|
1229
1230
|
return result;
|
|
1230
1231
|
}
|
|
@@ -1398,13 +1399,28 @@ async function validateSchema(context, result) {
|
|
|
1398
1399
|
}
|
|
1399
1400
|
}
|
|
1400
1401
|
}
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1402
|
+
const hasCapability = Boolean(
|
|
1403
|
+
manifest.contracts || manifest.dynamic_contracts || manifest.actions || manifest.validators || manifest.routes || manifest.services || manifest.contexts || manifest.init || manifest.setup || manifest.agentkit || manifest.commands
|
|
1404
|
+
);
|
|
1405
|
+
if (!hasCapability) {
|
|
1406
|
+
if (manifest.global === true) {
|
|
1407
|
+
const hasGlobalEntry = !context.isNpmPackage || checkExportExists("init", context, ".") || checkExportExists("setup", context, ".");
|
|
1408
|
+
if (!hasGlobalEntry) {
|
|
1409
|
+
result.errors.push({
|
|
1410
|
+
type: "export-mismatch",
|
|
1411
|
+
message: 'Plugin declares "global: true" but exports no init/setup handler to run on each page',
|
|
1412
|
+
location: "plugin.yaml",
|
|
1413
|
+
suggestion: 'Export an "init" (or "setup") handler from the package entry, or declare a capability'
|
|
1414
|
+
});
|
|
1415
|
+
}
|
|
1416
|
+
} else {
|
|
1417
|
+
result.warnings.push({
|
|
1418
|
+
type: "schema",
|
|
1419
|
+
message: "Plugin declares no capabilities (contracts, dynamic_contracts, actions, validators, routes, services, contexts, init, setup, agentkit, commands)",
|
|
1420
|
+
location: "plugin.yaml",
|
|
1421
|
+
suggestion: "Declare at least one capability. See agent-kit/plugin/plugin-structure.md"
|
|
1422
|
+
});
|
|
1423
|
+
}
|
|
1408
1424
|
}
|
|
1409
1425
|
if (manifest.services) {
|
|
1410
1426
|
if (!Array.isArray(manifest.services)) {
|
|
@@ -1468,14 +1484,22 @@ async function validateSchema(context, result) {
|
|
|
1468
1484
|
}
|
|
1469
1485
|
if (manifest.actions) {
|
|
1470
1486
|
for (const entry of manifest.actions) {
|
|
1471
|
-
|
|
1487
|
+
if (typeof entry === "object" && entry.devOnly !== void 0 && typeof entry.devOnly !== "boolean") {
|
|
1488
|
+
result.errors.push({
|
|
1489
|
+
type: "schema",
|
|
1490
|
+
message: `Action "${entry.name}" devOnly must be a boolean`,
|
|
1491
|
+
location: "plugin.yaml actions"
|
|
1492
|
+
});
|
|
1493
|
+
}
|
|
1494
|
+
const { name: exportName, devOnly } = normalizeActionEntry(entry);
|
|
1472
1495
|
if (exportName) {
|
|
1473
1496
|
validateHandlerRef(
|
|
1474
1497
|
exportName,
|
|
1475
1498
|
`Action "${exportName}"`,
|
|
1476
1499
|
"plugin.yaml actions",
|
|
1477
1500
|
context,
|
|
1478
|
-
result
|
|
1501
|
+
result,
|
|
1502
|
+
devOnly ? "./tools" : "."
|
|
1479
1503
|
);
|
|
1480
1504
|
}
|
|
1481
1505
|
}
|
|
@@ -1520,7 +1544,8 @@ async function validateSchema(context, result) {
|
|
|
1520
1544
|
`Route "${route.path}" component`,
|
|
1521
1545
|
`plugin.yaml routes`,
|
|
1522
1546
|
context,
|
|
1523
|
-
result
|
|
1547
|
+
result,
|
|
1548
|
+
route.devOnly ? "./tools" : "."
|
|
1524
1549
|
);
|
|
1525
1550
|
}
|
|
1526
1551
|
if (route.jayHtml) {
|
|
@@ -1574,7 +1599,8 @@ async function validateSchema(context, result) {
|
|
|
1574
1599
|
`Validator "${validator.name}" handler`,
|
|
1575
1600
|
"plugin.yaml validators",
|
|
1576
1601
|
context,
|
|
1577
|
-
result
|
|
1602
|
+
result,
|
|
1603
|
+
"./tools"
|
|
1578
1604
|
);
|
|
1579
1605
|
}
|
|
1580
1606
|
});
|
|
@@ -1594,7 +1620,8 @@ async function validateSchema(context, result) {
|
|
|
1594
1620
|
"Setup handler",
|
|
1595
1621
|
"plugin.yaml setup",
|
|
1596
1622
|
context,
|
|
1597
|
-
result
|
|
1623
|
+
result,
|
|
1624
|
+
"./tools"
|
|
1598
1625
|
);
|
|
1599
1626
|
}
|
|
1600
1627
|
}
|
|
@@ -1604,22 +1631,23 @@ async function validateSchema(context, result) {
|
|
|
1604
1631
|
"Agent-kit handler",
|
|
1605
1632
|
"plugin.yaml agentkit",
|
|
1606
1633
|
context,
|
|
1607
|
-
result
|
|
1634
|
+
result,
|
|
1635
|
+
"./tools"
|
|
1608
1636
|
);
|
|
1609
1637
|
}
|
|
1610
1638
|
}
|
|
1611
|
-
function checkExportExists(exportName, context) {
|
|
1639
|
+
function checkExportExists(exportName, context, exportKey = ".") {
|
|
1612
1640
|
const packageJsonPath = path.join(context.pluginPath, "package.json");
|
|
1613
1641
|
if (!fs.existsSync(packageJsonPath)) return true;
|
|
1614
1642
|
let mainPath;
|
|
1615
1643
|
try {
|
|
1616
1644
|
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8"));
|
|
1617
|
-
if (packageJson.exports?.[
|
|
1618
|
-
const entry = packageJson.exports[
|
|
1645
|
+
if (packageJson.exports?.[exportKey]) {
|
|
1646
|
+
const entry = packageJson.exports[exportKey];
|
|
1619
1647
|
const entryPath = typeof entry === "string" ? entry : entry.default || entry.import;
|
|
1620
1648
|
if (entryPath) mainPath = path.join(context.pluginPath, entryPath);
|
|
1621
1649
|
}
|
|
1622
|
-
if (!mainPath && packageJson.main) {
|
|
1650
|
+
if (!mainPath && exportKey === "." && packageJson.main) {
|
|
1623
1651
|
mainPath = path.join(context.pluginPath, packageJson.main);
|
|
1624
1652
|
}
|
|
1625
1653
|
} catch {
|
|
@@ -1641,7 +1669,63 @@ function checkExportExists(exportName, context) {
|
|
|
1641
1669
|
function isRelativePath(value) {
|
|
1642
1670
|
return value.startsWith("./") || value.startsWith("../");
|
|
1643
1671
|
}
|
|
1644
|
-
function
|
|
1672
|
+
function resolveEntryFile(context, exportKey) {
|
|
1673
|
+
const packageJsonPath = path.join(context.pluginPath, "package.json");
|
|
1674
|
+
if (!fs.existsSync(packageJsonPath)) return void 0;
|
|
1675
|
+
try {
|
|
1676
|
+
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8"));
|
|
1677
|
+
const entry = packageJson.exports?.[exportKey];
|
|
1678
|
+
const entryPath = typeof entry === "string" ? entry : entry?.default || entry?.import || void 0;
|
|
1679
|
+
const resolved = entryPath || (exportKey === "." ? packageJson.main : void 0) ? path.join(context.pluginPath, entryPath || packageJson.main) : void 0;
|
|
1680
|
+
return resolved && fs.existsSync(resolved) ? resolved : void 0;
|
|
1681
|
+
} catch {
|
|
1682
|
+
return void 0;
|
|
1683
|
+
}
|
|
1684
|
+
}
|
|
1685
|
+
function hasComponentCapability(context) {
|
|
1686
|
+
const m = context.manifest;
|
|
1687
|
+
return Boolean(m.contracts || m.dynamic_contracts || m.routes);
|
|
1688
|
+
}
|
|
1689
|
+
function needsToolsEntry(manifest) {
|
|
1690
|
+
if (manifest.validators || manifest.commands || manifest.agentkit || manifest.setup) {
|
|
1691
|
+
return true;
|
|
1692
|
+
}
|
|
1693
|
+
if (manifest.actions) {
|
|
1694
|
+
return manifest.actions.some((entry) => normalizeActionEntry(entry).devOnly === true);
|
|
1695
|
+
}
|
|
1696
|
+
return false;
|
|
1697
|
+
}
|
|
1698
|
+
function detectInteractivePhase(context) {
|
|
1699
|
+
if (!context.isNpmPackage) return "unknown";
|
|
1700
|
+
const entryFile = resolveEntryFile(context, ".");
|
|
1701
|
+
if (!entryFile) return "unknown";
|
|
1702
|
+
try {
|
|
1703
|
+
const content = fs.readFileSync(entryFile, "utf-8");
|
|
1704
|
+
return content.includes("withInteractiveMark(");
|
|
1705
|
+
} catch {
|
|
1706
|
+
return "unknown";
|
|
1707
|
+
}
|
|
1708
|
+
}
|
|
1709
|
+
function validateNoCompilerLeak(context, result) {
|
|
1710
|
+
if (!context.isNpmPackage) return;
|
|
1711
|
+
const entryFile = resolveEntryFile(context, ".");
|
|
1712
|
+
if (!entryFile) return;
|
|
1713
|
+
let content;
|
|
1714
|
+
try {
|
|
1715
|
+
content = fs.readFileSync(entryFile, "utf-8");
|
|
1716
|
+
} catch {
|
|
1717
|
+
return;
|
|
1718
|
+
}
|
|
1719
|
+
if (content.includes("@jay-framework/compiler-")) {
|
|
1720
|
+
result.errors.push({
|
|
1721
|
+
type: "compiler-leak",
|
|
1722
|
+
message: 'Serve entry "." (dist/index.js) imports "@jay-framework/compiler-…" — the compiler must not reach the production serve bundle',
|
|
1723
|
+
location: entryFile,
|
|
1724
|
+
suggestion: 'Move the compiler-using handler (validator, agentkit, setup, or a devOnly action) to lib/tools.ts (the "./tools" export) and remove its re-export from lib/index.ts. A compiler-using action is really a command or a devOnly action (DL#179/#180).'
|
|
1725
|
+
});
|
|
1726
|
+
}
|
|
1727
|
+
}
|
|
1728
|
+
function validateHandlerRef(value, label, location, context, result, exportKey = ".") {
|
|
1645
1729
|
if (context.isNpmPackage) {
|
|
1646
1730
|
if (isRelativePath(value)) {
|
|
1647
1731
|
result.errors.push({
|
|
@@ -1650,12 +1734,13 @@ function validateHandlerRef(value, label, location, context, result) {
|
|
|
1650
1734
|
location,
|
|
1651
1735
|
suggestion: `Export the function from the package entry point and use the export name instead of a path`
|
|
1652
1736
|
});
|
|
1653
|
-
} else if (!checkExportExists(value, context)) {
|
|
1737
|
+
} else if (!checkExportExists(value, context, exportKey)) {
|
|
1738
|
+
const entryFile = exportKey === "./tools" ? "lib/tools.ts" : exportKey === "./client" ? "lib/index.client.ts" : "lib/index.ts";
|
|
1654
1739
|
result.errors.push({
|
|
1655
1740
|
type: "export-mismatch",
|
|
1656
|
-
message: `${label} "${value}" is not exported from the
|
|
1741
|
+
message: `${label} "${value}" is not exported from the "${exportKey}" entry`,
|
|
1657
1742
|
location,
|
|
1658
|
-
suggestion: `Add "export { ${value} } from '...'" to the
|
|
1743
|
+
suggestion: `Add "export { ${value} } from '...'" to ${entryFile} (the "${exportKey}" export)`
|
|
1659
1744
|
});
|
|
1660
1745
|
}
|
|
1661
1746
|
} else if (isRelativePath(value)) {
|
|
@@ -1929,11 +2014,30 @@ async function validatePackageJson(context, result) {
|
|
|
1929
2014
|
});
|
|
1930
2015
|
}
|
|
1931
2016
|
if (!packageJson.exports["./client"]) {
|
|
1932
|
-
|
|
2017
|
+
const interactivity = detectInteractivePhase(context);
|
|
2018
|
+
const needsClient = context.manifest.contexts !== void 0 || interactivity === true;
|
|
2019
|
+
if (needsClient) {
|
|
2020
|
+
result.errors.push({
|
|
2021
|
+
type: "export-mismatch",
|
|
2022
|
+
message: 'package.json exports missing "./client" entry point, but the plugin ' + (context.manifest.contexts !== void 0 ? "declares contexts (client-side by definition)" : "provides an interactive component"),
|
|
2023
|
+
location: packageJsonPath,
|
|
2024
|
+
suggestion: 'Add "./client": "./dist/index.client.js" to exports. The client bundle provides components for hydration and client-side contexts. Build with: vite build (client) + vite build --ssr (server)'
|
|
2025
|
+
});
|
|
2026
|
+
} else if (interactivity === "unknown" && hasComponentCapability(context)) {
|
|
2027
|
+
result.warnings.push({
|
|
2028
|
+
type: "export-mismatch",
|
|
2029
|
+
message: 'package.json exports missing "./client" entry point; could not determine whether any component is interactive (build the plugin before validating)',
|
|
2030
|
+
location: packageJsonPath,
|
|
2031
|
+
suggestion: 'If any component declares an interactive phase, add "./client": "./dist/index.client.js"'
|
|
2032
|
+
});
|
|
2033
|
+
}
|
|
2034
|
+
}
|
|
2035
|
+
if (!packageJson.exports["./tools"] && needsToolsEntry(context.manifest)) {
|
|
2036
|
+
result.errors.push({
|
|
1933
2037
|
type: "export-mismatch",
|
|
1934
|
-
message: 'package.json exports missing "./
|
|
2038
|
+
message: 'package.json exports missing "./tools" entry point, but the plugin declares tools capabilities (validators, commands, agentkit, setup, or devOnly actions)',
|
|
1935
2039
|
location: packageJsonPath,
|
|
1936
|
-
suggestion: 'Add "./
|
|
2040
|
+
suggestion: 'Add "./tools": "./dist/tools.js" to exports and re-export those handlers from lib/tools.ts'
|
|
1937
2041
|
});
|
|
1938
2042
|
}
|
|
1939
2043
|
if (context.manifest.contracts) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jay-framework/plugin-validator",
|
|
3
|
-
"version": "0.24.
|
|
3
|
+
"version": "0.24.3",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"description": "Validation tool for Jay Stack plugins",
|
|
@@ -25,14 +25,14 @@
|
|
|
25
25
|
"test:watch": "vitest"
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@jay-framework/compiler-jay-html": "^0.24.
|
|
29
|
-
"@jay-framework/compiler-shared": "^0.24.
|
|
30
|
-
"@jay-framework/typescript-bridge": "^0.24.
|
|
28
|
+
"@jay-framework/compiler-jay-html": "^0.24.3",
|
|
29
|
+
"@jay-framework/compiler-shared": "^0.24.3",
|
|
30
|
+
"@jay-framework/typescript-bridge": "^0.24.3",
|
|
31
31
|
"chalk": "^4.1.2",
|
|
32
32
|
"yaml": "^2.3.4"
|
|
33
33
|
},
|
|
34
34
|
"devDependencies": {
|
|
35
|
-
"@jay-framework/dev-environment": "^0.24.
|
|
35
|
+
"@jay-framework/dev-environment": "^0.24.3",
|
|
36
36
|
"@types/node": "^22.15.21",
|
|
37
37
|
"rimraf": "^5.0.5",
|
|
38
38
|
"tsup": "^8.0.1",
|