feat(FN-2166): persist dev server script configuration across sessions

- Extend dev server store with config defaults, normalization, and JSON persistence alongside runtime state.
- Add GET/PUT /api/dev-server/config endpoints with strict request validation for nullable fields and preview URLs.
- Add dashboard API helpers plus a useDevServerConfig hook to load and update selected script, source, command, and preview override.
- Update DevServerView and styles to support saved script selection, change/clear actions, and synchronized command/preview inputs.
- Expand dev server store/routes/component tests and document the config endpoint in architecture docs.
This commit is contained in:
Fusion
2026-04-20 11:36:39 -07:00
committed by gsxdsm
parent ca6ed92dd1
commit 42476ec3da
12 changed files with 1096 additions and 229 deletions

View File

@@ -2,7 +2,9 @@ import { glob, readFile } from "node:fs/promises";
import { dirname, join, relative, resolve } from "node:path";
/** Script names in priority order (most likely first) */
export const DEV_SCRIPT_NAMES = ["dev", "start", "web", "frontend", "serve", "storybook", "preview"] as const;
export const DEV_SERVER_SCRIPT_NAMES = ["dev", "start", "serve", "web", "frontend", "preview", "storybook"] as const;
/** @deprecated use DEV_SERVER_SCRIPT_NAMES */
export const DEV_SCRIPT_NAMES = DEV_SERVER_SCRIPT_NAMES;
/** Framework indicators in devDependencies/dependencies */
export const FRAMEWORK_INDICATORS = [
@@ -47,20 +49,24 @@ async function readPackageJson(filePath: string): Promise<PackageJsonShape | nul
}
}
export function isCandidateScript(name: string): boolean {
return DEV_SERVER_SCRIPT_NAMES.includes(name as (typeof DEV_SERVER_SCRIPT_NAMES)[number]);
}
function getScriptPriorityScore(scriptName: string): number {
const index = DEV_SCRIPT_NAMES.indexOf(scriptName as (typeof DEV_SCRIPT_NAMES)[number]);
const index = DEV_SERVER_SCRIPT_NAMES.indexOf(scriptName as (typeof DEV_SERVER_SCRIPT_NAMES)[number]);
if (index === -1) {
return 0;
}
if (DEV_SCRIPT_NAMES.length <= 1) {
if (DEV_SERVER_SCRIPT_NAMES.length <= 1) {
return 0.5;
}
const maxBoost = 0.5;
const minBoost = 0.2;
const delta = maxBoost - minBoost;
const ratio = index / (DEV_SCRIPT_NAMES.length - 1);
const ratio = index / (DEV_SERVER_SCRIPT_NAMES.length - 1);
return maxBoost - (ratio * delta);
}
@@ -115,7 +121,7 @@ function extractScripts(pkg: PackageJsonShape): Array<{ name: string; command: s
const scripts = pkg.scripts ?? {};
const output: Array<{ name: string; command: string }> = [];
for (const scriptName of DEV_SCRIPT_NAMES) {
for (const scriptName of DEV_SERVER_SCRIPT_NAMES) {
const command = scripts[scriptName];
if (typeof command === "string" && command.trim().length > 0) {
output.push({ name: scriptName, command: command.trim() });