ballrush-core 0.12.0 → 1.3.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.
@@ -1,4 +1,4 @@
1
- import { ParticipantEvent } from "../types";
1
+ import { GameMode, ParticipantEvent } from "../types";
2
2
  export declare class EventTemplate {
3
3
  private templateId;
4
4
  private groupId;
@@ -15,8 +15,9 @@ export declare class EventTemplate {
15
15
  private defaultParticipants;
16
16
  private maxPlayers;
17
17
  private createdAt;
18
+ private gameMode;
18
19
  constructor(templateId: number, groupId: number, adminId: number, description: string, title: string, preViewImg: string, options: string[], autoPost: boolean, defaultTime: string, defaultDayOfWeek: number, // 0..6
19
- defaultLocation: string, defaultDuration: string, defaultParticipants: ParticipantEvent[], maxPlayers: number, createdAt: Date);
20
+ defaultLocation: string, defaultDuration: string, defaultParticipants: ParticipantEvent[], maxPlayers: number, createdAt: Date, gameMode?: GameMode);
20
21
  static create(p: {
21
22
  templateId: number;
22
23
  groupId: number;
@@ -33,6 +34,7 @@ export declare class EventTemplate {
33
34
  defaultParticipants?: ParticipantEvent[];
34
35
  maxPlayers: number;
35
36
  createdAt?: Date;
37
+ gameMode?: GameMode;
36
38
  }): EventTemplate;
37
39
  getTemplateId(): number;
38
40
  getGroupId(): number;
@@ -49,6 +51,7 @@ export declare class EventTemplate {
49
51
  getDefaultParticipants(): ParticipantEvent[];
50
52
  getMaxPlayers(): number;
51
53
  getCreatedAt(): Date;
54
+ getGameMode(): GameMode;
52
55
  setGroupId(groupId: number): void;
53
56
  setAdminId(adminId: number): void;
54
57
  setDescription(description: string): void;
@@ -62,6 +65,7 @@ export declare class EventTemplate {
62
65
  setDefaultDuration(duration: string): void;
63
66
  setDefaultParticipants(participants: ParticipantEvent[]): void;
64
67
  setMaxPlayers(n: number): void;
68
+ setGameMode(mode: GameMode): void;
65
69
  addDefaultParticipant(p: ParticipantEvent): void;
66
70
  removeDefaultParticipant(userId: number): void;
67
71
  toObject(): {
@@ -80,5 +84,6 @@ export declare class EventTemplate {
80
84
  defaultParticipants: ParticipantEvent[];
81
85
  maxPlayers: number;
82
86
  createdAt: Date;
87
+ gameMode: GameMode;
83
88
  };
84
89
  }
@@ -1,9 +1,10 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.EventTemplate = void 0;
4
+ const types_1 = require("../types");
4
5
  class EventTemplate {
5
6
  constructor(templateId, groupId, adminId, description, title, preViewImg, options, autoPost, defaultTime, defaultDayOfWeek, // 0..6
6
- defaultLocation, defaultDuration, defaultParticipants, maxPlayers, createdAt) {
7
+ defaultLocation, defaultDuration, defaultParticipants, maxPlayers, createdAt, gameMode = types_1.DEFAULT_GAME_MODE) {
7
8
  this.templateId = templateId;
8
9
  this.groupId = groupId;
9
10
  this.adminId = adminId;
@@ -19,13 +20,14 @@ class EventTemplate {
19
20
  this.defaultParticipants = defaultParticipants;
20
21
  this.maxPlayers = maxPlayers;
21
22
  this.createdAt = createdAt;
23
+ this.gameMode = gameMode;
22
24
  }
23
25
  static create(p) {
24
26
  if (!p.title)
25
27
  throw new Error("Title is required");
26
28
  if (p.maxPlayers < 1)
27
29
  throw new Error("maxPlayers must be >= 1");
28
- return new EventTemplate(p.templateId, p.groupId, p.adminId, p.description, p.title, p.preViewImg, [...p.options], p.autoPost ?? false, p.defaultTime, p.defaultDayOfWeek, p.defaultLocation, p.defaultDuration, [...(p.defaultParticipants ?? [])], p.maxPlayers, p.createdAt ?? new Date());
30
+ return new EventTemplate(p.templateId, p.groupId, p.adminId, p.description, p.title, p.preViewImg, [...p.options], p.autoPost ?? false, p.defaultTime, p.defaultDayOfWeek, p.defaultLocation, p.defaultDuration, [...(p.defaultParticipants ?? [])], p.maxPlayers, p.createdAt ?? new Date(), p.gameMode ?? types_1.DEFAULT_GAME_MODE);
29
31
  }
30
32
  // ---- GETTERS
31
33
  getTemplateId() { return this.templateId; }
@@ -43,6 +45,7 @@ class EventTemplate {
43
45
  getDefaultParticipants() { return this.defaultParticipants; }
44
46
  getMaxPlayers() { return this.maxPlayers; }
45
47
  getCreatedAt() { return this.createdAt; }
48
+ getGameMode() { return this.gameMode; }
46
49
  // ---- SETTERS
47
50
  setGroupId(groupId) { this.groupId = groupId; }
48
51
  setAdminId(adminId) { this.adminId = adminId; }
@@ -57,6 +60,7 @@ class EventTemplate {
57
60
  setDefaultDuration(duration) { this.defaultDuration = duration; }
58
61
  setDefaultParticipants(participants) { this.defaultParticipants = participants; }
59
62
  setMaxPlayers(n) { this.maxPlayers = n; }
63
+ setGameMode(mode) { this.gameMode = mode; }
60
64
  addDefaultParticipant(p) {
61
65
  this.defaultParticipants = [...this.defaultParticipants, p];
62
66
  }
@@ -80,6 +84,7 @@ class EventTemplate {
80
84
  defaultParticipants: this.defaultParticipants,
81
85
  maxPlayers: this.maxPlayers,
82
86
  createdAt: this.createdAt,
87
+ gameMode: this.gameMode,
83
88
  };
84
89
  }
85
90
  }
@@ -1,4 +1,4 @@
1
- import { EventStatus, Match, ParticipantEvent, Team } from "../types";
1
+ import { EventStatus, GameMode, Match, ParticipantEvent, Team } from "../types";
2
2
  export declare class Event {
3
3
  private readonly eventId;
4
4
  private readonly groupId;
@@ -11,8 +11,10 @@ export declare class Event {
11
11
  private readonly createdBy;
12
12
  private isRatingApplied;
13
13
  private seasonId;
14
+ private gameMode;
14
15
  private reportMessageId?;
15
16
  private status;
17
+ private excludeFromStats;
16
18
  private constructor();
17
19
  static create(params: {
18
20
  eventId: number;
@@ -28,6 +30,8 @@ export declare class Event {
28
30
  reportMessageId?: number;
29
31
  isRatingApplied?: boolean;
30
32
  status?: EventStatus;
33
+ gameMode?: GameMode;
34
+ excludeFromStats?: boolean;
31
35
  }): Event;
32
36
  getEventId(): number;
33
37
  getGroupId(): number;
@@ -42,6 +46,10 @@ export declare class Event {
42
46
  getCreatedBy(): number;
43
47
  getStatus(): EventStatus;
44
48
  getIsRatingApplied(): boolean;
49
+ getExcludeFromStats(): boolean;
50
+ getGameMode(): GameMode;
51
+ canChangeGameMode(): boolean;
52
+ setGameMode(mode: GameMode): void;
45
53
  reschedule(newDate: Date): void;
46
54
  attachMessage(messageId: number): void;
47
55
  attachReportMessage(reportMessageId: number): void;
@@ -49,6 +57,7 @@ export declare class Event {
49
57
  replaceTeams(teams: Team[]): void;
50
58
  replaceMatches(matches: Match[]): void;
51
59
  setRatingApplied(mark: boolean): void;
60
+ setExcludeFromStats(value: boolean): void;
52
61
  setStatus(status: EventStatus): void;
53
62
  isActive(): boolean;
54
63
  isInProgress(): boolean;
@@ -1,8 +1,9 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.Event = void 0;
4
+ const types_1 = require("../types");
4
5
  class Event {
5
- constructor(eventId, groupId, messageId, templateId, date, participants, teams, matches, createdBy, isRatingApplied, seasonId, reportMessageId, status = "active") {
6
+ constructor(eventId, groupId, messageId, templateId, date, participants, teams, matches, createdBy, isRatingApplied, seasonId, gameMode, reportMessageId, status = "active", excludeFromStats = false) {
6
7
  this.eventId = eventId;
7
8
  this.groupId = groupId;
8
9
  this.messageId = messageId;
@@ -14,14 +15,16 @@ class Event {
14
15
  this.createdBy = createdBy;
15
16
  this.isRatingApplied = isRatingApplied;
16
17
  this.seasonId = seasonId;
18
+ this.gameMode = gameMode;
17
19
  this.reportMessageId = reportMessageId;
18
20
  this.status = status;
21
+ this.excludeFromStats = excludeFromStats;
19
22
  }
20
23
  static create(params) {
21
24
  if (!params.seasonId) {
22
25
  throw new Error("Event must have a season ID");
23
26
  }
24
- return new Event(params.eventId, params.groupId, params.messageId ?? 0, params.templateId, params.date, params.participants ?? [], params.teams ?? [], params.matches ?? [], params.createdBy, params.isRatingApplied ?? false, params.seasonId, params.reportMessageId, params.status ?? "active");
27
+ return new Event(params.eventId, params.groupId, params.messageId ?? 0, params.templateId, params.date, params.participants ?? [], params.teams ?? [], params.matches ?? [], params.createdBy, params.isRatingApplied ?? false, params.seasonId, params.gameMode ?? types_1.DEFAULT_GAME_MODE, params.reportMessageId, params.status ?? "active", params.excludeFromStats ?? false);
25
28
  }
26
29
  getEventId() { return this.eventId; }
27
30
  getGroupId() { return this.groupId; }
@@ -36,6 +39,15 @@ class Event {
36
39
  getCreatedBy() { return this.createdBy; }
37
40
  getStatus() { return this.status; }
38
41
  getIsRatingApplied() { return this.isRatingApplied; }
42
+ getExcludeFromStats() { return this.excludeFromStats; }
43
+ getGameMode() { return this.gameMode; }
44
+ canChangeGameMode() { return this.matches.length === 0; }
45
+ setGameMode(mode) {
46
+ if (!this.canChangeGameMode()) {
47
+ throw new Error("Cannot change game mode after the first match score has been entered");
48
+ }
49
+ this.gameMode = mode;
50
+ }
39
51
  reschedule(newDate) {
40
52
  this.date = newDate;
41
53
  }
@@ -57,6 +69,9 @@ class Event {
57
69
  setRatingApplied(mark) {
58
70
  this.isRatingApplied = mark;
59
71
  }
72
+ setExcludeFromStats(value) {
73
+ this.excludeFromStats = value;
74
+ }
60
75
  setStatus(status) {
61
76
  this.status = status;
62
77
  }
@@ -1,5 +1,5 @@
1
1
  import { Schema } from "mongoose";
2
- import { ParticipantEvent } from "../../types";
2
+ import { GameMode, ParticipantEvent } from "../../types";
3
3
  export interface EventTemplateDoc {
4
4
  templateId: number;
5
5
  groupId: number;
@@ -15,6 +15,7 @@ export interface EventTemplateDoc {
15
15
  defaultDuration: string;
16
16
  defaultParticipants: ParticipantEvent[];
17
17
  maxPlayers: number;
18
+ gameMode: GameMode;
18
19
  createdAt: Date;
19
20
  }
20
21
  export declare function createEventTemplateSchema(): Schema<EventTemplateDoc, import("mongoose").Model<EventTemplateDoc, any, any, any, import("mongoose").Document<unknown, any, EventTemplateDoc, any, {}> & EventTemplateDoc & {
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.createEventTemplateSchema = createEventTemplateSchema;
4
4
  const mongoose_1 = require("mongoose");
5
+ const types_1 = require("../../types");
5
6
  function createEventTemplateSchema() {
6
7
  const DefaultParticipantSchema = new mongoose_1.Schema({
7
8
  userId: { type: Number, required: true },
@@ -24,6 +25,7 @@ function createEventTemplateSchema() {
24
25
  defaultDuration: { type: String, required: true },
25
26
  defaultParticipants: { type: [DefaultParticipantSchema], default: [] },
26
27
  maxPlayers: { type: Number, required: true, min: 1 },
28
+ gameMode: { type: String, enum: ["first_to_2", "timed_7min"], default: types_1.DEFAULT_GAME_MODE },
27
29
  createdAt: { type: Date, default: Date.now },
28
30
  });
29
31
  return schema;
@@ -1,5 +1,5 @@
1
1
  import { Schema } from "mongoose";
2
- import { EventStatus, Match, MatchEvent, ParticipantEvent, Team } from "../../types";
2
+ import { EventStatus, GameMode, Match, MatchEvent, ParticipantEvent, Team } from "../../types";
3
3
  export interface EventDoc {
4
4
  eventId: number;
5
5
  groupId: number;
@@ -13,6 +13,8 @@ export interface EventDoc {
13
13
  seasonId: string;
14
14
  status: EventStatus;
15
15
  isRatingApplied: boolean;
16
+ excludeFromStats?: boolean;
17
+ gameMode: GameMode;
16
18
  reportMessageId?: number;
17
19
  }
18
20
  export declare function createParticipantEventSchema(): Schema<ParticipantEvent, import("mongoose").Model<ParticipantEvent, any, any, any, import("mongoose").Document<unknown, any, ParticipantEvent, any, {}> & ParticipantEvent & {
@@ -6,6 +6,7 @@ exports.createMatchEventSchema = createMatchEventSchema;
6
6
  exports.createMatchSchema = createMatchSchema;
7
7
  exports.createEventSchema = createEventSchema;
8
8
  const mongoose_1 = require("mongoose");
9
+ const types_1 = require("../../types");
9
10
  function createParticipantEventSchema() {
10
11
  return new mongoose_1.Schema({
11
12
  userId: { type: Number, required: true },
@@ -56,7 +57,13 @@ function createEventSchema() {
56
57
  teams: { type: [TeamSchema], default: [] },
57
58
  matches: { type: [MatchSchema], default: [] },
58
59
  isRatingApplied: { type: Boolean },
60
+ excludeFromStats: { type: Boolean, default: false },
59
61
  reportMessageId: { type: Number },
62
+ gameMode: {
63
+ type: String,
64
+ enum: ["first_to_2", "timed_7min"],
65
+ default: types_1.DEFAULT_GAME_MODE,
66
+ },
60
67
  status: {
61
68
  type: String,
62
69
  enum: ["active", "in_progress", "completed"],
@@ -1,5 +1,5 @@
1
1
  import { Event } from "../domain/event";
2
- import { ParticipantEvent, Team, Match, EventStatus } from "../types";
2
+ import { ParticipantEvent, Team, Match, EventStatus, GameMode } from "../types";
3
3
  export type EventWrite = {
4
4
  eventId: number;
5
5
  groupId: number;
@@ -14,6 +14,8 @@ export type EventWrite = {
14
14
  seasonId: string;
15
15
  isRatingApplied?: boolean;
16
16
  reportMessageId?: number;
17
+ gameMode?: GameMode;
18
+ excludeFromStats?: boolean;
17
19
  };
18
20
  export interface EventsRepository {
19
21
  create(eventData: Event): Promise<Event>;
@@ -14,10 +14,19 @@ export type GroupWrite = {
14
14
  language: LanguageType;
15
15
  isPublic: boolean;
16
16
  };
17
+ export type GroupFieldsUpdate = Partial<Pick<GroupWrite, "groupName" | "timezone" | "isMessageLast" | "language" | "isPublic">>;
17
18
  export interface GroupsRepository {
18
19
  create(groupData: Group): Promise<Group>;
19
20
  findById(groupId: number): Promise<Group | null>;
20
21
  update(groupId: number, updates: Group): Promise<Group | null>;
22
+ /**
23
+ * Partial $set update for non-participant scalar fields.
24
+ * Use this instead of update() whenever you only need to change metadata
25
+ * (timezone, language, isMessageLast, etc.) — update() rewrites participants
26
+ * from the in-memory cache and can clobber rating data written by other
27
+ * processes between cache load and save.
28
+ */
29
+ updateFields(groupId: number, fields: GroupFieldsUpdate): Promise<void>;
21
30
  findAll(): Promise<Group[]>;
22
31
  delete(groupId: number): Promise<void>;
23
32
  }
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.toDomain = toDomain;
4
4
  exports.toDoc = toDoc;
5
5
  const event_template_1 = require("../../domain/event-template");
6
+ const types_1 = require("../../types");
6
7
  function toDomain(doc) {
7
8
  return event_template_1.EventTemplate.create({
8
9
  templateId: doc.templateId,
@@ -25,6 +26,7 @@ function toDomain(doc) {
25
26
  })),
26
27
  maxPlayers: doc.maxPlayers,
27
28
  createdAt: new Date(doc.createdAt),
29
+ gameMode: doc.gameMode ?? types_1.DEFAULT_GAME_MODE,
28
30
  });
29
31
  }
30
32
  ;
@@ -44,6 +46,7 @@ function toDoc(entity) {
44
46
  defaultDuration: entity.getDefaultDuration(),
45
47
  defaultParticipants: entity.getDefaultParticipants(),
46
48
  maxPlayers: entity.getMaxPlayers(),
49
+ gameMode: entity.getGameMode(),
47
50
  createdAt: entity.getCreatedAt(),
48
51
  };
49
52
  }
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.toDomain = toDomain;
4
4
  exports.toDoc = toDoc;
5
5
  const event_1 = require("../../domain/event");
6
+ const types_1 = require("../../types");
6
7
  function toDomain(doc) {
7
8
  return event_1.Event.create({
8
9
  eventId: doc.eventId,
@@ -18,6 +19,8 @@ function toDomain(doc) {
18
19
  seasonId: doc.seasonId,
19
20
  reportMessageId: doc.reportMessageId,
20
21
  status: doc.status,
22
+ gameMode: doc.gameMode ?? types_1.DEFAULT_GAME_MODE,
23
+ excludeFromStats: doc.excludeFromStats ?? false,
21
24
  });
22
25
  }
23
26
  function toDoc(event) {
@@ -35,5 +38,7 @@ function toDoc(event) {
35
38
  seasonId: event.getSeasonId(),
36
39
  isRatingApplied: event.getIsRatingApplied(),
37
40
  reportMessageId: event.getReportMessageId(),
41
+ gameMode: event.getGameMode(),
42
+ excludeFromStats: event.getExcludeFromStats(),
38
43
  };
39
44
  }
@@ -1,5 +1,5 @@
1
1
  import type { Model } from "mongoose";
2
- import type { GroupsRepository } from "../../ports/groups.repository";
2
+ import type { GroupsRepository, GroupFieldsUpdate } from "../../ports/groups.repository";
3
3
  import type { GroupDoc } from "../../mongo";
4
4
  import { Group } from "../../domain/group";
5
5
  export declare class MongoGroupsRepository implements GroupsRepository {
@@ -8,6 +8,7 @@ export declare class MongoGroupsRepository implements GroupsRepository {
8
8
  create(group: Group): Promise<Group>;
9
9
  findById(groupId: number): Promise<Group | null>;
10
10
  update(groupId: number, updates: Group): Promise<Group | null>;
11
+ updateFields(groupId: number, fields: GroupFieldsUpdate): Promise<void>;
11
12
  findAll(): Promise<Group[]>;
12
13
  delete(groupId: number): Promise<void>;
13
14
  }
@@ -68,6 +68,22 @@ class MongoGroupsRepository {
68
68
  throw new errors_1.QueryError('update', 'Group', groupId, error);
69
69
  }
70
70
  }
71
+ async updateFields(groupId, fields) {
72
+ try {
73
+ if (!groupId || groupId === 0) {
74
+ throw new errors_1.ValidationError('Group', 'groupId', groupId, 'must be a non-zero number');
75
+ }
76
+ if (!fields || Object.keys(fields).length === 0)
77
+ return;
78
+ await this.GroupModel.updateOne({ groupId }, { $set: fields });
79
+ }
80
+ catch (error) {
81
+ if (error instanceof errors_1.ValidationError) {
82
+ throw error;
83
+ }
84
+ throw new errors_1.QueryError('updateFields', 'Group', groupId, error);
85
+ }
86
+ }
71
87
  async findAll() {
72
88
  try {
73
89
  const docs = await this.GroupModel.find().lean();
@@ -1,6 +1,8 @@
1
1
  export type EventStatus = "active" | "in_progress" | "completed";
2
2
  export type LanguageType = "en" | "ru" | "ua";
3
3
  export type DayOfWeek = "Monday" | "Tuesday" | "Wednesday" | "Thursday" | "Friday" | "Saturday" | "Sunday";
4
+ export type GameMode = "first_to_2" | "timed_7min";
5
+ export declare const DEFAULT_GAME_MODE: GameMode;
4
6
  export interface ParticipantBase {
5
7
  userId: number;
6
8
  }
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.RatingSource = exports.PlayerPosition = void 0;
3
+ exports.RatingSource = exports.PlayerPosition = exports.DEFAULT_GAME_MODE = void 0;
4
+ exports.DEFAULT_GAME_MODE = "first_to_2";
4
5
  var PlayerPosition;
5
6
  (function (PlayerPosition) {
6
7
  PlayerPosition["FW"] = "FW";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ballrush-core",
3
- "version": "0.12.0",
3
+ "version": "1.3.0",
4
4
  "private": false,
5
5
  "license": "MIT",
6
6
  "main": "./dist/index.js",