refactor: rename core-public -> engine-public; fold helpers in; drop shared

Tighten the package model around the determinism invariant: everything the
engine imports must itself be deterministic.

- Rename core-public -> engine-public (package, dir, `engine-public/*` alias,
  all 118 import sites). It is the deterministic public surface the engine
  depends on and that client/server also use.
- Move the pure formatting helpers (renderNumber/renderTroops) from shared
  into engine-public/format — the engine uses them and they're deterministic.
- Drop the now-empty shared package (and its alias). `shared` was intended for
  client/server-shared, engine-forbidden code; there is none yet, so it is
  removed and can be re-added when such code appears.

Final graph: engine-public (leaf) <- engine <- {client, server};
client/server also -> engine-public.

Verified: tsc clean; 1364 + 65 tests pass; prod build succeeds; engine-public
is a leaf; engine has no client/server imports. Lockfile regenerated.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Evan Pelle
2026-06-10 20:32:32 +00:00
parent a7f992e9b0
commit 43d07ca85f
145 changed files with 193 additions and 201 deletions
+1 -1
View File
@@ -5,7 +5,7 @@
"type": "module",
"main": "src/Server.ts",
"dependencies": {
"@openfront/core-public": "*",
"@openfront/engine-public": "*",
"@openfront/engine": "*"
}
}
+1 -1
View File
@@ -6,7 +6,7 @@ import {
GameRecordSchema,
ID,
PartialGameRecord,
} from "core-public/Schemas";
} from "engine-public/Schemas";
import { replacer } from "engine/Util";
import { logger } from "./Logger";
import { ServerEnv } from "./ServerEnv";
+2 -2
View File
@@ -1,7 +1,7 @@
import WebSocket from "ws";
import { TokenPayload } from "core-public/ApiSchemas";
import { TokenPayload } from "engine-public/ApiSchemas";
import { Tick } from "engine/game/Game";
import { ClientID, PlayerCosmetics, Winner } from "core-public/Schemas";
import { ClientID, PlayerCosmetics, Winner } from "engine-public/Schemas";
export class Client {
public lastPing: number = Date.now();
+1 -1
View File
@@ -1,5 +1,5 @@
import { RateLimiter } from "limiter";
import { ClientID } from "core-public/Schemas";
import { ClientID } from "engine-public/Schemas";
const INTENTS_PER_SECOND = 10;
const INTENTS_PER_MINUTE = 150;
+1 -1
View File
@@ -7,7 +7,7 @@ import {
GameMode,
GameType,
} from "engine/game/Game";
import { GameConfig, GameID, PublicGameType } from "core-public/Schemas";
import { GameConfig, GameID, PublicGameType } from "engine-public/Schemas";
import { Client } from "./Client";
import { GamePhase, GameServer } from "./GameServer";
+1 -1
View File
@@ -1,6 +1,6 @@
import { z } from "zod";
import { buildAssetUrl } from "engine/AssetUrls";
import { ClanTagSchema, GameInfo, UsernameSchema } from "core-public/Schemas";
import { ClanTagSchema, GameInfo, UsernameSchema } from "engine-public/Schemas";
import { formatPlayerDisplayName } from "engine/Util";
import { GameMode } from "engine/game/Game";
import { getRuntimeAssetManifest } from "./RuntimeAssetManifest";
+1 -1
View File
@@ -4,7 +4,7 @@ import { parse } from "node-html-parser";
import path from "path";
import type { Logger } from "winston";
import { z } from "zod";
import { GAME_ID_REGEX, GameInfo } from "core-public/Schemas";
import { GAME_ID_REGEX, GameInfo } from "engine-public/Schemas";
import { replacer } from "engine/Util";
import type { GameManager } from "./GameManager";
import {
+2 -2
View File
@@ -2,7 +2,7 @@ import ipAnonymize from "ip-anonymize";
import { Logger } from "winston";
import WebSocket from "ws";
import { z } from "zod";
import { isAdminRole } from "core-public/ApiSchemas";
import { isAdminRole } from "engine-public/ApiSchemas";
import { GameEnv } from "engine/configuration/Config";
import { GameType } from "engine/game/Game";
import {
@@ -23,7 +23,7 @@ import {
ServerTurnMessage,
StampedIntent,
Turn,
} from "core-public/Schemas";
} from "engine-public/Schemas";
import { createPartialGameRecord } from "engine/Util";
import { archive, finalizeGameRecord } from "./Archive";
import { Client } from "./Client";
+1 -1
View File
@@ -4,7 +4,7 @@ import {
PublicGameInfoSchema,
PublicGamesSchema,
PublicGameTypeSchema,
} from "core-public/Schemas";
} from "engine-public/Schemas";
export type WorkerLobbyList = z.infer<typeof WorkerLobbyListSchema>;
export type WorkerReady = z.infer<typeof WorkerReadySchema>;
+1 -1
View File
@@ -16,7 +16,7 @@ import {
mapCategories,
} from "engine/game/Game";
import { PseudoRandom } from "engine/PseudoRandom";
import { GameConfig, PublicGameType, TeamCountConfig } from "core-public/Schemas";
import { GameConfig, PublicGameType, TeamCountConfig } from "engine-public/Schemas";
import { logger } from "./Logger";
import { getMapLandTiles } from "./MapLandTiles";
+1 -1
View File
@@ -1,6 +1,6 @@
import { Worker } from "cluster";
import winston from "winston";
import { PublicGameInfo, PublicGameType } from "core-public/Schemas";
import { PublicGameInfo, PublicGameType } from "engine-public/Schemas";
import { generateID } from "engine/Util";
import {
MasterCreateGame,
+3 -3
View File
@@ -11,15 +11,15 @@ import {
} from "obscenity";
import countries from "resources/countries.json";
import { Cosmetics } from "core-public/CosmeticSchemas";
import { decodePatternData } from "core-public/PatternDecoder";
import { Cosmetics } from "engine-public/CosmeticSchemas";
import { decodePatternData } from "engine-public/PatternDecoder";
import {
PlayerColor,
PlayerCosmeticRefs,
PlayerCosmetics,
PlayerPattern,
PlayerSkin,
} from "core-public/Schemas";
} from "engine-public/Schemas";
import { simpleHash } from "engine/Util";
const countryCodes = countries.filter((c) => !c.restricted).map((c) => c.code);
+2 -2
View File
@@ -1,7 +1,7 @@
import { base64url } from "jose";
import { Logger } from "winston";
import { ReservedClanTagsResponseSchema } from "core-public/ClanApiSchemas";
import { CosmeticsSchema } from "core-public/CosmeticSchemas";
import { ReservedClanTagsResponseSchema } from "engine-public/ClanApiSchemas";
import { CosmeticsSchema } from "engine-public/CosmeticSchemas";
import { startPolling } from "./PollingLoop";
import {
FailOpenPrivilegeChecker,
+1 -1
View File
@@ -1,7 +1,7 @@
import { JWK } from "jose";
import { z } from "zod";
import { GameEnv, parseGameEnv } from "engine/configuration/Config";
import { GameID } from "core-public/Schemas";
import { GameID } from "engine-public/Schemas";
import { simpleHash } from "engine/Util";
const JwksSchema = z.object({
+2 -2
View File
@@ -14,9 +14,9 @@ import {
GameID,
PartialGameRecordSchema,
ServerErrorMessage,
} from "core-public/Schemas";
} from "engine-public/Schemas";
import { generateID, replacer } from "engine/Util";
import { CreateGameInputSchema } from "core-public/WorkerSchemas";
import { CreateGameInputSchema } from "engine-public/WorkerSchemas";
import { archive, finalizeGameRecord } from "./Archive";
import { Client } from "./Client";
import { GameManager } from "./GameManager";
+1 -1
View File
@@ -4,7 +4,7 @@ import {
PublicGameInfo,
PublicGames,
PublicLobbyMessage,
} from "core-public/Schemas";
} from "engine-public/Schemas";
import { GameManager } from "./GameManager";
import {
MasterMessageSchema,
+2 -2
View File
@@ -5,9 +5,9 @@ import {
TokenPayloadSchema,
UserMeResponse,
UserMeResponseSchema,
} from "core-public/ApiSchemas";
} from "engine-public/ApiSchemas";
import { GameEnv } from "engine/configuration/Config";
import { PersistentIdSchema } from "core-public/Schemas";
import { PersistentIdSchema } from "engine-public/Schemas";
import { ServerEnv } from "./ServerEnv";
type TokenVerificationResult =