feat: refactor utility functions for improved consistency and usability; consolidate parsing logic
This commit is contained in:
@@ -2,6 +2,7 @@ import type { WorldStateRow } from '../context.js';
|
||||
import type { BattleSimJobPayload, BattleSimRequestPayload } from './types.js';
|
||||
import { loadUnitSetDefinitionByName } from './unitSetLoader.js';
|
||||
import type { WarEngineConfig } from '@sammo-ts/logic';
|
||||
import { asRecord } from '@sammo-ts/common';
|
||||
|
||||
const DEFAULT_WAR_CONFIG = {
|
||||
armPerPhase: 500,
|
||||
@@ -11,13 +12,6 @@ const DEFAULT_WAR_CONFIG = {
|
||||
maxAtmosByWar: 150,
|
||||
};
|
||||
|
||||
const asRecord = (value: unknown): Record<string, unknown> => {
|
||||
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
||||
return {};
|
||||
}
|
||||
return value as Record<string, unknown>;
|
||||
};
|
||||
|
||||
const resolveNumber = (record: Record<string, unknown>, keys: string[], fallback: number): number => {
|
||||
for (const key of keys) {
|
||||
const value = record[key];
|
||||
|
||||
+10
-15
@@ -1,3 +1,5 @@
|
||||
import { parseNumberWithFallback } from '@sammo-ts/common';
|
||||
|
||||
export interface GameApiConfig {
|
||||
host: string;
|
||||
port: number;
|
||||
@@ -13,17 +15,6 @@ export interface GameApiConfig {
|
||||
flushChannel: string;
|
||||
}
|
||||
|
||||
const parseNumber = (value: string | undefined, fallback: number, label: string): number => {
|
||||
if (!value) {
|
||||
return fallback;
|
||||
}
|
||||
const parsed = Number(value);
|
||||
if (Number.isNaN(parsed)) {
|
||||
throw new Error(`${label} must be a number.`);
|
||||
}
|
||||
return parsed;
|
||||
};
|
||||
|
||||
export const resolveGameApiConfigFromEnv = (env: NodeJS.ProcessEnv = process.env): GameApiConfig => {
|
||||
const profile = env.PROFILE ?? env.SERVER_PROFILE ?? 'hwe';
|
||||
const scenario = env.SCENARIO ?? 'default';
|
||||
@@ -36,19 +27,23 @@ export const resolveGameApiConfigFromEnv = (env: NodeJS.ProcessEnv = process.env
|
||||
|
||||
return {
|
||||
host: env.GAME_API_HOST ?? '0.0.0.0',
|
||||
port: parseNumber(env.GAME_API_PORT, 14000, 'GAME_API_PORT'),
|
||||
port: parseNumberWithFallback(env.GAME_API_PORT, 14000, 'GAME_API_PORT'),
|
||||
trpcPath: env.TRPC_PATH ?? '/trpc',
|
||||
eventsPath: env.GAME_API_EVENTS_PATH ?? '/events',
|
||||
profile,
|
||||
scenario,
|
||||
profileName,
|
||||
daemonRequestTimeoutMs: parseNumber(env.DAEMON_REQUEST_TIMEOUT_MS, 5000, 'DAEMON_REQUEST_TIMEOUT_MS'),
|
||||
battleSimRequestTimeoutMs: parseNumber(
|
||||
daemonRequestTimeoutMs: parseNumberWithFallback(env.DAEMON_REQUEST_TIMEOUT_MS, 5000, 'DAEMON_REQUEST_TIMEOUT_MS'),
|
||||
battleSimRequestTimeoutMs: parseNumberWithFallback(
|
||||
env.BATTLE_SIM_REQUEST_TIMEOUT_MS,
|
||||
8000,
|
||||
'BATTLE_SIM_REQUEST_TIMEOUT_MS'
|
||||
),
|
||||
battleSimResultTtlSeconds: parseNumber(env.BATTLE_SIM_RESULT_TTL_SECONDS, 60, 'BATTLE_SIM_RESULT_TTL_SECONDS'),
|
||||
battleSimResultTtlSeconds: parseNumberWithFallback(
|
||||
env.BATTLE_SIM_RESULT_TTL_SECONDS,
|
||||
60,
|
||||
'BATTLE_SIM_RESULT_TTL_SECONDS'
|
||||
),
|
||||
gameTokenSecret: secret,
|
||||
flushChannel: `${gatewayPrefix}:flush`,
|
||||
};
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { GameApiContext, WorldStateRow } from '../context.js';
|
||||
import { asRecord, isRecord } from '@sammo-ts/common';
|
||||
|
||||
export type MapCityCompact = [number, number, number, number, number, number];
|
||||
export type MapNationCompact = [number, string, string, number];
|
||||
@@ -45,11 +46,6 @@ const MAP_VERSION = 0 as const;
|
||||
const BASE_MAP_TTL_SECONDS = 30;
|
||||
const PUBLIC_MAP_TTL_SECONDS = 600;
|
||||
|
||||
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 resolveStartYear = (worldState: WorldStateRow): number => {
|
||||
const meta = asRecord(worldState.meta);
|
||||
const scenarioMeta = asRecord(meta.scenarioMeta);
|
||||
|
||||
@@ -4,6 +4,7 @@ import { randomBytes } from 'node:crypto';
|
||||
|
||||
import type { WorldStateRow } from '../../context.js';
|
||||
import { authedProcedure, router } from '../../trpc.js';
|
||||
import { asNumber, asRecord, asStringArray } from '@sammo-ts/common';
|
||||
import {
|
||||
isPersonalityTraitKey,
|
||||
isWarTraitKey,
|
||||
@@ -23,15 +24,6 @@ const DEFAULT_JOIN_STAT = {
|
||||
bonusMax: 5,
|
||||
};
|
||||
|
||||
const asRecord = (value: unknown): Record<string, unknown> =>
|
||||
value && typeof value === 'object' && !Array.isArray(value) ? (value as Record<string, unknown>) : {};
|
||||
|
||||
const asNumber = (value: unknown, fallback: number): number =>
|
||||
typeof value === 'number' && Number.isFinite(value) ? value : fallback;
|
||||
|
||||
const asStringArray = (value: unknown): string[] =>
|
||||
Array.isArray(value) ? value.filter((item): item is string => typeof item === 'string') : [];
|
||||
|
||||
const resolveJoinStat = (worldState: WorldStateRow) => {
|
||||
const config = asRecord(worldState.config);
|
||||
const stat = asRecord(config.stat);
|
||||
|
||||
@@ -13,6 +13,7 @@ import type {
|
||||
TriggerValue,
|
||||
} from '@sammo-ts/logic';
|
||||
import { evaluateConstraints, loadGeneralTurnCommandSpecs, loadNationTurnCommandSpecs } from '@sammo-ts/logic';
|
||||
import { asRecord, isRecord } from '@sammo-ts/common';
|
||||
|
||||
import type { CityRow, GeneralRow, NationRow, WorldStateRow } from '../context.js';
|
||||
import { loadTurnCommandProfile } from './turnCommandProfile.js';
|
||||
@@ -73,11 +74,6 @@ 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 asTriggerRecord = (value: unknown): Record<string, TriggerValue> =>
|
||||
isRecord(value) ? (value as Record<string, TriggerValue>) : {};
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { DatabaseClient, GeneralTurnRow, NationTurnRow, InputJsonValue } from '../context.js';
|
||||
import { isRecord } from '@sammo-ts/common';
|
||||
|
||||
export const DEFAULT_TURN_ACTION = '휴식';
|
||||
export const MAX_GENERAL_TURNS = 30;
|
||||
@@ -15,9 +16,6 @@ export interface ReservedTurnView {
|
||||
args: 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,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)) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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>) : {};
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { randomUUID } from 'node:crypto';
|
||||
import { parseJson } from '@sammo-ts/common';
|
||||
|
||||
export type OAuthMode = 'login' | 'change_pw';
|
||||
|
||||
@@ -41,17 +42,6 @@ interface RedisClientLike {
|
||||
multi(): RedisPipeline;
|
||||
}
|
||||
|
||||
const parseJson = <T>(raw: string | null): T | null => {
|
||||
if (!raw) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return JSON.parse(raw) as T;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
export class RedisOAuthSessionStore implements OAuthSessionStore {
|
||||
private readonly client: RedisClientLike;
|
||||
private readonly prefix: string;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { randomUUID } from 'node:crypto';
|
||||
import { parseJson } from '@sammo-ts/common';
|
||||
|
||||
import { createGatewayRedisKeyBuilder } from './redisKeys.js';
|
||||
import type {
|
||||
@@ -30,17 +31,6 @@ interface RedisClientLike {
|
||||
del(key: string): Promise<number>;
|
||||
}
|
||||
|
||||
const parseJson = <T>(raw: string | null): T | null => {
|
||||
if (!raw) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return JSON.parse(raw) as T;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
// Redis 세션 저장소는 게이트웨이와 게임 서버 간 SSO 토큰을 관리한다.
|
||||
export class RedisGatewaySessionService implements GatewaySessionService {
|
||||
private readonly client: RedisClientLike;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import path from 'node:path';
|
||||
import { parseBooleanWithFallback, parseNumberWithFallback } from '@sammo-ts/common';
|
||||
|
||||
export interface GatewayApiConfig {
|
||||
host: string;
|
||||
@@ -36,31 +37,6 @@ export interface GatewayOrchestratorConfig {
|
||||
worktreeRoot: string;
|
||||
}
|
||||
|
||||
const parseNumber = (value: string | undefined, fallback: number, label: string): number => {
|
||||
if (!value) {
|
||||
return fallback;
|
||||
}
|
||||
const parsed = Number(value);
|
||||
if (Number.isNaN(parsed)) {
|
||||
throw new Error(`${label} must be a number.`);
|
||||
}
|
||||
return parsed;
|
||||
};
|
||||
|
||||
const parseBoolean = (value: string | undefined, fallback: boolean): boolean => {
|
||||
if (!value) {
|
||||
return fallback;
|
||||
}
|
||||
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 fallback;
|
||||
};
|
||||
|
||||
const resolveSchemaName = (value: string | undefined): string => {
|
||||
if (!value) {
|
||||
return 'public';
|
||||
@@ -83,36 +59,44 @@ export const resolveGatewayApiConfigFromEnv = (env: NodeJS.ProcessEnv = process.
|
||||
const redisKeyPrefix = env.GATEWAY_REDIS_PREFIX ?? 'sammo:gateway';
|
||||
return {
|
||||
host: env.GATEWAY_API_HOST ?? '0.0.0.0',
|
||||
port: parseNumber(env.GATEWAY_API_PORT, 13000, 'GATEWAY_API_PORT'),
|
||||
port: parseNumberWithFallback(env.GATEWAY_API_PORT, 13000, 'GATEWAY_API_PORT'),
|
||||
trpcPath: env.TRPC_PATH ?? '/trpc',
|
||||
dbSchema: resolveSchemaName(env.GATEWAY_DB_SCHEMA),
|
||||
redisKeyPrefix,
|
||||
flushChannel: `${redisKeyPrefix}:flush`,
|
||||
sessionTtlSeconds: parseNumber(env.SESSION_TTL_SECONDS, 60 * 60 * 24 * 7, 'SESSION_TTL_SECONDS'),
|
||||
gameSessionTtlSeconds: parseNumber(env.GAME_SESSION_TTL_SECONDS, 60 * 60 * 6, 'GAME_SESSION_TTL_SECONDS'),
|
||||
sessionTtlSeconds: parseNumberWithFallback(env.SESSION_TTL_SECONDS, 60 * 60 * 24 * 7, 'SESSION_TTL_SECONDS'),
|
||||
gameSessionTtlSeconds: parseNumberWithFallback(
|
||||
env.GAME_SESSION_TTL_SECONDS,
|
||||
60 * 60 * 6,
|
||||
'GAME_SESSION_TTL_SECONDS'
|
||||
),
|
||||
gameTokenSecret: secret,
|
||||
oauthSessionTtlSeconds: parseNumber(env.OAUTH_SESSION_TTL_SECONDS, 10 * 60, 'OAUTH_SESSION_TTL_SECONDS'),
|
||||
oauthSessionTtlSeconds: parseNumberWithFallback(
|
||||
env.OAUTH_SESSION_TTL_SECONDS,
|
||||
10 * 60,
|
||||
'OAUTH_SESSION_TTL_SECONDS'
|
||||
),
|
||||
kakaoRestKey,
|
||||
kakaoAdminKey: env.KAKAO_ADMIN_KEY,
|
||||
kakaoRedirectUri,
|
||||
publicBaseUrl,
|
||||
orchestratorEnabled: parseBoolean(env.GATEWAY_ORCHESTRATOR_ENABLED, false),
|
||||
orchestratorReconcileIntervalMs: parseNumber(
|
||||
orchestratorEnabled: parseBooleanWithFallback(env.GATEWAY_ORCHESTRATOR_ENABLED, false),
|
||||
orchestratorReconcileIntervalMs: parseNumberWithFallback(
|
||||
env.GATEWAY_ORCHESTRATOR_RECONCILE_MS,
|
||||
15000,
|
||||
'GATEWAY_ORCHESTRATOR_RECONCILE_MS'
|
||||
),
|
||||
orchestratorScheduleIntervalMs: parseNumber(
|
||||
orchestratorScheduleIntervalMs: parseNumberWithFallback(
|
||||
env.GATEWAY_ORCHESTRATOR_SCHEDULE_MS,
|
||||
5000,
|
||||
'GATEWAY_ORCHESTRATOR_SCHEDULE_MS'
|
||||
),
|
||||
orchestratorBuildIntervalMs: parseNumber(
|
||||
orchestratorBuildIntervalMs: parseNumberWithFallback(
|
||||
env.GATEWAY_ORCHESTRATOR_BUILD_MS,
|
||||
10000,
|
||||
'GATEWAY_ORCHESTRATOR_BUILD_MS'
|
||||
),
|
||||
orchestratorAdminIntervalMs: parseNumber(
|
||||
orchestratorAdminIntervalMs: parseNumberWithFallback(
|
||||
env.GATEWAY_ORCHESTRATOR_ADMIN_MS,
|
||||
5000,
|
||||
'GATEWAY_ORCHESTRATOR_ADMIN_MS'
|
||||
@@ -135,22 +119,22 @@ export const resolveGatewayOrchestratorConfigFromEnv = (
|
||||
dbSchema: resolveSchemaName(env.GATEWAY_DB_SCHEMA),
|
||||
redisKeyPrefix,
|
||||
gameTokenSecret: secret,
|
||||
orchestratorReconcileIntervalMs: parseNumber(
|
||||
orchestratorReconcileIntervalMs: parseNumberWithFallback(
|
||||
env.GATEWAY_ORCHESTRATOR_RECONCILE_MS,
|
||||
15000,
|
||||
'GATEWAY_ORCHESTRATOR_RECONCILE_MS'
|
||||
),
|
||||
orchestratorScheduleIntervalMs: parseNumber(
|
||||
orchestratorScheduleIntervalMs: parseNumberWithFallback(
|
||||
env.GATEWAY_ORCHESTRATOR_SCHEDULE_MS,
|
||||
5000,
|
||||
'GATEWAY_ORCHESTRATOR_SCHEDULE_MS'
|
||||
),
|
||||
orchestratorBuildIntervalMs: parseNumber(
|
||||
orchestratorBuildIntervalMs: parseNumberWithFallback(
|
||||
env.GATEWAY_ORCHESTRATOR_BUILD_MS,
|
||||
10000,
|
||||
'GATEWAY_ORCHESTRATOR_BUILD_MS'
|
||||
),
|
||||
orchestratorAdminIntervalMs: parseNumber(
|
||||
orchestratorAdminIntervalMs: parseNumberWithFallback(
|
||||
env.GATEWAY_ORCHESTRATOR_ADMIN_MS,
|
||||
5000,
|
||||
'GATEWAY_ORCHESTRATOR_ADMIN_MS'
|
||||
|
||||
@@ -2,6 +2,7 @@ import path from 'node:path';
|
||||
|
||||
import { seedScenarioToDatabase, type ScenarioInstallOptions } from '@sammo-ts/game-engine';
|
||||
import { createGamePostgresConnector, resolvePostgresConfigFromEnv } from '@sammo-ts/infra';
|
||||
import { isRecord } from '@sammo-ts/common';
|
||||
|
||||
import type { BuildRunner } from './buildRunner.js';
|
||||
import type { ProcessManager } from './processManager.js';
|
||||
@@ -103,9 +104,6 @@ interface GatewayAdminActionResult {
|
||||
detail?: string;
|
||||
}
|
||||
|
||||
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 => {
|
||||
|
||||
Reference in New Issue
Block a user