volleyballsimtypes 0.0.471 → 0.0.474

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 (47) hide show
  1. package/dist/cjs/src/api/index.d.ts +2 -0
  2. package/dist/cjs/src/data/models/tactics.d.ts +2 -0
  3. package/dist/cjs/src/data/transformers/rally-event-injury.test.js +27 -0
  4. package/dist/cjs/src/data/transformers/rally-event.d.ts +1 -0
  5. package/dist/cjs/src/data/transformers/rally-event.js +16 -8
  6. package/dist/cjs/src/service/event/in-play-event.d.ts +1 -0
  7. package/dist/cjs/src/service/event/schemas/block.z.d.ts +1 -0
  8. package/dist/cjs/src/service/event/schemas/incident.z.d.ts +1 -0
  9. package/dist/cjs/src/service/event/schemas/incident.z.js +4 -1
  10. package/dist/cjs/src/service/event/schemas/reception.z.d.ts +1 -0
  11. package/dist/cjs/src/service/event/schemas/serve.z.d.ts +1 -0
  12. package/dist/cjs/src/service/event/schemas/set.z.d.ts +1 -0
  13. package/dist/cjs/src/service/event/schemas/spike.z.d.ts +1 -0
  14. package/dist/cjs/src/service/team/libero-sub.d.ts +4 -1
  15. package/dist/cjs/src/service/team/pinch-condition.d.ts +12 -2
  16. package/dist/cjs/src/service/team/pinch-condition.js +10 -2
  17. package/dist/cjs/src/service/team/schemas/designated-sub.z.d.ts +13 -1
  18. package/dist/cjs/src/service/team/schemas/designated-sub.z.js +3 -1
  19. package/dist/cjs/src/service/team/schemas/libero-sub.z.d.ts +94 -0
  20. package/dist/cjs/src/service/team/schemas/libero-sub.z.js +23 -6
  21. package/dist/cjs/src/service/team/schemas/libero-sub.z.test.js +33 -2
  22. package/dist/cjs/src/service/team/schemas/tactics.z.d.ts +68 -0
  23. package/dist/cjs/src/service/team/schemas/team.z.d.ts +68 -0
  24. package/dist/esm/src/api/index.d.ts +2 -0
  25. package/dist/esm/src/data/models/tactics.d.ts +2 -0
  26. package/dist/esm/src/data/transformers/rally-event-injury.test.js +29 -2
  27. package/dist/esm/src/data/transformers/rally-event.d.ts +1 -0
  28. package/dist/esm/src/data/transformers/rally-event.js +16 -8
  29. package/dist/esm/src/service/event/in-play-event.d.ts +1 -0
  30. package/dist/esm/src/service/event/schemas/block.z.d.ts +1 -0
  31. package/dist/esm/src/service/event/schemas/incident.z.d.ts +1 -0
  32. package/dist/esm/src/service/event/schemas/incident.z.js +4 -1
  33. package/dist/esm/src/service/event/schemas/reception.z.d.ts +1 -0
  34. package/dist/esm/src/service/event/schemas/serve.z.d.ts +1 -0
  35. package/dist/esm/src/service/event/schemas/set.z.d.ts +1 -0
  36. package/dist/esm/src/service/event/schemas/spike.z.d.ts +1 -0
  37. package/dist/esm/src/service/team/libero-sub.d.ts +4 -1
  38. package/dist/esm/src/service/team/pinch-condition.d.ts +12 -2
  39. package/dist/esm/src/service/team/pinch-condition.js +9 -1
  40. package/dist/esm/src/service/team/schemas/designated-sub.z.d.ts +13 -1
  41. package/dist/esm/src/service/team/schemas/designated-sub.z.js +4 -2
  42. package/dist/esm/src/service/team/schemas/libero-sub.z.d.ts +94 -0
  43. package/dist/esm/src/service/team/schemas/libero-sub.z.js +23 -6
  44. package/dist/esm/src/service/team/schemas/libero-sub.z.test.js +33 -2
  45. package/dist/esm/src/service/team/schemas/tactics.z.d.ts +68 -0
  46. package/dist/esm/src/service/team/schemas/team.z.d.ts +68 -0
  47. package/package.json +1 -1
