Seed default config on install; share defaults JSON with runtime
The defaults now live in a single file, scripts/config.default.json:
- src/config.ts imports it via 'with { type: "json" }' and derives
DEFAULT_CONFIG (with seconds->ms conversion at the boundary), so the
CLI help text always matches what the seeded user config contains.
- scripts/install.sh copies the same file to
$XDG_CONFIG_HOME/move/config.json only if no file is already there.
Existing configs (yours or from a previous install) are never
overwritten.
- scripts/uninstall.sh does not touch the user config file at all,
following the strict Unix convention. It does print a one-line
notice pointing at the path so the user can rm -rf the dir
themselves if they want a fully-clean removal.
- tsconfig.json gains resolveJsonModule:true so tsc accepts the JSON
import.
- A small runtime assertion in src/config.ts (assertSeedShape) fails
loudly if the seed file is missing keys or has wrong types.
- README Configuration section documents the seed behavior; Uninstall
section documents the leave-config-behind policy with the rm-it-
yourself command; Files table gains scripts/config.default.json.
This commit is contained in:
+49
-6
@@ -21,6 +21,13 @@
|
||||
|
||||
import { join } from "node:path";
|
||||
|
||||
// Single source of truth for default values. The same file ships in the
|
||||
// install tree and is copied to $XDG_CONFIG_HOME/move/config.json on a
|
||||
// fresh install (only if no config exists there yet). Values use the CLI
|
||||
// units (seconds for time fields, ms for stepDelay, pixels for stepCount);
|
||||
// the seconds->ms conversion happens below where DEFAULT_CONFIG is built.
|
||||
import seedRaw from "../scripts/config.default.json" with { type: "json" };
|
||||
|
||||
/**
|
||||
* The shape of a resolved runtime configuration. `readonly` to make
|
||||
* accidental mutation a type error.
|
||||
@@ -44,16 +51,52 @@ export interface Config {
|
||||
readonly verbose: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shape of `scripts/config.default.json` after parsing. The cast below
|
||||
* trusts the file's structure; `assertSeedShape` performs a small runtime
|
||||
* sanity check at import time so a corrupted seed file fails loudly
|
||||
* instead of silently producing `NaN` or `undefined` defaults.
|
||||
*/
|
||||
interface SeedShape {
|
||||
moveInterval: number; // seconds
|
||||
checkInterval: number; // seconds
|
||||
stepDelay: number; // milliseconds
|
||||
stepCount: number; // pixels
|
||||
verbose: boolean;
|
||||
}
|
||||
|
||||
function assertSeedShape(raw: unknown): asserts raw is SeedShape {
|
||||
if (typeof raw !== "object" || raw === null) {
|
||||
throw new Error("scripts/config.default.json: root must be an object");
|
||||
}
|
||||
const r = raw as Record<string, unknown>;
|
||||
for (const key of ["moveInterval", "checkInterval", "stepDelay", "stepCount"] as const) {
|
||||
const v = r[key];
|
||||
if (typeof v !== "number" || !Number.isFinite(v) || v <= 0) {
|
||||
throw new Error(`scripts/config.default.json: '${key}' must be a positive finite number (got ${JSON.stringify(v)})`);
|
||||
}
|
||||
}
|
||||
if (typeof r.verbose !== "boolean") {
|
||||
throw new Error(`scripts/config.default.json: 'verbose' must be a boolean (got ${JSON.stringify(r.verbose)})`);
|
||||
}
|
||||
}
|
||||
|
||||
assertSeedShape(seedRaw);
|
||||
const seed: SeedShape = seedRaw;
|
||||
|
||||
/**
|
||||
* Built-in defaults used when neither the CLI nor the config file supplies
|
||||
* a value for a given field.
|
||||
* a value for a given field. Derived from `scripts/config.default.json`
|
||||
* (the single source of truth); time-valued fields are converted from
|
||||
* seconds to milliseconds here so the rest of the codebase works in
|
||||
* internal units.
|
||||
*/
|
||||
export const DEFAULT_CONFIG: Config = {
|
||||
moveInterval: 4 * 60 * 1000, // 4 minutes in milliseconds
|
||||
checkInterval: 10 * 1000, // Check every 10 seconds
|
||||
stepDelay: 50, // ms between mouse steps
|
||||
stepCount: 250, // pixels to move per sweep
|
||||
verbose: false, // quiet by default; startup banner still prints
|
||||
moveInterval: seed.moveInterval * 1000,
|
||||
checkInterval: seed.checkInterval * 1000,
|
||||
stepDelay: seed.stepDelay,
|
||||
stepCount: seed.stepCount,
|
||||
verbose: seed.verbose,
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user