You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

81 lines
3.4 KiB

import { loadEnv, type Plugin } from 'vite';
import type { SessionPersona } from '../../project/persona.js';
import { NoopAnalyticsRecorder, SqliteAnalyticsRecorder } from './analytics.js';
import { loadConfig } from './config.js';
import { resolveSessionDefaults } from './defaults.js';
import { selectProvider } from './providers/index.js';
import type { ToolRegistry } from './tools.js';
import { handleRealtimeUpgrade } from './ws-handler.js';
export interface RealtimeDevWsPluginOptions {
/**
* Raw, env-independent pieces (project persona + tool registry) — taken as
* parameters rather than imported here so this module (server/core) never
* has to import project code directly. See vite.config.ts, the
* composition root that supplies these, and docs/TOOLS.md.
*
* Deliberately NOT pre-resolved against env vars by the caller: resolving
* them happens inside configureServer below, gated by the VITEST check —
* see the comment on that check for why.
*/
persona: SessionPersona;
toolRegistry: ToolRegistry;
}
/**
* Wires the same upgrade handling used in production (see repo-root
* server.js) into Vite's dev server, so `npm run dev` supports the realtime
* WS endpoint without a production build.
*
* Loads .env itself: this hook runs at dev-server startup, outside
* SvelteKit's own request/module graph, so `$env/...` isn't available here —
* see the note in ./tokens.ts for why this whole module tree reads
* `process.env` directly instead.
*/
export function realtimeDevWsPlugin(options: RealtimeDevWsPluginOptions): Plugin {
return {
name: 'webavatar-realtime-ws-dev',
configureServer(server) {
// vitest's browser-mode project boots its own Vite dev server for
// component tests; this plugin has nothing to do there. Critically,
// this guard is also what keeps loadConfig()'s fail-fast validation
// (below) from running during `npm run test:unit` — that suite's
// `test.projects` entries both `extends: './vite.config.ts'`, so this
// plugin's config gets evaluated there too, and unit tests need no env
// vars set at all. Don't move config resolution above this line.
if (process.env.VITEST) return;
const loaded = loadEnv('development', process.cwd(), '');
for (const [key, value] of Object.entries(loaded)) {
if (process.env[key] === undefined) process.env[key] = value;
}
const config = loadConfig();
const provider = selectProvider(config);
const recorder = config.analytics.enabled
? new SqliteAnalyticsRecorder(config.analytics.dbPath, config.analytics)
: new NoopAnalyticsRecorder();
const defaults = {
...resolveSessionDefaults(options.persona, config.session),
toolRegistry: options.toolRegistry
};
server.httpServer?.on('upgrade', (req, socket, head) => {
handleRealtimeUpgrade(req, socket, head, {
provider,
defaults,
recorder,
clientToolTimeoutMs: config.session.clientToolTimeoutMs,
// This plugin only ever runs in dev (see the VITEST guard above),
// so this is a literal, not an env lookup — debug-log forwarding
// is frictionless here. See ../../../../server.js for the prod side.
isDev: true,
// Unlike isDev, this reads from config (not a literal) — a
// developer may still want DEBUG_LOG_MESSAGES=false locally, e.g.
// to rehearse prod-like log hygiene. See docs/LOGGING.md.
logMessages: config.debug.messages
});
});
}
};
}