@@ -114,6 +114,8 @@ export interface ApiInjuryReplacement {
114
114
  export interface ApiLiberoSetSubConfig {
115
115
  mode: LiberoSubMode;
116
116
  fatigueBand?: EnergyBand;
117
+ conditions?: PinchCondition[];
118
+ conditionLogic?: ConditionLogic;
117
119
  }
118
120
  export interface ApiLiberoSubConfig extends ApiLiberoSetSubConfig {
119
121
  sets?: ApiLiberoSetSubConfig[];
@@ -47,6 +47,8 @@ export interface InjuryReplacementAttributes {
47
47
  export interface LiberoSetSubConfigAttributes {
48
48
  mode: LiberoSubMode;
49
49
  fatigueBand?: EnergyBand;
50
+ conditions?: PinchCondition[];
51
+ conditionLogic?: ConditionLogic;
50
52
  }
51
53
  export interface LiberoSubConfigAttributes extends LiberoSetSubConfigAttributes {
52
54
  sets?: LiberoSetSubConfigAttributes[];
@@ -76,6 +76,33 @@ const rally_event_1 = require("./rally-event");
76
76
  const decoded = (0, rally_event_1.transformToSpike)(legacy, roster);
77
77
  (0, globals_1.expect)(decoded.incident).toBeUndefined();
78
78
  });
79
+ (0, globals_1.it)('packs a non-actor blocker incident with the hurt player under h and round-trips it', () => {
80
+ const lead = (0, uuid_1.v4)();
81
+ const helper = (0, uuid_1.v4)();
82
+ const blockRoster = [lead, helper];
83
+ const blockIndex = new Map(blockRoster.map((id, i) => [id, i]));
84
+ const block = service_1.Block.create({
85
+ playerId: lead,
86
+ blockers: [lead, helper],
87
+ score: 48,
88
+ target: 8,
89
+ failure: service_1.BlockFailureEnum.NO_FAILURE,
90
+ type: service_1.BlockTypeEnum.DOUBLE,
91
+ incident: { kind: 'KNOCK', severity: 3, playerId: helper }
92
+ });
93
+ const compact = (0, rally_event_1.transformToCompact)(block, blockIndex);
94
+ (0, globals_1.expect)(compact.i).toBe(13);
95
+ (0, globals_1.expect)(compact.h).toBe(1);
96
+ const decoded = (0, rally_event_1.transformToBlock)(compact, blockRoster);
97
+ (0, globals_1.expect)(decoded.incident).toEqual({ kind: 'KNOCK', severity: 3, playerId: helper });
98
+ });
99
+ (0, globals_1.it)('omits h when the hurt player IS the event actor', () => {
100
+ const compact = (0, rally_event_1.transformToCompact)(makeSpike({ kind: 'INJURY', severity: 2, playerId }), playerIndex);
101
+ (0, globals_1.expect)(compact.i).toBe(2);
102
+ (0, globals_1.expect)('h' in compact).toBe(false);
103
+ const decoded = (0, rally_event_1.transformToSpike)(compact, roster);
104
+ (0, globals_1.expect)(decoded.incident).toEqual({ kind: 'INJURY', severity: 2 });
105
+ });
79
106
  (0, globals_1.it)('rejects an out-of-range or malformed incident at the schema', () => {
80
107
  (0, globals_1.expect)(() => makeSpike({ kind: 'INJURY', severity: 5 })).toThrow(/INVALID_SPIKE/);
81
108
  (0, globals_1.expect)(() => makeSpike({ kind: 'INJURY', severity: 0 })).toThrow(/INVALID_SPIKE/);
@@ -11,6 +11,7 @@ export interface CompactEvent {
11
11
  o?: number;
12
12
  j?: number;
13
13
  i?: number;
14
+ h?: number;
14
15
  }
15
16
  export declare function transformToCompact(evt: RallyEvent, playerIndex: Map<string, number>): CompactEvent;
16
17
  export declare function transformToBlock(event: CompactEvent, roster: string[]): Block;
@@ -15,10 +15,14 @@ function incidentToCompact(incident) {
15
15
  return undefined;
16
16
  return incident.kind === 'KNOCK' ? 10 + incident.severity : incident.severity;
17
17
  }
18
- function incidentFromCompact(i) {
18
+ function incidentFromCompact(i, h, roster) {
19
19
  if (i == null)
20
20
  return undefined;
21
- return i >= 10 ? { kind: 'KNOCK', severity: i - 10 } : { kind: 'INJURY', severity: i };
21
+ const playerId = h != null && roster != null ? roster[h] : undefined;
22
+ return {
23
+ ...(i >= 10 ? { kind: 'KNOCK', severity: i - 10 } : { kind: 'INJURY', severity: i }),
24
+ ...(playerId != null ? { playerId } : {})
25
+ };
22
26
  }
23
27
  function transformToCompact(evt, playerIndex) {
24
28
  const out = {
@@ -31,8 +35,12 @@ function transformToCompact(evt, playerIndex) {
31
35
  out.a = evt.target;
32
36
  out.s = evt.score;
33
37
  const i = incidentToCompact(evt.incident);
34
- if (i != null)
38
+ if (i != null) {
35
39
  out.i = i;
40
+ if (evt.incident?.playerId != null && evt.incident.playerId !== evt.playerId) {
41
+ out.h = playerIndex.get(evt.incident.playerId);
42
+ }
43
+ }
36
44
  if (evt instanceof service_1.Block) {
37
45
  out.b = evt.blockers.map(id => playerIndex.get(id));
38
46
  }
@@ -56,7 +64,7 @@ function transformToBlock(event, roster) {
56
64
  target: event.a,
57
65
  blockers: (event.b ?? []).map(i => roster[i]),
58
66
  score: event.s,
59
- incident: incidentFromCompact(event.i)
67
+ incident: incidentFromCompact(event.i, event.h, roster)
60
68
  });
61
69
  }
62
70
  function transformToLiberoReplacement(event, roster) {
@@ -73,7 +81,7 @@ function transformToReception(event, roster) {
73
81
  playerId: roster[event.p],
74
82
  target: event.a,
75
83
  score: event.s,
76
- incident: incidentFromCompact(event.i)
84
+ incident: incidentFromCompact(event.i, event.h, roster)
77
85
  });
78
86
  }
79
87
  function transformToServe(event, roster) {
@@ -83,7 +91,7 @@ function transformToServe(event, roster) {
83
91
  playerId: roster[event.p],
84
92
  target: event.a,
85
93
  score: event.s,
86
- incident: incidentFromCompact(event.i)
94
+ incident: incidentFromCompact(event.i, event.h, roster)
87
95
  });
88
96
  }
89
97
  function transformToSet(event, roster) {
@@ -93,7 +101,7 @@ function transformToSet(event, roster) {
93
101
  playerId: roster[event.p],
94
102
  target: event.a,
95
103
  score: event.s,
96
- incident: incidentFromCompact(event.i)
104
+ incident: incidentFromCompact(event.i, event.h, roster)
97
105
  });
98
106
  }
99
107
  function transformToSpike(event, roster) {
@@ -103,7 +111,7 @@ function transformToSpike(event, roster) {
103
111
  playerId: roster[event.p],
104
112
  target: event.a,
105
113
  score: event.s,
106
- incident: incidentFromCompact(event.i)
114
+ incident: incidentFromCompact(event.i, event.h, roster)
107
115
  });
108
116
  }
109
117
  function transformToSubstitution(event, roster) {
@@ -4,6 +4,7 @@ import { Trait } from '../player';
4
4
  export interface EventIncident {
5
5
  readonly kind: 'KNOCK' | 'INJURY';
6
6
  readonly severity: number;
7
+ readonly playerId?: string;
7
8
  }
8
9
  export interface InPlayEventOpts extends RallyEventOpts {
9
10
  readonly target: CourtTarget;
@@ -33,6 +33,7 @@ export declare const BlockInputSchema: z.ZodObject<{
33
33
  INJURY: "INJURY";
34
34
  }>;
35
35
  severity: z.ZodNumber;
36
+ playerId: z.ZodOptional<z.ZodUUID>;
36
37
  }, z.core.$strip>>;
37
38
  }, z.core.$strip>;
38
39
  export type BlockInput = z.infer<typeof BlockInputSchema>;
@@ -5,5 +5,6 @@ export declare const EventIncidentSchema: z.ZodObject<{
5
5
  INJURY: "INJURY";
6
6
  }>;
7
7
  severity: z.ZodNumber;
8
+ playerId: z.ZodOptional<z.ZodUUID>;
8
9
  }, z.core.$strip>;
9
10
  export type EventIncidentInput = z.infer<typeof EventIncidentSchema>;
@@ -7,5 +7,8 @@ const zod_1 = require("zod");
7
7
  // InjurySeverityEnum value (1-4).
8
8
  exports.EventIncidentSchema = zod_1.z.object({
9
9
  kind: zod_1.z.enum(['KNOCK', 'INJURY']),
10
- severity: zod_1.z.number().int().min(1).max(4)
10
+ severity: zod_1.z.number().int().min(1).max(4),
11
+ // Who got hurt, ONLY when it is not the event's actor (a secondary blocker on a multi-player block);
12
+ // absent = the event's own playerId.
13
+ playerId: zod_1.z.uuid().optional()
11
14
  }).refine(v => v.kind !== 'KNOCK' || v.severity <= 3, { message: 'INVALID_KNOCK_TIER' });
@@ -32,6 +32,7 @@ export declare const ReceptionInputSchema: z.ZodObject<{
32
32
  INJURY: "INJURY";
33
33
  }>;
34
34
  severity: z.ZodNumber;
35
+ playerId: z.ZodOptional<z.ZodUUID>;
35
36
  }, z.core.$strip>>;
