glost-registry 0.5.0

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/src/types.ts ADDED
@@ -0,0 +1,275 @@
1
+ /**
2
+ * Plugin Registry Types
3
+ *
4
+ * Type definitions for the enhanced plugin registry.
5
+ *
6
+ * @packageDocumentation
7
+ */
8
+
9
+ import type { GLOSTExtension } from "glost-extensions";
10
+
11
+ /**
12
+ * Plugin category
13
+ */
14
+ export type PluginCategory =
15
+ | "transformer"
16
+ | "enhancer"
17
+ | "generator"
18
+ | "analyzer"
19
+ | "utility";
20
+
21
+ /**
22
+ * Plugin metadata
23
+ *
24
+ * Extended metadata for plugins beyond the basic extension interface.
25
+ */
26
+ export interface PluginMetadata {
27
+ /** Unique plugin identifier */
28
+ id: string;
29
+
30
+ /** Human-readable name */
31
+ name: string;
32
+
33
+ /** Plugin version (semver) */
34
+ version: string;
35
+
36
+ /** Description of what the plugin does */
37
+ description: string;
38
+
39
+ /** Plugin author */
40
+ author?: string;
41
+
42
+ /** Plugin repository URL */
43
+ repository?: string;
44
+
45
+ /** Plugin homepage/documentation URL */
46
+ homepage?: string;
47
+
48
+ /** Plugin category */
49
+ category: PluginCategory;
50
+
51
+ /** Tags for searching/filtering */
52
+ tags: string[];
53
+
54
+ /** Plugin capabilities */
55
+ supports: PluginCapabilities;
56
+
57
+ /** Dependencies */
58
+ requires?: PluginRequirements;
59
+
60
+ /** Conflicting plugins */
61
+ conflicts?: string[];
62
+
63
+ /** Configuration options schema */
64
+ options?: PluginOptionsSchema;
65
+
66
+ /** Usage examples */
67
+ examples?: PluginExample[];
68
+
69
+ /** When the plugin was registered */
70
+ registeredAt?: Date;
71
+ }
72
+
73
+ /**
74
+ * Plugin capabilities
75
+ */
76
+ export interface PluginCapabilities {
77
+ /** Supported language codes */
78
+ languages?: string[];
79
+
80
+ /** Supported node types */
81
+ nodeTypes?: string[];
82
+
83
+ /** Whether the plugin supports async operations */
84
+ async: boolean;
85
+
86
+ /** Whether the plugin can run in parallel with others */
87
+ parallel?: boolean;
88
+
89
+ /** Custom capabilities */
90
+ custom?: Record<string, any>;
91
+ }
92
+
93
+ /**
94
+ * Plugin requirements
95
+ */
96
+ export interface PluginRequirements {
97
+ /** Required plugins (IDs) */
98
+ plugins?: string[];
99
+
100
+ /** Required GLOST version (semver range) */
101
+ glostVersion?: string;
102
+
103
+ /** Required Node.js version (semver range) */
104
+ nodeVersion?: string;
105
+
106
+ /** Custom requirements */
107
+ custom?: Record<string, any>;
108
+ }
109
+
110
+ /**
111
+ * Plugin options schema
112
+ */
113
+ export interface PluginOptionsSchema {
114
+ /** Schema type */
115
+ type: "object";
116
+
117
+ /** Properties definition */
118
+ properties?: Record<string, PropertySchema>;
119
+
120
+ /** Required properties */
121
+ required?: string[];
122
+
123
+ /** Allow additional properties */
124
+ additionalProperties?: boolean;
125
+ }
126
+
127
+ /**
128
+ * Property schema
129
+ */
130
+ export interface PropertySchema {
131
+ /** Property type */
132
+ type: "string" | "number" | "boolean" | "array" | "object";
133
+
134
+ /** Property description */
135
+ description?: string;
136
+
137
+ /** Default value */
138
+ default?: any;
139
+
140
+ /** Enum values */
141
+ enum?: any[];
142
+
143
+ /** Array items schema */
144
+ items?: PropertySchema;
145
+
146
+ /** Object properties schema */
147
+ properties?: Record<string, PropertySchema>;
148
+ }
149
+
150
+ /**
151
+ * Plugin example
152
+ */
153
+ export interface PluginExample {
154
+ /** Example title */
155
+ title: string;
156
+
157
+ /** Example description */
158
+ description?: string;
159
+
160
+ /** Example code */
161
+ code: string;
162
+
163
+ /** Expected output description */
164
+ output?: string;
165
+ }
166
+
167
+ /**
168
+ * Plugin query for searching
169
+ */
170
+ export interface PluginQuery {
171
+ /** Search by keyword */
172
+ keyword?: string;
173
+
174
+ /** Filter by category */
175
+ category?: PluginCategory;
176
+
177
+ /** Filter by language support */
178
+ language?: string;
179
+
180
+ /** Filter by tags */
181
+ tags?: string[];
182
+
183
+ /** Filter by author */
184
+ author?: string;
185
+
186
+ /** Filter by capability */
187
+ capability?: string;
188
+ }
189
+
190
+ /**
191
+ * Conflict report
192
+ */
193
+ export interface ConflictReport {
194
+ /** Whether conflicts were found */
195
+ hasConflicts: boolean;
196
+
197
+ /** Conflict details */
198
+ conflicts: PluginConflict[];
199
+ }
200
+
201
+ /**
202
+ * Plugin conflict
203
+ */
204
+ export interface PluginConflict {
205
+ /** First plugin in conflict */
206
+ plugin1: string;
207
+
208
+ /** Second plugin in conflict */
209
+ plugin2: string;
210
+
211
+ /** Conflict reason */
212
+ reason: string;
213
+
214
+ /** Conflict severity */
215
+ severity: "error" | "warning";
216
+ }
217
+
218
+ /**
219
+ * Validation result
220
+ */
221
+ export interface ValidationResult {
222
+ /** Whether validation passed */
223
+ valid: boolean;
224
+
225
+ /** Validation errors */
226
+ errors: ValidationError[];
227
+
228
+ /** Validation warnings */
229
+ warnings: ValidationWarning[];
230
+ }
231
+
232
+ /**
233
+ * Validation error
234
+ */
235
+ export interface ValidationError {
236
+ /** Plugin ID */
237
+ plugin: string;
238
+
239
+ /** Error message */
240
+ message: string;
241
+
242
+ /** Error code */
243
+ code: string;
244
+ }
245
+
246
+ /**
247
+ * Validation warning
248
+ */
249
+ export interface ValidationWarning {
250
+ /** Plugin ID */
251
+ plugin: string;
252
+
253
+ /** Warning message */
254
+ message: string;
255
+
256
+ /** Warning code */
257
+ code: string;
258
+ }
259
+
260
+ /**
261
+ * Registry statistics
262
+ */
263
+ export interface RegistryStatistics {
264
+ /** Total number of plugins */
265
+ total: number;
266
+
267
+ /** Plugins by category */
268
+ byCategory: Record<PluginCategory, number>;
269
+
270
+ /** Plugins by language */
271
+ byLanguage: Record<string, number>;
272
+
273
+ /** Most popular tags */
274
+ topTags: Array<{ tag: string; count: number }>;
275
+ }
@@ -0,0 +1,240 @@
1
+ /**
2
+ * Plugin Validation
3
+ *
4
+ * Validation utilities for plugin configurations and dependencies.
5
+ *
6
+ * @packageDocumentation
7
+ */
8
+
9
+ import type {
10
+ PluginMetadata,
11
+ ValidationResult,
12
+ ValidationError,
13
+ ValidationWarning,
14
+ } from "./types.js";
15
+
16
+ /**
17
+ * Plugin validator
18
+ */
19
+ export class PluginValidator {
20
+ /**
21
+ * Validate plugin metadata
22
+ *
23
+ * @param metadata - Plugin metadata to validate
24
+ * @returns Validation result
25
+ */
26
+ static validateMetadata(metadata: PluginMetadata): ValidationResult {
27
+ const errors: ValidationError[] = [];
28
+ const warnings: ValidationWarning[] = [];
29
+
30
+ // Check required fields
31
+ if (!metadata.id) {
32
+ errors.push({
33
+ plugin: metadata.id || "unknown",
34
+ message: "Plugin ID is required",
35
+ code: "MISSING_ID",
36
+ });
37
+ }
38
+
39
+ if (!metadata.name) {
40
+ errors.push({
41
+ plugin: metadata.id,
42
+ message: "Plugin name is required",
43
+ code: "MISSING_NAME",
44
+ });
45
+ }
46
+
47
+ if (!metadata.version) {
48
+ errors.push({
49
+ plugin: metadata.id,
50
+ message: "Plugin version is required",
51
+ code: "MISSING_VERSION",
52
+ });
53
+ }
54
+
55
+ if (!metadata.description) {
56
+ warnings.push({
57
+ plugin: metadata.id,
58
+ message: "Plugin description is recommended",
59
+ code: "MISSING_DESCRIPTION",
60
+ });
61
+ }
62
+
63
+ // Validate version format (basic semver check)
64
+ if (metadata.version && !/^\d+\.\d+\.\d+/.test(metadata.version)) {
65
+ warnings.push({
66
+ plugin: metadata.id,
67
+ message: "Version should follow semver format (e.g., 1.0.0)",
68
+ code: "INVALID_VERSION_FORMAT",
69
+ });
70
+ }
71
+
72
+ // Validate category
73
+ const validCategories = [
74
+ "transformer",
75
+ "enhancer",
76
+ "generator",
77
+ "analyzer",
78
+ "utility",
79
+ ];
80
+ if (!validCategories.includes(metadata.category)) {
81
+ errors.push({
82
+ plugin: metadata.id,
83
+ message: `Invalid category "${metadata.category}". Must be one of: ${validCategories.join(", ")}`,
84
+ code: "INVALID_CATEGORY",
85
+ });
86
+ }
87
+
88
+ // Validate tags
89
+ if (!metadata.tags || metadata.tags.length === 0) {
90
+ warnings.push({
91
+ plugin: metadata.id,
92
+ message: "At least one tag is recommended for discoverability",
93
+ code: "MISSING_TAGS",
94
+ });
95
+ }
96
+
97
+ // Validate URLs
98
+ if (metadata.repository && !this.isValidUrl(metadata.repository)) {
99
+ warnings.push({
100
+ plugin: metadata.id,
101
+ message: "Repository URL appears to be invalid",
102
+ code: "INVALID_REPOSITORY_URL",
103
+ });
104
+ }
105
+
106
+ if (metadata.homepage && !this.isValidUrl(metadata.homepage)) {
107
+ warnings.push({
108
+ plugin: metadata.id,
109
+ message: "Homepage URL appears to be invalid",
110
+ code: "INVALID_HOMEPAGE_URL",
111
+ });
112
+ }
113
+
114
+ return {
115
+ valid: errors.length === 0,
116
+ errors,
117
+ warnings,
118
+ };
119
+ }
120
+
121
+ /**
122
+ * Validate plugin compatibility with system
123
+ *
124
+ * @param metadata - Plugin metadata
125
+ * @param glostVersion - Current GLOST version
126
+ * @param nodeVersion - Current Node.js version
127
+ * @returns Validation result
128
+ */
129
+ static validateCompatibility(
130
+ metadata: PluginMetadata,
131
+ glostVersion?: string,
132
+ nodeVersion?: string
133
+ ): ValidationResult {
134
+ const errors: ValidationError[] = [];
135
+ const warnings: ValidationWarning[] = [];
136
+
137
+ // Check GLOST version requirement
138
+ if (metadata.requires?.glostVersion && glostVersion) {
139
+ if (!this.isVersionCompatible(glostVersion, metadata.requires.glostVersion)) {
140
+ errors.push({
141
+ plugin: metadata.id,
142
+ message: `Plugin requires GLOST ${metadata.requires.glostVersion}, but ${glostVersion} is installed`,
143
+ code: "INCOMPATIBLE_GLOST_VERSION",
144
+ });
145
+ }
146
+ }
147
+
148
+ // Check Node.js version requirement
149
+ if (metadata.requires?.nodeVersion && nodeVersion) {
150
+ if (!this.isVersionCompatible(nodeVersion, metadata.requires.nodeVersion)) {
151
+ errors.push({
152
+ plugin: metadata.id,
153
+ message: `Plugin requires Node.js ${metadata.requires.nodeVersion}, but ${nodeVersion} is running`,
154
+ code: "INCOMPATIBLE_NODE_VERSION",
155
+ });
156
+ }
157
+ }
158
+
159
+ return {
160
+ valid: errors.length === 0,
161
+ errors,
162
+ warnings,
163
+ };
164
+ }
165
+
166
+ /**
167
+ * Validate plugin dependencies
168
+ *
169
+ * @param metadata - Plugin metadata
170
+ * @param availablePlugins - Set of available plugin IDs
171
+ * @returns Validation result
172
+ */
173
+ static validateDependencies(
174
+ metadata: PluginMetadata,
175
+ availablePlugins: Set<string>
176
+ ): ValidationResult {
177
+ const errors: ValidationError[] = [];
178
+ const warnings: ValidationWarning[] = [];
179
+
180
+ if (metadata.requires?.plugins) {
181
+ for (const dep of metadata.requires.plugins) {
182
+ if (!availablePlugins.has(dep)) {
183
+ errors.push({
184
+ plugin: metadata.id,
185
+ message: `Required plugin "${dep}" is not available`,
186
+ code: "MISSING_DEPENDENCY",
187
+ });
188
+ }
189
+ }
190
+ }
191
+
192
+ return {
193
+ valid: errors.length === 0,
194
+ errors,
195
+ warnings,
196
+ };
197
+ }
198
+
199
+ /**
200
+ * Check if a version is compatible with a requirement
201
+ *
202
+ * @param version - Actual version
203
+ * @param requirement - Required version or range
204
+ * @returns True if compatible
205
+ */
206
+ private static isVersionCompatible(
207
+ version: string,
208
+ requirement: string
209
+ ): boolean {
210
+ // Simple comparison - in production would use semver library
211
+ // For now, just check if version >= requirement
212
+ const versionParts = version.split(".").map(Number);
213
+ const requirementParts = requirement.replace(/[^\d.]/g, "").split(".").map(Number);
214
+
215
+ for (let i = 0; i < 3; i++) {
216
+ const v = versionParts[i] || 0;
217
+ const r = requirementParts[i] || 0;
218
+
219
+ if (v > r) return true;
220
+ if (v < r) return false;
221
+ }
222
+
223
+ return true;
224
+ }
225
+
226
+ /**
227
+ * Check if a string is a valid URL
228
+ *
229
+ * @param url - URL to check
230
+ * @returns True if valid
231
+ */
232
+ private static isValidUrl(url: string): boolean {
233
+ try {
234
+ new URL(url);
235
+ return true;
236
+ } catch {
237
+ return false;
238
+ }
239
+ }
240
+ }