@slip-stream-kit/vite 0.3.3 → 0.3.13

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.
package/dist/index.js CHANGED
@@ -103,7 +103,8 @@ var mergeServerConfig = (resolved, user) => {
103
103
  if (user?.strictPort == null && resolved.strictPort != null) server.strictPort = resolved.strictPort;
104
104
  }
105
105
  if (user?.host == null && resolved.host != null) server.host = resolved.host;
106
- if (user?.hmr == null && resolved.hmr != null) server.hmr = resolved.hmr;
106
+ const resolvedWs = resolved.ws ?? resolved.hmr;
107
+ if (user?.ws == null && user?.hmr == null && resolvedWs != null) server.ws = resolvedWs;
107
108
  const proxy = withoutUserRoutes(resolved.proxy, user?.proxy);
108
109
  if (Object.keys(proxy).length > 0) server.proxy = proxy;
109
110
  return server;
package/dist/index.js.map CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/lib/plugin/plugin.ts", "../src/lib/dev-context/dev-context.ts", "../src/lib/server-config/server-config.ts"],
4
- "sourcesContent": ["import type { InfraKitDevOptions } from '@slip-stream-kit/config/vite'\nimport { infraKitDev } from '@slip-stream-kit/config/vite'\nimport process from 'node:process'\nimport type { Plugin, ViteDevServer } from 'vite'\n\nimport type { RestartableServer } from '../dev-context/dev-context'\nimport { proxySignature, watchDevContext } from '../dev-context/dev-context'\nimport { hasPinnedPortConflict, mergeServerConfig } from '../server-config/server-config'\n\n/**\n * Everything `infraKitDev()` accepts, minus `command` \u2014 the plugin knows vite's command without being\n * told (see {@link infraKit}), and accepting it would only let a consumer contradict it.\n */\nexport interface InfraKitPluginOptions extends Omit<InfraKitDevOptions, 'command'> {\n /**\n * Re-resolve the proxy and restart the dev server when the `.infra-kit/dev-context` fragments change,\n * so a backend started AFTER the frontend flips its route from `cloud` to `local` on its own. Default\n * `true`. Set `false` to freeze the proxy at whatever it resolved to when vite booted.\n */\n restartOnDevContextChange?: boolean\n}\n\n/** Adapt a real vite dev server to the narrow surface {@link watchDevContext} drives. */\nconst asRestartable = (server: ViteDevServer): RestartableServer => {\n return {\n watcher: {\n add: (target) => {\n server.watcher.add(target)\n },\n on: (event, listener) => {\n server.watcher.on(event, (file: string) => {\n listener(file)\n })\n },\n },\n restart: async () => {\n await server.restart()\n },\n warn: (message) => {\n server.config.logger.warn(message)\n },\n }\n}\n\n/**\n * The infra-kit vite plugin: a per-worktree dev port (or the one `infra-kit dev` assigned this UI),\n * HMR pointed at the portless HTTPS alias, and the `dev.proxy` map from the package's\n * `infra-kit.config.ts` \u2014 resolved live, and re-resolved whenever the local dev set changes.\n *\n * `apply: 'serve'` is load-bearing, not tidiness. Vite filters plugins by `apply` BEFORE it runs their\n * `config` hooks, so on `build` this plugin does not exist at all. The helper it wraps had to be told\n * (`infraKitDev({ command })`) and would otherwise fail-fast on a cloud route with no sourced env \u2014 a\n * failure whose only cause was a consumer forgetting to thread an argument. That class of bug is gone.\n *\n * @example\n * // vite.config.ts\n * import { infraKit } from '@slip-stream-kit/vite'\n * import { defineConfig } from 'vite'\n *\n * export default defineConfig({ plugins: [infraKit()] })\n */\nexport const infraKit = (options: InfraKitPluginOptions = {}): Plugin => {\n const cwd = options.cwd ?? process.cwd()\n\n let signature = proxySignature({})\n let pinnedPortConflict = false\n let dispose: () => void = () => {}\n\n return {\n name: 'infra-kit',\n apply: 'serve',\n\n config: async (userConfig) => {\n const resolved = await infraKitDev({ ...options, cwd, command: 'serve' })\n\n signature = proxySignature(resolved.proxy)\n pinnedPortConflict = hasPinnedPortConflict(resolved, userConfig.server)\n\n return { server: mergeServerConfig(resolved, userConfig.server) }\n },\n\n configureServer: (server) => {\n if (pinnedPortConflict) {\n server.config.logger.warn(\n '[infra-kit] server.port is pinned in this vite config, but `infra-kit dev` assigned this UI a ' +\n 'different port and registered its portless alias against THAT one. The pin wins \u2014 and the ' +\n 'hero URL will 502. Remove server.port to let the runner place it.',\n )\n }\n\n if (options.restartOnDevContextChange === false) return\n\n dispose = watchDevContext({ server: asRestartable(server), cwd, options, current: signature }).dispose\n },\n\n // The dev server calls this on close (including on the restart the watcher itself triggers), so the\n // pending debounce of a server that is going away never fires against the one replacing it.\n buildEnd: () => {\n dispose()\n dispose = () => {}\n },\n }\n}\n", "import type { InfraKitDevOptions, InfraKitViteProxy } from '@slip-stream-kit/config/vite'\nimport { infraKitDev } from '@slip-stream-kit/config/vite'\nimport fs from 'node:fs'\nimport path from 'node:path'\n\n/** Repo-relative dev-context fragment directory the `infra-kit dev` runner writes into. */\nconst DEV_CONTEXT_DIR = path.join('.infra-kit', 'dev-context')\n\n/** The state directory that holds {@link DEV_CONTEXT_DIR} \u2014 watched too, so a first fragment is seen. */\nconst STATE_DIR = '.infra-kit'\n\n/** The package's own config: editing `dev.proxy` must take effect without a manual restart. */\nconst PACKAGE_CONFIG_FILE = 'infra-kit.config.ts'\n\n/** Coalesce the burst of writes a runner makes when it brings several packages up at once. */\nconst DEBOUNCE_MS = 150\n\n/**\n * A dummy `port` passed to the re-resolve. Only `proxy` is read from the result, and an omitted port\n * makes the helper probe (bind + release) a fresh free one \u2014 a pointless side effect to repeat on every\n * fragment write. Any number suppresses it.\n */\nconst PROBE_PORT = 1\n\n/** Search upward from `start` for `relative`, returning the first hit or undefined. */\nconst findUp = (start: string, relative: string): string | undefined => {\n let dir = path.resolve(start)\n\n for (;;) {\n const candidate = path.join(dir, relative)\n\n if (fs.existsSync(candidate)) return candidate\n\n const parent = path.dirname(dir)\n\n if (parent === dir) return undefined\n\n dir = parent\n }\n}\n\n/** The paths whose changes can alter this package's proxy map. */\nexport interface WatchTargets {\n /** The dev-context fragment dir, or `undefined` when no repo root could be located. */\n fragmentDir: string | undefined\n /** The dir to watch so the fragment dir is seen even when it does not exist yet. */\n stateDir: string | undefined\n /** This package's own `infra-kit.config.ts` (watched whether or not it exists yet). */\n configFile: string\n}\n\n/**\n * Where to watch, resolved from the package dir.\n *\n * The fragment dir is found by searching upward \u2014 but it may legitimately NOT EXIST yet: a frontend\n * started before any backend has none, and that is exactly the case the live re-resolve exists to fix.\n * So fall back to the `.infra-kit` state dir (also searched upward), then to the git root, and let the\n * watcher pick up the directory when the runner creates it. With no repo root at all there is nothing\n * to watch and the plugin stays inert.\n */\nexport const resolveWatchTargets = (cwd: string): WatchTargets => {\n const configFile = path.join(cwd, PACKAGE_CONFIG_FILE)\n const existing = findUp(cwd, DEV_CONTEXT_DIR)\n\n if (existing) return { fragmentDir: existing, stateDir: path.dirname(existing), configFile }\n\n const state = findUp(cwd, STATE_DIR)\n\n if (state) return { fragmentDir: path.join(state, 'dev-context'), stateDir: state, configFile }\n\n const gitDir = findUp(cwd, '.git')\n\n if (!gitDir) return { fragmentDir: undefined, stateDir: undefined, configFile }\n\n const root = path.dirname(gitDir)\n\n return { fragmentDir: path.join(root, DEV_CONTEXT_DIR), stateDir: path.join(root, STATE_DIR), configFile }\n}\n\n/** Order-independent, field-stable digest of a resolved proxy map. */\nexport const proxySignature = (proxy: InfraKitViteProxy): string => {\n const entries = Object.entries(proxy)\n .map(([routePath, entry]) => {\n const fields = Object.entries(entry as unknown as Record<string, unknown>).sort(([a], [b]) => {\n return a < b ? -1 : 1\n })\n\n return [routePath, fields] as const\n })\n .sort(([a], [b]) => {\n return a < b ? -1 : 1\n })\n\n return JSON.stringify(entries)\n}\n\n/**\n * The slice of `ViteDevServer` this module needs. Narrowed to an interface so the watch loop is\n * driveable by a fake in tests \u2014 booting a real vite dev server to assert \"it restarted once\" would\n * test vite, not this.\n */\nexport interface RestartableServer {\n watcher: {\n add: (target: string) => void\n on: (event: 'add' | 'change' | 'unlink' | 'addDir', listener: (file: string) => void) => void\n }\n restart: () => Promise<void>\n warn: (message: string) => void\n}\n\nexport interface WatchDevContextArgs {\n server: RestartableServer\n /** The package dir whose config + proxy are resolved (the plugin's `cwd`). */\n cwd: string\n /** The options the plugin was constructed with, re-applied on every re-resolve. */\n options: InfraKitDevOptions\n /** The signature of the proxy map vite is currently serving (taken in the `config` hook). */\n current: string\n}\n\n/**\n * Re-resolve the proxy on every dev-context write, and restart vite when \u2014 and ONLY when \u2014 the resolved\n * proxy actually changed.\n *\n * This is the whole reason the plugin exists as a plugin. `infraKitDev()` resolves once, while vite is\n * computing its config, so a backend started AFTER the frontend can never flip its route from `cloud` to\n * `local`: the answer was already baked. Watching the fragments closes that, and a `local \u2192 cloud`\n * demotion (a backend that died) heals the same way.\n *\n * The signature compare is not an optimisation, it is the loop guard. The runner rewrites its fragments\n * on every restart of its own (fresh `pid`, fresh `writtenAt`) and writes a fragment for THIS UI too, so\n * a watcher that restarted on any write would restart on writes caused by its own restart. Comparing the\n * resolved PROXY \u2014 the only thing a restart would change \u2014 makes those writes inert by construction.\n */\nexport const watchDevContext = (args: WatchDevContextArgs): { dispose: () => void } => {\n const { server, cwd, options, current } = args\n const { fragmentDir, stateDir, configFile } = resolveWatchTargets(cwd)\n\n if (!fragmentDir || !stateDir) return { dispose: () => {} }\n\n let signature = current\n let timer: NodeJS.Timeout | undefined\n\n const recompute = async (): Promise<void> => {\n let proxy: InfraKitViteProxy\n\n try {\n proxy = (await infraKitDev({ ...options, cwd, command: 'serve', port: PROBE_PORT })).proxy\n } catch (error) {\n // A half-written fragment, or a `dev.proxy` edit mid-save, resolves to an error. It is transient by\n // nature \u2014 the next write re-runs this \u2014 so warn and keep serving the proxy we already have rather\n // than tearing the dev server down over it.\n server.warn(`[infra-kit] dev-context changed but the proxy could not be re-resolved: ${String(error)}`)\n\n return\n }\n\n const next = proxySignature(proxy)\n\n if (next === signature) return\n\n signature = next\n await server.restart()\n }\n\n const schedule = (): void => {\n clearTimeout(timer)\n timer = setTimeout(() => {\n void recompute()\n }, DEBOUNCE_MS)\n }\n\n const isRelevant = (file: string): boolean => {\n return file === configFile || file === fragmentDir || file.startsWith(fragmentDir + path.sep)\n }\n\n server.watcher.add(stateDir)\n server.watcher.add(configFile)\n\n for (const event of ['add', 'change', 'unlink', 'addDir'] as const) {\n server.watcher.on(event, (file: string) => {\n if (isRelevant(file)) schedule()\n })\n }\n\n return {\n dispose: () => {\n clearTimeout(timer)\n },\n }\n}\n", "import type { InfraKitViteProxy } from '@slip-stream-kit/config/vite'\n\n/**\n * What `infraKitDev()` resolves: the ready-made vite `server` block. Re-declared structurally rather\n * than imported, because the helper types it inline on its return type and there is no named export\n * to reach for.\n */\nexport interface ResolvedDevServer {\n port?: number\n host?: string | boolean\n strictPort?: boolean\n hmr?: { protocol: 'wss'; host: string; clientPort: number }\n proxy: InfraKitViteProxy\n}\n\n/**\n * The subset of a consumer's `server` block this plugin can collide with. Structurally compatible with\n * vite's `ServerOptions`, so the plugin passes `config.server` straight in without a cast.\n */\nexport interface UserServerConfig {\n port?: number\n host?: string | boolean\n strictPort?: boolean\n hmr?: unknown\n proxy?: Record<string, unknown>\n}\n\n/** The `server` block this plugin contributes \u2014 every key optional, because every key is conditional. */\nexport interface InfraKitServerConfig {\n port?: number\n host?: string | boolean\n strictPort?: boolean\n hmr?: ResolvedDevServer['hmr']\n proxy?: InfraKitViteProxy\n}\n\n/** Drop every route the consumer declared themselves \u2014 an explicit route in their config outranks ours. */\nconst withoutUserRoutes = (\n proxy: InfraKitViteProxy,\n userProxy: Record<string, unknown> | undefined,\n): InfraKitViteProxy => {\n if (!userProxy) return proxy\n\n const result: InfraKitViteProxy = {}\n\n for (const [routePath, entry] of Object.entries(proxy)) {\n if (routePath in userProxy) continue\n result[routePath] = entry\n }\n\n return result\n}\n\n/**\n * The `server` block to return from the `config` hook, given what `infraKitDev()` resolved and what the\n * consumer wrote themselves.\n *\n * This exists because of ONE fact about vite: a `config` hook's result is merged **over** the user config\n * (`mergeConfig(userConfig, pluginResult)`), so anything emitted here WINS against an explicit setting in\n * the consumer's own `vite.config.ts`. A plugin that just returns everything `infraKitDev()` produced\n * would therefore silently overrule a hand-pinned `server.port` \u2014 the exact opposite of what a plugin\n * should do, and worse than the `server: await infraKitDev()` spread it replaces (there, at least, the\n * consumer could see the assignment).\n *\n * So: emit only what the consumer left unset. `strictPort` rides with `port` \u2014 it is meaningless applied\n * to someone else's port, and forcing it onto a hand-pinned one would turn a soft collision into a hard\n * boot failure.\n */\nexport const mergeServerConfig = (\n resolved: ResolvedDevServer,\n user: UserServerConfig | undefined,\n): InfraKitServerConfig => {\n const server: InfraKitServerConfig = {}\n\n if (user?.port == null) {\n if (resolved.port != null) server.port = resolved.port\n if (user?.strictPort == null && resolved.strictPort != null) server.strictPort = resolved.strictPort\n }\n\n if (user?.host == null && resolved.host != null) server.host = resolved.host\n if (user?.hmr == null && resolved.hmr != null) server.hmr = resolved.hmr\n\n const proxy = withoutUserRoutes(resolved.proxy, user?.proxy)\n\n if (Object.keys(proxy).length > 0) server.proxy = proxy\n\n return server\n}\n\n/**\n * True when the consumer pinned `server.port` while `infra-kit dev` had already ASSIGNED this UI a port\n * (`strictPort` on the resolved block is the runner's fingerprint \u2014 only the Layer-B path sets it).\n *\n * Honouring the pin is still right (it is explicit), but it is not free: the runner registered a portless\n * alias pointing at the port it assigned, and the UI is about to bind a different one. The hero URL then\n * 502s, and nothing in that failure names the pinned port as the cause. Worth one warning line.\n */\nexport const hasPinnedPortConflict = (resolved: ResolvedDevServer, user: UserServerConfig | undefined): boolean => {\n return user?.port != null && resolved.strictPort === true && user.port !== resolved.port\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import type { InfraKitDevOptions } from '@slip-stream-kit/config/vite'\nimport { infraKitDev } from '@slip-stream-kit/config/vite'\nimport process from 'node:process'\nimport type { Plugin, ViteDevServer } from 'vite'\n\nimport type { RestartableServer } from '../dev-context/dev-context'\nimport { proxySignature, watchDevContext } from '../dev-context/dev-context'\nimport { hasPinnedPortConflict, mergeServerConfig } from '../server-config/server-config'\n\n/**\n * Everything `infraKitDev()` accepts, minus `command` \u2014 the plugin knows vite's command without being\n * told (see {@link infraKit}), and accepting it would only let a consumer contradict it.\n */\nexport interface InfraKitPluginOptions extends Omit<InfraKitDevOptions, 'command'> {\n /**\n * Re-resolve the proxy and restart the dev server when the `.infra-kit/dev-context` fragments change,\n * so a backend started AFTER the frontend flips its route from `cloud` to `local` on its own. Default\n * `true`. Set `false` to freeze the proxy at whatever it resolved to when vite booted.\n */\n restartOnDevContextChange?: boolean\n}\n\n/** Adapt a real vite dev server to the narrow surface {@link watchDevContext} drives. */\nconst asRestartable = (server: ViteDevServer): RestartableServer => {\n return {\n watcher: {\n add: (target) => {\n server.watcher.add(target)\n },\n on: (event, listener) => {\n server.watcher.on(event, (file: string) => {\n listener(file)\n })\n },\n },\n restart: async () => {\n await server.restart()\n },\n warn: (message) => {\n server.config.logger.warn(message)\n },\n }\n}\n\n/**\n * The infra-kit vite plugin: a per-worktree dev port (or the one `infra-kit dev` assigned this UI),\n * HMR pointed at the portless HTTPS alias, and the `dev.proxy` map from the package's\n * `infra-kit.config.ts` \u2014 resolved live, and re-resolved whenever the local dev set changes.\n *\n * `apply: 'serve'` is load-bearing, not tidiness. Vite filters plugins by `apply` BEFORE it runs their\n * `config` hooks, so on `build` this plugin does not exist at all. The helper it wraps had to be told\n * (`infraKitDev({ command })`) and would otherwise fail-fast on a cloud route with no sourced env \u2014 a\n * failure whose only cause was a consumer forgetting to thread an argument. That class of bug is gone.\n *\n * @example\n * // vite.config.ts\n * import { infraKit } from '@slip-stream-kit/vite'\n * import { defineConfig } from 'vite'\n *\n * export default defineConfig({ plugins: [infraKit()] })\n */\nexport const infraKit = (options: InfraKitPluginOptions = {}): Plugin => {\n const cwd = options.cwd ?? process.cwd()\n\n let signature = proxySignature({})\n let pinnedPortConflict = false\n let dispose: () => void = () => {}\n\n return {\n name: 'infra-kit',\n apply: 'serve',\n\n config: async (userConfig) => {\n const resolved = await infraKitDev({ ...options, cwd, command: 'serve' })\n\n signature = proxySignature(resolved.proxy)\n pinnedPortConflict = hasPinnedPortConflict(resolved, userConfig.server)\n\n return { server: mergeServerConfig(resolved, userConfig.server) }\n },\n\n configureServer: (server) => {\n if (pinnedPortConflict) {\n server.config.logger.warn(\n '[infra-kit] server.port is pinned in this vite config, but `infra-kit dev` assigned this UI a ' +\n 'different port and registered its portless alias against THAT one. The pin wins \u2014 and the ' +\n 'hero URL will 502. Remove server.port to let the runner place it.',\n )\n }\n\n if (options.restartOnDevContextChange === false) return\n\n dispose = watchDevContext({ server: asRestartable(server), cwd, options, current: signature }).dispose\n },\n\n // The dev server calls this on close (including on the restart the watcher itself triggers), so the\n // pending debounce of a server that is going away never fires against the one replacing it.\n buildEnd: () => {\n dispose()\n dispose = () => {}\n },\n }\n}\n", "import type { InfraKitDevOptions, InfraKitViteProxy } from '@slip-stream-kit/config/vite'\nimport { infraKitDev } from '@slip-stream-kit/config/vite'\nimport fs from 'node:fs'\nimport path from 'node:path'\n\n/** Repo-relative dev-context fragment directory the `infra-kit dev` runner writes into. */\nconst DEV_CONTEXT_DIR = path.join('.infra-kit', 'dev-context')\n\n/** The state directory that holds {@link DEV_CONTEXT_DIR} \u2014 watched too, so a first fragment is seen. */\nconst STATE_DIR = '.infra-kit'\n\n/** The package's own config: editing `dev.proxy` must take effect without a manual restart. */\nconst PACKAGE_CONFIG_FILE = 'infra-kit.config.ts'\n\n/** Coalesce the burst of writes a runner makes when it brings several packages up at once. */\nconst DEBOUNCE_MS = 150\n\n/**\n * A dummy `port` passed to the re-resolve. Only `proxy` is read from the result, and an omitted port\n * makes the helper probe (bind + release) a fresh free one \u2014 a pointless side effect to repeat on every\n * fragment write. Any number suppresses it.\n */\nconst PROBE_PORT = 1\n\n/** Search upward from `start` for `relative`, returning the first hit or undefined. */\nconst findUp = (start: string, relative: string): string | undefined => {\n let dir = path.resolve(start)\n\n for (;;) {\n const candidate = path.join(dir, relative)\n\n if (fs.existsSync(candidate)) return candidate\n\n const parent = path.dirname(dir)\n\n if (parent === dir) return undefined\n\n dir = parent\n }\n}\n\n/** The paths whose changes can alter this package's proxy map. */\nexport interface WatchTargets {\n /** The dev-context fragment dir, or `undefined` when no repo root could be located. */\n fragmentDir: string | undefined\n /** The dir to watch so the fragment dir is seen even when it does not exist yet. */\n stateDir: string | undefined\n /** This package's own `infra-kit.config.ts` (watched whether or not it exists yet). */\n configFile: string\n}\n\n/**\n * Where to watch, resolved from the package dir.\n *\n * The fragment dir is found by searching upward \u2014 but it may legitimately NOT EXIST yet: a frontend\n * started before any backend has none, and that is exactly the case the live re-resolve exists to fix.\n * So fall back to the `.infra-kit` state dir (also searched upward), then to the git root, and let the\n * watcher pick up the directory when the runner creates it. With no repo root at all there is nothing\n * to watch and the plugin stays inert.\n */\nexport const resolveWatchTargets = (cwd: string): WatchTargets => {\n const configFile = path.join(cwd, PACKAGE_CONFIG_FILE)\n const existing = findUp(cwd, DEV_CONTEXT_DIR)\n\n if (existing) return { fragmentDir: existing, stateDir: path.dirname(existing), configFile }\n\n const state = findUp(cwd, STATE_DIR)\n\n if (state) return { fragmentDir: path.join(state, 'dev-context'), stateDir: state, configFile }\n\n const gitDir = findUp(cwd, '.git')\n\n if (!gitDir) return { fragmentDir: undefined, stateDir: undefined, configFile }\n\n const root = path.dirname(gitDir)\n\n return { fragmentDir: path.join(root, DEV_CONTEXT_DIR), stateDir: path.join(root, STATE_DIR), configFile }\n}\n\n/** Order-independent, field-stable digest of a resolved proxy map. */\nexport const proxySignature = (proxy: InfraKitViteProxy): string => {\n const entries = Object.entries(proxy)\n .map(([routePath, entry]) => {\n const fields = Object.entries(entry as unknown as Record<string, unknown>).sort(([a], [b]) => {\n return a < b ? -1 : 1\n })\n\n return [routePath, fields] as const\n })\n .sort(([a], [b]) => {\n return a < b ? -1 : 1\n })\n\n return JSON.stringify(entries)\n}\n\n/**\n * The slice of `ViteDevServer` this module needs. Narrowed to an interface so the watch loop is\n * driveable by a fake in tests \u2014 booting a real vite dev server to assert \"it restarted once\" would\n * test vite, not this.\n */\nexport interface RestartableServer {\n watcher: {\n add: (target: string) => void\n on: (event: 'add' | 'change' | 'unlink' | 'addDir', listener: (file: string) => void) => void\n }\n restart: () => Promise<void>\n warn: (message: string) => void\n}\n\nexport interface WatchDevContextArgs {\n server: RestartableServer\n /** The package dir whose config + proxy are resolved (the plugin's `cwd`). */\n cwd: string\n /** The options the plugin was constructed with, re-applied on every re-resolve. */\n options: InfraKitDevOptions\n /** The signature of the proxy map vite is currently serving (taken in the `config` hook). */\n current: string\n}\n\n/**\n * Re-resolve the proxy on every dev-context write, and restart vite when \u2014 and ONLY when \u2014 the resolved\n * proxy actually changed.\n *\n * This is the whole reason the plugin exists as a plugin. `infraKitDev()` resolves once, while vite is\n * computing its config, so a backend started AFTER the frontend can never flip its route from `cloud` to\n * `local`: the answer was already baked. Watching the fragments closes that, and a `local \u2192 cloud`\n * demotion (a backend that died) heals the same way.\n *\n * The signature compare is not an optimisation, it is the loop guard. The runner rewrites its fragments\n * on every restart of its own (fresh `pid`, fresh `writtenAt`) and writes a fragment for THIS UI too, so\n * a watcher that restarted on any write would restart on writes caused by its own restart. Comparing the\n * resolved PROXY \u2014 the only thing a restart would change \u2014 makes those writes inert by construction.\n */\nexport const watchDevContext = (args: WatchDevContextArgs): { dispose: () => void } => {\n const { server, cwd, options, current } = args\n const { fragmentDir, stateDir, configFile } = resolveWatchTargets(cwd)\n\n if (!fragmentDir || !stateDir) return { dispose: () => {} }\n\n let signature = current\n let timer: NodeJS.Timeout | undefined\n\n const recompute = async (): Promise<void> => {\n let proxy: InfraKitViteProxy\n\n try {\n proxy = (await infraKitDev({ ...options, cwd, command: 'serve', port: PROBE_PORT })).proxy\n } catch (error) {\n // A half-written fragment, or a `dev.proxy` edit mid-save, resolves to an error. It is transient by\n // nature \u2014 the next write re-runs this \u2014 so warn and keep serving the proxy we already have rather\n // than tearing the dev server down over it.\n server.warn(`[infra-kit] dev-context changed but the proxy could not be re-resolved: ${String(error)}`)\n\n return\n }\n\n const next = proxySignature(proxy)\n\n if (next === signature) return\n\n signature = next\n await server.restart()\n }\n\n const schedule = (): void => {\n clearTimeout(timer)\n timer = setTimeout(() => {\n void recompute()\n }, DEBOUNCE_MS)\n }\n\n const isRelevant = (file: string): boolean => {\n return file === configFile || file === fragmentDir || file.startsWith(fragmentDir + path.sep)\n }\n\n server.watcher.add(stateDir)\n server.watcher.add(configFile)\n\n for (const event of ['add', 'change', 'unlink', 'addDir'] as const) {\n server.watcher.on(event, (file: string) => {\n if (isRelevant(file)) schedule()\n })\n }\n\n return {\n dispose: () => {\n clearTimeout(timer)\n },\n }\n}\n", "import type { InfraKitViteProxy } from '@slip-stream-kit/config/vite'\n\n/**\n * What `infraKitDev()` resolves: the ready-made vite `server` block. Re-declared structurally rather\n * than imported, because the helper types it inline on its return type and there is no named export\n * to reach for.\n */\nexport interface ResolvedDevServer {\n port?: number\n host?: string | boolean\n strictPort?: boolean\n /**\n * The alias websocket override. `@slip-stream-kit/config` <= 0.3.3 spells it `hmr`; later versions\n * spell it `ws`. This package depends on `^0.3.3`, which permits BOTH, so both are declared and\n * {@link mergeServerConfig} accepts either \u2014 reading only `ws` would silently wire HMR to nothing\n * for anyone resolving the older config, with no error to explain it.\n */\n ws?: { protocol: 'wss'; host: string; clientPort: number }\n /** Legacy spelling of {@link ws}, emitted by `@slip-stream-kit/config` <= 0.3.3. */\n hmr?: { protocol: 'wss'; host: string; clientPort: number }\n proxy: InfraKitViteProxy\n}\n\n/**\n * The subset of a consumer's `server` block this plugin can collide with. Structurally compatible with\n * vite's `ServerOptions`, so the plugin passes `config.server` straight in without a cast.\n */\nexport interface UserServerConfig {\n port?: number\n host?: string | boolean\n strictPort?: boolean\n /** Legacy alias of {@link ws}. Still declared because a consumer setting it must still win \u2014 see {@link mergeServerConfig}. */\n hmr?: unknown\n ws?: unknown\n proxy?: Record<string, unknown>\n}\n\n/** The `server` block this plugin contributes \u2014 every key optional, because every key is conditional. */\nexport interface InfraKitServerConfig {\n port?: number\n host?: string | boolean\n strictPort?: boolean\n ws?: ResolvedDevServer['ws']\n proxy?: InfraKitViteProxy\n}\n\n/** Drop every route the consumer declared themselves \u2014 an explicit route in their config outranks ours. */\nconst withoutUserRoutes = (\n proxy: InfraKitViteProxy,\n userProxy: Record<string, unknown> | undefined,\n): InfraKitViteProxy => {\n if (!userProxy) return proxy\n\n const result: InfraKitViteProxy = {}\n\n for (const [routePath, entry] of Object.entries(proxy)) {\n if (routePath in userProxy) continue\n result[routePath] = entry\n }\n\n return result\n}\n\n/**\n * The `server` block to return from the `config` hook, given what `infraKitDev()` resolved and what the\n * consumer wrote themselves.\n *\n * This exists because of ONE fact about vite: a `config` hook's result is merged **over** the user config\n * (`mergeConfig(userConfig, pluginResult)`), so anything emitted here WINS against an explicit setting in\n * the consumer's own `vite.config.ts`. A plugin that just returns everything `infraKitDev()` produced\n * would therefore silently overrule a hand-pinned `server.port` \u2014 the exact opposite of what a plugin\n * should do, and worse than the `server: await infraKitDev()` spread it replaces (there, at least, the\n * consumer could see the assignment).\n *\n * So: emit only what the consumer left unset. `strictPort` rides with `port` \u2014 it is meaningless applied\n * to someone else's port, and forcing it onto a hand-pinned one would turn a soft collision into a hard\n * boot failure.\n */\nexport const mergeServerConfig = (\n resolved: ResolvedDevServer,\n user: UserServerConfig | undefined,\n): InfraKitServerConfig => {\n const server: InfraKitServerConfig = {}\n\n if (user?.port == null) {\n if (resolved.port != null) server.port = resolved.port\n if (user?.strictPort == null && resolved.strictPort != null) server.strictPort = resolved.strictPort\n }\n\n if (user?.host == null && resolved.host != null) server.host = resolved.host\n // Guard on BOTH keys, not just `ws`. Vite 8 back-fills a legacy `server.hmr` onto `server.ws` with\n // `??=`, so ours would already be set by the time the compat shim ran: a consumer who hand-wrote\n // `server.hmr` would silently lose to us, which is the exact failure this whole module exists to\n // prevent. Their explicit setting outranks ours whichever spelling they used.\n // Always CONTRIBUTE as `ws` (vite 8 deprecated `server.hmr.*`), but ACCEPT either spelling from the\n // config package \u2014 see {@link ResolvedDevServer.ws}. Translating here means a consumer still on\n // config <= 0.3.3 stops getting the deprecation warning as soon as this package is published, with\n // no lockstep upgrade required.\n const resolvedWs = resolved.ws ?? resolved.hmr\n\n if (user?.ws == null && user?.hmr == null && resolvedWs != null) server.ws = resolvedWs\n\n const proxy = withoutUserRoutes(resolved.proxy, user?.proxy)\n\n if (Object.keys(proxy).length > 0) server.proxy = proxy\n\n return server\n}\n\n/**\n * True when the consumer pinned `server.port` while `infra-kit dev` had already ASSIGNED this UI a port\n * (`strictPort` on the resolved block is the runner's fingerprint \u2014 only the Layer-B path sets it).\n *\n * Honouring the pin is still right (it is explicit), but it is not free: the runner registered a portless\n * alias pointing at the port it assigned, and the UI is about to bind a different one. The hero URL then\n * 502s, and nothing in that failure names the pinned port as the cause. Worth one warning line.\n */\nexport const hasPinnedPortConflict = (resolved: ResolvedDevServer, user: UserServerConfig | undefined): boolean => {\n return user?.port != null && resolved.strictPort === true && user.port !== resolved.port\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["infraKitDev", "infraKitDev"]
7
7
  }
