ballrush-core 0.5.1 → 0.5.3

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.
@@ -9,3 +9,4 @@ export * from "./schemas/stat-group-last-100-games.schema";
9
9
  export * from "./schemas/stat-group-activity-chart.schema";
10
10
  export * from "./schemas/stat-user-profile.schema";
11
11
  export * from "./schemas/stat-user-match-history.schema";
12
+ export * from "./schemas/stat-event.schema";
@@ -25,3 +25,4 @@ __exportStar(require("./schemas/stat-group-last-100-games.schema"), exports);
25
25
  __exportStar(require("./schemas/stat-group-activity-chart.schema"), exports);
26
26
  __exportStar(require("./schemas/stat-user-profile.schema"), exports);
27
27
  __exportStar(require("./schemas/stat-user-match-history.schema"), exports);
28
+ __exportStar(require("./schemas/stat-event.schema"), exports);
@@ -0,0 +1,96 @@
1
+ import { Schema } from "mongoose";
2
+ /**
3
+ * Mongoose document interface for StatEventPlayer
4
+ */
5
+ export interface StatEventPlayerDoc {
6
+ userId: number;
7
+ playerName: string;
8
+ avatarUrl: string;
9
+ position: string;
10
+ }
11
+ /**
12
+ * Mongoose document interface for StatEventTeam
13
+ */
14
+ export interface StatEventTeamDoc {
15
+ teamId: number;
16
+ teamName: string;
17
+ totalPoints: number;
18
+ avgTeamRating: number;
19
+ players: StatEventPlayerDoc[];
20
+ }
21
+ /**
22
+ * Mongoose document interface for StatEventStandingsRow
23
+ */
24
+ export interface StatEventStandingsRowDoc {
25
+ rank: number;
26
+ teamName: string;
27
+ played: number;
28
+ wins: number;
29
+ draws: number;
30
+ losses: number;
31
+ goalsFor: number;
32
+ goalsAgainst: number;
33
+ goalDifference: number;
34
+ points: number;
35
+ }
36
+ /**
37
+ * Mongoose document interface for StatEventMatch
38
+ */
39
+ export interface StatEventMatchDoc {
40
+ matchId: number;
41
+ matchNumber: number;
42
+ time: string;
43
+ status: string;
44
+ team1Name: string;
45
+ team1Score: number;
46
+ team2Name: string;
47
+ team2Score: number;
48
+ }
49
+ /**
50
+ * Mongoose document interface for StatEventHeader
51
+ */
52
+ export interface StatEventHeaderDoc {
53
+ status: 'Finished' | 'In Progress' | 'Upcoming';
54
+ seasonName: string;
55
+ groupName: string;
56
+ eventName: string;
57
+ date: Date;
58
+ timeRange: string;
59
+ location: string;
60
+ winnerName: string | null;
61
+ }
62
+ /**
63
+ * Mongoose document interface for StatEventOverviewStats
64
+ */
65
+ export interface StatEventOverviewStatsDoc {
66
+ totalGoals: number;
67
+ goalsPerMatch: number;
68
+ }
69
+ /**
70
+ * Mongoose document interface for StatEventOverview
71
+ */
72
+ export interface StatEventOverviewDoc {
73
+ stats: StatEventOverviewStatsDoc;
74
+ standings: StatEventStandingsRowDoc[];
75
+ teams: StatEventTeamDoc[];
76
+ }
77
+ /**
78
+ * Mongoose document interface for StatEvent
79
+ */
80
+ export interface StatEventDoc {
81
+ eventId: number;
82
+ header: StatEventHeaderDoc;
83
+ overview: StatEventOverviewDoc;
84
+ matches: StatEventMatchDoc[];
85
+ calculatedAt: Date;
86
+ version: number;
87
+ }
88
+ /**
89
+ * Factory function to create StatEvent schema
90
+ * Collection: stat_events
91
+ */
92
+ export declare function createStatEventSchema(): Schema<StatEventDoc, import("mongoose").Model<StatEventDoc, any, any, any, import("mongoose").Document<unknown, any, StatEventDoc> & StatEventDoc & {
93
+ _id: import("mongoose").Types.ObjectId;
94
+ }, any>, {}, {}, {}, {}, import("mongoose").DefaultSchemaOptions, StatEventDoc, import("mongoose").Document<unknown, {}, import("mongoose").FlatRecord<StatEventDoc>> & import("mongoose").FlatRecord<StatEventDoc> & {
95
+ _id: import("mongoose").Types.ObjectId;
96
+ }>;
@@ -0,0 +1,80 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createStatEventSchema = createStatEventSchema;
4
+ const mongoose_1 = require("mongoose");
5
+ /**
6
+ * Factory function to create StatEvent schema
7
+ * Collection: stat_events
8
+ */
9
+ function createStatEventSchema() {
10
+ const PlayerSchema = new mongoose_1.Schema({
11
+ userId: { type: Number, required: true },
12
+ playerName: { type: String, required: true },
13
+ avatarUrl: { type: String, required: true },
14
+ position: { type: String, required: true },
15
+ }, { _id: false });
16
+ const TeamSchema = new mongoose_1.Schema({
17
+ teamId: { type: Number, required: true },
18
+ teamName: { type: String, required: true },
19
+ totalPoints: { type: Number, required: true, min: 0, default: 0 },
20
+ avgTeamRating: { type: Number, required: true, min: 0, default: 0 },
21
+ players: { type: [PlayerSchema], default: [] },
22
+ }, { _id: false });
23
+ const StandingsRowSchema = new mongoose_1.Schema({
24
+ rank: { type: Number, required: true, min: 1 },
25
+ teamName: { type: String, required: true },
26
+ played: { type: Number, required: true, min: 0, default: 0 },
27
+ wins: { type: Number, required: true, min: 0, default: 0 },
28
+ draws: { type: Number, required: true, min: 0, default: 0 },
29
+ losses: { type: Number, required: true, min: 0, default: 0 },
30
+ goalsFor: { type: Number, required: true, min: 0, default: 0 },
31
+ goalsAgainst: { type: Number, required: true, min: 0, default: 0 },
32
+ goalDifference: { type: Number, required: true, default: 0 },
33
+ points: { type: Number, required: true, min: 0, default: 0 },
34
+ }, { _id: false });
35
+ const MatchSchema = new mongoose_1.Schema({
36
+ matchId: { type: Number, required: true },
37
+ matchNumber: { type: Number, required: true, min: 1 },
38
+ time: { type: String, required: true },
39
+ status: { type: String, required: true },
40
+ team1Name: { type: String, required: true },
41
+ team1Score: { type: Number, required: true, min: 0, default: 0 },
42
+ team2Name: { type: String, required: true },
43
+ team2Score: { type: Number, required: true, min: 0, default: 0 },
44
+ }, { _id: false });
45
+ const HeaderSchema = new mongoose_1.Schema({
46
+ status: { type: String, enum: ['Finished', 'In Progress', 'Upcoming'], required: true },
47
+ seasonName: { type: String, required: true },
48
+ groupName: { type: String, required: true },
49
+ eventName: { type: String, required: true },
50
+ date: { type: Date, required: true },
51
+ timeRange: { type: String, required: true },
52
+ location: { type: String, required: true },
53
+ winnerName: { type: String, default: null },
54
+ }, { _id: false });
55
+ const OverviewStatsSchema = new mongoose_1.Schema({
56
+ totalGoals: { type: Number, required: true, min: 0, default: 0 },
57
+ goalsPerMatch: { type: Number, required: true, min: 0, default: 0 },
58
+ }, { _id: false });
59
+ const OverviewSchema = new mongoose_1.Schema({
60
+ stats: { type: OverviewStatsSchema, required: true },
61
+ standings: { type: [StandingsRowSchema], default: [] },
62
+ teams: { type: [TeamSchema], default: [] },
63
+ }, { _id: false });
64
+ const StatEventSchema = new mongoose_1.Schema({
65
+ eventId: { type: Number, required: true, unique: true, index: true },
66
+ header: { type: HeaderSchema, required: true },
67
+ overview: { type: OverviewSchema, required: true },
68
+ matches: { type: [MatchSchema], default: [] },
69
+ calculatedAt: { type: Date, default: Date.now },
70
+ version: { type: Number, required: true, default: 1, min: 1 },
71
+ }, {
72
+ collection: 'stat_events',
73
+ versionKey: false,
74
+ });
75
+ // Index for fast lookup by eventId
76
+ StatEventSchema.index({ eventId: 1 }, { unique: true });
77
+ // Index for cache invalidation by calculatedAt
78
+ StatEventSchema.index({ calculatedAt: -1 });
79
+ return StatEventSchema;
80
+ }
@@ -20,10 +20,15 @@ export interface StatGroupSeasonParticipantDoc {
20
20
  export interface StatGroupSeasonDoc {
21
21
  groupId: number;
22
22
  seasonName: string;
23
+ totalMatches: number;
24
+ totalGoals: number;
25
+ averageGoalsPerMatch: number;
26
+ totalEvents: number;
27
+ totalParticipants: number;
23
28
  participants: StatGroupSeasonParticipantDoc[];
24
29
  startDate: Date;
25
30
  endDate: Date | null;
26
- status: 'in_progress' | 'completed';
31
+ status: 'upcoming' | 'in_progress' | 'ended';
27
32
  updatedAt: Date;
28
33
  }
29
34
  /**
@@ -22,10 +22,15 @@ function createStatGroupSeasonSchema() {
22
22
  const StatGroupSeasonSchema = new mongoose_1.Schema({
23
23
  groupId: { type: Number, required: true, index: true },
24
24
  seasonName: { type: String, required: true },
25
+ totalMatches: { type: Number, required: true, min: 0, default: 0 },
26
+ totalGoals: { type: Number, required: true, min: 0, default: 0 },
27
+ averageGoalsPerMatch: { type: Number, required: true, min: 0, default: 0 },
28
+ totalEvents: { type: Number, required: true, min: 0, default: 0 },
29
+ totalParticipants: { type: Number, required: true, min: 0, default: 0 },
25
30
  participants: { type: [ParticipantSchema], default: [] },
26
31
  startDate: { type: Date, required: true },
27
32
  endDate: { type: Date, default: null },
28
- status: { type: String, enum: ['in_progress', 'completed'], required: true },
33
+ status: { type: String, enum: ['upcoming', 'in_progress', 'ended'], required: true },
29
34
  updatedAt: { type: Date, default: Date.now },
30
35
  }, {
31
36
  collection: 'stat_group_seasons',
@@ -9,3 +9,4 @@ export * from "./stat-group-last-100-games.repository";
9
9
  export * from "./stat-group-activity-chart.repository";
10
10
  export * from "./stat-user-profile.repository";
11
11
  export * from "./stat-user-match-history.repository";
12
+ export * from "./stat-event.repository";
@@ -25,3 +25,4 @@ __exportStar(require("./stat-group-last-100-games.repository"), exports);
25
25
  __exportStar(require("./stat-group-activity-chart.repository"), exports);
26
26
  __exportStar(require("./stat-user-profile.repository"), exports);
27
27
  __exportStar(require("./stat-user-match-history.repository"), exports);
28
+ __exportStar(require("./stat-event.repository"), exports);
@@ -0,0 +1,14 @@
1
+ import { StatEvent } from "../types/stat-event.types";
2
+ /**
3
+ * Repository interface for StatEvent
4
+ * Provides pre-aggregated event data including standings, statistics,
5
+ * and enriched rosters/matches for the Event Page
6
+ */
7
+ export interface StatEventRepository {
8
+ /**
9
+ * Finds complete event data by event ID
10
+ * @param eventId - The event ID
11
+ * @returns Complete event data or null if not found
12
+ */
13
+ findEventById(eventId: number): Promise<StatEvent | null>;
14
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -9,3 +9,4 @@ export * from "./mongo/stat-group-last-100-games.repository";
9
9
  export * from "./mongo/stat-group-activity-chart.repository";
10
10
  export * from "./mongo/stat-user-profile.repository";
11
11
  export * from "./mongo/stat-user-match-history.repository";
12
+ export * from "./mongo/stat-event.repository";
@@ -25,3 +25,4 @@ __exportStar(require("./mongo/stat-group-last-100-games.repository"), exports);
25
25
  __exportStar(require("./mongo/stat-group-activity-chart.repository"), exports);
26
26
  __exportStar(require("./mongo/stat-user-profile.repository"), exports);
27
27
  __exportStar(require("./mongo/stat-user-match-history.repository"), exports);
28
+ __exportStar(require("./mongo/stat-event.repository"), exports);
@@ -0,0 +1,13 @@
1
+ import type { Model } from "mongoose";
2
+ import type { StatEventRepository } from "../../ports/stat-event.repository";
3
+ import type { StatEventDoc } from "../../mongo/schemas/stat-event.schema";
4
+ import type { StatEvent } from "../../types/stat-event.types";
5
+ /**
6
+ * MongoDB implementation of StatEventRepository
7
+ * Provides read-only access to pre-aggregated event data
8
+ */
9
+ export declare class MongoStatEventRepository implements StatEventRepository {
10
+ private readonly model;
11
+ constructor(model: Model<StatEventDoc>);
12
+ findEventById(eventId: number): Promise<StatEvent | null>;
13
+ }
@@ -0,0 +1,91 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MongoStatEventRepository = void 0;
4
+ const errors_1 = require("../../errors");
5
+ /**
6
+ * MongoDB implementation of StatEventRepository
7
+ * Provides read-only access to pre-aggregated event data
8
+ */
9
+ class MongoStatEventRepository {
10
+ constructor(model) {
11
+ this.model = model;
12
+ }
13
+ async findEventById(eventId) {
14
+ try {
15
+ // Validate input
16
+ if (!eventId || eventId === 0) {
17
+ throw new errors_1.ValidationError('StatEvent', 'eventId', eventId, 'must be a non-zero number');
18
+ }
19
+ // Find event by ID
20
+ const doc = await this.model
21
+ .findOne({ eventId })
22
+ .lean();
23
+ if (!doc) {
24
+ return null;
25
+ }
26
+ // Map document to domain type
27
+ return {
28
+ eventId: doc.eventId,
29
+ header: {
30
+ status: doc.header.status,
31
+ seasonName: doc.header.seasonName,
32
+ groupName: doc.header.groupName,
33
+ eventName: doc.header.eventName,
34
+ date: doc.header.date,
35
+ timeRange: doc.header.timeRange,
36
+ location: doc.header.location,
37
+ winnerName: doc.header.winnerName,
38
+ },
39
+ overview: {
40
+ stats: {
41
+ totalGoals: doc.overview.stats.totalGoals,
42
+ goalsPerMatch: doc.overview.stats.goalsPerMatch,
43
+ },
44
+ standings: doc.overview.standings.map(s => ({
45
+ rank: s.rank,
46
+ teamName: s.teamName,
47
+ played: s.played,
48
+ wins: s.wins,
49
+ draws: s.draws,
50
+ losses: s.losses,
51
+ goalsFor: s.goalsFor,
52
+ goalsAgainst: s.goalsAgainst,
53
+ goalDifference: s.goalDifference,
54
+ points: s.points,
55
+ })),
56
+ teams: doc.overview.teams.map(t => ({
57
+ teamId: t.teamId,
58
+ teamName: t.teamName,
59
+ totalPoints: t.totalPoints,
60
+ avgTeamRating: t.avgTeamRating,
61
+ players: t.players.map(p => ({
62
+ userId: p.userId,
63
+ playerName: p.playerName,
64
+ avatarUrl: p.avatarUrl,
65
+ position: p.position,
66
+ })),
67
+ })),
68
+ },
69
+ matches: doc.matches.map(m => ({
70
+ matchId: m.matchId,
71
+ matchNumber: m.matchNumber,
72
+ time: m.time,
73
+ status: m.status,
74
+ team1Name: m.team1Name,
75
+ team1Score: m.team1Score,
76
+ team2Name: m.team2Name,
77
+ team2Score: m.team2Score,
78
+ })),
79
+ calculatedAt: doc.calculatedAt,
80
+ version: doc.version,
81
+ };
82
+ }
83
+ catch (error) {
84
+ if (error instanceof errors_1.ValidationError) {
85
+ throw error;
86
+ }
87
+ throw new errors_1.QueryError('findEventById', 'StatEvent', eventId, error);
88
+ }
89
+ }
90
+ }
91
+ exports.MongoStatEventRepository = MongoStatEventRepository;
@@ -25,6 +25,11 @@ class MongoStatGroupSeasonRepository {
25
25
  return docs.map(doc => ({
26
26
  groupId: doc.groupId,
27
27
  seasonName: doc.seasonName,
28
+ totalMatches: doc.totalMatches,
29
+ totalGoals: doc.totalGoals,
30
+ averageGoalsPerMatch: doc.averageGoalsPerMatch,
31
+ totalEvents: doc.totalEvents,
32
+ totalParticipants: doc.totalParticipants,
28
33
  participants: doc.participants.map(p => ({
29
34
  userId: p.userId,
30
35
  playerName: p.playerName,
@@ -5,3 +5,4 @@ export * from "./stat-group-last-100-games.types";
5
5
  export * from "./stat-group-activity-chart.types";
6
6
  export * from "./stat-user-profile.types";
7
7
  export * from "./stat-user-match-history.types";
8
+ export * from "./stat-event.types";
@@ -21,3 +21,4 @@ __exportStar(require("./stat-group-last-100-games.types"), exports);
21
21
  __exportStar(require("./stat-group-activity-chart.types"), exports);
22
22
  __exportStar(require("./stat-user-profile.types"), exports);
23
23
  __exportStar(require("./stat-user-match-history.types"), exports);
24
+ __exportStar(require("./stat-event.types"), exports);
@@ -0,0 +1,66 @@
1
+ /**
2
+ * Statistics: Event Page Types
3
+ * Pre-aggregated event data with standings, statistics, and enriched rosters/matches
4
+ */
5
+ export interface StatEventPlayer {
6
+ userId: number;
7
+ playerName: string;
8
+ avatarUrl: string;
9
+ position: string;
10
+ }
11
+ export interface StatEventTeam {
12
+ teamId: number;
13
+ teamName: string;
14
+ totalPoints: number;
15
+ avgTeamRating: number;
16
+ players: StatEventPlayer[];
17
+ }
18
+ export interface StatEventStandingsRow {
19
+ rank: number;
20
+ teamName: string;
21
+ played: number;
22
+ wins: number;
23
+ draws: number;
24
+ losses: number;
25
+ goalsFor: number;
26
+ goalsAgainst: number;
27
+ goalDifference: number;
28
+ points: number;
29
+ }
30
+ export interface StatEventMatch {
31
+ matchId: number;
32
+ matchNumber: number;
33
+ time: string;
34
+ status: string;
35
+ team1Name: string;
36
+ team1Score: number;
37
+ team2Name: string;
38
+ team2Score: number;
39
+ }
40
+ export interface StatEventHeader {
41
+ status: 'Finished' | 'In Progress' | 'Upcoming';
42
+ seasonName: string;
43
+ groupName: string;
44
+ eventName: string;
45
+ date: Date;
46
+ timeRange: string;
47
+ location: string;
48
+ winnerName: string | null;
49
+ }
50
+ export interface StatEventOverviewStats {
51
+ totalGoals: number;
52
+ goalsPerMatch: number;
53
+ }
54
+ export interface StatEventOverview {
55
+ stats: StatEventOverviewStats;
56
+ standings: StatEventStandingsRow[];
57
+ teams: StatEventTeam[];
58
+ }
59
+ export interface StatEvent {
60
+ eventId: number;
61
+ header: StatEventHeader;
62
+ overview: StatEventOverview;
63
+ matches: StatEventMatch[];
64
+ calculatedAt: Date;
65
+ version: number;
66
+ }
@@ -0,0 +1,6 @@
1
+ "use strict";
2
+ /**
3
+ * Statistics: Event Page Types
4
+ * Pre-aggregated event data with standings, statistics, and enriched rosters/matches
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -17,9 +17,14 @@ export interface StatGroupSeasonParticipant {
17
17
  export interface StatGroupSeason {
18
18
  groupId: number;
19
19
  seasonName: string;
20
+ totalMatches: number;
21
+ totalGoals: number;
22
+ averageGoalsPerMatch: number;
23
+ totalEvents: number;
24
+ totalParticipants: number;
20
25
  participants: StatGroupSeasonParticipant[];
21
26
  startDate: Date;
22
27
  endDate: Date | null;
23
- status: 'in_progress' | 'completed';
28
+ status: 'upcoming' | 'in_progress' | 'ended';
24
29
  updatedAt: Date;
25
30
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ballrush-core",
3
- "version": "0.5.1",
3
+ "version": "0.5.3",
4
4
  "private": false,
5
5
  "license": "MIT",
6
6
  "main": "./dist/index.js",