From d05a529c52d3459da34ad28c47ae967926841a36 Mon Sep 17 00:00:00 2001 From: Cooldude2606 <25043174+Cooldude2606@users.noreply.github.com> Date: Tue, 3 Mar 2026 22:22:59 +0000 Subject: [PATCH] Improve config field definition type --- packages/controller/controller.ts | 2 +- packages/lib/src/config/classes.ts | 31 ++++++++++++++++++++------ packages/lib/src/config/definitions.ts | 3 +-- 3 files changed, 26 insertions(+), 10 deletions(-) diff --git a/packages/controller/controller.ts b/packages/controller/controller.ts index 85b20875..16b2d343 100644 --- a/packages/controller/controller.ts +++ b/packages/controller/controller.ts @@ -387,7 +387,7 @@ async function initialize(): Promise { controllerConfig.set("controller.version", version); // Allows tracking last loaded version - if (!controllerConfig.get("controller.auth_secret")) { + if (controllerConfig.get("controller.auth_secret") === "") { logger.info("Generating new controller authentication secret"); let asyncRandomBytes = util.promisify(crypto.randomBytes); let bytes = await asyncRandomBytes(256); diff --git a/packages/lib/src/config/classes.ts b/packages/lib/src/config/classes.ts index 464dd943..cbd15a73 100644 --- a/packages/lib/src/config/classes.ts +++ b/packages/lib/src/config/classes.ts @@ -63,14 +63,24 @@ export const ConfigSchema = Type.Record(Type.String(), Type.Union([ ])); export type ConfigSchema = Static; -type FieldType = "boolean" | "string" | "number" | "object"; type FieldValue = null | boolean | string | number | object; -export interface FieldDefinition { + +type FieldType = + T extends boolean ? "boolean" : + T extends string ? "string" : + T extends number ? "number" : + T extends object ? "object" : + never; + +export interface FieldDefinition< + Type extends FieldValue = any, + Fields extends Record = any, +> { /** * string declaring the type of the config value, supports boolean, * string, number and object. */ - type: FieldType; + type: FieldType; /** * Text used to identify the config field in user interfaces. */ @@ -79,6 +89,11 @@ export interface FieldDefinition { * Text used to describe the config field in user interfaces. */ description?: string; + /** + * Validation function ran to confirm the value is consistent with other config values. + * Should throw an error if the value in invalid / inconsistent + */ + extendedValidation?: (value: Type, config: Config) => void; /** * Value to use for the autocomplete attribute in the Web UI's input element. * Useful for making browsers behave properly with credential inputs. @@ -93,7 +108,7 @@ export interface FieldDefinition { /** * True if this field can be set to null. Defaults to false. */ - optional?: boolean; + optional?: Type extends null ? true : false; /** * Value this config field should take in a newly initialized config. * This can also be an function returning the value to use. @@ -137,8 +152,10 @@ export interface FieldDefinition { hidden?: boolean; } -export type ConfigDefs = { - [Prop in keyof Fields]: FieldDefinition; +export type ConfigDefs< + Fields extends Record +> = { + [Prop in keyof Fields]: FieldDefinition; } function defaultValue(def: FieldDefinition) { @@ -177,7 +194,7 @@ export enum ConfigAccess { * @extends events.EventEmitter */ export class Config< - Fields extends { [Field in keyof Fields]: FieldValue }, + Fields extends Record > extends events.EventEmitter { /** * Mapping of config name to field meta data diff --git a/packages/lib/src/config/definitions.ts b/packages/lib/src/config/definitions.ts index 18d30609..21eea55c 100644 --- a/packages/lib/src/config/definitions.ts +++ b/packages/lib/src/config/definitions.ts @@ -144,7 +144,7 @@ export class ControllerConfig extends classes.Config { "Do not share this.", restartRequired: true, type: "string", - optional: true, + initialValue: "", }, "controller.heartbeat_interval": { title: "Heartbeat Interval", @@ -495,7 +495,6 @@ export class InstanceConfig extends classes.Config { "the process. Set to 0 to disable.", type: "number", initialValue: 300, - optional: true, }, "factorio.game_port": { description: "UDP port to run game on, uses a port in host.factorio_port_range if null",