Files
webstudio/scripts/check-package-boundaries.mjs
Oleg Isonen d291e4b671 feat: Builder api runtime contracts guardrails for MCP (#5857)
This PR expands Webstudio MCP and CLI into a more complete agent
authoring interface. Agents can discover project capabilities, edit real
Webstudio content, use data/resources, verify visual output, and report
useful issues when workflows fail.

## MCP User Capabilities

- Create, update, duplicate, reorder, and delete pages.
- Create pages from templates and manage reusable page templates.
- Inspect project pages, instances, props, styles, assets, variables,
resources, and metadata.
- Insert Webstudio components and authored JSX fragments into pages.
- Edit text, props, styles, breakpoints, design tokens, and CSS
variables.
- Update page metadata, SEO fields, redirects, marketplace metadata, and
project settings.
- Create and use static data variables.
- Create and use dynamic resources fetched over HTTP.
- Use REST, GraphQL, and built-in system resources in authored pages.
- Bind data variables and resources to visible page content.
- Create resource-backed pages such as sitemap XML, structured SEO data,
and API-driven content sections.
- Preview current project state without requiring a publish step.
- Capture screenshots for visual verification.
- Verify responsive designs across desktop, tablet, and mobile
breakpoints.
- Use screenshot diff and OCR-style checks to inspect rendered output.
- Reset and refresh local MCP project state when needed.
- Discover available CLI/MCP commands from generated manuals.
- Use clearer examples for resources, data variables, JSX authoring,
previewing, and visual checks.
- Read marketplace product metadata without requiring a full project
snapshot.
- Get safer guidance when using low-level patch tools.
- Get a ready-to-copy Discord report template when MCP/CLI behavior is
confusing or broken.
2026-07-10 02:13:22 +02:00

66 lines
1.8 KiB
JavaScript

import { readdir, readFile } from "node:fs/promises";
import { join } from "node:path";
const workspaceRoots = ["apps", "packages"];
const runtimeDependencyFields = [
"dependencies",
"peerDependencies",
"optionalDependencies",
];
const readJson = async (path) => JSON.parse(await readFile(path, "utf8"));
const findWorkspacePackageJsons = async () => {
const packageJsons = [];
for (const root of workspaceRoots) {
const entries = await readdir(root, { withFileTypes: true });
for (const entry of entries) {
if (entry.isDirectory()) {
packageJsons.push(join(root, entry.name, "package.json"));
}
}
}
return packageJsons;
};
const packages = await Promise.all(
(await findWorkspacePackageJsons()).map(async (path) => ({
path,
manifest: await readJson(path),
}))
);
const workspacePackageByName = new Map(
packages.map((pkg) => [pkg.manifest.name, pkg])
);
const violations = [];
for (const pkg of packages) {
if (pkg.manifest.private === true) {
continue;
}
for (const field of runtimeDependencyFields) {
for (const dependencyName of Object.keys(pkg.manifest[field] ?? {})) {
const dependency = workspacePackageByName.get(dependencyName);
if (dependency?.manifest.private === true) {
violations.push(
`${pkg.manifest.name} (${pkg.path}) lists private workspace package ` +
`${dependencyName} in ${field}. Use devDependencies for build-time ` +
`generation or move the shared runtime contract to a public package.`
);
}
}
}
}
if (violations.length > 0) {
console.error("Package boundary violations found:\n");
for (const violation of violations) {
console.error(`- ${violation}`);
}
process.exit(1);
}
console.info("Package boundaries are valid.");