36
37
  }, z.core.$strip>;
37
38
  export type ReceptionInput = z.infer<typeof ReceptionInputSchema>;
@@ -32,6 +32,7 @@ export declare const ServeInputSchema: z.ZodObject<{
32
32
  INJURY: "INJURY";
33
33
  }>;
34
34
  severity: z.ZodNumber;
35
+ playerId: z.ZodOptional<z.ZodUUID>;
35
36
  }, z.core.$strip>>;
36
37
  }, z.core.$strip>;
37
38
  export type ServeInput = z.infer<typeof ServeInputSchema>;
@@ -32,6 +32,7 @@ export declare const SetInputSchema: z.ZodObject<{
32
32
  INJURY: "INJURY";
33
33
  }>;
34
34
  severity: z.ZodNumber;
35
+ playerId: z.ZodOptional<z.ZodUUID>;
35
36
  }, z.core.$strip>>;
36
37
  }, z.core.$strip>;
37
38
  export type SetInput = z.infer<typeof SetInputSchema>;
@@ -32,6 +32,7 @@ export declare const SpikeInputSchema: z.ZodObject<{
32
32
  INJURY: "INJURY";
33
33
  }>;
34
34
  severity: z.ZodNumber;
35
+ playerId: z.ZodOptional<z.ZodUUID>;
35
36
  }, z.core.$strip>>;
36
37
  }, z.core.$strip>;
37
38
  export type SpikeInput = z.infer<typeof SpikeInputSchema>;
@@ -1,8 +1,11 @@
1
1
  import { EnergyBand } from './energy-band';
