refactor: duration fields → number|null (milliseconds)

- throttle, timeout, interval: string|null → number|null
- parseDurationField now returns parsed ms (5s→5000, 10m→600000, 1h→3600000)
- biome.json: ignore dist/** from checks

小橘 <xiaoju@shazhou.work>
This commit is contained in:
2026-04-22 07:41:22 +00:00
parent 1949007c99
commit 8784a4edc4
4 changed files with 29 additions and 12 deletions
+3
View File
@@ -1,5 +1,8 @@
{
"$schema": "https://biomejs.dev/schemas/1.9.0/schema.json",
"files": {
"ignore": ["**/dist/**"]
},
"organizeImports": {
"enabled": true
},
+3 -3
View File
@@ -35,17 +35,17 @@ describe("parseNerveConfig", () => {
expect(result.ok).toBe(true);
if (!result.ok) return;
expect(result.value.senses.cpu).toEqual({ group: "system", throttle: "5s", timeout: null });
expect(result.value.senses.cpu).toEqual({ group: "system", throttle: 5000, timeout: null });
expect(result.value.senses.memory).toEqual({
group: "system",
throttle: null,
timeout: "10s",
timeout: 10_000,
});
expect(result.value.reflexes).toHaveLength(3);
expect(result.value.reflexes[0]).toEqual({
kind: "sense",
sense: "cpu",
interval: "30s",
interval: 30_000,
on: null,
});
expect(result.value.reflexes[1]).toEqual({
+20 -6
View File
@@ -4,24 +4,38 @@ import type { Result } from "./result.js";
import { err, ok } from "./result.js";
import type { NerveConfig, ReflexConfig, SenseConfig, WorkflowConfig } from "./types.js";
const DURATION_RE = /^\d+[smh]$/;
const DURATION_RE = /^(\d+)([smh])$/;
function isValidDuration(value: string): boolean {
return DURATION_RE.test(value);
const DURATION_MULTIPLIERS: Record<string, number> = {
s: 1_000,
m: 60_000,
h: 3_600_000,
};
function parseDurationToMs(value: string): number | null {
const match = DURATION_RE.exec(value);
if (!match) return null;
return Number(match[1]) * DURATION_MULTIPLIERS[match[2]];
}
function isValidGroupName(value: string): boolean {
return /^[a-zA-Z0-9_-]+$/.test(value);
}
function parseDurationField(field: unknown, label: string): Result<string | null> {
function parseDurationField(field: unknown, label: string): Result<number | null> {
if (field === undefined || field === null) return ok(null);
if (typeof field !== "string" || !isValidDuration(field)) {
if (typeof field !== "string") {
return err(
new Error(`${label}: invalid duration "${field}" (expected e.g. "5s", "10m", "1h")`),
);
}
return ok(field);
const ms = parseDurationToMs(field);
if (ms === null) {
return err(
new Error(`${label}: invalid duration "${field}" (expected e.g. "5s", "10m", "1h")`),
);
}
return ok(ms);
}
function validateSenseConfig(name: string, raw: unknown): Result<SenseConfig> {
+3 -3
View File
@@ -7,14 +7,14 @@ export type Signal = {
export type SenseConfig = {
group: string;
throttle: string | null;
timeout: string | null;
throttle: number | null;
timeout: number | null;
};
export type SenseReflexConfig = {
kind: "sense";
sense: string;
interval: string | null;
interval: number | null;
on: string[] | null;
};