@@ -8,6 +8,18 @@ export interface ResolvedDevServer {
8
8
  port?: number;
9
9
  host?: string | boolean;
10
10
  strictPort?: boolean;
11
+ /**
12
+ * The alias websocket override. `@slip-stream-kit/config` <= 0.3.3 spells it `hmr`; later versions
13
+ * spell it `ws`. This package depends on `^0.3.3`, which permits BOTH, so both are declared and
14
+ * {@link mergeServerConfig} accepts either — reading only `ws` would silently wire HMR to nothing
15
+ * for anyone resolving the older config, with no error to explain it.
16
+ */
17
+ ws?: {
18
+ protocol: 'wss';
19
+ host: string;
20
+ clientPort: number;
21
+ };
22
+ /** Legacy spelling of {@link ws}, emitted by `@slip-stream-kit/config` <= 0.3.3. */
11
23
  hmr?: {
12
24
  protocol: 'wss';
13
25
  host: string;
@@ -23,7 +35,9 @@ export interface UserServerConfig {
23
35
  port?: number;
24
36
  host?: string | boolean;
25
37
  strictPort?: boolean;
38
+ /** Legacy alias of {@link ws}. Still declared because a consumer setting it must still win — see {@link mergeServerConfig}. */
26
39
  hmr?: unknown;
40
+ ws?: unknown;
27
41
  proxy?: Record<string, unknown>;
28
42
  }
29
43
  /** The `server` block this plugin contributes — every key optional, because every key is conditional. */
@@ -31,7 +45,7 @@ export interface InfraKitServerConfig {
31
45
  port?: number;
32
46
  host?: string | boolean;
33
47
  strictPort?: boolean;
34
- hmr?: ResolvedDevServer['hmr'];
48
+ ws?: ResolvedDevServer['ws'];
35
49
  proxy?: InfraKitViteProxy;
36
50
  }
37
51
  /**
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@slip-stream-kit/vite",
3
3
  "type": "module",
4
- "version": "0.3.3",
4
+ "version": "0.3.13",
5
5
  "description": "The infra-kit Vite plugin: per-worktree dev port, portless HTTPS alias HMR, and the config-driven dev proxy — wired from one plugin entry, and re-resolved live when a backend comes up.",
6
6
  "author": "Arthur Saenko <arthur.saenz7@gmail.com> (https://github.com/ArthurSaenz)",
7
7
  "license": "MIT",
@@ -50,18 +50,18 @@
50
50
  "fix": "pnpm run prettier-fix && pnpm run eslint-fix && pnpm run qa"
51
51
  },
52
52
  "dependencies": {
53
- "@slip-stream-kit/config": "^0.3.3"
53
+ "@slip-stream-kit/config": "^0.3.13"
54
54
  },
55
55
  "peerDependencies": {
56
- "vite": ">=6"
56
+ "vite": ">=8"
57
57
  },
58
58
  "devDependencies": {
59
- "@types/node": "^26.1.0",
59
+ "@types/node": "catalog:",
60
60
  "@wl/eslint-config": "workspace:*",
61
61
  "@wl/vitest-config": "workspace:*",
62
- "esbuild": "^0.28.1",
62
+ "esbuild": "^0.28.2",
63
63
  "typescript": "^6.0.3",
64
- "vite": "catalog:",
64
+ "vite": "^8.2.1",
65
65
  "vitest": "^4.1.9"
66
66
  }
67
67
  }