@springfield/ham-radio-registry 1.0.1
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 +19 -0
- package/README.md +93 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -0
- package/dist/registry/config-registry.d.ts +51 -0
- package/dist/registry/config-registry.d.ts.map +1 -0
- package/dist/registry/config-registry.js +280 -0
- package/dist/registry/config-registry.js.map +1 -0
- package/dist/registry/shared-components.d.ts +14 -0
- package/dist/registry/shared-components.d.ts.map +1 -0
- package/dist/registry/shared-components.js +68 -0
- package/dist/registry/shared-components.js.map +1 -0
- package/dist/types/plugin-module.d.ts +90 -0
- package/dist/types/plugin-module.d.ts.map +1 -0
- package/dist/types/plugin-module.js +2 -0
- package/dist/types/plugin-module.js.map +1 -0
- package/dist/types/radio-config.d.ts +40 -0
- package/dist/types/radio-config.d.ts.map +1 -0
- package/dist/types/radio-config.js +2 -0
- package/dist/types/radio-config.js.map +1 -0
- package/dist/utils/npm-client.d.ts +27 -0
- package/dist/utils/npm-client.d.ts.map +1 -0
- package/dist/utils/npm-client.js +111 -0
- package/dist/utils/npm-client.js.map +1 -0
- package/package.json +73 -0
- package/src/index.ts +37 -0
- package/src/registry/config-registry.ts +379 -0
- package/src/registry/shared-components.ts +80 -0
- package/src/types/plugin-module.ts +96 -0
- package/src/types/radio-config.ts +43 -0
- package/src/utils/npm-client.ts +167 -0
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import type { ILogLayer } from 'loglayer';
|
|
2
|
+
import type { NpmPluginInfo } from '../types/plugin-module.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* NPM registry client for plugin discovery and validation
|
|
6
|
+
*/
|
|
7
|
+
export interface NpmClient {
|
|
8
|
+
// Get package information from npm registry
|
|
9
|
+
getPackageInfo(packageName: string): Promise<any>;
|
|
10
|
+
|
|
11
|
+
// Search for packages with query
|
|
12
|
+
searchPackages(query: string): Promise<NpmPluginInfo[]>;
|
|
13
|
+
|
|
14
|
+
// Get package download statistics
|
|
15
|
+
getPackageStats(packageName: string): Promise<any>;
|
|
16
|
+
|
|
17
|
+
// Validate package exists and is accessible
|
|
18
|
+
validatePackage(packageName: string): Promise<boolean>;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* NPM search response interface
|
|
23
|
+
*/
|
|
24
|
+
interface NpmSearchResponse {
|
|
25
|
+
objects: Array<{
|
|
26
|
+
package: {
|
|
27
|
+
name: string;
|
|
28
|
+
version: string;
|
|
29
|
+
description?: string;
|
|
30
|
+
author?: any;
|
|
31
|
+
license?: string;
|
|
32
|
+
repository?: { url: string };
|
|
33
|
+
homepage?: string;
|
|
34
|
+
keywords?: string[];
|
|
35
|
+
date?: string;
|
|
36
|
+
springfield?: any;
|
|
37
|
+
};
|
|
38
|
+
}>;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Default implementation of NPM client using npm registry API
|
|
43
|
+
*/
|
|
44
|
+
export class DefaultNpmClient implements NpmClient {
|
|
45
|
+
private logger: ILogLayer;
|
|
46
|
+
private baseUrl = 'https://registry.npmjs.org';
|
|
47
|
+
|
|
48
|
+
constructor(logger: ILogLayer) {
|
|
49
|
+
this.logger = logger;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
async getPackageInfo(packageName: string): Promise<any> {
|
|
53
|
+
try {
|
|
54
|
+
const response = await fetch(`${this.baseUrl}/${packageName}`);
|
|
55
|
+
|
|
56
|
+
if (!response.ok) {
|
|
57
|
+
if (response.status === 404) {
|
|
58
|
+
throw new Error(`Package not found: ${packageName}`);
|
|
59
|
+
}
|
|
60
|
+
throw new Error(`Failed to fetch package info: ${response.statusText}`);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const packageInfo = await response.json();
|
|
64
|
+
|
|
65
|
+
this.logger.withMetadata({ packageName }).info('Retrieved package info');
|
|
66
|
+
return packageInfo;
|
|
67
|
+
} catch (error) {
|
|
68
|
+
this.logger.withError(error).error('Failed to get package info');
|
|
69
|
+
throw error;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
async searchPackages(query: string): Promise<NpmPluginInfo[]> {
|
|
74
|
+
try {
|
|
75
|
+
const searchQuery = encodeURIComponent(query);
|
|
76
|
+
const response = await fetch(`${this.baseUrl}/-/v1/search?text=${searchQuery}&size=50`);
|
|
77
|
+
|
|
78
|
+
if (!response.ok) {
|
|
79
|
+
throw new Error(`Failed to search packages: ${response.statusText}`);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const searchResults = (await response.json()) as NpmSearchResponse;
|
|
83
|
+
const packages = searchResults.objects || [];
|
|
84
|
+
|
|
85
|
+
// Filter and transform results
|
|
86
|
+
const pluginPackages: NpmPluginInfo[] = [];
|
|
87
|
+
|
|
88
|
+
for (const pkg of packages) {
|
|
89
|
+
const packageInfo = pkg.package;
|
|
90
|
+
|
|
91
|
+
// Check if this is a radio module
|
|
92
|
+
if (this.isRadioModule(packageInfo)) {
|
|
93
|
+
pluginPackages.push(this.transformToNpmPluginInfo(packageInfo));
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
this.logger.withMetadata({ query, results: pluginPackages.length }).info('Searched packages');
|
|
98
|
+
return pluginPackages;
|
|
99
|
+
} catch (error) {
|
|
100
|
+
this.logger.withError(error).error('Failed to search packages');
|
|
101
|
+
throw error;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
async getPackageStats(packageName: string): Promise<any> {
|
|
106
|
+
try {
|
|
107
|
+
// Note: npm registry doesn't provide download stats via public API
|
|
108
|
+
// This would require npmjs.com API or alternative data source
|
|
109
|
+
this.logger.withMetadata({ packageName }).warn('Package stats not available via public npm registry API');
|
|
110
|
+
return { downloads: 0, lastUpdated: new Date().toISOString() };
|
|
111
|
+
} catch (error) {
|
|
112
|
+
this.logger.withError(error).error('Failed to get package stats');
|
|
113
|
+
throw error;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
async validatePackage(packageName: string): Promise<boolean> {
|
|
118
|
+
try {
|
|
119
|
+
await this.getPackageInfo(packageName);
|
|
120
|
+
return true;
|
|
121
|
+
} catch (error) {
|
|
122
|
+
if (error instanceof Error && error.message.includes('not found')) {
|
|
123
|
+
return false;
|
|
124
|
+
}
|
|
125
|
+
throw error;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
private isRadioModule(packageInfo: any): boolean {
|
|
130
|
+
const name = packageInfo.name || '';
|
|
131
|
+
|
|
132
|
+
// Check naming convention
|
|
133
|
+
const isNamedCorrectly = name.includes('radio-module') || name.startsWith('@springfield/radio-module-');
|
|
134
|
+
|
|
135
|
+
// Check springfield plugin field
|
|
136
|
+
const hasSpringfieldField = packageInfo.springfield?.pluginType === 'radio-module';
|
|
137
|
+
|
|
138
|
+
// Check keywords
|
|
139
|
+
const hasKeywords = packageInfo.keywords?.includes('radio-module');
|
|
140
|
+
|
|
141
|
+
return isNamedCorrectly || hasSpringfieldField || hasKeywords;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
private transformToNpmPluginInfo(packageInfo: any): NpmPluginInfo {
|
|
145
|
+
return {
|
|
146
|
+
name: packageInfo.name,
|
|
147
|
+
version: packageInfo.version,
|
|
148
|
+
description: packageInfo.description || '',
|
|
149
|
+
author: this.extractAuthor(packageInfo.author),
|
|
150
|
+
license: packageInfo.license || 'Unknown',
|
|
151
|
+
repository: packageInfo.repository?.url || packageInfo.homepage,
|
|
152
|
+
downloads: 0, // Not available via public API
|
|
153
|
+
lastUpdated: packageInfo.date || new Date().toISOString(),
|
|
154
|
+
springfield: packageInfo.springfield,
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
private extractAuthor(author: any): string {
|
|
159
|
+
if (typeof author === 'string') {
|
|
160
|
+
return author;
|
|
161
|
+
}
|
|
162
|
+
if (typeof author === 'object' && author.name) {
|
|
163
|
+
return author.name;
|
|
164
|
+
}
|
|
165
|
+
return 'Unknown';
|
|
166
|
+
}
|
|
167
|
+
}
|