2
- export type LiberoSubMode = 'NEVER' | 'FATIGUE' | 'ALWAYS';
2
+ import { ConditionLogic, PinchCondition } from './pinch-condition';
3
+ export type LiberoSubMode = 'NEVER' | 'FATIGUE' | 'ALWAYS' | 'CONDITIONS';
3
4
  export interface LiberoSetSubConfig {
4
5
  readonly mode: LiberoSubMode;
5
6
  readonly fatigueBand?: EnergyBand;
7
+ readonly conditions?: PinchCondition[];
8
+ readonly conditionLogic?: ConditionLogic;
6
9
  }
7
10
  export interface LiberoSubConfig extends LiberoSetSubConfig {
8
11
  readonly sets?: LiberoSetSubConfig[];
@@ -6,7 +6,12 @@ export declare enum PinchConditionType {
6
6
  AFTER_ROTATIONS = "AFTER_ROTATIONS",// from the team's Nth serve turn in the set onward
7
7
  FROM_SET = "FROM_SET",// only set N or later
8
8
  MIN_OWN_SCORE = "MIN_OWN_SCORE",// only once own score >= N
9
- MIN_OPPONENT_SCORE = "MIN_OPPONENT_SCORE"
9
+ MIN_OPPONENT_SCORE = "MIN_OPPONENT_SCORE",// only once opponent score >= N
10
+ PHASE = "PHASE"
11
+ }
12
+ export declare enum MatchPhase {
13
+ SERVING = "SERVING",
14
+ RECEIVING = "RECEIVING"
10
15
  }
11
16
  export declare enum OpponentRelation {
12
17
  AHEAD = "AHEAD",
@@ -50,7 +55,11 @@ export interface MinOpponentScoreCondition {
50
55
  readonly type: PinchConditionType.MIN_OPPONENT_SCORE;
51
56
  readonly score: number;
52
57
  }
53
- export type PinchCondition = AsapCondition | TeamSetPointCondition | OpponentSetPointCondition | ScoreDiffCondition | AfterRotationsCondition | FromSetCondition | MinOwnScoreCondition | MinOpponentScoreCondition;
58
+ export interface PhaseCondition {
59
+ readonly type: PinchConditionType.PHASE;
60
+ readonly phase: MatchPhase;
61
+ }
62
+ export type PinchCondition = AsapCondition | TeamSetPointCondition | OpponentSetPointCondition | ScoreDiffCondition | AfterRotationsCondition | FromSetCondition | MinOwnScoreCondition | MinOpponentScoreCondition | PhaseCondition;
54
63
  export type ConditionLogic = 'ALL' | 'ANY';
55
64
  export interface PinchSetState {
56
65
  readonly ownScore: number;
@@ -58,4 +67,5 @@ export interface PinchSetState {
58
67
  readonly setTarget: number;
59
68
  readonly setNumber: number;
60
69
  readonly ownServeTurns: number;
70
+ readonly phase: MatchPhase;
61
71
  }
@@ -3,7 +3,7 @@
3
3
  // wins back the serve, out the moment the serve is lost). Each condition is evaluated against the live set
4
4
  // state; a pinch entry combines them with ALL (every condition must hold) or ANY (one is enough).
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.ScoreDirection = exports.OpponentRelation = exports.PinchConditionType = void 0;
6
+ exports.ScoreDirection = exports.OpponentRelation = exports.MatchPhase = exports.PinchConditionType = void 0;
7
7
  var PinchConditionType;
8
8
  (function (PinchConditionType) {
9
9
  PinchConditionType["ASAP"] = "ASAP";
@@ -13,8 +13,16 @@ var PinchConditionType;
13
13
  PinchConditionType["AFTER_ROTATIONS"] = "AFTER_ROTATIONS";
14
14
  PinchConditionType["FROM_SET"] = "FROM_SET";
15
15
  PinchConditionType["MIN_OWN_SCORE"] = "MIN_OWN_SCORE";
16
- PinchConditionType["MIN_OPPONENT_SCORE"] = "MIN_OPPONENT_SCORE"; // only once opponent score >= N
16
+ PinchConditionType["MIN_OPPONENT_SCORE"] = "MIN_OPPONENT_SCORE";
17
+ PinchConditionType["PHASE"] = "PHASE"; // the team is serving or receiving this rally (used by the libero deployment rule)
17
18
  })(PinchConditionType || (exports.PinchConditionType = PinchConditionType = {}));
19
+ // Which side of the rally the team is on. Known at each dead ball: the serving team is SERVING, the other
20
+ // RECEIVING. Consumed by the PHASE condition (libero deployment); pinch servers always evaluate while SERVING.
21
+ var MatchPhase;
22
+ (function (MatchPhase) {
23
+ MatchPhase["SERVING"] = "SERVING";
24
+ MatchPhase["RECEIVING"] = "RECEIVING";
25
+ })(MatchPhase || (exports.MatchPhase = MatchPhase = {}));
18
26
  var OpponentRelation;
19
27
  (function (OpponentRelation) {
20
28
  OpponentRelation["AHEAD"] = "AHEAD";
@@ -1,7 +1,7 @@
1
1
  import { z } from 'zod';
2
2
  import { Player } from '../../player';
3
3
  import { EnergyBand } from '../energy-band';
4
- import { OpponentRelation, PinchConditionType, ScoreDirection } from '../pinch-condition';
4
+ import { MatchPhase, OpponentRelation, PinchConditionType, ScoreDirection } from '../pinch-condition';
5
5
  export declare const SubBandSchema: z.ZodUnion<readonly [z.ZodEnum<typeof EnergyBand>, z.ZodLiteral<"NEVER">]>;
6
6
  export declare const PinchConditionSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
7
7
  type: z.ZodLiteral<PinchConditionType.ASAP>;
@@ -28,6 +28,9 @@ export declare const PinchConditionSchema: z.ZodDiscriminatedUnion<[z.ZodObject<
28
28
  }, z.core.$strip>, z.ZodObject<{
29
29
  type: z.ZodLiteral<PinchConditionType.MIN_OPPONENT_SCORE>;
30
30
  score: z.ZodNumber;
31
+ }, z.core.$strip>, z.ZodObject<{
32
+ type: z.ZodLiteral<PinchConditionType.PHASE>;
33
+ phase: z.ZodEnum<typeof MatchPhase>;
31
34
  }, z.core.$strip>], "type">;
32
35
  export declare const ConditionLogicSchema: z.ZodUnion<readonly [z.ZodLiteral<"ALL">, z.ZodLiteral<"ANY">]>;
33
36
  export declare const SubModeSchema: z.ZodEnum<{
@@ -69,6 +72,9 @@ export declare const SetSubConfigSchema: z.ZodObject<{
69
72
  }, z.core.$strip>, z.ZodObject<{
70
73
  type: z.ZodLiteral<PinchConditionType.MIN_OPPONENT_SCORE>;
71
74
  score: z.ZodNumber;
75
+ }, z.core.$strip>, z.ZodObject<{
76
+ type: z.ZodLiteral<PinchConditionType.PHASE>;
77
+ phase: z.ZodEnum<typeof MatchPhase>;
72
78
  }, z.core.$strip>], "type">>>;
73
79
  conditionLogic: z.ZodOptional<z.ZodUnion<readonly [z.ZodLiteral<"ALL">, z.ZodLiteral<"ANY">]>>;
74
80
  }, z.core.$strip>;
@@ -107,6 +113,9 @@ export declare const DesignatedSubSchema: z.ZodObject<{
107
113
  }, z.core.$strip>, z.ZodObject<{
108
114
  type: z.ZodLiteral<PinchConditionType.MIN_OPPONENT_SCORE>;
109
115
  score: z.ZodNumber;
116
+ }, z.core.$strip>, z.ZodObject<{
117
+ type: z.ZodLiteral<PinchConditionType.PHASE>;
118
+ phase: z.ZodEnum<typeof MatchPhase>;
110
119
  }, z.core.$strip>], "type">>>;
111
120
  conditionLogic: z.ZodOptional<z.ZodUnion<readonly [z.ZodLiteral<"ALL">, z.ZodLiteral<"ANY">]>>;
112
121
  }, z.core.$strip>>>;
@@ -143,6 +152,9 @@ export declare const DesignatedSubSchema: z.ZodObject<{
143
152
  }, z.core.$strip>, z.ZodObject<{
144
153
  type: z.ZodLiteral<PinchConditionType.MIN_OPPONENT_SCORE>;
145
154
  score: z.ZodNumber;
155
+ }, z.core.$strip>, z.ZodObject<{
156
+ type: z.ZodLiteral<PinchConditionType.PHASE>;
157
+ phase: z.ZodEnum<typeof MatchPhase>;
146
158
  }, z.core.$strip>], "type">>>;
