everything-dev
Version:
A consolidated product package for building Module Federation apps with oRPC APIs.
1 lines • 15.1 kB
Source Map (JSON)
{"version":3,"file":"plugins.mjs","names":[],"sources":["../../src/commands/plugins.ts"],"sourcesContent":["import { existsSync } from \"node:fs\";\nimport { basename, join } from \"node:path\";\nimport { Context } from \"effect\";\nimport { getPluginRef } from \"../build\";\nimport { generateCodeArtifacts } from \"../code-artifacts\";\nimport { MISSING_CONFIG_MESSAGE } from \"../config\";\nimport type { PluginListResult } from \"../contract\";\nimport { fetchRemotePluginManifest } from \"../fastkv\";\nimport { publishToFastKv } from \"../publish\";\nimport { openResolution } from \"../resolution/session\";\nimport type { BosConfig } from \"../types\";\nimport { saveBosConfig } from \"../utils/save-config\";\nimport { type BosBuilder, BosDepsTag } from \"./shared\";\n\nexport function registerPlugins(builder: BosBuilder) {\n return {\n pluginAdd: builder.pluginAdd.handler(async ({ input, context }) => {\n const deps = Context.get(context[\"effect/context\"], BosDepsTag);\n const session = deps.session;\n if (!session?.config) {\n return {\n status: \"error\" as const,\n key: \"\",\n error: MISSING_CONFIG_MESSAGE,\n };\n }\n\n const isBosRef = input.source.startsWith(\"bos://\");\n const isLocal = input.source.startsWith(\"local:\");\n const key = sanitizePluginKey(\n input.as ??\n (isBosRef ? (input.source.split(\"/\").pop() ?? \"plugin\") : defaultPluginKey(input.source)),\n );\n const existing = session.config.plugins?.[key];\n const existingEntry = existing && typeof existing === \"object\" ? existing : {};\n // Adding a source is a switch, not an additive patch: a remote URL (or\n // bos:// ref) replaces a conflicting local development ref. Dual-mode\n // (local in dev, production URL in prod) remains expressible by\n // hand-editing the authored config.\n const applyEntryDelta = (base: Record<string, unknown>): Record<string, unknown> => {\n if (isBosRef) {\n const { development: _dropped, ...rest } = base;\n return { ...rest, extends: input.source };\n }\n if (isLocal) {\n const { extends: _dropped, ...rest } = base;\n return { ...rest, development: input.source };\n }\n const { development: _dropped, ...rest } = base;\n return { ...rest, production: input.production ?? input.source };\n };\n const nextPlugins = { ...session.config.plugins };\n nextPlugins[key] = applyEntryDelta(existingEntry);\n\n const nextConfig: BosConfig = {\n ...session.config,\n plugins: nextPlugins,\n };\n\n const authored = session.rawConfig;\n if (authored) {\n // Authoring-surface save: the authored leaf input + delta, never the\n // resolved config (which would bake inherited values into the file).\n const authoredExisting = authored.plugins?.[key];\n const authoredBase: Record<string, unknown> =\n typeof authoredExisting === \"string\"\n ? { extends: authoredExisting }\n : authoredExisting && typeof authoredExisting === \"object\"\n ? (authoredExisting as Record<string, unknown>)\n : {};\n await saveBosConfig(session.root, {\n ...authored,\n plugins: {\n ...authored.plugins,\n [key]: applyEntryDelta(authoredBase),\n },\n });\n } else {\n await saveBosConfig(session.root, nextConfig);\n }\n await generateCodeArtifacts(session.root, nextConfig);\n\n const stored = nextConfig.plugins?.[key];\n const storedObj = stored && typeof stored === \"object\" ? stored : {};\n\n return {\n status: \"added\" as const,\n key,\n development: storedObj.development,\n production: storedObj.production,\n integrity: storedObj.integrity,\n version: storedObj.version,\n };\n }),\n\n pluginRemove: builder.pluginRemove.handler(async ({ input, context }) => {\n const deps = Context.get(context[\"effect/context\"], BosDepsTag);\n const session = deps.session;\n if (!session?.config) {\n return {\n status: \"error\" as const,\n key: input.key,\n error: MISSING_CONFIG_MESSAGE,\n };\n }\n\n if (!session.config.plugins?.[input.key]) {\n return {\n status: \"error\" as const,\n key: input.key,\n error: `Plugin '${input.key}' is not configured`,\n };\n }\n\n const nextPlugins = { ...session.config.plugins };\n delete nextPlugins[input.key];\n const nextConfig: BosConfig = {\n ...session.config,\n plugins: Object.keys(nextPlugins).length > 0 ? nextPlugins : undefined,\n };\n\n const authored = session.rawConfig;\n if (authored) {\n const authoredPlugins: Record<string, unknown> = { ...authored.plugins };\n if (authoredPlugins[input.key] === undefined) {\n // Inherited from the parent runtime. JSON-form children can express\n // removal with a null sentinel (dropped by the extends merge); the\n // TS descriptor schema cannot, so only those error out.\n if (existsSync(join(session.root, \"bos.app.ts\"))) {\n return {\n status: \"error\" as const,\n key: input.key,\n 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)`,\n };\n }\n authoredPlugins[input.key] = null;\n } else {\n delete authoredPlugins[input.key];\n }\n await saveBosConfig(session.root, {\n ...authored,\n plugins: Object.keys(authoredPlugins).length > 0 ? authoredPlugins : undefined,\n });\n } else {\n await saveBosConfig(session.root, nextConfig);\n }\n await generateCodeArtifacts(session.root, nextConfig);\n\n return {\n status: \"removed\" as const,\n key: input.key,\n };\n }),\n\n pluginList: builder.pluginList.handler(async ({ context }) => {\n const deps = Context.get(context[\"effect/context\"], BosDepsTag);\n const plugins: PluginListResult[\"plugins\"] = listPluginAttachments(\n deps.session?.config ?? null,\n );\n return {\n status: \"listed\" as const,\n plugins,\n };\n }),\n\n pluginPublish: builder.pluginPublish.handler(async ({ input, context }) => {\n const deps = Context.get(context[\"effect/context\"], BosDepsTag);\n const session = deps.session;\n if (!session?.config) {\n return {\n status: \"error\" as const,\n key: input.key,\n error: MISSING_CONFIG_MESSAGE,\n };\n }\n\n const attachment = session.config?.plugins?.[input.key];\n if (!attachment) {\n return {\n status: \"error\" as const,\n key: input.key,\n error: `Plugin '${input.key}' is not configured`,\n };\n }\n\n const localPath = pluginLocalPath(session.root, attachment);\n if (!localPath) {\n return {\n status: \"error\" as const,\n key: input.key,\n error: `Plugin '${input.key}' does not have a local development path`,\n };\n }\n\n const result = await publishToFastKv({\n bosConfig: session.config,\n runtimeConfig: session.runtime,\n configDir: session.root,\n env: \"production\",\n build: true,\n dryRun: false,\n verbose: false,\n packages: input.key,\n });\n if (result.status === \"error\") {\n return {\n status: \"error\" as const,\n key: input.key,\n error: result.error,\n };\n }\n\n const nextSession = (await openResolution({ cwd: session.root })) ?? session;\n const production = (\n nextSession.config?.plugins?.[input.key] as\n | {\n production?: string;\n }\n | undefined\n )?.production;\n\n const manifest = production ? await fetchRemotePluginManifest(production) : null;\n if (production && nextSession.config) {\n await generateCodeArtifacts(session.root, nextSession.config);\n }\n\n return {\n status: \"published\" as const,\n key: input.key,\n path: localPath,\n production,\n fingerprint: result.fingerprint,\n version: manifest?.plugin.version,\n };\n }),\n };\n}\n\ntype PluginAttachmentConfig = NonNullable<BosConfig[\"plugins\"]>[string];\n\nfunction sanitizePluginKey(value: string): string {\n return value\n .replace(/[^A-Za-z0-9/_-]/g, \"-\")\n .replace(/\\/+/g, \"/\")\n .split(\"/\")\n .filter(Boolean)\n .map((segment) => segment.replace(/[^A-Za-z0-9_-]/g, \"-\"))\n .join(\"/\")\n .replace(/^\\/+|\\/+$/g, \"\");\n}\n\nfunction defaultPluginKey(source: string): string {\n const normalized = source.replace(/^local:/, \"\").replace(/\\/$/, \"\");\n if (source.startsWith(\"local:\")) {\n return sanitizePluginKey(basename(normalized)) || \"plugin\";\n }\n\n try {\n const url = new URL(source);\n return sanitizePluginKey(basename(url.pathname) || url.hostname) || \"plugin\";\n } catch {\n return sanitizePluginKey(source) || \"plugin\";\n }\n}\n\nfunction pluginLocalPath(configDir: string, attachment: PluginAttachmentConfig): string | null {\n const ref = getPluginRef(attachment);\n const source = ref?.development ?? ref?.production;\n if (!source?.startsWith(\"local:\")) {\n return null;\n }\n\n return join(configDir, source.slice(\"local:\".length));\n}\n\nfunction listPluginAttachments(config: BosConfig | null) {\n return (Object.entries(config?.plugins ?? {}) as Array<[string, PluginAttachmentConfig]>)\n .map(([key, attachment]) => {\n const ref = getPluginRef(attachment);\n return {\n key,\n development: ref?.development,\n production: ref?.production,\n localPath: ref?.development?.startsWith(\"local:\")\n ? ref.development.slice(\"local:\".length)\n : undefined,\n source: ref?.development?.startsWith(\"local:\") ? (\"local\" as const) : (\"remote\" as const),\n integrity: ref?.integrity,\n version: ref?.version,\n name: ref?.name,\n };\n })\n .sort((a, b) => a.key.localeCompare(b.key));\n}\n"],"mappings":"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"}