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/dist/types.js ADDED
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Plugin Registry Types
3
+ *
4
+ * Type definitions for the enhanced plugin registry.
5
+ *
6
+ * @packageDocumentation
7
+ */
8
+ export {};
9
+ //# sourceMappingURL=types.js.map
@@ -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
+ }
@@ -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";