{"version":3,"file":"vite.mjs","names":[],"sources":["../src/vite.ts"],"sourcesContent":["import { mkdir, writeFile } from \"node:fs/promises\";\nimport type { IncomingMessage, ServerResponse } from \"node:http\";\nimport path from \"node:path\";\nimport type { Plugin, ResolvedConfig, UserConfig } from \"vite\";\nimport { bootstrapChannels } from \"./channels/bootstrap\";\nimport type { ChannelRegistry } from \"./channels\";\nimport { createLogger } from \"./logger\";\nimport { TAKO_INTERNAL_HOST_SUFFIX, handleTakoEndpoint } from \"./tako/endpoints\";\nimport { initServerRuntime } from \"./tako/init\";\nimport { initBootstrapFromFd, readViaInheritedFd } from \"./tako/secrets-fd\";\nimport { installConsoleBridge } from \"./tako/console-bridge\";\nimport { writeViaInheritedFd } from \"./tako/readiness\";\nimport { installStdioBridge } from \"./tako/stdio-bridge\";\n\ninterface ViteEntryChunkLike {\n  type: \"chunk\";\n  fileName: string;\n  isEntry: boolean;\n}\n\nconst WRAPPED_ENTRY_FILE = \"tako-entry.mjs\";\nconst TAKO_DEV_ALLOWED_HOSTS_ENV = \"TAKO_DEV_ALLOWED_HOSTS\";\n\nfunction statusAppName(fallback: string): string {\n  const [appName = \"\"] = (process.env[\"TAKO_APP_NAME\"] || fallback).split(\"/\");\n  return appName || fallback;\n}\n\nfunction toPosixPath(filePath: string): string {\n  return filePath.replaceAll(\"\\\\\", \"/\");\n}\n\nfunction toRelativeImportSpecifier(filePath: string): string {\n  const normalized = toPosixPath(filePath);\n  if (normalized.startsWith(\"./\") || normalized.startsWith(\"../\")) {\n    return normalized;\n  }\n  return `./${normalized}`;\n}\n\nfunction renderWrappedEntrySource(compiledMain: string): string {\n  const importSpecifier = toRelativeImportSpecifier(compiledMain);\n  return `import entryModule, * as entryNamespace from ${JSON.stringify(importSpecifier)};\nimport { handleTakoEndpoint, normalizeFetchResponse } from \"tako.sh/internal\";\n\nconst fetchHandler =\n  typeof entryModule === \"function\"\n    ? entryModule\n    : entryModule && typeof entryModule.fetch === \"function\"\n      ? entryModule.fetch.bind(entryModule)\n      : typeof entryNamespace.fetch === \"function\"\n        ? entryNamespace.fetch\n        : null;\n\nif (!fetchHandler) {\n  throw new Error(\n    \"Invalid server entry: export a default fetch function, a default object with fetch, or a named fetch export.\",\n  );\n}\n\nexport default async function(request) {\n  const [appName = \"\"] = (process.env.TAKO_APP_NAME ?? \"app\").split(\"/\");\n  const takoResponse = await handleTakoEndpoint(request, {\n    status: \"healthy\",\n    app: appName || \"app\",\n    version: process.env.TAKO_BUILD ?? \"unknown\",\n    instance_id: process.env.TAKO_INSTANCE_ID ?? \"unknown\",\n    pid: process.pid,\n    uptime_seconds: 0,\n  });\n  if (takoResponse) return takoResponse;\n  return normalizeFetchResponse(await fetchHandler(request));\n};\n`;\n}\n\nfunction pickCompiledMain(entries: string[]): string {\n  if (entries.length === 0) {\n    throw new Error(\n      \"Could not detect server entry chunk in Vite build output. Ensure your SSR/server build emits an entry chunk.\",\n    );\n  }\n\n  if (entries.length === 1) {\n    return entries[0]!;\n  }\n\n  const serverEntries = entries.filter((entry) =>\n    entry\n      .split(\"/\")\n      .map((segment) => segment.toLowerCase())\n      .includes(\"server\"),\n  );\n\n  if (serverEntries.length === 1) {\n    return serverEntries[0]!;\n  }\n\n  throw new Error(\n    `Could not choose a single server entry chunk from Vite output. Found: ${entries.join(\", \")}. Configure your build to emit one server entry chunk.`,\n  );\n}\n\nfunction nodeRequestToFetch(req: IncomingMessage): Promise<Request> {\n  const host = req.headers.host ?? \"localhost\";\n  const url = `http://${host}${req.url ?? \"/\"}`;\n  const headers = new Headers();\n  for (const [key, val] of Object.entries(req.headers)) {\n    if (val === undefined) continue;\n    if (Array.isArray(val)) {\n      for (const v of val) headers.append(key, v);\n    } else {\n      headers.set(key, val);\n    }\n  }\n  return new Promise((resolve, reject) => {\n    const chunks: Buffer[] = [];\n    req.on(\"data\", (chunk: Buffer) => chunks.push(chunk));\n    req.on(\"end\", () => {\n      const init: RequestInit = {\n        method: req.method ?? \"GET\",\n        headers,\n      };\n      if (chunks.length > 0) {\n        init.body = Buffer.concat(chunks) as unknown as BodyInit;\n      }\n      resolve(new Request(url, init));\n    });\n    req.on(\"error\", reject);\n  });\n}\n\nasync function sendFetchResponse(res: ServerResponse, response: Response): Promise<void> {\n  res.statusCode = response.status;\n  for (const [key, val] of response.headers.entries()) {\n    res.setHeader(key, val);\n  }\n  res.end(Buffer.from(await response.arrayBuffer()));\n}\n\nfunction mergeServeAllowedHosts(\n  existing: unknown,\n  extraHosts = devAllowedHostsFromEnv(),\n): true | string[] {\n  if (existing === true) {\n    return true;\n  }\n\n  const merged = Array.isArray(existing)\n    ? existing.filter((host): host is string => typeof host === \"string\")\n    : [];\n\n  for (const host of [\".test\", \".tako.test\", ...extraHosts]) {\n    if (!merged.includes(host)) {\n      merged.push(host);\n    }\n  }\n\n  return merged;\n}\n\nfunction devAllowedHostsFromEnv(): string[] {\n  const raw = process.env[TAKO_DEV_ALLOWED_HOSTS_ENV];\n  if (!raw) {\n    return [];\n  }\n  return raw\n    .split(\",\")\n    .map((host) => host.trim())\n    .filter(Boolean);\n}\n\nfunction isViteEntryChunk(chunk: unknown): chunk is ViteEntryChunkLike {\n  if (!chunk || typeof chunk !== \"object\") {\n    return false;\n  }\n\n  const maybeChunk = chunk as Partial<ViteEntryChunkLike>;\n  return (\n    maybeChunk.type === \"chunk\" &&\n    maybeChunk.isEntry === true &&\n    typeof maybeChunk.fileName === \"string\"\n  );\n}\n\n/**\n * Vite plugin that wires a project up to the Tako build/dev pipeline.\n *\n * Responsibilities:\n * - Marks SDK server entries as SSR-external so Vite doesn't try to bundle\n *   server-only modules (secrets, workflow RPC, storage signing, etc.).\n * - In dev, swaps Vite's default logger for structured JSON lines so the\n *   tako dev server can render them alongside other subprocess logs.\n * - Under `tako dev`, reports the dev server's bound port back to the parent\n *   over fd 4 and adds configured dev route hosts to `server.allowedHosts`.\n * - On build, records the entry chunk filenames so the Tako runtime can find\n *   the generated entrypoint.\n *\n * Add to `vite.config.ts` alongside any framework plugin:\n *\n * @example\n * ```typescript\n * import { defineConfig } from \"vite\";\n * import { tako } from \"tako.sh/vite\";\n *\n * export default defineConfig({ plugins: [tako()] });\n * ```\n *\n * @returns A Vite {@link Plugin} instance.\n */\nexport function tako(): Plugin {\n  let resolvedConfig: ResolvedConfig | null = null;\n  let entryChunks: string[] = [];\n  let sawBundleGeneration = false;\n  let activeCommand: \"build\" | \"serve\" | null = null;\n\n  return {\n    name: \"tako-vite-entry\",\n    config(userConfig, env) {\n      activeCommand = env.command;\n\n      const config: UserConfig = {};\n\n      // Exclude the SDK from Vite's SSR transform — it's a server-side\n      // dependency with runtime dynamic imports Vite can't statically analyze.\n      config.ssr = { external: [\"tako.sh\", \"tako.sh/server\", \"tako.sh/internal\"] };\n\n      // Under the tako dev server, emit structured JSON log lines so the\n      // parent process can render Vite output alongside other subprocess logs.\n      if (process.env[\"ENV\"] === \"development\") {\n        installStdioBridge(\"app\");\n        installConsoleBridge(\"app\");\n        config.customLogger = createLogger(\"vite\").toViteLogger();\n      }\n\n      if (activeCommand === \"serve\") {\n        // Let Vite pick its own port — the configureServer hook reports\n        // the actual bound port to Tako via fd 4.\n        config.server = {\n          allowedHosts: mergeServeAllowedHosts(userConfig.server?.allowedHosts),\n          host: \"127.0.0.1\",\n        };\n      }\n\n      return config;\n    },\n    configResolved(config) {\n      resolvedConfig = config;\n    },\n    configureServer(server) {\n      // Read the same fd bootstrap used by server entrypoints so dev SSR can\n      // sign image URLs and authenticate internal SDK requests.\n      initBootstrapFromFd(readViaInheritedFd);\n\n      // Wire up the same server-runtime install used by the production\n      // entrypoint so user server fns can `signal()`, `.enqueue()`, and\n      // publish to channels during `tako dev` without boot-time setup.\n      initServerRuntime();\n\n      // Discover channel definitions from `<appRoot>/channels/` once at startup.\n      // The registry feeds the internal channel-auth/dispatch endpoints.\n      let channelsPromise: Promise<ChannelRegistry> | null = null;\n      const getChannels = (): Promise<ChannelRegistry> => {\n        if (!channelsPromise) {\n          channelsPromise = bootstrapChannels({ appDir: process.cwd() }).then((r) => r.registry);\n        }\n        return channelsPromise;\n      };\n\n      server.middlewares.use((req: IncomingMessage, res: ServerResponse, next: () => void) => {\n        const host = (req.headers.host ?? \"\").split(\":\")[0] ?? \"\";\n        if (!host.endsWith(TAKO_INTERNAL_HOST_SUFFIX)) {\n          next();\n          return;\n        }\n        Promise.all([nodeRequestToFetch(req), getChannels()])\n          .then(([fetchReq, channels]) =>\n            handleTakoEndpoint(\n              fetchReq,\n              {\n                status: \"healthy\",\n                app: statusAppName(\"dev\"),\n                version: process.env[\"TAKO_BUILD\"] ?? \"dev\",\n                instance_id: process.env[\"TAKO_INSTANCE_ID\"] ?? \"dev\",\n                pid: process.pid,\n                uptime_seconds: 0,\n              },\n              channels,\n            ),\n          )\n          .then((response) => {\n            if (response) return sendFetchResponse(res, response);\n            next();\n            return;\n          })\n          .catch(() => next());\n      });\n\n      server.httpServer?.once(\"listening\", () => {\n        const addr = server.httpServer?.address();\n        if (addr && typeof addr === \"object\") {\n          writeViaInheritedFd(4, addr.port);\n        }\n      });\n    },\n    generateBundle(_options, bundle) {\n      sawBundleGeneration = true;\n      entryChunks = Object.values(bundle)\n        .filter(isViteEntryChunk)\n        .map((chunk) => chunk.fileName)\n        .sort();\n    },\n    async closeBundle() {\n      if (activeCommand === \"serve\") {\n        return;\n      }\n      if (!resolvedConfig) {\n        throw new Error(\"tako was not initialized by Vite configResolved hook.\");\n      }\n      if (!sawBundleGeneration) {\n        return;\n      }\n\n      const outDirAbs = path.isAbsolute(resolvedConfig.build.outDir)\n        ? path.normalize(resolvedConfig.build.outDir)\n        : path.resolve(resolvedConfig.root, resolvedConfig.build.outDir);\n      const compiledMain = pickCompiledMain(entryChunks);\n      const wrappedEntrySource = renderWrappedEntrySource(compiledMain);\n      const wrappedEntryPath = path.resolve(outDirAbs, WRAPPED_ENTRY_FILE);\n\n      await mkdir(path.dirname(wrappedEntryPath), { recursive: true });\n      await writeFile(wrappedEntryPath, wrappedEntrySource, \"utf8\");\n    },\n  };\n}\n"],"mappings":"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"}