feat: refactor utility functions for improved consistency and usability; consolidate parsing logic

This commit is contained in:
2026-01-17 12:36:58 +00:00
parent bc76b6e725
commit 07ea17dacf
25 changed files with 162 additions and 211 deletions
+2 -4
View File
@@ -1,9 +1,7 @@
import type { City, Nation } from '@sammo-ts/logic';
import { asRecord, isRecord } from '@sammo-ts/common';
export const isRecord = (value: unknown): value is Record<string, unknown> =>
value !== null && typeof value === 'object' && !Array.isArray(value);
export const asRecord = (value: unknown): Record<string, unknown> => (isRecord(value) ? value : {});
export { asRecord, isRecord };
export const readNumber = (value: unknown, fallback = 0): number => {
if (typeof value === 'number' && Number.isFinite(value)) {
+8 -32
View File
@@ -1,4 +1,5 @@
import type { TurnSchedule } from '@sammo-ts/logic';
import { parseOptionalBoolean, parseOptionalNumber } from '@sammo-ts/common';
import type { TurnRunBudget } from '../lifecycle/types.js';
import { resolveDatabaseUrl } from '../scenario/databaseUrl.js';
@@ -24,36 +25,11 @@ const DEFAULT_BUDGET: TurnRunBudget = {
catchUpCap: 1,
};
const parseNumber = (value: string | undefined): number | undefined => {
if (!value) {
return undefined;
}
const parsed = Number(value);
if (!Number.isFinite(parsed)) {
return undefined;
}
return parsed;
};
const parseBoolean = (value: string | undefined): boolean | undefined => {
if (!value) {
return undefined;
}
const normalized = value.trim().toLowerCase();
if (['1', 'true', 'yes', 'y', 'on'].includes(normalized)) {
return true;
}
if (['0', 'false', 'no', 'n', 'off'].includes(normalized)) {
return false;
}
return undefined;
};
const buildBudgetOverride = (env: NodeJS.ProcessEnv, override?: Partial<TurnRunBudget>): TurnRunBudget | undefined => {
const budgetOverride: Partial<TurnRunBudget> = {
budgetMs: parseNumber(env.TURN_BUDGET_MS),
maxGenerals: parseNumber(env.TURN_MAX_GENERALS),
catchUpCap: parseNumber(env.TURN_CATCH_UP_CAP),
budgetMs: parseOptionalNumber(env.TURN_BUDGET_MS),
maxGenerals: parseOptionalNumber(env.TURN_MAX_GENERALS),
catchUpCap: parseOptionalNumber(env.TURN_CATCH_UP_CAP),
...override,
};
@@ -78,10 +54,10 @@ export const runTurnDaemonCli = async (options: TurnDaemonCliOptions = {}): Prom
schema: env.GATEWAY_DB_SCHEMA ?? 'public',
}));
const budget = buildBudgetOverride(env, options.budget);
const tickMinutes = options.tickMinutes ?? parseNumber(env.TURN_TICK_MINUTES);
const enableDatabaseFlush = options.enableDatabaseFlush ?? parseBoolean(env.TURN_FLUSH_DB) ?? true;
const pauseGateIntervalMs = parseNumber(env.TURN_PAUSE_GATE_MS);
const adminActionIntervalMs = options.adminActionIntervalMs ?? parseNumber(env.TURN_ADMIN_ACTION_MS);
const tickMinutes = options.tickMinutes ?? parseOptionalNumber(env.TURN_TICK_MINUTES);
const enableDatabaseFlush = options.enableDatabaseFlush ?? parseOptionalBoolean(env.TURN_FLUSH_DB) ?? true;
const pauseGateIntervalMs = parseOptionalNumber(env.TURN_PAUSE_GATE_MS);
const adminActionIntervalMs = options.adminActionIntervalMs ?? parseOptionalNumber(env.TURN_ADMIN_ACTION_MS);
const runtime = await createTurnDaemonRuntime({
profile,
@@ -1,4 +1,5 @@
import { createGatewayPostgresConnector } from '@sammo-ts/infra';
import { isRecord } from '@sammo-ts/common';
export type GatewayAdminActionStatus = 'REQUESTED' | 'APPLIED' | 'FAILED' | 'IGNORED';
@@ -53,9 +54,6 @@ export interface GatewayAdminActionConsumer {
const DEFAULT_POLL_MS = 5000;
const isRecord = (value: unknown): value is Record<string, unknown> =>
Boolean(value) && typeof value === 'object' && !Array.isArray(value);
const normalizeMeta = (value: unknown): Record<string, unknown> => (isRecord(value) ? value : {});
const normalizeStatus = (value: unknown): GatewayAdminActionStatus | null => {
@@ -15,16 +15,12 @@ import {
ITEM_KEYS,
loadItemModules,
} from '@sammo-ts/logic';
import { asRecord } from '@sammo-ts/common';
const DEFAULT_GENERAL_GOLD = 1000;
const DEFAULT_GENERAL_RICE = 1000;
const DEFAULT_CREW_TYPE_ID = 1100;
const isRecord = (value: unknown): value is Record<string, unknown> =>
value !== null && typeof value === 'object' && !Array.isArray(value);
const asRecord = (value: unknown): Record<string, unknown> => (isRecord(value) ? value : {});
const normalizeCode = (value: string | null | undefined): string | null => {
if (!value || value === 'None') {
return null;
@@ -21,7 +21,7 @@ import {
resolveGeneralAction,
} from '@sammo-ts/logic';
import { LogCategory, LogFormat, LogScope } from '@sammo-ts/logic';
import { LiteHashDRBG } from '@sammo-ts/common';
import { asRecord, LiteHashDRBG } from '@sammo-ts/common';
import type { ConstraintContext, StateView } from '@sammo-ts/logic';
@@ -43,11 +43,6 @@ import type { AiReservedTurnProvider } from './ai/types.js';
const DEFAULT_ACTION = '휴식';
const isRecord = (value: unknown): value is Record<string, unknown> =>
value !== null && typeof value === 'object' && !Array.isArray(value);
const asRecord = (value: unknown): Record<string, unknown> => (isRecord(value) ? value : {});
const resolveConstraintEnv = (
world: TurnWorldState,
scenarioMeta: ScenarioMeta | undefined,
@@ -325,7 +320,7 @@ class WorldStateView implements StateView {
}
}
const extractArgsRecord = (value: unknown): Record<string, unknown> => (isRecord(value) ? value : {});
const extractArgsRecord = (value: unknown): Record<string, unknown> => asRecord(value);
const buildConstraintContext = (
general: TurnGeneral,
@@ -1,4 +1,5 @@
import { createGamePostgresConnector, type InputJsonValue, type TurnEngineDatabaseClient } from '@sammo-ts/infra';
import { isRecord } from '@sammo-ts/common';
export interface ReservedTurnEntry {
action: string;
@@ -22,9 +23,6 @@ const DEFAULT_NATION_TURNS = 12;
const asJson = (value: unknown): InputJsonValue => value as InputJsonValue;
const isRecord = (value: unknown): value is Record<string, unknown> =>
value !== null && typeof value === 'object' && !Array.isArray(value);
const normalizeAction = (action: string | null | undefined): string =>
action && action.length > 0 ? action : DEFAULT_TURN_ACTION;
+1 -5
View File
@@ -10,6 +10,7 @@ import {
} from '@sammo-ts/infra';
import type { City, Nation, ScenarioConfig, ScenarioMeta, Troop, TriggerValue } from '@sammo-ts/logic';
import { z } from 'zod';
import { asRecord, isRecord } from '@sammo-ts/common';
import { getNextTickTime } from '../lifecycle/getNextTickTime.js';
import type { MapLoaderOptions } from '../scenario/mapLoader.js';
@@ -27,11 +28,6 @@ interface TurnWorldLoaderOptions {
type JsonRecord = Record<string, unknown>;
const isRecord = (value: unknown): value is JsonRecord =>
value !== null && typeof value === 'object' && !Array.isArray(value);
const asRecord = (value: unknown): JsonRecord => (isRecord(value) ? value : {});
const asTriggerRecord = (value: unknown): Record<string, TriggerValue> =>
isRecord(value) ? (value as Record<string, TriggerValue>) : {};