every-plugin 3.0.0-rc.3 → 3.0.0-rc.5

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.
Files changed (56) hide show
  1. package/dist/build/ui/generated-config.cjs +4 -0
  2. package/dist/build/ui/generated-config.cjs.map +1 -1
  3. package/dist/build/ui/generated-config.d.cts.map +1 -1
  4. package/dist/build/ui/generated-config.d.mts.map +1 -1
  5. package/dist/build/ui/generated-config.mjs +4 -1
  6. package/dist/build/ui/generated-config.mjs.map +1 -1
  7. package/dist/client.cjs +69 -0
  8. package/dist/client.cjs.map +1 -0
  9. package/dist/client.d.cts +15 -0
  10. package/dist/client.d.cts.map +1 -0
  11. package/dist/client.d.mts +15 -0
  12. package/dist/client.d.mts.map +1 -0
  13. package/dist/client.mjs +68 -0
  14. package/dist/client.mjs.map +1 -0
  15. package/dist/dev/host.cjs +149 -0
  16. package/dist/dev/host.cjs.map +1 -0
  17. package/dist/dev/host.d.cts +32 -0
  18. package/dist/dev/host.d.cts.map +1 -0
  19. package/dist/dev/host.d.mts +32 -0
  20. package/dist/dev/host.d.mts.map +1 -0
  21. package/dist/dev/host.mjs +148 -0
  22. package/dist/dev/host.mjs.map +1 -0
  23. package/dist/dev/serve.cjs +16 -111
  24. package/dist/dev/serve.cjs.map +1 -1
  25. package/dist/dev/serve.d.cts.map +1 -1
  26. package/dist/dev/serve.d.mts.map +1 -1
  27. package/dist/dev/serve.mjs +17 -112
  28. package/dist/dev/serve.mjs.map +1 -1
  29. package/dist/index.cjs +2 -0
  30. package/dist/index.d.cts +3 -2
  31. package/dist/index.d.mts +3 -2
  32. package/dist/index.mjs +2 -1
  33. package/dist/plugin.d.cts +1 -1
  34. package/dist/remote-entry.cjs +0 -1
  35. package/dist/remote-entry.cjs.map +1 -1
  36. package/dist/remote-entry.d.cts +1 -2
  37. package/dist/remote-entry.d.cts.map +1 -1
  38. package/dist/remote-entry.d.mts +1 -2
  39. package/dist/remote-entry.d.mts.map +1 -1
  40. package/dist/remote-entry.mjs +1 -1
  41. package/dist/remote-entry.mjs.map +1 -1
  42. package/dist/runtime/mf-config.cjs +1 -1
  43. package/dist/runtime/mf-config.mjs +1 -1
  44. package/dist/runtime/services/plugin-loader.service.d.cts +2 -2
  45. package/dist/types.d.cts +23 -2
  46. package/dist/types.d.cts.map +1 -1
  47. package/dist/types.d.mts +22 -1
  48. package/dist/types.d.mts.map +1 -1
  49. package/package.json +19 -2
  50. package/src/build/ui/generated-config.ts +4 -0
  51. package/src/client.ts +111 -0
  52. package/src/dev/host.ts +235 -0
  53. package/src/dev/serve.ts +22 -140
  54. package/src/index.ts +9 -0
  55. package/src/remote-entry.ts +1 -2
  56. package/src/types.ts +36 -2