147
159
  conditionLogic: z.ZodOptional<z.ZodUnion<readonly [z.ZodLiteral<"ALL">, z.ZodLiteral<"ANY">]>>;
148
160
  starter: z.ZodCustom<Player, Player>;
@@ -17,7 +17,9 @@ exports.PinchConditionSchema = zod_1.z.discriminatedUnion('type', [
17
17
  zod_1.z.object({ type: zod_1.z.literal(pinch_condition_1.PinchConditionType.AFTER_ROTATIONS), rotations: zod_1.z.number().int().min(1).max(6) }),
18
18
  zod_1.z.object({ type: zod_1.z.literal(pinch_condition_1.PinchConditionType.FROM_SET), set: zod_1.z.number().int().min(1).max(5) }),
19
19
  zod_1.z.object({ type: zod_1.z.literal(pinch_condition_1.PinchConditionType.MIN_OWN_SCORE), score: zod_1.z.number().int().min(0).max(40) }),
20
- zod_1.z.object({ type: zod_1.z.literal(pinch_condition_1.PinchConditionType.MIN_OPPONENT_SCORE), score: zod_1.z.number().int().min(0).max(40) })
20
+ zod_1.z.object({ type: zod_1.z.literal(pinch_condition_1.PinchConditionType.MIN_OPPONENT_SCORE), score: zod_1.z.number().int().min(0).max(40) }),
21
+ // PHASE is used by the libero deployment rule (which reuses this schema); pinch servers never offer it.
22
+ zod_1.z.object({ type: zod_1.z.literal(pinch_condition_1.PinchConditionType.PHASE), phase: zod_1.z.nativeEnum(pinch_condition_1.MatchPhase) })
21
23
  ]);
22
24
  exports.ConditionLogicSchema = zod_1.z.union([zod_1.z.literal('ALL'), zod_1.z.literal('ANY')]);
