volleyballsimtypes 0.0.480 → 0.0.482

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 (37) hide show
  1. package/dist/cjs/src/api/index.d.ts +8 -2
  2. package/dist/cjs/src/data/models/tactics.d.ts +11 -3
  3. package/dist/cjs/src/data/models/tactics.js +9 -0
  4. package/dist/cjs/src/data/transformers/tactics.js +12 -4
  5. package/dist/cjs/src/data/transformers/tactics.test.js +23 -9
  6. package/dist/cjs/src/service/competition/standing.js +5 -0
  7. package/dist/cjs/src/service/competition/standing.test.js +19 -8
  8. package/dist/cjs/src/service/team/blocking-preference.d.ts +5 -0
  9. package/dist/cjs/src/service/team/blocking-preference.js +2 -0
  10. package/dist/cjs/src/service/team/index.d.ts +1 -0
  11. package/dist/cjs/src/service/team/index.js +1 -0
  12. package/dist/cjs/src/service/team/offensive-preference.d.ts +1 -2
  13. package/dist/cjs/src/service/team/schemas/tactics.z.d.ts +11 -3
  14. package/dist/cjs/src/service/team/schemas/tactics.z.js +43 -10
  15. package/dist/cjs/src/service/team/schemas/tactics.z.test.js +81 -8
  16. package/dist/cjs/src/service/team/schemas/team.z.d.ts +10 -2
  17. package/dist/cjs/src/service/team/tactics.d.ts +5 -0
  18. package/dist/cjs/src/service/team/tactics.js +3 -1
  19. package/dist/esm/src/api/index.d.ts +8 -2
  20. package/dist/esm/src/data/models/tactics.d.ts +11 -3
  21. package/dist/esm/src/data/models/tactics.js +9 -0
  22. package/dist/esm/src/data/transformers/tactics.js +12 -4
  23. package/dist/esm/src/data/transformers/tactics.test.js +24 -10
  24. package/dist/esm/src/service/competition/standing.js +5 -0
  25. package/dist/esm/src/service/competition/standing.test.js +19 -8
  26. package/dist/esm/src/service/team/blocking-preference.d.ts +5 -0
  27. package/dist/esm/src/service/team/blocking-preference.js +1 -0
  28. package/dist/esm/src/service/team/index.d.ts +1 -0
  29. package/dist/esm/src/service/team/index.js +1 -0
  30. package/dist/esm/src/service/team/offensive-preference.d.ts +1 -2
  31. package/dist/esm/src/service/team/schemas/tactics.z.d.ts +11 -3
  32. package/dist/esm/src/service/team/schemas/tactics.z.js +44 -11
  33. package/dist/esm/src/service/team/schemas/tactics.z.test.js +81 -8
  34. package/dist/esm/src/service/team/schemas/team.z.d.ts +10 -2
  35. package/dist/esm/src/service/team/tactics.d.ts +5 -0
  36. package/dist/esm/src/service/team/tactics.js +3 -1
  37. package/package.json +1 -1
@@ -1,5 +1,6 @@
1
1
  import { describe, it, expect } from '@jest/globals';
2
2
  import { TacticsInputSchema } from './tactics.z';
3
+ import { RoleEnum } from '../../player';
3
4
  import { makeCountry, makePlayer } from '../../test-helpers';
4
5
  describe('TacticsInputSchema — second libero rules', () => {
5
6
  const country = makeCountry();
@@ -93,7 +94,7 @@ describe('TacticsInputSchema — per-set tactics (systemSets / offensivePreferen
93
94
  { rotationSystem: '4-2', designatedSetters: [p1, p4] }
94
95
  ],
95
96
  offensivePreferenceSets: [
96
- [{ rotation: 1, order: [p2, p3] }],
97
+ [{ rotation: 1, weights: { [p2.id]: 60, [p3.id]: 40 } }],
97
98
  []
98
99
  ]
99
100
  }));
