Add plugin ecosystem protocol for vendor interoperability#374
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
|
@copilot 所有文档统一用英文 |
Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
Done! All documentation has been unified to English only (commit 5e0ae52):
All tests passing (1,822/1,822) and build successful. |
|
This PR is very large. Consider breaking it into smaller PRs for easier review. |
There was a problem hiding this comment.
Pull request overview
This pull request establishes a comprehensive plugin ecosystem protocol for ObjectStack, enabling standardized interoperability between plugins from different vendors. The PR introduces robust capability declaration, protocol conformance tracking, and plugin registry mechanisms.
Changes:
- Introduces Plugin Capability Protocol schemas for protocol implementation, interface provision, dependency declaration, and extension points
- Adds Plugin Registry schemas for discovery, quality metrics, and vendor trust management
- Integrates capabilities into the existing ManifestSchema for backward compatibility
- Provides comprehensive documentation and reference implementation
Reviewed changes
Copilot reviewed 34 out of 34 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| packages/spec/src/system/plugin-capability.zod.ts | Core protocol schemas defining capability conformance, protocol references, interfaces, dependencies, and extension points |
| packages/spec/src/system/plugin-capability.test.ts | Comprehensive test coverage for all plugin capability schemas |
| packages/spec/src/hub/plugin-registry.zod.ts | Registry schemas for plugin discovery, vendor verification, quality metrics, and search/filtering |
| packages/spec/src/system/manifest.zod.ts | Integration of optional capabilities field into existing manifest schema |
| packages/spec/src/system/index.ts | Export addition for plugin-capability schemas |
| packages/spec/src/hub/index.ts | Export addition for plugin-registry schemas |
| packages/spec/json-schema/system/*.json | Auto-generated JSON schemas for all new TypeScript schemas |
| packages/spec/json-schema/hub/*.json | Auto-generated JSON schemas for registry types |
| examples/plugin-advanced-crm/objectstack.config.ts | Reference implementation demonstrating all capability features |
| examples/plugin-advanced-crm/README.md | Documentation explaining the example implementation |
| content/docs/developers/plugin-ecosystem.mdx | Comprehensive architecture guide (776 lines) |
| content/docs/references/system/plugin-capability.mdx | Schema reference documentation |
| content/docs/references/hub/plugin-registry.mdx | Registry schema reference documentation |
| content/docs/references/system/meta.json | Navigation metadata update |
| content/docs/references/hub/meta.json | Navigation metadata update |
| content/docs/developers/meta.json | Navigation metadata update |
| import { z } from 'zod'; | ||
| import { PluginCapabilityManifestSchema } from '../system/plugin-capability.zod'; | ||
|
|
||
| /** | ||
| * # Plugin Registry Protocol | ||
| * | ||
| * Defines the schema for the plugin discovery and registry system. | ||
| * This enables plugins from different vendors to be discovered, validated, | ||
| * and composed together in the ObjectStack ecosystem. | ||
| */ | ||
|
|
||
| /** | ||
| * Plugin Vendor Information | ||
| */ | ||
| export const PluginVendorSchema = z.object({ | ||
| /** | ||
| * Vendor identifier (reverse domain notation) | ||
| * Example: "com.acme", "org.apache", "com.objectstack" | ||
| */ | ||
| id: z.string() | ||
| .regex(/^[a-z][a-z0-9]*(\.[a-z][a-z0-9]*)+$/) | ||
| .describe('Vendor identifier (reverse domain)'), | ||
|
|
||
| /** | ||
| * Vendor display name | ||
| */ | ||
| name: z.string(), | ||
|
|
||
| /** | ||
| * Vendor website | ||
| */ | ||
| website: z.string().url().optional(), | ||
|
|
||
| /** | ||
| * Contact email | ||
| */ | ||
| email: z.string().email().optional(), | ||
|
|
||
| /** | ||
| * Verification status | ||
| */ | ||
| verified: z.boolean().default(false).describe('Whether vendor is verified by ObjectStack'), | ||
|
|
||
| /** | ||
| * Trust level | ||
| */ | ||
| trustLevel: z.enum(['official', 'verified', 'community', 'unverified']).default('unverified'), | ||
| }); | ||
|
|
||
| /** | ||
| * Plugin Quality Metrics | ||
| */ | ||
| export const PluginQualityMetricsSchema = z.object({ | ||
| /** | ||
| * Test coverage percentage | ||
| */ | ||
| testCoverage: z.number().min(0).max(100).optional(), | ||
|
|
||
| /** | ||
| * Documentation score (0-100) | ||
| */ | ||
| documentationScore: z.number().min(0).max(100).optional(), | ||
|
|
||
| /** | ||
| * Code quality score (0-100) | ||
| */ | ||
| codeQuality: z.number().min(0).max(100).optional(), | ||
|
|
||
| /** | ||
| * Security scan status | ||
| */ | ||
| securityScan: z.object({ | ||
| lastScanDate: z.string().datetime().optional(), | ||
| vulnerabilities: z.object({ | ||
| critical: z.number().int().min(0).default(0), | ||
| high: z.number().int().min(0).default(0), | ||
| medium: z.number().int().min(0).default(0), | ||
| low: z.number().int().min(0).default(0), | ||
| }).optional(), | ||
| passed: z.boolean().default(false), | ||
| }).optional(), | ||
|
|
||
| /** | ||
| * Conformance test results | ||
| */ | ||
| conformanceTests: z.array(z.object({ | ||
| protocolId: z.string().describe('Protocol being tested'), | ||
| passed: z.boolean(), | ||
| totalTests: z.number().int().min(0), | ||
| passedTests: z.number().int().min(0), | ||
| lastRunDate: z.string().datetime().optional(), | ||
| })).optional(), | ||
| }); | ||
|
|
||
| /** | ||
| * Plugin Usage Statistics | ||
| */ | ||
| export const PluginStatisticsSchema = z.object({ | ||
| /** | ||
| * Total downloads | ||
| */ | ||
| downloads: z.number().int().min(0).default(0), | ||
|
|
||
| /** | ||
| * Downloads in the last 30 days | ||
| */ | ||
| downloadsLastMonth: z.number().int().min(0).default(0), | ||
|
|
||
| /** | ||
| * Number of active installations | ||
| */ | ||
| activeInstallations: z.number().int().min(0).default(0), | ||
|
|
||
| /** | ||
| * User ratings | ||
| */ | ||
| ratings: z.object({ | ||
| average: z.number().min(0).max(5).default(0), | ||
| count: z.number().int().min(0).default(0), | ||
| distribution: z.object({ | ||
| '5': z.number().int().min(0).default(0), | ||
| '4': z.number().int().min(0).default(0), | ||
| '3': z.number().int().min(0).default(0), | ||
| '2': z.number().int().min(0).default(0), | ||
| '1': z.number().int().min(0).default(0), | ||
| }).optional(), | ||
| }).optional(), | ||
|
|
||
| /** | ||
| * GitHub stars (if open source) | ||
| */ | ||
| stars: z.number().int().min(0).optional(), | ||
|
|
||
| /** | ||
| * Number of dependent plugins | ||
| */ | ||
| dependents: z.number().int().min(0).default(0), | ||
| }); | ||
|
|
||
| /** | ||
| * Plugin Registry Entry | ||
| * Complete metadata for a plugin in the registry. | ||
| */ | ||
| export const PluginRegistryEntrySchema = z.object({ | ||
| /** | ||
| * Plugin identifier (must match manifest.id) | ||
| */ | ||
| id: z.string() | ||
| .regex(/^([a-z][a-z0-9]*\.)+[a-z][a-z0-9-]+$/) | ||
| .describe('Plugin identifier (reverse domain notation)'), | ||
|
|
||
| /** | ||
| * Current version | ||
| */ | ||
| version: z.string().regex(/^\d+\.\d+\.\d+$/), | ||
|
|
||
| /** | ||
| * Plugin display name | ||
| */ | ||
| name: z.string(), | ||
|
|
||
| /** | ||
| * Short description | ||
| */ | ||
| description: z.string().optional(), | ||
|
|
||
| /** | ||
| * Detailed documentation/README | ||
| */ | ||
| readme: z.string().optional(), | ||
|
|
||
| /** | ||
| * Plugin type/category | ||
| */ | ||
| category: z.enum([ | ||
| 'data', // Data management, storage, databases | ||
| 'integration', // External service integrations | ||
| 'ui', // UI components and themes | ||
| 'analytics', // Analytics and reporting | ||
| 'security', // Security, auth, compliance | ||
| 'automation', // Workflows and automation | ||
| 'ai', // AI/ML capabilities | ||
| 'utility', // General utilities | ||
| 'driver', // Database/storage drivers | ||
| 'gateway', // API gateways | ||
| 'adapter', // Runtime adapters | ||
| ]).optional(), | ||
|
|
||
| /** | ||
| * Tags for categorization | ||
| */ | ||
| tags: z.array(z.string()).optional(), | ||
|
|
||
| /** | ||
| * Vendor information | ||
| */ | ||
| vendor: PluginVendorSchema, | ||
|
|
||
| /** | ||
| * Capability manifest (what the plugin implements/provides) | ||
| */ | ||
| capabilities: PluginCapabilityManifestSchema.optional(), | ||
|
|
||
| /** | ||
| * Compatibility information | ||
| */ | ||
| compatibility: z.object({ | ||
| /** | ||
| * Minimum ObjectStack version required | ||
| */ | ||
| minObjectStackVersion: z.string().optional(), | ||
|
|
||
| /** | ||
| * Maximum ObjectStack version supported | ||
| */ | ||
| maxObjectStackVersion: z.string().optional(), | ||
|
|
||
| /** | ||
| * Node.js version requirement | ||
| */ | ||
| nodeVersion: z.string().optional(), | ||
|
|
||
| /** | ||
| * Supported platforms | ||
| */ | ||
| platforms: z.array(z.enum(['linux', 'darwin', 'win32', 'browser'])).optional(), | ||
| }).optional(), | ||
|
|
||
| /** | ||
| * Links and resources | ||
| */ | ||
| links: z.object({ | ||
| homepage: z.string().url().optional(), | ||
| repository: z.string().url().optional(), | ||
| documentation: z.string().url().optional(), | ||
| bugs: z.string().url().optional(), | ||
| changelog: z.string().url().optional(), | ||
| }).optional(), | ||
|
|
||
| /** | ||
| * Media assets | ||
| */ | ||
| media: z.object({ | ||
| icon: z.string().url().optional(), | ||
| logo: z.string().url().optional(), | ||
| screenshots: z.array(z.string().url()).optional(), | ||
| video: z.string().url().optional(), | ||
| }).optional(), | ||
|
|
||
| /** | ||
| * Quality metrics | ||
| */ | ||
| quality: PluginQualityMetricsSchema.optional(), | ||
|
|
||
| /** | ||
| * Usage statistics | ||
| */ | ||
| statistics: PluginStatisticsSchema.optional(), | ||
|
|
||
| /** | ||
| * License information | ||
| */ | ||
| license: z.string().optional().describe('SPDX license identifier'), | ||
|
|
||
| /** | ||
| * Pricing (if commercial) | ||
| */ | ||
| pricing: z.object({ | ||
| model: z.enum(['free', 'freemium', 'paid', 'enterprise']), | ||
| price: z.number().min(0).optional(), | ||
| currency: z.string().default('USD').optional(), | ||
| billingPeriod: z.enum(['one-time', 'monthly', 'yearly']).optional(), | ||
| }).optional(), | ||
|
|
||
| /** | ||
| * Publication dates | ||
| */ | ||
| publishedAt: z.string().datetime().optional(), | ||
| updatedAt: z.string().datetime().optional(), | ||
|
|
||
| /** | ||
| * Deprecation status | ||
| */ | ||
| deprecated: z.boolean().default(false), | ||
| deprecationMessage: z.string().optional(), | ||
| replacedBy: z.string().optional().describe('Plugin ID that replaces this one'), | ||
|
|
||
| /** | ||
| * Feature flags | ||
| */ | ||
| flags: z.object({ | ||
| experimental: z.boolean().default(false), | ||
| beta: z.boolean().default(false), | ||
| featured: z.boolean().default(false), | ||
| verified: z.boolean().default(false), | ||
| }).optional(), | ||
| }); | ||
|
|
||
| /** | ||
| * Plugin Search Filters | ||
| */ | ||
| export const PluginSearchFiltersSchema = z.object({ | ||
| /** | ||
| * Search query | ||
| */ | ||
| query: z.string().optional(), | ||
|
|
||
| /** | ||
| * Filter by category | ||
| */ | ||
| category: z.array(z.string()).optional(), | ||
|
|
||
| /** | ||
| * Filter by tags | ||
| */ | ||
| tags: z.array(z.string()).optional(), | ||
|
|
||
| /** | ||
| * Filter by vendor trust level | ||
| */ | ||
| trustLevel: z.array(z.enum(['official', 'verified', 'community', 'unverified'])).optional(), | ||
|
|
||
| /** | ||
| * Filter by protocols implemented | ||
| */ | ||
| implementsProtocols: z.array(z.string()).optional(), | ||
|
|
||
| /** | ||
| * Filter by pricing model | ||
| */ | ||
| pricingModel: z.array(z.enum(['free', 'freemium', 'paid', 'enterprise'])).optional(), | ||
|
|
||
| /** | ||
| * Minimum rating | ||
| */ | ||
| minRating: z.number().min(0).max(5).optional(), | ||
|
|
||
| /** | ||
| * Sort options | ||
| */ | ||
| sortBy: z.enum([ | ||
| 'relevance', | ||
| 'downloads', | ||
| 'rating', | ||
| 'updated', | ||
| 'name', | ||
| ]).optional(), | ||
|
|
||
| /** | ||
| * Sort order | ||
| */ | ||
| sortOrder: z.enum(['asc', 'desc']).default('desc').optional(), | ||
|
|
||
| /** | ||
| * Pagination | ||
| */ | ||
| page: z.number().int().min(1).default(1).optional(), | ||
| limit: z.number().int().min(1).max(100).default(20).optional(), | ||
| }); | ||
|
|
||
| /** | ||
| * Plugin Installation Configuration | ||
| */ | ||
| export const PluginInstallConfigSchema = z.object({ | ||
| /** | ||
| * Plugin identifier to install | ||
| */ | ||
| pluginId: z.string(), | ||
|
|
||
| /** | ||
| * Version to install (supports semver ranges) | ||
| */ | ||
| version: z.string().optional().describe('Defaults to latest'), | ||
|
|
||
| /** | ||
| * Plugin-specific configuration values | ||
| */ | ||
| config: z.record(z.any()).optional(), | ||
|
|
||
| /** | ||
| * Whether to auto-update | ||
| */ | ||
| autoUpdate: z.boolean().default(false).optional(), | ||
|
|
||
| /** | ||
| * Installation options | ||
| */ | ||
| options: z.object({ | ||
| /** | ||
| * Skip dependency installation | ||
| */ | ||
| skipDependencies: z.boolean().default(false).optional(), | ||
|
|
||
| /** | ||
| * Force reinstall | ||
| */ | ||
| force: z.boolean().default(false).optional(), | ||
|
|
||
| /** | ||
| * Installation target | ||
| */ | ||
| target: z.enum(['system', 'space', 'user']).default('space').optional(), | ||
| }).optional(), | ||
| }); | ||
|
|
||
| // Export types | ||
| export type PluginVendor = z.infer<typeof PluginVendorSchema>; | ||
| export type PluginQualityMetrics = z.infer<typeof PluginQualityMetricsSchema>; | ||
| export type PluginStatistics = z.infer<typeof PluginStatisticsSchema>; | ||
| export type PluginRegistryEntry = z.infer<typeof PluginRegistryEntrySchema>; | ||
| export type PluginSearchFilters = z.infer<typeof PluginSearchFiltersSchema>; | ||
| export type PluginInstallConfig = z.infer<typeof PluginInstallConfigSchema>; |
There was a problem hiding this comment.
The PluginRegistryEntrySchema and related schemas lack test coverage. While plugin-capability.zod.ts has comprehensive tests in plugin-capability.test.ts, the plugin-registry.zod.ts file (413 lines) does not have a corresponding test file. This is inconsistent with the testing patterns seen in the system directory where most schema files have accompanying test files (e.g., manifest.test.ts, plugin.test.ts, datasource.test.ts).
Establishes standardized protocols for plugin discovery, capability declaration, and cross-vendor interoperability in the ObjectStack microkernel architecture.
Core Protocols
Plugin Capability Manifest (
system/plugin-capability.zod.ts)Plugin Registry (
hub/plugin-registry.zod.ts)Naming Convention
Distinguishes package vs. code-level identifiers:
com.acme.crm.customer-managementcontact_service,customer_validatorInteroperability Patterns
Documentation
All documentation unified in English:
/content/docs/developers/plugin-ecosystem.mdx/IMPLEMENTATION_SUMMARY.md/examples/plugin-advanced-crm/Integration
Extends
ManifestSchemawith optionalcapabilitiesfield for backward compatibility. Registry schemas exported via@objectstack/spec/hub.Original prompt
✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.