@pilaf/backends 1.0.1 → 1.2.0
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 +1170 -10
- package/lib/collectors/DockerLogCollector.js +334 -0
- package/lib/collectors/index.js +9 -0
- package/lib/core/CommandRouter.js +154 -0
- package/lib/core/CorrelationStrategy.js +172 -0
- package/lib/core/LogCollector.js +194 -0
- package/lib/core/LogParser.js +125 -0
- package/lib/core/index.js +26 -0
- package/lib/errors/index.js +363 -0
- package/lib/helpers/EventObserver.js +267 -0
- package/lib/helpers/QueryHelper.js +279 -0
- package/lib/helpers/index.js +13 -0
- package/lib/index.js +72 -1
- package/lib/mineflayer-backend.js +266 -1
- package/lib/monitoring/CircularBuffer.js +202 -0
- package/lib/monitoring/LogMonitor.js +303 -0
- package/lib/monitoring/correlations/TagCorrelationStrategy.js +214 -0
- package/lib/monitoring/correlations/UsernameCorrelationStrategy.js +233 -0
- package/lib/monitoring/correlations/index.js +13 -0
- package/lib/monitoring/index.js +16 -0
- package/lib/parsers/MinecraftLogParser.js +512 -0
- package/lib/parsers/PatternRegistry.js +366 -0
- package/lib/parsers/fixtures/minecraft-logs.js +188 -0
- package/lib/parsers/index.js +13 -0
- package/lib/rcon-backend.js +42 -26
- package/package.json +2 -1
|
@@ -0,0 +1,512 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MinecraftLogParser - Parser for Minecraft server logs
|
|
3
|
+
*
|
|
4
|
+
* Parses raw Minecraft server log lines into structured events.
|
|
5
|
+
* Supports Minecraft versions 1.19, 1.20, 1.21+.
|
|
6
|
+
*
|
|
7
|
+
* Responsibilities (MECE):
|
|
8
|
+
* - ONLY: Parse raw log lines into structured events
|
|
9
|
+
* - NOT: Collect logs (LogCollector's responsibility)
|
|
10
|
+
* - NOT: Correlate events (CorrelationStrategy's responsibility)
|
|
11
|
+
*
|
|
12
|
+
* Event Categories:
|
|
13
|
+
* - Entity: Player join/leave/death/spawn
|
|
14
|
+
* - Movement: Teleport events
|
|
15
|
+
* - Command: Server command execution
|
|
16
|
+
* - World: Time/weather/save changes
|
|
17
|
+
* - Status: Server lifecycle events
|
|
18
|
+
* - Plugin: Custom plugin events (extensible)
|
|
19
|
+
*
|
|
20
|
+
* Usage Example:
|
|
21
|
+
* const parser = new MinecraftLogParser();
|
|
22
|
+
* const event = parser.parse('[12:34:56] [Server thread/INFO]: TestPlayer joined');
|
|
23
|
+
* // Returns event object with type, data, raw, timestamp, thread, level
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
const { LogParser } = require('../core/LogParser.js');
|
|
27
|
+
const { PatternRegistry } = require('./PatternRegistry.js');
|
|
28
|
+
const { UnknownPatternError } = require('../errors/index.js');
|
|
29
|
+
|
|
30
|
+
class MinecraftLogParser extends LogParser {
|
|
31
|
+
/**
|
|
32
|
+
* Create a MinecraftLogParser
|
|
33
|
+
* @param {Object} options - Parser options
|
|
34
|
+
* @param {boolean} [options.includeMetadata=true] - Include timestamp/thread/level in output
|
|
35
|
+
* @param {boolean} [options.strictMode=false] - Throw on unknown patterns (vs return null)
|
|
36
|
+
*/
|
|
37
|
+
constructor(options = {}) {
|
|
38
|
+
super();
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Parser options
|
|
42
|
+
* @private
|
|
43
|
+
* @type {Object}
|
|
44
|
+
*/
|
|
45
|
+
this._options = {
|
|
46
|
+
includeMetadata: options?.includeMetadata !== false,
|
|
47
|
+
strictMode: options?.strictMode || false
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Pattern registry for all log patterns
|
|
52
|
+
* @private
|
|
53
|
+
* @type {PatternRegistry}
|
|
54
|
+
*/
|
|
55
|
+
this._registry = new PatternRegistry();
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Initialize all pattern categories
|
|
59
|
+
* @private
|
|
60
|
+
*/
|
|
61
|
+
this._initializePatterns();
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Parse a log line into a structured event
|
|
66
|
+
*
|
|
67
|
+
* @param {string} line - Raw log line
|
|
68
|
+
* @returns {Object|null} - Parsed event or null if no pattern matches
|
|
69
|
+
* @property {string} type - Event type (e.g., 'entity.join', 'movement.teleport')
|
|
70
|
+
* @property {Object} data - Event data (extracted from pattern)
|
|
71
|
+
* @property {string} raw - Original log line
|
|
72
|
+
* @property {string} [timestamp] - Time from log line (if includeMetadata)
|
|
73
|
+
* @property {string} [thread] - Thread name (if includeMetadata)
|
|
74
|
+
* @property {string} [level] - Log level (if includeMetadata)
|
|
75
|
+
* @throws {UnknownPatternError} If strictMode is true and pattern doesn't match
|
|
76
|
+
*/
|
|
77
|
+
parse(line) {
|
|
78
|
+
if (!line || typeof line !== 'string') {
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Trim the line
|
|
83
|
+
const trimmed = line.trim();
|
|
84
|
+
if (!trimmed) {
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Try to match against all patterns in priority order
|
|
89
|
+
const match = this._registry.match(trimmed);
|
|
90
|
+
|
|
91
|
+
if (!match) {
|
|
92
|
+
if (this._options.strictMode) {
|
|
93
|
+
throw new UnknownPatternError(trimmed);
|
|
94
|
+
}
|
|
95
|
+
// Even when no pattern matches, return metadata if requested
|
|
96
|
+
if (this._options.includeMetadata) {
|
|
97
|
+
const metadata = this._extractMetadata(trimmed);
|
|
98
|
+
return {
|
|
99
|
+
type: null,
|
|
100
|
+
data: null,
|
|
101
|
+
raw: trimmed,
|
|
102
|
+
...metadata
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
return null;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Build result object
|
|
109
|
+
const result = {
|
|
110
|
+
type: match.name,
|
|
111
|
+
data: match.data,
|
|
112
|
+
raw: trimmed
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
// Add metadata if requested
|
|
116
|
+
if (this._options.includeMetadata) {
|
|
117
|
+
const metadata = this._extractMetadata(trimmed);
|
|
118
|
+
Object.assign(result, metadata);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return result;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Extract metadata (timestamp, thread, level) from log line
|
|
126
|
+
*
|
|
127
|
+
* @private
|
|
128
|
+
* @param {string} line - Log line
|
|
129
|
+
* @returns {Object} - Extracted metadata
|
|
130
|
+
*/
|
|
131
|
+
_extractMetadata(line) {
|
|
132
|
+
// Minecraft log format: [HH:MM:SS] [Thread/LEVEL]: Message
|
|
133
|
+
// Or: [HH:MM:SS] [Thread/INFO]: [uuid] Message (for some versions)
|
|
134
|
+
|
|
135
|
+
const metadataRegex = /^\[(\d{2}:\d{2}:\d{2})\]\s+\[([^\]]+)\/(INFO|WARN|ERROR|DEBUG)\]:/;
|
|
136
|
+
const match = line.match(metadataRegex);
|
|
137
|
+
|
|
138
|
+
if (match) {
|
|
139
|
+
return {
|
|
140
|
+
timestamp: match[1],
|
|
141
|
+
thread: match[2],
|
|
142
|
+
level: match[3]
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Fallback: try to extract just timestamp
|
|
147
|
+
const timestampRegex = /^\[(\d{2}:\d{2}:\d{2})\]/;
|
|
148
|
+
const timestampMatch = line.match(timestampRegex);
|
|
149
|
+
|
|
150
|
+
if (timestampMatch) {
|
|
151
|
+
return {
|
|
152
|
+
timestamp: timestampMatch[1],
|
|
153
|
+
thread: null,
|
|
154
|
+
level: null
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
return {
|
|
159
|
+
timestamp: null,
|
|
160
|
+
thread: null,
|
|
161
|
+
level: null
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Initialize all Minecraft log patterns
|
|
167
|
+
*
|
|
168
|
+
* Patterns are registered in priority order (higher priority = tested first).
|
|
169
|
+
* Priority ranges:
|
|
170
|
+
* - 10+: Most specific patterns (teleport with coordinates)
|
|
171
|
+
* - 8-9: Specific sub-categories (death types)
|
|
172
|
+
* - 5-7: Player actions and commands
|
|
173
|
+
* - 3-4: World state changes
|
|
174
|
+
* - 1-2: Status messages
|
|
175
|
+
* - 0: Plugin/custom patterns (fallback)
|
|
176
|
+
*
|
|
177
|
+
* @private
|
|
178
|
+
*/
|
|
179
|
+
_initializePatterns() {
|
|
180
|
+
// =========================================================================
|
|
181
|
+
// PRIORITY 10: Movement Events (Most Specific)
|
|
182
|
+
// =========================================================================
|
|
183
|
+
|
|
184
|
+
this._registry.addPattern(
|
|
185
|
+
'movement.teleport',
|
|
186
|
+
/Teleported\s+(\w+)\s+from\s+([\d.-]+),\s*([\d.-]+),\s*([\d.-]+)\s+to\s+([\d.-]+),\s*([\d.-]+),\s*([\d.-]+)/,
|
|
187
|
+
(match) => ({
|
|
188
|
+
player: match[1],
|
|
189
|
+
from: { x: parseFloat(match[2]), y: parseFloat(match[3]), z: parseFloat(match[4]) },
|
|
190
|
+
to: { x: parseFloat(match[5]), y: parseFloat(match[6]), z: parseFloat(match[7]) }
|
|
191
|
+
}),
|
|
192
|
+
10
|
|
193
|
+
);
|
|
194
|
+
|
|
195
|
+
// =========================================================================
|
|
196
|
+
// PRIORITY 8: Entity Death Events
|
|
197
|
+
// =========================================================================
|
|
198
|
+
|
|
199
|
+
// Death by entity
|
|
200
|
+
this._registry.addPattern(
|
|
201
|
+
'entity.death.slain',
|
|
202
|
+
/(\w+)\s+was\s+slain\s+by\s+(.+)/,
|
|
203
|
+
(match) => ({
|
|
204
|
+
player: match[1],
|
|
205
|
+
killer: match[2],
|
|
206
|
+
cause: 'entity_attack'
|
|
207
|
+
}),
|
|
208
|
+
8
|
|
209
|
+
);
|
|
210
|
+
|
|
211
|
+
// Death by fall
|
|
212
|
+
this._registry.addPattern(
|
|
213
|
+
'entity.death.fall',
|
|
214
|
+
/(\w+)\s+fell\s+from\s+a\s+high\s+place/,
|
|
215
|
+
(match) => ({
|
|
216
|
+
player: match[1],
|
|
217
|
+
cause: 'fall'
|
|
218
|
+
}),
|
|
219
|
+
8
|
|
220
|
+
);
|
|
221
|
+
|
|
222
|
+
// Death by fire
|
|
223
|
+
this._registry.addPattern(
|
|
224
|
+
'entity.death.fire',
|
|
225
|
+
/(\w+)\s+(?:burned\s+to\s+death|was\s+burnt\s+to\s+a\s+crisp)/,
|
|
226
|
+
(match) => ({
|
|
227
|
+
player: match[1],
|
|
228
|
+
cause: 'fire'
|
|
229
|
+
}),
|
|
230
|
+
8
|
|
231
|
+
);
|
|
232
|
+
|
|
233
|
+
// Death by lava
|
|
234
|
+
this._registry.addPattern(
|
|
235
|
+
'entity.death.lava',
|
|
236
|
+
/(\w+)\s+(?:tried\s+to\s+swim\s+in\s+lava|was\s+killed\s+by\s+(?:Magma|Lava)(?:\s+Block)?)/,
|
|
237
|
+
(match) => ({
|
|
238
|
+
player: match[1],
|
|
239
|
+
cause: 'lava'
|
|
240
|
+
}),
|
|
241
|
+
8
|
|
242
|
+
);
|
|
243
|
+
|
|
244
|
+
// Death by drowning
|
|
245
|
+
this._registry.addPattern(
|
|
246
|
+
'entity.death.drown',
|
|
247
|
+
/(\w+)\s+drowned/,
|
|
248
|
+
(match) => ({
|
|
249
|
+
player: match[1],
|
|
250
|
+
cause: 'drown'
|
|
251
|
+
}),
|
|
252
|
+
8
|
|
253
|
+
);
|
|
254
|
+
|
|
255
|
+
// Death by sprinting into wall
|
|
256
|
+
this._registry.addPattern(
|
|
257
|
+
'entity.death.sprint',
|
|
258
|
+
/(\w+)\s+splatted\s+against\s+a\s+wall/,
|
|
259
|
+
(match) => ({
|
|
260
|
+
player: match[1],
|
|
261
|
+
cause: 'sprint_into_wall'
|
|
262
|
+
}),
|
|
263
|
+
8
|
|
264
|
+
);
|
|
265
|
+
|
|
266
|
+
// Generic death
|
|
267
|
+
this._registry.addPattern(
|
|
268
|
+
'entity.death.generic',
|
|
269
|
+
/(\w+)\s+died/,
|
|
270
|
+
(match) => ({
|
|
271
|
+
player: match[1],
|
|
272
|
+
cause: 'unknown'
|
|
273
|
+
}),
|
|
274
|
+
8
|
|
275
|
+
);
|
|
276
|
+
|
|
277
|
+
// =========================================================================
|
|
278
|
+
// PRIORITY 6: Player Actions (join, leave, command)
|
|
279
|
+
// =========================================================================
|
|
280
|
+
|
|
281
|
+
// Player joined
|
|
282
|
+
this._registry.addPattern(
|
|
283
|
+
'entity.join',
|
|
284
|
+
/([^\s]+)\s+joined\s+the\s+game/,
|
|
285
|
+
(match) => ({
|
|
286
|
+
player: match[1]
|
|
287
|
+
}),
|
|
288
|
+
6
|
|
289
|
+
);
|
|
290
|
+
|
|
291
|
+
// Player left
|
|
292
|
+
this._registry.addPattern(
|
|
293
|
+
'entity.leave',
|
|
294
|
+
/(\w+)\s+(?:lost\s+connection:\s*(.+)|left\s+the\s+game)/,
|
|
295
|
+
(match) => ({
|
|
296
|
+
player: match[1],
|
|
297
|
+
reason: match[2]?.trim() || 'Left the game'
|
|
298
|
+
}),
|
|
299
|
+
6
|
|
300
|
+
);
|
|
301
|
+
|
|
302
|
+
// Player issued command
|
|
303
|
+
this._registry.addPattern(
|
|
304
|
+
'command.issued',
|
|
305
|
+
/(\w+)\s+issued\s+server\s+command:\s*(.+)/,
|
|
306
|
+
(match) => ({
|
|
307
|
+
player: match[1],
|
|
308
|
+
command: match[2]?.trim()
|
|
309
|
+
}),
|
|
310
|
+
6
|
|
311
|
+
);
|
|
312
|
+
|
|
313
|
+
// Player UUID/spawn (modern versions)
|
|
314
|
+
this._registry.addPattern(
|
|
315
|
+
'entity.spawn',
|
|
316
|
+
/UUID\s+of\s+player\s+(\w+)\s+is\s+([a-f0-9-]{36})/,
|
|
317
|
+
(match) => ({
|
|
318
|
+
player: match[1],
|
|
319
|
+
uuid: match[2]
|
|
320
|
+
}),
|
|
321
|
+
6
|
|
322
|
+
);
|
|
323
|
+
|
|
324
|
+
// =========================================================================
|
|
325
|
+
// PRIORITY 4: World Events (time, weather, save)
|
|
326
|
+
// =========================================================================
|
|
327
|
+
|
|
328
|
+
// Time change
|
|
329
|
+
this._registry.addPattern(
|
|
330
|
+
'world.time',
|
|
331
|
+
/Changing\s+the\s+time\s+to\s+(\d+)/,
|
|
332
|
+
(match) => ({
|
|
333
|
+
time: parseInt(match[1], 10)
|
|
334
|
+
}),
|
|
335
|
+
4
|
|
336
|
+
);
|
|
337
|
+
|
|
338
|
+
// Weather change
|
|
339
|
+
this._registry.addPattern(
|
|
340
|
+
'world.weather',
|
|
341
|
+
/Changing\s+the\s+weather\s+to\s+(\w+)/,
|
|
342
|
+
(match) => ({
|
|
343
|
+
weather: match[1]
|
|
344
|
+
}),
|
|
345
|
+
4
|
|
346
|
+
);
|
|
347
|
+
|
|
348
|
+
// Difficulty change
|
|
349
|
+
this._registry.addPattern(
|
|
350
|
+
'world.difficulty',
|
|
351
|
+
/Changing\s+the\s+difficulty\s+to\s+(\w+)/,
|
|
352
|
+
(match) => ({
|
|
353
|
+
difficulty: match[1]
|
|
354
|
+
}),
|
|
355
|
+
4
|
|
356
|
+
);
|
|
357
|
+
|
|
358
|
+
// Game mode change
|
|
359
|
+
this._registry.addPattern(
|
|
360
|
+
'world.gamemode',
|
|
361
|
+
/(?:The\s+game\s+mode|Gamemode)\s+has\s+been\s+updated\s+to\s+(\w+)/,
|
|
362
|
+
(match) => ({
|
|
363
|
+
gamemode: match[1]
|
|
364
|
+
}),
|
|
365
|
+
4
|
|
366
|
+
);
|
|
367
|
+
|
|
368
|
+
// Save start
|
|
369
|
+
this._registry.addPattern(
|
|
370
|
+
'world.save.start',
|
|
371
|
+
/Saving\s+(?:the\s+game|chunks\s+for\s+level)/,
|
|
372
|
+
(match) => ({}),
|
|
373
|
+
4
|
|
374
|
+
);
|
|
375
|
+
|
|
376
|
+
// Save complete
|
|
377
|
+
this._registry.addPattern(
|
|
378
|
+
'world.save.complete',
|
|
379
|
+
/Saved\s+the\s+game/,
|
|
380
|
+
(match) => ({}),
|
|
381
|
+
4
|
|
382
|
+
);
|
|
383
|
+
|
|
384
|
+
// =========================================================================
|
|
385
|
+
// PRIORITY 2: Status Events (server lifecycle)
|
|
386
|
+
// =========================================================================
|
|
387
|
+
|
|
388
|
+
// Server starting (version)
|
|
389
|
+
this._registry.addPattern(
|
|
390
|
+
'status.start',
|
|
391
|
+
/Starting\s+minecraft\s+server\s+version\s+(.+)/,
|
|
392
|
+
(match) => ({
|
|
393
|
+
version: match[1]?.trim()
|
|
394
|
+
}),
|
|
395
|
+
2
|
|
396
|
+
);
|
|
397
|
+
|
|
398
|
+
// Server starting (generic)
|
|
399
|
+
this._registry.addPattern(
|
|
400
|
+
'status.starting',
|
|
401
|
+
/Starting\s+(?:minecraft\s+server|server)/,
|
|
402
|
+
(match) => ({}),
|
|
403
|
+
2
|
|
404
|
+
);
|
|
405
|
+
|
|
406
|
+
// Loading properties
|
|
407
|
+
this._registry.addPattern(
|
|
408
|
+
'status.loading',
|
|
409
|
+
/Loading\s+(?:properties|chunks|world)/,
|
|
410
|
+
(match) => ({}),
|
|
411
|
+
2
|
|
412
|
+
);
|
|
413
|
+
|
|
414
|
+
// Default game type
|
|
415
|
+
this._registry.addPattern(
|
|
416
|
+
'status.gametype',
|
|
417
|
+
/Default\s+game\s+type:\s+(\w+)/,
|
|
418
|
+
(match) => ({
|
|
419
|
+
gameType: match[1]
|
|
420
|
+
}),
|
|
421
|
+
2
|
|
422
|
+
);
|
|
423
|
+
|
|
424
|
+
// Generating keypair
|
|
425
|
+
this._registry.addPattern(
|
|
426
|
+
'status.keypair',
|
|
427
|
+
/Generating\s+keypair/,
|
|
428
|
+
(match) => ({}),
|
|
429
|
+
2
|
|
430
|
+
);
|
|
431
|
+
|
|
432
|
+
// Preparing level
|
|
433
|
+
this._registry.addPattern(
|
|
434
|
+
'status.preparing',
|
|
435
|
+
/Preparing\s+(?:level\s+"([^"]+)"|start\s+region)/,
|
|
436
|
+
(match) => match[1] ? { level: match[1] } : {},
|
|
437
|
+
2
|
|
438
|
+
);
|
|
439
|
+
|
|
440
|
+
// Done loading
|
|
441
|
+
this._registry.addPattern(
|
|
442
|
+
'status.done',
|
|
443
|
+
/Done\s+\([^)]+\)!\s+For\s+help,\s+type\s+"help"/,
|
|
444
|
+
(match) => ({}),
|
|
445
|
+
2
|
|
446
|
+
);
|
|
447
|
+
|
|
448
|
+
// Time elapsed
|
|
449
|
+
this._registry.addPattern(
|
|
450
|
+
'status.elapsed',
|
|
451
|
+
/Time\s+elapsed:\s+(\d+)\s+ms/,
|
|
452
|
+
(match) => ({
|
|
453
|
+
elapsed: parseInt(match[1], 10)
|
|
454
|
+
}),
|
|
455
|
+
2
|
|
456
|
+
);
|
|
457
|
+
|
|
458
|
+
// =========================================================================
|
|
459
|
+
// PRIORITY 1: Plugin Events (extensible fallback)
|
|
460
|
+
// =========================================================================
|
|
461
|
+
|
|
462
|
+
// Placeholder for plugin-defined patterns
|
|
463
|
+
// Users can add custom patterns via addPattern() method
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
/**
|
|
467
|
+
* Add a custom pattern to the parser
|
|
468
|
+
*
|
|
469
|
+
* Allows users to extend the parser with plugin-specific patterns.
|
|
470
|
+
*
|
|
471
|
+
* @param {string} name - Pattern name (e.g., 'plugin.myevent')
|
|
472
|
+
* @param {string|RegExp} pattern - Regex pattern to match
|
|
473
|
+
* @param {Function} handler - Function to extract data from regex match
|
|
474
|
+
* @param {number} [priority] - Priority (0-10, default 1 for plugin patterns)
|
|
475
|
+
* @returns {void}
|
|
476
|
+
*/
|
|
477
|
+
addPattern(name, pattern, handler, priority) {
|
|
478
|
+
this._registry.addPattern(name, pattern, handler, priority ?? 1);
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
/**
|
|
482
|
+
* Remove a pattern from the parser
|
|
483
|
+
*
|
|
484
|
+
* @param {string} name - Pattern name to remove
|
|
485
|
+
* @returns {boolean} - True if pattern was removed
|
|
486
|
+
*/
|
|
487
|
+
removePattern(name) {
|
|
488
|
+
return this._registry.removePattern(name);
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
/**
|
|
492
|
+
* Get all registered patterns
|
|
493
|
+
*
|
|
494
|
+
* @returns {Array} - Array of pattern definitions
|
|
495
|
+
*/
|
|
496
|
+
getPatterns() {
|
|
497
|
+
return this._registry.getPatterns();
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
/**
|
|
501
|
+
* Clone the parser with all its patterns
|
|
502
|
+
*
|
|
503
|
+
* @returns {MinecraftLogParser} - A new parser instance with cloned patterns
|
|
504
|
+
*/
|
|
505
|
+
clone() {
|
|
506
|
+
const cloned = new MinecraftLogParser(this._options);
|
|
507
|
+
cloned._registry = this._registry.clone();
|
|
508
|
+
return cloned;
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
module.exports = { MinecraftLogParser };
|