{"version":3,"file":"index.cjs","names":[],"sources":["../../src/build/build.ts","../../src/island/island.ts","../../src/ssr/server.ts","../../src/image/pipeline.ts","../../src/middleware/stream-boundary.ts","../../src/build/vite-build.ts"],"sourcesContent":["import { cp, mkdir, stat, writeFile } from \"node:fs/promises\";\nimport { join, dirname } from \"node:path\";\nimport { scanRoutes, type PageRoute, type ScannedRoutes } from \"../router/route-scanner.js\";\nimport { scanIslands, type IslandModule } from \"../island/scan.js\";\nimport { generateClientEntry } from \"../island/generate-entry.js\";\nimport { renderPage, renderErrorPage } from \"../ssr/render.js\";\nimport { scanActions, actionNames } from \"../action/scan.js\";\nimport { consumeImageRegistry, setImageManifest, type ImageFormat } from \"../image/index.js\";\nimport { processImageBatch, type ImageManifest } from \"../image/service.js\";\nimport { runIntegrationHook, type NixKitIntegration } from \"../integrations/index.js\";\nimport type { RouteParams, GenerateStaticParams } from \"../types.js\";\n\nexport interface BuildConfig {\n  /** Absolute path to the app directory (e.g. /project/src/app). */\n  appDir: string;\n  /** Absolute path to the output directory (e.g. /project/dist). */\n  outDir: string;\n  /** Absolute path to the project root (e.g. /project). When provided, action\n   * paths in the serialized HTML shell are made relative to this root. */\n  root?: string;\n  /** Base path for the client entry module, e.g. \"/_nix-js/entry-client.js\". */\n  clientEntry?: string;\n  /** Default language for the HTML shell. */\n  lang?: string;\n  /**\n   * Absolute path to the islands directory (e.g. /project/src/islands).\n   * When set, `build` scans it and generates a client entry module listing\n   * every island so you don't have to maintain `entry-client.ts` by hand.\n   */\n  islandsDir?: string;\n  /**\n   * Absolute path where the generated client entry module is written\n   * (e.g. /project/.nix-js/entry-client.ts). Required when `islandsDir` is set.\n   */\n  generatedEntry?: string;\n  /**\n   * Import specifier the generated entry uses for `hydrateIslands`.\n   * Defaults to the published subpath `@deijose/nix-js-kit/island`.\n   */\n  hydrateImport?: string;\n  /**\n   * Import specifier the generated entry uses for `startClientRouter`.\n   * Defaults to the published subpath `@deijose/nix-js-kit/router`.\n   */\n  routerImport?: string;\n  /** Absolute path to the public directory for static assets (optional). */\n  publicDir?: string;\n  /** Image formats to generate when sharp is available. Defaults to [\"webp\", \"avif\"]. */\n  imageFormats?: ImageFormat[];\n  /**\n   * Whether the SSR render endpoint (`/__nix-js/render`) exists at runtime.\n   * Defaults to `true` (dev, preview and SSR deployments). Set to `false` for\n   * fully static outputs so the emitted HTML tells the client router to skip\n   * the endpoint (no 404 storms on static hosts like Vercel).\n   */\n  renderEndpoint?: boolean;\n  /**\n   * Integrations to invoke during the build lifecycle. When provided, the\n   * `build` hook fires after all pages and image variants are generated,\n   * giving integrations a chance to write post-build artifacts (sitemaps,\n   * robots.txt, search indexes, etc.) into the output directory.\n   */\n  integrations?: NixKitIntegration[];\n}\n\nexport interface BuildResult {\n  /** Number of static HTML pages generated. */\n  pages: number;\n  /** Paths that were skipped because they are dynamic without a static param list. */\n  skipped: string[];\n  /** Absolute paths to the generated HTML files. */\n  files: string[];\n  /** Islands discovered when `islandsDir` is set. */\n  islands: IslandModule[];\n  /** Absolute path to the generated client entry, if one was written. */\n  generatedEntry?: string;\n  /** Number of image variants generated (0 if sharp is not installed). */\n  imagesProcessed: number;\n  /** Absolute path to the output directory where build artifacts were written.\n   * When called via the CLI, this is the atomic staging directory (not the\n   * final `dist/`). Integration `build` hooks should write post-build\n   * artifacts here so they survive the atomic swap. */\n  outDir: string;\n}\n\nfunction urlToFilePath(outDir: string, urlPath: string): string {\n  if (urlPath === \"/\") {\n    return join(outDir, \"index.html\");\n  }\n\n  const segments = urlPath.slice(1).split(\"/\");\n  return join(outDir, ...segments, \"index.html\");\n}\n\nfunction isDynamic(path: string): boolean {\n  return path.includes(\":\");\n}\n\nfunction buildConcreteUrl(path: string, params: RouteParams): string {\n  return path.replace(/:([a-zA-Z0-9_]+)(\\*)?/g, (_, name, catchAll) => {\n    const value = params[name];\n    if (value === undefined || value === null) {\n      throw new Error(\n        `Missing value for dynamic segment \"${name}\" in path \"${path}\"`,\n      );\n    }\n    if (catchAll) {\n      return Array.isArray(value) ? value.join(\"/\") : String(value);\n    }\n    return String(value);\n  });\n}\n\n/**\n * Builds a static site from a scanned route tree.\n *\n * @param config Build configuration.\n * @returns Summary of generated files.\n */\nexport async function build(config: BuildConfig): Promise<BuildResult> {\n  if (config.publicDir) {\n    try {\n      if ((await stat(config.publicDir)).isDirectory()) {\n        await mkdir(config.outDir, { recursive: true });\n        await cp(config.publicDir, config.outDir, { recursive: true, force: true });\n      }\n    } catch (error) {\n      if ((error as NodeJS.ErrnoException).code !== \"ENOENT\") throw error;\n    }\n  }\n\n  const routes = await scanRoutes(config.appDir);\n  const actions = await scanActions(config.appDir);\n  // Only action names are serialized into the HTML shell; full paths stay on the server.\n  const publicActions = actionNames(actions);\n  const result: BuildResult = { pages: 0, skipped: [], files: [], islands: [], imagesProcessed: 0, outDir: config.outDir };\n\n  // Scan islands and generate the client entry before rendering pages, so the\n  // hydration bundle stays in sync with what the app actually uses.\n  if (config.islandsDir) {\n    result.islands = await scanIslands(config.islandsDir);\n  }\n\n  if (config.generatedEntry) {\n    result.generatedEntry = await generateClientEntry({\n      islands: result.islands,\n      outFile: config.generatedEntry,\n      hydrateImport: config.hydrateImport,\n      routerImport: config.routerImport,\n    });\n  }\n\n  for (const route of routes.pages) {\n    if (!isDynamic(route.path)) {\n      const filePath = await buildPage(config, route, publicActions);\n      result.pages++;\n      result.files.push(filePath);\n      continue;\n    }\n\n    const dynamicFiles = await buildDynamicPages(config, route, publicActions);\n    if (dynamicFiles.length === 0) {\n      result.skipped.push(route.path);\n    } else {\n      result.pages += dynamicFiles.length;\n      result.files.push(...dynamicFiles);\n    }\n  }\n\n  // Generate static 404 and 500 error pages when they exist.\n  const errorConfig = { lang: config.lang, clientEntry: config.clientEntry, renderEndpoint: false };\n  if (routes.error404) {\n    const result404 = await renderErrorPage({\n      routes,\n      status: 404,\n      config: errorConfig,\n      actions: publicActions,\n    });\n    if (result404) {\n      const filePath = join(config.outDir, \"404.html\");\n      await mkdir(dirname(filePath), { recursive: true });\n      await writeFile(filePath, result404.html, \"utf8\");\n      result.files.push(filePath);\n    }\n  }\n\n  if (routes.error500) {\n    const result500 = await renderErrorPage({\n      routes,\n      status: 500,\n      config: errorConfig,\n      actions: publicActions,\n    });\n    if (result500) {\n      const filePath = join(config.outDir, \"500.html\");\n      await mkdir(dirname(filePath), { recursive: true });\n      await writeFile(filePath, result500.html, \"utf8\");\n      result.files.push(filePath);\n    }\n  }\n\n  // Process registered images with the ImageService (if sharp is installed).\n  // This is a two-pass process:\n  //   1. First render pass registers all images (already done above).\n  //   2. Process registered images → produce manifest.\n  //   3. If variants were generated, set the manifest and re-render pages\n  //      so the markup uses real <picture>/<source> with hashed URLs.\n  const registeredImages = consumeImageRegistry();\n  let manifest: ImageManifest | null = null;\n  if (registeredImages.length > 0 && config.publicDir) {\n    const manifestPath = join(config.outDir, \".nix-js\", \"image-manifest.json\");\n    const processResult = await processImageBatch(registeredImages, {\n      publicDir: config.publicDir,\n      outDir: config.outDir,\n      formats: config.imageFormats,\n      manifestPath,\n    });\n    result.imagesProcessed = processResult.count;\n\n    if (processResult.optimized && processResult.count > 0) {\n      manifest = processResult.manifest;\n      setImageManifest(manifest);\n\n      // Re-render all pages with the manifest so image() emits <picture>.\n      result.pages = 0;\n      result.files = [];\n      for (const route of routes.pages) {\n        if (!isDynamic(route.path)) {\n          const filePath = await buildPage(config, route, publicActions);\n          result.pages++;\n          result.files.push(filePath);\n          continue;\n        }\n        const dynamicFiles = await buildDynamicPages(config, route, publicActions);\n        if (dynamicFiles.length === 0) {\n          result.skipped.push(route.path);\n        } else {\n          result.pages += dynamicFiles.length;\n          result.files.push(...dynamicFiles);\n        }\n      }\n\n      // Re-render error pages too.\n      if (routes.error404) {\n        const result404 = await renderErrorPage({\n          routes,\n          status: 404,\n          config: errorConfig,\n          actions: publicActions,\n        });\n        if (result404) {\n          const filePath = join(config.outDir, \"404.html\");\n          await mkdir(dirname(filePath), { recursive: true });\n          await writeFile(filePath, result404.html, \"utf8\");\n          result.files.push(filePath);\n        }\n      }\n      if (routes.error500) {\n        const result500 = await renderErrorPage({\n          routes,\n          status: 500,\n          config: errorConfig,\n          actions: publicActions,\n        });\n        if (result500) {\n          const filePath = join(config.outDir, \"500.html\");\n          await mkdir(dirname(filePath), { recursive: true });\n          await writeFile(filePath, result500.html, \"utf8\");\n          result.files.push(filePath);\n        }\n      }\n    }\n  }\n\n  // Clear the manifest so subsequent builds start fresh.\n  setImageManifest(null);\n\n  // Fire the `build` integration hook so integrations can write\n  // post-build artifacts (sitemaps, robots.txt, search indexes, etc.)\n  // into the output directory. This runs after all pages, image variants,\n  // and the manifest are written, but before the atomic staging commit\n  // (when called via the CLI), so integration artifacts survive the swap.\n  if (config.integrations && config.integrations.length > 0) {\n    await runIntegrationHook(config.integrations, \"build\", [\n      result,\n      { root: config.root ?? config.outDir, command: \"build\" },\n    ]);\n  }\n\n  return result;\n}\n\nasync function buildPage(\n  config: BuildConfig,\n  route: PageRoute,\n  actions: Record<string, string[]>,\n): Promise<string> {\n  return buildConcretePage(config, route, {}, actions);\n}\n\nasync function buildDynamicPages(\n  config: BuildConfig,\n  route: PageRoute,\n  actions: Record<string, string[]>,\n): Promise<string[]> {\n  const { generateStaticParams } = (await import(\n    route.pagePath\n  )) as { generateStaticParams?: GenerateStaticParams };\n\n  if (!generateStaticParams) {\n    return [];\n  }\n\n  const paramList = await generateStaticParams();\n  if (!Array.isArray(paramList) || paramList.length === 0) {\n    return [];\n  }\n\n  const files: string[] = [];\n  for (const params of paramList) {\n    files.push(await buildConcretePage(config, route, params, actions));\n  }\n  return files;\n}\n\nasync function buildConcretePage(\n  config: BuildConfig,\n  route: PageRoute,\n  params: RouteParams,\n  actions: Record<string, string[]>,\n): Promise<string> {\n  const { html: htmlOut } = await renderPage({\n    route,\n    params,\n    searchParams: new URLSearchParams(),\n    config: { lang: config.lang, clientEntry: config.clientEntry, renderEndpoint: false },\n    actions,\n  });\n\n  const urlPath = isDynamic(route.path) ? buildConcreteUrl(route.path, params) : route.path;\n  const filePath = urlToFilePath(config.outDir, urlPath);\n  await mkdir(dirname(filePath), { recursive: true });\n  await writeFile(filePath, htmlOut, \"utf8\");\n\n  return filePath;\n}\n\nexport { scanRoutes, type PageRoute, type ScannedRoutes };\n","import { NIX_RENDER_PROTOCOL, type NixTemplate, type ServerRenderProtocolContext } from \"@deijose/nix-js\";\n\n// --- Islands helper ---\n//\n// Marks a component as an island. During server-side rendering it emits a\n// static placeholder with `data-nix-js-island` attributes. The client entry finds\n// these markers and hydrates them with the real component + reactive signals.\n//\n// SSR strategy\n// ------------\n// By default the component is executed on the server to produce a fallback HTML\n// fragment (better first paint, SEO, less layout shift). Components that access\n// browser-only globals (`document`, `window`, `navigator`, ...) in their body\n// cannot run on the server. Two opt-out mechanisms are provided, mirroring the\n// industry standard (Astro `client:only`, Next.js `dynamic(..., { ssr: false })`):\n//\n//   1. directive: \"only\"  — shortcut for client-only with `load` scheduling.\n//   2. options: { ssr: false } — client-only with any directive (load/idle/visible).\n//\n// When SSR is skipped, only `options.fallback` (a NixTemplate or string) is\n// rendered into the marker. The client hydrates from scratch.\n//\n// When SSR runs and the component throws, the error is NOT swallowed: it is\n// re-thrown wrapped with an actionable message naming the island and suggesting\n// `directive: \"only\"` / `{ ssr: false }` / `isSSR()`. This matches Astro and\n// Next.js, which never try/catch to \"auto-detect\" client-only components.\n\nexport type IslandDirective = \"load\" | \"idle\" | \"visible\" | \"only\";\n\nexport interface IslandComponent<TProps = unknown> {\n  (props: TProps): NixTemplate | null | false | undefined;\n}\n\n/**\n * Options for {@link island}.\n *\n * - `ssr`: Whether to execute the component on the server. Defaults to `true`\n *   unless `directive === \"only\"` (then `false`). When `false`, the component\n *   is never called during SSR; only `fallback` is rendered.\n * - `fallback`: HTML to render inside the island marker when SSR is skipped or\n *   the component returns null/false. Accepts a `NixTemplate` (reactive, with\n *   signals) or a plain string. Defaults to an empty string.\n */\nexport interface IslandOptions {\n  ssr?: boolean;\n  fallback?: NixTemplate | string;\n}\n\n/**\n * Renders a component to a static HTML string with island markers.\n *\n * @param name Unique island name used by the client entry to look up the module.\n * @param component Island component. Executed on the server unless `directive`\n *   is `\"only\"` or `options.ssr` is `false`.\n * @param props Props passed to the component and serialized for hydration.\n * @param directive When to hydrate on the client. Use `\"only\"` to skip SSR\n *   entirely (client-only island).\n * @param options SSR strategy and fallback content.\n * @returns A NixTemplate that renders the island placeholder.\n */\nexport function island<TProps>(\n  name: string,\n  component: IslandComponent<TProps>,\n  props: TProps,\n  directive: IslandDirective = \"load\",\n  options?: IslandOptions,\n): NixTemplate {\n  // `directive: \"only\"` forces ssr off; explicit `options.ssr` wins otherwise.\n  const ssr = directive === \"only\" ? false : (options?.ssr ?? true);\n  const fallback = options?.fallback;\n\n  const markerHtml = (innerHtml: string) =>\n    `<div data-nix-js-island=\"${escapeHtml(name)}\" data-directive=\"${directive}\" data-props='${serializeProps(props)}'>${innerHtml}</div>`;\n\n  return {\n    __isNixTemplate: true as const,\n    [NIX_RENDER_PROTOCOL]: {\n      async renderServer(context: ServerRenderProtocolContext) {\n        let innerHtml = \"\";\n        if (ssr) {\n          try {\n            const template = component(props);\n            if (template !== null && template !== false && template !== undefined) {\n              innerHtml = await context.render(template, { markers: true });\n            } else {\n              // Component returned null/false/undefined — render fallback if any.\n              innerHtml = await renderFallback(fallback, context);\n            }\n          } catch (error) {\n            throw wrapIslandSSRError(name, error);\n          }\n        } else {\n          innerHtml = await renderFallback(fallback, context);\n        }\n        return markerHtml(innerHtml);\n      },\n    },\n    _render(parent: Node, before: Node | null): () => void {\n      const container = document.createElement(\"div\");\n      let innerHtml = \"\";\n      if (ssr) {\n        const template = component(props);\n        if (template !== null && template !== false && template !== undefined) {\n          const dispose = template._render(container, null);\n          innerHtml = container.innerHTML;\n          dispose();\n        } else {\n          // null/false/undefined — render fallback if any.\n          innerHtml = renderFallbackSync(fallback, container);\n        }\n      } else {\n        innerHtml = renderFallbackSync(fallback, container);\n      }\n      const wrapper = document.createElement(\"template\");\n      wrapper.innerHTML = markerHtml(innerHtml);\n      const fragment = wrapper.content;\n      const inserted = fragment.firstChild;\n      parent.insertBefore(fragment, before);\n      return () => {\n        if (inserted?.parentNode) inserted.parentNode.removeChild(inserted);\n      };\n    },\n  } as unknown as NixTemplate;\n}\n\n/**\n * Wraps an SSR error from an island component with an actionable message.\n *\n * Following the Astro/Next.js convention, SSR errors are never silently\n * swallowed — they propagate so real bugs surface. The wrapper adds the island\n * name and three concrete remediation paths.\n */\nfunction wrapIslandSSRError(name: string, error: unknown): Error {\n  const cause = error instanceof Error ? error : new Error(String(error));\n  const msg = error instanceof Error ? error.message : String(error);\n  return new Error(\n    `[nix-js-kit] Island \"${name}\" threw during SSR: ${msg}\\n` +\n    `  If the component accesses browser-only globals (document, window, etc.),\\n` +\n    `  use directive: \"only\" or options: { ssr: false } to skip server rendering.\\n` +\n    `  For environment reads (matchMedia, localStorage, navigator) you may guard\\n` +\n    `  the access with isSSR() from \"@deijose/nix-js-kit\".`,\n    { cause },\n  );\n}\n\n/** Renders the fallback (NixTemplate or string) to an HTML string on the server. */\nasync function renderFallback(\n  fallback: NixTemplate | string | undefined,\n  context: ServerRenderProtocolContext,\n): Promise<string> {\n  if (fallback == null || fallback === \"\") return \"\";\n  if (typeof fallback === \"string\") return fallback;\n  return context.render(fallback, { markers: false });\n}\n\n/** Renders the fallback into a container and returns its innerHTML (client path). */\nfunction renderFallbackSync(fallback: NixTemplate | string | undefined, container: HTMLElement): string {\n  if (fallback == null || fallback === \"\") return \"\";\n  if (typeof fallback === \"string\") return fallback;\n  const dispose = fallback._render(container, null);\n  const html = container.innerHTML;\n  dispose();\n  return html;\n}\n\nfunction escapeHtml(value: string): string {\n  return value\n    .replace(/&/g, \"&amp;\")\n    .replace(/</g, \"&lt;\")\n    .replace(/>/g, \"&gt;\")\n    .replace(/\"/g, \"&quot;\")\n    .replace(/'/g, \"&#39;\");\n}\n\nfunction serializeProps(props: unknown): string {\n  return JSON.stringify(props ?? null)\n    .replace(/</g, \"\\\\u003c\")\n    .replace(/'/g, \"\\\\u0027\");\n}\n","import { createServer, type IncomingMessage, type Server } from \"node:http\";\nimport { readFile } from \"node:fs/promises\";\nimport { extname } from \"node:path\";\nimport { scanRoutes } from \"../router/route-scanner.js\";\nimport { scanActions, actionNames } from \"../action/scan.js\";\nimport { handleActionRequest, type ActionSecurityOptions } from \"../action/server.js\";\nimport { getCachedHtml, setCachedHtml } from \"../cache.js\";\nimport { matchApiRoute, matchRoute } from \"./match.js\";\nimport { renderPage, renderErrorPage } from \"./render.js\";\nimport { renderPageBody, renderStreamingPage, RouteNotFoundError } from \"./stream.js\";\nimport { loadMiddleware, matchesMiddleware, runMiddleware } from \"../middleware/index.js\";\nimport { incomingMessageToRequest } from \"../runtime/node-http.js\";\nimport { resolveStaticFile } from \"../runtime/static.js\";\nimport { toPublicErrorInfo } from \"../errors.js\";\n\nexport interface SsrServerOptions {\n  /** Absolute path to the app directory (e.g. /project/src/app). */\n  appDir: string;\n  /** Absolute path to the project root. When provided, action paths in the\n   * serialized HTML shell are made relative to this root. */\n  root?: string;\n  /** Absolute path to the public directory for static files (optional). */\n  publicDir?: string;\n  /** Base path for the client entry module, e.g. \"/_nix-js/entry-client.js\". */\n  clientEntry?: string;\n  /** Default language for the HTML shell. */\n  lang?: string;\n  port?: number;\n  host?: string;\n  /** Absolute path to the ISR cache directory (optional). */\n  cacheDir?: string;\n  /** Default revalidate interval in seconds when a page does not export one. */\n  defaultRevalidate?: number;\n  /** If true, render pages with loading.ts boundaries using streaming. */\n  streaming?: boolean;\n  /** CSRF / origin policy applied to the server actions endpoint. */\n  actionSecurity?: ActionSecurityOptions;\n}\n\nexport interface SsrServer {\n  server: Server;\n  listen(): Promise<void>;\n  close(): Promise<void>;\n}\n\n/**\n * Create an SSR server that renders pages on demand and serves static files.\n */\nexport async function createSsrServer(options: SsrServerOptions): Promise<SsrServer> {\n  const routes = await scanRoutes(options.appDir);\n  const actions = await scanActions(options.appDir);\n  const publicActions = actionNames(actions);\n\n  // Load user middleware (src/middleware.ts) if it exists.\n  const middleware = options.root ? await loadMiddleware(options.root) : null;\n\n  const resolveAction = async (name: string, page?: string) => {\n    const pageKey = resolveActionPageKey(page, routes);\n    const pageActions = pageKey ? actions[pageKey] : Object.values(actions).find((p) => p[name]) ?? undefined;\n    const actionPath = pageActions ? pageActions[name] : undefined;\n    if (!actionPath) return undefined;\n    const mod = (await import(actionPath)) as Record<string, unknown>;\n    const action = mod[name];\n    if (typeof action === \"function\") {\n      return action as (...args: unknown[]) => unknown;\n    }\n    return undefined;\n  };\n\n  const server = createServer(async (req, res) => {\n    let urlPath = req.url ?? \"/\";\n    if (urlPath.includes(\"?\")) urlPath = urlPath.split(\"?\")[0];\n\n    // Server actions endpoint.\n    if (urlPath === \"/__nix-js/actions\" && req.method === \"POST\") {\n      try {\n        const body = await readRequestBody(req);\n        const request = incomingMessageToRequest(req, body);\n        const response = await handleActionRequest(request, resolveAction, options.actionSecurity);\n        res.writeHead(response.status, Object.fromEntries(response.headers.entries()));\n        res.end(await response.text());\n      } catch (err) {\n        console.error(\"[action] error handling\", err);\n        res.writeHead(500, { \"Content-Type\": \"text/plain; charset=utf-8\" });\n        res.end(toPublicErrorInfo(err).message);\n      }\n      return;\n    }\n\n    if (urlPath === \"/__nix-js/render\") {\n      const renderUrl = new URL(req.url ?? \"/\", \"http://localhost\");\n      const page = renderUrl.searchParams.get(\"page\") ?? \"/\";\n      const search = renderUrl.searchParams.get(\"search\") ?? \"\";\n      const wantsJson = (req.headers[\"accept\"] ?? \"\").includes(\"application/json\");\n      try {\n        const request = incomingMessageToRequest(req);\n\n        // ISR: cache the real content served by this endpoint when a cache\n        // directory is configured, so streamed pages regenerate on a TTL.\n        let body: string;\n        let title: string;\n        let lastRenderedCookie: string | undefined;\n        let lastRenderedHead: string | undefined;\n        const ttl = await resolveTtl(options, page, routes);\n        const cacheKey = `/__nix-js/render${page}?${search}`;\n        if (options.cacheDir && typeof ttl === \"number\" && canUsePublicCache(request)) {\n          const cached = await getCachedHtml(options.cacheDir, cacheKey);\n          if (cached) {\n            body = extractBody(cached.html);\n            title = extractTitle(cached.html);\n          } else {\n            const rendered = await renderPageBody({\n              routes,\n              pathname: page,\n              searchParams: new URLSearchParams(search),\n              config: { lang: options.lang ?? \"es\", clientEntry: options.clientEntry },\n              actions: publicActions,\n              request,\n            });\n            body = rendered.body;\n            title = rendered.title;\n            lastRenderedCookie = rendered.clearActionErrorCookie;\n            lastRenderedHead = rendered.head;\n            await setCachedHtml(options.cacheDir, cacheKey, rendered.fullHtml ?? \"\", ttl);\n          }\n        } else {\n          const rendered = await renderPageBody({\n            routes,\n            pathname: page,\n            searchParams: new URLSearchParams(search),\n            config: { lang: options.lang ?? \"es\", clientEntry: options.clientEntry },\n            actions: publicActions,\n            request,\n          });\n          body = rendered.body;\n          title = rendered.title;\n          lastRenderedCookie = rendered.clearActionErrorCookie;\n          lastRenderedHead = rendered.head;\n        }\n\n        if (wantsJson) {\n          const headers: Record<string, string> = { \"Content-Type\": \"application/json; charset=utf-8\" };\n          // The SPA router applies the cookie via document.cookie so the next\n          // full reload does not re-feed stale errors to the page.\n          const setCookie = lastRenderedCookie;\n          if (setCookie) headers[\"X-Nix-Action-Clear-Cookie\"] = setCookie;\n          res.writeHead(200, headers);\n          res.end(JSON.stringify({ title, body, head: lastRenderedHead, clearActionErrorCookie: setCookie }));\n        } else {\n          const headers: Record<string, string> = { \"Content-Type\": \"text/html; charset=utf-8\" };\n          if (lastRenderedCookie) headers[\"Set-Cookie\"] = lastRenderedCookie;\n          res.writeHead(200, headers);\n          res.end(body);\n        }\n      } catch (err) {\n        if (err instanceof RouteNotFoundError) {\n          console.log(`[ssr] render endpoint: no route for ${page}`);\n          res.writeHead(404, { \"Content-Type\": \"text/plain\" });\n          res.end(\"Not Found\");\n          return;\n        }\n        console.error(\"[ssr] streaming render error\", err);\n        res.writeHead(500, { \"Content-Type\": \"text/plain\" });\n        res.end(\"Internal Server Error\");\n      }\n      return;\n    }\n\n    // Run middleware before routing (skip for internal endpoints handled above).\n    let middlewareHeaders: Record<string, string> | undefined;\n    if (middleware && matchesMiddleware(urlPath, middleware.config)) {\n      const mwResult = await runMiddleware(middleware, incomingMessageToRequest(req));\n      if (mwResult.kind === \"response\") {\n        res.writeHead(mwResult.response.status, Object.fromEntries(mwResult.response.headers.entries()));\n        res.end(Buffer.from(await mwResult.response.arrayBuffer()));\n        return;\n      }\n      middlewareHeaders = mwResult.headers;\n    }\n\n    // Try API routes first.\n    const apiMatch = matchApiRoute(urlPath, routes.api);\n    if (apiMatch) {\n      try {\n        const mod = (await import(apiMatch.route.routePath)) as Record<\n          string,\n          (request: Request, context?: { params: Record<string, string | string[]> }) => unknown\n        >;\n        const handler = mod[req.method ?? \"GET\"];\n        if (typeof handler !== \"function\") {\n          res.writeHead(405, { \"Content-Type\": \"text/plain\" });\n          res.end(`Method not allowed: ${req.method}`);\n          return;\n        }\n        const body = req.method && req.method !== \"GET\" && req.method !== \"HEAD\" ? await readRequestBody(req) : undefined;\n        const request = incomingMessageToRequest(req, body);\n        applyHeaders(request.headers, middlewareHeaders);\n        const response = (await handler(request, { params: apiMatch.params })) as Response;\n        res.writeHead(response.status, Object.fromEntries(response.headers.entries()));\n        res.end(Buffer.from(await response.arrayBuffer()));\n      } catch (err) {\n        console.error(\"[api] error handling\", urlPath, err);\n        res.writeHead(500, { \"Content-Type\": \"text/plain; charset=utf-8\" });\n        res.end(toPublicErrorInfo(err).message);\n      }\n      return;\n    }\n\n    // Try static files first.\n    if (options.publicDir) {\n      try {\n        const served = await tryServeStatic(res, options.publicDir, urlPath);\n        if (served) return;\n      } catch (err) {\n        console.error(\"[static] error serving\", urlPath, err);\n      }\n    }\n\n    // Try SSR page rendering.\n    const match = matchRoute(urlPath, routes.pages);\n    const config = { lang: options.lang ?? \"es\", clientEntry: options.clientEntry };\n    if (match) {\n      try {\n        const request = incomingMessageToRequest(req);\n        applyHeaders(request.headers, middlewareHeaders);\n\n        let html: string;\n        let clearActionErrorCookie: string | undefined;\n        const revalidate = match.route.dataPath\n          ? ((await import(match.route.dataPath)) as { revalidate?: number }).revalidate\n          : undefined;\n        const ttl = revalidate ?? options.defaultRevalidate;\n        const useStreaming = options.streaming !== false && match.route.loadingPath;\n        if (useStreaming) {\n          html = await renderStreamingPage({\n            route: match.route,\n            params: match.params,\n            searchParams: new URLSearchParams(req.url?.split(\"?\")[1] ?? \"\"),\n            config,\n            actions: publicActions,\n            request,\n          });\n        } else if (options.cacheDir && typeof ttl === \"number\" && canUsePublicCache(request)) {\n          const cacheKey = new URL(request.url).pathname + new URL(request.url).search;\n          const cached = await getCachedHtml(options.cacheDir, cacheKey);\n          if (cached) {\n            html = cached.html;\n          } else {\n            const result = await renderPage({\n              route: match.route,\n              params: match.params,\n              searchParams: new URLSearchParams(req.url?.split(\"?\")[1] ?? \"\"),\n              config,\n              actions: publicActions,\n              request,\n            });\n            html = result.html;\n            clearActionErrorCookie = result.clearActionErrorCookie;\n            await setCachedHtml(options.cacheDir, cacheKey, html, ttl);\n          }\n        } else {\n          const result = await renderPage({\n            route: match.route,\n            params: match.params,\n            searchParams: new URLSearchParams(req.url?.split(\"?\")[1] ?? \"\"),\n            config,\n            actions: publicActions,\n            request,\n          });\n          html = result.html;\n          clearActionErrorCookie = result.clearActionErrorCookie;\n        }\n        const responseHeaders: Record<string, string> = { \"Content-Type\": \"text/html; charset=utf-8\" };\n        if (clearActionErrorCookie) responseHeaders[\"Set-Cookie\"] = clearActionErrorCookie;\n        res.writeHead(200, responseHeaders);\n        res.end(html);\n        return;\n      } catch (err) {\n        console.error(\"[ssr] error rendering\", urlPath, err);\n        const errorResult = await renderErrorPage({\n          routes,\n          status: 500,\n          error: err,\n          config,\n          actions: publicActions,\n        });\n        if (errorResult) {\n          res.writeHead(errorResult.status, { \"Content-Type\": \"text/html; charset=utf-8\" });\n          res.end(errorResult.html);\n        } else {\n          res.writeHead(500, { \"Content-Type\": \"text/plain; charset=utf-8\" });\n          res.end(toPublicErrorInfo(err).message);\n        }\n        return;\n      }\n    }\n\n    const errorResult = await renderErrorPage({\n      routes,\n      status: 404,\n      config,\n      actions: publicActions,\n    });\n    if (errorResult) {\n      res.writeHead(errorResult.status, { \"Content-Type\": \"text/html; charset=utf-8\" });\n      res.end(errorResult.html);\n      return;\n    }\n\n    res.writeHead(404, { \"Content-Type\": \"text/plain; charset=utf-8\" });\n    res.end(`Not found: ${req.url}`);\n  });\n\n  return {\n    server,\n    listen() {\n      return new Promise((resolve) => {\n        server.listen(options.port ?? 3000, options.host ?? \"127.0.0.1\", () => {\n          console.log(\n            `\\n  → SSR server http://${options.host ?? \"127.0.0.1\"}:${options.port ?? 3000}`,\n          );\n          resolve();\n        });\n      });\n    },\n    close() {\n      return new Promise((resolve, reject) => {\n        server.close((err) => (err ? reject(err) : resolve()));\n      });\n    },\n  };\n}\n\nasync function tryServeStatic(\n  res: import(\"node:http\").ServerResponse,\n  publicDir: string,\n  urlPath: string,\n): Promise<boolean> {\n  const filePath = await resolveStaticFile(publicDir, urlPath);\n  if (!filePath) return false;\n  const contentType = guessContentType(filePath);\n  let data: Buffer | string = await readFile(filePath);\n  if (contentType.includes(\"text/html\")) {\n    // The SSG build bakes `render-endpoint content=\"off\"` into the static\n    // HTML so purely static deployments never probe the endpoint. This server\n    // (SSR `start`) DOES expose /__nix-js/render, so advertise it: SPA\n    // navigations then fetch live server-rendered content instead of the\n    // stale static file (e.g. after a mutating server action).\n    data = data\n      .toString(\"utf8\")\n      .replace(\n        '<meta name=\"nix-js:render-endpoint\" content=\"off\" />',\n        '<meta name=\"nix-js:render-endpoint\" content=\"on\" />',\n      );\n  }\n  res.writeHead(200, { \"Content-Type\": contentType, \"Content-Length\": Buffer.byteLength(data) });\n  res.end(data);\n  return true;\n}\n\nfunction canUsePublicCache(request: Request): boolean {\n  return !request.headers.has(\"cookie\") && !request.headers.has(\"authorization\");\n}\n\nfunction applyHeaders(headers: Headers, values: Record<string, string> | undefined): void {\n  if (!values) return;\n  for (const [name, value] of Object.entries(values)) headers.set(name, value);\n}\n\nfunction readRequestBody(req: IncomingMessage): Promise<string> {\n  return new Promise((resolve, reject) => {\n    let body = \"\";\n    req.setEncoding(\"utf8\");\n    req.on(\"data\", (chunk) => {\n      body += chunk;\n    });\n    req.on(\"end\", () => resolve(body));\n    req.on(\"error\", reject);\n  });\n}\n\nfunction guessContentType(filePath: string): string {\n  switch (extname(filePath)) {\n    case \".html\":\n      return \"text/html; charset=utf-8\";\n    case \".js\":\n    case \".mjs\":\n      return \"application/javascript; charset=utf-8\";\n    case \".css\":\n      return \"text/css; charset=utf-8\";\n    case \".json\":\n      return \"application/json; charset=utf-8\";\n    case \".svg\":\n      return \"image/svg+xml\";\n    case \".png\":\n      return \"image/png\";\n    case \".jpg\":\n    case \".jpeg\":\n      return \"image/jpeg\";\n    case \".webp\":\n      return \"image/webp\";\n    case \".avif\":\n      return \"image/avif\";\n    case \".ico\":\n      return \"image/x-icon\";\n    case \".woff\":\n      return \"font/woff\";\n    case \".woff2\":\n      return \"font/woff2\";\n    case \".wasm\":\n      return \"application/wasm\";\n    default:\n      return \"application/octet-stream\";\n  }\n}\n\n/**\n * Maps a concrete page path (e.g. `/movies/inception`) to the route pattern\n * key used by the action registry (e.g. `/movies/:slug`). Falls back to the\n * path itself when it matches an exact registry key.\n */\nexport function resolveActionPageKey(\n  page: string | undefined,\n  routes: Awaited<ReturnType<typeof scanRoutes>>,\n): string | undefined {\n  if (!page) return undefined;\n  if (routes.pages.some((route) => route.path === page)) return page;\n  const match = matchRoute(page, routes.pages);\n  return match ? match.route.path : page;\n}\n\n/** Resolves the ISR TTL for a page: route `revalidate` or the default. */\nasync function resolveTtl(\n  options: SsrServerOptions,\n  pathname: string,\n  routes: Awaited<ReturnType<typeof scanRoutes>>,\n): Promise<number | undefined> {\n  const match = matchRoute(pathname, routes.pages);\n  if (!match) return undefined;\n  const revalidate = match.route.dataPath\n    ? ((await import(match.route.dataPath)) as { revalidate?: number }).revalidate\n    : undefined;\n  return revalidate ?? options.defaultRevalidate;\n}\n\nfunction extractBody(fullHtml: string): string {\n  const match = fullHtml.match(/<div id=\"app\">([\\s\\S]*)<\\/div>\\s*(<script|$)/);\n  return match ? match[1].trim() : fullHtml;\n}\n\nfunction extractTitle(fullHtml: string): string {\n  const match = fullHtml.match(/<title>([^<]*)<\\/title>/);\n  return match ? match[1] : \"\";\n}\n","// --- Image pipeline: build-time variant generation with sharp (optional) ---\n//\n// When `sharp` is installed, the pipeline generates WebP/AVIF variants at\n// multiple widths for every image registered during the render pass. Variants\n// are written to the output directory with a content-based hash so they can\n// be cached indefinitely.\n//\n// When `sharp` is not installed, the pipeline is a no-op and `image()`\n// emits a plain `<img>` with srcset pointing to manually-provided files.\n\nimport { readFile, mkdir, stat } from \"node:fs/promises\";\nimport { join, dirname, extname, basename } from \"node:path\";\nimport { createHash } from \"node:crypto\";\nimport type { ImageFormat } from \"./index.js\";\n\nlet sharpLoader: (() => Promise<any>) | null | undefined;\n\nasync function loadSharp(): Promise<any | null> {\n  if (sharpLoader === null) return null;\n  if (sharpLoader) return sharpLoader();\n  try {\n    // @ts-ignore — `sharp` is an optional peer dependency.\n    const mod = await import(\"sharp\");\n    const sharp = mod.default;\n    if (typeof sharp !== \"function\") {\n      sharpLoader = null;\n      return null;\n    }\n    sharpLoader = async () => sharp;\n    return sharp;\n  } catch {\n    sharpLoader = null;\n    return null;\n  }\n}\n\nexport interface PipelineOptions {\n  /** Absolute path to the public directory (source images live here). */\n  publicDir: string;\n  /** Absolute path to the output directory (variants are written here). */\n  outDir: string;\n  /** Formats to generate. Defaults to [\"webp\", \"avif\"]. */\n  formats?: ImageFormat[];\n  /** Quality (1-100). Defaults to 80. */\n  quality?: number;\n}\n\nexport interface ProcessedImage {\n  /** Original source path (e.g. \"/images/hero.jpg\"). */\n  src: string;\n  /** Generated variant paths relative to outDir. */\n  variants: { path: string; width: number; format: ImageFormat }[];\n}\n\n/**\n * Processes a batch of images: for each image, generates variants at the\n * specified widths and formats using sharp. Returns the list of generated\n * files.\n *\n * If sharp is not installed, returns an empty array and logs a warning once.\n */\nexport async function processImages(\n  images: { src: string; widths: number[]; formats: ImageFormat[] }[],\n  options: PipelineOptions,\n): Promise<ProcessedImage[]> {\n  const sharp = await loadSharp();\n  if (!sharp) {\n    console.warn(\n      \"[nix-js-kit] Image optimization requires `sharp`. Install it with:\\n\" +\n      \"  npm install sharp\\n\" +\n      \"  # or\\n\" +\n      \"  bun add sharp\\n\" +\n      \"Skipping image processing — using original files.\",\n    );\n    return [];\n  }\n\n  const { publicDir, outDir, formats = [\"webp\", \"avif\"], quality = 80 } = options;\n  const results: ProcessedImage[] = [];\n  let warned = false;\n\n  for (const { src, widths, formats: imgFormats } of images) {\n    const sourcePath = join(publicDir, src.replace(/^\\//, \"\"));\n    try {\n      await stat(sourcePath);\n    } catch {\n      if (!warned) {\n        console.warn(`[nix-js-kit] Image not found: ${sourcePath}. Skipping.`);\n        warned = true;\n      }\n      continue;\n    }\n\n    const buffer = await readFile(sourcePath);\n    const hash = createHash(\"md5\").update(buffer).digest(\"hex\").slice(0, 8);\n    const ext = extname(src);\n    const base = basename(src, ext);\n    const dir = dirname(src);\n\n    const variants: { path: string; width: number; format: ImageFormat }[] = [];\n    const targetFormats = imgFormats.length > 0 ? imgFormats : formats;\n\n    for (const width of widths) {\n      for (const format of targetFormats) {\n        const variantName = `${base}-${width}w-${hash}.${format}`;\n        const variantRelPath = join(dir, variantName);\n        const variantAbsPath = join(outDir, variantRelPath.replace(/^\\//, \"\"));\n\n        try {\n          await mkdir(dirname(variantAbsPath), { recursive: true });\n          await sharp(buffer)\n            .resize({ width, withoutEnlargement: true })\n            .toFormat(format, { quality })\n            .toFile(variantAbsPath);\n          variants.push({ path: variantRelPath, width, format });\n        } catch (err) {\n          console.warn(`[nix-js-kit] Failed to generate ${variantName}:`, err);\n        }\n      }\n    }\n\n    if (variants.length > 0) {\n      results.push({ src, variants });\n    }\n  }\n\n  return results;\n}\n\n/**\n * Checks whether sharp is available without actually loading it.\n */\nexport async function isSharpAvailable(): Promise<boolean> {\n  const sharp = await loadSharp();\n  return sharp !== null;\n}\n","// --- Stream boundary (per-request, real Suspense streaming) ---\n//\n// `streamBoundary()` wraps a promise in a loading fallback. During SSR runtime,\n// the server emits the fallback HTML immediately, then streams a `<template>`\n// chunk with a replacement script that the browser executes to swap the\n// fallback for the resolved content in-place (real Suspense streaming).\n//\n// In SSG (build time), boundaries are resolved synchronously — the build waits\n// for all promises before writing the HTML, so no streaming occurs.\n//\n// Boundaries are tracked per-request via AsyncLocalStorage to avoid global\n// state leakage between concurrent requests.\n\nimport type { NixTemplate } from \"@deijose/nix-js\";\nimport { randomUUID } from \"node:crypto\";\nimport { AsyncLocalStorage } from \"node:async_hooks\";\n\nexport interface StreamBoundaryOptions<T> {\n  /** Fallback content shown while the promise resolves. */\n  fallback: NixTemplate;\n  /** Promise that resolves to a NixTemplate. */\n  promise: Promise<T>;\n  /** Renders the resolved value to a NixTemplate. */\n  children: (value: T) => NixTemplate;\n}\n\n/** Per-request boundary registry. */\ninterface BoundaryContext {\n  boundaries: Map<string, {\n    promise: Promise<unknown>;\n    children: (value: unknown) => NixTemplate;\n  }>;\n}\n\nconst boundaryALS = new AsyncLocalStorage<BoundaryContext>();\n\n/**\n * Gets the current per-request boundary context, if any.\n * Used by the streaming response to collect boundaries for later resolution.\n */\nexport function getCurrentBoundaryContext(): BoundaryContext | undefined {\n  return boundaryALS.getStore();\n}\n\n/**\n * Runs a function within a per-request boundary context.\n * Used by the SSR streaming pipeline to collect boundaries.\n */\nexport function withBoundaryContext<T>(fn: () => T): T {\n  const ctx: BoundaryContext = { boundaries: new Map() };\n  return boundaryALS.run(ctx, fn);\n}\n\n/**\n * Builds the fallback HTML wrapper for a boundary ID.\n * The fallback content is wrapped in a `<div>` with the boundary ID so the\n * browser can find it and replace it when the resolved content arrives.\n *\n * (v2.1 — Fix #4: real Suspense streaming with `<template>` replacement)\n */\nexport function buildFallbackHtml(boundaryId: string, fallbackHtml: string): string {\n  return `<div id=\"${boundaryId}\" style=\"display:contents\" data-nix-js-boundary=\"${boundaryId}\">${fallbackHtml}</div>`;\n}\n\n/**\n * Builds the resolved content chunk for a boundary ID.\n * Emits a `<template>` element with the resolved content, followed by a\n * `<script>` that replaces the fallback div with the template content\n * in-place. This is real Suspense streaming — the browser swaps the DOM\n * node without a full re-render.\n *\n * (v2.1 — Fix #4: real Suspense streaming with `<template>` replacement)\n */\nexport function buildResolvedChunk(boundaryId: string, resolvedHtml: string): string {\n  // Escape the resolved HTML for safe embedding inside a <template> tag.\n  // <template> content is inert (not parsed as DOM), so we store the raw\n  // HTML and clone it via `content.cloneNode(true)`.\n  return `<template id=\"${boundaryId}-tpl\">${resolvedHtml}</template>` +\n    `<script>(function(){` +\n    `var t=document.getElementById(${JSON.stringify(boundaryId + \"-tpl\")});` +\n    `var f=document.getElementById(${JSON.stringify(boundaryId)});` +\n    `if(t&&f){f.replaceWith(t.content.cloneNode(true));}` +\n    `document.dispatchEvent(new CustomEvent(\"nix-js:rendered\"));` +\n    `})();</script>`;\n}\n\n/**\n * Creates a stream boundary. During SSR, emits the fallback and registers the\n * promise for later resolution by the streaming pipeline. During SSG, the\n * build awaits all boundaries before writing HTML.\n *\n * The boundary ID is deterministic per-request via crypto.randomUUID().\n */\nexport function streamBoundary<T>(options: StreamBoundaryOptions<T>): NixTemplate {\n  const id = `nix-js-stream-${randomUUID().slice(0, 8)}`;\n  const ctx = boundaryALS.getStore();\n\n  // In SSR mode with a boundary context, register the promise for later.\n  if (ctx) {\n    ctx.boundaries.set(id, {\n      promise: options.promise,\n      children: options.children as (value: unknown) => NixTemplate,\n    });\n  }\n\n  return {\n    __isNixTemplate: true as const,\n    mount(container: Element | string) {\n      const el = typeof container === \"string\" ? document.querySelector(container) : container;\n      if (!el) throw new Error(\"[nix-js-kit] streamBoundary(): container not found\");\n      // Render fallback initially.\n      const handle = options.fallback.mount(el);\n      // Attempt to resolve and swap (works in both SSR and client).\n      options.promise\n        .then((value) => {\n          const content = options.children(value);\n          el.innerHTML = \"\";\n          const childHandle = content.mount(el);\n          // Store the new handle for cleanup.\n          (handle as any).__nixChildHandle = childHandle;\n        })\n        .catch((err) => {\n          console.error(`[nix-js-kit] streamBoundary ${id} failed:`, err);\n        });\n      return {\n        unmount() {\n          const childHandle = (handle as any).__nixChildHandle;\n          if (childHandle?.unmount) childHandle.unmount();\n          handle.unmount();\n        },\n      };\n    },\n    _render(parent: Node, before: Node | null): () => void {\n      // For SSR/build: render fallback inline. The promise resolution is\n      // handled by the streaming pipeline when available.\n      const dispose = options.fallback._render(parent, before);\n\n      // Kick off the promise resolution in the background.\n      options.promise\n        .then((value) => {\n          void value;\n        })\n        .catch((err) => {\n          console.error(`[nix-js-kit] streamBoundary ${id} failed:`, err);\n        });\n\n      return dispose;\n    },\n  } as unknown as NixTemplate;\n}\n","import { mkdir, rm, rename, stat, cp, access } from \"node:fs/promises\";\nimport { existsSync } from \"node:fs\";\nimport { join, resolve, dirname, relative } from \"node:path\";\nimport { build as viteBuild, type InlineConfig, type PluginOption } from \"vite\";\nimport { nixJsInterpolationPlugin, shouldUseLegacyInterpolation, type InterpolationMode } from \"../vite/interpolation-plugin.js\";\n\n// --- Programmatic Vite build orchestration ---\n//\n// Replaces the previous `spawnSync(\"npx\", [\"vite\", \"build\", ...])` approach\n// with direct use of the Vite JavaScript API. Benefits:\n//\n//   * No child-process overhead or `npx` resolution latency.\n//   * Shared module cache across build phases (faster large builds).\n//   * Structured errors instead of exit-code parsing.\n//   * Atomic output staging: build into a temp directory, then rename to the\n//     final destination so a crashed build never leaves a half-written dist.\n\nexport interface ClientBuildOptions {\n  /** Project root (absolute). */\n  root: string;\n  /** Absolute path to the user's Vite client config (e.g. vite.client.config.ts). */\n  userConfigPath: string;\n  /** Absolute path to the app directory (used by the interpolation plugin). */\n  appDir: string;\n  /** Absolute path to the islands directory (used by the interpolation plugin). */\n  islandsDir: string;\n  /** Output directory for the client bundle (absolute). */\n  outDir: string;\n  /** Optional base path. */\n  base?: string;\n  /** Optional log prefix. */\n  logPrefix?: string;\n  /**\n   * How the legacy interpolation transform is handled (default: \"auto\").\n   * With a Nix.js core that supports partial attribute interpolation natively\n   * the transform is not applied; use \"legacy\" for migrations against older\n   * cores and \"off\" to never transform.\n   */\n  interpolation?: InterpolationMode;\n}\n\nexport interface ClientBuildResult {\n  /** Output directory (same as `outDir` input). */\n  outDir: string;\n  /** Number of chunks/assets emitted, if reported by Vite. */\n  outputCount: number;\n}\n\n/**\n * Build the client hydration bundle using the Vite JavaScript API.\n *\n * The user's config is loaded programmatically and the nix-js interpolation\n * plugin is injected so partial attribute interpolations inside islands are\n * transformed before reaching the browser.\n */\nexport async function buildClientBundle(options: ClientBuildOptions): Promise<ClientBuildResult> {\n  const log = options.logPrefix ?? \"[client]\";\n  console.log(`${log} Building hydration bundle...`);\n\n  const userConfig = await loadUserConfig(options.userConfigPath, options.root);\n  const pluginOptions: PluginOption = shouldUseLegacyInterpolation(options.interpolation ?? \"auto\")\n    ? nixJsInterpolationPlugin({\n        appDir: options.appDir,\n        islandsDir: options.islandsDir,\n      })\n    : [];\n\n  const config: InlineConfig = {\n    ...userConfig,\n    root: options.root,\n    base: options.base ?? userConfig.base ?? \"/\",\n    build: {\n      ...(userConfig.build ?? {}),\n      outDir: options.outDir,\n      emptyOutDir: true,\n    },\n    plugins: [...(userConfig.plugins ?? []), pluginOptions],\n    configFile: false,\n  };\n\n  const result = await viteBuild(config);\n  const outputs = Array.isArray(result) ? result : [result];\n  const outputCount = outputs.reduce(\n    (n, r) => n + (\"output\" in r ? (r.output?.length ?? 0) : 0),\n    0,\n  );\n  console.log(`${log} ✓ ${outputCount} asset(s) emitted → ${relative(options.root, options.outDir)}`);\n  return { outDir: options.outDir, outputCount };\n}\n\nasync function loadUserConfig(path: string, _root: string): Promise<InlineConfig> {\n  const mod = await import(path);\n  const raw = mod.default ?? mod;\n  const resolved = typeof raw === \"function\" ? await raw({ command: \"build\", mode: \"production\" }) : raw;\n  return (resolved && typeof resolved.then === \"function\" ? await resolved : resolved) ?? {};\n}\n\n// --- Atomic output staging ---\n\nexport interface AtomicStageOptions {\n  /** Final destination directory (absolute). */\n  outDir: string;\n  /** Build into this temp directory first, then rename to `outDir`. */\n  tempDir?: string;\n  /** Whether to preserve existing content in `outDir` during the swap. */\n  keepExisting?: boolean;\n}\n\nexport interface AtomicStage {\n  tempDir: string;\n  /** Call after the build succeeds to atomically swap temp → outDir. */\n  commit: () => Promise<void>;\n  /** Call on failure to clean up the temp directory. */\n  rollback: () => Promise<void>;\n}\n\n/**\n * Prepare an atomic staging directory for build output.\n *\n * Usage:\n *   const stage = await beginAtomicStage({ outDir });\n *   try {\n *     await buildInto(stage.tempDir);\n *     await stage.commit();\n *   } catch (err) {\n *     await stage.rollback();\n *     throw err;\n *   }\n */\nexport async function beginAtomicStage(options: AtomicStageOptions): Promise<AtomicStage> {\n  const outDir = resolve(options.outDir);\n  const tempDir = resolve(options.tempDir ?? join(dirname(outDir), `.${basename(outDir)}.tmp-${process.pid}`));\n\n  // Start from a clean temp directory.\n  await rm(tempDir, { recursive: true, force: true });\n  await mkdir(tempDir, { recursive: true });\n\n  const commit = async () => {\n    // Backup the existing output if requested, then swap.\n    const backup = options.keepExisting && existsSync(outDir) ? `${outDir}.bak-${process.pid}` : undefined;\n    if (backup) {\n      await rm(backup, { recursive: true, force: true });\n      await safeRename(outDir, backup);\n    }\n    try {\n      await safeRename(tempDir, outDir);\n    } catch (err) {\n      // On some platforms, renaming across mount points fails. Fall back to a\n      // recursive copy + clean, which is not atomic but still correct.\n      if (isCrossDevice(err)) {\n        await cp(tempDir, outDir, { recursive: true, force: true });\n        await rm(tempDir, { recursive: true, force: true });\n      } else {\n        if (backup) await safeRename(backup, outDir);\n        throw err;\n      }\n    }\n    if (backup) await rm(backup, { recursive: true, force: true });\n  };\n\n  const rollback = async () => {\n    await rm(tempDir, { recursive: true, force: true });\n  };\n\n  return { tempDir, commit, rollback };\n}\n\nfunction basename(path: string): string {\n  const parts = path.split(/[\\\\/]+/).filter(Boolean);\n  return parts[parts.length - 1] ?? \"output\";\n}\n\nasync function safeRename(src: string, dest: string): Promise<void> {\n  await rm(dest, { recursive: true, force: true });\n  try {\n    await rename(src, dest);\n  } catch (err) {\n    if (isCrossDevice(err)) {\n      await cp(src, dest, { recursive: true, force: true });\n      await rm(src, { recursive: true, force: true });\n    } else {\n      throw err;\n    }\n  }\n}\n\nfunction isCrossDevice(err: unknown): boolean {\n  const code = (err as NodeJS.ErrnoException)?.code;\n  return code === \"EXDEV\";\n}\n\n// --- Public asset copy ---\n\nexport interface CopyPublicAssetsOptions {\n  /** Absolute path to the public directory. */\n  publicDir: string;\n  /** Absolute path to the output directory. */\n  outDir: string;\n}\n\n/**\n * Copy the public directory into the output directory.\n * Returns the number of files copied.\n */\nexport async function copyPublicAssets(options: CopyPublicAssetsOptions): Promise<number> {\n  try {\n    await access(options.publicDir);\n    const s = await stat(options.publicDir);\n    if (!s.isDirectory()) return 0;\n  } catch {\n    return 0;\n  }\n  await mkdir(options.outDir, { recursive: true });\n  await cp(options.publicDir, options.outDir, { recursive: true, force: true });\n  return countFiles(options.outDir);\n}\n\nasync function countFiles(dir: string): Promise<number> {\n  const { readdir } = await import(\"node:fs/promises\");\n  let count = 0;\n  async function walk(d: string): Promise<void> {\n    const entries = await readdir(d, { withFileTypes: true });\n    for (const entry of entries) {\n      const path = join(d, entry.name);\n      if (entry.isDirectory()) await walk(path);\n      else count++;\n    }\n  }\n  await walk(dir);\n  return count;\n}\n"],"mappings":"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"}