@@ -102,7 +103,7 @@ describe('TacticsInputSchema — per-set tactics (systemSets / offensivePreferen
102
103
  it('carries the per-set fields through Tactics.create parsing', () => {
103
104
  const res = TacticsInputSchema.safeParse(baseInput({
104
105
  systemSets: [{ rotationSystem: '5-1', designatedSetters: [p1] }],
105
- offensivePreferenceSets: [[{ rotation: 2, order: [p3] }]]
106
+ offensivePreferenceSets: [[{ rotation: 2, weights: { [p3.id]: 100 } }]]
106
107
  }));
107
108
  expect(res.success).toBe(true);
108
109
  if (res.success) {
@@ -151,7 +152,7 @@ describe('TacticsInputSchema — per-set tactics (systemSets / offensivePreferen
151
152
  });
152
153
  it('rejects a per-set preferences list with a duplicate rotation', () => {
153
154
  const res = TacticsInputSchema.safeParse(baseInput({
154
- offensivePreferenceSets: [[{ rotation: 1, order: [p2] }, { rotation: 1, order: [p3] }]]
155
+ offensivePreferenceSets: [[{ rotation: 1, weights: { [p2.id]: 100 } }, { rotation: 1, weights: { [p3.id]: 100 } }]]
155
156
  }));
156
157
  expect(res.success).toBe(false);
157
158
  if (!res.success) {
@@ -159,18 +160,27 @@ describe('TacticsInputSchema — per-set tactics (systemSets / offensivePreferen
159
160
  expect(issue?.path).toEqual(['offensivePreferenceSets', 0]);
160
161
  }
161
162
  });
162
- it('rejects a per-set preferences order that lists a non-starter', () => {
163
+ it('rejects a per-set preferences map that keys a non-starter', () => {
163
164
  const res = TacticsInputSchema.safeParse(baseInput({
164
- offensivePreferenceSets: [[{ rotation: 1, order: [reserve] }]]
165
+ offensivePreferenceSets: [[{ rotation: 1, weights: { [reserve.id]: 100 } }]]
165
166
  }));
166
167
  expect(res.success).toBe(false);
167
168
  if (!res.success) {
168
169
  expect(res.error.issues.some(i => i.message === 'OFFENSIVE_PREFERENCE_ATTACKER_NOT_A_STARTER')).toBe(true);
169
170
  }
170
171
  });
172
+ it('rejects offensive preference weights that do not sum to 100', () => {
173
+ const res = TacticsInputSchema.safeParse(baseInput({
174
+ offensivePreferences: [{ rotation: 1, weights: { [p2.id]: 50, [p3.id]: 40 } }]
175
+ }));
176
+ expect(res.success).toBe(false);
177
+ if (!res.success) {
178
+ expect(res.error.issues.some(i => i.message === 'OFFENSIVE_PREFERENCE_WEIGHTS_MUST_SUM_TO_100')).toBe(true);
179
+ }
180
+ });
171
181
  it('accepts offensive preferences with a starter-keyed tempos map', () => {
172
182
  const res = TacticsInputSchema.safeParse(baseInput({
173
- offensivePreferences: [{ rotation: 1, order: [p2, p3], tempos: { [p2.id]: 'FAST', [p3.id]: 'HIGH' } }]
183
+ offensivePreferences: [{ rotation: 1, weights: { [p2.id]: 60, [p3.id]: 40 }, tempos: { [p2.id]: 'FAST', [p3.id]: 'HIGH' } }]
174
184
  }));
175
185
  expect(res.success).toBe(true);
176
186
  if (res.success) {
@@ -179,7 +189,7 @@ describe('TacticsInputSchema — per-set tactics (systemSets / offensivePreferen
179
189
  });
180
190
  it('rejects a tempos map keyed by a non-starter', () => {
181
191
  const res = TacticsInputSchema.safeParse(baseInput({
182
- offensivePreferences: [{ rotation: 1, order: [p2], tempos: { [reserve.id]: 'FAST' } }]
192
+ offensivePreferences: [{ rotation: 1, weights: { [p2.id]: 100 }, tempos: { [reserve.id]: 'FAST' } }]
183
193
  }));
184
194
  expect(res.success).toBe(false);
185
195
  if (!res.success) {
@@ -188,7 +198,7 @@ describe('TacticsInputSchema — per-set tactics (systemSets / offensivePreferen
188
198
  });
189
199
  it('rejects an invalid tempo value', () => {
190
200
  const res = TacticsInputSchema.safeParse(baseInput({
191
- offensivePreferences: [{ rotation: 1, order: [p2], tempos: { [p2.id]: 'SLOW' } }]
201
+ offensivePreferences: [{ rotation: 1, weights: { [p2.id]: 100 }, tempos: { [p2.id]: 'SLOW' } }]
192
202
  }));
193
203
  expect(res.success).toBe(false);
194
204
  });
@@ -206,3 +216,66 @@ describe('TacticsInputSchema — per-set tactics (systemSets / offensivePreferen
206
216
  }
207
217
  });
208
218
  });
219
+ describe('TacticsInputSchema — blocking preferences', () => {
220
+ const country = makeCountry();
221
+ const p1 = makePlayer(country);
222
+ const p2 = makePlayer(country);
223
+ const p3 = makePlayer(country);
224
+ const p4 = makePlayer(country);
225
+ const p5 = makePlayer(country);
226
+ const p6 = makePlayer(country);
227
+ const reserve = makePlayer(country);
228
+ // Front-row starters (zones 2/3/4) are p2, p3, p4; the validator only requires target keys to be starters.
229
+ function baseInput(overrides = {}) {
230
+ return {
231
+ lineup: { 4: p4, 3: p3, 2: p2, 5: p5, 6: p6, 1: p1, bench: [] },
232
+ liberoReplacements: [],
233
+ ...overrides
234
+ };
235
+ }
236
+ it('accepts a blocking preference that keys starters to attacking functions', () => {
237
+ const res = TacticsInputSchema.safeParse(baseInput({
238
+ blockingPreferences: [{ rotation: 1, targets: { [p4.id]: RoleEnum.OUTSIDE_HITTER, [p3.id]: RoleEnum.MIDDLE_BLOCKER } }]
239
+ }));
240
+ expect(res.success).toBe(true);
241
+ if (res.success) {
242
+ expect(res.data.blockingPreferences[0].targets[p4.id]).toBe(RoleEnum.OUTSIDE_HITTER);
243
+ }
244
+ });
245
+ it('rejects a blocking target that is not a starter', () => {
246
+ const res = TacticsInputSchema.safeParse(baseInput({
247
+ blockingPreferences: [{ rotation: 1, targets: { [reserve.id]: RoleEnum.OUTSIDE_HITTER } }]
248
+ }));
249
+ expect(res.success).toBe(false);
250
+ if (!res.success) {
251
+ expect(res.error.issues.some(i => i.message === 'BLOCKING_PREFERENCE_BLOCKER_NOT_A_STARTER')).toBe(true);
252
+ }
253
+ });
254
+ it('rejects a blocking target function of libero', () => {
255
+ const res = TacticsInputSchema.safeParse(baseInput({
256
+ blockingPreferences: [{ rotation: 1, targets: { [p4.id]: RoleEnum.LIBERO } }]
257
+ }));
258
+ expect(res.success).toBe(false);
259
+ if (!res.success) {
260
+ expect(res.error.issues.some(i => i.message === 'BLOCKING_TARGET_CANNOT_BE_LIBERO')).toBe(true);
261
+ }
262
+ });
263
+ it('rejects a duplicate rotation in blocking preferences', () => {
264
+ const res = TacticsInputSchema.safeParse(baseInput({
265
+ blockingPreferences: [
266
+ { rotation: 1, targets: { [p4.id]: RoleEnum.OUTSIDE_HITTER } },
267
+ { rotation: 1, targets: { [p3.id]: RoleEnum.MIDDLE_BLOCKER } }
268
+ ]
269
+ }));
270
+ expect(res.success).toBe(false);
271
+ if (!res.success) {
272
+ expect(res.error.issues.some(i => i.message === 'BLOCKING_PREFERENCE_DUPLICATE_ROTATION')).toBe(true);
273
+ }
274
+ });
275
+ it('accepts per-set blocking preference overrides', () => {
276
+ const res = TacticsInputSchema.safeParse(baseInput({
277
+ blockingPreferenceSets: [[{ rotation: 1, targets: { [p4.id]: RoleEnum.OPPOSITE_HITTER } }], []]
278
+ }));
279
+ expect(res.success).toBe(true);
280
+ });
281
+ });
@@ -277,7 +277,7 @@ export declare const TeamInputSchema: z.ZodObject<{
277
277
  designatedSetters: z.ZodDefault<z.ZodArray<z.ZodCustom<Player, Player>>>;
278
278
  offensivePreferences: z.ZodDefault<z.ZodArray<z.ZodObject<{
279
279
  rotation: z.ZodNumber;
280
- order: z.ZodArray<z.ZodCustom<Player, Player>>;
280
+ weights: z.ZodRecord<z.ZodString, z.ZodNumber>;
281
281
  tempos: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodEnum<{
282
282
  FAST: "FAST";
283
283
  HIGH: "HIGH";
@@ -289,12 +289,20 @@ export declare const TeamInputSchema: z.ZodObject<{
289
289
  }, z.core.$strip>>>;
290
290
  offensivePreferenceSets: z.ZodOptional<z.ZodArray<z.ZodArray<z.ZodObject<{
291
291
  rotation: z.ZodNumber;
292
- order: z.ZodArray<z.ZodCustom<Player, Player>>;
292
+ weights: z.ZodRecord<z.ZodString, z.ZodNumber>;
293
293
  tempos: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodEnum<{
294
294
  FAST: "FAST";
295
295
  HIGH: "HIGH";
296
296
  }>>>;
297
297
  }, z.core.$strip>>>>;
298
+ blockingPreferences: z.ZodDefault<z.ZodArray<z.ZodObject<{
299
+ rotation: z.ZodNumber;
300
+ targets: z.ZodRecord<z.ZodString, z.ZodEnum<typeof import("../../player").RoleEnum> & z.ZodType<import("../../player").RoleEnum.SETTER | import("../../player").RoleEnum.OUTSIDE_HITTER | import("../../player").RoleEnum.OPPOSITE_HITTER | import("../../player").RoleEnum.MIDDLE_BLOCKER, import("../../player").RoleEnum, z.core.$ZodTypeInternals<import("../../player").RoleEnum.SETTER | import("../../player").RoleEnum.OUTSIDE_HITTER | import("../../player").RoleEnum.OPPOSITE_HITTER | import("../../player").RoleEnum.MIDDLE_BLOCKER, import("../../player").RoleEnum>>>;
301
+ }, z.core.$strip>>>;
302
+ blockingPreferenceSets: z.ZodOptional<z.ZodArray<z.ZodArray<z.ZodObject<{
303
+ rotation: z.ZodNumber;
304
+ targets: z.ZodRecord<z.ZodString, z.ZodEnum<typeof import("../../player").RoleEnum> & z.ZodType<import("../../player").RoleEnum.SETTER | import("../../player").RoleEnum.OUTSIDE_HITTER | import("../../player").RoleEnum.OPPOSITE_HITTER | import("../../player").RoleEnum.MIDDLE_BLOCKER, import("../../player").RoleEnum, z.core.$ZodTypeInternals<import("../../player").RoleEnum.SETTER | import("../../player").RoleEnum.OUTSIDE_HITTER | import("../../player").RoleEnum.OPPOSITE_HITTER | import("../../player").RoleEnum.MIDDLE_BLOCKER, import("../../player").RoleEnum>>>;
305
+ }, z.core.$strip>>>>;
298
306
  replaceKnockedImmediately: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodBoolean>>;
299
307
  injuryReplacements: z.ZodDefault<z.ZodArray<z.ZodObject<{
300
308
  starter: z.ZodCustom<Player, Player>;
@@ -5,6 +5,7 @@ import { LiberoSubConfig } from './libero-sub';
5
5
  import { SubBand } from './energy-band';
6
6
  import { RotationSystemEnum } from './rotation-system';
7
7
  import { OffensivePreference } from './offensive-preference';
8
+ import { BlockingPreference } from './blocking-preference';
8
9
  import { InjuryReplacement } from './injury-replacement';
9
10
  import { LiberoInjuryConfig } from './libero-injury';
10
11
  export interface StartingLineup {
@@ -38,6 +39,8 @@ export interface TacticsOpts {
38
39
  readonly replaceKnockedImmediately: Record<string, boolean>;
39
40
  readonly injuryReplacements: InjuryReplacement[];
40
41
  readonly liberoInjury?: LiberoInjuryConfig;
42
+ readonly blockingPreferences: BlockingPreference[];
43
+ readonly blockingPreferenceSets?: BlockingPreference[][];
41
44
  }
42
45
  export declare class Tactics {
43
46
  readonly lineup: StartingLineup;
@@ -56,6 +59,8 @@ export declare class Tactics {
56
59
  readonly replaceKnockedImmediately: Record<string, boolean>;
57
60
  readonly injuryReplacements: InjuryReplacement[];
58
61
  readonly liberoInjury?: LiberoInjuryConfig;
62
+ readonly blockingPreferences: BlockingPreference[];
63
+ readonly blockingPreferenceSets?: BlockingPreference[][];
59
64
  static create(input: unknown): Tactics;
60
65
  private constructor();
61
66
  findPlayerInLineup(id: string): CourtPosition | undefined;
@@ -15,7 +15,7 @@ export class Tactics {
15
15
  }
16
16
  return new Tactics(result.data);
17
17
  }
18
- constructor({ lineup, liberoReplacements, receiveRotationOffset = false, designatedSubs = [], substitutionBand = EnergyBand.TIRED, secondLibero, liberoSub, liberoReplacementSets, rotationSystem = RotationSystemEnum.SIX_ZERO, designatedSetters = [], offensivePreferences = [], systemSets, offensivePreferenceSets, replaceKnockedImmediately = {}, injuryReplacements = [], liberoInjury }) {
18
+ constructor({ lineup, liberoReplacements, receiveRotationOffset = false, designatedSubs = [], substitutionBand = EnergyBand.TIRED, secondLibero, liberoSub, liberoReplacementSets, rotationSystem = RotationSystemEnum.SIX_ZERO, designatedSetters = [], offensivePreferences = [], systemSets, offensivePreferenceSets, replaceKnockedImmediately = {}, injuryReplacements = [], liberoInjury, blockingPreferences = [], blockingPreferenceSets }) {
19
19
  this.lineup = lineup;
20
20
  this.liberoReplacements = liberoReplacements;
21
21
  this.receiveRotationOffset = receiveRotationOffset;
@@ -32,6 +32,8 @@ export class Tactics {
32
32
  this.replaceKnockedImmediately = replaceKnockedImmediately;
33
33
  this.injuryReplacements = injuryReplacements;
34
34
  this.liberoInjury = liberoInjury;
35
+ this.blockingPreferences = blockingPreferences;
36
+ this.blockingPreferenceSets = blockingPreferenceSets;
35
37
  }
36
38
  findPlayerInLineup(id) {
37
39
  if (this.lineup.bench.some(p => p.id === id))
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "volleyballsimtypes",
3
- "version": "0.0.480",
3
+ "version": "0.0.482",
4
4
  "description": "vbsim types",
5
5
  "main": "./dist/cjs/src/index.js",
6
6
  "module": "./dist/esm/src/index.js",