23
25
  exports.SubModeSchema = zod_1.z.enum(['FATIGUE', 'PINCH', 'NEVER']);
@@ -4,14 +4,46 @@ export declare const LiberoSubModeSchema: z.ZodEnum<{
4
4
  NEVER: "NEVER";
5
5
  FATIGUE: "FATIGUE";
6
6
  ALWAYS: "ALWAYS";
7
+ CONDITIONS: "CONDITIONS";
7
8
  }>;
8
9
  export declare const LiberoSetSubConfigSchema: z.ZodObject<{
9
10
  mode: z.ZodEnum<{
10
11
  NEVER: "NEVER";
11
12
  FATIGUE: "FATIGUE";
12
13
  ALWAYS: "ALWAYS";
14
+ CONDITIONS: "CONDITIONS";
13
15
  }>;
14
16
  fatigueBand: z.ZodOptional<z.ZodEnum<typeof EnergyBand>>;
17
+ conditions: z.ZodOptional<z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
18
+ type: z.ZodLiteral<import("..").PinchConditionType.ASAP>;
19
+ }, z.core.$strip>, z.ZodObject<{
20
+ type: z.ZodLiteral<import("..").PinchConditionType.TEAM_SET_POINT>;
21
+ margin: z.ZodNumber;
22
+ opponent: z.ZodEnum<typeof import("..").OpponentRelation>;
23
+ }, z.core.$strip>, z.ZodObject<{
24
+ type: z.ZodLiteral<import("..").PinchConditionType.OPPONENT_SET_POINT>;
25
+ margin: z.ZodNumber;
26
+ }, z.core.$strip>, z.ZodObject<{
27
+ type: z.ZodLiteral<import("..").PinchConditionType.SCORE_DIFF>;
28
+ direction: z.ZodEnum<typeof import("..").ScoreDirection>;
29
+ points: z.ZodNumber;
30
+ }, z.core.$strip>, z.ZodObject<{
31
+ type: z.ZodLiteral<import("..").PinchConditionType.AFTER_ROTATIONS>;
32
+ rotations: z.ZodNumber;
33
+ }, z.core.$strip>, z.ZodObject<{
34
+ type: z.ZodLiteral<import("..").PinchConditionType.FROM_SET>;
35
+ set: z.ZodNumber;
36
+ }, z.core.$strip>, z.ZodObject<{
37
+ type: z.ZodLiteral<import("..").PinchConditionType.MIN_OWN_SCORE>;
38
+ score: z.ZodNumber;
39
+ }, z.core.$strip>, z.ZodObject<{
40
+ type: z.ZodLiteral<import("..").PinchConditionType.MIN_OPPONENT_SCORE>;
41
+ score: z.ZodNumber;
42
+ }, z.core.$strip>, z.ZodObject<{
43
+ type: z.ZodLiteral<import("..").PinchConditionType.PHASE>;
44
+ phase: z.ZodEnum<typeof import("..").MatchPhase>;
45
+ }, z.core.$strip>], "type">>>;
46
+ conditionLogic: z.ZodOptional<z.ZodUnion<readonly [z.ZodLiteral<"ALL">, z.ZodLiteral<"ANY">]>>;
15
47
  }, z.core.$strip>;
16
48
  export declare const LiberoSubConfigSchema: z.ZodObject<{
17
49
  sets: z.ZodOptional<z.ZodArray<z.ZodObject<{
@@ -19,13 +51,75 @@ export declare const LiberoSubConfigSchema: z.ZodObject<{
19
51
  NEVER: "NEVER";
20
52
  FATIGUE: "FATIGUE";
21
53
  ALWAYS: "ALWAYS";
54
+ CONDITIONS: "CONDITIONS";
22
55
  }>;
23
56
  fatigueBand: z.ZodOptional<z.ZodEnum<typeof EnergyBand>>;
57
+ conditions: z.ZodOptional<z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
58
+ type: z.ZodLiteral<import("..").PinchConditionType.ASAP>;
59
+ }, z.core.$strip>, z.ZodObject<{
60
+ type: z.ZodLiteral<import("..").PinchConditionType.TEAM_SET_POINT>;
61
+ margin: z.ZodNumber;
62
+ opponent: z.ZodEnum<typeof import("..").OpponentRelation>;
63
+ }, z.core.$strip>, z.ZodObject<{
64
+ type: z.ZodLiteral<import("..").PinchConditionType.OPPONENT_SET_POINT>;
65
+ margin: z.ZodNumber;
66
+ }, z.core.$strip>, z.ZodObject<{
67
+ type: z.ZodLiteral<import("..").PinchConditionType.SCORE_DIFF>;
68
+ direction: z.ZodEnum<typeof import("..").ScoreDirection>;
69
+ points: z.ZodNumber;
70
+ }, z.core.$strip>, z.ZodObject<{
71
+ type: z.ZodLiteral<import("..").PinchConditionType.AFTER_ROTATIONS>;
72
+ rotations: z.ZodNumber;
73
+ }, z.core.$strip>, z.ZodObject<{
74
+ type: z.ZodLiteral<import("..").PinchConditionType.FROM_SET>;
75
+ set: z.ZodNumber;
76
+ }, z.core.$strip>, z.ZodObject<{
77
+ type: z.ZodLiteral<import("..").PinchConditionType.MIN_OWN_SCORE>;
78
+ score: z.ZodNumber;
79
+ }, z.core.$strip>, z.ZodObject<{
80
+ type: z.ZodLiteral<import("..").PinchConditionType.MIN_OPPONENT_SCORE>;
81
+ score: z.ZodNumber;
82
+ }, z.core.$strip>, z.ZodObject<{
83
+ type: z.ZodLiteral<import("..").PinchConditionType.PHASE>;
84
+ phase: z.ZodEnum<typeof import("..").MatchPhase>;
85
+ }, z.core.$strip>], "type">>>;
86
+ conditionLogic: z.ZodOptional<z.ZodUnion<readonly [z.ZodLiteral<"ALL">, z.ZodLiteral<"ANY">]>>;
24
87
  }, z.core.$strip>>>;
