volleyballsimtypes 0.0.473 → 0.0.475
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.
- package/dist/cjs/src/api/index.d.ts +8 -0
- package/dist/cjs/src/data/models/tactics.d.ts +10 -1
- package/dist/cjs/src/data/models/tactics.js +4 -0
- package/dist/cjs/src/data/transformers/tactics.js +66 -2
- package/dist/cjs/src/data/transformers/tactics.test.js +44 -0
- package/dist/cjs/src/service/team/index.d.ts +1 -0
- package/dist/cjs/src/service/team/index.js +1 -0
- package/dist/cjs/src/service/team/libero-injury.d.ts +6 -0
- package/dist/cjs/src/service/team/libero-injury.js +2 -0
- package/dist/cjs/src/service/team/libero-sub.d.ts +4 -1
- package/dist/cjs/src/service/team/pinch-condition.d.ts +12 -2
- package/dist/cjs/src/service/team/pinch-condition.js +10 -2
- package/dist/cjs/src/service/team/schemas/designated-sub.z.d.ts +13 -1
- package/dist/cjs/src/service/team/schemas/designated-sub.z.js +3 -1
- package/dist/cjs/src/service/team/schemas/libero-injury.z.test.d.ts +1 -0
- package/dist/cjs/src/service/team/schemas/libero-injury.z.test.js +71 -0
- package/dist/cjs/src/service/team/schemas/libero-sub.z.d.ts +94 -0
- package/dist/cjs/src/service/team/schemas/libero-sub.z.js +23 -6
- package/dist/cjs/src/service/team/schemas/libero-sub.z.test.js +33 -2
- package/dist/cjs/src/service/team/schemas/tactics.z.d.ts +73 -0
- package/dist/cjs/src/service/team/schemas/tactics.z.js +41 -1
- package/dist/cjs/src/service/team/schemas/team.z.d.ts +73 -0
- package/dist/cjs/src/service/team/tactics.d.ts +3 -0
- package/dist/cjs/src/service/team/tactics.js +2 -1
- package/dist/esm/src/api/index.d.ts +8 -0
- package/dist/esm/src/data/models/tactics.d.ts +10 -1
- package/dist/esm/src/data/models/tactics.js +4 -0
- package/dist/esm/src/data/transformers/tactics.js +66 -2
- package/dist/esm/src/data/transformers/tactics.test.js +44 -0
- package/dist/esm/src/service/team/index.d.ts +1 -0
- package/dist/esm/src/service/team/index.js +1 -0
- package/dist/esm/src/service/team/libero-injury.d.ts +6 -0
- package/dist/esm/src/service/team/libero-injury.js +1 -0
- package/dist/esm/src/service/team/libero-sub.d.ts +4 -1
- package/dist/esm/src/service/team/pinch-condition.d.ts +12 -2
- package/dist/esm/src/service/team/pinch-condition.js +9 -1
- package/dist/esm/src/service/team/schemas/designated-sub.z.d.ts +13 -1
- package/dist/esm/src/service/team/schemas/designated-sub.z.js +4 -2
- package/dist/esm/src/service/team/schemas/libero-injury.z.test.d.ts +1 -0
- package/dist/esm/src/service/team/schemas/libero-injury.z.test.js +69 -0
- package/dist/esm/src/service/team/schemas/libero-sub.z.d.ts +94 -0
- package/dist/esm/src/service/team/schemas/libero-sub.z.js +23 -6
- package/dist/esm/src/service/team/schemas/libero-sub.z.test.js +33 -2
- package/dist/esm/src/service/team/schemas/tactics.z.d.ts +73 -0
- package/dist/esm/src/service/team/schemas/tactics.z.js +41 -1
- package/dist/esm/src/service/team/schemas/team.z.d.ts +73 -0
- package/dist/esm/src/service/team/tactics.d.ts +3 -0
- package/dist/esm/src/service/team/tactics.js +2 -1
- package/package.json +1 -1
|
@@ -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
|
|
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
|
}
|
|
@@ -10,8 +10,16 @@ export var PinchConditionType;
|
|
|
10
10
|
PinchConditionType["AFTER_ROTATIONS"] = "AFTER_ROTATIONS";
|
|
11
11
|
PinchConditionType["FROM_SET"] = "FROM_SET";
|
|
12
12
|
PinchConditionType["MIN_OWN_SCORE"] = "MIN_OWN_SCORE";
|
|
13
|
-
PinchConditionType["MIN_OPPONENT_SCORE"] = "MIN_OPPONENT_SCORE";
|
|
13
|
+
PinchConditionType["MIN_OPPONENT_SCORE"] = "MIN_OPPONENT_SCORE";
|
|
14
|
+
PinchConditionType["PHASE"] = "PHASE"; // the team is serving or receiving this rally (used by the libero deployment rule)
|
|
14
15
|
})(PinchConditionType || (PinchConditionType = {}));
|
|
16
|
+
// Which side of the rally the team is on. Known at each dead ball: the serving team is SERVING, the other
|
|
17
|
+
// RECEIVING. Consumed by the PHASE condition (libero deployment); pinch servers always evaluate while SERVING.
|
|
18
|
+
export var MatchPhase;
|
|
19
|
+
(function (MatchPhase) {
|
|
20
|
+
MatchPhase["SERVING"] = "SERVING";
|
|
21
|
+
MatchPhase["RECEIVING"] = "RECEIVING";
|
|
22
|
+
})(MatchPhase || (MatchPhase = {}));
|
|
15
23
|
export var OpponentRelation;
|
|
16
24
|
(function (OpponentRelation) {
|
|
17
25
|
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>;
|
|
@@ -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
|
const playerInstanceSchema = z.custom((v) => v instanceof Player, {
|
|
6
6
|
message: 'INVALID_PLAYER_INSTANCE'
|
|
7
7
|
});
|
|
@@ -14,7 +14,9 @@ export const PinchConditionSchema = z.discriminatedUnion('type', [
|
|
|
14
14
|
z.object({ type: z.literal(PinchConditionType.AFTER_ROTATIONS), rotations: z.number().int().min(1).max(6) }),
|
|
15
15
|
z.object({ type: z.literal(PinchConditionType.FROM_SET), set: z.number().int().min(1).max(5) }),
|
|
16
16
|
z.object({ type: z.literal(PinchConditionType.MIN_OWN_SCORE), score: z.number().int().min(0).max(40) }),
|
|
17
|
-
z.object({ type: z.literal(PinchConditionType.MIN_OPPONENT_SCORE), score: z.number().int().min(0).max(40) })
|
|
17
|
+
z.object({ type: z.literal(PinchConditionType.MIN_OPPONENT_SCORE), score: z.number().int().min(0).max(40) }),
|
|
18
|
+
// PHASE is used by the libero deployment rule (which reuses this schema); pinch servers never offer it.
|
|
19
|
+
z.object({ type: z.literal(PinchConditionType.PHASE), phase: z.nativeEnum(MatchPhase) })
|
|
18
20
|
]);
|
|
19
21
|
export const ConditionLogicSchema = z.union([z.literal('ALL'), z.literal('ANY')]);
|
|
20
22
|
export const SubModeSchema = z.enum(['FATIGUE', 'PINCH', 'NEVER']);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { describe, it, expect } from '@jest/globals';
|
|
2
|
+
import { TacticsInputSchema } from './tactics.z';
|
|
3
|
+
import { makeCountry, makePlayer } from '../../test-helpers';
|
|
4
|
+
describe('TacticsInputSchema — liberoInjury (owner 2026-07-06)', () => {
|
|
5
|
+
const country = makeCountry();
|
|
6
|
+
const p1 = makePlayer(country);
|
|
7
|
+
const p2 = makePlayer(country);
|
|
8
|
+
const p3 = makePlayer(country);
|
|
9
|
+
const p4 = makePlayer(country);
|
|
10
|
+
const p5 = makePlayer(country);
|
|
11
|
+
const p6 = makePlayer(country);
|
|
12
|
+
const benchA = makePlayer(country);
|
|
13
|
+
const benchB = makePlayer(country);
|
|
14
|
+
const libero = makePlayer(country);
|
|
15
|
+
const l2 = makePlayer(country); // second libero (a reserve)
|
|
16
|
+
const reserveA = makePlayer(country);
|
|
17
|
+
const reserveB = makePlayer(country);
|
|
18
|
+
function baseInput(overrides = {}) {
|
|
19
|
+
return {
|
|
20
|
+
lineup: { 4: p4, 3: p3, 2: p2, 5: p5, 6: p6, 1: p1, 0: libero, bench: [benchA, benchB] },
|
|
21
|
+
liberoReplacements: [],
|
|
22
|
+
secondLibero: l2,
|
|
23
|
+
...overrides
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
const hasIssue = (res, message) => !res.success && res.error.issues.some(i => i.message === message);
|
|
27
|
+
it('accepts no liberoInjury', () => {
|
|
28
|
+
expect(TacticsInputSchema.safeParse(baseInput()).success).toBe(true);
|
|
29
|
+
});
|
|
30
|
+
it('accepts a reserve as the pre-match libero replacement', () => {
|
|
31
|
+
expect(TacticsInputSchema.safeParse(baseInput({ liberoInjury: { preMatchReplacement: reserveA } })).success).toBe(true);
|
|
32
|
+
});
|
|
33
|
+
it('accepts the L2 as the replacement with a reserve backfill (new L2)', () => {
|
|
34
|
+
expect(TacticsInputSchema.safeParse(baseInput({ liberoInjury: { preMatchReplacement: l2, secondLiberoBackfill: reserveA } })).success).toBe(true);
|
|
35
|
+
});
|
|
36
|
+
it('rejects a starter / the libero / a bench player as the pre-match replacement', () => {
|
|
37
|
+
for (const bad of [p2, libero, benchA]) {
|
|
38
|
+
const res = TacticsInputSchema.safeParse(baseInput({ liberoInjury: { preMatchReplacement: bad } }));
|
|
39
|
+
expect(hasIssue(res, 'LIBERO_INJURY_REPLACEMENT_MUST_BE_RESERVE_OR_L2')).toBe(true);
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
it('rejects a pre-match replacement when there is no starting libero', () => {
|
|
43
|
+
const res = TacticsInputSchema.safeParse(baseInput({
|
|
44
|
+
lineup: { 4: p4, 3: p3, 2: p2, 5: p5, 6: p6, 1: p1, bench: [benchA, benchB] },
|
|
45
|
+
secondLibero: undefined,
|
|
46
|
+
liberoInjury: { preMatchReplacement: reserveA }
|
|
47
|
+
}));
|
|
48
|
+
expect(hasIssue(res, 'LIBERO_INJURY_REQUIRES_LIBERO')).toBe(true);
|
|
49
|
+
});
|
|
50
|
+
it('rejects a backfill when the replacement is not the L2', () => {
|
|
51
|
+
const res = TacticsInputSchema.safeParse(baseInput({ liberoInjury: { preMatchReplacement: reserveA, secondLiberoBackfill: reserveB } }));
|
|
52
|
+
expect(hasIssue(res, 'LIBERO_BACKFILL_REQUIRES_L2_REPLACEMENT')).toBe(true);
|
|
53
|
+
});
|
|
54
|
+
it('rejects a non-reserve backfill (bench player) even when the L2 is the replacement', () => {
|
|
55
|
+
const res = TacticsInputSchema.safeParse(baseInput({ liberoInjury: { preMatchReplacement: l2, secondLiberoBackfill: benchA } }));
|
|
56
|
+
expect(hasIssue(res, 'LIBERO_BACKFILL_MUST_BE_RESERVE')).toBe(true);
|
|
57
|
+
});
|
|
58
|
+
it('accepts a bench or reserve player as the in-match designate', () => {
|
|
59
|
+
for (const ok of [benchA, reserveA]) {
|
|
60
|
+
expect(TacticsInputSchema.safeParse(baseInput({ liberoInjury: { inMatchDesignate: ok } })).success).toBe(true);
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
it('rejects a starter / the libero / the L2 as the in-match designate', () => {
|
|
64
|
+
for (const bad of [p2, libero, l2]) {
|
|
65
|
+
const res = TacticsInputSchema.safeParse(baseInput({ liberoInjury: { inMatchDesignate: bad } }));
|
|
66
|
+
expect(hasIssue(res, 'LIBERO_DESIGNATE_MUST_BE_BENCH_OR_RESERVE')).toBe(true);
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
});
|
|
@@ -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>;
|
|
@@ -1,17 +1,34 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import { EnergyBand } from '../energy-band';
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
//
|
|
6
|
-
//
|
|
3
|
+
import { ConditionLogicSchema, PinchConditionSchema } from './designated-sub.z';
|
|
4
|
+
export const LiberoSubModeSchema = z.enum(['NEVER', 'FATIGUE', 'ALWAYS', 'CONDITIONS']);
|
|
5
|
+
// Fields shared by the all-sets config and each per-set config. The swapped-in player is always the dedicated
|
|
6
|
+
// `secondLibero`, so there is no bench to validate; the CONDITIONS mode reuses the pinch-server condition
|
|
7
|
+
// catalog (PinchConditionSchema, which includes the libero-only PHASE condition) + ALL/ANY logic.
|
|
7
8
|
const liberoSetSubConfigShape = {
|
|
8
9
|
mode: LiberoSubModeSchema,
|
|
9
10
|
// fatigue mode only (absent => the team default `substitutionBand`):
|
|
10
|
-
fatigueBand: z.nativeEnum(EnergyBand).optional()
|
|
11
|
+
fatigueBand: z.nativeEnum(EnergyBand).optional(),
|
|
12
|
+
// conditions mode only:
|
|
13
|
+
conditions: z.array(PinchConditionSchema).optional(),
|
|
14
|
+
conditionLogic: ConditionLogicSchema.optional()
|
|
11
15
|
};
|
|
12
|
-
|
|
16
|
+
// CONDITIONS mode needs at least one condition (mirrors the designated-sub pinch refine).
|
|
17
|
+
function refineConditions(data, ctx) {
|
|
18
|
+
if (data.mode !== 'CONDITIONS')
|
|
19
|
+
return;
|
|
20
|
+
if (data.conditions == null || data.conditions.length === 0) {
|
|
21
|
+
ctx.addIssue({ code: 'custom', message: 'LIBERO_CONDITIONS_REQUIRE_A_CONDITION', path: ['conditions'] });
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
export const LiberoSetSubConfigSchema = z.object(liberoSetSubConfigShape).superRefine(refineConditions);
|
|
13
25
|
export const LiberoSubConfigSchema = z.object({
|
|
14
26
|
...liberoSetSubConfigShape,
|
|
15
27
|
// Optional per-set overrides (index 0 = set 1 ... index 4 = set 5); absent => the flat config for all sets.
|
|
16
28
|
sets: z.array(LiberoSetSubConfigSchema).max(5).optional()
|
|
29
|
+
}).superRefine((data, ctx) => {
|
|
30
|
+
// The flat config is the fallback for any set a partial `sets` array doesn't cover, so validate it whenever
|
|
31
|
+
// `sets` is absent or covers fewer than 5 sets (each present per-set config is validated by its own schema).
|
|
32
|
+
if (data.sets == null || data.sets.length < 5)
|
|
33
|
+
refineConditions(data, ctx);
|
|
17
34
|
});
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { describe, it, expect } from '@jest/globals';
|
|
2
2
|
import { LiberoSetSubConfigSchema, LiberoSubConfigSchema, LiberoSubModeSchema } from './libero-sub.z';
|
|
3
3
|
import { EnergyBand } from '../energy-band';
|
|
4
|
+
import { MatchPhase, PinchConditionType } from '../pinch-condition';
|
|
4
5
|
describe('LiberoSubModeSchema', () => {
|
|
5
|
-
it('accepts NEVER, FATIGUE and
|
|
6
|
-
for (const mode of ['NEVER', 'FATIGUE', 'ALWAYS']) {
|
|
6
|
+
it('accepts NEVER, FATIGUE, ALWAYS and CONDITIONS', () => {
|
|
7
|
+
for (const mode of ['NEVER', 'FATIGUE', 'ALWAYS', 'CONDITIONS']) {
|
|
7
8
|
expect(LiberoSubModeSchema.safeParse(mode).success).toBe(true);
|
|
8
9
|
}
|
|
9
10
|
});
|
|
@@ -21,6 +22,36 @@ describe('LiberoSetSubConfigSchema', () => {
|
|
|
21
22
|
it('rejects an unknown band', () => {
|
|
22
23
|
expect(LiberoSetSubConfigSchema.safeParse({ mode: 'FATIGUE', fatigueBand: 'SLEEPY' }).success).toBe(false);
|
|
23
24
|
});
|
|
25
|
+
it('accepts CONDITIONS with a PHASE condition (serve/receive split)', () => {
|
|
26
|
+
const res = LiberoSetSubConfigSchema.safeParse({
|
|
27
|
+
mode: 'CONDITIONS',
|
|
28
|
+
conditionLogic: 'ALL',
|
|
29
|
+
conditions: [{ type: PinchConditionType.PHASE, phase: MatchPhase.RECEIVING }]
|
|
30
|
+
});
|
|
31
|
+
expect(res.success).toBe(true);
|
|
32
|
+
});
|
|
33
|
+
it('accepts CONDITIONS mixing a pinch condition and a PHASE condition', () => {
|
|
34
|
+
const res = LiberoSetSubConfigSchema.safeParse({
|
|
35
|
+
mode: 'CONDITIONS',
|
|
36
|
+
conditionLogic: 'ANY',
|
|
37
|
+
conditions: [
|
|
38
|
+
{ type: PinchConditionType.MIN_OWN_SCORE, score: 20 },
|
|
39
|
+
{ type: PinchConditionType.PHASE, phase: MatchPhase.SERVING }
|
|
40
|
+
]
|
|
41
|
+
});
|
|
42
|
+
expect(res.success).toBe(true);
|
|
43
|
+
});
|
|
44
|
+
it('rejects CONDITIONS mode with no conditions', () => {
|
|
45
|
+
expect(LiberoSetSubConfigSchema.safeParse({ mode: 'CONDITIONS' }).success).toBe(false);
|
|
46
|
+
expect(LiberoSetSubConfigSchema.safeParse({ mode: 'CONDITIONS', conditions: [] }).success).toBe(false);
|
|
47
|
+
});
|
|
48
|
+
it('rejects an invalid PHASE value', () => {
|
|
49
|
+
const res = LiberoSetSubConfigSchema.safeParse({
|
|
50
|
+
mode: 'CONDITIONS',
|
|
51
|
+
conditions: [{ type: PinchConditionType.PHASE, phase: 'ATTACKING' }]
|
|
52
|
+
});
|
|
53
|
+
expect(res.success).toBe(false);
|
|
54
|
+
});
|
|
24
55
|
});
|
|
25
56
|
describe('LiberoSubConfigSchema', () => {
|
|
26
57
|
it('accepts a flat config with no per-set overrides', () => {
|
|
@@ -50,6 +50,9 @@ export declare const TacticsInputSchema: z.ZodObject<{
|
|
|
50
50
|
}, z.core.$strip>, z.ZodObject<{
|
|
51
51
|
type: z.ZodLiteral<import("..").PinchConditionType.MIN_OPPONENT_SCORE>;
|
|
52
52
|
score: z.ZodNumber;
|
|
53
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
54
|
+
type: z.ZodLiteral<import("..").PinchConditionType.PHASE>;
|
|
55
|
+
phase: z.ZodEnum<typeof import("..").MatchPhase>;
|
|
53
56
|
}, z.core.$strip>], "type">>>;
|
|
54
57
|
conditionLogic: z.ZodOptional<z.ZodUnion<readonly [z.ZodLiteral<"ALL">, z.ZodLiteral<"ANY">]>>;
|
|
55
58
|
}, z.core.$strip>>>;
|
|
@@ -86,6 +89,9 @@ export declare const TacticsInputSchema: z.ZodObject<{
|
|
|
86
89
|
}, z.core.$strip>, z.ZodObject<{
|
|
87
90
|
type: z.ZodLiteral<import("..").PinchConditionType.MIN_OPPONENT_SCORE>;
|
|
88
91
|
score: z.ZodNumber;
|
|
92
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
93
|
+
type: z.ZodLiteral<import("..").PinchConditionType.PHASE>;
|
|
94
|
+
phase: z.ZodEnum<typeof import("..").MatchPhase>;
|
|
89
95
|
}, z.core.$strip>], "type">>>;
|
|
90
96
|
conditionLogic: z.ZodOptional<z.ZodUnion<readonly [z.ZodLiteral<"ALL">, z.ZodLiteral<"ANY">]>>;
|
|
91
97
|
starter: z.ZodCustom<Player, Player>;
|
|
@@ -98,15 +104,77 @@ export declare const TacticsInputSchema: z.ZodObject<{
|
|
|
98
104
|
NEVER: "NEVER";
|
|
99
105
|
FATIGUE: "FATIGUE";
|
|
100
106
|
ALWAYS: "ALWAYS";
|
|
107
|
+
CONDITIONS: "CONDITIONS";
|
|
101
108
|
}>;
|
|
102
109
|
fatigueBand: z.ZodOptional<z.ZodEnum<typeof EnergyBand>>;
|
|
110
|
+
conditions: z.ZodOptional<z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
111
|
+
type: z.ZodLiteral<import("..").PinchConditionType.ASAP>;
|
|
112
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
113
|
+
type: z.ZodLiteral<import("..").PinchConditionType.TEAM_SET_POINT>;
|
|
114
|
+
margin: z.ZodNumber;
|
|
115
|
+
opponent: z.ZodEnum<typeof import("..").OpponentRelation>;
|
|
116
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
117
|
+
type: z.ZodLiteral<import("..").PinchConditionType.OPPONENT_SET_POINT>;
|
|
118
|
+
margin: z.ZodNumber;
|
|
119
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
120
|
+
type: z.ZodLiteral<import("..").PinchConditionType.SCORE_DIFF>;
|
|
121
|
+
direction: z.ZodEnum<typeof import("..").ScoreDirection>;
|
|
122
|
+
points: z.ZodNumber;
|
|
123
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
124
|
+
type: z.ZodLiteral<import("..").PinchConditionType.AFTER_ROTATIONS>;
|
|
125
|
+
rotations: z.ZodNumber;
|
|
126
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
127
|
+
type: z.ZodLiteral<import("..").PinchConditionType.FROM_SET>;
|
|
128
|
+
set: z.ZodNumber;
|
|
129
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
130
|
+
type: z.ZodLiteral<import("..").PinchConditionType.MIN_OWN_SCORE>;
|
|
131
|
+
score: z.ZodNumber;
|
|
132
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
133
|
+
type: z.ZodLiteral<import("..").PinchConditionType.MIN_OPPONENT_SCORE>;
|
|
134
|
+
score: z.ZodNumber;
|
|
135
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
136
|
+
type: z.ZodLiteral<import("..").PinchConditionType.PHASE>;
|
|
137
|
+
phase: z.ZodEnum<typeof import("..").MatchPhase>;
|
|
138
|
+
}, z.core.$strip>], "type">>>;
|
|
139
|
+
conditionLogic: z.ZodOptional<z.ZodUnion<readonly [z.ZodLiteral<"ALL">, z.ZodLiteral<"ANY">]>>;
|
|
103
140
|
}, z.core.$strip>>>;
|
|
104
141
|
mode: z.ZodEnum<{
|
|
105
142
|
NEVER: "NEVER";
|
|
106
143
|
FATIGUE: "FATIGUE";
|
|
107
144
|
ALWAYS: "ALWAYS";
|
|
145
|
+
CONDITIONS: "CONDITIONS";
|
|
108
146
|
}>;
|
|
109
147
|
fatigueBand: z.ZodOptional<z.ZodEnum<typeof EnergyBand>>;
|
|
148
|
+
conditions: z.ZodOptional<z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
149
|
+
type: z.ZodLiteral<import("..").PinchConditionType.ASAP>;
|
|
150
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
151
|
+
type: z.ZodLiteral<import("..").PinchConditionType.TEAM_SET_POINT>;
|
|
152
|
+
margin: z.ZodNumber;
|
|
153
|
+
opponent: z.ZodEnum<typeof import("..").OpponentRelation>;
|
|
154
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
155
|
+
type: z.ZodLiteral<import("..").PinchConditionType.OPPONENT_SET_POINT>;
|
|
156
|
+
margin: z.ZodNumber;
|
|
157
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
158
|
+
type: z.ZodLiteral<import("..").PinchConditionType.SCORE_DIFF>;
|
|
159
|
+
direction: z.ZodEnum<typeof import("..").ScoreDirection>;
|
|
160
|
+
points: z.ZodNumber;
|
|
161
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
162
|
+
type: z.ZodLiteral<import("..").PinchConditionType.AFTER_ROTATIONS>;
|
|
163
|
+
rotations: z.ZodNumber;
|
|
164
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
165
|
+
type: z.ZodLiteral<import("..").PinchConditionType.FROM_SET>;
|
|
166
|
+
set: z.ZodNumber;
|
|
167
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
168
|
+
type: z.ZodLiteral<import("..").PinchConditionType.MIN_OWN_SCORE>;
|
|
169
|
+
score: z.ZodNumber;
|
|
170
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
171
|
+
type: z.ZodLiteral<import("..").PinchConditionType.MIN_OPPONENT_SCORE>;
|
|
172
|
+
score: z.ZodNumber;
|
|
173
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
174
|
+
type: z.ZodLiteral<import("..").PinchConditionType.PHASE>;
|
|
175
|
+
phase: z.ZodEnum<typeof import("..").MatchPhase>;
|
|
176
|
+
}, z.core.$strip>], "type">>>;
|
|
177
|
+
conditionLogic: z.ZodOptional<z.ZodUnion<readonly [z.ZodLiteral<"ALL">, z.ZodLiteral<"ANY">]>>;
|
|
110
178
|
}, z.core.$strip>>;
|
|
111
179
|
liberoReplacementSets: z.ZodOptional<z.ZodArray<z.ZodArray<z.ZodCustom<Player, Player>>>>;
|
|
112
180
|
rotationSystem: z.ZodDefault<z.ZodEnum<typeof RotationSystemEnum>>;
|
|
@@ -129,5 +197,10 @@ export declare const TacticsInputSchema: z.ZodObject<{
|
|
|
129
197
|
replacement: z.ZodCustom<Player, Player>;
|
|
130
198
|
benchBackfill: z.ZodOptional<z.ZodCustom<Player, Player>>;
|
|
131
199
|
}, z.core.$strip>>>;
|
|
200
|
+
liberoInjury: z.ZodOptional<z.ZodObject<{
|
|
201
|
+
preMatchReplacement: z.ZodOptional<z.ZodCustom<Player, Player>>;
|
|
202
|
+
secondLiberoBackfill: z.ZodOptional<z.ZodCustom<Player, Player>>;
|
|
203
|
+
inMatchDesignate: z.ZodOptional<z.ZodCustom<Player, Player>>;
|
|
204
|
+
}, z.core.$strip>>;
|
|
132
205
|
}, z.core.$strip>;
|
|
133
206
|
export type TacticsInput = z.infer<typeof TacticsInputSchema>;
|
|
@@ -87,7 +87,17 @@ export const TacticsInputSchema = z.object({
|
|
|
87
87
|
starter: playerInstanceSchema,
|
|
88
88
|
replacement: playerInstanceSchema,
|
|
89
89
|
benchBackfill: playerInstanceSchema.optional()
|
|
90
|
-
})).default([])
|
|
90
|
+
})).default([]),
|
|
91
|
+
// Libero injuries (owner 2026-07-06). All optional => an unconfigured team behaves exactly as before.
|
|
92
|
+
// preMatchReplacement: the reserve or the L2 that takes an already-injured starting libero's zone at match
|
|
93
|
+
// load; secondLiberoBackfill: the reserve that becomes the new L2 (only when the L2 is the replacement);
|
|
94
|
+
// inMatchDesignate: a bench/reserve player re-designated as a full libero if the sole libero goes down
|
|
95
|
+
// in-match with no L2. Cross-field pool rules are in the superRefine below.
|
|
96
|
+
liberoInjury: z.object({
|
|
97
|
+
preMatchReplacement: playerInstanceSchema.optional(),
|
|
98
|
+
secondLiberoBackfill: playerInstanceSchema.optional(),
|
|
99
|
+
inMatchDesignate: playerInstanceSchema.optional()
|
|
100
|
+
}).optional()
|
|
91
101
|
}).superRefine((data, ctx) => {
|
|
92
102
|
// Cross-field rules for the rotation system. These keep an invalid config from ever reaching the sim (the
|
|
93
103
|
// pinch-server outage was caused by an unvalidated tactics payload).
|
|
@@ -229,4 +239,34 @@ export const TacticsInputSchema = z.object({
|
|
|
229
239
|
seenIncoming.add(ir.benchBackfill.id);
|
|
230
240
|
}
|
|
231
241
|
}
|
|
242
|
+
// Libero injuries (owner 2026-07-06). preMatchReplacement is a RESERVE or the L2 (never a court starter, the
|
|
243
|
+
// libero, or a bench player) and needs a starting libero to replace. secondLiberoBackfill (the new L2) is a
|
|
244
|
+
// RESERVE, valid ONLY when the L2 itself is the replacement. inMatchDesignate is a bench or reserve player NOT
|
|
245
|
+
// on court (never a court starter, the libero, or the L2).
|
|
246
|
+
const liberoInjury = data.liberoInjury;
|
|
247
|
+
if (liberoInjury != null) {
|
|
248
|
+
const preMatch = liberoInjury.preMatchReplacement;
|
|
249
|
+
const backfill = liberoInjury.secondLiberoBackfill;
|
|
250
|
+
const designate = liberoInjury.inMatchDesignate;
|
|
251
|
+
const secondLiberoId = data.secondLibero?.id;
|
|
252
|
+
if (preMatch != null) {
|
|
253
|
+
if (libero == null) {
|
|
254
|
+
ctx.addIssue({ code: 'custom', message: 'LIBERO_INJURY_REQUIRES_LIBERO', path: ['liberoInjury', 'preMatchReplacement'] });
|
|
255
|
+
}
|
|
256
|
+
if (inLineupIds.has(preMatch.id)) {
|
|
257
|
+
ctx.addIssue({ code: 'custom', message: 'LIBERO_INJURY_REPLACEMENT_MUST_BE_RESERVE_OR_L2', path: ['liberoInjury', 'preMatchReplacement'] });
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
if (backfill != null) {
|
|
261
|
+
if (preMatch == null || preMatch.id !== secondLiberoId) {
|
|
262
|
+
ctx.addIssue({ code: 'custom', message: 'LIBERO_BACKFILL_REQUIRES_L2_REPLACEMENT', path: ['liberoInjury', 'secondLiberoBackfill'] });
|
|
263
|
+
}
|
|
264
|
+
if (inLineupIds.has(backfill.id) || backfill.id === secondLiberoId) {
|
|
265
|
+
ctx.addIssue({ code: 'custom', message: 'LIBERO_BACKFILL_MUST_BE_RESERVE', path: ['liberoInjury', 'secondLiberoBackfill'] });
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
if (designate != null && (starterIds.has(designate.id) || designate.id === liberoId || designate.id === secondLiberoId)) {
|
|
269
|
+
ctx.addIssue({ code: 'custom', message: 'LIBERO_DESIGNATE_MUST_BE_BENCH_OR_RESERVE', path: ['liberoInjury', 'inMatchDesignate'] });
|
|
270
|
+
}
|
|
271
|
+
}
|
|
232
272
|
});
|