@seed-ship/mcp-ui-cli 1.0.2
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/CHANGELOG.md +76 -0
- package/LICENSE +21 -0
- package/README.md +67 -0
- package/dist/cli.cjs +12 -0
- package/dist/cli.cjs.map +1 -0
- package/dist/cli.js +11 -0
- package/dist/cli.js.map +1 -0
- package/dist/diff-B1c8BBVB.cjs +420 -0
- package/dist/diff-B1c8BBVB.cjs.map +1 -0
- package/dist/diff-B8QJSPfQ.js +420 -0
- package/dist/diff-B8QJSPfQ.js.map +1 -0
- package/dist/index.cjs +8 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -0
- package/package.json +71 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# @mcp-ui/cli Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
## [1.0.0] - 2025-01-14
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
- Initial release of `@mcp-ui/cli` package
|
|
9
|
+
- Command-line interface for component registry operations
|
|
10
|
+
- `validate` command: Validates registries against JSON Schema and Zod
|
|
11
|
+
- `generate-types` command: Generates TypeScript types from component schemas
|
|
12
|
+
- `test-examples` command: Tests all component examples for validity
|
|
13
|
+
- `diff` command: Compares registry versions for breaking changes
|
|
14
|
+
|
|
15
|
+
### Features
|
|
16
|
+
- **Dual Validation**: Both JSON Schema (Ajv) and Zod validation
|
|
17
|
+
- **Type Generation**: Automatic TypeScript type generation with json-schema-to-typescript
|
|
18
|
+
- **Example Testing**: Validates all component examples against schemas
|
|
19
|
+
- **Breaking Change Detection**: Semantic diff with breaking/non-breaking classification
|
|
20
|
+
- **Beautiful Output**: Colorful terminal output with chalk and ora spinners
|
|
21
|
+
- **Exit Codes**: Proper exit codes for CI/CD integration
|
|
22
|
+
- **Verbose Mode**: Detailed output for debugging
|
|
23
|
+
|
|
24
|
+
### Commands
|
|
25
|
+
|
|
26
|
+
#### validate
|
|
27
|
+
```bash
|
|
28
|
+
mcp-ui validate <file> [--strict] [--verbose]
|
|
29
|
+
```
|
|
30
|
+
Validates a component registry against both JSON Schema and Zod schemas.
|
|
31
|
+
|
|
32
|
+
#### generate-types
|
|
33
|
+
```bash
|
|
34
|
+
mcp-ui generate-types <input> [output] [--namespace <name>] [--export-all]
|
|
35
|
+
```
|
|
36
|
+
Generates TypeScript types from component schemas.
|
|
37
|
+
|
|
38
|
+
#### test-examples
|
|
39
|
+
```bash
|
|
40
|
+
mcp-ui test-examples <file> [--component <id>] [--verbose]
|
|
41
|
+
```
|
|
42
|
+
Tests all examples in a component registry.
|
|
43
|
+
|
|
44
|
+
#### diff
|
|
45
|
+
```bash
|
|
46
|
+
mcp-ui diff <old> <new> [--json] [--fail-on-breaking]
|
|
47
|
+
```
|
|
48
|
+
Compares two registry versions for breaking changes.
|
|
49
|
+
|
|
50
|
+
### Dependencies
|
|
51
|
+
- commander: CLI framework
|
|
52
|
+
- ajv: JSON Schema validation
|
|
53
|
+
- zod: Runtime TypeScript validation
|
|
54
|
+
- json-schema-to-typescript: Type generation
|
|
55
|
+
- chalk: Terminal colors
|
|
56
|
+
- ora: Spinners
|
|
57
|
+
|
|
58
|
+
### Installation
|
|
59
|
+
```bash
|
|
60
|
+
pnpm add -D @mcp-ui/cli
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### Usage
|
|
64
|
+
```bash
|
|
65
|
+
# Validate a registry
|
|
66
|
+
mcp-ui validate ./my-registry.json
|
|
67
|
+
|
|
68
|
+
# Generate types
|
|
69
|
+
mcp-ui generate-types ./my-registry.json ./types.ts
|
|
70
|
+
|
|
71
|
+
# Test examples
|
|
72
|
+
mcp-ui test-examples ./my-registry.json
|
|
73
|
+
|
|
74
|
+
# Check for breaking changes
|
|
75
|
+
mcp-ui diff ./old-registry.json ./new-registry.json --fail-on-breaking
|
|
76
|
+
```
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Gabriel Brument
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# @mcp-ui/cli
|
|
2
|
+
|
|
3
|
+
CLI tools for validating and generating MCP UI component registries.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pnpm add -D @mcp-ui/cli
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Commands
|
|
12
|
+
|
|
13
|
+
### Validate Registry
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
mcp-ui validate registry.json
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Validates a component registry against the JSON Schema specification.
|
|
20
|
+
|
|
21
|
+
### Generate TypeScript Types
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
mcp-ui generate-types registry.json --output types/
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Generates TypeScript type definitions from a component registry.
|
|
28
|
+
|
|
29
|
+
### Test Examples
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
mcp-ui test-examples registry.json
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Validates all component examples in the registry.
|
|
36
|
+
|
|
37
|
+
### Create Component
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
mcp-ui create-component quickchart-line
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Creates a new component template.
|
|
44
|
+
|
|
45
|
+
### Check Breaking Changes
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
mcp-ui diff registry-old.json registry-new.json
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Compares two registry files and reports breaking changes.
|
|
52
|
+
|
|
53
|
+
## Usage in CI
|
|
54
|
+
|
|
55
|
+
```yaml
|
|
56
|
+
# .github/workflows/validate.yml
|
|
57
|
+
- name: Validate Component Registry
|
|
58
|
+
run: pnpm mcp-ui validate registry.json
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Documentation
|
|
62
|
+
|
|
63
|
+
See the [full documentation](../../docs/features/generative-ui/) for more details.
|
|
64
|
+
|
|
65
|
+
## License
|
|
66
|
+
|
|
67
|
+
MIT
|
package/dist/cli.cjs
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
const commander = require("commander");
|
|
4
|
+
const diff = require("./diff-B1c8BBVB.cjs");
|
|
5
|
+
const program = new commander.Command();
|
|
6
|
+
program.name("mcp-ui").description("CLI tools for MCP UI component registries").version("1.0.0");
|
|
7
|
+
program.command("validate").description("Validate a component registry against the schema").argument("<file>", "Path to registry JSON file").option("--strict", "Enable strict validation mode", false).option("--verbose", "Show detailed validation output", false).action(diff.validateCommand);
|
|
8
|
+
program.command("generate-types").description("Generate TypeScript types from a registry").argument("<input>", "Path to registry JSON file").argument("[output]", "Output file path (default: stdout)").option("--namespace <name>", "Wrap types in a namespace").option("--export-all", "Export all generated types", false).action(diff.generateTypesCommand);
|
|
9
|
+
program.command("test-examples").description("Test all examples in a component registry").argument("<file>", "Path to registry JSON file").option("--component <id>", "Test only specific component").option("--verbose", "Show detailed test output", false).action(diff.testExamplesCommand);
|
|
10
|
+
program.command("diff").description("Compare two registry versions for breaking changes").argument("<old>", "Path to old registry JSON file").argument("<new>", "Path to new registry JSON file").option("--json", "Output diff as JSON", false).option("--fail-on-breaking", "Exit with error if breaking changes found", false).action(diff.diffCommand);
|
|
11
|
+
program.parse();
|
|
12
|
+
//# sourceMappingURL=cli.cjs.map
|
package/dist/cli.cjs.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.cjs","sources":["../src/cli.ts"],"sourcesContent":["#!/usr/bin/env node\n\n/**\n * @seed-ship/mcp-ui-cli\n *\n * Command-line interface for MCP UI component registry operations\n */\n\nimport { Command } from 'commander'\nimport { validateCommand } from './commands/validate.js'\nimport { generateTypesCommand } from './commands/generate-types.js'\nimport { testExamplesCommand } from './commands/test-examples.js'\nimport { diffCommand } from './commands/diff.js'\n\nconst program = new Command()\n\nprogram.name('mcp-ui').description('CLI tools for MCP UI component registries').version('1.0.0')\n\n// Validate command\nprogram\n .command('validate')\n .description('Validate a component registry against the schema')\n .argument('<file>', 'Path to registry JSON file')\n .option('--strict', 'Enable strict validation mode', false)\n .option('--verbose', 'Show detailed validation output', false)\n .action(validateCommand)\n\n// Generate types command\nprogram\n .command('generate-types')\n .description('Generate TypeScript types from a registry')\n .argument('<input>', 'Path to registry JSON file')\n .argument('[output]', 'Output file path (default: stdout)')\n .option('--namespace <name>', 'Wrap types in a namespace')\n .option('--export-all', 'Export all generated types', false)\n .action(generateTypesCommand)\n\n// Test examples command\nprogram\n .command('test-examples')\n .description('Test all examples in a component registry')\n .argument('<file>', 'Path to registry JSON file')\n .option('--component <id>', 'Test only specific component')\n .option('--verbose', 'Show detailed test output', false)\n .action(testExamplesCommand)\n\n// Diff command\nprogram\n .command('diff')\n .description('Compare two registry versions for breaking changes')\n .argument('<old>', 'Path to old registry JSON file')\n .argument('<new>', 'Path to new registry JSON file')\n .option('--json', 'Output diff as JSON', false)\n .option('--fail-on-breaking', 'Exit with error if breaking changes found', false)\n .action(diffCommand)\n\nprogram.parse()\n"],"names":["Command","validateCommand","generateTypesCommand","testExamplesCommand","diffCommand"],"mappings":";;;;AAcA,MAAM,UAAU,IAAIA,UAAAA,QAAA;AAEpB,QAAQ,KAAK,QAAQ,EAAE,YAAY,2CAA2C,EAAE,QAAQ,OAAO;AAG/F,QACG,QAAQ,UAAU,EAClB,YAAY,kDAAkD,EAC9D,SAAS,UAAU,4BAA4B,EAC/C,OAAO,YAAY,iCAAiC,KAAK,EACzD,OAAO,aAAa,mCAAmC,KAAK,EAC5D,OAAOC,oBAAe;AAGzB,QACG,QAAQ,gBAAgB,EACxB,YAAY,2CAA2C,EACvD,SAAS,WAAW,4BAA4B,EAChD,SAAS,YAAY,oCAAoC,EACzD,OAAO,sBAAsB,2BAA2B,EACxD,OAAO,gBAAgB,8BAA8B,KAAK,EAC1D,OAAOC,yBAAoB;AAG9B,QACG,QAAQ,eAAe,EACvB,YAAY,2CAA2C,EACvD,SAAS,UAAU,4BAA4B,EAC/C,OAAO,oBAAoB,8BAA8B,EACzD,OAAO,aAAa,6BAA6B,KAAK,EACtD,OAAOC,wBAAmB;AAG7B,QACG,QAAQ,MAAM,EACd,YAAY,oDAAoD,EAChE,SAAS,SAAS,gCAAgC,EAClD,SAAS,SAAS,gCAAgC,EAClD,OAAO,UAAU,uBAAuB,KAAK,EAC7C,OAAO,sBAAsB,6CAA6C,KAAK,EAC/E,OAAOC,KAAAA,WAAW;AAErB,QAAQ,MAAA;"}
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Command } from "commander";
|
|
3
|
+
import { v as validateCommand, g as generateTypesCommand, t as testExamplesCommand, d as diffCommand } from "./diff-B8QJSPfQ.js";
|
|
4
|
+
const program = new Command();
|
|
5
|
+
program.name("mcp-ui").description("CLI tools for MCP UI component registries").version("1.0.0");
|
|
6
|
+
program.command("validate").description("Validate a component registry against the schema").argument("<file>", "Path to registry JSON file").option("--strict", "Enable strict validation mode", false).option("--verbose", "Show detailed validation output", false).action(validateCommand);
|
|
7
|
+
program.command("generate-types").description("Generate TypeScript types from a registry").argument("<input>", "Path to registry JSON file").argument("[output]", "Output file path (default: stdout)").option("--namespace <name>", "Wrap types in a namespace").option("--export-all", "Export all generated types", false).action(generateTypesCommand);
|
|
8
|
+
program.command("test-examples").description("Test all examples in a component registry").argument("<file>", "Path to registry JSON file").option("--component <id>", "Test only specific component").option("--verbose", "Show detailed test output", false).action(testExamplesCommand);
|
|
9
|
+
program.command("diff").description("Compare two registry versions for breaking changes").argument("<old>", "Path to old registry JSON file").argument("<new>", "Path to new registry JSON file").option("--json", "Output diff as JSON", false).option("--fail-on-breaking", "Exit with error if breaking changes found", false).action(diffCommand);
|
|
10
|
+
program.parse();
|
|
11
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sources":["../src/cli.ts"],"sourcesContent":["#!/usr/bin/env node\n\n/**\n * @seed-ship/mcp-ui-cli\n *\n * Command-line interface for MCP UI component registry operations\n */\n\nimport { Command } from 'commander'\nimport { validateCommand } from './commands/validate.js'\nimport { generateTypesCommand } from './commands/generate-types.js'\nimport { testExamplesCommand } from './commands/test-examples.js'\nimport { diffCommand } from './commands/diff.js'\n\nconst program = new Command()\n\nprogram.name('mcp-ui').description('CLI tools for MCP UI component registries').version('1.0.0')\n\n// Validate command\nprogram\n .command('validate')\n .description('Validate a component registry against the schema')\n .argument('<file>', 'Path to registry JSON file')\n .option('--strict', 'Enable strict validation mode', false)\n .option('--verbose', 'Show detailed validation output', false)\n .action(validateCommand)\n\n// Generate types command\nprogram\n .command('generate-types')\n .description('Generate TypeScript types from a registry')\n .argument('<input>', 'Path to registry JSON file')\n .argument('[output]', 'Output file path (default: stdout)')\n .option('--namespace <name>', 'Wrap types in a namespace')\n .option('--export-all', 'Export all generated types', false)\n .action(generateTypesCommand)\n\n// Test examples command\nprogram\n .command('test-examples')\n .description('Test all examples in a component registry')\n .argument('<file>', 'Path to registry JSON file')\n .option('--component <id>', 'Test only specific component')\n .option('--verbose', 'Show detailed test output', false)\n .action(testExamplesCommand)\n\n// Diff command\nprogram\n .command('diff')\n .description('Compare two registry versions for breaking changes')\n .argument('<old>', 'Path to old registry JSON file')\n .argument('<new>', 'Path to new registry JSON file')\n .option('--json', 'Output diff as JSON', false)\n .option('--fail-on-breaking', 'Exit with error if breaking changes found', false)\n .action(diffCommand)\n\nprogram.parse()\n"],"names":[],"mappings":";;;AAcA,MAAM,UAAU,IAAI,QAAA;AAEpB,QAAQ,KAAK,QAAQ,EAAE,YAAY,2CAA2C,EAAE,QAAQ,OAAO;AAG/F,QACG,QAAQ,UAAU,EAClB,YAAY,kDAAkD,EAC9D,SAAS,UAAU,4BAA4B,EAC/C,OAAO,YAAY,iCAAiC,KAAK,EACzD,OAAO,aAAa,mCAAmC,KAAK,EAC5D,OAAO,eAAe;AAGzB,QACG,QAAQ,gBAAgB,EACxB,YAAY,2CAA2C,EACvD,SAAS,WAAW,4BAA4B,EAChD,SAAS,YAAY,oCAAoC,EACzD,OAAO,sBAAsB,2BAA2B,EACxD,OAAO,gBAAgB,8BAA8B,KAAK,EAC1D,OAAO,oBAAoB;AAG9B,QACG,QAAQ,eAAe,EACvB,YAAY,2CAA2C,EACvD,SAAS,UAAU,4BAA4B,EAC/C,OAAO,oBAAoB,8BAA8B,EACzD,OAAO,aAAa,6BAA6B,KAAK,EACtD,OAAO,mBAAmB;AAG7B,QACG,QAAQ,MAAM,EACd,YAAY,oDAAoD,EAChE,SAAS,SAAS,gCAAgC,EAClD,SAAS,SAAS,gCAAgC,EAClD,OAAO,UAAU,uBAAuB,KAAK,EAC7C,OAAO,sBAAsB,6CAA6C,KAAK,EAC/E,OAAO,WAAW;AAErB,QAAQ,MAAA;"}
|
|
@@ -0,0 +1,420 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
const fs = require("fs");
|
|
3
|
+
const path = require("path");
|
|
4
|
+
const Ajv = require("ajv");
|
|
5
|
+
const chalk = require("chalk");
|
|
6
|
+
const ora = require("ora");
|
|
7
|
+
const mcpUiSpec = require("@seed-ship/mcp-ui-spec");
|
|
8
|
+
const url = require("url");
|
|
9
|
+
const jsonSchemaToTypescript = require("json-schema-to-typescript");
|
|
10
|
+
var _documentCurrentScript = typeof document !== "undefined" ? document.currentScript : null;
|
|
11
|
+
const __filename$1 = url.fileURLToPath(typeof document === "undefined" ? require("url").pathToFileURL(__filename).href : _documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === "SCRIPT" && _documentCurrentScript.src || new URL("diff-B1c8BBVB.cjs", document.baseURI).href);
|
|
12
|
+
const __dirname$1 = path.dirname(__filename$1);
|
|
13
|
+
async function validateCommand(file, options) {
|
|
14
|
+
var _a;
|
|
15
|
+
const spinner = ora("Loading registry...").start();
|
|
16
|
+
try {
|
|
17
|
+
const registryPath = path.resolve(process.cwd(), file);
|
|
18
|
+
const registryContent = fs.readFileSync(registryPath, "utf-8");
|
|
19
|
+
const registry = JSON.parse(registryContent);
|
|
20
|
+
spinner.text = "Validating with Zod schema...";
|
|
21
|
+
const zodResult = mcpUiSpec.ComponentRegistrySchema.safeParse(registry);
|
|
22
|
+
if (!zodResult.success) {
|
|
23
|
+
spinner.fail("Zod validation failed");
|
|
24
|
+
console.error(chalk.red("\nValidation Errors:"));
|
|
25
|
+
zodResult.error.errors.forEach((err) => {
|
|
26
|
+
console.error(chalk.red(` • ${err.path.join(".")}: ${err.message}`));
|
|
27
|
+
});
|
|
28
|
+
process.exit(1);
|
|
29
|
+
}
|
|
30
|
+
spinner.text = "Validating with JSON Schema...";
|
|
31
|
+
const schemaPath = path.join(
|
|
32
|
+
__dirname$1,
|
|
33
|
+
"../../node_modules/@seed-ship/mcp-ui-spec/schemas/component-registry-v1.json"
|
|
34
|
+
);
|
|
35
|
+
let jsonSchemaContent;
|
|
36
|
+
try {
|
|
37
|
+
jsonSchemaContent = fs.readFileSync(schemaPath, "utf-8");
|
|
38
|
+
} catch {
|
|
39
|
+
const localSchemaPath = path.join(
|
|
40
|
+
__dirname$1,
|
|
41
|
+
"../../../mcp-ui-spec/schemas/component-registry-v1.json"
|
|
42
|
+
);
|
|
43
|
+
jsonSchemaContent = fs.readFileSync(localSchemaPath, "utf-8");
|
|
44
|
+
}
|
|
45
|
+
const jsonSchema = JSON.parse(jsonSchemaContent);
|
|
46
|
+
const ajv = new Ajv({
|
|
47
|
+
allErrors: options.strict,
|
|
48
|
+
verbose: options.verbose,
|
|
49
|
+
strict: options.strict
|
|
50
|
+
});
|
|
51
|
+
const validate = ajv.compile(jsonSchema);
|
|
52
|
+
const jsonValid = validate(registry);
|
|
53
|
+
if (!jsonValid && validate.errors) {
|
|
54
|
+
spinner.fail("JSON Schema validation failed");
|
|
55
|
+
console.error(chalk.red("\nValidation Errors:"));
|
|
56
|
+
validate.errors.forEach((err) => {
|
|
57
|
+
const path2 = err.instancePath || "root";
|
|
58
|
+
console.error(chalk.red(` • ${path2}: ${err.message}`));
|
|
59
|
+
if (options.verbose && err.params) {
|
|
60
|
+
console.error(chalk.gray(` Params: ${JSON.stringify(err.params)}`));
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
process.exit(1);
|
|
64
|
+
}
|
|
65
|
+
spinner.succeed(chalk.green("Registry is valid!"));
|
|
66
|
+
if (options.verbose) {
|
|
67
|
+
console.log(chalk.gray("\nRegistry Summary:"));
|
|
68
|
+
console.log(chalk.gray(` Version: ${registry.version}`));
|
|
69
|
+
console.log(chalk.gray(` Components: ${registry.components.length}`));
|
|
70
|
+
if ((_a = registry.metadata) == null ? void 0 : _a.name) {
|
|
71
|
+
console.log(chalk.gray(` Name: ${registry.metadata.name}`));
|
|
72
|
+
}
|
|
73
|
+
console.log(chalk.gray("\nComponents:"));
|
|
74
|
+
const validRegistry = registry;
|
|
75
|
+
validRegistry.components.forEach((comp) => {
|
|
76
|
+
console.log(chalk.gray(` • ${comp.id} (${comp.type}): ${comp.examples.length} example(s)`));
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
process.exit(0);
|
|
80
|
+
} catch (error) {
|
|
81
|
+
spinner.fail("Validation failed");
|
|
82
|
+
if (error instanceof Error) {
|
|
83
|
+
if (error.message.includes("ENOENT")) {
|
|
84
|
+
console.error(chalk.red(`
|
|
85
|
+
File not found: ${file}`));
|
|
86
|
+
} else if (error instanceof SyntaxError) {
|
|
87
|
+
console.error(chalk.red("\nInvalid JSON syntax"));
|
|
88
|
+
console.error(chalk.gray(error.message));
|
|
89
|
+
} else {
|
|
90
|
+
console.error(chalk.red("\nUnexpected error:"));
|
|
91
|
+
console.error(chalk.gray(error.message));
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
process.exit(1);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
async function generateTypesCommand(input, output, options) {
|
|
98
|
+
const spinner = ora("Loading registry...").start();
|
|
99
|
+
try {
|
|
100
|
+
const registryPath = path.resolve(process.cwd(), input);
|
|
101
|
+
const registryContent = fs.readFileSync(registryPath, "utf-8");
|
|
102
|
+
const registry = JSON.parse(registryContent);
|
|
103
|
+
spinner.text = "Generating TypeScript types...";
|
|
104
|
+
const typeDefinitions = [];
|
|
105
|
+
typeDefinitions.push("/**");
|
|
106
|
+
typeDefinitions.push(" * Auto-generated TypeScript types from component registry");
|
|
107
|
+
typeDefinitions.push(" * Generated by @seed-ship/mcp-ui-cli");
|
|
108
|
+
typeDefinitions.push(" */");
|
|
109
|
+
typeDefinitions.push("");
|
|
110
|
+
if (options.namespace) {
|
|
111
|
+
typeDefinitions.push(`export namespace ${options.namespace} {`);
|
|
112
|
+
}
|
|
113
|
+
for (const component of registry.components) {
|
|
114
|
+
spinner.text = `Generating types for ${component.id}...`;
|
|
115
|
+
const typeName = componentIdToTypeName(component.id);
|
|
116
|
+
try {
|
|
117
|
+
const ts = await jsonSchemaToTypescript.compile(component.schema, typeName, {
|
|
118
|
+
bannerComment: `/* ${component.name} - ${component.description || "No description"} */`,
|
|
119
|
+
style: {
|
|
120
|
+
semi: false,
|
|
121
|
+
singleQuote: true
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
if (options.namespace) {
|
|
125
|
+
const indented = ts.split("\n").map((line) => line ? ` ${line}` : line).join("\n");
|
|
126
|
+
typeDefinitions.push(indented);
|
|
127
|
+
} else {
|
|
128
|
+
typeDefinitions.push(ts);
|
|
129
|
+
}
|
|
130
|
+
typeDefinitions.push("");
|
|
131
|
+
} catch (error) {
|
|
132
|
+
spinner.warn(`Skipping ${component.id}: type generation failed`);
|
|
133
|
+
if (error instanceof Error) {
|
|
134
|
+
console.error(chalk.yellow(` ${error.message}`));
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
if (options.namespace) {
|
|
139
|
+
typeDefinitions.push("}");
|
|
140
|
+
}
|
|
141
|
+
const generatedCode = typeDefinitions.join("\n");
|
|
142
|
+
spinner.succeed("Type generation complete");
|
|
143
|
+
if (output) {
|
|
144
|
+
const outputPath = path.resolve(process.cwd(), output);
|
|
145
|
+
fs.writeFileSync(outputPath, generatedCode, "utf-8");
|
|
146
|
+
console.log(chalk.green(`✓ Types written to ${output}`));
|
|
147
|
+
} else {
|
|
148
|
+
console.log("\n" + generatedCode);
|
|
149
|
+
}
|
|
150
|
+
process.exit(0);
|
|
151
|
+
} catch (error) {
|
|
152
|
+
spinner.fail("Type generation failed");
|
|
153
|
+
if (error instanceof Error) {
|
|
154
|
+
if (error.message.includes("ENOENT")) {
|
|
155
|
+
console.error(chalk.red(`
|
|
156
|
+
File not found: ${input}`));
|
|
157
|
+
} else {
|
|
158
|
+
console.error(chalk.red("\nUnexpected error:"));
|
|
159
|
+
console.error(chalk.gray(error.message));
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
process.exit(1);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
function componentIdToTypeName(id) {
|
|
166
|
+
return id.split("-").map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("");
|
|
167
|
+
}
|
|
168
|
+
async function testExamplesCommand(file, options) {
|
|
169
|
+
const spinner = ora("Loading registry...").start();
|
|
170
|
+
try {
|
|
171
|
+
const registryPath = path.resolve(process.cwd(), file);
|
|
172
|
+
const registryContent = fs.readFileSync(registryPath, "utf-8");
|
|
173
|
+
const registry = JSON.parse(registryContent);
|
|
174
|
+
const componentsToTest = options.component ? registry.components.filter((c) => c.id === options.component) : registry.components;
|
|
175
|
+
if (componentsToTest.length === 0) {
|
|
176
|
+
spinner.fail(`Component not found: ${options.component}`);
|
|
177
|
+
process.exit(1);
|
|
178
|
+
}
|
|
179
|
+
spinner.text = "Running example tests...";
|
|
180
|
+
const results = [];
|
|
181
|
+
let totalExamples = 0;
|
|
182
|
+
let passedExamples = 0;
|
|
183
|
+
for (const component of componentsToTest) {
|
|
184
|
+
spinner.text = `Testing examples for ${component.id}...`;
|
|
185
|
+
const ajv = new Ajv({ allErrors: true, verbose: options.verbose });
|
|
186
|
+
const validate = ajv.compile(component.schema);
|
|
187
|
+
for (const example of component.examples) {
|
|
188
|
+
totalExamples++;
|
|
189
|
+
const valid = validate(example.params);
|
|
190
|
+
const result = {
|
|
191
|
+
componentId: component.id,
|
|
192
|
+
exampleName: example.name,
|
|
193
|
+
passed: valid
|
|
194
|
+
};
|
|
195
|
+
if (!valid && validate.errors) {
|
|
196
|
+
result.errors = validate.errors.map((err) => {
|
|
197
|
+
const path2 = err.instancePath || "root";
|
|
198
|
+
return `${path2}: ${err.message}`;
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
if (valid) {
|
|
202
|
+
passedExamples++;
|
|
203
|
+
}
|
|
204
|
+
results.push(result);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
spinner.stop();
|
|
208
|
+
console.log(chalk.bold("\nTest Results:\n"));
|
|
209
|
+
let hasFailures = false;
|
|
210
|
+
for (const result of results) {
|
|
211
|
+
const status = result.passed ? chalk.green("✓") : chalk.red("✗");
|
|
212
|
+
const componentLabel = chalk.cyan(result.componentId);
|
|
213
|
+
const exampleLabel = chalk.gray(result.exampleName);
|
|
214
|
+
console.log(`${status} ${componentLabel} › ${exampleLabel}`);
|
|
215
|
+
if (!result.passed && result.errors) {
|
|
216
|
+
hasFailures = true;
|
|
217
|
+
result.errors.forEach((error) => {
|
|
218
|
+
console.log(chalk.red(` ${error}`));
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
console.log("");
|
|
223
|
+
console.log(chalk.bold("Summary:"));
|
|
224
|
+
console.log(` ${chalk.green(`${passedExamples} passed`)} / ${totalExamples} total`);
|
|
225
|
+
if (hasFailures) {
|
|
226
|
+
console.log(chalk.red(` ${totalExamples - passedExamples} failed`));
|
|
227
|
+
process.exit(1);
|
|
228
|
+
}
|
|
229
|
+
console.log(chalk.green("\n✓ All examples are valid!"));
|
|
230
|
+
process.exit(0);
|
|
231
|
+
} catch (error) {
|
|
232
|
+
spinner.fail("Example testing failed");
|
|
233
|
+
if (error instanceof Error) {
|
|
234
|
+
if (error.message.includes("ENOENT")) {
|
|
235
|
+
console.error(chalk.red(`
|
|
236
|
+
File not found: ${file}`));
|
|
237
|
+
} else if (error instanceof SyntaxError) {
|
|
238
|
+
console.error(chalk.red("\nInvalid JSON syntax"));
|
|
239
|
+
console.error(chalk.gray(error.message));
|
|
240
|
+
} else {
|
|
241
|
+
console.error(chalk.red("\nUnexpected error:"));
|
|
242
|
+
console.error(chalk.gray(error.message));
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
process.exit(1);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
async function diffCommand(oldFile, newFile, options) {
|
|
249
|
+
const spinner = ora("Loading registries...").start();
|
|
250
|
+
try {
|
|
251
|
+
const oldPath = path.resolve(process.cwd(), oldFile);
|
|
252
|
+
const newPath = path.resolve(process.cwd(), newFile);
|
|
253
|
+
const oldRegistry = JSON.parse(fs.readFileSync(oldPath, "utf-8"));
|
|
254
|
+
const newRegistry = JSON.parse(fs.readFileSync(newPath, "utf-8"));
|
|
255
|
+
spinner.text = "Comparing registries...";
|
|
256
|
+
const changes = [];
|
|
257
|
+
const oldComponents = new Map(oldRegistry.components.map((c) => [c.id, c]));
|
|
258
|
+
const newComponents = new Map(newRegistry.components.map((c) => [c.id, c]));
|
|
259
|
+
for (const [id] of oldComponents) {
|
|
260
|
+
if (!newComponents.has(id)) {
|
|
261
|
+
changes.push({
|
|
262
|
+
type: "removed",
|
|
263
|
+
breaking: true,
|
|
264
|
+
componentId: id,
|
|
265
|
+
message: `Component '${id}' was removed`
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
for (const [id] of newComponents) {
|
|
270
|
+
if (!oldComponents.has(id)) {
|
|
271
|
+
changes.push({
|
|
272
|
+
type: "added",
|
|
273
|
+
breaking: false,
|
|
274
|
+
componentId: id,
|
|
275
|
+
message: `Component '${id}' was added`
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
for (const [id, newComponent] of newComponents) {
|
|
280
|
+
const oldComponent = oldComponents.get(id);
|
|
281
|
+
if (!oldComponent) continue;
|
|
282
|
+
if (!oldComponent.deprecated && newComponent.deprecated) {
|
|
283
|
+
changes.push({
|
|
284
|
+
type: "deprecated",
|
|
285
|
+
breaking: false,
|
|
286
|
+
componentId: id,
|
|
287
|
+
message: `Component '${id}' was deprecated${newComponent.deprecationMessage ? `: ${newComponent.deprecationMessage}` : ""}`
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
if (oldComponent.type !== newComponent.type) {
|
|
291
|
+
changes.push({
|
|
292
|
+
type: "modified",
|
|
293
|
+
breaking: true,
|
|
294
|
+
componentId: id,
|
|
295
|
+
field: "type",
|
|
296
|
+
oldValue: oldComponent.type,
|
|
297
|
+
newValue: newComponent.type,
|
|
298
|
+
message: `Component '${id}' type changed from '${oldComponent.type}' to '${newComponent.type}'`
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
const oldRequired = new Set(oldComponent.schema.required || []);
|
|
302
|
+
const newRequired = new Set(newComponent.schema.required || []);
|
|
303
|
+
for (const field of newRequired) {
|
|
304
|
+
if (!oldRequired.has(field)) {
|
|
305
|
+
changes.push({
|
|
306
|
+
type: "modified",
|
|
307
|
+
breaking: true,
|
|
308
|
+
componentId: id,
|
|
309
|
+
field: `schema.required`,
|
|
310
|
+
message: `Component '${id}' now requires field '${field}'`
|
|
311
|
+
});
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
for (const field of oldRequired) {
|
|
315
|
+
if (!newRequired.has(field)) {
|
|
316
|
+
changes.push({
|
|
317
|
+
type: "modified",
|
|
318
|
+
breaking: false,
|
|
319
|
+
componentId: id,
|
|
320
|
+
field: `schema.required`,
|
|
321
|
+
message: `Component '${id}' no longer requires field '${field}'`
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
if (oldComponent.version !== newComponent.version) {
|
|
326
|
+
const oldVersion = oldComponent.version || "0.0.0";
|
|
327
|
+
const newVersion = newComponent.version || "0.0.0";
|
|
328
|
+
const breaking = isMajorVersionChange(oldVersion, newVersion);
|
|
329
|
+
changes.push({
|
|
330
|
+
type: "modified",
|
|
331
|
+
breaking,
|
|
332
|
+
componentId: id,
|
|
333
|
+
field: "version",
|
|
334
|
+
oldValue: oldVersion,
|
|
335
|
+
newValue: newVersion,
|
|
336
|
+
message: `Component '${id}' version changed from ${oldVersion} to ${newVersion}`
|
|
337
|
+
});
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
spinner.stop();
|
|
341
|
+
if (options.json) {
|
|
342
|
+
console.log(JSON.stringify({ changes }, null, 2));
|
|
343
|
+
} else {
|
|
344
|
+
displayHumanReadableDiff(changes);
|
|
345
|
+
}
|
|
346
|
+
const hasBreakingChanges = changes.some((c) => c.breaking);
|
|
347
|
+
if (options.failOnBreaking && hasBreakingChanges) {
|
|
348
|
+
console.error(chalk.red("\n✗ Breaking changes detected!"));
|
|
349
|
+
process.exit(1);
|
|
350
|
+
}
|
|
351
|
+
process.exit(0);
|
|
352
|
+
} catch (error) {
|
|
353
|
+
spinner.fail("Diff failed");
|
|
354
|
+
if (error instanceof Error) {
|
|
355
|
+
if (error.message.includes("ENOENT")) {
|
|
356
|
+
console.error(chalk.red(`
|
|
357
|
+
File not found`));
|
|
358
|
+
} else {
|
|
359
|
+
console.error(chalk.red("\nUnexpected error:"));
|
|
360
|
+
console.error(chalk.gray(error.message));
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
process.exit(1);
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
function displayHumanReadableDiff(changes) {
|
|
367
|
+
console.log(chalk.bold("\nRegistry Changes:\n"));
|
|
368
|
+
if (changes.length === 0) {
|
|
369
|
+
console.log(chalk.gray("No changes detected"));
|
|
370
|
+
return;
|
|
371
|
+
}
|
|
372
|
+
const breakingChanges = changes.filter((c) => c.breaking);
|
|
373
|
+
const nonBreakingChanges = changes.filter((c) => !c.breaking);
|
|
374
|
+
if (breakingChanges.length > 0) {
|
|
375
|
+
console.log(chalk.red.bold("Breaking Changes:"));
|
|
376
|
+
for (const change of breakingChanges) {
|
|
377
|
+
const icon = getChangeIcon(change.type);
|
|
378
|
+
console.log(chalk.red(` ${icon} ${change.message}`));
|
|
379
|
+
}
|
|
380
|
+
console.log("");
|
|
381
|
+
}
|
|
382
|
+
if (nonBreakingChanges.length > 0) {
|
|
383
|
+
console.log(chalk.yellow.bold("Non-Breaking Changes:"));
|
|
384
|
+
for (const change of nonBreakingChanges) {
|
|
385
|
+
const icon = getChangeIcon(change.type);
|
|
386
|
+
console.log(chalk.yellow(` ${icon} ${change.message}`));
|
|
387
|
+
}
|
|
388
|
+
console.log("");
|
|
389
|
+
}
|
|
390
|
+
console.log(chalk.bold("Summary:"));
|
|
391
|
+
console.log(` Total changes: ${changes.length}`);
|
|
392
|
+
console.log(
|
|
393
|
+
` Breaking: ${breakingChanges.length > 0 ? chalk.red(breakingChanges.length) : chalk.green("0")}`
|
|
394
|
+
);
|
|
395
|
+
console.log(` Non-breaking: ${nonBreakingChanges.length}`);
|
|
396
|
+
}
|
|
397
|
+
function getChangeIcon(type) {
|
|
398
|
+
switch (type) {
|
|
399
|
+
case "added":
|
|
400
|
+
return "+";
|
|
401
|
+
case "removed":
|
|
402
|
+
return "-";
|
|
403
|
+
case "modified":
|
|
404
|
+
return "~";
|
|
405
|
+
case "deprecated":
|
|
406
|
+
return "⚠";
|
|
407
|
+
default:
|
|
408
|
+
return "•";
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
function isMajorVersionChange(oldVersion, newVersion) {
|
|
412
|
+
const oldMajor = parseInt(oldVersion.split(".")[0], 10);
|
|
413
|
+
const newMajor = parseInt(newVersion.split(".")[0], 10);
|
|
414
|
+
return newMajor > oldMajor;
|
|
415
|
+
}
|
|
416
|
+
exports.diffCommand = diffCommand;
|
|
417
|
+
exports.generateTypesCommand = generateTypesCommand;
|
|
418
|
+
exports.testExamplesCommand = testExamplesCommand;
|
|
419
|
+
exports.validateCommand = validateCommand;
|
|
420
|
+
//# sourceMappingURL=diff-B1c8BBVB.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"diff-B1c8BBVB.cjs","sources":["../src/commands/validate.ts","../src/commands/generate-types.ts","../src/commands/test-examples.ts","../src/commands/diff.ts"],"sourcesContent":["/**\n * Validate command - validates component registry against JSON Schema\n */\n\nimport { readFileSync } from 'fs'\nimport { resolve } from 'path'\nimport Ajv from 'ajv'\nimport chalk from 'chalk'\nimport ora from 'ora'\nimport { ComponentRegistrySchema, type ComponentRegistry } from '@seed-ship/mcp-ui-spec'\n\n// Load JSON Schema from @seed-ship/mcp-ui-spec package\nimport { fileURLToPath } from 'url'\nimport { dirname, join } from 'path'\n\nconst __filename = fileURLToPath(import.meta.url)\nconst __dirname = dirname(__filename)\n\ninterface ValidateOptions {\n strict?: boolean\n verbose?: boolean\n}\n\nexport async function validateCommand(file: string, options: ValidateOptions) {\n const spinner = ora('Loading registry...').start()\n\n try {\n // Read registry file\n const registryPath = resolve(process.cwd(), file)\n const registryContent = readFileSync(registryPath, 'utf-8')\n const registry = JSON.parse(registryContent)\n\n spinner.text = 'Validating with Zod schema...'\n\n // First validate with Zod (runtime validation)\n const zodResult = ComponentRegistrySchema.safeParse(registry)\n\n if (!zodResult.success) {\n spinner.fail('Zod validation failed')\n console.error(chalk.red('\\nValidation Errors:'))\n zodResult.error.errors.forEach((err) => {\n console.error(chalk.red(` • ${err.path.join('.')}: ${err.message}`))\n })\n process.exit(1)\n }\n\n spinner.text = 'Validating with JSON Schema...'\n\n // Load JSON Schema for secondary validation\n const schemaPath = join(\n __dirname,\n '../../node_modules/@seed-ship/mcp-ui-spec/schemas/component-registry-v1.json'\n )\n let jsonSchemaContent: string\n\n try {\n jsonSchemaContent = readFileSync(schemaPath, 'utf-8')\n } catch {\n // Fallback to local schema if node_modules path fails\n const localSchemaPath = join(\n __dirname,\n '../../../mcp-ui-spec/schemas/component-registry-v1.json'\n )\n jsonSchemaContent = readFileSync(localSchemaPath, 'utf-8')\n }\n\n const jsonSchema = JSON.parse(jsonSchemaContent)\n\n // Validate with JSON Schema using Ajv\n const ajv = new Ajv({\n allErrors: options.strict,\n verbose: options.verbose,\n strict: options.strict,\n })\n\n const validate = ajv.compile(jsonSchema)\n const jsonValid = validate(registry)\n\n if (!jsonValid && validate.errors) {\n spinner.fail('JSON Schema validation failed')\n console.error(chalk.red('\\nValidation Errors:'))\n validate.errors.forEach((err) => {\n const path = err.instancePath || 'root'\n console.error(chalk.red(` • ${path}: ${err.message}`))\n if (options.verbose && err.params) {\n console.error(chalk.gray(` Params: ${JSON.stringify(err.params)}`))\n }\n })\n process.exit(1)\n }\n\n spinner.succeed(chalk.green('Registry is valid!'))\n\n // Summary\n if (options.verbose) {\n console.log(chalk.gray('\\nRegistry Summary:'))\n console.log(chalk.gray(` Version: ${registry.version}`))\n console.log(chalk.gray(` Components: ${registry.components.length}`))\n\n if (registry.metadata?.name) {\n console.log(chalk.gray(` Name: ${registry.metadata.name}`))\n }\n\n console.log(chalk.gray('\\nComponents:'))\n const validRegistry = registry as ComponentRegistry\n validRegistry.components.forEach((comp) => {\n console.log(chalk.gray(` • ${comp.id} (${comp.type}): ${comp.examples.length} example(s)`))\n })\n }\n\n process.exit(0)\n } catch (error) {\n spinner.fail('Validation failed')\n\n if (error instanceof Error) {\n if (error.message.includes('ENOENT')) {\n console.error(chalk.red(`\\nFile not found: ${file}`))\n } else if (error instanceof SyntaxError) {\n console.error(chalk.red('\\nInvalid JSON syntax'))\n console.error(chalk.gray(error.message))\n } else {\n console.error(chalk.red('\\nUnexpected error:'))\n console.error(chalk.gray(error.message))\n }\n }\n\n process.exit(1)\n }\n}\n","/**\n * Generate Types command - generates TypeScript types from registry\n */\n\nimport { readFileSync, writeFileSync } from 'fs'\nimport { resolve } from 'path'\nimport { compile } from 'json-schema-to-typescript'\nimport chalk from 'chalk'\nimport ora from 'ora'\nimport type { Component, ComponentRegistry } from '@seed-ship/mcp-ui-spec'\n\ninterface GenerateTypesOptions {\n namespace?: string\n exportAll?: boolean\n}\n\nexport async function generateTypesCommand(\n input: string,\n output: string | undefined,\n options: GenerateTypesOptions\n) {\n const spinner = ora('Loading registry...').start()\n\n try {\n // Read registry file\n const registryPath = resolve(process.cwd(), input)\n const registryContent = readFileSync(registryPath, 'utf-8')\n const registry: ComponentRegistry = JSON.parse(registryContent)\n\n spinner.text = 'Generating TypeScript types...'\n\n // Generate types for each component\n const typeDefinitions: string[] = []\n\n // Add header comment\n typeDefinitions.push('/**')\n typeDefinitions.push(' * Auto-generated TypeScript types from component registry')\n typeDefinitions.push(' * Generated by @seed-ship/mcp-ui-cli')\n typeDefinitions.push(' */')\n typeDefinitions.push('')\n\n if (options.namespace) {\n typeDefinitions.push(`export namespace ${options.namespace} {`)\n }\n\n // Generate types for each component\n for (const component of registry.components as Component[]) {\n spinner.text = `Generating types for ${component.id}...`\n\n // Generate interface name from component ID\n const typeName = componentIdToTypeName(component.id)\n\n try {\n const ts = await compile(component.schema as any, typeName, {\n bannerComment: `/* ${component.name} - ${component.description || 'No description'} */`,\n style: {\n semi: false,\n singleQuote: true,\n },\n })\n\n // Add the generated type\n if (options.namespace) {\n // Indent for namespace\n const indented = ts\n .split('\\n')\n .map((line) => (line ? ` ${line}` : line))\n .join('\\n')\n typeDefinitions.push(indented)\n } else {\n typeDefinitions.push(ts)\n }\n\n typeDefinitions.push('')\n } catch (error) {\n spinner.warn(`Skipping ${component.id}: type generation failed`)\n if (error instanceof Error) {\n console.error(chalk.yellow(` ${error.message}`))\n }\n }\n }\n\n if (options.namespace) {\n typeDefinitions.push('}')\n }\n\n const generatedCode = typeDefinitions.join('\\n')\n\n spinner.succeed('Type generation complete')\n\n // Write to file or stdout\n if (output) {\n const outputPath = resolve(process.cwd(), output)\n writeFileSync(outputPath, generatedCode, 'utf-8')\n console.log(chalk.green(`✓ Types written to ${output}`))\n } else {\n console.log('\\n' + generatedCode)\n }\n\n process.exit(0)\n } catch (error) {\n spinner.fail('Type generation failed')\n\n if (error instanceof Error) {\n if (error.message.includes('ENOENT')) {\n console.error(chalk.red(`\\nFile not found: ${input}`))\n } else {\n console.error(chalk.red('\\nUnexpected error:'))\n console.error(chalk.gray(error.message))\n }\n }\n\n process.exit(1)\n }\n}\n\n/**\n * Convert component ID to TypeScript type name\n * Examples:\n * quickchart-bar -> QuickchartBar\n * metric-card -> MetricCard\n * data-table -> DataTable\n */\nfunction componentIdToTypeName(id: string): string {\n return id\n .split('-')\n .map((part) => part.charAt(0).toUpperCase() + part.slice(1))\n .join('')\n}\n","/**\n * Test Examples command - validates all component examples\n */\n\nimport { readFileSync } from 'fs'\nimport { resolve } from 'path'\nimport Ajv from 'ajv'\nimport chalk from 'chalk'\nimport ora from 'ora'\nimport type { ComponentRegistry } from '@seed-ship/mcp-ui-spec'\n\ninterface TestExamplesOptions {\n component?: string\n verbose?: boolean\n}\n\ninterface TestResult {\n componentId: string\n exampleName: string\n passed: boolean\n errors?: string[]\n}\n\nexport async function testExamplesCommand(file: string, options: TestExamplesOptions) {\n const spinner = ora('Loading registry...').start()\n\n try {\n // Read registry file\n const registryPath = resolve(process.cwd(), file)\n const registryContent = readFileSync(registryPath, 'utf-8')\n const registry: ComponentRegistry = JSON.parse(registryContent)\n\n // Filter components if specific component requested\n const componentsToTest = options.component\n ? registry.components.filter((c) => c.id === options.component)\n : registry.components\n\n if (componentsToTest.length === 0) {\n spinner.fail(`Component not found: ${options.component}`)\n process.exit(1)\n }\n\n spinner.text = 'Running example tests...'\n\n const results: TestResult[] = []\n let totalExamples = 0\n let passedExamples = 0\n\n // Test each component's examples\n for (const component of componentsToTest) {\n spinner.text = `Testing examples for ${component.id}...`\n\n // Create Ajv validator for this component's schema\n const ajv = new Ajv({ allErrors: true, verbose: options.verbose })\n const validate = ajv.compile(component.schema as any)\n\n // Test each example\n for (const example of component.examples) {\n totalExamples++\n\n const valid = validate(example.params)\n const result: TestResult = {\n componentId: component.id,\n exampleName: example.name,\n passed: valid,\n }\n\n if (!valid && validate.errors) {\n result.errors = validate.errors.map((err) => {\n const path = err.instancePath || 'root'\n return `${path}: ${err.message}`\n })\n }\n\n if (valid) {\n passedExamples++\n }\n\n results.push(result)\n }\n }\n\n spinner.stop()\n\n // Display results\n console.log(chalk.bold('\\nTest Results:\\n'))\n\n let hasFailures = false\n\n for (const result of results) {\n const status = result.passed ? chalk.green('✓') : chalk.red('✗')\n const componentLabel = chalk.cyan(result.componentId)\n const exampleLabel = chalk.gray(result.exampleName)\n\n console.log(`${status} ${componentLabel} › ${exampleLabel}`)\n\n if (!result.passed && result.errors) {\n hasFailures = true\n result.errors.forEach((error) => {\n console.log(chalk.red(` ${error}`))\n })\n }\n }\n\n // Summary\n console.log('')\n console.log(chalk.bold('Summary:'))\n console.log(` ${chalk.green(`${passedExamples} passed`)} / ${totalExamples} total`)\n\n if (hasFailures) {\n console.log(chalk.red(` ${totalExamples - passedExamples} failed`))\n process.exit(1)\n }\n\n console.log(chalk.green('\\n✓ All examples are valid!'))\n process.exit(0)\n } catch (error) {\n spinner.fail('Example testing failed')\n\n if (error instanceof Error) {\n if (error.message.includes('ENOENT')) {\n console.error(chalk.red(`\\nFile not found: ${file}`))\n } else if (error instanceof SyntaxError) {\n console.error(chalk.red('\\nInvalid JSON syntax'))\n console.error(chalk.gray(error.message))\n } else {\n console.error(chalk.red('\\nUnexpected error:'))\n console.error(chalk.gray(error.message))\n }\n }\n\n process.exit(1)\n }\n}\n","/**\n * Diff command - compares two registry versions for breaking changes\n */\n\nimport { readFileSync } from 'fs'\nimport { resolve } from 'path'\nimport chalk from 'chalk'\nimport ora from 'ora'\nimport type { ComponentRegistry } from '@seed-ship/mcp-ui-spec'\n\ninterface DiffOptions {\n json?: boolean\n failOnBreaking?: boolean\n}\n\ninterface ChangeType {\n type: 'added' | 'removed' | 'modified' | 'deprecated'\n breaking: boolean\n componentId: string\n field?: string\n oldValue?: unknown\n newValue?: unknown\n message: string\n}\n\nexport async function diffCommand(oldFile: string, newFile: string, options: DiffOptions) {\n const spinner = ora('Loading registries...').start()\n\n try {\n // Read both registry files\n const oldPath = resolve(process.cwd(), oldFile)\n const newPath = resolve(process.cwd(), newFile)\n\n const oldRegistry: ComponentRegistry = JSON.parse(readFileSync(oldPath, 'utf-8'))\n const newRegistry: ComponentRegistry = JSON.parse(readFileSync(newPath, 'utf-8'))\n\n spinner.text = 'Comparing registries...'\n\n const changes: ChangeType[] = []\n\n // Create maps for easier lookup\n const oldComponents = new Map(oldRegistry.components.map((c) => [c.id, c]))\n const newComponents = new Map(newRegistry.components.map((c) => [c.id, c]))\n\n // Check for removed components (breaking)\n for (const [id] of oldComponents) {\n if (!newComponents.has(id)) {\n changes.push({\n type: 'removed',\n breaking: true,\n componentId: id,\n message: `Component '${id}' was removed`,\n })\n }\n }\n\n // Check for added components (non-breaking)\n for (const [id] of newComponents) {\n if (!oldComponents.has(id)) {\n changes.push({\n type: 'added',\n breaking: false,\n componentId: id,\n message: `Component '${id}' was added`,\n })\n }\n }\n\n // Check for modified components\n for (const [id, newComponent] of newComponents) {\n const oldComponent = oldComponents.get(id)\n if (!oldComponent) continue\n\n // Check deprecation\n if (!oldComponent.deprecated && newComponent.deprecated) {\n changes.push({\n type: 'deprecated',\n breaking: false,\n componentId: id,\n message: `Component '${id}' was deprecated${\n newComponent.deprecationMessage ? `: ${newComponent.deprecationMessage}` : ''\n }`,\n })\n }\n\n // Check type change (breaking)\n if (oldComponent.type !== newComponent.type) {\n changes.push({\n type: 'modified',\n breaking: true,\n componentId: id,\n field: 'type',\n oldValue: oldComponent.type,\n newValue: newComponent.type,\n message: `Component '${id}' type changed from '${oldComponent.type}' to '${newComponent.type}'`,\n })\n }\n\n // Check required fields change (breaking)\n const oldRequired = new Set(oldComponent.schema.required || [])\n const newRequired = new Set(newComponent.schema.required || [])\n\n for (const field of newRequired) {\n if (!oldRequired.has(field)) {\n changes.push({\n type: 'modified',\n breaking: true,\n componentId: id,\n field: `schema.required`,\n message: `Component '${id}' now requires field '${field}'`,\n })\n }\n }\n\n for (const field of oldRequired) {\n if (!newRequired.has(field)) {\n changes.push({\n type: 'modified',\n breaking: false,\n componentId: id,\n field: `schema.required`,\n message: `Component '${id}' no longer requires field '${field}'`,\n })\n }\n }\n\n // Check version change\n if (oldComponent.version !== newComponent.version) {\n const oldVersion = oldComponent.version || '0.0.0'\n const newVersion = newComponent.version || '0.0.0'\n\n const breaking = isMajorVersionChange(oldVersion, newVersion)\n\n changes.push({\n type: 'modified',\n breaking,\n componentId: id,\n field: 'version',\n oldValue: oldVersion,\n newValue: newVersion,\n message: `Component '${id}' version changed from ${oldVersion} to ${newVersion}`,\n })\n }\n }\n\n spinner.stop()\n\n // Output results\n if (options.json) {\n console.log(JSON.stringify({ changes }, null, 2))\n } else {\n displayHumanReadableDiff(changes)\n }\n\n // Check if we should fail on breaking changes\n const hasBreakingChanges = changes.some((c) => c.breaking)\n if (options.failOnBreaking && hasBreakingChanges) {\n console.error(chalk.red('\\n✗ Breaking changes detected!'))\n process.exit(1)\n }\n\n process.exit(0)\n } catch (error) {\n spinner.fail('Diff failed')\n\n if (error instanceof Error) {\n if (error.message.includes('ENOENT')) {\n console.error(chalk.red(`\\nFile not found`))\n } else {\n console.error(chalk.red('\\nUnexpected error:'))\n console.error(chalk.gray(error.message))\n }\n }\n\n process.exit(1)\n }\n}\n\nfunction displayHumanReadableDiff(changes: ChangeType[]) {\n console.log(chalk.bold('\\nRegistry Changes:\\n'))\n\n if (changes.length === 0) {\n console.log(chalk.gray('No changes detected'))\n return\n }\n\n const breakingChanges = changes.filter((c) => c.breaking)\n const nonBreakingChanges = changes.filter((c) => !c.breaking)\n\n if (breakingChanges.length > 0) {\n console.log(chalk.red.bold('Breaking Changes:'))\n for (const change of breakingChanges) {\n const icon = getChangeIcon(change.type)\n console.log(chalk.red(` ${icon} ${change.message}`))\n }\n console.log('')\n }\n\n if (nonBreakingChanges.length > 0) {\n console.log(chalk.yellow.bold('Non-Breaking Changes:'))\n for (const change of nonBreakingChanges) {\n const icon = getChangeIcon(change.type)\n console.log(chalk.yellow(` ${icon} ${change.message}`))\n }\n console.log('')\n }\n\n // Summary\n console.log(chalk.bold('Summary:'))\n console.log(` Total changes: ${changes.length}`)\n console.log(\n ` Breaking: ${breakingChanges.length > 0 ? chalk.red(breakingChanges.length) : chalk.green('0')}`\n )\n console.log(` Non-breaking: ${nonBreakingChanges.length}`)\n}\n\nfunction getChangeIcon(type: ChangeType['type']): string {\n switch (type) {\n case 'added':\n return '+'\n case 'removed':\n return '-'\n case 'modified':\n return '~'\n case 'deprecated':\n return '⚠'\n default:\n return '•'\n }\n}\n\nfunction isMajorVersionChange(oldVersion: string, newVersion: string): boolean {\n const oldMajor = parseInt(oldVersion.split('.')[0], 10)\n const newMajor = parseInt(newVersion.split('.')[0], 10)\n return newMajor > oldMajor\n}\n"],"names":["__filename","fileURLToPath","__dirname","dirname","resolve","readFileSync","ComponentRegistrySchema","join","path","compile","writeFileSync"],"mappings":"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"}
|
|
@@ -0,0 +1,420 @@
|
|
|
1
|
+
import { readFileSync, writeFileSync } from "fs";
|
|
2
|
+
import { dirname, resolve, join } from "path";
|
|
3
|
+
import Ajv from "ajv";
|
|
4
|
+
import chalk from "chalk";
|
|
5
|
+
import ora from "ora";
|
|
6
|
+
import { ComponentRegistrySchema } from "@seed-ship/mcp-ui-spec";
|
|
7
|
+
import { fileURLToPath } from "url";
|
|
8
|
+
import { compile } from "json-schema-to-typescript";
|
|
9
|
+
const __filename$1 = fileURLToPath(import.meta.url);
|
|
10
|
+
const __dirname$1 = dirname(__filename$1);
|
|
11
|
+
async function validateCommand(file, options) {
|
|
12
|
+
var _a;
|
|
13
|
+
const spinner = ora("Loading registry...").start();
|
|
14
|
+
try {
|
|
15
|
+
const registryPath = resolve(process.cwd(), file);
|
|
16
|
+
const registryContent = readFileSync(registryPath, "utf-8");
|
|
17
|
+
const registry = JSON.parse(registryContent);
|
|
18
|
+
spinner.text = "Validating with Zod schema...";
|
|
19
|
+
const zodResult = ComponentRegistrySchema.safeParse(registry);
|
|
20
|
+
if (!zodResult.success) {
|
|
21
|
+
spinner.fail("Zod validation failed");
|
|
22
|
+
console.error(chalk.red("\nValidation Errors:"));
|
|
23
|
+
zodResult.error.errors.forEach((err) => {
|
|
24
|
+
console.error(chalk.red(` • ${err.path.join(".")}: ${err.message}`));
|
|
25
|
+
});
|
|
26
|
+
process.exit(1);
|
|
27
|
+
}
|
|
28
|
+
spinner.text = "Validating with JSON Schema...";
|
|
29
|
+
const schemaPath = join(
|
|
30
|
+
__dirname$1,
|
|
31
|
+
"../../node_modules/@seed-ship/mcp-ui-spec/schemas/component-registry-v1.json"
|
|
32
|
+
);
|
|
33
|
+
let jsonSchemaContent;
|
|
34
|
+
try {
|
|
35
|
+
jsonSchemaContent = readFileSync(schemaPath, "utf-8");
|
|
36
|
+
} catch {
|
|
37
|
+
const localSchemaPath = join(
|
|
38
|
+
__dirname$1,
|
|
39
|
+
"../../../mcp-ui-spec/schemas/component-registry-v1.json"
|
|
40
|
+
);
|
|
41
|
+
jsonSchemaContent = readFileSync(localSchemaPath, "utf-8");
|
|
42
|
+
}
|
|
43
|
+
const jsonSchema = JSON.parse(jsonSchemaContent);
|
|
44
|
+
const ajv = new Ajv({
|
|
45
|
+
allErrors: options.strict,
|
|
46
|
+
verbose: options.verbose,
|
|
47
|
+
strict: options.strict
|
|
48
|
+
});
|
|
49
|
+
const validate = ajv.compile(jsonSchema);
|
|
50
|
+
const jsonValid = validate(registry);
|
|
51
|
+
if (!jsonValid && validate.errors) {
|
|
52
|
+
spinner.fail("JSON Schema validation failed");
|
|
53
|
+
console.error(chalk.red("\nValidation Errors:"));
|
|
54
|
+
validate.errors.forEach((err) => {
|
|
55
|
+
const path = err.instancePath || "root";
|
|
56
|
+
console.error(chalk.red(` • ${path}: ${err.message}`));
|
|
57
|
+
if (options.verbose && err.params) {
|
|
58
|
+
console.error(chalk.gray(` Params: ${JSON.stringify(err.params)}`));
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
process.exit(1);
|
|
62
|
+
}
|
|
63
|
+
spinner.succeed(chalk.green("Registry is valid!"));
|
|
64
|
+
if (options.verbose) {
|
|
65
|
+
console.log(chalk.gray("\nRegistry Summary:"));
|
|
66
|
+
console.log(chalk.gray(` Version: ${registry.version}`));
|
|
67
|
+
console.log(chalk.gray(` Components: ${registry.components.length}`));
|
|
68
|
+
if ((_a = registry.metadata) == null ? void 0 : _a.name) {
|
|
69
|
+
console.log(chalk.gray(` Name: ${registry.metadata.name}`));
|
|
70
|
+
}
|
|
71
|
+
console.log(chalk.gray("\nComponents:"));
|
|
72
|
+
const validRegistry = registry;
|
|
73
|
+
validRegistry.components.forEach((comp) => {
|
|
74
|
+
console.log(chalk.gray(` • ${comp.id} (${comp.type}): ${comp.examples.length} example(s)`));
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
process.exit(0);
|
|
78
|
+
} catch (error) {
|
|
79
|
+
spinner.fail("Validation failed");
|
|
80
|
+
if (error instanceof Error) {
|
|
81
|
+
if (error.message.includes("ENOENT")) {
|
|
82
|
+
console.error(chalk.red(`
|
|
83
|
+
File not found: ${file}`));
|
|
84
|
+
} else if (error instanceof SyntaxError) {
|
|
85
|
+
console.error(chalk.red("\nInvalid JSON syntax"));
|
|
86
|
+
console.error(chalk.gray(error.message));
|
|
87
|
+
} else {
|
|
88
|
+
console.error(chalk.red("\nUnexpected error:"));
|
|
89
|
+
console.error(chalk.gray(error.message));
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
process.exit(1);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
async function generateTypesCommand(input, output, options) {
|
|
96
|
+
const spinner = ora("Loading registry...").start();
|
|
97
|
+
try {
|
|
98
|
+
const registryPath = resolve(process.cwd(), input);
|
|
99
|
+
const registryContent = readFileSync(registryPath, "utf-8");
|
|
100
|
+
const registry = JSON.parse(registryContent);
|
|
101
|
+
spinner.text = "Generating TypeScript types...";
|
|
102
|
+
const typeDefinitions = [];
|
|
103
|
+
typeDefinitions.push("/**");
|
|
104
|
+
typeDefinitions.push(" * Auto-generated TypeScript types from component registry");
|
|
105
|
+
typeDefinitions.push(" * Generated by @seed-ship/mcp-ui-cli");
|
|
106
|
+
typeDefinitions.push(" */");
|
|
107
|
+
typeDefinitions.push("");
|
|
108
|
+
if (options.namespace) {
|
|
109
|
+
typeDefinitions.push(`export namespace ${options.namespace} {`);
|
|
110
|
+
}
|
|
111
|
+
for (const component of registry.components) {
|
|
112
|
+
spinner.text = `Generating types for ${component.id}...`;
|
|
113
|
+
const typeName = componentIdToTypeName(component.id);
|
|
114
|
+
try {
|
|
115
|
+
const ts = await compile(component.schema, typeName, {
|
|
116
|
+
bannerComment: `/* ${component.name} - ${component.description || "No description"} */`,
|
|
117
|
+
style: {
|
|
118
|
+
semi: false,
|
|
119
|
+
singleQuote: true
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
if (options.namespace) {
|
|
123
|
+
const indented = ts.split("\n").map((line) => line ? ` ${line}` : line).join("\n");
|
|
124
|
+
typeDefinitions.push(indented);
|
|
125
|
+
} else {
|
|
126
|
+
typeDefinitions.push(ts);
|
|
127
|
+
}
|
|
128
|
+
typeDefinitions.push("");
|
|
129
|
+
} catch (error) {
|
|
130
|
+
spinner.warn(`Skipping ${component.id}: type generation failed`);
|
|
131
|
+
if (error instanceof Error) {
|
|
132
|
+
console.error(chalk.yellow(` ${error.message}`));
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
if (options.namespace) {
|
|
137
|
+
typeDefinitions.push("}");
|
|
138
|
+
}
|
|
139
|
+
const generatedCode = typeDefinitions.join("\n");
|
|
140
|
+
spinner.succeed("Type generation complete");
|
|
141
|
+
if (output) {
|
|
142
|
+
const outputPath = resolve(process.cwd(), output);
|
|
143
|
+
writeFileSync(outputPath, generatedCode, "utf-8");
|
|
144
|
+
console.log(chalk.green(`✓ Types written to ${output}`));
|
|
145
|
+
} else {
|
|
146
|
+
console.log("\n" + generatedCode);
|
|
147
|
+
}
|
|
148
|
+
process.exit(0);
|
|
149
|
+
} catch (error) {
|
|
150
|
+
spinner.fail("Type generation failed");
|
|
151
|
+
if (error instanceof Error) {
|
|
152
|
+
if (error.message.includes("ENOENT")) {
|
|
153
|
+
console.error(chalk.red(`
|
|
154
|
+
File not found: ${input}`));
|
|
155
|
+
} else {
|
|
156
|
+
console.error(chalk.red("\nUnexpected error:"));
|
|
157
|
+
console.error(chalk.gray(error.message));
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
process.exit(1);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
function componentIdToTypeName(id) {
|
|
164
|
+
return id.split("-").map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("");
|
|
165
|
+
}
|
|
166
|
+
async function testExamplesCommand(file, options) {
|
|
167
|
+
const spinner = ora("Loading registry...").start();
|
|
168
|
+
try {
|
|
169
|
+
const registryPath = resolve(process.cwd(), file);
|
|
170
|
+
const registryContent = readFileSync(registryPath, "utf-8");
|
|
171
|
+
const registry = JSON.parse(registryContent);
|
|
172
|
+
const componentsToTest = options.component ? registry.components.filter((c) => c.id === options.component) : registry.components;
|
|
173
|
+
if (componentsToTest.length === 0) {
|
|
174
|
+
spinner.fail(`Component not found: ${options.component}`);
|
|
175
|
+
process.exit(1);
|
|
176
|
+
}
|
|
177
|
+
spinner.text = "Running example tests...";
|
|
178
|
+
const results = [];
|
|
179
|
+
let totalExamples = 0;
|
|
180
|
+
let passedExamples = 0;
|
|
181
|
+
for (const component of componentsToTest) {
|
|
182
|
+
spinner.text = `Testing examples for ${component.id}...`;
|
|
183
|
+
const ajv = new Ajv({ allErrors: true, verbose: options.verbose });
|
|
184
|
+
const validate = ajv.compile(component.schema);
|
|
185
|
+
for (const example of component.examples) {
|
|
186
|
+
totalExamples++;
|
|
187
|
+
const valid = validate(example.params);
|
|
188
|
+
const result = {
|
|
189
|
+
componentId: component.id,
|
|
190
|
+
exampleName: example.name,
|
|
191
|
+
passed: valid
|
|
192
|
+
};
|
|
193
|
+
if (!valid && validate.errors) {
|
|
194
|
+
result.errors = validate.errors.map((err) => {
|
|
195
|
+
const path = err.instancePath || "root";
|
|
196
|
+
return `${path}: ${err.message}`;
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
if (valid) {
|
|
200
|
+
passedExamples++;
|
|
201
|
+
}
|
|
202
|
+
results.push(result);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
spinner.stop();
|
|
206
|
+
console.log(chalk.bold("\nTest Results:\n"));
|
|
207
|
+
let hasFailures = false;
|
|
208
|
+
for (const result of results) {
|
|
209
|
+
const status = result.passed ? chalk.green("✓") : chalk.red("✗");
|
|
210
|
+
const componentLabel = chalk.cyan(result.componentId);
|
|
211
|
+
const exampleLabel = chalk.gray(result.exampleName);
|
|
212
|
+
console.log(`${status} ${componentLabel} › ${exampleLabel}`);
|
|
213
|
+
if (!result.passed && result.errors) {
|
|
214
|
+
hasFailures = true;
|
|
215
|
+
result.errors.forEach((error) => {
|
|
216
|
+
console.log(chalk.red(` ${error}`));
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
console.log("");
|
|
221
|
+
console.log(chalk.bold("Summary:"));
|
|
222
|
+
console.log(` ${chalk.green(`${passedExamples} passed`)} / ${totalExamples} total`);
|
|
223
|
+
if (hasFailures) {
|
|
224
|
+
console.log(chalk.red(` ${totalExamples - passedExamples} failed`));
|
|
225
|
+
process.exit(1);
|
|
226
|
+
}
|
|
227
|
+
console.log(chalk.green("\n✓ All examples are valid!"));
|
|
228
|
+
process.exit(0);
|
|
229
|
+
} catch (error) {
|
|
230
|
+
spinner.fail("Example testing failed");
|
|
231
|
+
if (error instanceof Error) {
|
|
232
|
+
if (error.message.includes("ENOENT")) {
|
|
233
|
+
console.error(chalk.red(`
|
|
234
|
+
File not found: ${file}`));
|
|
235
|
+
} else if (error instanceof SyntaxError) {
|
|
236
|
+
console.error(chalk.red("\nInvalid JSON syntax"));
|
|
237
|
+
console.error(chalk.gray(error.message));
|
|
238
|
+
} else {
|
|
239
|
+
console.error(chalk.red("\nUnexpected error:"));
|
|
240
|
+
console.error(chalk.gray(error.message));
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
process.exit(1);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
async function diffCommand(oldFile, newFile, options) {
|
|
247
|
+
const spinner = ora("Loading registries...").start();
|
|
248
|
+
try {
|
|
249
|
+
const oldPath = resolve(process.cwd(), oldFile);
|
|
250
|
+
const newPath = resolve(process.cwd(), newFile);
|
|
251
|
+
const oldRegistry = JSON.parse(readFileSync(oldPath, "utf-8"));
|
|
252
|
+
const newRegistry = JSON.parse(readFileSync(newPath, "utf-8"));
|
|
253
|
+
spinner.text = "Comparing registries...";
|
|
254
|
+
const changes = [];
|
|
255
|
+
const oldComponents = new Map(oldRegistry.components.map((c) => [c.id, c]));
|
|
256
|
+
const newComponents = new Map(newRegistry.components.map((c) => [c.id, c]));
|
|
257
|
+
for (const [id] of oldComponents) {
|
|
258
|
+
if (!newComponents.has(id)) {
|
|
259
|
+
changes.push({
|
|
260
|
+
type: "removed",
|
|
261
|
+
breaking: true,
|
|
262
|
+
componentId: id,
|
|
263
|
+
message: `Component '${id}' was removed`
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
for (const [id] of newComponents) {
|
|
268
|
+
if (!oldComponents.has(id)) {
|
|
269
|
+
changes.push({
|
|
270
|
+
type: "added",
|
|
271
|
+
breaking: false,
|
|
272
|
+
componentId: id,
|
|
273
|
+
message: `Component '${id}' was added`
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
for (const [id, newComponent] of newComponents) {
|
|
278
|
+
const oldComponent = oldComponents.get(id);
|
|
279
|
+
if (!oldComponent) continue;
|
|
280
|
+
if (!oldComponent.deprecated && newComponent.deprecated) {
|
|
281
|
+
changes.push({
|
|
282
|
+
type: "deprecated",
|
|
283
|
+
breaking: false,
|
|
284
|
+
componentId: id,
|
|
285
|
+
message: `Component '${id}' was deprecated${newComponent.deprecationMessage ? `: ${newComponent.deprecationMessage}` : ""}`
|
|
286
|
+
});
|
|
287
|
+
}
|
|
288
|
+
if (oldComponent.type !== newComponent.type) {
|
|
289
|
+
changes.push({
|
|
290
|
+
type: "modified",
|
|
291
|
+
breaking: true,
|
|
292
|
+
componentId: id,
|
|
293
|
+
field: "type",
|
|
294
|
+
oldValue: oldComponent.type,
|
|
295
|
+
newValue: newComponent.type,
|
|
296
|
+
message: `Component '${id}' type changed from '${oldComponent.type}' to '${newComponent.type}'`
|
|
297
|
+
});
|
|
298
|
+
}
|
|
299
|
+
const oldRequired = new Set(oldComponent.schema.required || []);
|
|
300
|
+
const newRequired = new Set(newComponent.schema.required || []);
|
|
301
|
+
for (const field of newRequired) {
|
|
302
|
+
if (!oldRequired.has(field)) {
|
|
303
|
+
changes.push({
|
|
304
|
+
type: "modified",
|
|
305
|
+
breaking: true,
|
|
306
|
+
componentId: id,
|
|
307
|
+
field: `schema.required`,
|
|
308
|
+
message: `Component '${id}' now requires field '${field}'`
|
|
309
|
+
});
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
for (const field of oldRequired) {
|
|
313
|
+
if (!newRequired.has(field)) {
|
|
314
|
+
changes.push({
|
|
315
|
+
type: "modified",
|
|
316
|
+
breaking: false,
|
|
317
|
+
componentId: id,
|
|
318
|
+
field: `schema.required`,
|
|
319
|
+
message: `Component '${id}' no longer requires field '${field}'`
|
|
320
|
+
});
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
if (oldComponent.version !== newComponent.version) {
|
|
324
|
+
const oldVersion = oldComponent.version || "0.0.0";
|
|
325
|
+
const newVersion = newComponent.version || "0.0.0";
|
|
326
|
+
const breaking = isMajorVersionChange(oldVersion, newVersion);
|
|
327
|
+
changes.push({
|
|
328
|
+
type: "modified",
|
|
329
|
+
breaking,
|
|
330
|
+
componentId: id,
|
|
331
|
+
field: "version",
|
|
332
|
+
oldValue: oldVersion,
|
|
333
|
+
newValue: newVersion,
|
|
334
|
+
message: `Component '${id}' version changed from ${oldVersion} to ${newVersion}`
|
|
335
|
+
});
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
spinner.stop();
|
|
339
|
+
if (options.json) {
|
|
340
|
+
console.log(JSON.stringify({ changes }, null, 2));
|
|
341
|
+
} else {
|
|
342
|
+
displayHumanReadableDiff(changes);
|
|
343
|
+
}
|
|
344
|
+
const hasBreakingChanges = changes.some((c) => c.breaking);
|
|
345
|
+
if (options.failOnBreaking && hasBreakingChanges) {
|
|
346
|
+
console.error(chalk.red("\n✗ Breaking changes detected!"));
|
|
347
|
+
process.exit(1);
|
|
348
|
+
}
|
|
349
|
+
process.exit(0);
|
|
350
|
+
} catch (error) {
|
|
351
|
+
spinner.fail("Diff failed");
|
|
352
|
+
if (error instanceof Error) {
|
|
353
|
+
if (error.message.includes("ENOENT")) {
|
|
354
|
+
console.error(chalk.red(`
|
|
355
|
+
File not found`));
|
|
356
|
+
} else {
|
|
357
|
+
console.error(chalk.red("\nUnexpected error:"));
|
|
358
|
+
console.error(chalk.gray(error.message));
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
process.exit(1);
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
function displayHumanReadableDiff(changes) {
|
|
365
|
+
console.log(chalk.bold("\nRegistry Changes:\n"));
|
|
366
|
+
if (changes.length === 0) {
|
|
367
|
+
console.log(chalk.gray("No changes detected"));
|
|
368
|
+
return;
|
|
369
|
+
}
|
|
370
|
+
const breakingChanges = changes.filter((c) => c.breaking);
|
|
371
|
+
const nonBreakingChanges = changes.filter((c) => !c.breaking);
|
|
372
|
+
if (breakingChanges.length > 0) {
|
|
373
|
+
console.log(chalk.red.bold("Breaking Changes:"));
|
|
374
|
+
for (const change of breakingChanges) {
|
|
375
|
+
const icon = getChangeIcon(change.type);
|
|
376
|
+
console.log(chalk.red(` ${icon} ${change.message}`));
|
|
377
|
+
}
|
|
378
|
+
console.log("");
|
|
379
|
+
}
|
|
380
|
+
if (nonBreakingChanges.length > 0) {
|
|
381
|
+
console.log(chalk.yellow.bold("Non-Breaking Changes:"));
|
|
382
|
+
for (const change of nonBreakingChanges) {
|
|
383
|
+
const icon = getChangeIcon(change.type);
|
|
384
|
+
console.log(chalk.yellow(` ${icon} ${change.message}`));
|
|
385
|
+
}
|
|
386
|
+
console.log("");
|
|
387
|
+
}
|
|
388
|
+
console.log(chalk.bold("Summary:"));
|
|
389
|
+
console.log(` Total changes: ${changes.length}`);
|
|
390
|
+
console.log(
|
|
391
|
+
` Breaking: ${breakingChanges.length > 0 ? chalk.red(breakingChanges.length) : chalk.green("0")}`
|
|
392
|
+
);
|
|
393
|
+
console.log(` Non-breaking: ${nonBreakingChanges.length}`);
|
|
394
|
+
}
|
|
395
|
+
function getChangeIcon(type) {
|
|
396
|
+
switch (type) {
|
|
397
|
+
case "added":
|
|
398
|
+
return "+";
|
|
399
|
+
case "removed":
|
|
400
|
+
return "-";
|
|
401
|
+
case "modified":
|
|
402
|
+
return "~";
|
|
403
|
+
case "deprecated":
|
|
404
|
+
return "⚠";
|
|
405
|
+
default:
|
|
406
|
+
return "•";
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
function isMajorVersionChange(oldVersion, newVersion) {
|
|
410
|
+
const oldMajor = parseInt(oldVersion.split(".")[0], 10);
|
|
411
|
+
const newMajor = parseInt(newVersion.split(".")[0], 10);
|
|
412
|
+
return newMajor > oldMajor;
|
|
413
|
+
}
|
|
414
|
+
export {
|
|
415
|
+
diffCommand as d,
|
|
416
|
+
generateTypesCommand as g,
|
|
417
|
+
testExamplesCommand as t,
|
|
418
|
+
validateCommand as v
|
|
419
|
+
};
|
|
420
|
+
//# sourceMappingURL=diff-B8QJSPfQ.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"diff-B8QJSPfQ.js","sources":["../src/commands/validate.ts","../src/commands/generate-types.ts","../src/commands/test-examples.ts","../src/commands/diff.ts"],"sourcesContent":["/**\n * Validate command - validates component registry against JSON Schema\n */\n\nimport { readFileSync } from 'fs'\nimport { resolve } from 'path'\nimport Ajv from 'ajv'\nimport chalk from 'chalk'\nimport ora from 'ora'\nimport { ComponentRegistrySchema, type ComponentRegistry } from '@seed-ship/mcp-ui-spec'\n\n// Load JSON Schema from @seed-ship/mcp-ui-spec package\nimport { fileURLToPath } from 'url'\nimport { dirname, join } from 'path'\n\nconst __filename = fileURLToPath(import.meta.url)\nconst __dirname = dirname(__filename)\n\ninterface ValidateOptions {\n strict?: boolean\n verbose?: boolean\n}\n\nexport async function validateCommand(file: string, options: ValidateOptions) {\n const spinner = ora('Loading registry...').start()\n\n try {\n // Read registry file\n const registryPath = resolve(process.cwd(), file)\n const registryContent = readFileSync(registryPath, 'utf-8')\n const registry = JSON.parse(registryContent)\n\n spinner.text = 'Validating with Zod schema...'\n\n // First validate with Zod (runtime validation)\n const zodResult = ComponentRegistrySchema.safeParse(registry)\n\n if (!zodResult.success) {\n spinner.fail('Zod validation failed')\n console.error(chalk.red('\\nValidation Errors:'))\n zodResult.error.errors.forEach((err) => {\n console.error(chalk.red(` • ${err.path.join('.')}: ${err.message}`))\n })\n process.exit(1)\n }\n\n spinner.text = 'Validating with JSON Schema...'\n\n // Load JSON Schema for secondary validation\n const schemaPath = join(\n __dirname,\n '../../node_modules/@seed-ship/mcp-ui-spec/schemas/component-registry-v1.json'\n )\n let jsonSchemaContent: string\n\n try {\n jsonSchemaContent = readFileSync(schemaPath, 'utf-8')\n } catch {\n // Fallback to local schema if node_modules path fails\n const localSchemaPath = join(\n __dirname,\n '../../../mcp-ui-spec/schemas/component-registry-v1.json'\n )\n jsonSchemaContent = readFileSync(localSchemaPath, 'utf-8')\n }\n\n const jsonSchema = JSON.parse(jsonSchemaContent)\n\n // Validate with JSON Schema using Ajv\n const ajv = new Ajv({\n allErrors: options.strict,\n verbose: options.verbose,\n strict: options.strict,\n })\n\n const validate = ajv.compile(jsonSchema)\n const jsonValid = validate(registry)\n\n if (!jsonValid && validate.errors) {\n spinner.fail('JSON Schema validation failed')\n console.error(chalk.red('\\nValidation Errors:'))\n validate.errors.forEach((err) => {\n const path = err.instancePath || 'root'\n console.error(chalk.red(` • ${path}: ${err.message}`))\n if (options.verbose && err.params) {\n console.error(chalk.gray(` Params: ${JSON.stringify(err.params)}`))\n }\n })\n process.exit(1)\n }\n\n spinner.succeed(chalk.green('Registry is valid!'))\n\n // Summary\n if (options.verbose) {\n console.log(chalk.gray('\\nRegistry Summary:'))\n console.log(chalk.gray(` Version: ${registry.version}`))\n console.log(chalk.gray(` Components: ${registry.components.length}`))\n\n if (registry.metadata?.name) {\n console.log(chalk.gray(` Name: ${registry.metadata.name}`))\n }\n\n console.log(chalk.gray('\\nComponents:'))\n const validRegistry = registry as ComponentRegistry\n validRegistry.components.forEach((comp) => {\n console.log(chalk.gray(` • ${comp.id} (${comp.type}): ${comp.examples.length} example(s)`))\n })\n }\n\n process.exit(0)\n } catch (error) {\n spinner.fail('Validation failed')\n\n if (error instanceof Error) {\n if (error.message.includes('ENOENT')) {\n console.error(chalk.red(`\\nFile not found: ${file}`))\n } else if (error instanceof SyntaxError) {\n console.error(chalk.red('\\nInvalid JSON syntax'))\n console.error(chalk.gray(error.message))\n } else {\n console.error(chalk.red('\\nUnexpected error:'))\n console.error(chalk.gray(error.message))\n }\n }\n\n process.exit(1)\n }\n}\n","/**\n * Generate Types command - generates TypeScript types from registry\n */\n\nimport { readFileSync, writeFileSync } from 'fs'\nimport { resolve } from 'path'\nimport { compile } from 'json-schema-to-typescript'\nimport chalk from 'chalk'\nimport ora from 'ora'\nimport type { Component, ComponentRegistry } from '@seed-ship/mcp-ui-spec'\n\ninterface GenerateTypesOptions {\n namespace?: string\n exportAll?: boolean\n}\n\nexport async function generateTypesCommand(\n input: string,\n output: string | undefined,\n options: GenerateTypesOptions\n) {\n const spinner = ora('Loading registry...').start()\n\n try {\n // Read registry file\n const registryPath = resolve(process.cwd(), input)\n const registryContent = readFileSync(registryPath, 'utf-8')\n const registry: ComponentRegistry = JSON.parse(registryContent)\n\n spinner.text = 'Generating TypeScript types...'\n\n // Generate types for each component\n const typeDefinitions: string[] = []\n\n // Add header comment\n typeDefinitions.push('/**')\n typeDefinitions.push(' * Auto-generated TypeScript types from component registry')\n typeDefinitions.push(' * Generated by @seed-ship/mcp-ui-cli')\n typeDefinitions.push(' */')\n typeDefinitions.push('')\n\n if (options.namespace) {\n typeDefinitions.push(`export namespace ${options.namespace} {`)\n }\n\n // Generate types for each component\n for (const component of registry.components as Component[]) {\n spinner.text = `Generating types for ${component.id}...`\n\n // Generate interface name from component ID\n const typeName = componentIdToTypeName(component.id)\n\n try {\n const ts = await compile(component.schema as any, typeName, {\n bannerComment: `/* ${component.name} - ${component.description || 'No description'} */`,\n style: {\n semi: false,\n singleQuote: true,\n },\n })\n\n // Add the generated type\n if (options.namespace) {\n // Indent for namespace\n const indented = ts\n .split('\\n')\n .map((line) => (line ? ` ${line}` : line))\n .join('\\n')\n typeDefinitions.push(indented)\n } else {\n typeDefinitions.push(ts)\n }\n\n typeDefinitions.push('')\n } catch (error) {\n spinner.warn(`Skipping ${component.id}: type generation failed`)\n if (error instanceof Error) {\n console.error(chalk.yellow(` ${error.message}`))\n }\n }\n }\n\n if (options.namespace) {\n typeDefinitions.push('}')\n }\n\n const generatedCode = typeDefinitions.join('\\n')\n\n spinner.succeed('Type generation complete')\n\n // Write to file or stdout\n if (output) {\n const outputPath = resolve(process.cwd(), output)\n writeFileSync(outputPath, generatedCode, 'utf-8')\n console.log(chalk.green(`✓ Types written to ${output}`))\n } else {\n console.log('\\n' + generatedCode)\n }\n\n process.exit(0)\n } catch (error) {\n spinner.fail('Type generation failed')\n\n if (error instanceof Error) {\n if (error.message.includes('ENOENT')) {\n console.error(chalk.red(`\\nFile not found: ${input}`))\n } else {\n console.error(chalk.red('\\nUnexpected error:'))\n console.error(chalk.gray(error.message))\n }\n }\n\n process.exit(1)\n }\n}\n\n/**\n * Convert component ID to TypeScript type name\n * Examples:\n * quickchart-bar -> QuickchartBar\n * metric-card -> MetricCard\n * data-table -> DataTable\n */\nfunction componentIdToTypeName(id: string): string {\n return id\n .split('-')\n .map((part) => part.charAt(0).toUpperCase() + part.slice(1))\n .join('')\n}\n","/**\n * Test Examples command - validates all component examples\n */\n\nimport { readFileSync } from 'fs'\nimport { resolve } from 'path'\nimport Ajv from 'ajv'\nimport chalk from 'chalk'\nimport ora from 'ora'\nimport type { ComponentRegistry } from '@seed-ship/mcp-ui-spec'\n\ninterface TestExamplesOptions {\n component?: string\n verbose?: boolean\n}\n\ninterface TestResult {\n componentId: string\n exampleName: string\n passed: boolean\n errors?: string[]\n}\n\nexport async function testExamplesCommand(file: string, options: TestExamplesOptions) {\n const spinner = ora('Loading registry...').start()\n\n try {\n // Read registry file\n const registryPath = resolve(process.cwd(), file)\n const registryContent = readFileSync(registryPath, 'utf-8')\n const registry: ComponentRegistry = JSON.parse(registryContent)\n\n // Filter components if specific component requested\n const componentsToTest = options.component\n ? registry.components.filter((c) => c.id === options.component)\n : registry.components\n\n if (componentsToTest.length === 0) {\n spinner.fail(`Component not found: ${options.component}`)\n process.exit(1)\n }\n\n spinner.text = 'Running example tests...'\n\n const results: TestResult[] = []\n let totalExamples = 0\n let passedExamples = 0\n\n // Test each component's examples\n for (const component of componentsToTest) {\n spinner.text = `Testing examples for ${component.id}...`\n\n // Create Ajv validator for this component's schema\n const ajv = new Ajv({ allErrors: true, verbose: options.verbose })\n const validate = ajv.compile(component.schema as any)\n\n // Test each example\n for (const example of component.examples) {\n totalExamples++\n\n const valid = validate(example.params)\n const result: TestResult = {\n componentId: component.id,\n exampleName: example.name,\n passed: valid,\n }\n\n if (!valid && validate.errors) {\n result.errors = validate.errors.map((err) => {\n const path = err.instancePath || 'root'\n return `${path}: ${err.message}`\n })\n }\n\n if (valid) {\n passedExamples++\n }\n\n results.push(result)\n }\n }\n\n spinner.stop()\n\n // Display results\n console.log(chalk.bold('\\nTest Results:\\n'))\n\n let hasFailures = false\n\n for (const result of results) {\n const status = result.passed ? chalk.green('✓') : chalk.red('✗')\n const componentLabel = chalk.cyan(result.componentId)\n const exampleLabel = chalk.gray(result.exampleName)\n\n console.log(`${status} ${componentLabel} › ${exampleLabel}`)\n\n if (!result.passed && result.errors) {\n hasFailures = true\n result.errors.forEach((error) => {\n console.log(chalk.red(` ${error}`))\n })\n }\n }\n\n // Summary\n console.log('')\n console.log(chalk.bold('Summary:'))\n console.log(` ${chalk.green(`${passedExamples} passed`)} / ${totalExamples} total`)\n\n if (hasFailures) {\n console.log(chalk.red(` ${totalExamples - passedExamples} failed`))\n process.exit(1)\n }\n\n console.log(chalk.green('\\n✓ All examples are valid!'))\n process.exit(0)\n } catch (error) {\n spinner.fail('Example testing failed')\n\n if (error instanceof Error) {\n if (error.message.includes('ENOENT')) {\n console.error(chalk.red(`\\nFile not found: ${file}`))\n } else if (error instanceof SyntaxError) {\n console.error(chalk.red('\\nInvalid JSON syntax'))\n console.error(chalk.gray(error.message))\n } else {\n console.error(chalk.red('\\nUnexpected error:'))\n console.error(chalk.gray(error.message))\n }\n }\n\n process.exit(1)\n }\n}\n","/**\n * Diff command - compares two registry versions for breaking changes\n */\n\nimport { readFileSync } from 'fs'\nimport { resolve } from 'path'\nimport chalk from 'chalk'\nimport ora from 'ora'\nimport type { ComponentRegistry } from '@seed-ship/mcp-ui-spec'\n\ninterface DiffOptions {\n json?: boolean\n failOnBreaking?: boolean\n}\n\ninterface ChangeType {\n type: 'added' | 'removed' | 'modified' | 'deprecated'\n breaking: boolean\n componentId: string\n field?: string\n oldValue?: unknown\n newValue?: unknown\n message: string\n}\n\nexport async function diffCommand(oldFile: string, newFile: string, options: DiffOptions) {\n const spinner = ora('Loading registries...').start()\n\n try {\n // Read both registry files\n const oldPath = resolve(process.cwd(), oldFile)\n const newPath = resolve(process.cwd(), newFile)\n\n const oldRegistry: ComponentRegistry = JSON.parse(readFileSync(oldPath, 'utf-8'))\n const newRegistry: ComponentRegistry = JSON.parse(readFileSync(newPath, 'utf-8'))\n\n spinner.text = 'Comparing registries...'\n\n const changes: ChangeType[] = []\n\n // Create maps for easier lookup\n const oldComponents = new Map(oldRegistry.components.map((c) => [c.id, c]))\n const newComponents = new Map(newRegistry.components.map((c) => [c.id, c]))\n\n // Check for removed components (breaking)\n for (const [id] of oldComponents) {\n if (!newComponents.has(id)) {\n changes.push({\n type: 'removed',\n breaking: true,\n componentId: id,\n message: `Component '${id}' was removed`,\n })\n }\n }\n\n // Check for added components (non-breaking)\n for (const [id] of newComponents) {\n if (!oldComponents.has(id)) {\n changes.push({\n type: 'added',\n breaking: false,\n componentId: id,\n message: `Component '${id}' was added`,\n })\n }\n }\n\n // Check for modified components\n for (const [id, newComponent] of newComponents) {\n const oldComponent = oldComponents.get(id)\n if (!oldComponent) continue\n\n // Check deprecation\n if (!oldComponent.deprecated && newComponent.deprecated) {\n changes.push({\n type: 'deprecated',\n breaking: false,\n componentId: id,\n message: `Component '${id}' was deprecated${\n newComponent.deprecationMessage ? `: ${newComponent.deprecationMessage}` : ''\n }`,\n })\n }\n\n // Check type change (breaking)\n if (oldComponent.type !== newComponent.type) {\n changes.push({\n type: 'modified',\n breaking: true,\n componentId: id,\n field: 'type',\n oldValue: oldComponent.type,\n newValue: newComponent.type,\n message: `Component '${id}' type changed from '${oldComponent.type}' to '${newComponent.type}'`,\n })\n }\n\n // Check required fields change (breaking)\n const oldRequired = new Set(oldComponent.schema.required || [])\n const newRequired = new Set(newComponent.schema.required || [])\n\n for (const field of newRequired) {\n if (!oldRequired.has(field)) {\n changes.push({\n type: 'modified',\n breaking: true,\n componentId: id,\n field: `schema.required`,\n message: `Component '${id}' now requires field '${field}'`,\n })\n }\n }\n\n for (const field of oldRequired) {\n if (!newRequired.has(field)) {\n changes.push({\n type: 'modified',\n breaking: false,\n componentId: id,\n field: `schema.required`,\n message: `Component '${id}' no longer requires field '${field}'`,\n })\n }\n }\n\n // Check version change\n if (oldComponent.version !== newComponent.version) {\n const oldVersion = oldComponent.version || '0.0.0'\n const newVersion = newComponent.version || '0.0.0'\n\n const breaking = isMajorVersionChange(oldVersion, newVersion)\n\n changes.push({\n type: 'modified',\n breaking,\n componentId: id,\n field: 'version',\n oldValue: oldVersion,\n newValue: newVersion,\n message: `Component '${id}' version changed from ${oldVersion} to ${newVersion}`,\n })\n }\n }\n\n spinner.stop()\n\n // Output results\n if (options.json) {\n console.log(JSON.stringify({ changes }, null, 2))\n } else {\n displayHumanReadableDiff(changes)\n }\n\n // Check if we should fail on breaking changes\n const hasBreakingChanges = changes.some((c) => c.breaking)\n if (options.failOnBreaking && hasBreakingChanges) {\n console.error(chalk.red('\\n✗ Breaking changes detected!'))\n process.exit(1)\n }\n\n process.exit(0)\n } catch (error) {\n spinner.fail('Diff failed')\n\n if (error instanceof Error) {\n if (error.message.includes('ENOENT')) {\n console.error(chalk.red(`\\nFile not found`))\n } else {\n console.error(chalk.red('\\nUnexpected error:'))\n console.error(chalk.gray(error.message))\n }\n }\n\n process.exit(1)\n }\n}\n\nfunction displayHumanReadableDiff(changes: ChangeType[]) {\n console.log(chalk.bold('\\nRegistry Changes:\\n'))\n\n if (changes.length === 0) {\n console.log(chalk.gray('No changes detected'))\n return\n }\n\n const breakingChanges = changes.filter((c) => c.breaking)\n const nonBreakingChanges = changes.filter((c) => !c.breaking)\n\n if (breakingChanges.length > 0) {\n console.log(chalk.red.bold('Breaking Changes:'))\n for (const change of breakingChanges) {\n const icon = getChangeIcon(change.type)\n console.log(chalk.red(` ${icon} ${change.message}`))\n }\n console.log('')\n }\n\n if (nonBreakingChanges.length > 0) {\n console.log(chalk.yellow.bold('Non-Breaking Changes:'))\n for (const change of nonBreakingChanges) {\n const icon = getChangeIcon(change.type)\n console.log(chalk.yellow(` ${icon} ${change.message}`))\n }\n console.log('')\n }\n\n // Summary\n console.log(chalk.bold('Summary:'))\n console.log(` Total changes: ${changes.length}`)\n console.log(\n ` Breaking: ${breakingChanges.length > 0 ? chalk.red(breakingChanges.length) : chalk.green('0')}`\n )\n console.log(` Non-breaking: ${nonBreakingChanges.length}`)\n}\n\nfunction getChangeIcon(type: ChangeType['type']): string {\n switch (type) {\n case 'added':\n return '+'\n case 'removed':\n return '-'\n case 'modified':\n return '~'\n case 'deprecated':\n return '⚠'\n default:\n return '•'\n }\n}\n\nfunction isMajorVersionChange(oldVersion: string, newVersion: string): boolean {\n const oldMajor = parseInt(oldVersion.split('.')[0], 10)\n const newMajor = parseInt(newVersion.split('.')[0], 10)\n return newMajor > oldMajor\n}\n"],"names":["__filename","__dirname"],"mappings":"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"}
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
const diff = require("./diff-B1c8BBVB.cjs");
|
|
4
|
+
exports.diffCommand = diff.diffCommand;
|
|
5
|
+
exports.generateTypesCommand = diff.generateTypesCommand;
|
|
6
|
+
exports.testExamplesCommand = diff.testExamplesCommand;
|
|
7
|
+
exports.validateCommand = diff.validateCommand;
|
|
8
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";"}
|
package/package.json
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@seed-ship/mcp-ui-cli",
|
|
3
|
+
"version": "1.0.2",
|
|
4
|
+
"description": "CLI tools for validating and generating MCP UI component registries",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"mcp-ui": "./dist/cli.js"
|
|
8
|
+
},
|
|
9
|
+
"main": "./dist/index.cjs",
|
|
10
|
+
"module": "./dist/index.js",
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"exports": {
|
|
13
|
+
".": {
|
|
14
|
+
"import": "./dist/index.js",
|
|
15
|
+
"require": "./dist/index.cjs",
|
|
16
|
+
"types": "./dist/index.d.ts"
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
"files": [
|
|
20
|
+
"dist",
|
|
21
|
+
"README.md",
|
|
22
|
+
"CHANGELOG.md"
|
|
23
|
+
],
|
|
24
|
+
"dependencies": {
|
|
25
|
+
"ajv": "^8.12.0",
|
|
26
|
+
"commander": "^11.1.0",
|
|
27
|
+
"json-schema-to-typescript": "^13.1.1",
|
|
28
|
+
"zod": "^3.22.4",
|
|
29
|
+
"chalk": "^5.3.0",
|
|
30
|
+
"ora": "^7.0.1",
|
|
31
|
+
"@seed-ship/mcp-ui-spec": "1.0.2"
|
|
32
|
+
},
|
|
33
|
+
"devDependencies": {
|
|
34
|
+
"@types/node": "^20.10.0",
|
|
35
|
+
"@typescript-eslint/eslint-plugin": "^6.15.0",
|
|
36
|
+
"@typescript-eslint/parser": "^6.15.0",
|
|
37
|
+
"eslint": "^8.56.0",
|
|
38
|
+
"typescript": "^5.3.3",
|
|
39
|
+
"vite": "^5.0.10",
|
|
40
|
+
"vitest": "^1.1.0"
|
|
41
|
+
},
|
|
42
|
+
"keywords": [
|
|
43
|
+
"mcp",
|
|
44
|
+
"ui",
|
|
45
|
+
"cli",
|
|
46
|
+
"validation",
|
|
47
|
+
"codegen",
|
|
48
|
+
"component-registry"
|
|
49
|
+
],
|
|
50
|
+
"author": "Gabriel Brument",
|
|
51
|
+
"license": "MIT",
|
|
52
|
+
"repository": {
|
|
53
|
+
"type": "git",
|
|
54
|
+
"url": "https://github.com/theseedship/mcp-ui.git",
|
|
55
|
+
"directory": "mcp-ui-cli"
|
|
56
|
+
},
|
|
57
|
+
"homepage": "https://github.com/theseedship/mcp-ui#readme",
|
|
58
|
+
"bugs": {
|
|
59
|
+
"url": "https://github.com/theseedship/mcp-ui/issues"
|
|
60
|
+
},
|
|
61
|
+
"scripts": {
|
|
62
|
+
"build": "vite build",
|
|
63
|
+
"build:types": "tsc --emitDeclarationOnly --outDir dist",
|
|
64
|
+
"dev": "vite build --watch",
|
|
65
|
+
"test": "vitest run",
|
|
66
|
+
"test:watch": "vitest",
|
|
67
|
+
"lint": "eslint src",
|
|
68
|
+
"typecheck": "tsc --noEmit",
|
|
69
|
+
"clean": "rm -rf dist"
|
|
70
|
+
}
|
|
71
|
+
}
|