/** * `deepspace/build` — build-time helpers an app's Vite config imports. * * Node-only. Nothing here may be reached from client or worker code; it runs * while `vite build` / `vite dev` is assembling the bundle, never inside it. * * The one job today is resolving the app's immutable id for the browser. * `wrangler.toml` is the single source of truth for app identity — the worker * reads `DEEPSPACE_APP_ID` off `env`, and every durable thing (data scope * `app:{appId}`, secrets, billing) keys to it. The browser has no `env`, so * the build stamps the same value in as `__DEEPSPACE_APP_ID__`, read from the * config THIS build is running against. A literal baked into the client once, * at scaffold time, is what let `deepspace deploy --env staging` ship a worker * writing staging's rooms beside a browser reading production's, with nothing * reporting the split. * * Living in the SDK rather than in a file copied into every app means one * definition, no per-app duplicate, and `smol-toml` stays the SDK's dependency * instead of every app's. */ /** * The build's environment, typed structurally. This module is imported from an * app's `vite.config.ts`, which sits outside the app's tsconfig `include` and * runs where the app carries no `@types/node`, so nothing here leans on the * ambient `NodeJS` namespace. */ type BuildEnv = Record; interface ResolveAppIdOptions { /** * The app root — the directory holding `wrangler.toml`. The caller passes * its own location (`fileURLToPath(new URL('.', import.meta.url))` from * `vite.config.ts`), so the config is found by an authoritative anchor * rather than a guessed `process.cwd()`. */ appDir: string; /** Defaults to `process.env`; injectable for tests. */ env?: BuildEnv; } /** * Resolve the app id the browser bundle must carry. * * Mirrors the Cloudflare Vite plugin so dev, a bare `vite build`, and * `deepspace deploy --env ` all agree: * * config ← CLOUDFLARE_VITE_WRANGLER_CONFIG_PATH, else /wrangler.toml * section ← [env..vars] when that is set, else [vars] * * Env blocks do NOT inherit `[vars]` — each environment is its own app — so a * missing or malformed id throws and fails the build. It never falls back to * another environment's id: a fallback that silently picks the wrong one is * exactly the bug this exists to remove. */ declare function resolveAppId({ appDir, env }: ResolveAppIdOptions): string; /** * Vite `define` entry consumed by an app's `vite.config.ts` and * `vitest.config.ts`, so unit tests see the same id the bundle is built with: * * ```ts * import { fileURLToPath } from 'node:url' * import { appIdDefine } from 'deepspace/build' * export default defineConfig({ * define: appIdDefine({ appDir: fileURLToPath(new URL('.', import.meta.url)) }), * }) * ``` */ declare function appIdDefine(options: ResolveAppIdOptions): Record; /** * `deepspaceBuild()` — the one Vite plugin an app's build config needs. * * It carries the build-time wiring that is identical in every DeepSpace app * and belongs to the SDK's contract rather than to app code: * * 1. the app id `define` (see ./app-id) — so the browser is keyed to the * environment THIS build targets, never a literal frozen at scaffold time; * 2. removing the preview `.dev.vars` the Cloudflare plugin drops beside the * built worker — DeepSpace has one local runtime, so that second plaintext * copy is not kept (`removeBuildDevVars` below; `deepspace deploy` calls * the same function on the artifact it ships); * 3. the client `dedupe` hint — two copies of React (or better-auth) break * the SDK's hooks, and which packages must be single-instance is the SDK's * knowledge, not the app's. * * Housing these here means a future fix to any of them ships in an SDK version * bump, with no edit to — and no migration of — a single app. * * The return is a structural Vite plugin: typing it against `vite` would make * the SDK carry a Vite dependency it otherwise does not need, and Vite's * plugin hooks are optional and method-shaped, so a compatible object is * assignable to `PluginOption` where an app composes it. */ /** * Packages that must resolve to a single instance in the client graph. Two * Reacts throw "Cannot read properties of null (reading 'useState')"; two * better-auth clients split the session. The SDK owns this list because it is * the SDK's hooks and auth client that break when it is wrong. */ declare const CLIENT_DEDUPE: readonly string[]; /** The subset of Vite's `Plugin` this helper implements — declared locally so * `deepspace/build` needs no `vite` dependency. Assignable to `PluginOption`. */ interface VitePluginLike { name: string; enforce?: 'pre' | 'post'; config(): { define: Record; resolve: { dedupe: string[]; }; }; closeBundle(): void; } type DeepspaceBuildOptions = ResolveAppIdOptions; declare function deepspaceBuild(options: DeepspaceBuildOptions): VitePluginLike; export { type BuildEnv, CLIENT_DEDUPE, type DeepspaceBuildOptions, type ResolveAppIdOptions, appIdDefine, deepspaceBuild, resolveAppId };