25
88
  mode: z.ZodEnum<{
26
89
  NEVER: "NEVER";
27
90
  FATIGUE: "FATIGUE";
28
91
  ALWAYS: "ALWAYS";
92
+ CONDITIONS: "CONDITIONS";
29
93
  }>;
30
94
  fatigueBand: z.ZodOptional<z.ZodEnum<typeof EnergyBand>>;
95
+ conditions: z.ZodOptional<z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
96
+ type: z.ZodLiteral<import("..").PinchConditionType.ASAP>;
97
+ }, z.core.$strip>, z.ZodObject<{
98
+ type: z.ZodLiteral<import("..").PinchConditionType.TEAM_SET_POINT>;
99
+ margin: z.ZodNumber;
100
+ opponent: z.ZodEnum<typeof import("..").OpponentRelation>;
101
+ }, z.core.$strip>, z.ZodObject<{
102
+ type: z.ZodLiteral<import("..").PinchConditionType.OPPONENT_SET_POINT>;
103
+ margin: z.ZodNumber;
104
+ }, z.core.$strip>, z.ZodObject<{
105
+ type: z.ZodLiteral<import("..").PinchConditionType.SCORE_DIFF>;
106
+ direction: z.ZodEnum<typeof import("..").ScoreDirection>;
107
+ points: z.ZodNumber;
108
+ }, z.core.$strip>, z.ZodObject<{
109
+ type: z.ZodLiteral<import("..").PinchConditionType.AFTER_ROTATIONS>;
110
+ rotations: z.ZodNumber;
111
+ }, z.core.$strip>, z.ZodObject<{
112
+ type: z.ZodLiteral<import("..").PinchConditionType.FROM_SET>;
113
+ set: z.ZodNumber;
114
+ }, z.core.$strip>, z.ZodObject<{
115
+ type: z.ZodLiteral<import("..").PinchConditionType.MIN_OWN_SCORE>;
116
+ score: z.ZodNumber;
117
+ }, z.core.$strip>, z.ZodObject<{
118
+ type: z.ZodLiteral<import("..").PinchConditionType.MIN_OPPONENT_SCORE>;
119
+ score: z.ZodNumber;
120
+ }, z.core.$strip>, z.ZodObject<{
121
+ type: z.ZodLiteral<import("..").PinchConditionType.PHASE>;
122
+ phase: z.ZodEnum<typeof import("..").MatchPhase>;
123
+ }, z.core.$strip>], "type">>>;
124
+ conditionLogic: z.ZodOptional<z.ZodUnion<readonly [z.ZodLiteral<"ALL">, z.ZodLiteral<"ANY">]>>;
31
125
  }, z.core.$strip>;
@@ -3,18 +3,35 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.LiberoSubConfigSchema = exports.LiberoSetSubConfigSchema = exports.LiberoSubModeSchema = void 0;
4
4
  const zod_1 = require("zod");
5
5
  const energy_band_1 = require("../energy-band");
6
- exports.LiberoSubModeSchema = zod_1.z.enum(['NEVER', 'FATIGUE', 'ALWAYS']);
7
- // Fields shared by the all-sets config and each per-set config. Unlike a designated sub there is no bench or
8
- // pinch condition here (the swapped-in player is always the dedicated `secondLibero`), so there is nothing to
9
- // cross-validate.
6
+ const designated_sub_z_1 = require("./designated-sub.z");
7
+ exports.LiberoSubModeSchema = zod_1.z.enum(['NEVER', 'FATIGUE', 'ALWAYS', 'CONDITIONS']);
8
+ // Fields shared by the all-sets config and each per-set config. The swapped-in player is always the dedicated
9
+ // `secondLibero`, so there is no bench to validate; the CONDITIONS mode reuses the pinch-server condition
10
+ // catalog (PinchConditionSchema, which includes the libero-only PHASE condition) + ALL/ANY logic.
10
11
  const liberoSetSubConfigShape = {
11
12
  mode: exports.LiberoSubModeSchema,
12
13
  // fatigue mode only (absent => the team default `substitutionBand`):
13
- fatigueBand: zod_1.z.nativeEnum(energy_band_1.EnergyBand).optional()
14
+ fatigueBand: zod_1.z.nativeEnum(energy_band_1.EnergyBand).optional(),
15
+ // conditions mode only:
16
+ conditions: zod_1.z.array(designated_sub_z_1.PinchConditionSchema).optional(),
17
+ conditionLogic: designated_sub_z_1.ConditionLogicSchema.optional()
14
18
  };
