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/LICENSE +21 -0
- package/README.md +283 -0
- package/dist/discovery.d.ts +146 -0
- package/dist/discovery.d.ts.map +1 -0
- package/dist/discovery.js +262 -0
- package/dist/discovery.js.map +1 -0
- package/dist/index.d.ts +35 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +34 -0
- package/dist/index.js.map +1 -0
- package/dist/registry.d.ts +193 -0
- package/dist/registry.d.ts.map +1 -0
- package/dist/registry.js +466 -0
- package/dist/registry.js.map +1 -0
- package/dist/types.d.ts +208 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +9 -0
- package/dist/types.js.map +1 -0
- package/dist/validation.d.ts +53 -0
- package/dist/validation.d.ts.map +1 -0
- package/dist/validation.js +204 -0
- package/dist/validation.js.map +1 -0
- package/package.json +45 -0
- package/src/discovery.ts +315 -0
- package/src/index.ts +50 -0
- package/src/registry.ts +557 -0
- package/src/types.ts +275 -0
- package/src/validation.ts +240 -0
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Plugin Validation
|
|
3
|
+
*
|
|
4
|
+
* Validation utilities for plugin configurations and dependencies.
|
|
5
|
+
*
|
|
6
|
+
* @packageDocumentation
|
|
7
|
+
*/
|
|
8
|
+
import type { PluginMetadata, ValidationResult } from "./types.js";
|
|
9
|
+
/**
|
|
10
|
+
* Plugin validator
|
|
11
|
+
*/
|
|
12
|
+
export declare class PluginValidator {
|
|
13
|
+
/**
|
|
14
|
+
* Validate plugin metadata
|
|
15
|
+
*
|
|
16
|
+
* @param metadata - Plugin metadata to validate
|
|
17
|
+
* @returns Validation result
|
|
18
|
+
*/
|
|
19
|
+
static validateMetadata(metadata: PluginMetadata): ValidationResult;
|
|
20
|
+
/**
|
|
21
|
+
* Validate plugin compatibility with system
|
|
22
|
+
*
|
|
23
|
+
* @param metadata - Plugin metadata
|
|
24
|
+
* @param glostVersion - Current GLOST version
|
|
25
|
+
* @param nodeVersion - Current Node.js version
|
|
26
|
+
* @returns Validation result
|
|
27
|
+
*/
|
|
28
|
+
static validateCompatibility(metadata: PluginMetadata, glostVersion?: string, nodeVersion?: string): ValidationResult;
|
|
29
|
+
/**
|
|
30
|
+
* Validate plugin dependencies
|
|
31
|
+
*
|
|
32
|
+
* @param metadata - Plugin metadata
|
|
33
|
+
* @param availablePlugins - Set of available plugin IDs
|
|
34
|
+
* @returns Validation result
|
|
35
|
+
*/
|
|
36
|
+
static validateDependencies(metadata: PluginMetadata, availablePlugins: Set<string>): ValidationResult;
|
|
37
|
+
/**
|
|
38
|
+
* Check if a version is compatible with a requirement
|
|
39
|
+
*
|
|
40
|
+
* @param version - Actual version
|
|
41
|
+
* @param requirement - Required version or range
|
|
42
|
+
* @returns True if compatible
|
|
43
|
+
*/
|
|
44
|
+
private static isVersionCompatible;
|
|
45
|
+
/**
|
|
46
|
+
* Check if a string is a valid URL
|
|
47
|
+
*
|
|
48
|
+
* @param url - URL to check
|
|
49
|
+
* @returns True if valid
|
|
50
|
+
*/
|
|
51
|
+
private static isValidUrl;
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=validation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../src/validation.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EACV,cAAc,EACd,gBAAgB,EAGjB,MAAM,YAAY,CAAC;AAEpB;;GAEG;AACH,qBAAa,eAAe;IAC1B;;;;;OAKG;IACH,MAAM,CAAC,gBAAgB,CAAC,QAAQ,EAAE,cAAc,GAAG,gBAAgB;IA+FnE;;;;;;;OAOG;IACH,MAAM,CAAC,qBAAqB,CAC1B,QAAQ,EAAE,cAAc,EACxB,YAAY,CAAC,EAAE,MAAM,EACrB,WAAW,CAAC,EAAE,MAAM,GACnB,gBAAgB;IAiCnB;;;;;;OAMG;IACH,MAAM,CAAC,oBAAoB,CACzB,QAAQ,EAAE,cAAc,EACxB,gBAAgB,EAAE,GAAG,CAAC,MAAM,CAAC,GAC5B,gBAAgB;IAuBnB;;;;;;OAMG;IACH,OAAO,CAAC,MAAM,CAAC,mBAAmB;IAoBlC;;;;;OAKG;IACH,OAAO,CAAC,MAAM,CAAC,UAAU;CAQ1B"}
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Plugin Validation
|
|
3
|
+
*
|
|
4
|
+
* Validation utilities for plugin configurations and dependencies.
|
|
5
|
+
*
|
|
6
|
+
* @packageDocumentation
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Plugin validator
|
|
10
|
+
*/
|
|
11
|
+
export class PluginValidator {
|
|
12
|
+
/**
|
|
13
|
+
* Validate plugin metadata
|
|
14
|
+
*
|
|
15
|
+
* @param metadata - Plugin metadata to validate
|
|
16
|
+
* @returns Validation result
|
|
17
|
+
*/
|
|
18
|
+
static validateMetadata(metadata) {
|
|
19
|
+
const errors = [];
|
|
20
|
+
const warnings = [];
|
|
21
|
+
// Check required fields
|
|
22
|
+
if (!metadata.id) {
|
|
23
|
+
errors.push({
|
|
24
|
+
plugin: metadata.id || "unknown",
|
|
25
|
+
message: "Plugin ID is required",
|
|
26
|
+
code: "MISSING_ID",
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
if (!metadata.name) {
|
|
30
|
+
errors.push({
|
|
31
|
+
plugin: metadata.id,
|
|
32
|
+
message: "Plugin name is required",
|
|
33
|
+
code: "MISSING_NAME",
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
if (!metadata.version) {
|
|
37
|
+
errors.push({
|
|
38
|
+
plugin: metadata.id,
|
|
39
|
+
message: "Plugin version is required",
|
|
40
|
+
code: "MISSING_VERSION",
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
if (!metadata.description) {
|
|
44
|
+
warnings.push({
|
|
45
|
+
plugin: metadata.id,
|
|
46
|
+
message: "Plugin description is recommended",
|
|
47
|
+
code: "MISSING_DESCRIPTION",
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
// Validate version format (basic semver check)
|
|
51
|
+
if (metadata.version && !/^\d+\.\d+\.\d+/.test(metadata.version)) {
|
|
52
|
+
warnings.push({
|
|
53
|
+
plugin: metadata.id,
|
|
54
|
+
message: "Version should follow semver format (e.g., 1.0.0)",
|
|
55
|
+
code: "INVALID_VERSION_FORMAT",
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
// Validate category
|
|
59
|
+
const validCategories = [
|
|
60
|
+
"transformer",
|
|
61
|
+
"enhancer",
|
|
62
|
+
"generator",
|
|
63
|
+
"analyzer",
|
|
64
|
+
"utility",
|
|
65
|
+
];
|
|
66
|
+
if (!validCategories.includes(metadata.category)) {
|
|
67
|
+
errors.push({
|
|
68
|
+
plugin: metadata.id,
|
|
69
|
+
message: `Invalid category "${metadata.category}". Must be one of: ${validCategories.join(", ")}`,
|
|
70
|
+
code: "INVALID_CATEGORY",
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
// Validate tags
|
|
74
|
+
if (!metadata.tags || metadata.tags.length === 0) {
|
|
75
|
+
warnings.push({
|
|
76
|
+
plugin: metadata.id,
|
|
77
|
+
message: "At least one tag is recommended for discoverability",
|
|
78
|
+
code: "MISSING_TAGS",
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
// Validate URLs
|
|
82
|
+
if (metadata.repository && !this.isValidUrl(metadata.repository)) {
|
|
83
|
+
warnings.push({
|
|
84
|
+
plugin: metadata.id,
|
|
85
|
+
message: "Repository URL appears to be invalid",
|
|
86
|
+
code: "INVALID_REPOSITORY_URL",
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
if (metadata.homepage && !this.isValidUrl(metadata.homepage)) {
|
|
90
|
+
warnings.push({
|
|
91
|
+
plugin: metadata.id,
|
|
92
|
+
message: "Homepage URL appears to be invalid",
|
|
93
|
+
code: "INVALID_HOMEPAGE_URL",
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
return {
|
|
97
|
+
valid: errors.length === 0,
|
|
98
|
+
errors,
|
|
99
|
+
warnings,
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Validate plugin compatibility with system
|
|
104
|
+
*
|
|
105
|
+
* @param metadata - Plugin metadata
|
|
106
|
+
* @param glostVersion - Current GLOST version
|
|
107
|
+
* @param nodeVersion - Current Node.js version
|
|
108
|
+
* @returns Validation result
|
|
109
|
+
*/
|
|
110
|
+
static validateCompatibility(metadata, glostVersion, nodeVersion) {
|
|
111
|
+
const errors = [];
|
|
112
|
+
const warnings = [];
|
|
113
|
+
// Check GLOST version requirement
|
|
114
|
+
if (metadata.requires?.glostVersion && glostVersion) {
|
|
115
|
+
if (!this.isVersionCompatible(glostVersion, metadata.requires.glostVersion)) {
|
|
116
|
+
errors.push({
|
|
117
|
+
plugin: metadata.id,
|
|
118
|
+
message: `Plugin requires GLOST ${metadata.requires.glostVersion}, but ${glostVersion} is installed`,
|
|
119
|
+
code: "INCOMPATIBLE_GLOST_VERSION",
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
// Check Node.js version requirement
|
|
124
|
+
if (metadata.requires?.nodeVersion && nodeVersion) {
|
|
125
|
+
if (!this.isVersionCompatible(nodeVersion, metadata.requires.nodeVersion)) {
|
|
126
|
+
errors.push({
|
|
127
|
+
plugin: metadata.id,
|
|
128
|
+
message: `Plugin requires Node.js ${metadata.requires.nodeVersion}, but ${nodeVersion} is running`,
|
|
129
|
+
code: "INCOMPATIBLE_NODE_VERSION",
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
return {
|
|
134
|
+
valid: errors.length === 0,
|
|
135
|
+
errors,
|
|
136
|
+
warnings,
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Validate plugin dependencies
|
|
141
|
+
*
|
|
142
|
+
* @param metadata - Plugin metadata
|
|
143
|
+
* @param availablePlugins - Set of available plugin IDs
|
|
144
|
+
* @returns Validation result
|
|
145
|
+
*/
|
|
146
|
+
static validateDependencies(metadata, availablePlugins) {
|
|
147
|
+
const errors = [];
|
|
148
|
+
const warnings = [];
|
|
149
|
+
if (metadata.requires?.plugins) {
|
|
150
|
+
for (const dep of metadata.requires.plugins) {
|
|
151
|
+
if (!availablePlugins.has(dep)) {
|
|
152
|
+
errors.push({
|
|
153
|
+
plugin: metadata.id,
|
|
154
|
+
message: `Required plugin "${dep}" is not available`,
|
|
155
|
+
code: "MISSING_DEPENDENCY",
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
return {
|
|
161
|
+
valid: errors.length === 0,
|
|
162
|
+
errors,
|
|
163
|
+
warnings,
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Check if a version is compatible with a requirement
|
|
168
|
+
*
|
|
169
|
+
* @param version - Actual version
|
|
170
|
+
* @param requirement - Required version or range
|
|
171
|
+
* @returns True if compatible
|
|
172
|
+
*/
|
|
173
|
+
static isVersionCompatible(version, requirement) {
|
|
174
|
+
// Simple comparison - in production would use semver library
|
|
175
|
+
// For now, just check if version >= requirement
|
|
176
|
+
const versionParts = version.split(".").map(Number);
|
|
177
|
+
const requirementParts = requirement.replace(/[^\d.]/g, "").split(".").map(Number);
|
|
178
|
+
for (let i = 0; i < 3; i++) {
|
|
179
|
+
const v = versionParts[i] || 0;
|
|
180
|
+
const r = requirementParts[i] || 0;
|
|
181
|
+
if (v > r)
|
|
182
|
+
return true;
|
|
183
|
+
if (v < r)
|
|
184
|
+
return false;
|
|
185
|
+
}
|
|
186
|
+
return true;
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Check if a string is a valid URL
|
|
190
|
+
*
|
|
191
|
+
* @param url - URL to check
|
|
192
|
+
* @returns True if valid
|
|
193
|
+
*/
|
|
194
|
+
static isValidUrl(url) {
|
|
195
|
+
try {
|
|
196
|
+
new URL(url);
|
|
197
|
+
return true;
|
|
198
|
+
}
|
|
199
|
+
catch {
|
|
200
|
+
return false;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
//# sourceMappingURL=validation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validation.js","sourceRoot":"","sources":["../src/validation.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "glost-registry",
|
|
3
|
+
"version": "0.5.0",
|
|
4
|
+
"description": "Plugin registry with discovery, metadata, and validation for GLOST",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"default": "./dist/index.js"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"files": [
|
|
15
|
+
"dist",
|
|
16
|
+
"src",
|
|
17
|
+
"README.md"
|
|
18
|
+
],
|
|
19
|
+
"keywords": [
|
|
20
|
+
"glost",
|
|
21
|
+
"registry",
|
|
22
|
+
"plugin",
|
|
23
|
+
"discovery",
|
|
24
|
+
"metadata"
|
|
25
|
+
],
|
|
26
|
+
"author": "",
|
|
27
|
+
"license": "MIT",
|
|
28
|
+
"dependencies": {
|
|
29
|
+
"glost-core": "0.5.0",
|
|
30
|
+
"glost-extensions": "0.4.0"
|
|
31
|
+
},
|
|
32
|
+
"devDependencies": {
|
|
33
|
+
"@types/node": "^20.0.0",
|
|
34
|
+
"typescript": "^5.3.3",
|
|
35
|
+
"vitest": "^1.6.0"
|
|
36
|
+
},
|
|
37
|
+
"peerDependencies": {
|
|
38
|
+
"glost-core": "^0.5.0"
|
|
39
|
+
},
|
|
40
|
+
"scripts": {
|
|
41
|
+
"build": "tsc",
|
|
42
|
+
"test": "vitest run --passWithNoTests",
|
|
43
|
+
"test:watch": "vitest"
|
|
44
|
+
}
|
|
45
|
+
}
|
package/src/discovery.ts
ADDED
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Plugin Discovery
|
|
3
|
+
*
|
|
4
|
+
* Utilities for discovering and filtering plugins.
|
|
5
|
+
*
|
|
6
|
+
* @packageDocumentation
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type { PluginMetadata, PluginQuery, PluginCategory } from "./types.js";
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Filter plugins by various criteria
|
|
13
|
+
*/
|
|
14
|
+
export class PluginFilter {
|
|
15
|
+
/**
|
|
16
|
+
* Filter plugins by keyword
|
|
17
|
+
*
|
|
18
|
+
* @param plugins - Plugins to filter
|
|
19
|
+
* @param keyword - Keyword to search for
|
|
20
|
+
* @returns Filtered plugins
|
|
21
|
+
*/
|
|
22
|
+
static byKeyword(plugins: PluginMetadata[], keyword: string): PluginMetadata[] {
|
|
23
|
+
const lowerKeyword = keyword.toLowerCase();
|
|
24
|
+
return plugins.filter((plugin) => {
|
|
25
|
+
return (
|
|
26
|
+
plugin.name.toLowerCase().includes(lowerKeyword) ||
|
|
27
|
+
plugin.description.toLowerCase().includes(lowerKeyword) ||
|
|
28
|
+
plugin.id.toLowerCase().includes(lowerKeyword) ||
|
|
29
|
+
plugin.tags.some((tag) => tag.toLowerCase().includes(lowerKeyword))
|
|
30
|
+
);
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Filter plugins by category
|
|
36
|
+
*
|
|
37
|
+
* @param plugins - Plugins to filter
|
|
38
|
+
* @param category - Category to filter by
|
|
39
|
+
* @returns Filtered plugins
|
|
40
|
+
*/
|
|
41
|
+
static byCategory(
|
|
42
|
+
plugins: PluginMetadata[],
|
|
43
|
+
category: PluginCategory
|
|
44
|
+
): PluginMetadata[] {
|
|
45
|
+
return plugins.filter((plugin) => plugin.category === category);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Filter plugins by language support
|
|
50
|
+
*
|
|
51
|
+
* @param plugins - Plugins to filter
|
|
52
|
+
* @param language - Language code
|
|
53
|
+
* @returns Filtered plugins
|
|
54
|
+
*/
|
|
55
|
+
static byLanguage(plugins: PluginMetadata[], language: string): PluginMetadata[] {
|
|
56
|
+
return plugins.filter((plugin) => {
|
|
57
|
+
return (
|
|
58
|
+
!plugin.supports.languages ||
|
|
59
|
+
plugin.supports.languages.includes(language)
|
|
60
|
+
);
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Filter plugins by tags
|
|
66
|
+
*
|
|
67
|
+
* @param plugins - Plugins to filter
|
|
68
|
+
* @param tags - Tags to match (any)
|
|
69
|
+
* @returns Filtered plugins
|
|
70
|
+
*/
|
|
71
|
+
static byTags(plugins: PluginMetadata[], tags: string[]): PluginMetadata[] {
|
|
72
|
+
return plugins.filter((plugin) => {
|
|
73
|
+
return tags.some((tag) => plugin.tags.includes(tag));
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Filter plugins by author
|
|
79
|
+
*
|
|
80
|
+
* @param plugins - Plugins to filter
|
|
81
|
+
* @param author - Author name
|
|
82
|
+
* @returns Filtered plugins
|
|
83
|
+
*/
|
|
84
|
+
static byAuthor(plugins: PluginMetadata[], author: string): PluginMetadata[] {
|
|
85
|
+
return plugins.filter((plugin) => plugin.author === author);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Filter plugins that support async
|
|
90
|
+
*
|
|
91
|
+
* @param plugins - Plugins to filter
|
|
92
|
+
* @returns Filtered plugins
|
|
93
|
+
*/
|
|
94
|
+
static asyncOnly(plugins: PluginMetadata[]): PluginMetadata[] {
|
|
95
|
+
return plugins.filter((plugin) => plugin.supports.async);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Filter plugins that support parallel execution
|
|
100
|
+
*
|
|
101
|
+
* @param plugins - Plugins to filter
|
|
102
|
+
* @returns Filtered plugins
|
|
103
|
+
*/
|
|
104
|
+
static parallelOnly(plugins: PluginMetadata[]): PluginMetadata[] {
|
|
105
|
+
return plugins.filter((plugin) => plugin.supports.parallel);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Apply multiple filters
|
|
110
|
+
*
|
|
111
|
+
* @param plugins - Plugins to filter
|
|
112
|
+
* @param query - Filter query
|
|
113
|
+
* @returns Filtered plugins
|
|
114
|
+
*/
|
|
115
|
+
static apply(plugins: PluginMetadata[], query: PluginQuery): PluginMetadata[] {
|
|
116
|
+
let result = plugins;
|
|
117
|
+
|
|
118
|
+
if (query.keyword) {
|
|
119
|
+
result = this.byKeyword(result, query.keyword);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (query.category) {
|
|
123
|
+
result = this.byCategory(result, query.category);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if (query.language) {
|
|
127
|
+
result = this.byLanguage(result, query.language);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
if (query.tags && query.tags.length > 0) {
|
|
131
|
+
result = this.byTags(result, query.tags);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
if (query.author) {
|
|
135
|
+
result = this.byAuthor(result, query.author);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return result;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Sort plugins by various criteria
|
|
144
|
+
*/
|
|
145
|
+
export class PluginSorter {
|
|
146
|
+
/**
|
|
147
|
+
* Sort plugins by name
|
|
148
|
+
*
|
|
149
|
+
* @param plugins - Plugins to sort
|
|
150
|
+
* @param ascending - Sort order
|
|
151
|
+
* @returns Sorted plugins
|
|
152
|
+
*/
|
|
153
|
+
static byName(plugins: PluginMetadata[], ascending = true): PluginMetadata[] {
|
|
154
|
+
return [...plugins].sort((a, b) => {
|
|
155
|
+
const result = a.name.localeCompare(b.name);
|
|
156
|
+
return ascending ? result : -result;
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Sort plugins by registration date
|
|
162
|
+
*
|
|
163
|
+
* @param plugins - Plugins to sort
|
|
164
|
+
* @param ascending - Sort order
|
|
165
|
+
* @returns Sorted plugins
|
|
166
|
+
*/
|
|
167
|
+
static byDate(plugins: PluginMetadata[], ascending = true): PluginMetadata[] {
|
|
168
|
+
return [...plugins].sort((a, b) => {
|
|
169
|
+
const dateA = a.registeredAt?.getTime() || 0;
|
|
170
|
+
const dateB = b.registeredAt?.getTime() || 0;
|
|
171
|
+
const result = dateA - dateB;
|
|
172
|
+
return ascending ? result : -result;
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Sort plugins by category
|
|
178
|
+
*
|
|
179
|
+
* @param plugins - Plugins to sort
|
|
180
|
+
* @returns Sorted plugins
|
|
181
|
+
*/
|
|
182
|
+
static byCategory(plugins: PluginMetadata[]): PluginMetadata[] {
|
|
183
|
+
const order: PluginCategory[] = [
|
|
184
|
+
"generator",
|
|
185
|
+
"transformer",
|
|
186
|
+
"enhancer",
|
|
187
|
+
"analyzer",
|
|
188
|
+
"utility",
|
|
189
|
+
];
|
|
190
|
+
|
|
191
|
+
return [...plugins].sort((a, b) => {
|
|
192
|
+
return order.indexOf(a.category) - order.indexOf(b.category);
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Plugin discovery helpers
|
|
199
|
+
*/
|
|
200
|
+
export class PluginDiscovery {
|
|
201
|
+
/**
|
|
202
|
+
* Find plugins related to a given plugin
|
|
203
|
+
*
|
|
204
|
+
* @param plugins - All plugins
|
|
205
|
+
* @param pluginId - Plugin to find related plugins for
|
|
206
|
+
* @returns Related plugins
|
|
207
|
+
*/
|
|
208
|
+
static findRelated(
|
|
209
|
+
plugins: PluginMetadata[],
|
|
210
|
+
pluginId: string
|
|
211
|
+
): PluginMetadata[] {
|
|
212
|
+
const plugin = plugins.find((p) => p.id === pluginId);
|
|
213
|
+
if (!plugin) {
|
|
214
|
+
return [];
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// Find plugins with similar tags
|
|
218
|
+
return plugins.filter((p) => {
|
|
219
|
+
if (p.id === pluginId) {
|
|
220
|
+
return false;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
const sharedTags = plugin.tags.filter((tag) => p.tags.includes(tag));
|
|
224
|
+
return sharedTags.length > 0;
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Find plugins that depend on a given plugin
|
|
230
|
+
*
|
|
231
|
+
* @param plugins - All plugins
|
|
232
|
+
* @param allExtensions - Map of extensions (for dependency info)
|
|
233
|
+
* @param pluginId - Plugin to find dependents for
|
|
234
|
+
* @returns Dependent plugins
|
|
235
|
+
*/
|
|
236
|
+
static findDependents(
|
|
237
|
+
plugins: PluginMetadata[],
|
|
238
|
+
allExtensions: Map<string, { dependencies?: string[] }>,
|
|
239
|
+
pluginId: string
|
|
240
|
+
): PluginMetadata[] {
|
|
241
|
+
return plugins.filter((p) => {
|
|
242
|
+
const ext = allExtensions.get(p.id);
|
|
243
|
+
return ext?.dependencies?.includes(pluginId);
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* Find missing dependencies for plugins
|
|
249
|
+
*
|
|
250
|
+
* @param plugins - Plugins to check
|
|
251
|
+
* @param allExtensions - Map of all extensions
|
|
252
|
+
* @returns Map of plugin ID to missing dependencies
|
|
253
|
+
*/
|
|
254
|
+
static findMissingDependencies(
|
|
255
|
+
plugins: PluginMetadata[],
|
|
256
|
+
allExtensions: Map<string, { dependencies?: string[] }>
|
|
257
|
+
): Map<string, string[]> {
|
|
258
|
+
const missing = new Map<string, string[]>();
|
|
259
|
+
const availableIds = new Set(plugins.map((p) => p.id));
|
|
260
|
+
|
|
261
|
+
for (const plugin of plugins) {
|
|
262
|
+
const ext = allExtensions.get(plugin.id);
|
|
263
|
+
if (ext?.dependencies) {
|
|
264
|
+
const missingDeps = ext.dependencies.filter(
|
|
265
|
+
(dep) => !availableIds.has(dep)
|
|
266
|
+
);
|
|
267
|
+
if (missingDeps.length > 0) {
|
|
268
|
+
missing.set(plugin.id, missingDeps);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
return missing;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* Suggest plugins based on a user's current plugins
|
|
278
|
+
*
|
|
279
|
+
* @param currentPlugins - Currently used plugins
|
|
280
|
+
* @param allPlugins - All available plugins
|
|
281
|
+
* @returns Suggested plugins
|
|
282
|
+
*/
|
|
283
|
+
static suggestPlugins(
|
|
284
|
+
currentPlugins: PluginMetadata[],
|
|
285
|
+
allPlugins: PluginMetadata[]
|
|
286
|
+
): PluginMetadata[] {
|
|
287
|
+
if (currentPlugins.length === 0) {
|
|
288
|
+
return [];
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
// Collect all tags from current plugins
|
|
292
|
+
const currentTags = new Set<string>();
|
|
293
|
+
for (const plugin of currentPlugins) {
|
|
294
|
+
plugin.tags.forEach((tag) => currentTags.add(tag));
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
// Find plugins with similar tags that aren't already used
|
|
298
|
+
const currentIds = new Set(currentPlugins.map((p) => p.id));
|
|
299
|
+
const suggestions = allPlugins.filter((plugin) => {
|
|
300
|
+
if (currentIds.has(plugin.id)) {
|
|
301
|
+
return false;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
const matchingTags = plugin.tags.filter((tag) => currentTags.has(tag));
|
|
305
|
+
return matchingTags.length > 0;
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
// Sort by number of matching tags
|
|
309
|
+
return suggestions.sort((a, b) => {
|
|
310
|
+
const aMatches = a.tags.filter((tag) => currentTags.has(tag)).length;
|
|
311
|
+
const bMatches = b.tags.filter((tag) => currentTags.has(tag)).length;
|
|
312
|
+
return bMatches - aMatches;
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GLOST Plugin Registry
|
|
3
|
+
*
|
|
4
|
+
* Enhanced plugin registry with discovery, metadata, and validation.
|
|
5
|
+
*
|
|
6
|
+
* @packageDocumentation
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```typescript
|
|
10
|
+
* import { pluginRegistry } from "glost-registry";
|
|
11
|
+
*
|
|
12
|
+
* // Register a plugin
|
|
13
|
+
* pluginRegistry.register(myExtension, {
|
|
14
|
+
* version: "1.0.0",
|
|
15
|
+
* description: "My awesome plugin",
|
|
16
|
+
* category: "enhancer",
|
|
17
|
+
* tags: ["transcription"],
|
|
18
|
+
* supports: {
|
|
19
|
+
* languages: ["th", "ja"],
|
|
20
|
+
* async: true
|
|
21
|
+
* }
|
|
22
|
+
* });
|
|
23
|
+
*
|
|
24
|
+
* // Search for plugins
|
|
25
|
+
* const plugins = pluginRegistry.search({ language: "th" });
|
|
26
|
+
*
|
|
27
|
+
* // Check for conflicts
|
|
28
|
+
* const report = pluginRegistry.checkConflicts(["plugin1", "plugin2"]);
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
export { PluginRegistry, pluginRegistry } from "./registry.js";
|
|
33
|
+
export { PluginFilter, PluginSorter, PluginDiscovery } from "./discovery.js";
|
|
34
|
+
export { PluginValidator } from "./validation.js";
|
|
35
|
+
export type {
|
|
36
|
+
PluginMetadata,
|
|
37
|
+
PluginCategory,
|
|
38
|
+
PluginCapabilities,
|
|
39
|
+
PluginRequirements,
|
|
40
|
+
PluginOptionsSchema,
|
|
41
|
+
PropertySchema,
|
|
42
|
+
PluginExample,
|
|
43
|
+
PluginQuery,
|
|
44
|
+
ConflictReport,
|
|
45
|
+
PluginConflict,
|
|
46
|
+
ValidationResult,
|
|
47
|
+
ValidationError,
|
|
48
|
+
ValidationWarning,
|
|
49
|
+
RegistryStatistics,
|
|
50
|
+
} from "./types.js";
|