@drmxrcy/tcg-core 0.0.0-202602060542
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +882 -0
- package/package.json +58 -0
- package/src/__tests__/alpha-clash-engine-definition.test.ts +319 -0
- package/src/__tests__/createMockAlphaClashGame.ts +462 -0
- package/src/__tests__/createMockGrandArchiveGame.ts +373 -0
- package/src/__tests__/createMockGundamGame.ts +379 -0
- package/src/__tests__/createMockLorcanaGame.ts +328 -0
- package/src/__tests__/createMockOnePieceGame.ts +429 -0
- package/src/__tests__/createMockRiftboundGame.ts +462 -0
- package/src/__tests__/grand-archive-engine-definition.test.ts +118 -0
- package/src/__tests__/gundam-engine-definition.test.ts +110 -0
- package/src/__tests__/integration-complete-game.test.ts +508 -0
- package/src/__tests__/integration-network-sync.test.ts +469 -0
- package/src/__tests__/lorcana-engine-definition.test.ts +100 -0
- package/src/__tests__/move-enumeration.test.ts +725 -0
- package/src/__tests__/multiplayer-engine.test.ts +555 -0
- package/src/__tests__/one-piece-engine-definition.test.ts +114 -0
- package/src/__tests__/riftbound-engine-definition.test.ts +124 -0
- package/src/actions/action-definition.test.ts +201 -0
- package/src/actions/action-definition.ts +122 -0
- package/src/actions/action-timing.test.ts +490 -0
- package/src/actions/action-timing.ts +257 -0
- package/src/cards/card-definition.test.ts +268 -0
- package/src/cards/card-definition.ts +27 -0
- package/src/cards/card-instance.test.ts +422 -0
- package/src/cards/card-instance.ts +49 -0
- package/src/cards/computed-properties.test.ts +530 -0
- package/src/cards/computed-properties.ts +84 -0
- package/src/cards/conditional-modifiers.test.ts +390 -0
- package/src/cards/modifiers.test.ts +286 -0
- package/src/cards/modifiers.ts +51 -0
- package/src/engine/MULTIPLAYER.md +425 -0
- package/src/engine/__tests__/rule-engine-flow.test.ts +348 -0
- package/src/engine/__tests__/rule-engine-history.test.ts +535 -0
- package/src/engine/__tests__/rule-engine-moves.test.ts +488 -0
- package/src/engine/__tests__/rule-engine.test.ts +366 -0
- package/src/engine/index.ts +14 -0
- package/src/engine/multiplayer-engine.example.ts +571 -0
- package/src/engine/multiplayer-engine.ts +409 -0
- package/src/engine/rule-engine.test.ts +286 -0
- package/src/engine/rule-engine.ts +1539 -0
- package/src/engine/tracker-system.ts +172 -0
- package/src/examples/__tests__/coin-flip-game.test.ts +641 -0
- package/src/filtering/card-filter.test.ts +230 -0
- package/src/filtering/card-filter.ts +91 -0
- package/src/filtering/card-query.test.ts +901 -0
- package/src/filtering/card-query.ts +273 -0
- package/src/filtering/filter-matching.test.ts +944 -0
- package/src/filtering/filter-matching.ts +315 -0
- package/src/flow/SERIALIZATION.md +428 -0
- package/src/flow/__tests__/flow-definition.test.ts +427 -0
- package/src/flow/__tests__/flow-manager.test.ts +756 -0
- package/src/flow/__tests__/flow-serialization.test.ts +565 -0
- package/src/flow/flow-definition.ts +453 -0
- package/src/flow/flow-manager.ts +1044 -0
- package/src/flow/index.ts +35 -0
- package/src/game-definition/__tests__/game-definition-validation.test.ts +359 -0
- package/src/game-definition/__tests__/game-definition.test.ts +291 -0
- package/src/game-definition/__tests__/move-definitions.test.ts +328 -0
- package/src/game-definition/game-definition.ts +261 -0
- package/src/game-definition/index.ts +28 -0
- package/src/game-definition/move-definitions.ts +188 -0
- package/src/game-definition/validation.ts +183 -0
- package/src/history/history-manager.test.ts +497 -0
- package/src/history/history-manager.ts +312 -0
- package/src/history/history-operations.ts +122 -0
- package/src/history/index.ts +9 -0
- package/src/history/types.ts +255 -0
- package/src/index.ts +32 -0
- package/src/logging/index.ts +27 -0
- package/src/logging/log-formatter.ts +187 -0
- package/src/logging/logger.ts +276 -0
- package/src/logging/types.ts +148 -0
- package/src/moves/create-move.test.ts +331 -0
- package/src/moves/create-move.ts +64 -0
- package/src/moves/move-enumeration.ts +228 -0
- package/src/moves/move-executor.test.ts +431 -0
- package/src/moves/move-executor.ts +195 -0
- package/src/moves/move-system.test.ts +380 -0
- package/src/moves/move-system.ts +463 -0
- package/src/moves/standard-moves.ts +231 -0
- package/src/operations/card-operations.test.ts +236 -0
- package/src/operations/card-operations.ts +116 -0
- package/src/operations/card-registry-impl.test.ts +251 -0
- package/src/operations/card-registry-impl.ts +70 -0
- package/src/operations/card-registry.test.ts +234 -0
- package/src/operations/card-registry.ts +106 -0
- package/src/operations/counter-operations.ts +152 -0
- package/src/operations/game-operations.test.ts +280 -0
- package/src/operations/game-operations.ts +140 -0
- package/src/operations/index.ts +24 -0
- package/src/operations/operations-impl.test.ts +354 -0
- package/src/operations/operations-impl.ts +468 -0
- package/src/operations/zone-operations.test.ts +295 -0
- package/src/operations/zone-operations.ts +223 -0
- package/src/rng/seeded-rng.test.ts +339 -0
- package/src/rng/seeded-rng.ts +123 -0
- package/src/targeting/index.ts +48 -0
- package/src/targeting/target-definition.test.ts +273 -0
- package/src/targeting/target-definition.ts +37 -0
- package/src/targeting/target-dsl.ts +279 -0
- package/src/targeting/target-resolver.ts +486 -0
- package/src/targeting/target-validation.test.ts +994 -0
- package/src/targeting/target-validation.ts +286 -0
- package/src/telemetry/events.ts +202 -0
- package/src/telemetry/index.ts +21 -0
- package/src/telemetry/telemetry-manager.ts +127 -0
- package/src/telemetry/types.ts +68 -0
- package/src/testing/__tests__/testing-utilities-integration.test.ts +161 -0
- package/src/testing/index.ts +88 -0
- package/src/testing/test-assertions.test.ts +341 -0
- package/src/testing/test-assertions.ts +256 -0
- package/src/testing/test-card-factory.test.ts +228 -0
- package/src/testing/test-card-factory.ts +111 -0
- package/src/testing/test-context-factory.ts +187 -0
- package/src/testing/test-end-assertions.test.ts +262 -0
- package/src/testing/test-end-assertions.ts +95 -0
- package/src/testing/test-engine-builder.test.ts +389 -0
- package/src/testing/test-engine-builder.ts +46 -0
- package/src/testing/test-flow-assertions.test.ts +284 -0
- package/src/testing/test-flow-assertions.ts +115 -0
- package/src/testing/test-player-builder.test.ts +132 -0
- package/src/testing/test-player-builder.ts +46 -0
- package/src/testing/test-replay-assertions.test.ts +356 -0
- package/src/testing/test-replay-assertions.ts +164 -0
- package/src/testing/test-rng-helpers.test.ts +260 -0
- package/src/testing/test-rng-helpers.ts +190 -0
- package/src/testing/test-state-builder.test.ts +373 -0
- package/src/testing/test-state-builder.ts +99 -0
- package/src/testing/test-zone-factory.test.ts +295 -0
- package/src/testing/test-zone-factory.ts +224 -0
- package/src/types/branded-utils.ts +54 -0
- package/src/types/branded.test.ts +175 -0
- package/src/types/branded.ts +33 -0
- package/src/types/index.ts +8 -0
- package/src/types/state.test.ts +198 -0
- package/src/types/state.ts +154 -0
- package/src/validation/card-type-guards.test.ts +242 -0
- package/src/validation/card-type-guards.ts +179 -0
- package/src/validation/index.ts +40 -0
- package/src/validation/schema-builders.test.ts +403 -0
- package/src/validation/schema-builders.ts +345 -0
- package/src/validation/type-guard-builder.test.ts +216 -0
- package/src/validation/type-guard-builder.ts +109 -0
- package/src/validation/validator-builder.test.ts +375 -0
- package/src/validation/validator-builder.ts +273 -0
- package/src/zones/index.ts +28 -0
- package/src/zones/zone-factory.test.ts +183 -0
- package/src/zones/zone-factory.ts +44 -0
- package/src/zones/zone-operations.test.ts +800 -0
- package/src/zones/zone-operations.ts +306 -0
- package/src/zones/zone-state-helpers.test.ts +337 -0
- package/src/zones/zone-state-helpers.ts +128 -0
- package/src/zones/zone-visibility.test.ts +156 -0
- package/src/zones/zone-visibility.ts +36 -0
- package/src/zones/zone.test.ts +186 -0
- package/src/zones/zone.ts +66 -0
package/src/index.ts
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @drmxrcy/tcg-core - Core TCG Engine Framework
|
|
3
|
+
*
|
|
4
|
+
* A declarative, type-safe framework for building trading card game engines.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
export * from "./actions/action-definition";
|
|
8
|
+
export * from "./actions/action-timing";
|
|
9
|
+
export * from "./cards/card-definition";
|
|
10
|
+
export * from "./cards/card-instance";
|
|
11
|
+
export * from "./cards/computed-properties";
|
|
12
|
+
export * from "./cards/modifiers";
|
|
13
|
+
export * from "./engine";
|
|
14
|
+
export * from "./engine/tracker-system";
|
|
15
|
+
export * from "./filtering/card-filter";
|
|
16
|
+
export * from "./filtering/card-query";
|
|
17
|
+
export * from "./filtering/filter-matching";
|
|
18
|
+
export * from "./flow";
|
|
19
|
+
export * from "./game-definition";
|
|
20
|
+
export * from "./history";
|
|
21
|
+
export * from "./logging";
|
|
22
|
+
export * from "./moves/create-move";
|
|
23
|
+
export * from "./moves/move-enumeration";
|
|
24
|
+
export * from "./moves/move-executor";
|
|
25
|
+
export * from "./moves/move-system";
|
|
26
|
+
export * from "./moves/standard-moves";
|
|
27
|
+
export * from "./operations";
|
|
28
|
+
export * from "./rng/seeded-rng";
|
|
29
|
+
export * from "./targeting";
|
|
30
|
+
export * from "./telemetry";
|
|
31
|
+
export * from "./types";
|
|
32
|
+
export * from "./zones";
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Logging Module
|
|
3
|
+
*
|
|
4
|
+
* Production-grade structured logging for TCG Core.
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* ```typescript
|
|
8
|
+
* import { Logger, LogLevel } from '@drmxrcy/tcg-core/logging';
|
|
9
|
+
*
|
|
10
|
+
* const logger = new Logger({
|
|
11
|
+
* level: 'DEVELOPER',
|
|
12
|
+
* pretty: true
|
|
13
|
+
* });
|
|
14
|
+
*
|
|
15
|
+
* logger.info('Game started', { players: 2 });
|
|
16
|
+
* logger.debug('Move validated', { moveId, validationResult });
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
export { createPinoFormatter, formatMessage } from "./log-formatter";
|
|
21
|
+
export { Logger } from "./logger";
|
|
22
|
+
export {
|
|
23
|
+
type LogContext,
|
|
24
|
+
type LoggerOptions,
|
|
25
|
+
LogLevel,
|
|
26
|
+
type VerbosityPreset,
|
|
27
|
+
} from "./types";
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Log Formatter
|
|
3
|
+
*
|
|
4
|
+
* Custom formatters for player-friendly log messages.
|
|
5
|
+
* Transforms internal engine events into readable output.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { LogContext, VerbosityPreset } from "./types";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Format log message for player-friendly output
|
|
12
|
+
*
|
|
13
|
+
* Transforms raw log messages and context into readable format
|
|
14
|
+
* based on the current verbosity level.
|
|
15
|
+
*
|
|
16
|
+
* @param message - Raw log message
|
|
17
|
+
* @param context - Log context with metadata
|
|
18
|
+
* @param level - Current verbosity preset
|
|
19
|
+
* @returns Formatted message string
|
|
20
|
+
*/
|
|
21
|
+
export function formatMessage(
|
|
22
|
+
message: string,
|
|
23
|
+
context: LogContext,
|
|
24
|
+
level: VerbosityPreset,
|
|
25
|
+
): string {
|
|
26
|
+
// For SILENT, return empty (should never be called)
|
|
27
|
+
if (level === "SILENT") {
|
|
28
|
+
return "";
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// NORMAL_PLAYER: Simple, clear messages
|
|
32
|
+
if (level === "NORMAL_PLAYER") {
|
|
33
|
+
return formatForNormalPlayer(message, context);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// ADVANCED_PLAYER: Include game mechanics details
|
|
37
|
+
if (level === "ADVANCED_PLAYER") {
|
|
38
|
+
return formatForAdvancedPlayer(message, context);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// DEVELOPER: Full internal details
|
|
42
|
+
return formatForDeveloper(message, context);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Format message for normal players
|
|
47
|
+
*
|
|
48
|
+
* Focus on basic game events without technical details.
|
|
49
|
+
* Examples: "Card played", "Turn ended", "Player drew 2 cards"
|
|
50
|
+
*/
|
|
51
|
+
function formatForNormalPlayer(message: string, context: LogContext): string {
|
|
52
|
+
let formatted = message;
|
|
53
|
+
|
|
54
|
+
// Add player context if available
|
|
55
|
+
if (context.playerId) {
|
|
56
|
+
formatted = `[Player ${context.playerId}] ${formatted}`;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Add turn context if available
|
|
60
|
+
if (context.turn !== undefined) {
|
|
61
|
+
formatted = `[Turn ${context.turn}] ${formatted}`;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return formatted;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Format message for advanced players
|
|
69
|
+
*
|
|
70
|
+
* Include game mechanics and rule details.
|
|
71
|
+
* Examples: "Card played (cost: 3 mana)", "Phase transition: Main -> Combat"
|
|
72
|
+
*/
|
|
73
|
+
function formatForAdvancedPlayer(message: string, context: LogContext): string {
|
|
74
|
+
let formatted = message;
|
|
75
|
+
|
|
76
|
+
// Add comprehensive game state context
|
|
77
|
+
const contextParts: string[] = [];
|
|
78
|
+
|
|
79
|
+
if (context.turn !== undefined) {
|
|
80
|
+
contextParts.push(`Turn ${context.turn}`);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (context.phase) {
|
|
84
|
+
contextParts.push(`Phase: ${context.phase}`);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (context.playerId) {
|
|
88
|
+
contextParts.push(`Player: ${context.playerId}`);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (contextParts.length > 0) {
|
|
92
|
+
formatted = `[${contextParts.join(" | ")}] ${formatted}`;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Add duration for performance-sensitive operations
|
|
96
|
+
if (context.duration !== undefined) {
|
|
97
|
+
formatted += ` (${context.duration}ms)`;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return formatted;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Format message for developers
|
|
105
|
+
*
|
|
106
|
+
* Include all internal details for debugging.
|
|
107
|
+
* Examples: Full context object, stack traces, state diffs
|
|
108
|
+
*/
|
|
109
|
+
function formatForDeveloper(message: string, context: LogContext): string {
|
|
110
|
+
let formatted = message;
|
|
111
|
+
|
|
112
|
+
// Add full context breadcrumbs
|
|
113
|
+
const contextParts: string[] = [];
|
|
114
|
+
|
|
115
|
+
if (context.turn !== undefined) {
|
|
116
|
+
contextParts.push(`T${context.turn}`);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (context.segment) {
|
|
120
|
+
contextParts.push(`S:${context.segment}`);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
if (context.phase) {
|
|
124
|
+
contextParts.push(`P:${context.phase}`);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
if (context.playerId) {
|
|
128
|
+
contextParts.push(`Player:${context.playerId}`);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
if (context.moveId) {
|
|
132
|
+
contextParts.push(`Move:${context.moveId}`);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
if (contextParts.length > 0) {
|
|
136
|
+
formatted = `[${contextParts.join("|")}] ${formatted}`;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Add performance metrics
|
|
140
|
+
if (context.duration !== undefined) {
|
|
141
|
+
formatted += ` [${context.duration}ms]`;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if (context.patchCount !== undefined) {
|
|
145
|
+
formatted += ` [${context.patchCount} patches]`;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Add error details
|
|
149
|
+
if (context.errorCode) {
|
|
150
|
+
formatted += ` [${context.errorCode}]`;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
return formatted;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Create Pino formatter options
|
|
158
|
+
*
|
|
159
|
+
* Returns Pino transport configuration for pretty printing
|
|
160
|
+
* based on verbosity level.
|
|
161
|
+
*
|
|
162
|
+
* @param level - Verbosity preset
|
|
163
|
+
* @returns Pino transport options
|
|
164
|
+
*/
|
|
165
|
+
export function createPinoFormatter(level: VerbosityPreset): unknown {
|
|
166
|
+
// For SILENT, no formatter needed
|
|
167
|
+
if (level === "SILENT") {
|
|
168
|
+
return undefined;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// Configure pino-pretty for human-readable output
|
|
172
|
+
return {
|
|
173
|
+
target: "pino-pretty",
|
|
174
|
+
options: {
|
|
175
|
+
colorize: true,
|
|
176
|
+
translateTime: "HH:MM:ss.l",
|
|
177
|
+
ignore: "pid,hostname",
|
|
178
|
+
// Customize format based on level
|
|
179
|
+
messageFormat:
|
|
180
|
+
level === "DEVELOPER"
|
|
181
|
+
? "{msg} {context}"
|
|
182
|
+
: level === "ADVANCED_PLAYER"
|
|
183
|
+
? "{msg}"
|
|
184
|
+
: "{msg}",
|
|
185
|
+
},
|
|
186
|
+
};
|
|
187
|
+
}
|
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Logger Class
|
|
3
|
+
*
|
|
4
|
+
* Core logging implementation wrapping Pino with TCG-specific features.
|
|
5
|
+
* Provides structured logging with configurable verbosity levels.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import pino, { type Logger as PinoLogger } from "pino";
|
|
9
|
+
import { type LogContext, type LoggerOptions, LogLevel } from "./types";
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Logger
|
|
13
|
+
*
|
|
14
|
+
* Structured logger with verbosity control and child logger support.
|
|
15
|
+
*
|
|
16
|
+
* Features:
|
|
17
|
+
* - Zero-overhead SILENT mode
|
|
18
|
+
* - Child loggers with namespace isolation
|
|
19
|
+
* - Structured context merging
|
|
20
|
+
* - Preset-based verbosity levels
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* ```typescript
|
|
24
|
+
* const logger = new Logger({ level: 'DEVELOPER', pretty: true });
|
|
25
|
+
*
|
|
26
|
+
* logger.info('Game started', { players: 2 });
|
|
27
|
+
* logger.debug('Move executed', { moveId: 'playCard', duration: 15 });
|
|
28
|
+
*
|
|
29
|
+
* const childLogger = logger.child('flow');
|
|
30
|
+
* childLogger.info('Phase transition', { from: 'main', to: 'combat' });
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
export class Logger {
|
|
34
|
+
private pinoLogger: PinoLogger | null;
|
|
35
|
+
private currentLevel: LogLevel;
|
|
36
|
+
private namespace?: string;
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Create a new Logger instance
|
|
40
|
+
*
|
|
41
|
+
* @param options - Logger configuration
|
|
42
|
+
*/
|
|
43
|
+
constructor(options: LoggerOptions = {}) {
|
|
44
|
+
const level = options.level ?? "SILENT";
|
|
45
|
+
|
|
46
|
+
// Map preset/level to numeric LogLevel
|
|
47
|
+
this.currentLevel = this.mapToLogLevel(level);
|
|
48
|
+
|
|
49
|
+
// For SILENT mode, skip Pino initialization entirely (zero overhead)
|
|
50
|
+
if (this.currentLevel === LogLevel.SILENT) {
|
|
51
|
+
this.pinoLogger = null;
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Map to Pino level strings
|
|
56
|
+
const pinoLevel = this.mapToPinoLevel(this.currentLevel);
|
|
57
|
+
|
|
58
|
+
// Configure Pino
|
|
59
|
+
const { level: _, ...restOptions } = options;
|
|
60
|
+
const pinoOptions: pino.LoggerOptions = {
|
|
61
|
+
level: pinoLevel,
|
|
62
|
+
...restOptions,
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
// Add pretty printing if requested
|
|
66
|
+
if (options.pretty !== false) {
|
|
67
|
+
pinoOptions.transport = {
|
|
68
|
+
target: "pino-pretty",
|
|
69
|
+
options: {
|
|
70
|
+
colorize: true,
|
|
71
|
+
translateTime: "HH:MM:ss.l",
|
|
72
|
+
ignore: "pid,hostname",
|
|
73
|
+
},
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Create Pino logger
|
|
78
|
+
this.pinoLogger = pino(pinoOptions);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Map preset or numeric level to LogLevel enum
|
|
83
|
+
*/
|
|
84
|
+
private mapToLogLevel(level: string | LogLevel): LogLevel {
|
|
85
|
+
if (typeof level === "number") {
|
|
86
|
+
return level;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Map preset strings to LogLevel
|
|
90
|
+
const presetMap: Record<string, LogLevel> = {
|
|
91
|
+
SILENT: LogLevel.SILENT,
|
|
92
|
+
NORMAL_PLAYER: LogLevel.INFO,
|
|
93
|
+
ADVANCED_PLAYER: LogLevel.DEBUG,
|
|
94
|
+
DEVELOPER: LogLevel.TRACE,
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
return presetMap[level] ?? LogLevel.SILENT;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Map LogLevel to Pino level string
|
|
102
|
+
*/
|
|
103
|
+
private mapToPinoLevel(level: LogLevel): pino.Level | "silent" {
|
|
104
|
+
const levelMap: Record<LogLevel, pino.Level | "silent"> = {
|
|
105
|
+
[LogLevel.SILENT]: "silent",
|
|
106
|
+
[LogLevel.ERROR]: "error",
|
|
107
|
+
[LogLevel.WARN]: "warn",
|
|
108
|
+
[LogLevel.INFO]: "info",
|
|
109
|
+
[LogLevel.DEBUG]: "debug",
|
|
110
|
+
[LogLevel.TRACE]: "trace",
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
return levelMap[level] ?? "silent";
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Check if a specific level is enabled
|
|
118
|
+
*/
|
|
119
|
+
private isLevelEnabled(level: LogLevel): boolean {
|
|
120
|
+
return this.currentLevel >= level;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Merge namespace into context
|
|
125
|
+
*/
|
|
126
|
+
private mergeContext(context?: LogContext): LogContext {
|
|
127
|
+
if (!this.namespace) {
|
|
128
|
+
return context ?? {};
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
return {
|
|
132
|
+
namespace: this.namespace,
|
|
133
|
+
...context,
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Log trace message (TRACE level)
|
|
139
|
+
*
|
|
140
|
+
* Most verbose logging for internal operations.
|
|
141
|
+
* Use for: operation details, state diffs, decision trees
|
|
142
|
+
*
|
|
143
|
+
* @param message - Log message
|
|
144
|
+
* @param context - Structured context
|
|
145
|
+
*/
|
|
146
|
+
trace(message: string, context?: LogContext): void {
|
|
147
|
+
// Zero-overhead check for SILENT mode
|
|
148
|
+
if (!(this.pinoLogger && this.isLevelEnabled(LogLevel.TRACE))) {
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
this.pinoLogger.trace(this.mergeContext(context), message);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Log debug message (DEBUG level)
|
|
157
|
+
*
|
|
158
|
+
* Detailed information for debugging and analysis.
|
|
159
|
+
* Use for: rule evaluations, condition checks, mechanics
|
|
160
|
+
*
|
|
161
|
+
* @param message - Log message
|
|
162
|
+
* @param context - Structured context
|
|
163
|
+
*/
|
|
164
|
+
debug(message: string, context?: LogContext): void {
|
|
165
|
+
// Zero-overhead check for SILENT mode
|
|
166
|
+
if (!(this.pinoLogger && this.isLevelEnabled(LogLevel.DEBUG))) {
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
this.pinoLogger.debug(this.mergeContext(context), message);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Log info message (INFO level)
|
|
175
|
+
*
|
|
176
|
+
* Standard informational messages about game events.
|
|
177
|
+
* Use for: move execution, phase transitions, game events
|
|
178
|
+
*
|
|
179
|
+
* @param message - Log message
|
|
180
|
+
* @param context - Structured context
|
|
181
|
+
*/
|
|
182
|
+
info(message: string, context?: LogContext): void {
|
|
183
|
+
// Zero-overhead check for SILENT mode
|
|
184
|
+
if (!(this.pinoLogger && this.isLevelEnabled(LogLevel.INFO))) {
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
this.pinoLogger.info(this.mergeContext(context), message);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Log warning message (WARN level)
|
|
193
|
+
*
|
|
194
|
+
* Warning conditions that don't prevent execution.
|
|
195
|
+
* Use for: failed conditions, invalid moves, edge cases
|
|
196
|
+
*
|
|
197
|
+
* @param message - Log message
|
|
198
|
+
* @param context - Structured context
|
|
199
|
+
*/
|
|
200
|
+
warn(message: string, context?: LogContext): void {
|
|
201
|
+
// Zero-overhead check for SILENT mode
|
|
202
|
+
if (!(this.pinoLogger && this.isLevelEnabled(LogLevel.WARN))) {
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
this.pinoLogger.warn(this.mergeContext(context), message);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Log error message (ERROR level)
|
|
211
|
+
*
|
|
212
|
+
* Error conditions requiring attention.
|
|
213
|
+
* Use for: exceptions, failures, critical errors
|
|
214
|
+
*
|
|
215
|
+
* @param message - Log message
|
|
216
|
+
* @param context - Structured context
|
|
217
|
+
*/
|
|
218
|
+
error(message: string, context?: LogContext): void {
|
|
219
|
+
// Zero-overhead check for SILENT mode
|
|
220
|
+
if (!(this.pinoLogger && this.isLevelEnabled(LogLevel.ERROR))) {
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
this.pinoLogger.error(this.mergeContext(context), message);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Create child logger with namespace
|
|
229
|
+
*
|
|
230
|
+
* Child loggers inherit configuration but add a namespace
|
|
231
|
+
* to all log entries for filtering and organization.
|
|
232
|
+
*
|
|
233
|
+
* @param namespace - Namespace identifier (e.g., 'flow', 'zones')
|
|
234
|
+
* @returns New Logger instance with namespace
|
|
235
|
+
*
|
|
236
|
+
* @example
|
|
237
|
+
* ```typescript
|
|
238
|
+
* const logger = new Logger({ level: 'DEBUG' });
|
|
239
|
+
* const flowLogger = logger.child('flow');
|
|
240
|
+
*
|
|
241
|
+
* flowLogger.info('Phase transition');
|
|
242
|
+
* // Output: [flow] Phase transition
|
|
243
|
+
* ```
|
|
244
|
+
*/
|
|
245
|
+
child(namespace: string): Logger {
|
|
246
|
+
// For SILENT mode, return a new SILENT logger
|
|
247
|
+
if (!this.pinoLogger) {
|
|
248
|
+
return new Logger({ level: "SILENT" });
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// Create child with Pino child logger
|
|
252
|
+
const childLogger = new Logger({ level: this.currentLevel });
|
|
253
|
+
childLogger.pinoLogger = this.pinoLogger.child({ namespace });
|
|
254
|
+
childLogger.namespace = namespace;
|
|
255
|
+
|
|
256
|
+
return childLogger;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* Get current log level
|
|
261
|
+
*
|
|
262
|
+
* @returns Current LogLevel
|
|
263
|
+
*/
|
|
264
|
+
getLevel(): LogLevel {
|
|
265
|
+
return this.currentLevel;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Check if logger is in SILENT mode
|
|
270
|
+
*
|
|
271
|
+
* @returns True if no logging will occur
|
|
272
|
+
*/
|
|
273
|
+
isSilent(): boolean {
|
|
274
|
+
return this.currentLevel === LogLevel.SILENT;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Logging Types
|
|
3
|
+
*
|
|
4
|
+
* Type definitions for the TCG Core logging system.
|
|
5
|
+
* Provides structured logging with configurable verbosity levels.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Log Level Enum
|
|
10
|
+
*
|
|
11
|
+
* Numeric levels for log filtering (lower = more critical)
|
|
12
|
+
* - SILENT (0): No logging
|
|
13
|
+
* - ERROR (1): Error conditions only
|
|
14
|
+
* - WARN (2): Warning conditions
|
|
15
|
+
* - INFO (3): Informational messages
|
|
16
|
+
* - DEBUG (4): Debug-level messages
|
|
17
|
+
* - TRACE (5): Trace-level messages (most verbose)
|
|
18
|
+
*/
|
|
19
|
+
export enum LogLevel {
|
|
20
|
+
SILENT = 0,
|
|
21
|
+
ERROR = 1,
|
|
22
|
+
WARN = 2,
|
|
23
|
+
INFO = 3,
|
|
24
|
+
DEBUG = 4,
|
|
25
|
+
TRACE = 5,
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Verbosity Preset
|
|
30
|
+
*
|
|
31
|
+
* Named presets for different user types:
|
|
32
|
+
* - SILENT: No logging (production default)
|
|
33
|
+
* - NORMAL_PLAYER: Basic game events (INFO level)
|
|
34
|
+
* - ADVANCED_PLAYER: Detailed mechanics (DEBUG level)
|
|
35
|
+
* - DEVELOPER: Full internal details (TRACE level)
|
|
36
|
+
*/
|
|
37
|
+
export type VerbosityPreset =
|
|
38
|
+
| "SILENT"
|
|
39
|
+
| "NORMAL_PLAYER"
|
|
40
|
+
| "ADVANCED_PLAYER"
|
|
41
|
+
| "DEVELOPER";
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Log Context
|
|
45
|
+
*
|
|
46
|
+
* Structured context information attached to log entries.
|
|
47
|
+
* Provides rich metadata for filtering, searching, and analysis.
|
|
48
|
+
*
|
|
49
|
+
* Common fields:
|
|
50
|
+
* - moveId: Move being executed
|
|
51
|
+
* - playerId: Player performing action
|
|
52
|
+
* - phase: Current game phase
|
|
53
|
+
* - turn: Current turn number
|
|
54
|
+
* - timestamp: Event timestamp
|
|
55
|
+
*
|
|
56
|
+
* Games can add custom fields via index signature.
|
|
57
|
+
*/
|
|
58
|
+
export type LogContext = {
|
|
59
|
+
/** Move identifier */
|
|
60
|
+
moveId?: string;
|
|
61
|
+
/** Player identifier */
|
|
62
|
+
playerId?: string;
|
|
63
|
+
/** Current game phase */
|
|
64
|
+
phase?: string;
|
|
65
|
+
/** Current game segment */
|
|
66
|
+
segment?: string;
|
|
67
|
+
/** Current turn number */
|
|
68
|
+
turn?: number;
|
|
69
|
+
/** Event timestamp */
|
|
70
|
+
timestamp?: number;
|
|
71
|
+
/** Error message (for error logs) */
|
|
72
|
+
error?: string;
|
|
73
|
+
/** Stack trace (for error logs) */
|
|
74
|
+
stack?: string;
|
|
75
|
+
/** Error code (for structured errors) */
|
|
76
|
+
errorCode?: string;
|
|
77
|
+
/** Operation duration in milliseconds */
|
|
78
|
+
duration?: number;
|
|
79
|
+
/** Number of patches generated */
|
|
80
|
+
patchCount?: number;
|
|
81
|
+
/** Custom context fields */
|
|
82
|
+
[key: string]: unknown;
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Logger Options
|
|
87
|
+
*
|
|
88
|
+
* Configuration for Logger instances.
|
|
89
|
+
*
|
|
90
|
+
* @example
|
|
91
|
+
* ```typescript
|
|
92
|
+
* // Use preset for convenience
|
|
93
|
+
* const options: LoggerOptions = {
|
|
94
|
+
* level: 'DEVELOPER',
|
|
95
|
+
* pretty: true
|
|
96
|
+
* };
|
|
97
|
+
*
|
|
98
|
+
* // Or use numeric level for fine control
|
|
99
|
+
* const options: LoggerOptions = {
|
|
100
|
+
* level: LogLevel.DEBUG,
|
|
101
|
+
* pretty: false,
|
|
102
|
+
* destination: process.stdout
|
|
103
|
+
* };
|
|
104
|
+
* ```
|
|
105
|
+
*/
|
|
106
|
+
export type LoggerOptions = {
|
|
107
|
+
/**
|
|
108
|
+
* Log level or verbosity preset
|
|
109
|
+
*
|
|
110
|
+
* Controls which messages are logged:
|
|
111
|
+
* - String presets: SILENT, NORMAL_PLAYER, ADVANCED_PLAYER, DEVELOPER
|
|
112
|
+
* - Numeric levels: LogLevel.SILENT through LogLevel.TRACE
|
|
113
|
+
*
|
|
114
|
+
* Default: 'SILENT' (no logging)
|
|
115
|
+
*/
|
|
116
|
+
level?: VerbosityPreset | LogLevel;
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Enable pretty printing
|
|
120
|
+
*
|
|
121
|
+
* When true, formats logs for human readability.
|
|
122
|
+
* When false, outputs JSON for machine consumption.
|
|
123
|
+
*
|
|
124
|
+
* Default: true
|
|
125
|
+
*/
|
|
126
|
+
pretty?: boolean;
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Log destination
|
|
130
|
+
*
|
|
131
|
+
* Where to write log output:
|
|
132
|
+
* - process.stdout (default)
|
|
133
|
+
* - process.stderr
|
|
134
|
+
* - Writable stream
|
|
135
|
+
* - File descriptor
|
|
136
|
+
*
|
|
137
|
+
* Default: process.stdout
|
|
138
|
+
*/
|
|
139
|
+
destination?: unknown;
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Additional Pino options
|
|
143
|
+
*
|
|
144
|
+
* Advanced configuration passed directly to Pino.
|
|
145
|
+
* See Pino documentation for available options.
|
|
146
|
+
*/
|
|
147
|
+
[key: string]: unknown;
|
|
148
|
+
};
|