cs2-log-parser 1.0.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.
Files changed (49) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +100 -0
  3. package/lib/assertions.d.ts +3 -0
  4. package/lib/assertions.js +13 -0
  5. package/lib/helpers.d.ts +1 -0
  6. package/lib/helpers.js +18 -0
  7. package/lib/index.d.ts +2 -0
  8. package/lib/index.js +1 -0
  9. package/lib/models.d.ts +80 -0
  10. package/lib/models.js +133 -0
  11. package/lib/parser.d.ts +9 -0
  12. package/lib/parser.js +74 -0
  13. package/lib/parsers/assisted.d.ts +8 -0
  14. package/lib/parsers/assisted.js +15 -0
  15. package/lib/parsers/attacked.d.ts +13 -0
  16. package/lib/parsers/attacked.js +25 -0
  17. package/lib/parsers/blinded.d.ts +9 -0
  18. package/lib/parsers/blinded.js +15 -0
  19. package/lib/parsers/connection.d.ts +20 -0
  20. package/lib/parsers/connection.js +34 -0
  21. package/lib/parsers/entity-triggered.d.ts +10 -0
  22. package/lib/parsers/entity-triggered.js +17 -0
  23. package/lib/parsers/index.d.ts +17 -0
  24. package/lib/parsers/index.js +17 -0
  25. package/lib/parsers/killed-by-bomb.d.ts +4 -0
  26. package/lib/parsers/killed-by-bomb.js +13 -0
  27. package/lib/parsers/killed.d.ts +9 -0
  28. package/lib/parsers/killed.js +22 -0
  29. package/lib/parsers/left-buyzone.d.ts +7 -0
  30. package/lib/parsers/left-buyzone.js +13 -0
  31. package/lib/parsers/picked-up.d.ts +7 -0
  32. package/lib/parsers/picked-up.js +13 -0
  33. package/lib/parsers/purchased.d.ts +7 -0
  34. package/lib/parsers/purchased.js +13 -0
  35. package/lib/parsers/rcon.d.ts +7 -0
  36. package/lib/parsers/rcon.js +13 -0
  37. package/lib/parsers/say.d.ts +8 -0
  38. package/lib/parsers/say.js +14 -0
  39. package/lib/parsers/scored.d.ts +8 -0
  40. package/lib/parsers/scored.js +14 -0
  41. package/lib/parsers/suicide.d.ts +7 -0
  42. package/lib/parsers/suicide.js +16 -0
  43. package/lib/parsers/switched-team.d.ts +8 -0
  44. package/lib/parsers/switched-team.js +14 -0
  45. package/lib/parsers/team-triggered.d.ts +11 -0
  46. package/lib/parsers/team-triggered.js +15 -0
  47. package/lib/parsers/threw.d.ts +9 -0
  48. package/lib/parsers/threw.js +16 -0
  49. package/package.json +43 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2020 Negezor
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,100 @@
1
+ # cs2-log-parser
2
+
3
+ A simple 0-dependency library that parses logs from a CS2 server into JavaScript objects, originally based on this fork by [BLAST](https://github.com/blastorg) (available [here](https://github.com/blastorg/srcds-log-parser)), and the original library made by [negezor](https://github.com/negezor) (available [here](https://github.com/negezor/srcds)).
4
+ These libraries however have not been updated in a while, which is the reason for making this one, which has been cleaned up & updated to the latest CS2 logging formats, with some new log parsers added.
5
+
6
+ For a list of the logs this library supports look inside the [tests](/tests/) directory.
7
+
8
+ ## Install
9
+
10
+ ```bash
11
+ npm i cs2-log-parser
12
+ ```
13
+
14
+ ## Usage
15
+
16
+ ### HTTP
17
+
18
+ CS2 supports sending logs over HTTP POST requests by giving it an address using `logaddress_add_http`.
19
+ Here is a simple example using [tinyhttp](https://github.com/tinyhttp/tinyhttp) and [milliparsec](https://github.com/tinyhttp/milliparsec) on how we can handle these on our server:
20
+
21
+ ```js
22
+ import { App } from "@tinyhttp/app";
23
+ import { text } from "milliparsec";
24
+ import { StatusCodes } from "http-status-codes";
25
+ import parse from "cs2-log-parser";
26
+
27
+ const app = new App();
28
+
29
+ app.post("/cs2_logs", text(), (req, res) => {
30
+
31
+ const logLines = req.body.split("\n");
32
+
33
+ for (const logLine of logLines) {
34
+
35
+ const event = parse(logLine, { format: "http" });
36
+
37
+ if (event === null) {
38
+ continue;
39
+ }
40
+
41
+ console.log(event);
42
+
43
+ }
44
+
45
+ res.sendStatus(StatusCodes.OK);
46
+
47
+ });
48
+
49
+ app.listen(3000);
50
+ ```
51
+
52
+ ### Log file
53
+
54
+ CS2 can write logs to a text file by setting `sv_logfile 1`, saved in a directory specified by `sv_logsdir` (defaults to `logs` inside the `game/csgo` folder).
55
+ Here is a simple example on how we can read these using Node's built-in `fs` & `path` modules:
56
+
57
+ ```js
58
+ import fs from "fs";
59
+ import path from "path";
60
+ import parse from "cs2-log-parser";
61
+
62
+ const logFilePath = path.join(
63
+ "C:",
64
+ "Program Files (x86)",
65
+ "Steam",
66
+ "steamapps",
67
+ "common",
68
+ "Counter-Strike Global Offensive",
69
+ "game",
70
+ "csgo",
71
+ "logs",
72
+ "2025_10_08_214757.log"
73
+ );
74
+
75
+ const logFileData = fs.readFileSync(logFilePath, "utf-8");
76
+
77
+ const logLines = logFileData.split("\n");
78
+
79
+ for (const logLine of logLines) {
80
+
81
+ // Log file lines begin with "L" & contain whitespace, we clean them up here
82
+ const cleanLogLine = logLine.slice(1).trim();
83
+
84
+ const event = parse(cleanLogLine, { format: "file" });
85
+
86
+ if (!event) {
87
+ continue;
88
+ }
89
+
90
+ console.log(event);
91
+
92
+ }
93
+ ```
94
+
95
+ ## Note
96
+
97
+ Some logs require additional ConVars to be set on the server in order to get logged, specifically:
98
+
99
+ - `attacked` needs `mp_logdetail` set to either `1` (to log enemy attacks), `2` (to log team attacks) or `3` (to log both).
100
+ - `picked_up` needs `mp_logdetail_items` set to `true` (or `1`).
@@ -0,0 +1,3 @@
1
+ import type { Vector, ConnectionState } from './models.js';
2
+ export declare function assertValidVector(vectorArray: number[]): asserts vectorArray is Vector;
3
+ export declare function assertValidConnectionState(state?: string): asserts state is ConnectionState;
@@ -0,0 +1,13 @@
1
+ export function assertValidVector(vectorArray) {
2
+ if (vectorArray.some((n) => isNaN(n))) {
3
+ throw new Error('Vector contains invalid number(s)');
4
+ }
5
+ if (vectorArray.length !== 3) {
6
+ throw new Error(`Vector must have 3 numbers, got ${vectorArray.length}`);
7
+ }
8
+ }
9
+ export function assertValidConnectionState(state) {
10
+ if (state !== 'connected' && state !== 'entered' && state !== 'disconnected') {
11
+ throw new Error(`Received unknown connection state, got ${state}`);
12
+ }
13
+ }
@@ -0,0 +1 @@
1
+ export declare function convertSteamIdTo64Dec(steamId: string): string;
package/lib/helpers.js ADDED
@@ -0,0 +1,18 @@
1
+ export function convertSteamIdTo64Dec(steamId) {
2
+ // old steam id format: STEAM_1:0:12345
3
+ if (steamId.includes('STEAM')) {
4
+ const steamIdSplit = steamId.split(':');
5
+ let commId = parseInt(steamIdSplit[2]) * 2;
6
+ if (steamIdSplit[1] === '1') {
7
+ commId += 1;
8
+ }
9
+ const newCommId = BigInt(commId) + BigInt(76561197960265728);
10
+ return newCommId.toString();
11
+ }
12
+ // new steam id format: [U:1:230970467]
13
+ const cleanedSteamId = steamId.replaceAll(/\[|\]/g, '');
14
+ const uSteamIdSplit = cleanedSteamId.split(':');
15
+ const commId = BigInt(uSteamIdSplit[2]) + BigInt(76561197960265728);
16
+ return commId.toString();
17
+ }
18
+ ;
package/lib/index.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ export { AssistedEvent, AttackedEvent, BlindedEvent, ConnectionEvent, EntityTriggeredEvent, KilledByBombEvent, KilledEvent, LeftBuyzoneEvent, PickedUpEvent, PurchasedEvent, RconEvent, SayEvent, ScoredEvent, SuicideEvent, SwitchedTeamEvent, TeamTriggeredEvent, ThrewEvent } from './parsers/index.js';
2
+ export { Event, ParseOptions, defaultParsers, parse as default } from './parser.js';
package/lib/index.js ADDED
@@ -0,0 +1 @@
1
+ export { defaultParsers, parse as default } from './parser.js';
@@ -0,0 +1,80 @@
1
+ export declare const baseEntityRe: RegExp;
2
+ export declare const basePlayerRe: RegExp;
3
+ export declare const vectorRe: RegExp;
4
+ export declare const entityRe: RegExp;
5
+ export declare enum Team {
6
+ Unassigned = 0,
7
+ Spectators = 1,
8
+ Terrorists = 2,
9
+ CounterTerrorists = 3,
10
+ Unknown = 999
11
+ }
12
+ export declare const teamMapping: Record<string, Team>;
13
+ export type EntityID = number;
14
+ export type KillModifier = 'attackerblind' | 'attackerinair' | 'headshot' | 'penetrated' | 'throughsmoke' | 'noscope';
15
+ export type SuicideMethod = 'world' | 'hegrenade' | 'inferno';
16
+ export type ConnectionState = 'connected' | 'entered' | 'disconnected';
17
+ export type HitGroup = 'generic' | 'head' | 'chest' | 'stomach' | 'left_arm' | 'right_arm' | 'left_leg' | 'right_leg';
18
+ export type SayTo = 'all' | 'team';
19
+ export type LogTimestampFormat = 'file' | 'http';
20
+ export type Entity = WorldEntity | ConsoleEntity | ChickenEntity | PlayerEntity | BotEntity | MultiplayerPhysicsPropEntity | DynamicPropEntity;
21
+ export type Vector = [number, number, number];
22
+ export interface BaseEvent<T, P> {
23
+ name: T;
24
+ payload: P;
25
+ receivedAt: Date;
26
+ }
27
+ export interface BaseParser<T extends BaseEvent<string, unknown>> {
28
+ name: T['name'];
29
+ patterns: RegExp[];
30
+ parse(groups: Record<string, string>): T['payload'];
31
+ }
32
+ interface BaseEntity {
33
+ type: string;
34
+ }
35
+ interface IdentifiedEntity extends BaseEntity {
36
+ id: EntityID;
37
+ }
38
+ interface PositionedEntity extends IdentifiedEntity {
39
+ position?: Vector;
40
+ }
41
+ export interface MultiplayerPhysicsPropEntity extends PositionedEntity {
42
+ type: 'prop_physics_multiplayer';
43
+ }
44
+ export interface DynamicPropEntity extends PositionedEntity {
45
+ type: 'prop_dynamic';
46
+ }
47
+ export interface WorldEntity extends BaseEntity {
48
+ type: 'world';
49
+ }
50
+ export interface ConsoleEntity extends BaseEntity {
51
+ type: 'console';
52
+ }
53
+ export interface ChickenEntity extends PositionedEntity {
54
+ type: 'chicken';
55
+ id: EntityID;
56
+ }
57
+ export interface BasePlayerEntity extends PositionedEntity {
58
+ id: EntityID;
59
+ name: string;
60
+ position?: Vector;
61
+ team: Team;
62
+ }
63
+ export interface PlayerEntity extends BasePlayerEntity {
64
+ type: 'player';
65
+ steamId3: string;
66
+ steamId64: string;
67
+ }
68
+ export interface BotEntity extends BasePlayerEntity {
69
+ type: 'bot';
70
+ }
71
+ export declare function parseTeam(rawTeam: string): Team;
72
+ export declare function parseKillModifiers(rawModifiers?: string): KillModifier[];
73
+ export declare function parseConnectionState(rawState?: string): ConnectionState;
74
+ export declare function parseHitGroup(rawHitGroup: string): HitGroup;
75
+ export declare function parseEquipmentList(rawEquipmentList: string): string[];
76
+ export declare function parseEntity(rawEntity: string): Entity;
77
+ export declare function parseVector(rawVector?: string): Vector;
78
+ export declare function concatPattern(literals: TemplateStringsArray, ...placeholders: (string | RegExp)[]): RegExp;
79
+ export declare function defineParser<T extends BaseEvent<string, unknown>>(parser: BaseParser<T>): BaseParser<T>;
80
+ export {};
package/lib/models.js ADDED
@@ -0,0 +1,133 @@
1
+ import { assertValidConnectionState, assertValidVector } from './assertions.js';
2
+ import { convertSteamIdTo64Dec } from './helpers.js';
3
+ // ----------------------------
4
+ // Regular expressions
5
+ // ----------------------------
6
+ const baseWorldRe = /World/;
7
+ export const baseEntityRe = /"(?<entityType>chicken|prop_physics_multiplayer|prop_dynamic)<(?<entityId>\d+)>"/;
8
+ export const basePlayerRe = /"(?<name>.+)<(?<entityId>\d*)><(?<steamId>(?:\[U:[01]:\d+\]|STEAM_[0-5]:[01]:\d+|BOT|Console))>(?:<(?<team>[^>]*)>)?"/;
9
+ export const vectorRe = /\[?[-.\d]+ [-.\d]+ [-.\d]+\]?/;
10
+ export const entityRe = /".+<\d*><(?:\[U:[01]:\d+\]|STEAM_[0-5]:[01]:\d+|BOT|Console)>(?:<[^>]*>)?"|"chicken<\d+>"|World/;
11
+ // ----------------------------
12
+ // Enums
13
+ // ----------------------------
14
+ export var Team;
15
+ (function (Team) {
16
+ Team[Team["Unassigned"] = 0] = "Unassigned";
17
+ Team[Team["Spectators"] = 1] = "Spectators";
18
+ Team[Team["Terrorists"] = 2] = "Terrorists";
19
+ Team[Team["CounterTerrorists"] = 3] = "CounterTerrorists";
20
+ Team[Team["Unknown"] = 999] = "Unknown";
21
+ })(Team || (Team = {}));
22
+ // ----------------------------
23
+ // Mappings
24
+ // ----------------------------
25
+ export const teamMapping = {
26
+ Unassigned: Team.Unassigned,
27
+ Spectator: Team.Spectators,
28
+ TERRORIST: Team.Terrorists,
29
+ CT: Team.CounterTerrorists
30
+ };
31
+ // ----------------------------
32
+ // Base parsers
33
+ // ----------------------------
34
+ export function parseTeam(rawTeam) {
35
+ const mappedTeam = teamMapping[rawTeam] ?? Team.Unknown;
36
+ return mappedTeam;
37
+ }
38
+ export function parseKillModifiers(rawModifiers) {
39
+ const modifiersArr = rawModifiers?.split(' ') ?? [];
40
+ return modifiersArr;
41
+ }
42
+ export function parseConnectionState(rawState) {
43
+ assertValidConnectionState(rawState);
44
+ return rawState;
45
+ }
46
+ export function parseHitGroup(rawHitGroup) {
47
+ // We don't want spaces in our values, and CS2 returns hitgroups with spaces:
48
+ // ("left leg", "right arm" etc), so we join them with underscores here
49
+ const formattedHitGroup = rawHitGroup.split(' ').join('_');
50
+ return formattedHitGroup;
51
+ }
52
+ export function parseEquipmentList(rawEquipmentList) {
53
+ const equipmentStr = rawEquipmentList.replaceAll('[', '').replaceAll(']', '').trim();
54
+ let equipment;
55
+ if (equipmentStr.length > 0) {
56
+ equipment = equipmentStr.split(' ');
57
+ }
58
+ else {
59
+ equipment = [];
60
+ }
61
+ return equipment;
62
+ }
63
+ export function parseEntity(rawEntity) {
64
+ // Test for "world" entity
65
+ if (baseWorldRe.test(rawEntity)) {
66
+ return {
67
+ type: 'world',
68
+ };
69
+ }
70
+ // Test for base entities (chicken, prop_dynamic & prop_physics_multiplayer)
71
+ if (baseEntityRe.test(rawEntity)) {
72
+ const baseEntityMatch = rawEntity.match(baseEntityRe);
73
+ const entityType = baseEntityMatch[1];
74
+ const entityId = Number(baseEntityMatch[2]);
75
+ if (entityType === 'chicken' || entityType === 'prop_dynamic' || entityType === 'prop_physics_multiplayer') {
76
+ return {
77
+ type: entityType,
78
+ id: entityId
79
+ };
80
+ }
81
+ else {
82
+ throw new Error(`Unknown base entity type "${rawEntity}"`);
83
+ }
84
+ }
85
+ if (basePlayerRe.test(rawEntity)) {
86
+ const { entityId: rawEntityId, steamId, name, team: rawTeam } = rawEntity.match(basePlayerRe).groups;
87
+ // Test for "Console" entity
88
+ if (steamId === 'Console') {
89
+ return {
90
+ type: 'console',
91
+ };
92
+ }
93
+ const entityId = Number(rawEntityId);
94
+ const team = parseTeam(rawTeam);
95
+ // Test for BOT entity
96
+ if (steamId === 'BOT') {
97
+ return {
98
+ type: 'bot',
99
+ id: entityId,
100
+ name,
101
+ team,
102
+ };
103
+ }
104
+ return {
105
+ type: 'player',
106
+ id: entityId,
107
+ steamId3: steamId,
108
+ steamId64: convertSteamIdTo64Dec(steamId),
109
+ name,
110
+ team,
111
+ };
112
+ }
113
+ throw new Error(`Failed to parse entity "${rawEntity}"`);
114
+ }
115
+ export function parseVector(rawVector) {
116
+ const vectorStrings = rawVector?.split(' ') ?? [];
117
+ const vectorNumbers = vectorStrings.map(Number);
118
+ assertValidVector(vectorNumbers);
119
+ return vectorNumbers;
120
+ }
121
+ export function concatPattern(literals, ...placeholders) {
122
+ let result = '';
123
+ for (let i = 0; i < placeholders.length; i += 1) {
124
+ result += literals[i];
125
+ const { [i]: placeholder } = placeholders;
126
+ result += placeholder instanceof RegExp ? placeholder.source : placeholder;
127
+ }
128
+ result += literals[literals.length - 1];
129
+ return new RegExp(result);
130
+ }
131
+ export function defineParser(parser) {
132
+ return parser;
133
+ }
@@ -0,0 +1,9 @@
1
+ import type { LogTimestampFormat, BaseParser } from './models.js';
2
+ import type { AssistedEvent, AttackedEvent, BlindedEvent, ConnectionEvent, EntityTriggeredEvent, KilledByBombEvent, KilledEvent, LeftBuyzoneEvent, PickedUpEvent, PurchasedEvent, RconEvent, SayEvent, ScoredEvent, SuicideEvent, SwitchedTeamEvent, TeamTriggeredEvent, ThrewEvent } from './parsers/index.js';
3
+ export type Event = AssistedEvent | AttackedEvent | BlindedEvent | ConnectionEvent | EntityTriggeredEvent | KilledByBombEvent | KilledEvent | LeftBuyzoneEvent | PickedUpEvent | PurchasedEvent | RconEvent | SayEvent | ScoredEvent | SuicideEvent | SwitchedTeamEvent | TeamTriggeredEvent | ThrewEvent;
4
+ export declare const defaultParsers: (BaseParser<AssistedEvent> | BaseParser<AttackedEvent> | BaseParser<BlindedEvent> | BaseParser<ConnectionEvent> | BaseParser<EntityTriggeredEvent> | BaseParser<KilledByBombEvent> | BaseParser<KilledEvent> | BaseParser<LeftBuyzoneEvent> | BaseParser<PickedUpEvent> | BaseParser<PurchasedEvent> | BaseParser<RconEvent> | BaseParser<SayEvent> | BaseParser<ScoredEvent> | BaseParser<SuicideEvent> | BaseParser<SwitchedTeamEvent> | BaseParser<TeamTriggeredEvent> | BaseParser<ThrewEvent>)[];
5
+ export interface ParseOptions {
6
+ parsers?: BaseParser<Event>[];
7
+ format?: LogTimestampFormat;
8
+ }
9
+ export declare function parse(rawLog: string, parseOptions?: ParseOptions): Event | null;
package/lib/parser.js ADDED
@@ -0,0 +1,74 @@
1
+ import { assistedParser, attackedParser, blindedParser, connectionParser, entityTriggeredParser, killedByBombParser, killedParser, leftBuyzoneParser, pickedUpParser, purchasedParser, rconParser, sayParser, scoredParser, suicideParser, switchedTeamParser, teamTriggeredParser, threwParser } from './parsers/index.js';
2
+ export const defaultParsers = [
3
+ assistedParser,
4
+ attackedParser,
5
+ blindedParser,
6
+ connectionParser,
7
+ entityTriggeredParser,
8
+ killedByBombParser,
9
+ killedParser,
10
+ leftBuyzoneParser,
11
+ pickedUpParser,
12
+ purchasedParser,
13
+ rconParser,
14
+ sayParser,
15
+ scoredParser,
16
+ suicideParser,
17
+ switchedTeamParser,
18
+ teamTriggeredParser,
19
+ threwParser
20
+ ];
21
+ // mm/dd/yyyy
22
+ const { length: LENGTH_OF_DATE_FILE } = '01/01/1970 - 00:00:00: ';
23
+ const { length: LENGTH_OF_DATE_HTTP } = '01/01/1970 - 00:00:00.000 - ';
24
+ function parseTimestamp(timestamp) {
25
+ const [dateStr, timeStr] = timestamp.split(' - ');
26
+ const [month, day, year] = dateStr.split('/');
27
+ const [hours, minutes, seconds] = timeStr.split(':');
28
+ // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date#date_time_string_format
29
+ const dateTimeStringFormat = `${year}-${month}-${day}T${hours}:${minutes}:${seconds}Z`;
30
+ const date = new Date(dateTimeStringFormat);
31
+ return date;
32
+ }
33
+ export function parse(rawLog, parseOptions) {
34
+ if (parseOptions === undefined) {
35
+ parseOptions = {
36
+ parsers: defaultParsers,
37
+ format: 'file'
38
+ };
39
+ }
40
+ if (parseOptions.parsers === undefined) {
41
+ parseOptions.parsers = defaultParsers;
42
+ }
43
+ if (parseOptions.format === undefined) {
44
+ parseOptions.format = 'file';
45
+ }
46
+ let log;
47
+ let timestamp;
48
+ if (parseOptions.format === 'file') {
49
+ timestamp = rawLog.substring(0, LENGTH_OF_DATE_FILE - 2);
50
+ log = rawLog.substring(LENGTH_OF_DATE_FILE);
51
+ }
52
+ else if (parseOptions.format === 'http') {
53
+ timestamp = rawLog.substring(0, LENGTH_OF_DATE_HTTP - 3);
54
+ log = rawLog.substring(LENGTH_OF_DATE_HTTP);
55
+ }
56
+ else {
57
+ throw new Error('Invalid CS2 log date format specified.');
58
+ }
59
+ for (const parser of parseOptions.parsers) {
60
+ const pattern = parser.patterns.find((pattern) => pattern.test(log));
61
+ if (pattern === undefined) {
62
+ continue;
63
+ }
64
+ const groups = log.match(pattern).groups;
65
+ const receivedAt = parseTimestamp(timestamp);
66
+ const payload = parser.parse(groups);
67
+ return {
68
+ name: parser.name,
69
+ receivedAt: receivedAt,
70
+ payload
71
+ };
72
+ }
73
+ return null;
74
+ }
@@ -0,0 +1,8 @@
1
+ import type { BaseEvent, Entity } from '../models.js';
2
+ export interface AssistedEventPayload {
3
+ assistant: Entity;
4
+ victim: Entity;
5
+ flashAssist: boolean;
6
+ }
7
+ export type AssistedEvent = BaseEvent<'assisted', AssistedEventPayload>;
8
+ export declare const assistedParser: import("../models.js").BaseParser<AssistedEvent>;
@@ -0,0 +1,15 @@
1
+ import { defineParser, concatPattern, entityRe, parseEntity } from '../models.js';
2
+ ;
3
+ export const assistedParser = defineParser({
4
+ name: 'assisted',
5
+ patterns: [
6
+ concatPattern `^(?<assistant>${entityRe}) (?<assistType>assisted|flash-assisted) killing (?<victim>${entityRe})$`
7
+ ],
8
+ parse({ assistant, victim, assistType }) {
9
+ return {
10
+ assistant: parseEntity(assistant),
11
+ victim: parseEntity(victim),
12
+ flashAssist: assistType === 'flash-assisted'
13
+ };
14
+ },
15
+ });
@@ -0,0 +1,13 @@
1
+ import type { BaseEvent, Entity, HitGroup } from '../models.js';
2
+ export interface AttackedEventPayload {
3
+ attacker: Entity;
4
+ victim: Entity;
5
+ weapon: string;
6
+ damage: number;
7
+ damageArmor: number;
8
+ remainingHealth: number;
9
+ remainingArmor: number;
10
+ hitGroup: HitGroup;
11
+ }
12
+ export type AttackedEvent = BaseEvent<'attacked', AttackedEventPayload>;
13
+ export declare const attackedParser: import("../models.js").BaseParser<AttackedEvent>;
@@ -0,0 +1,25 @@
1
+ import { defineParser, concatPattern, entityRe, vectorRe, parseEntity, parseVector, parseHitGroup } from '../models.js';
2
+ export const attackedParser = defineParser({
3
+ name: 'attacked',
4
+ patterns: [
5
+ concatPattern `^(?<attacker>${entityRe}) \\[(?<attackerPos>${vectorRe})\\] attacked (?<victim>${entityRe}) \\[(?<victimPos>${vectorRe})\\] with "(?<weapon>[^"]+)" \\(damage "(?<damageAmount>[^"]+)"\\) \\(damage_armor "(?<damageArmorAmount>[^"]+)"\\) \\(health "(?<remainingHealth>[^"]+)"\\) \\(armor "(?<remainingArmor>[^"]+)"\\) \\(hitgroup "(?<hitGroup>[^"]+)"\\)$`,
6
+ ],
7
+ parse: ({ attacker, attackerPos, victim, victimPos, weapon, damageAmount, damageArmorAmount, remainingHealth, remainingArmor, hitGroup }) => {
8
+ return {
9
+ attacker: {
10
+ ...parseEntity(attacker),
11
+ position: parseVector(attackerPos)
12
+ },
13
+ victim: {
14
+ ...parseEntity(victim),
15
+ position: parseVector(victimPos)
16
+ },
17
+ damage: Number(damageAmount),
18
+ damageArmor: Number(damageArmorAmount),
19
+ remainingHealth: Number(remainingHealth),
20
+ remainingArmor: Number(remainingArmor),
21
+ hitGroup: parseHitGroup(hitGroup),
22
+ weapon
23
+ };
24
+ }
25
+ });
@@ -0,0 +1,9 @@
1
+ import type { BaseEvent, Entity } from '../models.js';
2
+ export interface BlindedEventPayload {
3
+ attacker: Entity;
4
+ victim: Entity;
5
+ blindDuration: number;
6
+ flashbangIndex: number;
7
+ }
8
+ export type BlindedEvent = BaseEvent<'blinded', BlindedEventPayload>;
9
+ export declare const blindedParser: import("../models.js").BaseParser<BlindedEvent>;
@@ -0,0 +1,15 @@
1
+ import { concatPattern, defineParser, entityRe, parseEntity } from '../models.js';
2
+ export const blindedParser = defineParser({
3
+ name: 'blinded',
4
+ patterns: [
5
+ concatPattern `^(?<victim>${entityRe}) blinded for (?<blindDuration>[^"]+) by (?<attacker>${entityRe}) from flashbang entindex (?<entIndex>[^"]+)$`
6
+ ],
7
+ parse: ({ attacker, victim, blindDuration, entIndex }) => {
8
+ return {
9
+ attacker: parseEntity(attacker),
10
+ victim: parseEntity(victim),
11
+ blindDuration: Number(blindDuration),
12
+ flashbangIndex: Number(entIndex)
13
+ };
14
+ }
15
+ });
@@ -0,0 +1,20 @@
1
+ import type { ConnectionState, BaseEvent, Entity } from '../models.js';
2
+ interface BaseConnectionEvent {
3
+ state: ConnectionState;
4
+ client: Entity;
5
+ }
6
+ interface ConnectedEvent extends BaseConnectionEvent {
7
+ state: 'connected';
8
+ address: string;
9
+ }
10
+ interface EnteredEvent extends BaseConnectionEvent {
11
+ state: 'entered';
12
+ }
13
+ interface DisconnectedEvent extends BaseConnectionEvent {
14
+ state: 'disconnected';
15
+ reason: string;
16
+ }
17
+ export type ConnectionEventPayload = ConnectedEvent | EnteredEvent | DisconnectedEvent;
18
+ export type ConnectionEvent = BaseEvent<'connection', ConnectionEventPayload>;
19
+ export declare const connectionParser: import("../models.js").BaseParser<ConnectionEvent>;
20
+ export {};
@@ -0,0 +1,34 @@
1
+ import { defineParser, concatPattern, entityRe, parseEntity, parseConnectionState } from '../models.js';
2
+ const basePattern = concatPattern `^(?<client>${entityRe})`;
3
+ export const connectionParser = defineParser({
4
+ name: 'connection',
5
+ patterns: [
6
+ concatPattern `${basePattern} (?<state>entered) the game$`,
7
+ concatPattern `${basePattern} (?<state>connected), address "(?<address>[^"]*)"$`,
8
+ concatPattern `${basePattern} (?<state>disconnected) \\(reason "(?<reason>[^"]*)"\\)$`,
9
+ ],
10
+ parse: ({ client: rawClient, state: rawState, address, reason }) => {
11
+ const state = parseConnectionState(rawState);
12
+ const client = parseEntity(rawClient);
13
+ if (state === 'connected') {
14
+ return {
15
+ state,
16
+ client,
17
+ address
18
+ };
19
+ }
20
+ else if (state === 'entered') {
21
+ return {
22
+ state,
23
+ client
24
+ };
25
+ }
26
+ else {
27
+ return {
28
+ state,
29
+ client,
30
+ reason
31
+ };
32
+ }
33
+ }
34
+ });
@@ -0,0 +1,10 @@
1
+ import type { Entity, BaseEvent } from '../models.js';
2
+ type EntityTriggeredSubEvent = 'game_commencing' | 'warmup_start' | 'warmup_end' | 'match_start' | 'round_start' | 'round_end' | 'begin_bomb_defuse_with_kit' | 'begin_bomb_defuse_without_kit' | 'defused_the_bomb' | 'planted_the_bomb' | 'got_the_bomb' | 'dropped_the_bomb' | 'touched_a_hostage' | 'rescued_a_hostage';
3
+ export interface EntityTriggeredEventPayload {
4
+ entity: Entity;
5
+ event: EntityTriggeredSubEvent;
6
+ value: string | null;
7
+ }
8
+ export type EntityTriggeredEvent = BaseEvent<'entity_triggered', EntityTriggeredEventPayload>;
9
+ export declare const entityTriggeredParser: import("../models.js").BaseParser<EntityTriggeredEvent>;
10
+ export {};
@@ -0,0 +1,17 @@
1
+ import { defineParser, concatPattern, entityRe, parseEntity } from '../models.js';
2
+ export const entityTriggeredParser = defineParser({
3
+ name: 'entity_triggered',
4
+ patterns: [
5
+ // concatPattern`^(?<entity>${entityRe}) triggered "(?<event>[^"]+)"(?: \\(value "(?<value>[^"]+)"\\))?$`,
6
+ concatPattern `^(?<entity>${entityRe}) triggered "(?<event>[^"]+)"$`,
7
+ concatPattern `^(?<entity>${entityRe}) triggered "(?<event>[^"]+)" on "(?<value>[^"]+)"$`,
8
+ concatPattern `^(?<entity>${entityRe}) triggered "(?<event>[^"]+)" at bombsite (?<value>[^"]+)$`
9
+ ],
10
+ parse: ({ entity, event, value }) => {
11
+ return {
12
+ entity: parseEntity(entity),
13
+ event: event.toLowerCase(),
14
+ value: value ?? null
15
+ };
16
+ }
17
+ });
@@ -0,0 +1,17 @@
1
+ export * from './assisted.js';
2
+ export * from './attacked.js';
3
+ export * from './blinded.js';
4
+ export * from './connection.js';
5
+ export * from './entity-triggered.js';
6
+ export * from './killed-by-bomb.js';
7
+ export * from './killed.js';
8
+ export * from './left-buyzone.js';
9
+ export * from './picked-up.js';
10
+ export * from './purchased.js';
11
+ export * from './rcon.js';
12
+ export * from './say.js';
13
+ export * from './scored.js';
14
+ export * from './suicide.js';
15
+ export * from './switched-team.js';
16
+ export * from './team-triggered.js';
17
+ export * from './threw.js';
@@ -0,0 +1,17 @@
1
+ export * from './assisted.js';
2
+ export * from './attacked.js';
3
+ export * from './blinded.js';
4
+ export * from './connection.js';
5
+ export * from './entity-triggered.js';
6
+ export * from './killed-by-bomb.js';
7
+ export * from './killed.js';
8
+ export * from './left-buyzone.js';
9
+ export * from './picked-up.js';
10
+ export * from './purchased.js';
11
+ export * from './rcon.js';
12
+ export * from './say.js';
13
+ export * from './scored.js';
14
+ export * from './suicide.js';
15
+ export * from './switched-team.js';
16
+ export * from './team-triggered.js';
17
+ export * from './threw.js';
@@ -0,0 +1,4 @@
1
+ import type { BaseEvent, Entity } from '../models.js';
2
+ export type KilledByBombEventPayload = Entity;
3
+ export type KilledByBombEvent = BaseEvent<'killed_by_bomb', KilledByBombEventPayload>;
4
+ export declare const killedByBombParser: import("../models.js").BaseParser<KilledByBombEvent>;
@@ -0,0 +1,13 @@
1
+ import { defineParser, concatPattern, entityRe, vectorRe, parseEntity, parseVector } from '../models.js';
2
+ export const killedByBombParser = defineParser({
3
+ name: 'killed_by_bomb',
4
+ patterns: [
5
+ concatPattern `^(?<victim>${entityRe}) \\[(?<victimPos>${vectorRe})\\] was killed by the bomb.$`
6
+ ],
7
+ parse: ({ victim, victimPos }) => {
8
+ return {
9
+ ...parseEntity(victim),
10
+ position: parseVector(victimPos)
11
+ };
12
+ }
13
+ });
@@ -0,0 +1,9 @@
1
+ import type { BaseEvent, Entity, KillModifier } from '../models.js';
2
+ export interface KilledEventPayload {
3
+ attacker: Entity;
4
+ victim: Entity;
5
+ weapon: string;
6
+ modifiers: KillModifier[];
7
+ }
8
+ export type KilledEvent = BaseEvent<'killed', KilledEventPayload>;
9
+ export declare const killedParser: import("../models.js").BaseParser<KilledEvent>;
@@ -0,0 +1,22 @@
1
+ import { defineParser, concatPattern, entityRe, vectorRe, parseEntity, parseVector, parseKillModifiers, baseEntityRe } from '../models.js';
2
+ export const killedParser = defineParser({
3
+ name: 'killed',
4
+ patterns: [
5
+ concatPattern `^(?<attacker>${entityRe}) \\[(?<attackerPos>${vectorRe})\\] killed (?<victim>${entityRe}) \\[(?<victimPos>${vectorRe})\\] with "(?<weapon>[^"]+)"(?: \\((?<modifiers>[^\\)]+)\\))?$`,
6
+ concatPattern `^(?<attacker>${entityRe}) \\[(?<attackerPos>${vectorRe})\\] killed other (?<victim>${baseEntityRe}) \\[(?<victimPos>${vectorRe})\\] with "(?<weapon>[^"]+)"(?: \\((?<modifiers>[^\\)]+)\\))?$`,
7
+ ],
8
+ parse: ({ attacker, attackerPos, victim, victimPos, weapon, modifiers }) => {
9
+ return {
10
+ attacker: {
11
+ ...parseEntity(attacker),
12
+ position: parseVector(attackerPos)
13
+ },
14
+ victim: {
15
+ ...parseEntity(victim),
16
+ position: parseVector(victimPos)
17
+ },
18
+ modifiers: parseKillModifiers(modifiers),
19
+ weapon
20
+ };
21
+ }
22
+ });
@@ -0,0 +1,7 @@
1
+ import type { BaseEvent, Entity } from '../models.js';
2
+ export interface LeftBuyzoneEventPayload {
3
+ player: Entity;
4
+ equipment: string[];
5
+ }
6
+ export type LeftBuyzoneEvent = BaseEvent<'left_buyzone', LeftBuyzoneEventPayload>;
7
+ export declare const leftBuyzoneParser: import("../models.js").BaseParser<LeftBuyzoneEvent>;
@@ -0,0 +1,13 @@
1
+ import { concatPattern, defineParser, entityRe, parseEntity, parseEquipmentList } from '../models.js';
2
+ export const leftBuyzoneParser = defineParser({
3
+ name: 'left_buyzone',
4
+ patterns: [
5
+ concatPattern `^(?<player>${entityRe}) left buyzone with (?<equipment>\\[.*\\])$`
6
+ ],
7
+ parse: ({ player, equipment }) => {
8
+ return {
9
+ player: parseEntity(player),
10
+ equipment: parseEquipmentList(equipment)
11
+ };
12
+ }
13
+ });
@@ -0,0 +1,7 @@
1
+ import type { BaseEvent, Entity } from '../models.js';
2
+ export interface PickedUpEventPayload {
3
+ player: Entity;
4
+ weapon: string;
5
+ }
6
+ export type PickedUpEvent = BaseEvent<'picked_up', PickedUpEventPayload>;
7
+ export declare const pickedUpParser: import("../models.js").BaseParser<PickedUpEvent>;
@@ -0,0 +1,13 @@
1
+ import { defineParser, concatPattern, entityRe, parseEntity } from '../models.js';
2
+ export const pickedUpParser = defineParser({
3
+ name: 'picked_up',
4
+ patterns: [
5
+ concatPattern `^(?<player>${entityRe}) picked up "(?<weapon>[^"]+)"$`
6
+ ],
7
+ parse: ({ player, weapon }) => {
8
+ return {
9
+ player: parseEntity(player),
10
+ weapon
11
+ };
12
+ }
13
+ });
@@ -0,0 +1,7 @@
1
+ import type { BaseEvent, Entity } from '../models.js';
2
+ export interface PurchasedEventPayload {
3
+ player: Entity;
4
+ weapon: string;
5
+ }
6
+ export type PurchasedEvent = BaseEvent<'purchased', PurchasedEventPayload>;
7
+ export declare const purchasedParser: import("../models.js").BaseParser<PurchasedEvent>;
@@ -0,0 +1,13 @@
1
+ import { defineParser, concatPattern, entityRe, parseEntity } from '../models.js';
2
+ export const purchasedParser = defineParser({
3
+ name: 'purchased',
4
+ patterns: [
5
+ concatPattern `^(?<player>${entityRe}) purchased "(?<weapon>[^"]+)"$`
6
+ ],
7
+ parse: ({ player, weapon }) => {
8
+ return {
9
+ player: parseEntity(player),
10
+ weapon
11
+ };
12
+ }
13
+ });
@@ -0,0 +1,7 @@
1
+ import type { BaseEvent } from '../models.js';
2
+ export interface RconEventPayload {
3
+ address: string;
4
+ command: string;
5
+ }
6
+ export type RconEvent = BaseEvent<'rcon', RconEventPayload>;
7
+ export declare const rconParser: import("../models.js").BaseParser<RconEvent>;
@@ -0,0 +1,13 @@
1
+ import { defineParser, concatPattern } from '../models.js';
2
+ export const rconParser = defineParser({
3
+ name: 'rcon',
4
+ patterns: [
5
+ concatPattern `^rcon from "(?<address>.+)": command "(?<command>.+)"$`,
6
+ ],
7
+ parse: ({ address, command }) => {
8
+ return {
9
+ address,
10
+ command
11
+ };
12
+ }
13
+ });
@@ -0,0 +1,8 @@
1
+ import type { BaseEvent, Entity, SayTo } from '../models.js';
2
+ export interface SayEventPayload {
3
+ player: Entity;
4
+ to: SayTo;
5
+ message: string;
6
+ }
7
+ export type SayEvent = BaseEvent<'say', SayEventPayload>;
8
+ export declare const sayParser: import("../models.js").BaseParser<SayEvent>;
@@ -0,0 +1,14 @@
1
+ import { defineParser, concatPattern, entityRe, parseEntity } from '../models.js';
2
+ export const sayParser = defineParser({
3
+ name: 'say',
4
+ patterns: [
5
+ concatPattern `^(?<player>${entityRe}) (?<to>say|say_team) "(?<message>.+)"$`
6
+ ],
7
+ parse: ({ player, to, message }) => {
8
+ return {
9
+ player: parseEntity(player),
10
+ to: to === 'say_team' ? 'team' : 'all',
11
+ message
12
+ };
13
+ }
14
+ });
@@ -0,0 +1,8 @@
1
+ import type { BaseEvent, Team } from '../models.js';
2
+ export interface ScoredEventPayload {
3
+ team: Team;
4
+ score: number;
5
+ playerCount: number;
6
+ }
7
+ export type ScoredEvent = BaseEvent<'scored', ScoredEventPayload>;
8
+ export declare const scoredParser: import("../models.js").BaseParser<ScoredEvent>;
@@ -0,0 +1,14 @@
1
+ import { defineParser, parseTeam, concatPattern } from '../models.js';
2
+ export const scoredParser = defineParser({
3
+ name: 'scored',
4
+ patterns: [
5
+ concatPattern `^Team "(?<team>[^"]+)" scored "(?<score>[^"]+)" with "(?<playerCount>[^"]+)" players$`
6
+ ],
7
+ parse: ({ team, score, playerCount }) => {
8
+ return {
9
+ team: parseTeam(team),
10
+ score: Number(score),
11
+ playerCount: Number(playerCount)
12
+ };
13
+ }
14
+ });
@@ -0,0 +1,7 @@
1
+ import type { Entity, SuicideMethod, BaseEvent } from '../models.js';
2
+ export interface SuicideEventPayload {
3
+ player: Entity;
4
+ method: SuicideMethod;
5
+ }
6
+ export type SuicideEvent = BaseEvent<'suicide', SuicideEventPayload>;
7
+ export declare const suicideParser: import("../models.js").BaseParser<SuicideEvent>;
@@ -0,0 +1,16 @@
1
+ import { defineParser, concatPattern, entityRe, vectorRe, parseEntity, parseVector } from '../models.js';
2
+ export const suicideParser = defineParser({
3
+ name: 'suicide',
4
+ patterns: [
5
+ concatPattern `^(?<player>${entityRe}) \\[(?<playerPos>${vectorRe})\\] committed suicide with "(?<how>.+)"$`,
6
+ ],
7
+ parse: ({ player, playerPos, how }) => {
8
+ return {
9
+ player: {
10
+ ...parseEntity(player),
11
+ position: parseVector(playerPos),
12
+ },
13
+ method: how
14
+ };
15
+ }
16
+ });
@@ -0,0 +1,8 @@
1
+ import type { Entity, Team, BaseEvent } from '../models.js';
2
+ export interface SwitchedTeamEventPayload {
3
+ player: Entity;
4
+ fromTeam: Team;
5
+ toTeam: Team;
6
+ }
7
+ export type SwitchedTeamEvent = BaseEvent<'switched_team', SwitchedTeamEventPayload>;
8
+ export declare const switchedTeamParser: import("../models.js").BaseParser<SwitchedTeamEvent>;
@@ -0,0 +1,14 @@
1
+ import { defineParser, concatPattern, entityRe, parseEntity, parseTeam } from '../models.js';
2
+ export const switchedTeamParser = defineParser({
3
+ name: 'switched_team',
4
+ patterns: [
5
+ concatPattern `^(?<player>${entityRe}) switched from team <(?<fromTeam>[^>]+)> to <(?<toTeam>[^>]+)>$`
6
+ ],
7
+ parse: ({ player, fromTeam, toTeam }) => {
8
+ return {
9
+ player: parseEntity(player),
10
+ fromTeam: parseTeam(fromTeam),
11
+ toTeam: parseTeam(toTeam)
12
+ };
13
+ }
14
+ });
@@ -0,0 +1,11 @@
1
+ import type { Team, BaseEvent } from '../models.js';
2
+ type TeamTriggeredSubEvent = 'sfui_notice_terrorists_win' | 'sfui_notice_cts_win' | 'sfui_notice_target_bombed' | 'sfui_notice_target_saved' | 'sfui_notice_bomb_defused' | 'sfui_notice_hostages_not_rescued' | 'sfui_notice_all_hostages_rescued';
3
+ export interface TeamTriggeredEventPayload {
4
+ team: Team;
5
+ event: TeamTriggeredSubEvent;
6
+ tScore: number;
7
+ ctScore: number;
8
+ }
9
+ export type TeamTriggeredEvent = BaseEvent<'team_triggered', TeamTriggeredEventPayload>;
10
+ export declare const teamTriggeredParser: import("../models.js").BaseParser<TeamTriggeredEvent>;
11
+ export {};
@@ -0,0 +1,15 @@
1
+ import { parseTeam, defineParser, concatPattern } from '../models.js';
2
+ export const teamTriggeredParser = defineParser({
3
+ name: 'team_triggered',
4
+ patterns: [
5
+ concatPattern `^Team "(?<team>[^"]+)" triggered "(?<event>[^"]+)" \\(CT "(?<ctScore>\\d+)"\\) \\(T "(?<tScore>\\d+)"\\)$`
6
+ ],
7
+ parse: ({ team, tScore, ctScore, event }) => {
8
+ return {
9
+ team: parseTeam(team),
10
+ tScore: Number(tScore),
11
+ ctScore: Number(ctScore),
12
+ event: event.toLowerCase()
13
+ };
14
+ }
15
+ });
@@ -0,0 +1,9 @@
1
+ import type { Entity, BaseEvent, Vector } from '../models.js';
2
+ export interface ThrewEventPayload {
3
+ player: Entity;
4
+ grenade: string;
5
+ position: Vector;
6
+ entIndex: number | null;
7
+ }
8
+ export type ThrewEvent = BaseEvent<'threw', ThrewEventPayload>;
9
+ export declare const threwParser: import("../models.js").BaseParser<ThrewEvent>;
@@ -0,0 +1,16 @@
1
+ import { concatPattern, defineParser, entityRe, vectorRe, parseEntity, parseVector } from '../models.js';
2
+ export const threwParser = defineParser({
3
+ name: 'threw',
4
+ patterns: [
5
+ concatPattern `^(?<player>${entityRe}) threw (?<grenade>.+) \\[(?<position>${vectorRe})\\]$`,
6
+ concatPattern `^(?<player>${entityRe}) threw (?<grenade>.+) \\[(?<position>${vectorRe})\\] flashbang entindex (?<entIndex>.+)\\)$`
7
+ ],
8
+ parse: ({ player, grenade, position, entIndex }) => {
9
+ return {
10
+ player: parseEntity(player),
11
+ position: parseVector(position),
12
+ entIndex: Number(entIndex) || null,
13
+ grenade
14
+ };
15
+ }
16
+ });
package/package.json ADDED
@@ -0,0 +1,43 @@
1
+ {
2
+ "name": "cs2-log-parser",
3
+ "version": "1.0.0",
4
+ "description": "Parses CS2 logs into JavaScript objects.",
5
+ "license": "MIT",
6
+ "author": "GekasD",
7
+ "keywords": [
8
+ "cs2",
9
+ "source2",
10
+ "log",
11
+ "parser",
12
+ "counter-strike",
13
+ "counter-strike-2"
14
+ ],
15
+ "homepage": "https://github.com/GekasD/cs2-log-parser",
16
+ "type": "module",
17
+ "main": "lib/index.js",
18
+ "files": [
19
+ "lib/**/*"
20
+ ],
21
+ "engines": {
22
+ "node": ">=22.0.0"
23
+ },
24
+ "scripts": {
25
+ "build": "npm run clean && npm run lint && npm run transpile",
26
+ "clean": "rimraf lib",
27
+ "lint": "eslint --fix",
28
+ "transpile": "tsc",
29
+ "test": "node --import tsx --test tests/*.test.ts",
30
+ "prepare": "npm run build"
31
+ },
32
+ "devDependencies": {
33
+ "@eslint/eslintrc": "^3.3.1",
34
+ "@eslint/js": "^9.37.0",
35
+ "@types/node": "^24.6.2",
36
+ "@typescript-eslint/eslint-plugin": "^8.46.0",
37
+ "@typescript-eslint/parser": "^8.46.0",
38
+ "eslint": "^9.37.0",
39
+ "rimraf": "^6.0.1",
40
+ "tsx": "^4.20.6",
41
+ "typescript": "^5.9.2"
42
+ }
43
+ }