15
- exports.LiberoSetSubConfigSchema = zod_1.z.object(liberoSetSubConfigShape);
19
+ // CONDITIONS mode needs at least one condition (mirrors the designated-sub pinch refine).
20
+ function refineConditions(data, ctx) {
21
+ if (data.mode !== 'CONDITIONS')
22
+ return;
23
+ if (data.conditions == null || data.conditions.length === 0) {
24
+ ctx.addIssue({ code: 'custom', message: 'LIBERO_CONDITIONS_REQUIRE_A_CONDITION', path: ['conditions'] });
25
+ }
26
+ }
27
+ exports.LiberoSetSubConfigSchema = zod_1.z.object(liberoSetSubConfigShape).superRefine(refineConditions);
16
28
  exports.LiberoSubConfigSchema = zod_1.z.object({
17
29
  ...liberoSetSubConfigShape,
18
30
  // Optional per-set overrides (index 0 = set 1 ... index 4 = set 5); absent => the flat config for all sets.
19
31
  sets: zod_1.z.array(exports.LiberoSetSubConfigSchema).max(5).optional()
32
+ }).superRefine((data, ctx) => {
33
+ // The flat config is the fallback for any set a partial `sets` array doesn't cover, so validate it whenever
34
+ // `sets` is absent or covers fewer than 5 sets (each present per-set config is validated by its own schema).
35
+ if (data.sets == null || data.sets.length < 5)
36
+ refineConditions(data, ctx);
20
37
  });
@@ -3,9 +3,10 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const globals_1 = require("@jest/globals");
4
4
  const libero_sub_z_1 = require("./libero-sub.z");
5
5
  const energy_band_1 = require("../energy-band");
6
+ const pinch_condition_1 = require("../pinch-condition");
6
7
  (0, globals_1.describe)('LiberoSubModeSchema', () => {
7
- (0, globals_1.it)('accepts NEVER, FATIGUE and ALWAYS', () => {
8
- for (const mode of ['NEVER', 'FATIGUE', 'ALWAYS']) {
8
+ (0, globals_1.it)('accepts NEVER, FATIGUE, ALWAYS and CONDITIONS', () => {
9
+ for (const mode of ['NEVER', 'FATIGUE', 'ALWAYS', 'CONDITIONS']) {
9
10
  (0, globals_1.expect)(libero_sub_z_1.LiberoSubModeSchema.safeParse(mode).success).toBe(true);
10
11
  }
11
12
  });
@@ -23,6 +24,36 @@ const energy_band_1 = require("../energy-band");
23
24
  (0, globals_1.it)('rejects an unknown band', () => {
24
25
  (0, globals_1.expect)(libero_sub_z_1.LiberoSetSubConfigSchema.safeParse({ mode: 'FATIGUE', fatigueBand: 'SLEEPY' }).success).toBe(false);
25
26
  });
27
+ (0, globals_1.it)('accepts CONDITIONS with a PHASE condition (serve/receive split)', () => {
28
+ const res = libero_sub_z_1.LiberoSetSubConfigSchema.safeParse({
29
+ mode: 'CONDITIONS',
30
+ conditionLogic: 'ALL',
31
+ conditions: [{ type: pinch_condition_1.PinchConditionType.PHASE, phase: pinch_condition_1.MatchPhase.RECEIVING }]
32
+ });
33
+ (0, globals_1.expect)(res.success).toBe(true);
34
+ });
35
+ (0, globals_1.it)('accepts CONDITIONS mixing a pinch condition and a PHASE condition', () => {
36
+ const res = libero_sub_z_1.LiberoSetSubConfigSchema.safeParse({
37
+ mode: 'CONDITIONS',
38
+ conditionLogic: 'ANY',
39
+ conditions: [
40
+ { type: pinch_condition_1.PinchConditionType.MIN_OWN_SCORE, score: 20 },
41
+ { type: pinch_condition_1.PinchConditionType.PHASE, phase: pinch_condition_1.MatchPhase.SERVING }
42
+ ]
43
+ });
44
+ (0, globals_1.expect)(res.success).toBe(true);
45
+ });
46
+ (0, globals_1.it)('rejects CONDITIONS mode with no conditions', () => {
47
+ (0, globals_1.expect)(libero_sub_z_1.LiberoSetSubConfigSchema.safeParse({ mode: 'CONDITIONS' }).success).toBe(false);
48
+ (0, globals_1.expect)(libero_sub_z_1.LiberoSetSubConfigSchema.safeParse({ mode: 'CONDITIONS', conditions: [] }).success).toBe(false);
49
+ });
50
+ (0, globals_1.it)('rejects an invalid PHASE value', () => {
51
+ const res = libero_sub_z_1.LiberoSetSubConfigSchema.safeParse({
52
+ mode: 'CONDITIONS',
53
+ conditions: [{ type: pinch_condition_1.PinchConditionType.PHASE, phase: 'ATTACKING' }]
54
+ });
55
+ (0, globals_1.expect)(res.success).toBe(false);
56
+ });
26
57
  });
27
58
  (0, globals_1.describe)('LiberoSubConfigSchema', () => {
28
59
  (0, globals_1.it)('accepts a flat config with no per-set overrides', () => {