feat: Implement Plugin Marketplace Manager and Client
- Added PluginMarketplaceManager to coordinate marketplace reads and manage installed plugins. - Introduced PluginMarketplaceClient for fetching marketplace metadata. - Implemented storage management for installed plugins using MemoryStorage. - Added methods for installing, uninstalling, and enabling/disabling plugins. - Created type definitions for PluginMarketplaceManager options and installed plugin records. - Added support for fetching full marketplace catalog and individual plugin metadata. - Included error handling for fetch operations and validation of plugin data. - Updated index.browser.ts to export new modules and types.
This commit is contained in:
94
dist/PluginMarketplaceClient.js
vendored
Normal file
94
dist/PluginMarketplaceClient.js
vendored
Normal file
@@ -0,0 +1,94 @@
|
||||
/**
|
||||
* Host-agnostic client for reading marketplace metadata.
|
||||
*/
|
||||
export class PluginMarketplaceClient {
|
||||
constructor(options) {
|
||||
this.indexUrl = options.indexUrl;
|
||||
this.baseUrl = options.baseUrl;
|
||||
this.fetchFn = options.fetchFn;
|
||||
}
|
||||
/**
|
||||
* Fetches and validates marketplace index.
|
||||
*/
|
||||
async fetchIndex() {
|
||||
const response = await this.getFetchFn()(this.indexUrl);
|
||||
if (!response.ok) {
|
||||
throw new Error(`Failed to fetch plugin index: HTTP ${response.status}`);
|
||||
}
|
||||
return normalizePluginIndex(await response.json());
|
||||
}
|
||||
/**
|
||||
* Fetches and validates single plugin metadata entry.
|
||||
*
|
||||
* @param pluginId - Identifier referenced by marketplace index.
|
||||
*/
|
||||
async fetchPlugin(pluginId) {
|
||||
const response = await this.getFetchFn()(this.buildMetadataUrl(pluginId));
|
||||
if (!response.ok) {
|
||||
throw new Error(`Failed to fetch plugin ${pluginId}: HTTP ${response.status}`);
|
||||
}
|
||||
return normalizePluginMetadata(await response.json(), pluginId);
|
||||
}
|
||||
/**
|
||||
* Fetches full marketplace catalog.
|
||||
*
|
||||
* Entries that fail fetch or validation get skipped so host can still render
|
||||
* rest of catalog.
|
||||
*/
|
||||
async fetchPlugins() {
|
||||
const index = await this.fetchIndex();
|
||||
const results = await Promise.allSettled(index.plugins.map((pluginId) => this.fetchPlugin(pluginId)));
|
||||
return results.flatMap((result) => (result.status === 'fulfilled' ? [result.value] : []));
|
||||
}
|
||||
buildMetadataUrl(pluginId) {
|
||||
const trimmedBaseUrl = this.baseUrl.endsWith('/') ? this.baseUrl : `${this.baseUrl}/`;
|
||||
return `${trimmedBaseUrl}${pluginId}.json`;
|
||||
}
|
||||
getFetchFn() {
|
||||
if (this.fetchFn) {
|
||||
return this.fetchFn;
|
||||
}
|
||||
if (typeof fetch !== 'function') {
|
||||
throw new Error('No fetch implementation available for PluginMarketplaceClient');
|
||||
}
|
||||
return fetch;
|
||||
}
|
||||
}
|
||||
function normalizePluginIndex(value) {
|
||||
const record = asRecord(value, 'Plugin index payload must be object');
|
||||
return {
|
||||
version: asString(record.version),
|
||||
updatedAt: asString(record.updatedAt),
|
||||
plugins: Array.isArray(record.plugins)
|
||||
? record.plugins.filter((entry) => typeof entry === 'string')
|
||||
: [],
|
||||
};
|
||||
}
|
||||
function normalizePluginMetadata(value, fallbackId) {
|
||||
const record = asRecord(value, `Plugin metadata for ${fallbackId} must be object`);
|
||||
return {
|
||||
id: asString(record.id) || fallbackId,
|
||||
name: asString(record.name),
|
||||
version: asString(record.version),
|
||||
description: asString(record.description),
|
||||
author: asString(record.author),
|
||||
repository: asString(record.repository),
|
||||
thumbnail: asString(record.thumbnail),
|
||||
downloadUrl: asString(record.downloadUrl),
|
||||
homepage: asString(record.homepage),
|
||||
tags: Array.isArray(record.tags)
|
||||
? record.tags.filter((entry) => typeof entry === 'string')
|
||||
: [],
|
||||
addedDate: asString(record.addedDate),
|
||||
};
|
||||
}
|
||||
function asRecord(value, message) {
|
||||
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
||||
throw new Error(message);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
function asString(value) {
|
||||
return typeof value === 'string' ? value : '';
|
||||
}
|
||||
//# sourceMappingURL=PluginMarketplaceClient.js.map
|
||||
Reference in New Issue
Block a user