@@ -1 +1 @@
1
- {"version":3,"file":"serve.mjs","names":[],"sources":["../../src/dev/serve.ts"],"sourcesContent":["import { spawn } from \"node:child_process\";\nimport fs from \"node:fs\";\nimport http from \"node:http\";\nimport path from \"node:path\";\nimport { Effect } from \"effect\";\nimport sirv from \"sirv\";\nimport { DEV_ENTRY_FILENAME } from \"../build/artifact-names\";\nimport { ensureGeneratedRspackConfig } from \"../build/rspack/generated-config\";\nimport { getPluginInfo, loadDevConfig } from \"../build/rspack/utils\";\nimport { ensureGeneratedUiRsbuildConfig, pluginLayoutKey } from \"../build/ui/generated-config\";\nimport { PLUGIN_ERROR_STATUS_MAP } from \"../errors\";\nimport { loadRemoteWithRetry } from \"../remote-entry\";\nimport { killChildEscalating, watchParentDeath } from \"./watch-kill\";\n\nconst corsHeaders = {\n \"Access-Control-Allow-Origin\": \"*\",\n \"Access-Control-Allow-Methods\": \"GET, POST, PUT, DELETE, PATCH, OPTIONS\",\n \"Access-Control-Allow-Headers\": \"X-Requested-With, content-type, Authorization\",\n};\n\nconst applyCorsHeaders = (res: http.ServerResponse) => {\n Object.entries(corsHeaders).forEach(([key, value]) => {\n res.setHeader(key, value);\n });\n};\n\nconst normalizePrefix = (prefix?: string): string => {\n if (!prefix) return \"\";\n const cleaned = prefix.replace(/^\\/+|\\/+$/g, \"\");\n return cleaned ? `/${cleaned}` : \"\";\n};\n\nconst readRuntimeConfigFromEnv = (): any => {\n const raw = process.env.BOS_RUNTIME_CONFIG;\n if (!raw) return null;\n try {\n return JSON.parse(raw);\n } catch {\n return null;\n }\n};\n\nconst collectSiblingRemotes = (runtimeConfig: any, pluginId: string) => {\n const siblings: Record<string, { remote: string }> = {};\n\n const isApi = pluginId === \"api\" || runtimeConfig?.api?.name === pluginId;\n\n const ownKey = runtimeConfig?.plugins?.[pluginId] ? pluginId : null;\n let dependsOn: string[] = [];\n if (ownKey) {\n dependsOn = runtimeConfig.plugins[ownKey].dependsOn ?? [];\n } else if (isApi) {\n dependsOn = runtimeConfig?.api?.dependsOn ?? [];\n }\n\n for (const depId of dependsOn) {\n const dep = runtimeConfig?.plugins?.[depId];\n if (!dep || dep.source !== \"local\" || !dep.url) continue;\n if (depId === pluginId) continue;\n const base = dep.url.replace(/\\/$/, \"\");\n siblings[depId] = { remote: `${base}/${DEV_ENTRY_FILENAME}` };\n }\n\n return { siblings, dependsOn };\n};\n\nconst loadPluginWithRetry = (\n runtime: any,\n pluginId: string,\n remoteUrl: string,\n options: Record<string, unknown> = { variables: {}, secrets: {} },\n pluginsMap?: Record<string, unknown>,\n timeoutMs = 90000,\n): Promise<any> =>\n Effect.runPromise(\n loadRemoteWithRetry({\n label: pluginId,\n remoteUrl,\n load: () => runtime.usePlugin(pluginId, options, pluginsMap),\n timeoutMs,\n }),\n );\n\nconst sendJson = (res: http.ServerResponse, status: number, body: unknown) => {\n res.statusCode = status;\n res.setHeader(\"content-type\", \"application/json; charset=utf-8\");\n res.end(JSON.stringify(body));\n};\n\nconst sendText = (res: http.ServerResponse, status: number, text: string) => {\n res.statusCode = status;\n res.setHeader(\"content-type\", \"text/plain; charset=utf-8\");\n res.end(text);\n};\n\nexport interface PluginDevServeOptions {\n cwd?: string;\n port?: number;\n watch?: boolean;\n}\n\nexport interface PluginDevServerHandle {\n port: number;\n close: () => Promise<void>;\n}\n\n/**\n * The dev server's plugin id — the workspace's npm package name, the same\n * value the host passes as `runtimeId`. Both initialize sites must agree:\n * `registerRemote` derives the MF remote name from it, and the plugin's\n * database layer derives its migration slug from it, so a pre-normalized\n * remote name here would migrate into a different schema than the host\n * expects. plugin.dev.ts no longer carries a pluginId field.\n */\nexport const resolveDevPluginId = (cwd: string): string => getPluginInfo(cwd).name;\n\nexport async function startPluginDevServer(\n options: PluginDevServeOptions = {},\n): Promise<PluginDevServerHandle> {\n process.env.BOS_DEV_SERVER = \"1\";\n const cwd = options.cwd ?? process.cwd();\n const pluginInfo = getPluginInfo(cwd);\n const devConfig = loadDevConfig(path.join(cwd, \"plugin.dev.ts\"));\n const pluginId = resolveDevPluginId(cwd);\n const compositionKey = pluginLayoutKey(cwd) ?? pluginId;\n const port = options.port ?? (Number(process.env.PORT) || devConfig?.port || 3999);\n const rpcPrefix = normalizePrefix(devConfig?.prefix);\n const rpcBase = `/api/rpc${rpcPrefix}`;\n\n const distDir = path.join(cwd, \"dist\");\n const serveStatic = sirv(distDir, { dev: true });\n\n // No-watch mode (BOS_NO_WATCH=1): build once, serve the built output.\n // Regression/CI stacks need no hot reload — watchers are the stack's\n // heaviest processes and stall runs freeze under their accumulated\n // footprint. One-shot builds exit; steady state is small static servers.\n const noWatch = options.watch === false || process.env.BOS_NO_WATCH === \"1\";\n\n const runOnce = (cmd: string, args: string[]) =>\n new Promise<void>((resolve, reject) => {\n const child = spawn(cmd, args, { cwd, stdio: \"inherit\", env: process.env });\n child.on(\"error\", reject);\n child.on(\"exit\", (code) => {\n if (code === 0) resolve();\n else reject(new Error(`❌ ${cmd} ${args.join(\" \")} exited with ${code}`));\n });\n });\n\n let watcher: ReturnType<typeof spawn> | null = null;\n if (noWatch) {\n const generatedConfig = ensureGeneratedRspackConfig(cwd);\n await runOnce(\"rspack\", generatedConfig ? [\"build\", \"--config\", generatedConfig] : [\"build\"]);\n } else if (options.watch !== false) {\n const generatedConfig = ensureGeneratedRspackConfig(cwd);\n watcher = spawn(\n \"rspack\",\n generatedConfig ? [\"build\", \"--watch\", \"--config\", generatedConfig] : [\"build\", \"--watch\"],\n {\n cwd,\n stdio: \"inherit\",\n env: process.env,\n },\n );\n watcher.on(\"error\", (error) => {\n console.error(`❌ Failed to spawn rspack build --watch: ${error.message}`);\n });\n }\n\n let uiWatcher: ReturnType<typeof spawn> | null = null;\n let uiStaticServer: http.Server | null = null;\n const uiPort = process.env.BOS_UI_PORT;\n const uiConfig = ensureGeneratedUiRsbuildConfig(cwd);\n if (uiConfig && noWatch && uiPort) {\n // Folder-form ui: one-shot build, then serve the ui source root's dist\n // (the factory's absolute distPath target) statically on the ui port.\n await runOnce(\"rsbuild\", [\"build\", \"--config\", uiConfig]);\n const uiDistDir =\n path.basename(cwd) === \"ui\" ? path.join(cwd, \"dist\") : path.join(cwd, \"ui\", \"dist\");\n if (!fs.existsSync(path.join(uiDistDir, DEV_ENTRY_FILENAME))) {\n console.error(\n `❌ UI dist is missing at ${uiDistDir} — the ui static server would serve 404s for every asset (build output layout mismatch?)`,\n );\n }\n const serveUiStatic = sirv(uiDistDir, { dev: true });\n uiStaticServer = http.createServer((req, res) => {\n applyCorsHeaders(res);\n serveUiStatic(req, res, () => {\n sendText(res, 404, \"Not Found\");\n });\n });\n await new Promise<void>((resolve, reject) => {\n uiStaticServer!.once(\"error\", reject);\n uiStaticServer!.listen(Number(uiPort), () => resolve());\n });\n console.log(`├─ 🎨 UI: http://localhost:${uiPort} (built, no watch)`);\n } else if (uiConfig) {\n uiWatcher = spawn(\"rsbuild\", [\"dev\", \"--config\", uiConfig], {\n cwd,\n stdio: \"inherit\",\n env: uiPort ? { ...process.env, PORT: uiPort } : process.env,\n });\n uiWatcher.on(\"error\", (error) => {\n console.error(`❌ Failed to spawn rsbuild ui dev: ${error.message}`);\n });\n console.log(`├─ 🎨 UI: ${uiPort ? `http://localhost:${uiPort}` : \"(rsbuild auto port)\"}`);\n }\n\n const handlers: { rpc: any; api: any } = { rpc: null, api: null };\n const effectContextHolder: { context: unknown } = { context: null };\n let runtime: { shutdown: () => Promise<void> } | null = null;\n\n const pathnameOf = (req: http.IncomingMessage): string =>\n ((req.url ?? \"/\").split(\"?\")[0] ?? \"/\").replace(/\\/+$/, \"\") || \"/\";\n\n const buildDevContext = (_req: http.IncomingMessage, webRequest: Request) => {\n const rawClone =\n webRequest.method === \"GET\" || webRequest.method === \"HEAD\" ? null : webRequest.clone();\n let cachedRawBody: string | null = null;\n return {\n reqHeaders: webRequest.headers,\n \"effect/context\": effectContextHolder.context,\n getRawBody: async (): Promise<string> => {\n if (cachedRawBody !== null) return cachedRawBody;\n if (!rawClone) {\n cachedRawBody = \"\";\n return cachedRawBody;\n }\n cachedRawBody = await rawClone.text();\n return cachedRawBody;\n },\n };\n };\n\n const toWebRequest = (req: http.IncomingMessage): Request => {\n const url = `http://localhost:${port}${req.url}`;\n return new Request(url, {\n method: req.method,\n headers: req.headers,\n body: req.method !== \"GET\" && req.method !== \"HEAD\" ? req : undefined,\n duplex: req.method !== \"GET\" && req.method !== \"HEAD\" ? \"half\" : undefined,\n } as RequestInit);\n };\n\n const handleOrpc = async (\n req: http.IncomingMessage,\n res: http.ServerResponse,\n handler: any,\n prefix: string,\n errorLabel: string,\n ) => {\n try {\n const webRequest = toWebRequest(req);\n const result = await handler.handle(webRequest, {\n prefix,\n context: buildDevContext(req, webRequest),\n });\n\n if (result.response) {\n res.statusCode = result.response.status;\n result.response.headers.forEach((value: string, key: string) => {\n res.setHeader(key, value);\n });\n const text = await result.response.text();\n if (!res.getHeader(\"content-length\")) {\n res.setHeader(\"content-length\", Buffer.byteLength(text).toString());\n }\n res.end(text);\n } else {\n sendText(res, 404, \"Not Found\");\n }\n } catch (error) {\n console.error(`${errorLabel} error:`, error);\n sendJson(res, 500, { error: (error as Error).message });\n }\n };\n\n const server = http.createServer((req, res) => {\n applyCorsHeaders(res);\n const pathname = pathnameOf(req);\n\n if (req.method === \"OPTIONS\") {\n res.statusCode = 200;\n res.end();\n return;\n }\n\n if (pathname === \"/\" && (req.method === \"GET\" || req.method === \"HEAD\")) {\n sendJson(res, 200, {\n ok: true,\n plugin: pluginInfo.normalizedName,\n version: pluginInfo.version,\n status: handlers.rpc ? \"ready\" : \"loading\",\n endpoints: {\n health: \"/\",\n docs: \"/api\",\n rpc: rpcBase,\n },\n });\n return;\n }\n\n if (pathname === \"/health\" && (req.method === \"GET\" || req.method === \"HEAD\")) {\n sendText(res, 200, \"OK\");\n return;\n }\n\n if (pathname.startsWith(`${rpcBase}/`)) {\n if (!handlers.rpc) {\n sendJson(res, 503, { error: \"Plugin still loading...\" });\n return;\n }\n handleOrpc(req, res, handlers.rpc, rpcBase, \"RPC\").catch(() => {});\n return;\n }\n\n if (pathname === \"/api\" || pathname.startsWith(\"/api/\")) {\n if (!handlers.api) {\n sendJson(res, 503, { error: \"Plugin still loading...\" });\n return;\n }\n handleOrpc(req, res, handlers.api, \"/api\", \"OpenAPI\").catch(() => {});\n return;\n }\n\n serveStatic(req, res, () => {\n sendText(res, 404, \"Not Found\");\n });\n });\n\n await new Promise<void>((resolve, reject) => {\n server.once(\"error\", reject);\n server.listen(port, () => resolve());\n });\n\n console.log(`🚀 ${pluginInfo.name} dev server listening on port ${port}`);\n console.log(`├─ 📡 RPC: http://localhost:${port}/api/rpc${rpcPrefix}`);\n console.log(`├─ 📖 Docs: http://localhost:${port}/api`);\n console.log(`└─ 💚 Health: http://localhost:${port}/`);\n\n const load = async () => {\n try {\n // Import framework internals directly. A package self-import can resolve\n // the published entry while this source-first dev server is running,\n // which leaves the runtime exports unavailable on Windows.\n const { createPluginRuntime } = await import(\"../runtime\");\n const { RPCHandler } = await import(\"@orpc/server/fetch\");\n const { OpenAPIHandler } = await import(\"@orpc/openapi/fetch\");\n const { OpenAPIGenerator } = await import(\"@orpc/openapi\");\n const { OpenAPIReferenceHandlerPlugin } = await import(\"@orpc/openapi/plugins\");\n const { ZodToJsonSchemaConverter } = await import(\"@orpc/zod\");\n const { onError } = await import(\"@orpc/server\");\n const { formatORPCError } = await import(\"../errors\");\n\n const runtimeConfig = readRuntimeConfigFromEnv();\n const { siblings, dependsOn } = collectSiblingRemotes(runtimeConfig, compositionKey);\n\n if (dependsOn.length > 0) {\n console.log(`🔗 Loading sibling plugin(s): ${dependsOn.join(\", \")}`);\n }\n\n const registry: Record<string, { remote: string }> = {\n [pluginId]: {\n remote: `http://localhost:${port}/${DEV_ENTRY_FILENAME}`,\n },\n ...siblings,\n };\n\n const mfRuntime = createPluginRuntime({ registry });\n runtime = mfRuntime;\n\n const pluginsMap: Record<string, unknown> = {};\n const siblingEffectContexts: unknown[] = [];\n for (const depId of Object.keys(siblings)) {\n const dep = await loadPluginWithRetry(mfRuntime, depId, siblings[depId]!.remote);\n pluginsMap[depId] = { client: dep.createClient, router: dep.router };\n if (dep.initialized?.effectContext) {\n siblingEffectContexts.push(dep.initialized.effectContext);\n }\n console.log(`✅ Loaded dependency plugin: ${depId}`);\n }\n\n const loaded: any = await loadPluginWithRetry(\n mfRuntime,\n pluginId,\n registry[pluginId]!.remote,\n (devConfig?.config ?? { variables: {}, secrets: {} }) as Record<string, unknown>,\n Object.keys(pluginsMap).length > 0 ? pluginsMap : undefined,\n );\n\n const { Context } = await import(\"effect\");\n effectContextHolder.context = [loaded.initialized?.effectContext, ...siblingEffectContexts]\n .filter(Boolean)\n .reduce((acc: any, ctx: any) => Context.merge(acc, ctx), Context.empty());\n\n handlers.rpc = new RPCHandler(loaded.router, {\n errorStatusMap: PLUGIN_ERROR_STATUS_MAP,\n interceptors: [\n onError((error: any) => {\n const formatted = formatORPCError(error);\n if (formatted) console.error(formatted);\n }),\n ],\n });\n\n const generator = new OpenAPIGenerator({ converters: [new ZodToJsonSchemaConverter()] });\n\n handlers.api = new OpenAPIHandler(loaded.router, {\n errorStatusMap: PLUGIN_ERROR_STATUS_MAP,\n plugins: [\n new OpenAPIReferenceHandlerPlugin({\n spec: () => generator.generate(loaded.router, { version: \"3.1.1\" }),\n }),\n ],\n interceptors: [\n onError((error: any) => {\n const formatted = formatORPCError(error);\n if (formatted) console.error(formatted);\n }),\n ],\n });\n\n console.log(`✅ Plugin ready: ${pluginId}`);\n } catch (error) {\n console.error(`❌ Failed to load plugin ${pluginId}:`, error);\n console.error(\" Server stays up; fix the build and retry via rebuild.\");\n }\n };\n\n void load();\n\n const close = async () => {\n handlers.rpc = null;\n handlers.api = null;\n effectContextHolder.context = null;\n for (const child of [watcher, uiWatcher]) {\n if (child) {\n await killChildEscalating(child);\n }\n }\n if (uiStaticServer) {\n await new Promise<void>((resolve) => uiStaticServer.close(() => resolve()));\n }\n if (runtime) await runtime.shutdown().catch(() => {});\n await new Promise<void>((resolve) => server.close(() => resolve()));\n };\n\n process.once(\"SIGINT\", async () => {\n const timeout = setTimeout(() => process.exit(0), 3000);\n await close();\n clearTimeout(timeout);\n process.exit(0);\n });\n process.once(\"SIGTERM\", async () => {\n const timeout = setTimeout(() => process.exit(0), 3000);\n await close();\n clearTimeout(timeout);\n process.exit(0);\n });\n\n watchParentDeath(async () => {\n const timeout = setTimeout(() => process.exit(0), 3000);\n await close();\n clearTimeout(timeout);\n process.exit(0);\n });\n\n return { port, close };\n}\n\nconst invokedAsCli = import.meta.main || (process.argv[1] ?? \"\").includes(\"every-plugin-serve\");\n\nif (invokedAsCli) {\n startPluginDevServer().catch((error) => {\n console.error(\"❌ Plugin dev server fatal error:\", error);\n process.exit(1);\n });\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"serve.mjs","names":[],"sources":["../../src/dev/serve.ts"],"sourcesContent":["import { spawn } from \"node:child_process\";\nimport fs from \"node:fs\";\nimport http from \"node:http\";\nimport path from \"node:path\";\nimport { Effect } from \"effect\";\nimport sirv from \"sirv\";\nimport { DEV_ENTRY_FILENAME } from \"../build/artifact-names\";\nimport { ensureGeneratedRspackConfig } from \"../build/rspack/generated-config\";\nimport { getPluginInfo, loadDevConfig } from \"../build/rspack/utils\";\nimport {\n ensureGeneratedUiRsbuildConfig,\n pluginLayoutKey,\n uiDistDirFor,\n} from \"../build/ui/generated-config\";\nimport { PLUGIN_ERROR_STATUS_MAP } from \"../errors\";\nimport { loadRemoteWithRetry } from \"../remote-entry\";\nimport { startDirectModuleHost } from \"./host\";\nimport { killChildEscalating, watchParentDeath } from \"./watch-kill\";\n\nconst corsHeaders = {\n \"Access-Control-Allow-Origin\": \"*\",\n \"Access-Control-Allow-Methods\": \"GET, POST, PUT, DELETE, PATCH, OPTIONS\",\n \"Access-Control-Allow-Headers\": \"X-Requested-With, content-type, Authorization\",\n};\n\nconst applyCorsHeaders = (res: http.ServerResponse) => {\n Object.entries(corsHeaders).forEach(([key, value]) => {\n res.setHeader(key, value);\n });\n};\n\nconst normalizePrefix = (prefix?: string): string => {\n if (!prefix) return \"\";\n const cleaned = prefix.replace(/^\\/+|\\/+$/g, \"\");\n return cleaned ? `/${cleaned}` : \"\";\n};\n\nconst readRuntimeConfigFromEnv = (): any => {\n const raw = process.env.BOS_RUNTIME_CONFIG;\n if (!raw) return null;\n try {\n return JSON.parse(raw);\n } catch {\n return null;\n }\n};\n\nconst collectSiblingRemotes = (runtimeConfig: any, pluginId: string) => {\n const siblings: Record<string, { remote: string }> = {};\n\n const isApi = pluginId === \"api\" || runtimeConfig?.api?.name === pluginId;\n\n const ownKey = runtimeConfig?.plugins?.[pluginId] ? pluginId : null;\n let dependsOn: string[] = [];\n if (ownKey) {\n dependsOn = runtimeConfig.plugins[ownKey].dependsOn ?? [];\n } else if (isApi) {\n dependsOn = runtimeConfig?.api?.dependsOn ?? [];\n }\n\n for (const depId of dependsOn) {\n const dep = runtimeConfig?.plugins?.[depId];\n if (!dep || dep.source !== \"local\" || !dep.url) continue;\n if (depId === pluginId) continue;\n const base = dep.url.replace(/\\/$/, \"\");\n siblings[depId] = { remote: `${base}/${DEV_ENTRY_FILENAME}` };\n }\n\n return { siblings, dependsOn };\n};\n\nconst loadPluginWithRetry = (\n runtime: any,\n pluginId: string,\n remoteUrl: string,\n options: Record<string, unknown> = { variables: {}, secrets: {} },\n pluginsMap?: Record<string, unknown>,\n timeoutMs = 90000,\n): Promise<any> =>\n Effect.runPromise(\n loadRemoteWithRetry({\n label: pluginId,\n remoteUrl,\n load: () => runtime.usePlugin(pluginId, options, pluginsMap),\n timeoutMs,\n }),\n );\n\nconst sendText = (res: http.ServerResponse, status: number, text: string) => {\n res.statusCode = status;\n res.setHeader(\"content-type\", \"text/plain; charset=utf-8\");\n res.end(text);\n};\n\nexport interface PluginDevServeOptions {\n cwd?: string;\n port?: number;\n watch?: boolean;\n}\n\nexport interface PluginDevServerHandle {\n port: number;\n close: () => Promise<void>;\n}\n\n/**\n * The dev server's plugin id — the workspace's npm package name, the same\n * value the host passes as `runtimeId`. Both initialize sites must agree:\n * `registerRemote` derives the MF remote name from it, and the plugin's\n * database layer derives its migration slug from it, so a pre-normalized\n * remote name here would migrate into a different schema than the host\n * expects. plugin.dev.ts no longer carries a pluginId field.\n */\nexport const resolveDevPluginId = (cwd: string): string => getPluginInfo(cwd).name;\n\nexport async function startPluginDevServer(\n options: PluginDevServeOptions = {},\n): Promise<PluginDevServerHandle> {\n process.env.BOS_DEV_SERVER = \"1\";\n const cwd = options.cwd ?? process.cwd();\n const pluginInfo = getPluginInfo(cwd);\n const devConfig = loadDevConfig(path.join(cwd, \"plugin.dev.ts\"));\n const pluginId = resolveDevPluginId(cwd);\n const compositionKey = pluginLayoutKey(cwd) ?? pluginId;\n const port = options.port ?? (Number(process.env.PORT) || devConfig?.port || 3999);\n const rpcPrefix = normalizePrefix(devConfig?.prefix);\n\n const distDir = path.join(cwd, \"dist\");\n const serveStatic = sirv(distDir, { dev: true });\n\n // No-watch mode (BOS_NO_WATCH=1): build once, serve the built output.\n // Regression/CI stacks need no hot reload — watchers are the stack's\n // heaviest processes and stall runs freeze under their accumulated\n // footprint. One-shot builds exit; steady state is small static servers.\n const noWatch = options.watch === false || process.env.BOS_NO_WATCH === \"1\";\n\n const runOnce = (cmd: string, args: string[]) =>\n new Promise<void>((resolve, reject) => {\n const child = spawn(cmd, args, { cwd, stdio: \"inherit\", env: process.env });\n child.on(\"error\", reject);\n child.on(\"exit\", (code) => {\n if (code === 0) resolve();\n else reject(new Error(`❌ ${cmd} ${args.join(\" \")} exited with ${code}`));\n });\n });\n\n let watcher: ReturnType<typeof spawn> | null = null;\n if (noWatch) {\n const generatedConfig = ensureGeneratedRspackConfig(cwd);\n await runOnce(\"rspack\", generatedConfig ? [\"build\", \"--config\", generatedConfig] : [\"build\"]);\n } else if (options.watch !== false) {\n const generatedConfig = ensureGeneratedRspackConfig(cwd);\n watcher = spawn(\n \"rspack\",\n generatedConfig ? [\"build\", \"--watch\", \"--config\", generatedConfig] : [\"build\", \"--watch\"],\n {\n cwd,\n stdio: \"inherit\",\n env: process.env,\n },\n );\n watcher.on(\"error\", (error) => {\n console.error(`❌ Failed to spawn rspack build --watch: ${error.message}`);\n });\n }\n\n let uiWatcher: ReturnType<typeof spawn> | null = null;\n let uiStaticServer: http.Server | null = null;\n const uiPort = process.env.BOS_UI_PORT;\n const uiConfig = ensureGeneratedUiRsbuildConfig(cwd);\n if (uiConfig && noWatch && uiPort) {\n // Folder-form ui: one-shot build, then serve the ui source root's dist\n // (the factory's absolute distPath target) statically on the ui port.\n await runOnce(\"rsbuild\", [\"build\", \"--config\", uiConfig]);\n const uiDistDir = uiDistDirFor(cwd);\n if (!fs.existsSync(path.join(uiDistDir, DEV_ENTRY_FILENAME))) {\n throw new Error(\n `❌ UI dist is missing at ${uiDistDir} after the rsbuild one-shot — build output layout mismatch?`,\n );\n }\n const serveUiStatic = sirv(uiDistDir, { dev: true });\n uiStaticServer = http.createServer((req, res) => {\n applyCorsHeaders(res);\n serveUiStatic(req, res, () => {\n sendText(res, 404, \"Not Found\");\n });\n });\n await new Promise<void>((resolve, reject) => {\n uiStaticServer!.once(\"error\", reject);\n uiStaticServer!.listen(Number(uiPort), () => resolve());\n });\n console.log(`├─ 🎨 UI: http://localhost:${uiPort} (built, no watch)`);\n } else if (uiConfig) {\n uiWatcher = spawn(\"rsbuild\", [\"dev\", \"--config\", uiConfig], {\n cwd,\n stdio: \"inherit\",\n env: uiPort ? { ...process.env, PORT: uiPort } : process.env,\n });\n uiWatcher.on(\"error\", (error) => {\n console.error(`❌ Failed to spawn rsbuild ui dev: ${error.message}`);\n });\n console.log(`├─ 🎨 UI: ${uiPort ? `http://localhost:${uiPort}` : \"(rsbuild auto port)\"}`);\n }\n\n const handlers: { rpc: any; api: any } = { rpc: null, api: null };\n const effectContextHolder: { context: unknown } = { context: null };\n let runtime: { shutdown: () => Promise<void> } | null = null;\n const host = await startDirectModuleHost({\n name: pluginInfo.normalizedName,\n version: pluginInfo.version,\n handlers,\n effectContext: () => effectContextHolder.context,\n port,\n staticHandler: serveStatic,\n });\n\n console.log(`🚀 ${pluginInfo.name} dev server listening on port ${host.port}`);\n console.log(`├─ 📡 RPC: http://localhost:${host.port}/api/rpc${rpcPrefix}`);\n console.log(`├─ 📖 Docs: http://localhost:${host.port}/api`);\n console.log(`└─ 💚 Health: http://localhost:${host.port}/`);\n\n const load = async () => {\n try {\n // Import framework internals directly. A package self-import can resolve\n // the published entry while this source-first dev server is running,\n // which leaves the runtime exports unavailable on Windows.\n const { createPluginRuntime } = await import(\"../runtime\");\n const { RPCHandler } = await import(\"@orpc/server/fetch\");\n const { OpenAPIHandler } = await import(\"@orpc/openapi/fetch\");\n const { OpenAPIGenerator } = await import(\"@orpc/openapi\");\n const { OpenAPIReferenceHandlerPlugin } = await import(\"@orpc/openapi/plugins\");\n const { ZodToJsonSchemaConverter } = await import(\"@orpc/zod\");\n const { onError } = await import(\"@orpc/server\");\n const { formatORPCError } = await import(\"../errors\");\n\n const runtimeConfig = readRuntimeConfigFromEnv();\n const { siblings, dependsOn } = collectSiblingRemotes(runtimeConfig, compositionKey);\n\n if (dependsOn.length > 0) {\n console.log(`🔗 Loading sibling plugin(s): ${dependsOn.join(\", \")}`);\n }\n\n const registry: Record<string, { remote: string }> = {\n [pluginId]: {\n remote: `http://localhost:${host.port}/${DEV_ENTRY_FILENAME}`,\n },\n ...siblings,\n };\n\n const mfRuntime = createPluginRuntime({ registry });\n runtime = mfRuntime;\n\n const pluginsMap: Record<string, unknown> = {};\n const siblingEffectContexts: unknown[] = [];\n for (const depId of Object.keys(siblings)) {\n const dep = await loadPluginWithRetry(mfRuntime, depId, siblings[depId]!.remote);\n pluginsMap[depId] = { client: dep.createClient, router: dep.router };\n if (dep.initialized?.effectContext) {\n siblingEffectContexts.push(dep.initialized.effectContext);\n }\n console.log(`✅ Loaded dependency plugin: ${depId}`);\n }\n\n const loaded: any = await loadPluginWithRetry(\n mfRuntime,\n pluginId,\n registry[pluginId]!.remote,\n (devConfig?.config ?? { variables: {}, secrets: {} }) as Record<string, unknown>,\n Object.keys(pluginsMap).length > 0 ? pluginsMap : undefined,\n );\n\n const { Context } = await import(\"effect\");\n effectContextHolder.context = [loaded.initialized?.effectContext, ...siblingEffectContexts]\n .filter(Boolean)\n .reduce((acc: any, ctx: any) => Context.merge(acc, ctx), Context.empty());\n\n handlers.rpc = new RPCHandler(loaded.router, {\n errorStatusMap: PLUGIN_ERROR_STATUS_MAP,\n interceptors: [\n onError((error: any) => {\n const formatted = formatORPCError(error);\n if (formatted) console.error(formatted);\n }),\n ],\n });\n\n const generator = new OpenAPIGenerator({ converters: [new ZodToJsonSchemaConverter()] });\n\n handlers.api = new OpenAPIHandler(loaded.router, {\n errorStatusMap: PLUGIN_ERROR_STATUS_MAP,\n plugins: [\n new OpenAPIReferenceHandlerPlugin({\n spec: () => generator.generate(loaded.router, { version: \"3.1.1\" }),\n }),\n ],\n interceptors: [\n onError((error: any) => {\n const formatted = formatORPCError(error);\n if (formatted) console.error(formatted);\n }),\n ],\n });\n\n console.log(`✅ Plugin ready: ${pluginId}`);\n } catch (error) {\n console.error(`❌ Failed to load plugin ${pluginId}:`, error);\n console.error(\" Server stays up; fix the build and retry via rebuild.\");\n }\n };\n\n void load();\n\n const close = async () => {\n handlers.rpc = null;\n handlers.api = null;\n effectContextHolder.context = null;\n for (const child of [watcher, uiWatcher]) {\n if (child) {\n await killChildEscalating(child);\n }\n }\n if (uiStaticServer) {\n await new Promise<void>((resolve) => uiStaticServer.close(() => resolve()));\n }\n await host.close();\n if (runtime) await runtime.shutdown().catch(() => {});\n };\n\n process.once(\"SIGINT\", async () => {\n const timeout = setTimeout(() => process.exit(0), 3000);\n await close();\n clearTimeout(timeout);\n process.exit(0);\n });\n process.once(\"SIGTERM\", async () => {\n const timeout = setTimeout(() => process.exit(0), 3000);\n await close();\n clearTimeout(timeout);\n process.exit(0);\n });\n\n watchParentDeath(async () => {\n const timeout = setTimeout(() => process.exit(0), 3000);\n await close();\n clearTimeout(timeout);\n process.exit(0);\n });\n\n return { port, close };\n}\n\nconst invokedAsCli = import.meta.main || (process.argv[1] ?? \"\").includes(\"every-plugin-serve\");\n\nif (invokedAsCli) {\n startPluginDevServer().catch((error) => {\n console.error(\"❌ Plugin dev server fatal error:\", error);\n process.exit(1);\n });\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAmBA,MAAM,cAAc;CAClB,+BAA+B;CAC/B,gCAAgC;CAChC,gCAAgC;AAClC;AAEA,MAAM,oBAAoB,QAA6B;CACrD,OAAO,QAAQ,WAAW,CAAC,CAAC,SAAS,CAAC,KAAK,WAAW;EACpD,IAAI,UAAU,KAAK,KAAK;CAC1B,CAAC;AACH;AAEA,MAAM,mBAAmB,WAA4B;CACnD,IAAI,CAAC,QAAQ,OAAO;CACpB,MAAM,UAAU,OAAO,QAAQ,cAAc,EAAE;CAC/C,OAAO,UAAU,IAAI,YAAY;AACnC;AAEA,MAAM,iCAAsC;CAC1C,MAAM,MAAM,QAAQ,IAAI;CACxB,IAAI,CAAC,KAAK,OAAO;CACjB,IAAI;EACF,OAAO,KAAK,MAAM,GAAG;CACvB,QAAQ;EACN,OAAO;CACT;AACF;AAEA,MAAM,yBAAyB,eAAoB,aAAqB;CACtE,MAAM,WAA+C,CAAC;CAEtD,MAAM,QAAQ,aAAa,SAAS,eAAe,KAAK,SAAS;CAEjE,MAAM,SAAS,eAAe,UAAU,YAAY,WAAW;CAC/D,IAAI,YAAsB,CAAC;CAC3B,IAAI,QACF,YAAY,cAAc,QAAQ,OAAO,CAAC,aAAa,CAAC;MACnD,IAAI,OACT,YAAY,eAAe,KAAK,aAAa,CAAC;CAGhD,KAAK,MAAM,SAAS,WAAW;EAC7B,MAAM,MAAM,eAAe,UAAU;EACrC,IAAI,CAAC,OAAO,IAAI,WAAW,WAAW,CAAC,IAAI,KAAK;EAChD,IAAI,UAAU,UAAU;EAExB,SAAS,SAAS,EAAE,QAAQ,GADf,IAAI,IAAI,QAAQ,OAAO,EACF,EAAE,GAAG,qBAAqB;CAC9D;CAEA,OAAO;EAAE;EAAU;CAAU;AAC/B;AAEA,MAAM,uBACJ,SACA,UACA,WACA,UAAmC;CAAE,WAAW,CAAC;CAAG,SAAS,CAAC;AAAE,GAChE,YACA,YAAY,QAEZ,OAAO,WACL,oBAAoB;CAClB,OAAO;CACP;CACA,YAAY,QAAQ,UAAU,UAAU,SAAS,UAAU;CAC3D;AACF,CAAC,CACH;AAEF,MAAM,YAAY,KAA0B,QAAgB,SAAiB;CAC3E,IAAI,aAAa;CACjB,IAAI,UAAU,gBAAgB,2BAA2B;CACzD,IAAI,IAAI,IAAI;AACd;;;;;;;;;AAqBA,MAAa,sBAAsB,QAAwB,cAAc,GAAG,CAAC,CAAC;AAE9E,eAAsB,qBACpB,UAAiC,CAAC,GACF;CAChC,QAAQ,IAAI,iBAAiB;CAC7B,MAAM,MAAM,QAAQ,OAAO,QAAQ,IAAI;CACvC,MAAM,aAAa,cAAc,GAAG;CACpC,MAAM,YAAY,cAAc,KAAK,KAAK,KAAK,eAAe,CAAC;CAC/D,MAAM,WAAW,mBAAmB,GAAG;CACvC,MAAM,iBAAiB,gBAAgB,GAAG,KAAK;CAC/C,MAAM,OAAO,QAAQ,SAAS,OAAO,QAAQ,IAAI,IAAI,KAAK,WAAW,QAAQ;CAC7E,MAAM,YAAY,gBAAgB,WAAW,MAAM;CAEnD,MAAM,UAAU,KAAK,KAAK,KAAK,MAAM;CACrC,MAAM,cAAc,KAAK,SAAS,EAAE,KAAK,KAAK,CAAC;CAM/C,MAAM,UAAU,QAAQ,UAAU,SAAS,QAAQ,IAAI,iBAAiB;CAExE,MAAM,WAAW,KAAa,SAC5B,IAAI,SAAe,SAAS,WAAW;EACrC,MAAM,QAAQ,MAAM,KAAK,MAAM;GAAE;GAAK,OAAO;GAAW,KAAK,QAAQ;EAAI,CAAC;EAC1E,MAAM,GAAG,SAAS,MAAM;EACxB,MAAM,GAAG,SAAS,SAAS;GACzB,IAAI,SAAS,GAAG,QAAQ;QACnB,uBAAO,IAAI,MAAM,KAAK,IAAI,GAAG,KAAK,KAAK,GAAG,EAAE,eAAe,MAAM,CAAC;EACzE,CAAC;CACH,CAAC;CAEH,IAAI,UAA2C;CAC/C,IAAI,SAAS;EACX,MAAM,kBAAkB,4BAA4B,GAAG;EACvD,MAAM,QAAQ,UAAU,kBAAkB;GAAC;GAAS;GAAY;EAAe,IAAI,CAAC,OAAO,CAAC;CAC9F,OAAO,IAAI,QAAQ,UAAU,OAAO;EAClC,MAAM,kBAAkB,4BAA4B,GAAG;EACvD,UAAU,MACR,UACA,kBAAkB;GAAC;GAAS;GAAW;GAAY;EAAe,IAAI,CAAC,SAAS,SAAS,GACzF;GACE;GACA,OAAO;GACP,KAAK,QAAQ;EACf,CACF;EACA,QAAQ,GAAG,UAAU,UAAU;GAC7B,QAAQ,MAAM,2CAA2C,MAAM,SAAS;EAC1E,CAAC;CACH;CAEA,IAAI,YAA6C;CACjD,IAAI,iBAAqC;CACzC,MAAM,SAAS,QAAQ,IAAI;CAC3B,MAAM,WAAW,+BAA+B,GAAG;CACnD,IAAI,YAAY,WAAW,QAAQ;EAGjC,MAAM,QAAQ,WAAW;GAAC;GAAS;GAAY;EAAQ,CAAC;EACxD,MAAM,YAAY,aAAa,GAAG;EAClC,IAAI,CAAC,GAAG,WAAW,KAAK,KAAK,2BAA6B,CAAC,GACzD,MAAM,IAAI,MACR,2BAA2B,UAAU,4DACvC;EAEF,MAAM,gBAAgB,KAAK,WAAW,EAAE,KAAK,KAAK,CAAC;EACnD,iBAAiB,KAAK,cAAc,KAAK,QAAQ;GAC/C,iBAAiB,GAAG;GACpB,cAAc,KAAK,WAAW;IAC5B,SAAS,KAAK,KAAK,WAAW;GAChC,CAAC;EACH,CAAC;EACD,MAAM,IAAI,SAAe,SAAS,WAAW;GAC3C,eAAgB,KAAK,SAAS,MAAM;GACpC,eAAgB,OAAO,OAAO,MAAM,SAAS,QAAQ,CAAC;EACxD,CAAC;EACD,QAAQ,IAAI,kCAAkC,OAAO,mBAAmB;CAC1E,OAAO,IAAI,UAAU;EACnB,YAAY,MAAM,WAAW;GAAC;GAAO;GAAY;EAAQ,GAAG;GAC1D;GACA,OAAO;GACP,KAAK,SAAS;IAAE,GAAG,QAAQ;IAAK,MAAM;GAAO,IAAI,QAAQ;EAC3D,CAAC;EACD,UAAU,GAAG,UAAU,UAAU;GAC/B,QAAQ,MAAM,qCAAqC,MAAM,SAAS;EACpE,CAAC;EACD,QAAQ,IAAI,iBAAiB,SAAS,oBAAoB,WAAW,uBAAuB;CAC9F;CAEA,MAAM,WAAmC;EAAE,KAAK;EAAM,KAAK;CAAK;CAChE,MAAM,sBAA4C,EAAE,SAAS,KAAK;CAClE,IAAI,UAAoD;CACxD,MAAM,OAAO,MAAM,sBAAsB;EACvC,MAAM,WAAW;EACjB,SAAS,WAAW;EACpB;EACA,qBAAqB,oBAAoB;EACzC;EACA,eAAe;CACjB,CAAC;CAED,QAAQ,IAAI,MAAM,WAAW,KAAK,gCAAgC,KAAK,MAAM;CAC7E,QAAQ,IAAI,kCAAkC,KAAK,KAAK,UAAU,WAAW;CAC7E,QAAQ,IAAI,kCAAkC,KAAK,KAAK,KAAK;CAC7D,QAAQ,IAAI,kCAAkC,KAAK,KAAK,EAAE;CAE1D,MAAM,OAAO,YAAY;EACvB,IAAI;GAIF,MAAM,EAAE,wBAAwB,MAAM,OAAO;GAC7C,MAAM,EAAE,eAAe,MAAM,OAAO;GACpC,MAAM,EAAE,mBAAmB,MAAM,OAAO;GACxC,MAAM,EAAE,qBAAqB,MAAM,OAAO;GAC1C,MAAM,EAAE,kCAAkC,MAAM,OAAO;GACvD,MAAM,EAAE,6BAA6B,MAAM,OAAO;GAClD,MAAM,EAAE,YAAY,MAAM,OAAO;GACjC,MAAM,EAAE,oBAAoB,MAAM,OAAO;GAEzC,MAAM,gBAAgB,yBAAyB;GAC/C,MAAM,EAAE,UAAU,cAAc,sBAAsB,eAAe,cAAc;GAEnF,IAAI,UAAU,SAAS,GACrB,QAAQ,IAAI,iCAAiC,UAAU,KAAK,IAAI,GAAG;GAGrE,MAAM,WAA+C;KAClD,WAAW,EACV,QAAQ,oBAAoB,KAAK,KAAK,GAAG,qBAC3C;IACA,GAAG;GACL;GAEA,MAAM,YAAY,oBAAoB,EAAE,SAAS,CAAC;GAClD,UAAU;GAEV,MAAM,aAAsC,CAAC;GAC7C,MAAM,wBAAmC,CAAC;GAC1C,KAAK,MAAM,SAAS,OAAO,KAAK,QAAQ,GAAG;IACzC,MAAM,MAAM,MAAM,oBAAoB,WAAW,OAAO,SAAS,MAAM,CAAE,MAAM;IAC/E,WAAW,SAAS;KAAE,QAAQ,IAAI;KAAc,QAAQ,IAAI;IAAO;IACnE,IAAI,IAAI,aAAa,eACnB,sBAAsB,KAAK,IAAI,YAAY,aAAa;IAE1D,QAAQ,IAAI,+BAA+B,OAAO;GACpD;GAEA,MAAM,SAAc,MAAM,oBACxB,WACA,UACA,SAAS,SAAS,CAAE,QACnB,WAAW,UAAU;IAAE,WAAW,CAAC;IAAG,SAAS,CAAC;GAAE,GACnD,OAAO,KAAK,UAAU,CAAC,CAAC,SAAS,IAAI,aAAa,MACpD;GAEA,MAAM,EAAE,YAAY,MAAM,OAAO;GACjC,oBAAoB,UAAU,CAAC,OAAO,aAAa,eAAe,GAAG,qBAAqB,CAAC,CACxF,OAAO,OAAO,CAAC,CACf,QAAQ,KAAU,QAAa,QAAQ,MAAM,KAAK,GAAG,GAAG,QAAQ,MAAM,CAAC;GAE1E,SAAS,MAAM,IAAI,WAAW,OAAO,QAAQ;IAC3C,gBAAgB;IAChB,cAAc,CACZ,SAAS,UAAe;KACtB,MAAM,YAAY,gBAAgB,KAAK;KACvC,IAAI,WAAW,QAAQ,MAAM,SAAS;IACxC,CAAC,CACH;GACF,CAAC;GAED,MAAM,YAAY,IAAI,iBAAiB,EAAE,YAAY,CAAC,IAAI,yBAAyB,CAAC,EAAE,CAAC;GAEvF,SAAS,MAAM,IAAI,eAAe,OAAO,QAAQ;IAC/C,gBAAgB;IAChB,SAAS,CACP,IAAI,8BAA8B,EAChC,YAAY,UAAU,SAAS,OAAO,QAAQ,EAAE,SAAS,QAAQ,CAAC,EACpE,CAAC,CACH;IACA,cAAc,CACZ,SAAS,UAAe;KACtB,MAAM,YAAY,gBAAgB,KAAK;KACvC,IAAI,WAAW,QAAQ,MAAM,SAAS;IACxC,CAAC,CACH;GACF,CAAC;GAED,QAAQ,IAAI,mBAAmB,UAAU;EAC3C,SAAS,OAAO;GACd,QAAQ,MAAM,2BAA2B,SAAS,IAAI,KAAK;GAC3D,QAAQ,MAAM,0DAA0D;EAC1E;CACF;CAEA,AAAK,KAAK;CAEV,MAAM,QAAQ,YAAY;EACxB,SAAS,MAAM;EACf,SAAS,MAAM;EACf,oBAAoB,UAAU;EAC9B,KAAK,MAAM,SAAS,CAAC,SAAS,SAAS,GACrC,IAAI,OACF,MAAM,oBAAoB,KAAK;EAGnC,IAAI,gBACF,MAAM,IAAI,SAAe,YAAY,eAAe,YAAY,QAAQ,CAAC,CAAC;EAE5E,MAAM,KAAK,MAAM;EACjB,IAAI,SAAS,MAAM,QAAQ,SAAS,CAAC,CAAC,YAAY,CAAC,CAAC;CACtD;CAEA,QAAQ,KAAK,UAAU,YAAY;EACjC,MAAM,UAAU,iBAAiB,QAAQ,KAAK,CAAC,GAAG,GAAI;EACtD,MAAM,MAAM;EACZ,aAAa,OAAO;EACpB,QAAQ,KAAK,CAAC;CAChB,CAAC;CACD,QAAQ,KAAK,WAAW,YAAY;EAClC,MAAM,UAAU,iBAAiB,QAAQ,KAAK,CAAC,GAAG,GAAI;EACtD,MAAM,MAAM;EACZ,aAAa,OAAO;EACpB,QAAQ,KAAK,CAAC;CAChB,CAAC;CAED,iBAAiB,YAAY;EAC3B,MAAM,UAAU,iBAAiB,QAAQ,KAAK,CAAC,GAAG,GAAI;EACtD,MAAM,MAAM;EACZ,aAAa,OAAO;EACpB,QAAQ,KAAK,CAAC;CAChB,CAAC;CAED,OAAO;EAAE;EAAM;CAAM;AACvB;AAIA,IAFqB,YAAY,SAAS,QAAQ,KAAK,MAAM,GAAE,CAAE,SAAS,oBAAoB,GAG5F,qBAAqB,CAAC,CAAC,OAAO,UAAU;CACtC,QAAQ,MAAM,oCAAoC,KAAK;CACvD,QAAQ,KAAK,CAAC;AAChB,CAAC"}
package/dist/index.cjs CHANGED
@@ -1,4 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
+ const require_client = require('./client.cjs');
2
3
  const require_effect_helpers = require('./effect-helpers.cjs');
3
4
  const require_errors = require('./runtime/errors.cjs');
4
5
  const require_errors$1 = require('./errors.cjs');
@@ -27,6 +28,7 @@ exports.buildScopedContext = require_effect_helpers.buildScopedContext;
27
28
  exports.classifyPluginFailure = require_errors.classifyPluginFailure;
28
29
  exports.createPlugin = require_plugin.createPlugin;
29
30
  exports.createPluginRuntime = require_runtime_index.createPluginRuntime;
31
+ exports.createRpcClient = require_client.createRpcClient;
30
32
  exports.defaultMissingSecretWarnings = require_errors.defaultMissingSecretWarnings;
31
33
  exports.extractFromFiberFailure = require_errors.extractFromFiberFailure;
32
34
  exports.formatORPCError = require_errors.formatORPCError;
package/dist/index.d.cts CHANGED
@@ -1,9 +1,10 @@
1
1
  import { remoteName } from "./identity.cjs";
2
+ import { RpcClientConfig, RpcConnectionErrorCopy, createRpcClient } from "./client.cjs";
2
3
  import { ModuleFederationError, PluginLoadFailureInfo, PluginRuntimeError, PluginWarningInfo, ValidationError, classifyPluginFailure, defaultMissingSecretWarnings, extractFromFiberFailure, formatORPCError, toPluginRuntimeError, wrapORPCError } from "./runtime/errors.cjs";
3
4
  import { BAD_REQUEST, CONNECTION_ERROR, FORBIDDEN, NOT_FOUND, PLUGIN_ERROR_STATUS_MAP, PluginErrors, RATE_LIMITED, SERVICE_UNAVAILABLE, TIMEOUT, UNAUTHORIZED } from "./errors.cjs";
4
5
  import { buildScoped, buildScopedContext } from "./effect-helpers.cjs";
5
6
  import { CreatePluginFn, CreatePluginWithPlugins, LoadedPluginWithBinding, Plugin, PluginConfigFor, PluginEnv, PluginIdTag, PluginInitializeInput, PluginWarningsFn, createPlugin, withPlugins } from "./plugin.cjs";
6
- import { AnyPlugin, EveryPlugin, InitializedPlugin, LoadedPlugin, PluginClientType, PluginConfigInput, PluginContext, PluginContract, PluginInstance, PluginMetadata, PluginRegistry, PluginRouterType, PluginRuntimeConfig, PluginSecrets, PluginServicesEntry, PluginVariables, RegisteredPlugins, SecretsConfig, UsePluginResult } from "./types.cjs";
7
+ import { AnyPlugin, EveryPlugin, InitializedPlugin, LoadedPlugin, ModuleClientEntry, ModuleEndpoint, ModuleSurface, ModuleTransport, PluginClientEntry, PluginClientType, PluginConfigInput, PluginContext, PluginContract, PluginEndpoint, PluginInstance, PluginMetadata, PluginRegistry, PluginRouterType, PluginRuntimeConfig, PluginSecrets, PluginServicesEntry, PluginSurface, PluginTransport, PluginVariables, RegisteredPlugins, SecretsConfig, UsePluginResult } from "./types.cjs";
7
8
  import { PluginRuntime, createPluginRuntime } from "./runtime/index.cjs";
8
9
  import "@orpc/openapi/extensions/route";
9
- export { type AnyPlugin, BAD_REQUEST, CONNECTION_ERROR, CreatePluginFn, CreatePluginWithPlugins, type EveryPlugin, FORBIDDEN, type InitializedPlugin, type LoadedPlugin, LoadedPluginWithBinding, ModuleFederationError, NOT_FOUND, PLUGIN_ERROR_STATUS_MAP, Plugin, type PluginClientType, PluginConfigFor, type PluginConfigInput, type PluginContext, type PluginContract, PluginEnv, PluginErrors, PluginIdTag, PluginInitializeInput, type PluginInstance, type PluginLoadFailureInfo, type PluginMetadata, type PluginRegistry, type PluginRouterType, PluginRuntime, type PluginRuntimeConfig, PluginRuntimeError, type PluginSecrets, type PluginServicesEntry, type PluginVariables, type PluginWarningInfo, PluginWarningsFn, RATE_LIMITED, type RegisteredPlugins, SERVICE_UNAVAILABLE, type SecretsConfig, TIMEOUT, UNAUTHORIZED, type UsePluginResult, ValidationError, buildScoped, buildScopedContext, classifyPluginFailure, createPlugin, createPluginRuntime, defaultMissingSecretWarnings, extractFromFiberFailure, formatORPCError, remoteName as getNormalizedRemoteName, toPluginRuntimeError, withPlugins, wrapORPCError };
10
+ export { type AnyPlugin, BAD_REQUEST, CONNECTION_ERROR, CreatePluginFn, CreatePluginWithPlugins, type EveryPlugin, FORBIDDEN, type InitializedPlugin, type LoadedPlugin, LoadedPluginWithBinding, type ModuleClientEntry, type ModuleEndpoint, ModuleFederationError, type ModuleSurface, type ModuleTransport, NOT_FOUND, PLUGIN_ERROR_STATUS_MAP, Plugin, type PluginClientEntry, type PluginClientType, PluginConfigFor, type PluginConfigInput, type PluginContext, type PluginContract, type PluginEndpoint, PluginEnv, PluginErrors, PluginIdTag, PluginInitializeInput, type PluginInstance, type PluginLoadFailureInfo, type PluginMetadata, type PluginRegistry, type PluginRouterType, PluginRuntime, type PluginRuntimeConfig, PluginRuntimeError, type PluginSecrets, type PluginServicesEntry, type PluginSurface, type PluginTransport, type PluginVariables, type PluginWarningInfo, PluginWarningsFn, RATE_LIMITED, type RegisteredPlugins, RpcClientConfig, RpcConnectionErrorCopy, SERVICE_UNAVAILABLE, type SecretsConfig, TIMEOUT, UNAUTHORIZED, type UsePluginResult, ValidationError, buildScoped, buildScopedContext, classifyPluginFailure, createPlugin, createPluginRuntime, createRpcClient, defaultMissingSecretWarnings, extractFromFiberFailure, formatORPCError, remoteName as getNormalizedRemoteName, toPluginRuntimeError, withPlugins, wrapORPCError };
package/dist/index.d.mts CHANGED
@@ -1,9 +1,10 @@
1
1
  import { remoteName } from "./identity.mjs";
2
+ import { RpcClientConfig, RpcConnectionErrorCopy, createRpcClient } from "./client.mjs";
2
3
  import { ModuleFederationError, PluginLoadFailureInfo, PluginRuntimeError, PluginWarningInfo, ValidationError, classifyPluginFailure, defaultMissingSecretWarnings, extractFromFiberFailure, formatORPCError, toPluginRuntimeError, wrapORPCError } from "./runtime/errors.mjs";
3
4
  import { BAD_REQUEST, CONNECTION_ERROR, FORBIDDEN, NOT_FOUND, PLUGIN_ERROR_STATUS_MAP, PluginErrors, RATE_LIMITED, SERVICE_UNAVAILABLE, TIMEOUT, UNAUTHORIZED } from "./errors.mjs";
4
5
  import { buildScoped, buildScopedContext } from "./effect-helpers.mjs";
5
6
  import { CreatePluginFn, CreatePluginWithPlugins, LoadedPluginWithBinding, Plugin, PluginConfigFor, PluginEnv, PluginIdTag, PluginInitializeInput, PluginWarningsFn, createPlugin, withPlugins } from "./plugin.mjs";
6
- import { AnyPlugin, EveryPlugin, InitializedPlugin, LoadedPlugin, PluginClientType, PluginConfigInput, PluginContext, PluginContract, PluginInstance, PluginMetadata, PluginRegistry, PluginRouterType, PluginRuntimeConfig, PluginSecrets, PluginServicesEntry, PluginVariables, RegisteredPlugins, SecretsConfig, UsePluginResult } from "./types.mjs";
7
+ import { AnyPlugin, EveryPlugin, InitializedPlugin, LoadedPlugin, ModuleClientEntry, ModuleEndpoint, ModuleSurface, ModuleTransport, PluginClientEntry, PluginClientType, PluginConfigInput, PluginContext, PluginContract, PluginEndpoint, PluginInstance, PluginMetadata, PluginRegistry, PluginRouterType, PluginRuntimeConfig, PluginSecrets, PluginServicesEntry, PluginSurface, PluginTransport, PluginVariables, RegisteredPlugins, SecretsConfig, UsePluginResult } from "./types.mjs";
7
8
  import { PluginRuntime, createPluginRuntime } from "./runtime/index.mjs";
8
9
  import "@orpc/openapi/extensions/route";
9
- export { type AnyPlugin, BAD_REQUEST, CONNECTION_ERROR, CreatePluginFn, CreatePluginWithPlugins, type EveryPlugin, FORBIDDEN, type InitializedPlugin, type LoadedPlugin, LoadedPluginWithBinding, ModuleFederationError, NOT_FOUND, PLUGIN_ERROR_STATUS_MAP, Plugin, type PluginClientType, PluginConfigFor, type PluginConfigInput, type PluginContext, type PluginContract, PluginEnv, PluginErrors, PluginIdTag, PluginInitializeInput, type PluginInstance, type PluginLoadFailureInfo, type PluginMetadata, type PluginRegistry, type PluginRouterType, PluginRuntime, type PluginRuntimeConfig, PluginRuntimeError, type PluginSecrets, type PluginServicesEntry, type PluginVariables, type PluginWarningInfo, PluginWarningsFn, RATE_LIMITED, type RegisteredPlugins, SERVICE_UNAVAILABLE, type SecretsConfig, TIMEOUT, UNAUTHORIZED, type UsePluginResult, ValidationError, buildScoped, buildScopedContext, classifyPluginFailure, createPlugin, createPluginRuntime, defaultMissingSecretWarnings, extractFromFiberFailure, formatORPCError, remoteName as getNormalizedRemoteName, toPluginRuntimeError, withPlugins, wrapORPCError };
10
+ export { type AnyPlugin, BAD_REQUEST, CONNECTION_ERROR, CreatePluginFn, CreatePluginWithPlugins, type EveryPlugin, FORBIDDEN, type InitializedPlugin, type LoadedPlugin, LoadedPluginWithBinding, type ModuleClientEntry, type ModuleEndpoint, ModuleFederationError, type ModuleSurface, type ModuleTransport, NOT_FOUND, PLUGIN_ERROR_STATUS_MAP, Plugin, type PluginClientEntry, type PluginClientType, PluginConfigFor, type PluginConfigInput, type PluginContext, type PluginContract, type PluginEndpoint, PluginEnv, PluginErrors, PluginIdTag, PluginInitializeInput, type PluginInstance, type PluginLoadFailureInfo, type PluginMetadata, type PluginRegistry, type PluginRouterType, PluginRuntime, type PluginRuntimeConfig, PluginRuntimeError, type PluginSecrets, type PluginServicesEntry, type PluginSurface, type PluginTransport, type PluginVariables, type PluginWarningInfo, PluginWarningsFn, RATE_LIMITED, type RegisteredPlugins, RpcClientConfig, RpcConnectionErrorCopy, SERVICE_UNAVAILABLE, type SecretsConfig, TIMEOUT, UNAUTHORIZED, type UsePluginResult, ValidationError, buildScoped, buildScopedContext, classifyPluginFailure, createPlugin, createPluginRuntime, createRpcClient, defaultMissingSecretWarnings, extractFromFiberFailure, formatORPCError, remoteName as getNormalizedRemoteName, toPluginRuntimeError, withPlugins, wrapORPCError };
package/dist/index.mjs CHANGED
@@ -1,3 +1,4 @@
1
+ import { createRpcClient } from "./client.mjs";
1
2
  import { buildScoped, buildScopedContext } from "./effect-helpers.mjs";
2
3
  import { ModuleFederationError, PluginRuntimeError, ValidationError, classifyPluginFailure, defaultMissingSecretWarnings, extractFromFiberFailure, formatORPCError, toPluginRuntimeError, wrapORPCError } from "./runtime/errors.mjs";
3
4
  import { BAD_REQUEST, CONNECTION_ERROR, FORBIDDEN, NOT_FOUND, PLUGIN_ERROR_STATUS_MAP, PluginErrors, RATE_LIMITED, SERVICE_UNAVAILABLE, TIMEOUT, UNAUTHORIZED } from "./errors.mjs";
@@ -6,4 +7,4 @@ import { remoteName } from "./identity.mjs";
6
7
  import { PluginRuntime, createPluginRuntime } from "./runtime/index.mjs";
7
8
  import "@orpc/openapi/extensions/route";
8
9
 
9
- export { BAD_REQUEST, CONNECTION_ERROR, FORBIDDEN, ModuleFederationError, NOT_FOUND, PLUGIN_ERROR_STATUS_MAP, PluginErrors, PluginIdTag, PluginRuntime, PluginRuntimeError, RATE_LIMITED, SERVICE_UNAVAILABLE, TIMEOUT, UNAUTHORIZED, ValidationError, buildScoped, buildScopedContext, classifyPluginFailure, createPlugin, createPluginRuntime, defaultMissingSecretWarnings, extractFromFiberFailure, formatORPCError, remoteName as getNormalizedRemoteName, toPluginRuntimeError, withPlugins, wrapORPCError };
10
+ export { BAD_REQUEST, CONNECTION_ERROR, FORBIDDEN, ModuleFederationError, NOT_FOUND, PLUGIN_ERROR_STATUS_MAP, PluginErrors, PluginIdTag, PluginRuntime, PluginRuntimeError, RATE_LIMITED, SERVICE_UNAVAILABLE, TIMEOUT, UNAUTHORIZED, ValidationError, buildScoped, buildScopedContext, classifyPluginFailure, createPlugin, createPluginRuntime, createRpcClient, defaultMissingSecretWarnings, extractFromFiberFailure, formatORPCError, remoteName as getNormalizedRemoteName, toPluginRuntimeError, withPlugins, wrapORPCError };
package/dist/plugin.d.cts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { PluginRuntimeError, PluginWarningInfo } from "./runtime/errors.cjs";
2
+ import { AnyContractRouter, AnySchema, InferSchemaOutput } from "@orpc/contract";
2
3
  import { ContractedRouter, Implementer } from "@orpc/server";
3
4
  import { Context, Effect, Layer, Scope } from "effect";
4
- import { AnyContractRouter, AnySchema, InferSchemaOutput } from "@orpc/contract";
5
5
  import { WithEffectContext } from "@orpc/experimental-effect";
6
6
  import "@orpc/experimental-effect/extensions/effect";
7
7
  //#region src/plugin.d.ts
@@ -98,6 +98,5 @@ const loadRemoteWithRetry = (options) => {
98
98
  exports.isRemoteEntryBody = isRemoteEntryBody;
99
99
  exports.loadRemoteWithRetry = loadRemoteWithRetry;
100
100
  exports.purgeRemoteEntryCache = purgeRemoteEntryCache;
101
- exports.sleep = sleep;
102
101
  exports.waitForRemoteEntryReady = waitForRemoteEntryReady;
103
102
  //# sourceMappingURL=remote-entry.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"remote-entry.cjs","names":["Schedule","Duration","Effect","classifyPluginFailure","deepestErrorStack","toPluginRuntimeError"],"sources":["../src/remote-entry.ts"],"sourcesContent":["/**\n * Remote-entry loading resilience — the ONE contract every MF consumer in\n * the dev stack shares (plugin dev serves, the host's plugin loader).\n *\n * Two failure classes it exists for:\n * 1. Cold compile: remoteEntry.js 404s until the remote's first build —\n * the 404 body gets eval'd as JS (\"Unexpected identifier 'Found'\").\n * 2. Watch-rebuild poisoning: a failed entry load is memoized forever in\n * `globalThis.__GLOBAL_LOADING_REMOTE_ENTRY__` (runtime-core never evicts\n * rejections), so retries without a purge await the same failure.\n */\nimport { Duration, Effect, Schedule } from \"effect\";\nimport type { PluginRuntimeError } from \"./runtime/errors\";\nimport {\n classifyPluginFailure,\n deepestErrorStack,\n type PluginFailureClassification,\n toPluginRuntimeError,\n} from \"./runtime/errors\";\n\nconst POLL_INTERVAL_MS = 300;\nconst DEFAULT_TIMEOUT_MS = 120_000;\nconst RETRY_BASE_DELAY_MS = 500;\nconst RETRY_MAX_DELAY_MS = 5_000;\n\nexport const sleep = (ms: number): Promise<void> =>\n new Promise((resolve) => setTimeout(resolve, ms));\n\n/**\n * Deletes the failed remote's keys from the process-global entry-load cache.\n * Deleting a RESOLVED entry would force a wasteful re-eval, so only call\n * this from failure paths.\n */\nexport const purgeRemoteEntryCache = (remoteUrl: string): void => {\n const globalLoading = (globalThis as Record<string, unknown>).__GLOBAL_LOADING_REMOTE_ENTRY__ as\n | Record<string, unknown>\n | undefined;\n if (!globalLoading) return;\n for (const key of Object.keys(globalLoading)) {\n if (key.endsWith(`:${remoteUrl}`)) delete globalLoading[key];\n }\n};\n\nexport const isRemoteEntryBody = (body: string): boolean =>\n !/not found|<(!doctype|html)/i.test(body.slice(0, 64));\n\nexport const waitForRemoteEntryReady = async (\n label: string,\n url: string,\n timeoutMs = DEFAULT_TIMEOUT_MS,\n): Promise<void> => {\n const deadline = Date.now() + timeoutMs;\n let announced = false;\n while (Date.now() < deadline) {\n const res = await fetch(url).catch(() => null);\n if (res?.ok && isRemoteEntryBody(await res.text().catch(() => \"\"))) return;\n if (!announced) {\n announced = true;\n console.log(`⏳ waiting for ${label} to compile (remote entry not ready)…`);\n }\n await sleep(POLL_INTERVAL_MS);\n }\n throw new Error(`${label} remote entry never became ready at ${url}`);\n};\n\nexport interface PluginLoadAttemptInfo {\n attempt: number;\n classification: PluginFailureClassification;\n}\n\nexport interface PluginLoadRetryOptions<T> {\n /** short plugin key for log prefixes (host entry key or plugin id) */\n label: string;\n remoteUrl: string;\n load: () => Promise<T>;\n /** wall-clock budget for the retry loop */\n timeoutMs?: number;\n /**\n * Budget for the readiness poll (cold-compile wait) — defaults to the\n * retry budget, preserving the historical behavior. Pass `0` to skip the\n * poll (useful when the caller stubs `load` and does not need network).\n */\n readinessTimeoutMs?: number;\n /**\n * Observes every failed attempt (including non-retryable ones that end the\n * loop). Defaults to classification-deduped console.error under the\n * `[Plugins]` prefix.\n */\n onAttempt?: ((error: unknown, info: PluginLoadAttemptInfo) => void) | undefined;\n}\n\n/**\n * The one plugin-load retry policy: poll the remote entry until it is actually\n * served, then load with capped exponential backoff — purging the poisoned\n * global entry cache between attempts so each retry genuinely re-fetches.\n *\n * Recovery policy is `classifyPluginFailure().retryable`: transient network\n * conditions retry until the wall-clock budget runs out; permanent failures\n * (MF identity skew, schema validation, dead remotes, TLS verification) fail\n * fast on the first attempt instead of burning the budget silently.\n *\n * Fails with the ORIGINAL error from `load` — the host builds\n * `PluginBootstrapError.cause` from it and classification walks it.\n */\nexport const loadRemoteWithRetry = <T>(\n options: PluginLoadRetryOptions<T>,\n): Effect.Effect<T, PluginRuntimeError> => {\n const {\n label,\n remoteUrl,\n load,\n timeoutMs = DEFAULT_TIMEOUT_MS,\n readinessTimeoutMs,\n onAttempt,\n } = options;\n const readinessTimeout = readinessTimeoutMs ?? timeoutMs;\n\n const schedule = Schedule.exponential(Duration.millis(RETRY_BASE_DELAY_MS)).pipe(\n Schedule.modifyDelay(({ output }) =>\n Effect.succeed(\n Duration.toMillis(output) > RETRY_MAX_DELAY_MS\n ? Duration.millis(RETRY_MAX_DELAY_MS)\n : output,\n ),\n ),\n Schedule.upTo({ duration: Duration.millis(timeoutMs) }),\n );\n\n let attempt = 0;\n let lastSignature: string | undefined;\n const reportFailure = (error: unknown): void => {\n attempt += 1;\n purgeRemoteEntryCache(remoteUrl);\n const classification = classifyPluginFailure(error);\n if (onAttempt) {\n onAttempt(error, { attempt, classification });\n return;\n }\n const signature = `${classification.kind}::${classification.message}`;\n if (signature === lastSignature) return;\n lastSignature = signature;\n // The message alone is rarely enough, and the wrapper's own stack points\n // at the wrapping frames — log the DEEPEST cause's stack (the original\n // throw site inside the evaluated remote entry).\n const stack = deepestErrorStack(error);\n console.error(\n `[Plugins][${label}] ❌ ${classification.kind} failure (attempt ${attempt}): ${classification.message}` +\n (classification.suggestion ? `\\n[Plugins][${label}] → ${classification.suggestion}` : \"\") +\n (stack ? `\\n[Plugins][${label}] ${stack}` : \"\"),\n );\n };\n\n const loadWithRetry = Effect.tryPromise({\n try: load,\n catch: (error) => toPluginRuntimeError(error, label, undefined, \"remote-entry-load\"),\n }).pipe(\n Effect.tapError((error) => Effect.sync(() => reportFailure(error))),\n Effect.retry({ schedule, while: (error) => classifyPluginFailure(error).retryable }),\n );\n\n // readinessTimeoutMs: 0 skips the cold-compile poll (tests stub `load` and\n // must not race a 1ms wall-clock window against the event loop).\n if (readinessTimeout === 0) {\n return loadWithRetry;\n }\n\n return Effect.tryPromise({\n try: () => waitForRemoteEntryReady(label, remoteUrl, readinessTimeout),\n catch: (error) => toPluginRuntimeError(error, label, undefined, \"remote-entry-readiness\"),\n }).pipe(Effect.flatMap(() => loadWithRetry));\n};\n"],"mappings":";;;;;;;;;;;;;;;;AAoBA,MAAM,mBAAmB;AACzB,MAAM,qBAAqB;AAC3B,MAAM,sBAAsB;AAC5B,MAAM,qBAAqB;AAE3B,MAAa,SAAS,OACpB,IAAI,SAAS,YAAY,WAAW,SAAS,EAAE,CAAC;;;;;;AAOlD,MAAa,yBAAyB,cAA4B;CAChE,MAAM,gBAAiB,WAAuC;CAG9D,IAAI,CAAC,eAAe;CACpB,KAAK,MAAM,OAAO,OAAO,KAAK,aAAa,GACzC,IAAI,IAAI,SAAS,IAAI,WAAW,GAAG,OAAO,cAAc;AAE5D;AAEA,MAAa,qBAAqB,SAChC,CAAC,8BAA8B,KAAK,KAAK,MAAM,GAAG,EAAE,CAAC;AAEvD,MAAa,0BAA0B,OACrC,OACA,KACA,YAAY,uBACM;CAClB,MAAM,WAAW,KAAK,IAAI,IAAI;CAC9B,IAAI,YAAY;CAChB,OAAO,KAAK,IAAI,IAAI,UAAU;EAC5B,MAAM,MAAM,MAAM,MAAM,GAAG,CAAC,CAAC,YAAY,IAAI;EAC7C,IAAI,KAAK,MAAM,kBAAkB,MAAM,IAAI,KAAK,CAAC,CAAC,YAAY,EAAE,CAAC,GAAG;EACpE,IAAI,CAAC,WAAW;GACd,YAAY;GACZ,QAAQ,IAAI,iBAAiB,MAAM,sCAAsC;EAC3E;EACA,MAAM,MAAM,gBAAgB;CAC9B;CACA,MAAM,IAAI,MAAM,GAAG,MAAM,sCAAsC,KAAK;AACtE;;;;;;;;;;;;;;AAyCA,MAAa,uBACX,YACyC;CACzC,MAAM,EACJ,OACA,WACA,MACA,YAAY,oBACZ,oBACA,cACE;CACJ,MAAM,mBAAmB,sBAAsB;CAE/C,MAAM,WAAWA,gBAAS,YAAYC,gBAAS,OAAO,mBAAmB,CAAC,CAAC,CAAC,KAC1ED,gBAAS,aAAa,EAAE,aACtBE,cAAO,QACLD,gBAAS,SAAS,MAAM,IAAI,qBACxBA,gBAAS,OAAO,kBAAkB,IAClC,MACN,CACF,GACAD,gBAAS,KAAK,EAAE,UAAUC,gBAAS,OAAO,SAAS,EAAE,CAAC,CACxD;CAEA,IAAI,UAAU;CACd,IAAI;CACJ,MAAM,iBAAiB,UAAyB;EAC9C,WAAW;EACX,sBAAsB,SAAS;EAC/B,MAAM,iBAAiBE,qCAAsB,KAAK;EAClD,IAAI,WAAW;GACb,UAAU,OAAO;IAAE;IAAS;GAAe,CAAC;GAC5C;EACF;EACA,MAAM,YAAY,GAAG,eAAe,KAAK,IAAI,eAAe;EAC5D,IAAI,cAAc,eAAe;EACjC,gBAAgB;EAIhB,MAAM,QAAQC,iCAAkB,KAAK;EACrC,QAAQ,MACN,aAAa,MAAM,MAAM,eAAe,KAAK,oBAAoB,QAAQ,KAAK,eAAe,aAC1F,eAAe,aAAa,eAAe,MAAM,MAAM,eAAe,eAAe,OACrF,QAAQ,eAAe,MAAM,IAAI,UAAU,GAChD;CACF;CAEA,MAAM,gBAAgBF,cAAO,WAAW;EACtC,KAAK;EACL,QAAQ,UAAUG,oCAAqB,OAAO,OAAO,QAAW,mBAAmB;CACrF,CAAC,CAAC,CAAC,KACDH,cAAO,UAAU,UAAUA,cAAO,WAAW,cAAc,KAAK,CAAC,CAAC,GAClEA,cAAO,MAAM;EAAE;EAAU,QAAQ,UAAUC,qCAAsB,KAAK,CAAC,CAAC;CAAU,CAAC,CACrF;CAIA,IAAI,qBAAqB,GACvB,OAAO;CAGT,OAAOD,cAAO,WAAW;EACvB,WAAW,wBAAwB,OAAO,WAAW,gBAAgB;EACrE,QAAQ,UAAUG,oCAAqB,OAAO,OAAO,QAAW,wBAAwB;CAC1F,CAAC,CAAC,CAAC,KAAKH,cAAO,cAAc,aAAa,CAAC;AAC7C"}
1
+ {"version":3,"file":"remote-entry.cjs","names":["Schedule","Duration","Effect","classifyPluginFailure","deepestErrorStack","toPluginRuntimeError"],"sources":["../src/remote-entry.ts"],"sourcesContent":["/**\n * Remote-entry loading resilience — the ONE contract every MF consumer in\n * the dev stack shares (plugin dev serves, the host's plugin loader).\n *\n * Two failure classes it exists for:\n * 1. Cold compile: remoteEntry.js 404s until the remote's first build —\n * the 404 body gets eval'd as JS (\"Unexpected identifier 'Found'\").\n * 2. Watch-rebuild poisoning: a failed entry load is memoized forever in\n * `globalThis.__GLOBAL_LOADING_REMOTE_ENTRY__` (runtime-core never evicts\n * rejections), so retries without a purge await the same failure.\n */\nimport { Duration, Effect, Schedule } from \"effect\";\nimport type { PluginRuntimeError } from \"./runtime/errors\";\nimport {\n classifyPluginFailure,\n deepestErrorStack,\n type PluginFailureClassification,\n toPluginRuntimeError,\n} from \"./runtime/errors\";\n\nconst POLL_INTERVAL_MS = 300;\nconst DEFAULT_TIMEOUT_MS = 120_000;\nconst RETRY_BASE_DELAY_MS = 500;\nconst RETRY_MAX_DELAY_MS = 5_000;\n\nconst sleep = (ms: number): Promise<void> => new Promise((resolve) => setTimeout(resolve, ms));\n\n/**\n * Deletes the failed remote's keys from the process-global entry-load cache.\n * Deleting a RESOLVED entry would force a wasteful re-eval, so only call\n * this from failure paths.\n */\nexport const purgeRemoteEntryCache = (remoteUrl: string): void => {\n const globalLoading = (globalThis as Record<string, unknown>).__GLOBAL_LOADING_REMOTE_ENTRY__ as\n | Record<string, unknown>\n | undefined;\n if (!globalLoading) return;\n for (const key of Object.keys(globalLoading)) {\n if (key.endsWith(`:${remoteUrl}`)) delete globalLoading[key];\n }\n};\n\nexport const isRemoteEntryBody = (body: string): boolean =>\n !/not found|<(!doctype|html)/i.test(body.slice(0, 64));\n\nexport const waitForRemoteEntryReady = async (\n label: string,\n url: string,\n timeoutMs = DEFAULT_TIMEOUT_MS,\n): Promise<void> => {\n const deadline = Date.now() + timeoutMs;\n let announced = false;\n while (Date.now() < deadline) {\n const res = await fetch(url).catch(() => null);\n if (res?.ok && isRemoteEntryBody(await res.text().catch(() => \"\"))) return;\n if (!announced) {\n announced = true;\n console.log(`⏳ waiting for ${label} to compile (remote entry not ready)…`);\n }\n await sleep(POLL_INTERVAL_MS);\n }\n throw new Error(`${label} remote entry never became ready at ${url}`);\n};\n\nexport interface PluginLoadAttemptInfo {\n attempt: number;\n classification: PluginFailureClassification;\n}\n\nexport interface PluginLoadRetryOptions<T> {\n /** short plugin key for log prefixes (host entry key or plugin id) */\n label: string;\n remoteUrl: string;\n load: () => Promise<T>;\n /** wall-clock budget for the retry loop */\n timeoutMs?: number;\n /**\n * Budget for the readiness poll (cold-compile wait) — defaults to the\n * retry budget, preserving the historical behavior. Pass `0` to skip the\n * poll (useful when the caller stubs `load` and does not need network).\n */\n readinessTimeoutMs?: number;\n /**\n * Observes every failed attempt (including non-retryable ones that end the\n * loop). Defaults to classification-deduped console.error under the\n * `[Plugins]` prefix.\n */\n onAttempt?: ((error: unknown, info: PluginLoadAttemptInfo) => void) | undefined;\n}\n\n/**\n * The one plugin-load retry policy: poll the remote entry until it is actually\n * served, then load with capped exponential backoff — purging the poisoned\n * global entry cache between attempts so each retry genuinely re-fetches.\n *\n * Recovery policy is `classifyPluginFailure().retryable`: transient network\n * conditions retry until the wall-clock budget runs out; permanent failures\n * (MF identity skew, schema validation, dead remotes, TLS verification) fail\n * fast on the first attempt instead of burning the budget silently.\n *\n * Fails with the ORIGINAL error from `load` — the host builds\n * `PluginBootstrapError.cause` from it and classification walks it.\n */\nexport const loadRemoteWithRetry = <T>(\n options: PluginLoadRetryOptions<T>,\n): Effect.Effect<T, PluginRuntimeError> => {\n const {\n label,\n remoteUrl,\n load,\n timeoutMs = DEFAULT_TIMEOUT_MS,\n readinessTimeoutMs,\n onAttempt,\n } = options;\n const readinessTimeout = readinessTimeoutMs ?? timeoutMs;\n\n const schedule = Schedule.exponential(Duration.millis(RETRY_BASE_DELAY_MS)).pipe(\n Schedule.modifyDelay(({ output }) =>\n Effect.succeed(\n Duration.toMillis(output) > RETRY_MAX_DELAY_MS\n ? Duration.millis(RETRY_MAX_DELAY_MS)\n : output,\n ),\n ),\n Schedule.upTo({ duration: Duration.millis(timeoutMs) }),\n );\n\n let attempt = 0;\n let lastSignature: string | undefined;\n const reportFailure = (error: unknown): void => {\n attempt += 1;\n purgeRemoteEntryCache(remoteUrl);\n const classification = classifyPluginFailure(error);\n if (onAttempt) {\n onAttempt(error, { attempt, classification });\n return;\n }\n const signature = `${classification.kind}::${classification.message}`;\n if (signature === lastSignature) return;\n lastSignature = signature;\n // The message alone is rarely enough, and the wrapper's own stack points\n // at the wrapping frames — log the DEEPEST cause's stack (the original\n // throw site inside the evaluated remote entry).\n const stack = deepestErrorStack(error);\n console.error(\n `[Plugins][${label}] ❌ ${classification.kind} failure (attempt ${attempt}): ${classification.message}` +\n (classification.suggestion ? `\\n[Plugins][${label}] → ${classification.suggestion}` : \"\") +\n (stack ? `\\n[Plugins][${label}] ${stack}` : \"\"),\n );\n };\n\n const loadWithRetry = Effect.tryPromise({\n try: load,\n catch: (error) => toPluginRuntimeError(error, label, undefined, \"remote-entry-load\"),\n }).pipe(\n Effect.tapError((error) => Effect.sync(() => reportFailure(error))),\n Effect.retry({ schedule, while: (error) => classifyPluginFailure(error).retryable }),\n );\n\n // readinessTimeoutMs: 0 skips the cold-compile poll (tests stub `load` and\n // must not race a 1ms wall-clock window against the event loop).\n if (readinessTimeout === 0) {\n return loadWithRetry;\n }\n\n return Effect.tryPromise({\n try: () => waitForRemoteEntryReady(label, remoteUrl, readinessTimeout),\n catch: (error) => toPluginRuntimeError(error, label, undefined, \"remote-entry-readiness\"),\n }).pipe(Effect.flatMap(() => loadWithRetry));\n};\n"],"mappings":";;;;;;;;;;;;;;;;AAoBA,MAAM,mBAAmB;AACzB,MAAM,qBAAqB;AAC3B,MAAM,sBAAsB;AAC5B,MAAM,qBAAqB;AAE3B,MAAM,SAAS,OAA8B,IAAI,SAAS,YAAY,WAAW,SAAS,EAAE,CAAC;;;;;;AAO7F,MAAa,yBAAyB,cAA4B;CAChE,MAAM,gBAAiB,WAAuC;CAG9D,IAAI,CAAC,eAAe;CACpB,KAAK,MAAM,OAAO,OAAO,KAAK,aAAa,GACzC,IAAI,IAAI,SAAS,IAAI,WAAW,GAAG,OAAO,cAAc;AAE5D;AAEA,MAAa,qBAAqB,SAChC,CAAC,8BAA8B,KAAK,KAAK,MAAM,GAAG,EAAE,CAAC;AAEvD,MAAa,0BAA0B,OACrC,OACA,KACA,YAAY,uBACM;CAClB,MAAM,WAAW,KAAK,IAAI,IAAI;CAC9B,IAAI,YAAY;CAChB,OAAO,KAAK,IAAI,IAAI,UAAU;EAC5B,MAAM,MAAM,MAAM,MAAM,GAAG,CAAC,CAAC,YAAY,IAAI;EAC7C,IAAI,KAAK,MAAM,kBAAkB,MAAM,IAAI,KAAK,CAAC,CAAC,YAAY,EAAE,CAAC,GAAG;EACpE,IAAI,CAAC,WAAW;GACd,YAAY;GACZ,QAAQ,IAAI,iBAAiB,MAAM,sCAAsC;EAC3E;EACA,MAAM,MAAM,gBAAgB;CAC9B;CACA,MAAM,IAAI,MAAM,GAAG,MAAM,sCAAsC,KAAK;AACtE;;;;;;;;;;;;;;AAyCA,MAAa,uBACX,YACyC;CACzC,MAAM,EACJ,OACA,WACA,MACA,YAAY,oBACZ,oBACA,cACE;CACJ,MAAM,mBAAmB,sBAAsB;CAE/C,MAAM,WAAWA,gBAAS,YAAYC,gBAAS,OAAO,mBAAmB,CAAC,CAAC,CAAC,KAC1ED,gBAAS,aAAa,EAAE,aACtBE,cAAO,QACLD,gBAAS,SAAS,MAAM,IAAI,qBACxBA,gBAAS,OAAO,kBAAkB,IAClC,MACN,CACF,GACAD,gBAAS,KAAK,EAAE,UAAUC,gBAAS,OAAO,SAAS,EAAE,CAAC,CACxD;CAEA,IAAI,UAAU;CACd,IAAI;CACJ,MAAM,iBAAiB,UAAyB;EAC9C,WAAW;EACX,sBAAsB,SAAS;EAC/B,MAAM,iBAAiBE,qCAAsB,KAAK;EAClD,IAAI,WAAW;GACb,UAAU,OAAO;IAAE;IAAS;GAAe,CAAC;GAC5C;EACF;EACA,MAAM,YAAY,GAAG,eAAe,KAAK,IAAI,eAAe;EAC5D,IAAI,cAAc,eAAe;EACjC,gBAAgB;EAIhB,MAAM,QAAQC,iCAAkB,KAAK;EACrC,QAAQ,MACN,aAAa,MAAM,MAAM,eAAe,KAAK,oBAAoB,QAAQ,KAAK,eAAe,aAC1F,eAAe,aAAa,eAAe,MAAM,MAAM,eAAe,eAAe,OACrF,QAAQ,eAAe,MAAM,IAAI,UAAU,GAChD;CACF;CAEA,MAAM,gBAAgBF,cAAO,WAAW;EACtC,KAAK;EACL,QAAQ,UAAUG,oCAAqB,OAAO,OAAO,QAAW,mBAAmB;CACrF,CAAC,CAAC,CAAC,KACDH,cAAO,UAAU,UAAUA,cAAO,WAAW,cAAc,KAAK,CAAC,CAAC,GAClEA,cAAO,MAAM;EAAE;EAAU,QAAQ,UAAUC,qCAAsB,KAAK,CAAC,CAAC;CAAU,CAAC,CACrF;CAIA,IAAI,qBAAqB,GACvB,OAAO;CAGT,OAAOD,cAAO,WAAW;EACvB,WAAW,wBAAwB,OAAO,WAAW,gBAAgB;EACrE,QAAQ,UAAUG,oCAAqB,OAAO,OAAO,QAAW,wBAAwB;CAC1F,CAAC,CAAC,CAAC,KAAKH,cAAO,cAAc,aAAa,CAAC;AAC7C"}
@@ -1,7 +1,6 @@
1
1
  import { PluginFailureClassification, PluginRuntimeError } from "./runtime/errors.cjs";
2
2
  import { Effect } from "effect";
3
3
  //#region src/remote-entry.d.ts
4
- declare const sleep: (ms: number) => Promise<void>;
5
4
  /**
6
5
  * Deletes the failed remote's keys from the process-global entry-load cache.
7
6
  * Deleting a RESOLVED entry would force a wasteful re-eval, so only call
@@ -49,5 +48,5 @@ interface PluginLoadRetryOptions<T> {
49
48
  */
50
49
  declare const loadRemoteWithRetry: <T>(options: PluginLoadRetryOptions<T>) => Effect.Effect<T, PluginRuntimeError>;
51
50
  //#endregion
52
- export { PluginLoadAttemptInfo, PluginLoadRetryOptions, isRemoteEntryBody, loadRemoteWithRetry, purgeRemoteEntryCache, sleep, waitForRemoteEntryReady };
51
+ export { PluginLoadAttemptInfo, PluginLoadRetryOptions, isRemoteEntryBody, loadRemoteWithRetry, purgeRemoteEntryCache, waitForRemoteEntryReady };
53
52
  //# sourceMappingURL=remote-entry.d.cts.map
@@ -1 +1 @@
1
- {"version":3,"file":"remote-entry.d.cts","names":[],"sources":["../src/remote-entry.ts"],"mappings":";;;cAyBa,QAAK,eAAiB;;;;;;cAQtB,wBAAqB;cAUrB,oBAAiB;cAGjB,0BAAuB,eACrB,aACF,uBAEV;UAec;EACf;EACA,gBAAgB;;UAGD,uBAAuB;;EAEtC;EACA;EACA,YAAY,QAAQ;;EAEpB;;;;;;EAMA;;;;;;EAMA,cAAc,gBAAgB,MAAM;;;;;;;;;;;;;;;cAgBzB,sBAAuB,GAAC,SAC1B,uBAAuB,OAC/B,OAAO,OAAO,GAAG"}
1
+ {"version":3,"file":"remote-entry.d.cts","names":[],"sources":["../src/remote-entry.ts"],"mappings":";;;;;;;;cAgCa,wBAAqB;cAUrB,oBAAiB;cAGjB,0BAAuB,eACrB,aACF,uBAEV;UAec;EACf;EACA,gBAAgB;;UAGD,uBAAuB;;EAEtC;EACA;EACA,YAAY,QAAQ;;EAEpB;;;;;;EAMA;;;;;;EAMA,cAAc,gBAAgB,MAAM;;;;;;;;;;;;;;;cAgBzB,sBAAuB,GAAC,SAC1B,uBAAuB,OAC/B,OAAO,OAAO,GAAG"}
@@ -1,7 +1,6 @@
1
1
  import { PluginFailureClassification, PluginRuntimeError } from "./runtime/errors.mjs";
2
2
  import { Effect } from "effect";
3
3
  //#region src/remote-entry.d.ts
4
- declare const sleep: (ms: number) => Promise<void>;
5
4
  /**
6
5
  * Deletes the failed remote's keys from the process-global entry-load cache.
7
6
  * Deleting a RESOLVED entry would force a wasteful re-eval, so only call
@@ -49,5 +48,5 @@ interface PluginLoadRetryOptions<T> {
49
48
  */
50
49
  declare const loadRemoteWithRetry: <T>(options: PluginLoadRetryOptions<T>) => Effect.Effect<T, PluginRuntimeError>;
51
50
  //#endregion
52
- export { PluginLoadAttemptInfo, PluginLoadRetryOptions, isRemoteEntryBody, loadRemoteWithRetry, purgeRemoteEntryCache, sleep, waitForRemoteEntryReady };
51
+ export { PluginLoadAttemptInfo, PluginLoadRetryOptions, isRemoteEntryBody, loadRemoteWithRetry, purgeRemoteEntryCache, waitForRemoteEntryReady };
53
52
  //# sourceMappingURL=remote-entry.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"remote-entry.d.mts","names":[],"sources":["../src/remote-entry.ts"],"mappings":";;;cAyBa,QAAK,eAAiB;;;;;;cAQtB,wBAAqB;cAUrB,oBAAiB;cAGjB,0BAAuB,eACrB,aACF,uBAEV;UAec;EACf;EACA,gBAAgB;;UAGD,uBAAuB;;EAEtC;EACA;EACA,YAAY,QAAQ;;EAEpB;;;;;;EAMA;;;;;;EAMA,cAAc,gBAAgB,MAAM;;;;;;;;;;;;;;;cAgBzB,sBAAuB,GAAC,SAC1B,uBAAuB,OAC/B,OAAO,OAAO,GAAG"}
1
+ {"version":3,"file":"remote-entry.d.mts","names":[],"sources":["../src/remote-entry.ts"],"mappings":";;;;;;;;cAgCa,wBAAqB;cAUrB,oBAAiB;cAGjB,0BAAuB,eACrB,aACF,uBAEV;UAec;EACf;EACA,gBAAgB;;UAGD,uBAAuB;;EAEtC;EACA;EACA,YAAY,QAAQ;;EAEpB;;;;;;EAMA;;;;;;EAMA,cAAc,gBAAgB,MAAM;;;;;;;;;;;;;;;cAgBzB,sBAAuB,GAAC,SAC1B,uBAAuB,OAC/B,OAAO,OAAO,GAAG"}
@@ -94,5 +94,5 @@ const loadRemoteWithRetry = (options) => {
94
94
  };
95
95
 
96
96
  //#endregion
97
- export { isRemoteEntryBody, loadRemoteWithRetry, purgeRemoteEntryCache, sleep, waitForRemoteEntryReady };
97
+ export { isRemoteEntryBody, loadRemoteWithRetry, purgeRemoteEntryCache, waitForRemoteEntryReady };
98
98
  //# sourceMappingURL=remote-entry.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"remote-entry.mjs","names":[],"sources":["../src/remote-entry.ts"],"sourcesContent":["/**\n * Remote-entry loading resilience — the ONE contract every MF consumer in\n * the dev stack shares (plugin dev serves, the host's plugin loader).\n *\n * Two failure classes it exists for:\n * 1. Cold compile: remoteEntry.js 404s until the remote's first build —\n * the 404 body gets eval'd as JS (\"Unexpected identifier 'Found'\").\n * 2. Watch-rebuild poisoning: a failed entry load is memoized forever in\n * `globalThis.__GLOBAL_LOADING_REMOTE_ENTRY__` (runtime-core never evicts\n * rejections), so retries without a purge await the same failure.\n */\nimport { Duration, Effect, Schedule } from \"effect\";\nimport type { PluginRuntimeError } from \"./runtime/errors\";\nimport {\n classifyPluginFailure,\n deepestErrorStack,\n type PluginFailureClassification,\n toPluginRuntimeError,\n} from \"./runtime/errors\";\n\nconst POLL_INTERVAL_MS = 300;\nconst DEFAULT_TIMEOUT_MS = 120_000;\nconst RETRY_BASE_DELAY_MS = 500;\nconst RETRY_MAX_DELAY_MS = 5_000;\n\nexport const sleep = (ms: number): Promise<void> =>\n new Promise((resolve) => setTimeout(resolve, ms));\n\n/**\n * Deletes the failed remote's keys from the process-global entry-load cache.\n * Deleting a RESOLVED entry would force a wasteful re-eval, so only call\n * this from failure paths.\n */\nexport const purgeRemoteEntryCache = (remoteUrl: string): void => {\n const globalLoading = (globalThis as Record<string, unknown>).__GLOBAL_LOADING_REMOTE_ENTRY__ as\n | Record<string, unknown>\n | undefined;\n if (!globalLoading) return;\n for (const key of Object.keys(globalLoading)) {\n if (key.endsWith(`:${remoteUrl}`)) delete globalLoading[key];\n }\n};\n\nexport const isRemoteEntryBody = (body: string): boolean =>\n !/not found|<(!doctype|html)/i.test(body.slice(0, 64));\n\nexport const waitForRemoteEntryReady = async (\n label: string,\n url: string,\n timeoutMs = DEFAULT_TIMEOUT_MS,\n): Promise<void> => {\n const deadline = Date.now() + timeoutMs;\n let announced = false;\n while (Date.now() < deadline) {\n const res = await fetch(url).catch(() => null);\n if (res?.ok && isRemoteEntryBody(await res.text().catch(() => \"\"))) return;\n if (!announced) {\n announced = true;\n console.log(`⏳ waiting for ${label} to compile (remote entry not ready)…`);\n }\n await sleep(POLL_INTERVAL_MS);\n }\n throw new Error(`${label} remote entry never became ready at ${url}`);\n};\n\nexport interface PluginLoadAttemptInfo {\n attempt: number;\n classification: PluginFailureClassification;\n}\n\nexport interface PluginLoadRetryOptions<T> {\n /** short plugin key for log prefixes (host entry key or plugin id) */\n label: string;\n remoteUrl: string;\n load: () => Promise<T>;\n /** wall-clock budget for the retry loop */\n timeoutMs?: number;\n /**\n * Budget for the readiness poll (cold-compile wait) — defaults to the\n * retry budget, preserving the historical behavior. Pass `0` to skip the\n * poll (useful when the caller stubs `load` and does not need network).\n */\n readinessTimeoutMs?: number;\n /**\n * Observes every failed attempt (including non-retryable ones that end the\n * loop). Defaults to classification-deduped console.error under the\n * `[Plugins]` prefix.\n */\n onAttempt?: ((error: unknown, info: PluginLoadAttemptInfo) => void) | undefined;\n}\n\n/**\n * The one plugin-load retry policy: poll the remote entry until it is actually\n * served, then load with capped exponential backoff — purging the poisoned\n * global entry cache between attempts so each retry genuinely re-fetches.\n *\n * Recovery policy is `classifyPluginFailure().retryable`: transient network\n * conditions retry until the wall-clock budget runs out; permanent failures\n * (MF identity skew, schema validation, dead remotes, TLS verification) fail\n * fast on the first attempt instead of burning the budget silently.\n *\n * Fails with the ORIGINAL error from `load` — the host builds\n * `PluginBootstrapError.cause` from it and classification walks it.\n */\nexport const loadRemoteWithRetry = <T>(\n options: PluginLoadRetryOptions<T>,\n): Effect.Effect<T, PluginRuntimeError> => {\n const {\n label,\n remoteUrl,\n load,\n timeoutMs = DEFAULT_TIMEOUT_MS,\n readinessTimeoutMs,\n onAttempt,\n } = options;\n const readinessTimeout = readinessTimeoutMs ?? timeoutMs;\n\n const schedule = Schedule.exponential(Duration.millis(RETRY_BASE_DELAY_MS)).pipe(\n Schedule.modifyDelay(({ output }) =>\n Effect.succeed(\n Duration.toMillis(output) > RETRY_MAX_DELAY_MS\n ? Duration.millis(RETRY_MAX_DELAY_MS)\n : output,\n ),\n ),\n Schedule.upTo({ duration: Duration.millis(timeoutMs) }),\n );\n\n let attempt = 0;\n let lastSignature: string | undefined;\n const reportFailure = (error: unknown): void => {\n attempt += 1;\n purgeRemoteEntryCache(remoteUrl);\n const classification = classifyPluginFailure(error);\n if (onAttempt) {\n onAttempt(error, { attempt, classification });\n return;\n }\n const signature = `${classification.kind}::${classification.message}`;\n if (signature === lastSignature) return;\n lastSignature = signature;\n // The message alone is rarely enough, and the wrapper's own stack points\n // at the wrapping frames — log the DEEPEST cause's stack (the original\n // throw site inside the evaluated remote entry).\n const stack = deepestErrorStack(error);\n console.error(\n `[Plugins][${label}] ❌ ${classification.kind} failure (attempt ${attempt}): ${classification.message}` +\n (classification.suggestion ? `\\n[Plugins][${label}] → ${classification.suggestion}` : \"\") +\n (stack ? `\\n[Plugins][${label}] ${stack}` : \"\"),\n );\n };\n\n const loadWithRetry = Effect.tryPromise({\n try: load,\n catch: (error) => toPluginRuntimeError(error, label, undefined, \"remote-entry-load\"),\n }).pipe(\n Effect.tapError((error) => Effect.sync(() => reportFailure(error))),\n Effect.retry({ schedule, while: (error) => classifyPluginFailure(error).retryable }),\n );\n\n // readinessTimeoutMs: 0 skips the cold-compile poll (tests stub `load` and\n // must not race a 1ms wall-clock window against the event loop).\n if (readinessTimeout === 0) {\n return loadWithRetry;\n }\n\n return Effect.tryPromise({\n try: () => waitForRemoteEntryReady(label, remoteUrl, readinessTimeout),\n catch: (error) => toPluginRuntimeError(error, label, undefined, \"remote-entry-readiness\"),\n }).pipe(Effect.flatMap(() => loadWithRetry));\n};\n"],"mappings":";;;;;;;;;;;;;;;AAoBA,MAAM,mBAAmB;AACzB,MAAM,qBAAqB;AAC3B,MAAM,sBAAsB;AAC5B,MAAM,qBAAqB;AAE3B,MAAa,SAAS,OACpB,IAAI,SAAS,YAAY,WAAW,SAAS,EAAE,CAAC;;;;;;AAOlD,MAAa,yBAAyB,cAA4B;CAChE,MAAM,gBAAiB,WAAuC;CAG9D,IAAI,CAAC,eAAe;CACpB,KAAK,MAAM,OAAO,OAAO,KAAK,aAAa,GACzC,IAAI,IAAI,SAAS,IAAI,WAAW,GAAG,OAAO,cAAc;AAE5D;AAEA,MAAa,qBAAqB,SAChC,CAAC,8BAA8B,KAAK,KAAK,MAAM,GAAG,EAAE,CAAC;AAEvD,MAAa,0BAA0B,OACrC,OACA,KACA,YAAY,uBACM;CAClB,MAAM,WAAW,KAAK,IAAI,IAAI;CAC9B,IAAI,YAAY;CAChB,OAAO,KAAK,IAAI,IAAI,UAAU;EAC5B,MAAM,MAAM,MAAM,MAAM,GAAG,CAAC,CAAC,YAAY,IAAI;EAC7C,IAAI,KAAK,MAAM,kBAAkB,MAAM,IAAI,KAAK,CAAC,CAAC,YAAY,EAAE,CAAC,GAAG;EACpE,IAAI,CAAC,WAAW;GACd,YAAY;GACZ,QAAQ,IAAI,iBAAiB,MAAM,sCAAsC;EAC3E;EACA,MAAM,MAAM,gBAAgB;CAC9B;CACA,MAAM,IAAI,MAAM,GAAG,MAAM,sCAAsC,KAAK;AACtE;;;;;;;;;;;;;;AAyCA,MAAa,uBACX,YACyC;CACzC,MAAM,EACJ,OACA,WACA,MACA,YAAY,oBACZ,oBACA,cACE;CACJ,MAAM,mBAAmB,sBAAsB;CAE/C,MAAM,WAAW,SAAS,YAAY,SAAS,OAAO,mBAAmB,CAAC,CAAC,CAAC,KAC1E,SAAS,aAAa,EAAE,aACtB,OAAO,QACL,SAAS,SAAS,MAAM,IAAI,qBACxB,SAAS,OAAO,kBAAkB,IAClC,MACN,CACF,GACA,SAAS,KAAK,EAAE,UAAU,SAAS,OAAO,SAAS,EAAE,CAAC,CACxD;CAEA,IAAI,UAAU;CACd,IAAI;CACJ,MAAM,iBAAiB,UAAyB;EAC9C,WAAW;EACX,sBAAsB,SAAS;EAC/B,MAAM,iBAAiB,sBAAsB,KAAK;EAClD,IAAI,WAAW;GACb,UAAU,OAAO;IAAE;IAAS;GAAe,CAAC;GAC5C;EACF;EACA,MAAM,YAAY,GAAG,eAAe,KAAK,IAAI,eAAe;EAC5D,IAAI,cAAc,eAAe;EACjC,gBAAgB;EAIhB,MAAM,QAAQ,kBAAkB,KAAK;EACrC,QAAQ,MACN,aAAa,MAAM,MAAM,eAAe,KAAK,oBAAoB,QAAQ,KAAK,eAAe,aAC1F,eAAe,aAAa,eAAe,MAAM,MAAM,eAAe,eAAe,OACrF,QAAQ,eAAe,MAAM,IAAI,UAAU,GAChD;CACF;CAEA,MAAM,gBAAgB,OAAO,WAAW;EACtC,KAAK;EACL,QAAQ,UAAU,qBAAqB,OAAO,OAAO,QAAW,mBAAmB;CACrF,CAAC,CAAC,CAAC,KACD,OAAO,UAAU,UAAU,OAAO,WAAW,cAAc,KAAK,CAAC,CAAC,GAClE,OAAO,MAAM;EAAE;EAAU,QAAQ,UAAU,sBAAsB,KAAK,CAAC,CAAC;CAAU,CAAC,CACrF;CAIA,IAAI,qBAAqB,GACvB,OAAO;CAGT,OAAO,OAAO,WAAW;EACvB,WAAW,wBAAwB,OAAO,WAAW,gBAAgB;EACrE,QAAQ,UAAU,qBAAqB,OAAO,OAAO,QAAW,wBAAwB;CAC1F,CAAC,CAAC,CAAC,KAAK,OAAO,cAAc,aAAa,CAAC;AAC7C"}
1
+ {"version":3,"file":"remote-entry.mjs","names":[],"sources":["../src/remote-entry.ts"],"sourcesContent":["/**\n * Remote-entry loading resilience — the ONE contract every MF consumer in\n * the dev stack shares (plugin dev serves, the host's plugin loader).\n *\n * Two failure classes it exists for:\n * 1. Cold compile: remoteEntry.js 404s until the remote's first build —\n * the 404 body gets eval'd as JS (\"Unexpected identifier 'Found'\").\n * 2. Watch-rebuild poisoning: a failed entry load is memoized forever in\n * `globalThis.__GLOBAL_LOADING_REMOTE_ENTRY__` (runtime-core never evicts\n * rejections), so retries without a purge await the same failure.\n */\nimport { Duration, Effect, Schedule } from \"effect\";\nimport type { PluginRuntimeError } from \"./runtime/errors\";\nimport {\n classifyPluginFailure,\n deepestErrorStack,\n type PluginFailureClassification,\n toPluginRuntimeError,\n} from \"./runtime/errors\";\n\nconst POLL_INTERVAL_MS = 300;\nconst DEFAULT_TIMEOUT_MS = 120_000;\nconst RETRY_BASE_DELAY_MS = 500;\nconst RETRY_MAX_DELAY_MS = 5_000;\n\nconst sleep = (ms: number): Promise<void> => new Promise((resolve) => setTimeout(resolve, ms));\n\n/**\n * Deletes the failed remote's keys from the process-global entry-load cache.\n * Deleting a RESOLVED entry would force a wasteful re-eval, so only call\n * this from failure paths.\n */\nexport const purgeRemoteEntryCache = (remoteUrl: string): void => {\n const globalLoading = (globalThis as Record<string, unknown>).__GLOBAL_LOADING_REMOTE_ENTRY__ as\n | Record<string, unknown>\n | undefined;\n if (!globalLoading) return;\n for (const key of Object.keys(globalLoading)) {\n if (key.endsWith(`:${remoteUrl}`)) delete globalLoading[key];\n }\n};\n\nexport const isRemoteEntryBody = (body: string): boolean =>\n !/not found|<(!doctype|html)/i.test(body.slice(0, 64));\n\nexport const waitForRemoteEntryReady = async (\n label: string,\n url: string,\n timeoutMs = DEFAULT_TIMEOUT_MS,\n): Promise<void> => {\n const deadline = Date.now() + timeoutMs;\n let announced = false;\n while (Date.now() < deadline) {\n const res = await fetch(url).catch(() => null);\n if (res?.ok && isRemoteEntryBody(await res.text().catch(() => \"\"))) return;\n if (!announced) {\n announced = true;\n console.log(`⏳ waiting for ${label} to compile (remote entry not ready)…`);\n }\n await sleep(POLL_INTERVAL_MS);\n }\n throw new Error(`${label} remote entry never became ready at ${url}`);\n};\n\nexport interface PluginLoadAttemptInfo {\n attempt: number;\n classification: PluginFailureClassification;\n}\n\nexport interface PluginLoadRetryOptions<T> {\n /** short plugin key for log prefixes (host entry key or plugin id) */\n label: string;\n remoteUrl: string;\n load: () => Promise<T>;\n /** wall-clock budget for the retry loop */\n timeoutMs?: number;\n /**\n * Budget for the readiness poll (cold-compile wait) — defaults to the\n * retry budget, preserving the historical behavior. Pass `0` to skip the\n * poll (useful when the caller stubs `load` and does not need network).\n */\n readinessTimeoutMs?: number;\n /**\n * Observes every failed attempt (including non-retryable ones that end the\n * loop). Defaults to classification-deduped console.error under the\n * `[Plugins]` prefix.\n */\n onAttempt?: ((error: unknown, info: PluginLoadAttemptInfo) => void) | undefined;\n}\n\n/**\n * The one plugin-load retry policy: poll the remote entry until it is actually\n * served, then load with capped exponential backoff — purging the poisoned\n * global entry cache between attempts so each retry genuinely re-fetches.\n *\n * Recovery policy is `classifyPluginFailure().retryable`: transient network\n * conditions retry until the wall-clock budget runs out; permanent failures\n * (MF identity skew, schema validation, dead remotes, TLS verification) fail\n * fast on the first attempt instead of burning the budget silently.\n *\n * Fails with the ORIGINAL error from `load` — the host builds\n * `PluginBootstrapError.cause` from it and classification walks it.\n */\nexport const loadRemoteWithRetry = <T>(\n options: PluginLoadRetryOptions<T>,\n): Effect.Effect<T, PluginRuntimeError> => {\n const {\n label,\n remoteUrl,\n load,\n timeoutMs = DEFAULT_TIMEOUT_MS,\n readinessTimeoutMs,\n onAttempt,\n } = options;\n const readinessTimeout = readinessTimeoutMs ?? timeoutMs;\n\n const schedule = Schedule.exponential(Duration.millis(RETRY_BASE_DELAY_MS)).pipe(\n Schedule.modifyDelay(({ output }) =>\n Effect.succeed(\n Duration.toMillis(output) > RETRY_MAX_DELAY_MS\n ? Duration.millis(RETRY_MAX_DELAY_MS)\n : output,\n ),\n ),\n Schedule.upTo({ duration: Duration.millis(timeoutMs) }),\n );\n\n let attempt = 0;\n let lastSignature: string | undefined;\n const reportFailure = (error: unknown): void => {\n attempt += 1;\n purgeRemoteEntryCache(remoteUrl);\n const classification = classifyPluginFailure(error);\n if (onAttempt) {\n onAttempt(error, { attempt, classification });\n return;\n }\n const signature = `${classification.kind}::${classification.message}`;\n if (signature === lastSignature) return;\n lastSignature = signature;\n // The message alone is rarely enough, and the wrapper's own stack points\n // at the wrapping frames — log the DEEPEST cause's stack (the original\n // throw site inside the evaluated remote entry).\n const stack = deepestErrorStack(error);\n console.error(\n `[Plugins][${label}] ❌ ${classification.kind} failure (attempt ${attempt}): ${classification.message}` +\n (classification.suggestion ? `\\n[Plugins][${label}] → ${classification.suggestion}` : \"\") +\n (stack ? `\\n[Plugins][${label}] ${stack}` : \"\"),\n );\n };\n\n const loadWithRetry = Effect.tryPromise({\n try: load,\n catch: (error) => toPluginRuntimeError(error, label, undefined, \"remote-entry-load\"),\n }).pipe(\n Effect.tapError((error) => Effect.sync(() => reportFailure(error))),\n Effect.retry({ schedule, while: (error) => classifyPluginFailure(error).retryable }),\n );\n\n // readinessTimeoutMs: 0 skips the cold-compile poll (tests stub `load` and\n // must not race a 1ms wall-clock window against the event loop).\n if (readinessTimeout === 0) {\n return loadWithRetry;\n }\n\n return Effect.tryPromise({\n try: () => waitForRemoteEntryReady(label, remoteUrl, readinessTimeout),\n catch: (error) => toPluginRuntimeError(error, label, undefined, \"remote-entry-readiness\"),\n }).pipe(Effect.flatMap(() => loadWithRetry));\n};\n"],"mappings":";;;;;;;;;;;;;;;AAoBA,MAAM,mBAAmB;AACzB,MAAM,qBAAqB;AAC3B,MAAM,sBAAsB;AAC5B,MAAM,qBAAqB;AAE3B,MAAM,SAAS,OAA8B,IAAI,SAAS,YAAY,WAAW,SAAS,EAAE,CAAC;;;;;;AAO7F,MAAa,yBAAyB,cAA4B;CAChE,MAAM,gBAAiB,WAAuC;CAG9D,IAAI,CAAC,eAAe;CACpB,KAAK,MAAM,OAAO,OAAO,KAAK,aAAa,GACzC,IAAI,IAAI,SAAS,IAAI,WAAW,GAAG,OAAO,cAAc;AAE5D;AAEA,MAAa,qBAAqB,SAChC,CAAC,8BAA8B,KAAK,KAAK,MAAM,GAAG,EAAE,CAAC;AAEvD,MAAa,0BAA0B,OACrC,OACA,KACA,YAAY,uBACM;CAClB,MAAM,WAAW,KAAK,IAAI,IAAI;CAC9B,IAAI,YAAY;CAChB,OAAO,KAAK,IAAI,IAAI,UAAU;EAC5B,MAAM,MAAM,MAAM,MAAM,GAAG,CAAC,CAAC,YAAY,IAAI;EAC7C,IAAI,KAAK,MAAM,kBAAkB,MAAM,IAAI,KAAK,CAAC,CAAC,YAAY,EAAE,CAAC,GAAG;EACpE,IAAI,CAAC,WAAW;GACd,YAAY;GACZ,QAAQ,IAAI,iBAAiB,MAAM,sCAAsC;EAC3E;EACA,MAAM,MAAM,gBAAgB;CAC9B;CACA,MAAM,IAAI,MAAM,GAAG,MAAM,sCAAsC,KAAK;AACtE;;;;;;;;;;;;;;AAyCA,MAAa,uBACX,YACyC;CACzC,MAAM,EACJ,OACA,WACA,MACA,YAAY,oBACZ,oBACA,cACE;CACJ,MAAM,mBAAmB,sBAAsB;CAE/C,MAAM,WAAW,SAAS,YAAY,SAAS,OAAO,mBAAmB,CAAC,CAAC,CAAC,KAC1E,SAAS,aAAa,EAAE,aACtB,OAAO,QACL,SAAS,SAAS,MAAM,IAAI,qBACxB,SAAS,OAAO,kBAAkB,IAClC,MACN,CACF,GACA,SAAS,KAAK,EAAE,UAAU,SAAS,OAAO,SAAS,EAAE,CAAC,CACxD;CAEA,IAAI,UAAU;CACd,IAAI;CACJ,MAAM,iBAAiB,UAAyB;EAC9C,WAAW;EACX,sBAAsB,SAAS;EAC/B,MAAM,iBAAiB,sBAAsB,KAAK;EAClD,IAAI,WAAW;GACb,UAAU,OAAO;IAAE;IAAS;GAAe,CAAC;GAC5C;EACF;EACA,MAAM,YAAY,GAAG,eAAe,KAAK,IAAI,eAAe;EAC5D,IAAI,cAAc,eAAe;EACjC,gBAAgB;EAIhB,MAAM,QAAQ,kBAAkB,KAAK;EACrC,QAAQ,MACN,aAAa,MAAM,MAAM,eAAe,KAAK,oBAAoB,QAAQ,KAAK,eAAe,aAC1F,eAAe,aAAa,eAAe,MAAM,MAAM,eAAe,eAAe,OACrF,QAAQ,eAAe,MAAM,IAAI,UAAU,GAChD;CACF;CAEA,MAAM,gBAAgB,OAAO,WAAW;EACtC,KAAK;EACL,QAAQ,UAAU,qBAAqB,OAAO,OAAO,QAAW,mBAAmB;CACrF,CAAC,CAAC,CAAC,KACD,OAAO,UAAU,UAAU,OAAO,WAAW,cAAc,KAAK,CAAC,CAAC,GAClE,OAAO,MAAM;EAAE;EAAU,QAAQ,UAAU,sBAAsB,KAAK,CAAC,CAAC;CAAU,CAAC,CACrF;CAIA,IAAI,qBAAqB,GACvB,OAAO;CAGT,OAAO,OAAO,WAAW;EACvB,WAAW,wBAAwB,OAAO,WAAW,gBAAgB;EACrE,QAAQ,UAAU,qBAAqB,OAAO,OAAO,QAAW,wBAAwB;CAC1F,CAAC,CAAC,CAAC,KAAK,OAAO,cAAc,aAAa,CAAC;AAC7C"}
@@ -3,7 +3,7 @@ const require_shared_deps_spec = require('../shared-deps-spec.cjs');
3
3
  let node_fs = require("node:fs");
4
4
 
5
5
  //#region src/runtime/mf-config.ts
6
- const PLUGIN_VERSION = "3.0.0-rc.3";
6
+ const PLUGIN_VERSION = "3.0.0-rc.5";
7
7
  const MF_CORE_SHARED_DEPS = require_shared_deps_spec.buildMfCoreSharedDependencies({ ownVersion: PLUGIN_VERSION });
8
8
 
9
9
  //#endregion
@@ -2,7 +2,7 @@ import { buildMfCoreSharedDependencies } from "../shared-deps-spec.mjs";
2
2
  import { readFileSync } from "node:fs";
3
3
 
4
4
  //#region src/runtime/mf-config.ts
5
- const PLUGIN_VERSION = "3.0.0-rc.3";
5
+ const PLUGIN_VERSION = "3.0.0-rc.5";
6
6
  const MF_CORE_SHARED_DEPS = buildMfCoreSharedDependencies({ ownVersion: PLUGIN_VERSION });
7
7
 
8
8
  //#endregion
@@ -2,5 +2,5 @@ import "../errors.cjs";
2
2
  import "../../types.cjs";
3
3
  import "./module-federation.service.cjs";
4
4
  import "./secrets.service.cjs";
5
- import { Context, Effect, Layer } from "effect";
6
- import { InferSchemaInput } from "@orpc/contract";
5
+ import { InferSchemaInput } from "@orpc/contract";
6
+ import { Context, Effect, Layer } from "effect";
package/dist/types.d.cts CHANGED
@@ -1,8 +1,8 @@
1
1
  import { PluginWarningInfo } from "./runtime/errors.cjs";
2
2
  import { Plugin } from "./plugin.cjs";
3
+ import { AnyContractRouter, AnySchema, InferSchemaInput, InferSchemaOutput } from "@orpc/contract";
3
4
  import { ContractedRouter, RouterClient } from "@orpc/server";
4
5
  import { Context, Scope } from "effect";
5
- import { AnyContractRouter, AnySchema, InferSchemaInput, InferSchemaOutput } from "@orpc/contract";
6
6
  //#region src/types.d.ts
7
7
  /**
8
8
  * Registry bindings interface - populated via module augmentation
@@ -17,6 +17,18 @@ import { AnyContractRouter, AnySchema, InferSchemaInput, InferSchemaOutput } fro
17
17
  * ```
18
18
  */
19
19
  interface RegisteredPlugins {}
20
+ type ModuleTransport = "inproc" | "rpc" | "mf";
21
+ type ModuleSurface = "api" | "ui" | "jobs";
22
+ interface ModuleEndpoint {
23
+ readonly transport: ModuleTransport;
24
+ readonly surface?: ModuleSurface;
25
+ readonly url: string;
26
+ readonly path?: `/${string}`;
27
+ readonly manifest?: string;
28
+ }
29
+ type PluginTransport = ModuleTransport;
30
+ type PluginSurface = ModuleSurface;
31
+ type PluginEndpoint = ModuleEndpoint;
20
32
  /**
21
33
  * Base type for any plugin instance.
22
34
  */
@@ -39,10 +51,12 @@ type AnyPluginConstructor = {
39
51
  type PluginRegistryEntry = {
40
52
  module: AnyPluginConstructor;
41
53
  remote?: string;
54
+ endpoint?: PluginEndpoint;
42
55
  version?: string;
43
56
  description?: string;
44
57
  } | {
45
58
  remote: string;
59
+ endpoint?: PluginEndpoint;
46
60
  version?: string;
47
61
  description?: string;
48
62
  };
@@ -107,7 +121,14 @@ type PluginClientType<T> = RouterClient<PluginRouterType<T>>;
107
121
  type PluginServicesEntry<T = any> = {
108
122
  client: (context?: PluginContextInput<T>) => PluginClientType<T>;
109
123
  router: PluginRouterType<T>;
124
+ endpoint?: PluginEndpoint;
125
+ };
126
+ type ModuleClientEntry<C extends AnyContractRouter = AnyContractRouter> = {
127
+ client: (context?: Record<string, unknown>) => RouterClient<ContractedRouter<C, any>>;
128
+ router?: ContractedRouter<C, any>;
129
+ endpoint?: PluginEndpoint;
110
130
  };
131
+ type PluginClientEntry<C extends AnyContractRouter = AnyContractRouter> = ModuleClientEntry<C>;
111
132
  /**
112
133
  * Extract plugin type from registered plugins by key
113
134
  * @param K - The plugin key
@@ -247,5 +268,5 @@ interface PluginRuntimeConfig<R extends Record<string, PluginRegistryEntry> = Re
247
268
  secrets?: SecretsConfig;
248
269
  }
249
270
  //#endregion
250
- export { AnyPlugin, AnyPluginConstructor, EveryPlugin, ExtractPluginType, InferRegistryFromEntries, InitializedPlugin, LoadedPlugin, PluginClientType, PluginConfigInput, PluginContext, PluginContextInput, PluginContract, PluginInstance, PluginMetadata, PluginRegistry, PluginRegistryEntry, PluginRouterType, PluginRuntimeConfig, PluginSecrets, PluginServicesEntry, PluginVariables, RegisteredPlugin, RegisteredPlugins, SecretsConfig, UsePluginResult };
271
+ export { AnyPlugin, AnyPluginConstructor, EveryPlugin, ExtractPluginType, InferRegistryFromEntries, InitializedPlugin, LoadedPlugin, ModuleClientEntry, ModuleEndpoint, ModuleSurface, ModuleTransport, PluginClientEntry, PluginClientType, PluginConfigInput, PluginContext, PluginContextInput, PluginContract, PluginEndpoint, PluginInstance, PluginMetadata, PluginRegistry, PluginRegistryEntry, PluginRouterType, PluginRuntimeConfig, PluginSecrets, PluginServicesEntry, PluginSurface, PluginTransport, PluginVariables, RegisteredPlugin, RegisteredPlugins, SecretsConfig, UsePluginResult };
251
272
  //# sourceMappingURL=types.d.cts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.cts","names":[],"sources":["../src/types.ts"],"mappings":";;;;;;;;;;;;;;;;;;UAwBiB;;;;KAKL,YAAY,OAAO,mBAAmB,WAAW,WAAW;;;;KAK5D;UACF;EACR;IACE,UAAU;IACV,WAAW;IACX,SAAS;IACT,SAAS;;;;;;KAOD;EACN,QAAQ;EAAsB;EAAiB;EAAkB;;EACjE;EAAgB;EAAkB;;;;;KAK5B,kBAAkB,KAAK;EAAY,cAAc;IAAM;;;;KAKvD,yBAAyB,UAAU,eAAe,2BAC3D,WAAW,IAAI,EAAE;EAAa,cAAc;IACzC,IACA,gBAAgB,oBACd,kBAAkB;;;;KAOd,eAAe,KAAK;EAC9B;IAAW,gBAAgB,UAAU;;IAEnC;;;;KAMQ,gBAAgB,KAAK;EAAY;IAAW,iBAAiB,UAAU;;IAC/E;;;;KAMQ,cAAc,KAAK;EAAY;IAAW,eAAe,UAAU;;IAC3E;;;;KAMQ,cAAc,KAAK;EAC7B;IAAW,eAAe,UAAU;;IAElC;;;;;KAOQ,iBAAiB,KAAK,iBAAiB,eAAe;;;;KAKtD,iBAAiB,KAAK,aAAa,iBAAiB;;;;;;KAOpD,oBAAoB;EAC9B,SAAS,UAAU,mBAAmB,OAAO,iBAAiB;EAC9D,QAAQ,iBAAiB;;;;;;;KAQf,iBAAiB,gBAAgB,GAAG,IAAI,qBAAqB,EAAE;EACzE,eAAe;IAEb;EACE,gBAAgB,UAAU;EAC1B,iBAAiB,UAAU;EAC3B,eAAe,UAAU;EACzB,eAAe,wBAAwB;IAEvC,OAAO,GAAG,GAAG,GAAG;;;;KAOV,mBAAmB,KAC7B,cAAc,WAAW,YAAY,iBAAiB,cAAc;;;;KAK1D,kBAAkB;EAC5B,WAAW,iBAAiB,gBAAgB;EAC5C,SAAS,iBAAiB,cAAc;;;;;KAM9B;WACD;WACA;WACA;;;;;KAMC,iBAAiB,eAAe;;;;;UAM3B;GACd;;;;;UAMc,aAAa,UAAU,YAAY;WACzC,gBAAgB;WAChB,UAAU;;;;;UAMJ,eAAe,UAAU,YAAY;WAC3C,QAAQ;WACR,UAAU;;;;;UAMJ,kBAAkB,UAAU,YAAY;WAC9C,QAAQ;WACR,UAAU;WACV;IACP,WAAW,kBAAkB;IAC7B,SAAS,kBAAkB;;;;;;;WAOpB,eAAe,QAAQ;WACvB,OAAO,MAAM;;WAEb,UAAU;;;;;KAMhB,oBAAoB,gBAAgB,GAAG,IAAI,qBAAqB,EAAE;EACrE,eAAe;IAEb;EACE,UAAU;EACV,WAAW;EACX,SAAS;EACT,SAAS;wBAGI,4IACF;;;;;;KAOL,gBAAgB,gBAAgB,GAAG,IAAI,qBACjD,oBAAoB,GAAG;WAER,eAAe,UAAU,mBAAmB,EAAE,QAAQ,iBAAiB,EAAE;WACzE,QAAQ,iBAAiB,EAAE;WAC3B,UAAU;WACV,aAAa,kBAAkB,iBAAiB,GAAG;IAE9D,oBAAoB,GAAG;;;;;;;KAQxB,WAAW,KAAK;EAAY,uBAAuB;IAAM,IAAI;;;;kBAKjD;;;;;;;;;;;;;;;;;;OAkBH,MACV,kBACA,SAAS,qBACP,gBAAgB,WAAW,UAAU,gBAAgB,GAAG,WAAW;;;;;UAMxD,oBACf,UAAU,eAAe,uBAAuB,eAAe;EAE/D,UAAU;EACV,UAAU"}
1
+ {"version":3,"file":"types.d.cts","names":[],"sources":["../src/types.ts"],"mappings":";;;;;;;;;;;;;;;;;;UAwBiB;KAEL;KAEA;UAEK;WACN,WAAW;WACX,UAAU;WACV;WACA;WACA;;KAGC,kBAAkB;KAElB,gBAAgB;KAEhB,iBAAiB;;;;KAKjB,YAAY,OAAO,mBAAmB,WAAW,WAAW;;;;KAK5D;UACF;EACR;IACE,UAAU;IACV,WAAW;IACX,SAAS;IACT,SAAS;;;;;;KAOD;EAEN,QAAQ;EACR;EACA,WAAW;EACX;EACA;;EAEA;EAAgB,WAAW;EAAgB;EAAkB;;;;;KAKvD,kBAAkB,KAAK;EAAY,cAAc;IAAM;;;;KAKvD,yBAAyB,UAAU,eAAe,2BAC3D,WAAW,IAAI,EAAE;EAAa,cAAc;IACzC,IACA,gBAAgB,oBACd,kBAAkB;;;;KAOd,eAAe,KAAK;EAC9B;IAAW,gBAAgB,UAAU;;IAEnC;;;;KAMQ,gBAAgB,KAAK;EAAY;IAAW,iBAAiB,UAAU;;IAC/E;;;;KAMQ,cAAc,KAAK;EAAY;IAAW,eAAe,UAAU;;IAC3E;;;;KAMQ,cAAc,KAAK;EAC7B;IAAW,eAAe,UAAU;;IAElC;;;;;KAOQ,iBAAiB,KAAK,iBAAiB,eAAe;;;;KAKtD,iBAAiB,KAAK,aAAa,iBAAiB;;;;;;KAOpD,oBAAoB;EAC9B,SAAS,UAAU,mBAAmB,OAAO,iBAAiB;EAC9D,QAAQ,iBAAiB;EACzB,WAAW;;KAGD,kBAAkB,UAAU,oBAAoB;EAC1D,SAAS,UAAU,4BAA4B,aAAa,iBAAiB;EAC7E,SAAS,iBAAiB;EAC1B,WAAW;;KAGD,kBAAkB,UAAU,oBAAoB,qBAC1D,kBAAkB;;;;;;KAOR,iBAAiB,gBAAgB,GAAG,IAAI,qBAAqB,EAAE;EACzE,eAAe;IAEb;EACE,gBAAgB,UAAU;EAC1B,iBAAiB,UAAU;EAC3B,eAAe,UAAU;EACzB,eAAe,wBAAwB;IAEvC,OAAO,GAAG,GAAG,GAAG;;;;KAOV,mBAAmB,KAC7B,cAAc,WAAW,YAAY,iBAAiB,cAAc;;;;KAK1D,kBAAkB;EAC5B,WAAW,iBAAiB,gBAAgB;EAC5C,SAAS,iBAAiB,cAAc;;;;;KAM9B;WACD;WACA;WACA;;;;;KAMC,iBAAiB,eAAe;;;;;UAM3B;GACd;;;;;UAMc,aAAa,UAAU,YAAY;WACzC,gBAAgB;WAChB,UAAU;;;;;UAMJ,eAAe,UAAU,YAAY;WAC3C,QAAQ;WACR,UAAU;;;;;UAMJ,kBAAkB,UAAU,YAAY;WAC9C,QAAQ;WACR,UAAU;WACV;IACP,WAAW,kBAAkB;IAC7B,SAAS,kBAAkB;;;;;;;WAOpB,eAAe,QAAQ;WACvB,OAAO,MAAM;;WAEb,UAAU;;;;;KAMhB,oBAAoB,gBAAgB,GAAG,IAAI,qBAAqB,EAAE;EACrE,eAAe;IAEb;EACE,UAAU;EACV,WAAW;EACX,SAAS;EACT,SAAS;wBAGI,4IACF;;;;;;KAOL,gBAAgB,gBAAgB,GAAG,IAAI,qBACjD,oBAAoB,GAAG;WAER,eAAe,UAAU,mBAAmB,EAAE,QAAQ,iBAAiB,EAAE;WACzE,QAAQ,iBAAiB,EAAE;WAC3B,UAAU;WACV,aAAa,kBAAkB,iBAAiB,GAAG;IAE9D,oBAAoB,GAAG;;;;;;;KAQxB,WAAW,KAAK;EAAY,uBAAuB;IAAM,IAAI;;;;kBAKjD;;;;;;;;;;;;;;;;;;OAkBH,MACV,kBACA,SAAS,qBACP,gBAAgB,WAAW,UAAU,gBAAgB,GAAG,WAAW;;;;;UAMxD,oBACf,UAAU,eAAe,uBAAuB,eAAe;EAE/D,UAAU;EACV,UAAU"}
package/dist/types.d.mts CHANGED
@@ -17,6 +17,18 @@ import { AnyContractRouter, AnySchema, InferSchemaInput, InferSchemaOutput } fro
17
17
  * ```
18
18
  */
19
19
  interface RegisteredPlugins {}
20
+ type ModuleTransport = "inproc" | "rpc" | "mf";
21
+ type ModuleSurface = "api" | "ui" | "jobs";
22
+ interface ModuleEndpoint {
23
+ readonly transport: ModuleTransport;
24
+ readonly surface?: ModuleSurface;
25
+ readonly url: string;
26
+ readonly path?: `/${string}`;
27
+ readonly manifest?: string;
28
+ }
29
+ type PluginTransport = ModuleTransport;
30
+ type PluginSurface = ModuleSurface;
31
+ type PluginEndpoint = ModuleEndpoint;
20
32
  /**
21
33
  * Base type for any plugin instance.
22
34
  */
@@ -39,10 +51,12 @@ type AnyPluginConstructor = {
39
51
  type PluginRegistryEntry = {
40
52
  module: AnyPluginConstructor;
41
53
  remote?: string;
54
+ endpoint?: PluginEndpoint;
42
55
  version?: string;
43
56
  description?: string;
44
57
  } | {
45
58
  remote: string;
59
+ endpoint?: PluginEndpoint;
46
60
  version?: string;
47
61
  description?: string;
48
62
  };
@@ -107,7 +121,14 @@ type PluginClientType<T> = RouterClient<PluginRouterType<T>>;
107
121
  type PluginServicesEntry<T = any> = {
108
122
  client: (context?: PluginContextInput<T>) => PluginClientType<T>;
109
123
  router: PluginRouterType<T>;
124
+ endpoint?: PluginEndpoint;
125
+ };
126
+ type ModuleClientEntry<C extends AnyContractRouter = AnyContractRouter> = {
127
+ client: (context?: Record<string, unknown>) => RouterClient<ContractedRouter<C, any>>;
128
+ router?: ContractedRouter<C, any>;
129
+ endpoint?: PluginEndpoint;
110
130
  };
131
+ type PluginClientEntry<C extends AnyContractRouter = AnyContractRouter> = ModuleClientEntry<C>;
111
132
  /**
112
133
  * Extract plugin type from registered plugins by key
113
134
  * @param K - The plugin key
@@ -247,5 +268,5 @@ interface PluginRuntimeConfig<R extends Record<string, PluginRegistryEntry> = Re
247
268
  secrets?: SecretsConfig;
248
269
  }
249
270
  //#endregion
250
- export { AnyPlugin, AnyPluginConstructor, EveryPlugin, ExtractPluginType, InferRegistryFromEntries, InitializedPlugin, LoadedPlugin, PluginClientType, PluginConfigInput, PluginContext, PluginContextInput, PluginContract, PluginInstance, PluginMetadata, PluginRegistry, PluginRegistryEntry, PluginRouterType, PluginRuntimeConfig, PluginSecrets, PluginServicesEntry, PluginVariables, RegisteredPlugin, RegisteredPlugins, SecretsConfig, UsePluginResult };
271
+ export { AnyPlugin, AnyPluginConstructor, EveryPlugin, ExtractPluginType, InferRegistryFromEntries, InitializedPlugin, LoadedPlugin, ModuleClientEntry, ModuleEndpoint, ModuleSurface, ModuleTransport, PluginClientEntry, PluginClientType, PluginConfigInput, PluginContext, PluginContextInput, PluginContract, PluginEndpoint, PluginInstance, PluginMetadata, PluginRegistry, PluginRegistryEntry, PluginRouterType, PluginRuntimeConfig, PluginSecrets, PluginServicesEntry, PluginSurface, PluginTransport, PluginVariables, RegisteredPlugin, RegisteredPlugins, SecretsConfig, UsePluginResult };
251
272
  //# sourceMappingURL=types.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.mts","names":[],"sources":["../src/types.ts"],"mappings":";;;;;;;;;;;;;;;;;;UAwBiB;;;;KAKL,YAAY,OAAO,mBAAmB,WAAW,WAAW;;;;KAK5D;UACF;EACR;IACE,UAAU;IACV,WAAW;IACX,SAAS;IACT,SAAS;;;;;;KAOD;EACN,QAAQ;EAAsB;EAAiB;EAAkB;;EACjE;EAAgB;EAAkB;;;;;KAK5B,kBAAkB,KAAK;EAAY,cAAc;IAAM;;;;KAKvD,yBAAyB,UAAU,eAAe,2BAC3D,WAAW,IAAI,EAAE;EAAa,cAAc;IACzC,IACA,gBAAgB,oBACd,kBAAkB;;;;KAOd,eAAe,KAAK;EAC9B;IAAW,gBAAgB,UAAU;;IAEnC;;;;KAMQ,gBAAgB,KAAK;EAAY;IAAW,iBAAiB,UAAU;;IAC/E;;;;KAMQ,cAAc,KAAK;EAAY;IAAW,eAAe,UAAU;;IAC3E;;;;KAMQ,cAAc,KAAK;EAC7B;IAAW,eAAe,UAAU;;IAElC;;;;;KAOQ,iBAAiB,KAAK,iBAAiB,eAAe;;;;KAKtD,iBAAiB,KAAK,aAAa,iBAAiB;;;;;;KAOpD,oBAAoB;EAC9B,SAAS,UAAU,mBAAmB,OAAO,iBAAiB;EAC9D,QAAQ,iBAAiB;;;;;;;KAQf,iBAAiB,gBAAgB,GAAG,IAAI,qBAAqB,EAAE;EACzE,eAAe;IAEb;EACE,gBAAgB,UAAU;EAC1B,iBAAiB,UAAU;EAC3B,eAAe,UAAU;EACzB,eAAe,wBAAwB;IAEvC,OAAO,GAAG,GAAG,GAAG;;;;KAOV,mBAAmB,KAC7B,cAAc,WAAW,YAAY,iBAAiB,cAAc;;;;KAK1D,kBAAkB;EAC5B,WAAW,iBAAiB,gBAAgB;EAC5C,SAAS,iBAAiB,cAAc;;;;;KAM9B;WACD;WACA;WACA;;;;;KAMC,iBAAiB,eAAe;;;;;UAM3B;GACd;;;;;UAMc,aAAa,UAAU,YAAY;WACzC,gBAAgB;WAChB,UAAU;;;;;UAMJ,eAAe,UAAU,YAAY;WAC3C,QAAQ;WACR,UAAU;;;;;UAMJ,kBAAkB,UAAU,YAAY;WAC9C,QAAQ;WACR,UAAU;WACV;IACP,WAAW,kBAAkB;IAC7B,SAAS,kBAAkB;;;;;;;WAOpB,eAAe,QAAQ;WACvB,OAAO,MAAM;;WAEb,UAAU;;;;;KAMhB,oBAAoB,gBAAgB,GAAG,IAAI,qBAAqB,EAAE;EACrE,eAAe;IAEb;EACE,UAAU;EACV,WAAW;EACX,SAAS;EACT,SAAS;wBAGI,4IACF;;;;;;KAOL,gBAAgB,gBAAgB,GAAG,IAAI,qBACjD,oBAAoB,GAAG;WAER,eAAe,UAAU,mBAAmB,EAAE,QAAQ,iBAAiB,EAAE;WACzE,QAAQ,iBAAiB,EAAE;WAC3B,UAAU;WACV,aAAa,kBAAkB,iBAAiB,GAAG;IAE9D,oBAAoB,GAAG;;;;;;;KAQxB,WAAW,KAAK;EAAY,uBAAuB;IAAM,IAAI;;;;kBAKjD;;;;;;;;;;;;;;;;;;OAkBH,MACV,kBACA,SAAS,qBACP,gBAAgB,WAAW,UAAU,gBAAgB,GAAG,WAAW;;;;;UAMxD,oBACf,UAAU,eAAe,uBAAuB,eAAe;EAE/D,UAAU;EACV,UAAU"}
1
+ {"version":3,"file":"types.d.mts","names":[],"sources":["../src/types.ts"],"mappings":";;;;;;;;;;;;;;;;;;UAwBiB;KAEL;KAEA;UAEK;WACN,WAAW;WACX,UAAU;WACV;WACA;WACA;;KAGC,kBAAkB;KAElB,gBAAgB;KAEhB,iBAAiB;;;;KAKjB,YAAY,OAAO,mBAAmB,WAAW,WAAW;;;;KAK5D;UACF;EACR;IACE,UAAU;IACV,WAAW;IACX,SAAS;IACT,SAAS;;;;;;KAOD;EAEN,QAAQ;EACR;EACA,WAAW;EACX;EACA;;EAEA;EAAgB,WAAW;EAAgB;EAAkB;;;;;KAKvD,kBAAkB,KAAK;EAAY,cAAc;IAAM;;;;KAKvD,yBAAyB,UAAU,eAAe,2BAC3D,WAAW,IAAI,EAAE;EAAa,cAAc;IACzC,IACA,gBAAgB,oBACd,kBAAkB;;;;KAOd,eAAe,KAAK;EAC9B;IAAW,gBAAgB,UAAU;;IAEnC;;;;KAMQ,gBAAgB,KAAK;EAAY;IAAW,iBAAiB,UAAU;;IAC/E;;;;KAMQ,cAAc,KAAK;EAAY;IAAW,eAAe,UAAU;;IAC3E;;;;KAMQ,cAAc,KAAK;EAC7B;IAAW,eAAe,UAAU;;IAElC;;;;;KAOQ,iBAAiB,KAAK,iBAAiB,eAAe;;;;KAKtD,iBAAiB,KAAK,aAAa,iBAAiB;;;;;;KAOpD,oBAAoB;EAC9B,SAAS,UAAU,mBAAmB,OAAO,iBAAiB;EAC9D,QAAQ,iBAAiB;EACzB,WAAW;;KAGD,kBAAkB,UAAU,oBAAoB;EAC1D,SAAS,UAAU,4BAA4B,aAAa,iBAAiB;EAC7E,SAAS,iBAAiB;EAC1B,WAAW;;KAGD,kBAAkB,UAAU,oBAAoB,qBAC1D,kBAAkB;;;;;;KAOR,iBAAiB,gBAAgB,GAAG,IAAI,qBAAqB,EAAE;EACzE,eAAe;IAEb;EACE,gBAAgB,UAAU;EAC1B,iBAAiB,UAAU;EAC3B,eAAe,UAAU;EACzB,eAAe,wBAAwB;IAEvC,OAAO,GAAG,GAAG,GAAG;;;;KAOV,mBAAmB,KAC7B,cAAc,WAAW,YAAY,iBAAiB,cAAc;;;;KAK1D,kBAAkB;EAC5B,WAAW,iBAAiB,gBAAgB;EAC5C,SAAS,iBAAiB,cAAc;;;;;KAM9B;WACD;WACA;WACA;;;;;KAMC,iBAAiB,eAAe;;;;;UAM3B;GACd;;;;;UAMc,aAAa,UAAU,YAAY;WACzC,gBAAgB;WAChB,UAAU;;;;;UAMJ,eAAe,UAAU,YAAY;WAC3C,QAAQ;WACR,UAAU;;;;;UAMJ,kBAAkB,UAAU,YAAY;WAC9C,QAAQ;WACR,UAAU;WACV;IACP,WAAW,kBAAkB;IAC7B,SAAS,kBAAkB;;;;;;;WAOpB,eAAe,QAAQ;WACvB,OAAO,MAAM;;WAEb,UAAU;;;;;KAMhB,oBAAoB,gBAAgB,GAAG,IAAI,qBAAqB,EAAE;EACrE,eAAe;IAEb;EACE,UAAU;EACV,WAAW;EACX,SAAS;EACT,SAAS;wBAGI,4IACF;;;;;;KAOL,gBAAgB,gBAAgB,GAAG,IAAI,qBACjD,oBAAoB,GAAG;WAER,eAAe,UAAU,mBAAmB,EAAE,QAAQ,iBAAiB,EAAE;WACzE,QAAQ,iBAAiB,EAAE;WAC3B,UAAU;WACV,aAAa,kBAAkB,iBAAiB,GAAG;IAE9D,oBAAoB,GAAG;;;;;;;KAQxB,WAAW,KAAK;EAAY,uBAAuB;IAAM,IAAI;;;;kBAKjD;;;;;;;;;;;;;;;;;;OAkBH,MACV,kBACA,SAAS,qBACP,gBAAgB,WAAW,UAAU,gBAAgB,GAAG,WAAW;;;;;UAMxD,oBACf,UAAU,eAAe,uBAAuB,eAAe;EAE/D,UAAU;EACV,UAAU"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "every-plugin",
3
- "version": "3.0.0-rc.3",
3
+ "version": "3.0.0-rc.5",
4
4
  "type": "module",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -18,6 +18,14 @@
18
18
  "import": "./dist/index.mjs",
19
19
  "require": "./dist/index.cjs"
20
20
  },
21
+ "./client": {
22
+ "bun": {
23
+ "types": "./src/client.ts",
24
+ "import": "./src/client.ts"
25
+ },
26
+ "types": "./dist/client.d.mts",
27
+ "import": "./dist/client.mjs"
28
+ },
21
29
  "./identity": {
22
30
  "bun": {
23
31
  "types": "./src/identity.ts",
@@ -144,6 +152,14 @@
144
152
  "import": "./dist/build/ui/index.mjs",
145
153
  "require": "./dist/build/ui/index.cjs"
146
154
  },
155
+ "./dev-host": {
156
+ "bun": {
157
+ "types": "./src/dev/host.ts",
158
+ "import": "./src/dev/host.ts"
159
+ },
160
+ "types": "./dist/dev/host.d.mts",
161
+ "import": "./dist/dev/host.mjs"
162
+ },
147
163
  "./dev-serve": {
148
164
  "bun": {
149
165
  "types": "./src/dev/serve.ts",
@@ -222,7 +238,8 @@
222
238
  "@rsbuild/plugin-react": "2.1.0",
223
239
  "@tanstack/router-generator": "1.167.33",
224
240
  "@tanstack/router-plugin": "1.168.35",
225
- "sirv": "3.0.2"
241
+ "sirv": "3.0.2",
242
+ "sonner": ">=2.0.0"
226
243
  },
227
244
  "keywords": [
228
245
  "plugin",
@@ -20,6 +20,10 @@ export function hasFolderFormUi(cwd: string): boolean {
20
20
  );
21
21
  }
22
22
 
23
+ export function uiDistDirFor(cwd: string): string {
24
+ return path.join(cwd, UI_DIR, "dist");
25
+ }
26
+
23
27
  function generatedUiConfig(pluginId: string): string {
24
28
  return `import path from "node:path";
25
29
  import { createUiRsbuildConfig } from "every-plugin/build/ui";