UNPKG

@ubiquity-os/plugin-sdk

Version:

SDK for plugin support.

741 lines (733 loc) 28.3 kB
"use strict"; var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // src/configuration.ts var configuration_exports = {}; __export(configuration_exports, { CONFIG_DEV_FULL_PATH: () => CONFIG_DEV_FULL_PATH, CONFIG_ORG_REPO: () => CONFIG_ORG_REPO, CONFIG_PROD_FULL_PATH: () => CONFIG_PROD_FULL_PATH, ConfigurationHandler: () => ConfigurationHandler, configSchema: () => configSchema, isGithubPlugin: () => isGithubPlugin }); module.exports = __toCommonJS(configuration_exports); var import_value = require("@sinclair/typebox/value"); var import_js_yaml = __toESM(require("js-yaml")); var import_node_buffer = require("node:buffer"); // src/configuration/schema.ts var import_webhooks = require("@octokit/webhooks"); var import_typebox = require("@sinclair/typebox"); var pluginNameRegex = new RegExp("^([0-9a-zA-Z-._]+)\\/([0-9a-zA-Z-._]+)(?::([0-9a-zA-Z-._]+))?(?:@([0-9a-zA-Z-._]+(?:\\/[0-9a-zA-Z-._]+)*))?$"); var urlRegex = /^https?:\/\/\S+$/; function parsePluginIdentifier(value) { if (urlRegex.test(value)) { return value; } const matches = pluginNameRegex.exec(value); if (!matches) { throw new Error(`Invalid plugin name: ${value}`); } return { owner: matches[1], repo: matches[2], workflowId: matches[3] || "compute.yml", ref: matches[4] || void 0 }; } function stringLiteralUnion(values) { const literals = values.map((value) => import_typebox.Type.Literal(value)); return import_typebox.Type.Union(literals); } var emitterType = stringLiteralUnion(import_webhooks.emitterEventNames); var runsOnSchema = import_typebox.Type.Array(emitterType, { default: [] }); var pluginSettingsSchema = import_typebox.Type.Union( [ import_typebox.Type.Null(), import_typebox.Type.Object( { with: import_typebox.Type.Record(import_typebox.Type.String(), import_typebox.Type.Unknown(), { default: {} }), runsOn: import_typebox.Type.Optional(runsOnSchema), skipBotEvents: import_typebox.Type.Optional(import_typebox.Type.Boolean()) }, { default: {} } ) ], { default: null } ); var configSchema = import_typebox.Type.Object( { imports: import_typebox.Type.Optional(import_typebox.Type.Array(import_typebox.Type.String(), { default: [] })), plugins: import_typebox.Type.Record(import_typebox.Type.String(), pluginSettingsSchema, { default: {} }) }, { additionalProperties: true } ); // src/helpers/urls.ts function normalizeBaseUrl(baseUrl) { let normalized = baseUrl.trim(); while (normalized.endsWith("/")) { normalized = normalized.slice(0, -1); } return normalized; } // src/types/manifest.ts var import_typebox2 = require("@sinclair/typebox"); var import_webhooks2 = require("@octokit/webhooks"); // src/helpers/runtime-manifest.ts var EMPTY_VALUE = String(); // src/types/manifest.ts var runEvent = import_typebox2.Type.Union(import_webhooks2.emitterEventNames.map((o) => import_typebox2.Type.Literal(o))); var exampleCommandExecutionSchema = import_typebox2.Type.Object({ commandInvocation: import_typebox2.Type.String({ minLength: 1 }), githubContext: import_typebox2.Type.Optional(import_typebox2.Type.Record(import_typebox2.Type.String(), import_typebox2.Type.Any())), expectedToolCallResult: import_typebox2.Type.Object({ function: import_typebox2.Type.String({ minLength: 1 }), parameters: import_typebox2.Type.Record(import_typebox2.Type.String(), import_typebox2.Type.Any()) }) }); var commandSchema = import_typebox2.Type.Object({ description: import_typebox2.Type.String({ minLength: 1 }), "ubiquity:example": import_typebox2.Type.String({ minLength: 1 }), parameters: import_typebox2.Type.Optional(import_typebox2.Type.Record(import_typebox2.Type.String(), import_typebox2.Type.Any())), examples: import_typebox2.Type.Optional(import_typebox2.Type.Array(exampleCommandExecutionSchema, { default: [] })) }); var manifestSchema = import_typebox2.Type.Object({ name: import_typebox2.Type.String({ minLength: 1 }), short_name: import_typebox2.Type.String({ minLength: 1 }), description: import_typebox2.Type.Optional(import_typebox2.Type.String({ default: "" })), commands: import_typebox2.Type.Optional(import_typebox2.Type.Record(import_typebox2.Type.String({ pattern: "^[A-Za-z-_]+$" }), commandSchema, { default: {} })), "ubiquity:listeners": import_typebox2.Type.Optional(import_typebox2.Type.Array(runEvent, { default: [] })), configuration: import_typebox2.Type.Optional(import_typebox2.Type.Record(import_typebox2.Type.String(), import_typebox2.Type.Any(), { default: {} })), skipBotEvents: import_typebox2.Type.Optional(import_typebox2.Type.Boolean({ default: true })), homepage_url: import_typebox2.Type.Optional(import_typebox2.Type.String()) }); // src/configuration.ts var CONFIG_PROD_FULL_PATH = ".github/.ubiquity-os.config.yml"; var CONFIG_DEV_FULL_PATH = ".github/.ubiquity-os.config.dev.yml"; var CONFIG_ORG_REPO = ".ubiquity-os"; var EMPTY_STRING = ""; var ENVIRONMENT_TO_CONFIG_SUFFIX = { development: "dev" }; var VALID_CONFIG_SUFFIX = /^[a-z0-9][a-z0-9_-]*$/i; var MAX_IMPORT_DEPTH = 6; function normalizeEnvironmentName(environment) { return String(environment ?? EMPTY_STRING).trim().toLowerCase(); } function getConfigPathCandidatesForEnvironment(environment) { const normalized = normalizeEnvironmentName(environment); if (!normalized) { return [CONFIG_PROD_FULL_PATH, CONFIG_DEV_FULL_PATH]; } if (normalized === "production" || normalized === "prod") { return [CONFIG_PROD_FULL_PATH]; } const suffix = ENVIRONMENT_TO_CONFIG_SUFFIX[normalized] ?? normalized; if (suffix === "dev") { return [CONFIG_DEV_FULL_PATH]; } if (!VALID_CONFIG_SUFFIX.test(suffix)) { return [CONFIG_DEV_FULL_PATH]; } return [`.github/.ubiquity-os.config.${suffix}.yml`, CONFIG_PROD_FULL_PATH]; } function normalizeImportKey(location) { const env = normalizeEnvironmentName(location.environment ?? "") || "default"; return `${location.owner}`.trim().toLowerCase() + "/" + `${location.repo}`.trim().toLowerCase() + "@" + env; } function isHttpUrl(value) { const trimmed = value.trim(); return trimmed.startsWith("http://") || trimmed.startsWith("https://"); } function resolveManifestUrl(pluginUrl) { try { const parsed = new URL(pluginUrl.trim()); let pathname = parsed.pathname; while (pathname.endsWith("/") && pathname.length > 1) { pathname = pathname.slice(0, -1); } if (pathname === "/") { pathname = EMPTY_STRING; } if (pathname.endsWith(".json")) { parsed.search = EMPTY_STRING; parsed.hash = EMPTY_STRING; return parsed.toString(); } parsed.pathname = `${pathname}/manifest.json`; parsed.search = EMPTY_STRING; parsed.hash = EMPTY_STRING; return parsed.toString(); } catch { return null; } } function parseImportSpec(value, defaultEnvironment) { const trimmed = value.trim(); if (!trimmed) return null; const [repoSpec, envSpecRaw] = trimmed.split("@"); const parts = repoSpec.split("/"); if (parts.length !== 2) return null; const [owner, repo] = parts; if (!owner || !repo) return null; const envSpec = envSpecRaw?.trim(); if (envSpec && !VALID_CONFIG_SUFFIX.test(envSpec)) { return null; } const inheritedEnvironment = defaultEnvironment?.trim(); return { owner, repo, environment: envSpec || inheritedEnvironment || void 0 }; } function readImports(logger, value, source) { if (!value) return []; if (!Array.isArray(value)) { logger.warn("Invalid imports; expected a list of strings.", { source }); return []; } const seen = /* @__PURE__ */ new Set(); const imports = []; for (const entry of value) { if (typeof entry !== "string") { logger.warn("Ignoring invalid import entry; expected string.", { source, entry }); continue; } const parsed = parseImportSpec(entry, source.environment); if (!parsed) { logger.warn("Ignoring invalid import entry; expected owner/repo.", { source, entry }); continue; } const key = normalizeImportKey(parsed); if (seen.has(key)) continue; seen.add(key); imports.push(parsed); } return imports; } function stripImports(config) { if (!config || typeof config !== "object") return config; const rest = { ...config }; delete rest.imports; return rest; } function unwrapTypeBoxError(error) { if (error && typeof error === "object" && "error" in error) { const inner = error.error; return inner ?? error; } return error; } function mergeImportedConfigs(imported, base) { if (!imported.length) { return base; } let merged = imported[0]; for (let i = 1; i < imported.length; i++) { merged = { ...merged, ...imported[i], plugins: { ...merged.plugins, ...imported[i].plugins } }; } return base ? { ...merged, ...base, plugins: { ...merged.plugins, ...base.plugins } } : merged; } function logOk(logger, message, metadata) { if (logger.ok) { logger.ok(message, metadata); } else { logger.info(message, metadata); } } function isGithubPlugin(plugin) { return typeof plugin !== "string"; } var ConfigurationHandler = class { constructor(_logger, _octokit, _environment = null, options) { this._logger = _logger; this._octokit = _octokit; this._environment = _environment; this._octokitFactory = options?.octokitFactory; } _manifestCache = {}; _manifestPromiseCache = {}; _octokitFactory; /** * Retrieves the configuration for the current plugin based on its manifest. * @param manifest - The plugin manifest containing the `short_name` identifier * @param location - Optional repository location (`owner/repo`) * @param options - Optional refs for organization/repository configuration lookups * @returns The plugin's configuration or null if not found **/ async getSelfConfiguration(manifest, location, options) { const cfg = await this.getConfiguration(location, options); let selfConfig; if (manifest.homepage_url) { const name = manifest.homepage_url; selfConfig = Object.keys(cfg.plugins).find((key) => normalizeBaseUrl(key) === normalizeBaseUrl(name)); } if (!selfConfig) { const name = manifest.short_name.split("@")[0]; selfConfig = Object.keys(cfg.plugins).find((key) => new RegExp(`^${name}(?:$|@.+)`).exec(key.replace(/:[^@]+/, ""))); } return selfConfig && cfg.plugins[selfConfig] ? cfg.plugins[selfConfig]?.["with"] : null; } /** * Retrieves and merges configuration from organization and repository levels. * @param location - Optional repository location (`owner` and `repo`). If not provided, returns the default configuration. * @param options - Optional refs for organization/repository configuration lookups * @returns The merged plugin configuration with resolved plugin settings. */ async getConfiguration(location, options) { const defaultConfiguration = stripImports(import_value.Value.Decode(configSchema, import_value.Value.Default(configSchema, {}))); if (!location) { this._logger.info("No location was provided, using the default configuration"); return defaultConfiguration; } const mergedConfiguration = await this._getMergedConfiguration(location, defaultConfiguration, options); const resolvedPlugins = await this._resolvePlugins(mergedConfiguration, location); return { ...mergedConfiguration, plugins: resolvedPlugins }; } async _getMergedConfiguration(location, defaultConfiguration, options) { const { owner, repo } = location; let mergedConfiguration = defaultConfiguration; this._logger.info("Fetching configurations from the organization and repository", { orgRepo: `${owner}/${CONFIG_ORG_REPO}`, repo: `${owner}/${repo}` }); const orgConfig = await this.getConfigurationFromRepo(owner, CONFIG_ORG_REPO, options?.orgRef ? { ref: options.orgRef } : void 0); const repoConfig = await this.getConfigurationFromRepo(owner, repo, options?.repoRef ? { ref: options.repoRef } : void 0); if (orgConfig.config) { mergedConfiguration = this.mergeConfigurations(mergedConfiguration, orgConfig.config); } if (repoConfig.config) { mergedConfiguration = this.mergeConfigurations(mergedConfiguration, repoConfig.config); } return mergedConfiguration; } async _resolvePlugins(mergedConfiguration, location) { const resolvedPlugins = {}; logOk(this._logger, "Found plugins enabled", { repo: `${location.owner}/${location.repo}`, plugins: Object.keys(mergedConfiguration.plugins).length }); for (const [pluginKey, pluginSettings] of Object.entries(mergedConfiguration.plugins)) { const resolved = await this._resolvePluginSettings(pluginKey, pluginSettings); if (!resolved) continue; resolvedPlugins[pluginKey] = resolved; } return resolvedPlugins; } async _resolvePluginSettings(pluginKey, pluginSettings) { const isUrlPlugin = isHttpUrl(pluginKey); let manifest = null; if (!isUrlPlugin) { let pluginIdentifier; try { pluginIdentifier = parsePluginIdentifier(pluginKey); } catch (error) { this._logger.warn("Invalid plugin identifier; skipping", { plugin: pluginKey, err: error }); return null; } manifest = await this.getManifest(pluginIdentifier); } else { manifest = await this._fetchUrlManifest(pluginKey); } let runsOn = pluginSettings?.runsOn ?? []; let shouldSkipBotEvents = pluginSettings?.skipBotEvents; if (manifest) { if (!runsOn.length) { runsOn = manifest["ubiquity:listeners"] ?? []; } if (shouldSkipBotEvents === void 0) { shouldSkipBotEvents = manifest.skipBotEvents ?? true; } } else { shouldSkipBotEvents = true; } return { ...pluginSettings, with: pluginSettings?.with ?? {}, runsOn, skipBotEvents: shouldSkipBotEvents }; } /** * Retrieves the configuration from the given owner/repository. Also returns the raw data and errors, if any. * * @param owner The repository owner * @param repository The repository name * @param options - Optional ref (branch, tag, or commit SHA) for the configuration lookup */ async getConfigurationFromRepo(owner, repository, options) { const location = { owner, repo: repository, environment: options?.environment }; const state = this._createImportState(); const octokit = await this._getOctokitForLocation(location, state); if (!octokit) { this._logger.warn("No Octokit available for configuration load", { owner, repository }); return { config: null, errors: null, rawData: null }; } const { config, imports, errors, rawData } = await this._loadConfigSource(location, octokit, options?.ref); if (!rawData) { return { config: null, errors: null, rawData: null }; } if (errors && errors.length) { this._logger.warn("YAML could not be decoded", { owner, repository, errors }); return { config: null, errors, rawData }; } if (!config) { this._logger.warn("YAML could not be decoded", { owner, repository }); return { config: null, errors, rawData }; } const importedConfigs = []; for (const next of imports) { const resolved = await this._resolveImportedConfiguration(next, state, 1); if (resolved) importedConfigs.push(resolved); } const mergedConfig = mergeImportedConfigs(importedConfigs, config); if (!mergedConfig) { return { config: null, errors: null, rawData }; } const decoded = this._decodeConfiguration(location, mergedConfig); return { config: decoded.config, errors: decoded.errors, rawData }; } _createImportState() { return { cache: /* @__PURE__ */ new Map(), inFlight: /* @__PURE__ */ new Set(), octokitByLocation: /* @__PURE__ */ new Map() }; } async _getOctokitForLocation(location, state) { const key = normalizeImportKey(location); if (state.octokitByLocation.has(key)) { return state.octokitByLocation.get(key) ?? null; } if (this._octokitFactory) { const resolved = await this._octokitFactory(location); if (resolved) { state.octokitByLocation.set(key, resolved); return resolved; } } state.octokitByLocation.set(key, this._octokit); return this._octokit; } async _loadConfigSource(location, octokit, ref) { const rawData = await this._download({ repository: location.repo, owner: location.owner, octokit, ref, environment: location.environment ?? this._environment }); if (!rawData) { this._logger.warn("No raw configuration data", { owner: location.owner, repository: location.repo }); return { config: null, imports: [], errors: null, rawData: null }; } logOk(this._logger, "Downloaded configuration file", { owner: location.owner, repository: location.repo }); const { yaml, errors } = this.parseYaml(rawData); const imports = readImports(this._logger, yaml?.imports, location); if (yaml && typeof yaml === "object" && !Array.isArray(yaml) && "imports" in yaml) { delete yaml.imports; } const targetRepoConfiguration = yaml; return { config: targetRepoConfiguration, imports, errors, rawData }; } _decodeConfiguration(location, config) { this._logger.debug("Decoding configuration", { owner: location.owner, repository: location.repo }); try { const configSchemaWithDefaults = import_value.Value.Default(configSchema, config); const errors = import_value.Value.Errors(configSchema, configSchemaWithDefaults); if (errors.First()) { for (const error of errors) { this._logger.warn("Configuration validation error", { err: error }); } } const decodedConfig = import_value.Value.Decode(configSchema, configSchemaWithDefaults); return { config: stripImports(decodedConfig), errors: errors.First() ? errors : null }; } catch (error) { this._logger.warn("Error decoding configuration; Will ignore.", { err: error, owner: location.owner, repository: location.repo }); const decodedError = unwrapTypeBoxError(error); return { config: null, errors: [decodedError] }; } } async _resolveImportedConfiguration(location, state, depth) { const key = normalizeImportKey(location); if (state.cache.has(key)) { return state.cache.get(key) ?? null; } if (state.inFlight.has(key)) { this._logger.warn("Skipping import due to circular reference.", { location }); return null; } if (depth > MAX_IMPORT_DEPTH) { this._logger.warn("Skipping import; maximum depth exceeded.", { location, depth }); return null; } state.inFlight.add(key); let resolved = null; try { const octokit = await this._getOctokitForLocation(location, state); if (!octokit) { this._logger.warn("Skipping import; no authorized Octokit for owner.", { location }); return null; } const { config, imports, errors } = await this._loadConfigSource(location, octokit); if (errors && errors.length) { this._logger.warn("Skipping import due to YAML parsing errors.", { location, errors }); return null; } if (!config) { return null; } const importedConfigs = []; for (const next of imports) { const nested = await this._resolveImportedConfiguration(next, state, depth + 1); if (nested) importedConfigs.push(nested); } const mergedConfig = mergeImportedConfigs(importedConfigs, config); if (!mergedConfig) return null; const decoded = this._decodeConfiguration(location, mergedConfig); resolved = decoded.config; } finally { state.inFlight.delete(key); state.cache.set(key, resolved); } return resolved; } async _download({ repository, owner, octokit, ref, environment }) { if (!repository || !owner) { this._logger.warn("Repo or owner is not defined, cannot download the requested file"); return null; } const pathList = getConfigPathCandidatesForEnvironment(environment ?? this._environment); for (const filePath of pathList) { const content = await this._tryDownloadPath({ repository, owner, octokit, filePath, ref }); if (content !== null) return content; } return null; } async _tryDownloadPath({ repository, owner, octokit, filePath, ref }) { try { this._logger.info("Attempting to fetch configuration", { owner, repository, filePath }); const { data, headers } = await octokit.rest.repos.getContent({ owner, repo: repository, path: filePath, ...ref ? { ref } : {}, mediaType: { format: "raw" } }); this._logger.debug("Configuration file found", { owner, repository, filePath, rateLimitRemaining: headers?.["x-ratelimit-remaining"] }); return data; } catch (err) { this._handleDownloadError(err, { owner, repository, filePath }); return null; } } _handleDownloadError(err, context) { const status = err && typeof err === "object" && "status" in err ? Number(err.status) : null; if (status === 404) { this._logger.warn("No configuration file found", context); return; } const metadata = { err, ...context, ...status ? { status } : {} }; if (status && status >= 500) { this._logger.error("Failed to download the requested file", metadata); } else { this._logger.warn("Failed to download the requested file", metadata); } } /* * Parse the raw YAML content and returns the loaded YAML, or errors if any. */ parseYaml(data) { this._logger.debug("Will attempt to parse YAML data"); try { if (data) { const parsedData = import_js_yaml.default.load(data); this._logger.debug("Parsed yaml data successfully"); return { yaml: parsedData ?? null, errors: null }; } } catch (error) { this._logger.warn("Error parsing YAML", { error }); return { errors: [error], yaml: null }; } this._logger.warn("Could not parse YAML"); return { yaml: null, errors: null }; } mergeConfigurations(configuration1, configuration2) { const mergedPlugins = { ...configuration1.plugins, ...configuration2.plugins }; return { ...configuration1, ...configuration2, plugins: mergedPlugins }; } getManifest(plugin) { return isGithubPlugin(plugin) ? this._fetchActionManifest(plugin) : this._fetchWorkerManifest(plugin); } async _fetchWorkerManifest(url) { const manifestUrl = resolveManifestUrl(url); if (!manifestUrl) { this._logger.warn("Invalid plugin URL; cannot fetch manifest", { pluginUrl: url }); return null; } const manifestKey = `url:${manifestUrl}`; if (this._manifestCache[manifestKey]) { return this._manifestCache[manifestKey]; } try { const result = await fetch(manifestUrl); if (!result.ok) { this._logger.error("Could not find a manifest for Worker", { manifestUrl, status: result.status }); return null; } const jsonData = await result.json(); const manifest = this._decodeManifest(jsonData); this._manifestCache[manifestKey] = manifest; return manifest; } catch (e) { this._logger.error("Could not find a manifest for Worker", { manifestUrl, err: e }); } return null; } async _fetchUrlManifest(pluginUrl) { const manifestUrl = resolveManifestUrl(pluginUrl); if (!manifestUrl) { this._logger.warn("Invalid plugin URL; cannot fetch manifest", { pluginUrl }); return null; } const manifestKey = `url:${manifestUrl}`; if (this._manifestCache[manifestKey]) { return this._manifestCache[manifestKey]; } if (this._manifestPromiseCache[manifestKey]) { return this._manifestPromiseCache[manifestKey]; } const manifestPromise = (async () => { if (typeof fetch !== "function") { this._logger.warn("Fetch is unavailable; cannot load URL manifest", { manifestUrl }); return null; } try { const response = await fetch(manifestUrl); if (!response.ok) { this._logger.warn("URL manifest request failed", { manifestUrl, status: response.status }); return null; } const data = await response.json(); const manifest = this._decodeManifest(data); this._manifestCache[manifestKey] = manifest; return manifest; } catch (e) { this._logger.warn("Could not load URL manifest", { manifestUrl, err: e }); return null; } })(); this._manifestPromiseCache[manifestKey] = manifestPromise; try { return await manifestPromise; } finally { delete this._manifestPromiseCache[manifestKey]; } } async _fetchActionManifest({ owner, repo, ref }) { const manifestKey = ref ? `${owner}:${repo}:${ref}` : `${owner}:${repo}`; if (this._manifestCache[manifestKey]) { return this._manifestCache[manifestKey]; } if (this._manifestPromiseCache[manifestKey]) { return this._manifestPromiseCache[manifestKey]; } const manifestPromise = (async () => { try { const { data } = await this._octokit.rest.repos.getContent({ owner, repo, path: "manifest.json", ref }); if ("content" in data) { const content = import_node_buffer.Buffer.from(data.content, "base64").toString(); const contentParsed = JSON.parse(content); const manifest = this._decodeManifest(contentParsed); this._manifestCache[manifestKey] = manifest; return manifest; } } catch (e) { this._logger.warn(`Could not find a valid manifest for ${owner}/${repo}${ref ? "@" + ref : ""}`, { owner, repo, ref, err: e }); } return null; })(); this._manifestPromiseCache[manifestKey] = manifestPromise; try { return await manifestPromise; } finally { delete this._manifestPromiseCache[manifestKey]; } } _decodeManifest(manifest) { const errors = [...import_value.Value.Errors(manifestSchema, manifest)]; if (errors.length) { for (const error of errors) { this._logger.warn("Manifest validation error", { error }); } throw new Error("Manifest is invalid."); } const defaultManifest = import_value.Value.Default(manifestSchema, manifest); return defaultManifest; } }; // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { CONFIG_DEV_FULL_PATH, CONFIG_ORG_REPO, CONFIG_PROD_FULL_PATH, ConfigurationHandler, configSchema, isGithubPlugin });