UNPKG

everything-dev

Version:

A consolidated product package for building Module Federation apps with oRPC APIs.

218 lines (216 loc) • 8.38 kB
const require_services_registry_fastkv = require('./registry/fastkv.cjs'); const require_load = require('./resolution/load.cjs'); const require_services_resolution_session = require('./resolution/session.cjs'); const require_artifacts = require('./codegen/artifacts.cjs'); const require_build_train = require('./workspace/build-train.cjs'); const require_save_config = require('../utils/save-config.cjs'); const require_train = require('./deploy/train.cjs'); let node_fs = require("node:fs"); let node_path = require("node:path"); let effect = require("effect"); //#region src/services/plugins-admin.ts var PluginAdminService = class PluginAdminService extends effect.Context.Service()("bos/PluginAdminService") { static Live = effect.Layer.effect(PluginAdminService, effect.Effect.map(require_train.DeployTrainService, (train) => PluginAdminService.of({ add: (session, input) => effect.Effect.gen(function* () { if (!session?.config) return { status: "error", key: "", error: require_load.MISSING_CONFIG_MESSAGE }; const isBosRef = input.source.startsWith("bos://"); const isLocal = input.source.startsWith("local:"); const key = sanitizePluginKey(input.as ?? (isBosRef ? input.source.split("/").pop() ?? "plugin" : defaultPluginKey(input.source))); const existing = session.config.plugins?.[key]; const existingEntry = existing && typeof existing === "object" ? existing : {}; const applyEntryDelta = (base) => { if (isBosRef) { const { development: _dropped, ...rest } = base; return { ...rest, extends: input.source }; } if (isLocal) { const { extends: _dropped, ...rest } = base; return { ...rest, development: input.source }; } const { development: _dropped, ...rest } = base; return { ...rest, production: input.production ?? input.source }; }; const nextPlugins = { ...session.config.plugins }; nextPlugins[key] = applyEntryDelta(existingEntry); const nextConfig = { ...session.config, plugins: nextPlugins }; const authored = session.rawConfig; if (authored) { const authoredExisting = authored.plugins?.[key]; const authoredBase = typeof authoredExisting === "string" ? { extends: authoredExisting } : authoredExisting && typeof authoredExisting === "object" ? authoredExisting : {}; yield* effect.Effect.tryPromise(() => require_save_config.saveBosConfig(session.root, { ...authored, plugins: { ...authored.plugins, [key]: applyEntryDelta(authoredBase) } })); } else yield* effect.Effect.tryPromise(() => require_save_config.saveBosConfig(session.root, nextConfig)); yield* effect.Effect.tryPromise(async () => { await require_artifacts.generateCodeArtifacts(session.root, nextConfig); }); const stored = nextConfig.plugins?.[key]; const storedObj = stored && typeof stored === "object" ? stored : {}; return { status: "added", key, development: storedObj.development, production: storedObj.production, integrity: storedObj.integrity, version: storedObj.version }; }), remove: (session, input) => effect.Effect.gen(function* () { if (!session?.config) return { status: "error", key: input.key, error: require_load.MISSING_CONFIG_MESSAGE }; if (!session.config.plugins?.[input.key]) return { status: "error", key: input.key, error: `Plugin '${input.key}' is not configured` }; const nextPlugins = { ...session.config.plugins }; delete nextPlugins[input.key]; const nextConfig = { ...session.config, plugins: Object.keys(nextPlugins).length > 0 ? nextPlugins : void 0 }; const authored = session.rawConfig; if (authored) { const authoredPlugins = { ...authored.plugins }; if (authoredPlugins[input.key] === void 0) { if ((0, node_fs.existsSync)((0, node_path.join)(session.root, "bos.app.ts"))) return { status: "error", key: input.key, error: `'${input.key}' is inherited from the parent runtime — remove it in the parent config or override it there (the authored descriptor cannot express removal)` }; authoredPlugins[input.key] = null; } else delete authoredPlugins[input.key]; yield* effect.Effect.tryPromise(() => require_save_config.saveBosConfig(session.root, { ...authored, plugins: Object.keys(authoredPlugins).length > 0 ? authoredPlugins : void 0 })); } else yield* effect.Effect.tryPromise(() => require_save_config.saveBosConfig(session.root, nextConfig)); yield* effect.Effect.tryPromise(async () => { await require_artifacts.generateCodeArtifacts(session.root, nextConfig); }); return { status: "removed", key: input.key }; }), list: (session) => effect.Effect.succeed(listPluginAttachments(session?.config ?? null)), publish: (session, input) => effect.Effect.gen(function* () { if (!session?.config) return { status: "error", key: input.key, error: require_load.MISSING_CONFIG_MESSAGE }; const attachment = session.config?.plugins?.[input.key]; if (!attachment) return { status: "error", key: input.key, error: `Plugin '${input.key}' is not configured` }; const localPath = pluginLocalPath(session.root, attachment); if (!localPath) return { status: "error", key: input.key, error: `Plugin '${input.key}' does not have a local development path` }; const result = yield* train.publish({ bosConfig: session.config, runtimeConfig: session.runtime, configDir: session.root, env: "production", build: true, dryRun: false, verbose: false, packages: input.key }).pipe(effect.Effect.catchTag("PublishTrainError", (error) => effect.Effect.succeed(error.toResult()))); if (result.status === "error") return { status: "error", key: input.key, error: result.error }; const nextSession = (yield* effect.Effect.tryPromise({ try: () => require_services_resolution_session.openResolution({ cwd: session.root }), catch: () => null })) ?? session; const production = (nextSession.config?.plugins?.[input.key])?.production; const manifest = production ? yield* effect.Effect.tryPromise({ try: () => require_services_registry_fastkv.fetchRemotePluginManifest(production), catch: () => null }) : null; const nextConfig = nextSession.config; if (production && nextConfig) yield* effect.Effect.tryPromise({ try: async () => { await require_artifacts.generateCodeArtifacts(session.root, nextConfig); }, catch: () => void 0 }); return { status: "published", key: input.key, path: localPath, production, fingerprint: result.fingerprint, version: manifest?.plugin.version }; }) }))); }; function sanitizePluginKey(value) { return value.replace(/[^A-Za-z0-9/_-]/g, "-").replace(/\/+/g, "/").split("/").filter(Boolean).map((segment) => segment.replace(/[^A-Za-z0-9_-]/g, "-")).join("/").replace(/^\/+|\/+$/g, ""); } function defaultPluginKey(source) { const normalized = source.replace(/^local:/, "").replace(/\/$/, ""); if (source.startsWith("local:")) return sanitizePluginKey((0, node_path.basename)(normalized)) || "plugin"; try { const url = new URL(source); return sanitizePluginKey((0, node_path.basename)(url.pathname) || url.hostname) || "plugin"; } catch { return sanitizePluginKey(source) || "plugin"; } } function pluginLocalPath(configDir, attachment) { const ref = require_build_train.getPluginRef(attachment); const source = ref?.development ?? ref?.production; if (!source?.startsWith("local:")) return null; return (0, node_path.join)(configDir, source.slice(6)); } function listPluginAttachments(config) { return Object.entries(config?.plugins ?? {}).map(([key, attachment]) => { const ref = require_build_train.getPluginRef(attachment); return { key, development: ref?.development, production: ref?.production, localPath: ref?.development?.startsWith("local:") ? ref.development.slice(6) : void 0, source: ref?.development?.startsWith("local:") ? "local" : "remote", integrity: ref?.integrity, version: ref?.version, name: ref?.name }; }).sort((a, b) => a.key.localeCompare(b.key)); } //#endregion exports.PluginAdminService = PluginAdminService; //# sourceMappingURL=plugins-admin.cjs.map