volleyballsimtypes 0.0.482 → 0.0.483

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 (59) hide show
  1. package/dist/cjs/src/api/index.d.ts +3 -17
  2. package/dist/cjs/src/data/models/tactics.d.ts +7 -17
  3. package/dist/cjs/src/data/transformers/match-preset-override.test.js +10 -1
  4. package/dist/cjs/src/data/transformers/rally-event.d.ts +2 -1
  5. package/dist/cjs/src/data/transformers/rally-event.js +12 -0
  6. package/dist/cjs/src/data/transformers/rally.js +2 -0
  7. package/dist/cjs/src/data/transformers/rally.test.d.ts +1 -0
  8. package/dist/cjs/src/data/transformers/rally.test.js +62 -0
  9. package/dist/cjs/src/data/transformers/tactics.js +26 -54
  10. package/dist/cjs/src/data/transformers/tactics.test.js +16 -11
  11. package/dist/cjs/src/service/team/designated-sub.d.ts +2 -4
  12. package/dist/cjs/src/service/team/index.d.ts +1 -0
  13. package/dist/cjs/src/service/team/index.js +1 -0
  14. package/dist/cjs/src/service/team/libero-sub.d.ts +0 -4
  15. package/dist/cjs/src/service/team/pinch-condition.d.ts +11 -3
  16. package/dist/cjs/src/service/team/pinch-condition.js +9 -4
  17. package/dist/cjs/src/service/team/schemas/designated-sub.z.d.ts +30 -32
  18. package/dist/cjs/src/service/team/schemas/designated-sub.z.js +27 -23
  19. package/dist/cjs/src/service/team/schemas/designated-sub.z.test.js +4 -2
  20. package/dist/cjs/src/service/team/schemas/libero-sub.z.d.ts +15 -34
  21. package/dist/cjs/src/service/team/schemas/libero-sub.z.js +10 -26
  22. package/dist/cjs/src/service/team/schemas/libero-sub.z.test.js +74 -35
  23. package/dist/cjs/src/service/team/schemas/tactics.z.d.ts +28 -36
  24. package/dist/cjs/src/service/team/schemas/tactics.z.js +4 -3
  25. package/dist/cjs/src/service/team/schemas/team.z.d.ts +28 -36
  26. package/dist/cjs/src/service/team/sub-config-convert.d.ts +13 -0
  27. package/dist/cjs/src/service/team/sub-config-convert.js +91 -0
  28. package/dist/cjs/src/service/team/sub-config-convert.test.d.ts +1 -0
  29. package/dist/cjs/src/service/team/sub-config-convert.test.js +84 -0
  30. package/dist/esm/src/api/index.d.ts +3 -17
  31. package/dist/esm/src/data/models/tactics.d.ts +7 -17
  32. package/dist/esm/src/data/transformers/match-preset-override.test.js +10 -1
  33. package/dist/esm/src/data/transformers/rally-event.d.ts +2 -1
  34. package/dist/esm/src/data/transformers/rally-event.js +12 -1
  35. package/dist/esm/src/data/transformers/rally.js +4 -2
  36. package/dist/esm/src/data/transformers/rally.test.d.ts +1 -0
  37. package/dist/esm/src/data/transformers/rally.test.js +60 -0
  38. package/dist/esm/src/data/transformers/tactics.js +27 -55
  39. package/dist/esm/src/data/transformers/tactics.test.js +16 -11
  40. package/dist/esm/src/service/team/designated-sub.d.ts +2 -4
  41. package/dist/esm/src/service/team/index.d.ts +1 -0
  42. package/dist/esm/src/service/team/index.js +1 -0
  43. package/dist/esm/src/service/team/libero-sub.d.ts +0 -4
  44. package/dist/esm/src/service/team/pinch-condition.d.ts +11 -3
  45. package/dist/esm/src/service/team/pinch-condition.js +9 -4
  46. package/dist/esm/src/service/team/schemas/designated-sub.z.d.ts +30 -32
  47. package/dist/esm/src/service/team/schemas/designated-sub.z.js +26 -22
  48. package/dist/esm/src/service/team/schemas/designated-sub.z.test.js +4 -2
  49. package/dist/esm/src/service/team/schemas/libero-sub.z.d.ts +15 -34
  50. package/dist/esm/src/service/team/schemas/libero-sub.z.js +9 -25
  51. package/dist/esm/src/service/team/schemas/libero-sub.z.test.js +75 -36
  52. package/dist/esm/src/service/team/schemas/tactics.z.d.ts +28 -36
  53. package/dist/esm/src/service/team/schemas/tactics.z.js +4 -3
  54. package/dist/esm/src/service/team/schemas/team.z.d.ts +28 -36
  55. package/dist/esm/src/service/team/sub-config-convert.d.ts +13 -0
  56. package/dist/esm/src/service/team/sub-config-convert.js +85 -0
  57. package/dist/esm/src/service/team/sub-config-convert.test.d.ts +1 -0
  58. package/dist/esm/src/service/team/sub-config-convert.test.js +82 -0
  59. package/package.json +1 -1
@@ -15,16 +15,9 @@ export declare const TacticsInputSchema: z.ZodObject<{
15
15
  }, z.core.$strip>;
16
16
  liberoReplacements: z.ZodArray<z.ZodCustom<Player, Player>>;
17
17
  receiveRotationOffset: z.ZodDefault<z.ZodBoolean>;
18
- designatedSubs: z.ZodDefault<z.ZodArray<z.ZodObject<{
19
- sets: z.ZodOptional<z.ZodArray<z.ZodObject<{
20
- mode: z.ZodEnum<{
21
- NEVER: "NEVER";
22
- FATIGUE: "FATIGUE";
23
- PINCH: "PINCH";
24
- }>;
18
+ designatedSubs: z.ZodDefault<z.ZodArray<z.ZodPipe<z.ZodTransform<unknown, unknown>, z.ZodObject<{
19
+ sets: z.ZodOptional<z.ZodArray<z.ZodPipe<z.ZodTransform<unknown, unknown>, z.ZodObject<{
25
20
  bench: z.ZodOptional<z.ZodCustom<Player, Player>>;
26
- fatigueBand: z.ZodOptional<z.ZodEnum<typeof EnergyBand>>;
27
- benchFatigueBand: z.ZodOptional<z.ZodEnum<typeof EnergyBand>>;
28
21
  conditions: z.ZodOptional<z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
29
22
  type: z.ZodLiteral<import("..").PinchConditionType.ASAP>;
30
23
  }, z.core.$strip>, z.ZodObject<{
@@ -56,9 +49,13 @@ export declare const TacticsInputSchema: z.ZodObject<{
56
49
  }, z.core.$strip>, z.ZodObject<{
57
50
  type: z.ZodLiteral<import("..").PinchConditionType.PHASE>;
58
51
  phase: z.ZodEnum<typeof import("..").MatchPhase>;
52
+ }, z.core.$strip>, z.ZodObject<{
53
+ type: z.ZodLiteral<import("..").PinchConditionType.FATIGUE>;
54
+ band: z.ZodOptional<z.ZodEnum<typeof EnergyBand>>;
59
55
  }, z.core.$strip>], "type">>>;
60
56
  conditionLogic: z.ZodOptional<z.ZodUnion<readonly [z.ZodLiteral<"ALL">, z.ZodLiteral<"ANY">]>>;
61
57
  pinchServer: z.ZodOptional<z.ZodBoolean>;
58
+ benchFatigueBand: z.ZodOptional<z.ZodEnum<typeof EnergyBand>>;
62
59
  subBackMode: z.ZodOptional<z.ZodEnum<{
63
60
  FATIGUE: "FATIGUE";
64
61
  CONDITIONS: "CONDITIONS";
@@ -94,17 +91,13 @@ export declare const TacticsInputSchema: z.ZodObject<{
94
91
  }, z.core.$strip>, z.ZodObject<{
95
92
  type: z.ZodLiteral<import("..").PinchConditionType.PHASE>;
96
93
  phase: z.ZodEnum<typeof import("..").MatchPhase>;
94
+ }, z.core.$strip>, z.ZodObject<{
95
+ type: z.ZodLiteral<import("..").PinchConditionType.FATIGUE>;
96
+ band: z.ZodOptional<z.ZodEnum<typeof EnergyBand>>;
97
97
  }, z.core.$strip>], "type">>>;
98
98
  subBackConditionLogic: z.ZodOptional<z.ZodUnion<readonly [z.ZodLiteral<"ALL">, z.ZodLiteral<"ANY">]>>;
99
- }, z.core.$strip>>>;
100
- mode: z.ZodEnum<{
101
- NEVER: "NEVER";
102
- FATIGUE: "FATIGUE";
103
- PINCH: "PINCH";
104
- }>;
99
+ }, z.core.$strip>>>>;
105
100
  bench: z.ZodOptional<z.ZodCustom<Player, Player>>;
106
- fatigueBand: z.ZodOptional<z.ZodEnum<typeof EnergyBand>>;
107
- benchFatigueBand: z.ZodOptional<z.ZodEnum<typeof EnergyBand>>;
108
101
  conditions: z.ZodOptional<z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
109
102
  type: z.ZodLiteral<import("..").PinchConditionType.ASAP>;
110
103
  }, z.core.$strip>, z.ZodObject<{
@@ -136,9 +129,13 @@ export declare const TacticsInputSchema: z.ZodObject<{
136
129
  }, z.core.$strip>, z.ZodObject<{
137
130
  type: z.ZodLiteral<import("..").PinchConditionType.PHASE>;
138
131
  phase: z.ZodEnum<typeof import("..").MatchPhase>;
132
+ }, z.core.$strip>, z.ZodObject<{
133
+ type: z.ZodLiteral<import("..").PinchConditionType.FATIGUE>;
134
+ band: z.ZodOptional<z.ZodEnum<typeof EnergyBand>>;
139
135
  }, z.core.$strip>], "type">>>;
140
136
  conditionLogic: z.ZodOptional<z.ZodUnion<readonly [z.ZodLiteral<"ALL">, z.ZodLiteral<"ANY">]>>;
141
137
  pinchServer: z.ZodOptional<z.ZodBoolean>;
138
+ benchFatigueBand: z.ZodOptional<z.ZodEnum<typeof EnergyBand>>;
142
139
  subBackMode: z.ZodOptional<z.ZodEnum<{
143
140
  FATIGUE: "FATIGUE";
144
141
  CONDITIONS: "CONDITIONS";
@@ -174,21 +171,17 @@ export declare const TacticsInputSchema: z.ZodObject<{
174
171
  }, z.core.$strip>, z.ZodObject<{
175
172
  type: z.ZodLiteral<import("..").PinchConditionType.PHASE>;
176
173
  phase: z.ZodEnum<typeof import("..").MatchPhase>;
174
+ }, z.core.$strip>, z.ZodObject<{
175
+ type: z.ZodLiteral<import("..").PinchConditionType.FATIGUE>;
176
+ band: z.ZodOptional<z.ZodEnum<typeof EnergyBand>>;
177
177
  }, z.core.$strip>], "type">>>;
178
178
  subBackConditionLogic: z.ZodOptional<z.ZodUnion<readonly [z.ZodLiteral<"ALL">, z.ZodLiteral<"ANY">]>>;
179
179
  starter: z.ZodCustom<Player, Player>;
180
- }, z.core.$strip>>>;
180
+ }, z.core.$strip>>>>;
181
181
  substitutionBand: z.ZodDefault<z.ZodUnion<readonly [z.ZodEnum<typeof EnergyBand>, z.ZodLiteral<"NEVER">]>>;
182
182
  secondLibero: z.ZodOptional<z.ZodCustom<Player, Player>>;
183
- liberoSub: z.ZodOptional<z.ZodObject<{
184
- sets: z.ZodOptional<z.ZodArray<z.ZodObject<{
185
- mode: z.ZodEnum<{
186
- NEVER: "NEVER";
187
- FATIGUE: "FATIGUE";
188
- CONDITIONS: "CONDITIONS";
189
- ALWAYS: "ALWAYS";
190
- }>;
191
- fatigueBand: z.ZodOptional<z.ZodEnum<typeof EnergyBand>>;
183
+ liberoSub: z.ZodOptional<z.ZodPipe<z.ZodTransform<unknown, unknown>, z.ZodObject<{
184
+ sets: z.ZodOptional<z.ZodArray<z.ZodPipe<z.ZodTransform<unknown, unknown>, z.ZodObject<{
192
185
  conditions: z.ZodOptional<z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
193
186
  type: z.ZodLiteral<import("..").PinchConditionType.ASAP>;
194
187
  }, z.core.$strip>, z.ZodObject<{
@@ -220,16 +213,12 @@ export declare const TacticsInputSchema: z.ZodObject<{
220
213
  }, z.core.$strip>, z.ZodObject<{
221
214
  type: z.ZodLiteral<import("..").PinchConditionType.PHASE>;
222
215
  phase: z.ZodEnum<typeof import("..").MatchPhase>;
216
+ }, z.core.$strip>, z.ZodObject<{
217
+ type: z.ZodLiteral<import("..").PinchConditionType.FATIGUE>;
218
+ band: z.ZodOptional<z.ZodEnum<typeof EnergyBand>>;
223
219
  }, z.core.$strip>], "type">>>;
224
220
  conditionLogic: z.ZodOptional<z.ZodUnion<readonly [z.ZodLiteral<"ALL">, z.ZodLiteral<"ANY">]>>;
225
- }, z.core.$strip>>>;
226
- mode: z.ZodEnum<{
227
- NEVER: "NEVER";
228
- FATIGUE: "FATIGUE";
229
- CONDITIONS: "CONDITIONS";
230
- ALWAYS: "ALWAYS";
231
- }>;
232
- fatigueBand: z.ZodOptional<z.ZodEnum<typeof EnergyBand>>;
221
+ }, z.core.$strip>>>>;
233
222
  conditions: z.ZodOptional<z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
234
223
  type: z.ZodLiteral<import("..").PinchConditionType.ASAP>;
235
224
  }, z.core.$strip>, z.ZodObject<{
@@ -261,9 +250,12 @@ export declare const TacticsInputSchema: z.ZodObject<{
261
250
  }, z.core.$strip>, z.ZodObject<{
262
251
  type: z.ZodLiteral<import("..").PinchConditionType.PHASE>;
263
252
  phase: z.ZodEnum<typeof import("..").MatchPhase>;
253
+ }, z.core.$strip>, z.ZodObject<{
254
+ type: z.ZodLiteral<import("..").PinchConditionType.FATIGUE>;
255
+ band: z.ZodOptional<z.ZodEnum<typeof EnergyBand>>;
264
256
  }, z.core.$strip>], "type">>>;
265
257
  conditionLogic: z.ZodOptional<z.ZodUnion<readonly [z.ZodLiteral<"ALL">, z.ZodLiteral<"ANY">]>>;
266
- }, z.core.$strip>>;
258
+ }, z.core.$strip>>>;
267
259
  liberoReplacementSets: z.ZodOptional<z.ZodArray<z.ZodArray<z.ZodCustom<Player, Player>>>>;
268
260
  rotationSystem: z.ZodDefault<z.ZodEnum<typeof RotationSystemEnum>>;
269
261
  designatedSetters: z.ZodDefault<z.ZodArray<z.ZodCustom<Player, Player>>>;
@@ -221,10 +221,11 @@ exports.TacticsInputSchema = zod_1.z.object({
221
221
  ...(libero != null ? [libero.id] : []),
222
222
  ...data.lineup.bench.map(p => p.id)
223
223
  ]);
224
- const liberoSubModes = data.liberoSub != null
225
- ? [data.liberoSub.mode, ...(data.liberoSub.sets ?? []).map(s => s.mode)]
224
+ // Unified condition engine: the rule wants a swap when ANY config (flat or per-set) has conditions.
225
+ const liberoConditionSets = data.liberoSub != null
226
+ ? [data.liberoSub.conditions ?? [], ...(data.liberoSub.sets ?? []).map(s => s.conditions ?? [])]
226
227
  : [];
227
- const wantsLiberoSwap = liberoSubModes.some(mode => mode !== 'NEVER');
228
+ const wantsLiberoSwap = liberoConditionSets.some(conditions => conditions.length > 0);
228
229
  if (wantsLiberoSwap && data.secondLibero == null) {
229
230
  ctx.addIssue({ code: 'custom', message: 'LIBERO_SWAP_REQUIRES_SECOND_LIBERO', path: ['secondLibero'] });
230
231
  }
@@ -23,16 +23,9 @@ export declare const TeamInputSchema: z.ZodObject<{
23
23
  }, z.core.$strip>;
24
24
  liberoReplacements: z.ZodArray<z.ZodCustom<Player, Player>>;
25
25
  receiveRotationOffset: z.ZodDefault<z.ZodBoolean>;
26
- designatedSubs: z.ZodDefault<z.ZodArray<z.ZodObject<{
27
- sets: z.ZodOptional<z.ZodArray<z.ZodObject<{
28
- mode: z.ZodEnum<{
29
- NEVER: "NEVER";
30
- FATIGUE: "FATIGUE";
31
- PINCH: "PINCH";
32
- }>;
26
+ designatedSubs: z.ZodDefault<z.ZodArray<z.ZodPipe<z.ZodTransform<unknown, unknown>, z.ZodObject<{
27
+ sets: z.ZodOptional<z.ZodArray<z.ZodPipe<z.ZodTransform<unknown, unknown>, z.ZodObject<{
33
28
  bench: z.ZodOptional<z.ZodCustom<Player, Player>>;
34
- fatigueBand: z.ZodOptional<z.ZodEnum<typeof import("..").EnergyBand>>;
35
- benchFatigueBand: z.ZodOptional<z.ZodEnum<typeof import("..").EnergyBand>>;
36
29
  conditions: z.ZodOptional<z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
37
30
  type: z.ZodLiteral<import("..").PinchConditionType.ASAP>;
38
31
  }, z.core.$strip>, z.ZodObject<{
@@ -64,9 +57,13 @@ export declare const TeamInputSchema: z.ZodObject<{
64
57
  }, z.core.$strip>, z.ZodObject<{
65
58
  type: z.ZodLiteral<import("..").PinchConditionType.PHASE>;
66
59
  phase: z.ZodEnum<typeof import("..").MatchPhase>;
60
+ }, z.core.$strip>, z.ZodObject<{
61
+ type: z.ZodLiteral<import("..").PinchConditionType.FATIGUE>;
62
+ band: z.ZodOptional<z.ZodEnum<typeof import("..").EnergyBand>>;
67
63
  }, z.core.$strip>], "type">>>;
68
64
  conditionLogic: z.ZodOptional<z.ZodUnion<readonly [z.ZodLiteral<"ALL">, z.ZodLiteral<"ANY">]>>;
69
65
  pinchServer: z.ZodOptional<z.ZodBoolean>;
66
+ benchFatigueBand: z.ZodOptional<z.ZodEnum<typeof import("..").EnergyBand>>;
70
67
  subBackMode: z.ZodOptional<z.ZodEnum<{
71
68
  FATIGUE: "FATIGUE";
72
69
  CONDITIONS: "CONDITIONS";
@@ -102,17 +99,13 @@ export declare const TeamInputSchema: z.ZodObject<{
102
99
  }, z.core.$strip>, z.ZodObject<{
103
100
  type: z.ZodLiteral<import("..").PinchConditionType.PHASE>;
104
101
  phase: z.ZodEnum<typeof import("..").MatchPhase>;
102
+ }, z.core.$strip>, z.ZodObject<{
103
+ type: z.ZodLiteral<import("..").PinchConditionType.FATIGUE>;
104
+ band: z.ZodOptional<z.ZodEnum<typeof import("..").EnergyBand>>;
105
105
  }, z.core.$strip>], "type">>>;
106
106
  subBackConditionLogic: z.ZodOptional<z.ZodUnion<readonly [z.ZodLiteral<"ALL">, z.ZodLiteral<"ANY">]>>;
107
- }, z.core.$strip>>>;
108
- mode: z.ZodEnum<{
109
- NEVER: "NEVER";
110
- FATIGUE: "FATIGUE";
111
- PINCH: "PINCH";
112
- }>;
107
+ }, z.core.$strip>>>>;
113
108
  bench: z.ZodOptional<z.ZodCustom<Player, Player>>;
114
- fatigueBand: z.ZodOptional<z.ZodEnum<typeof import("..").EnergyBand>>;
115
- benchFatigueBand: z.ZodOptional<z.ZodEnum<typeof import("..").EnergyBand>>;
116
109
  conditions: z.ZodOptional<z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
117
110
  type: z.ZodLiteral<import("..").PinchConditionType.ASAP>;
118
111
  }, z.core.$strip>, z.ZodObject<{
@@ -144,9 +137,13 @@ export declare const TeamInputSchema: z.ZodObject<{
144
137
  }, z.core.$strip>, z.ZodObject<{
145
138
  type: z.ZodLiteral<import("..").PinchConditionType.PHASE>;
146
139
  phase: z.ZodEnum<typeof import("..").MatchPhase>;
140
+ }, z.core.$strip>, z.ZodObject<{
141
+ type: z.ZodLiteral<import("..").PinchConditionType.FATIGUE>;
142
+ band: z.ZodOptional<z.ZodEnum<typeof import("..").EnergyBand>>;
147
143
  }, z.core.$strip>], "type">>>;
148
144
  conditionLogic: z.ZodOptional<z.ZodUnion<readonly [z.ZodLiteral<"ALL">, z.ZodLiteral<"ANY">]>>;
149
145
  pinchServer: z.ZodOptional<z.ZodBoolean>;
146
+ benchFatigueBand: z.ZodOptional<z.ZodEnum<typeof import("..").EnergyBand>>;
150
147
  subBackMode: z.ZodOptional<z.ZodEnum<{
151
148
  FATIGUE: "FATIGUE";
152
149
  CONDITIONS: "CONDITIONS";
@@ -182,21 +179,17 @@ export declare const TeamInputSchema: z.ZodObject<{
182
179
  }, z.core.$strip>, z.ZodObject<{
183
180
  type: z.ZodLiteral<import("..").PinchConditionType.PHASE>;
184
181
  phase: z.ZodEnum<typeof import("..").MatchPhase>;
182
+ }, z.core.$strip>, z.ZodObject<{
183
+ type: z.ZodLiteral<import("..").PinchConditionType.FATIGUE>;
184
+ band: z.ZodOptional<z.ZodEnum<typeof import("..").EnergyBand>>;
185
185
  }, z.core.$strip>], "type">>>;
186
186
  subBackConditionLogic: z.ZodOptional<z.ZodUnion<readonly [z.ZodLiteral<"ALL">, z.ZodLiteral<"ANY">]>>;
187
187
  starter: z.ZodCustom<Player, Player>;
188
- }, z.core.$strip>>>;
188
+ }, z.core.$strip>>>>;
189
189
  substitutionBand: z.ZodDefault<z.ZodUnion<readonly [z.ZodEnum<typeof import("..").EnergyBand>, z.ZodLiteral<"NEVER">]>>;
190
190
  secondLibero: z.ZodOptional<z.ZodCustom<Player, Player>>;
191
- liberoSub: z.ZodOptional<z.ZodObject<{
192
- sets: z.ZodOptional<z.ZodArray<z.ZodObject<{
193
- mode: z.ZodEnum<{
194
- NEVER: "NEVER";
195
- FATIGUE: "FATIGUE";
196
- CONDITIONS: "CONDITIONS";
197
- ALWAYS: "ALWAYS";
198
- }>;
199
- fatigueBand: z.ZodOptional<z.ZodEnum<typeof import("..").EnergyBand>>;
191
+ liberoSub: z.ZodOptional<z.ZodPipe<z.ZodTransform<unknown, unknown>, z.ZodObject<{
192
+ sets: z.ZodOptional<z.ZodArray<z.ZodPipe<z.ZodTransform<unknown, unknown>, z.ZodObject<{
200
193
  conditions: z.ZodOptional<z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
201
194
  type: z.ZodLiteral<import("..").PinchConditionType.ASAP>;
202
195
  }, z.core.$strip>, z.ZodObject<{
@@ -228,16 +221,12 @@ export declare const TeamInputSchema: z.ZodObject<{
228
221
  }, z.core.$strip>, z.ZodObject<{
229
222
  type: z.ZodLiteral<import("..").PinchConditionType.PHASE>;
230
223
  phase: z.ZodEnum<typeof import("..").MatchPhase>;
224
+ }, z.core.$strip>, z.ZodObject<{
225
+ type: z.ZodLiteral<import("..").PinchConditionType.FATIGUE>;
226
+ band: z.ZodOptional<z.ZodEnum<typeof import("..").EnergyBand>>;
231
227
  }, z.core.$strip>], "type">>>;
232
228
  conditionLogic: z.ZodOptional<z.ZodUnion<readonly [z.ZodLiteral<"ALL">, z.ZodLiteral<"ANY">]>>;
233
- }, z.core.$strip>>>;
234
- mode: z.ZodEnum<{
235
- NEVER: "NEVER";
236
- FATIGUE: "FATIGUE";
237
- CONDITIONS: "CONDITIONS";
238
- ALWAYS: "ALWAYS";
239
- }>;
240
- fatigueBand: z.ZodOptional<z.ZodEnum<typeof import("..").EnergyBand>>;
229
+ }, z.core.$strip>>>>;
241
230
  conditions: z.ZodOptional<z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
242
231
  type: z.ZodLiteral<import("..").PinchConditionType.ASAP>;
243
232
  }, z.core.$strip>, z.ZodObject<{
@@ -269,9 +258,12 @@ export declare const TeamInputSchema: z.ZodObject<{
269
258
  }, z.core.$strip>, z.ZodObject<{
270
259
  type: z.ZodLiteral<import("..").PinchConditionType.PHASE>;
271
260
  phase: z.ZodEnum<typeof import("..").MatchPhase>;
261
+ }, z.core.$strip>, z.ZodObject<{
262
+ type: z.ZodLiteral<import("..").PinchConditionType.FATIGUE>;
263
+ band: z.ZodOptional<z.ZodEnum<typeof import("..").EnergyBand>>;
272
264
  }, z.core.$strip>], "type">>>;
273
265
  conditionLogic: z.ZodOptional<z.ZodUnion<readonly [z.ZodLiteral<"ALL">, z.ZodLiteral<"ANY">]>>;
274
- }, z.core.$strip>>;
266
+ }, z.core.$strip>>>;
275
267
  liberoReplacementSets: z.ZodOptional<z.ZodArray<z.ZodArray<z.ZodCustom<Player, Player>>>>;
276
268
  rotationSystem: z.ZodDefault<z.ZodEnum<typeof import("..").RotationSystemEnum>>;
277
269
  designatedSetters: z.ZodDefault<z.ZodArray<z.ZodCustom<Player, Player>>>;
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Convert ONE legacy set-sub config (flat or per-set entry). Handles BOTH legacy generations:
3
+ * mode-era rows ({mode: NEVER|FATIGUE|PINCH}) and the oldest isPinchServer-era rows (no mode;
4
+ * isPinchServer flag + a 'NEVER' fatigueBand for the old never-sub state). Non-records and
5
+ * already-unified shapes (neither mode nor isPinchServer nor fatigueBand keys) pass through.
6
+ */
7
+ export declare function convertLegacySetSubConfig(raw: unknown): unknown;
8
+ /** Convert a legacy DesignatedSub (flat fields + per-set entries); unified shapes pass through. */
9
+ export declare function convertLegacyDesignatedSub(raw: unknown): unknown;
10
+ /** Convert ONE legacy libero set-sub config; unified shapes pass through. */
11
+ export declare function convertLegacyLiberoSetSubConfig(raw: unknown): unknown;
12
+ /** Convert a legacy LiberoSubConfig (flat fields + per-set entries); unified shapes pass through. */
13
+ export declare function convertLegacyLiberoSubConfig(raw: unknown): unknown;
@@ -0,0 +1,91 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.convertLegacySetSubConfig = convertLegacySetSubConfig;
4
+ exports.convertLegacyDesignatedSub = convertLegacyDesignatedSub;
5
+ exports.convertLegacyLiberoSetSubConfig = convertLegacyLiberoSetSubConfig;
6
+ exports.convertLegacyLiberoSubConfig = convertLegacyLiberoSubConfig;
7
+ const energy_band_1 = require("./energy-band");
8
+ const pinch_condition_1 = require("./pinch-condition");
9
+ function isRecord(v) {
10
+ return typeof v === 'object' && v != null && !Array.isArray(v);
11
+ }
12
+ function fatigueCondition(band) {
13
+ return Object.values(energy_band_1.EnergyBand).includes(band)
14
+ ? { type: pinch_condition_1.PinchConditionType.FATIGUE, band }
15
+ : { type: pinch_condition_1.PinchConditionType.FATIGUE };
16
+ }
17
+ /**
18
+ * Convert ONE legacy set-sub config (flat or per-set entry). Handles BOTH legacy generations:
19
+ * mode-era rows ({mode: NEVER|FATIGUE|PINCH}) and the oldest isPinchServer-era rows (no mode;
20
+ * isPinchServer flag + a 'NEVER' fatigueBand for the old never-sub state). Non-records and
21
+ * already-unified shapes (neither mode nor isPinchServer nor fatigueBand keys) pass through.
22
+ */
23
+ function convertLegacySetSubConfig(raw) {
24
+ if (!isRecord(raw))
25
+ return raw;
26
+ // Derive the mode for the oldest (pre-mode) generation; a unified shape has none of these keys.
27
+ const mode = raw.mode ?? (raw.isPinchServer === true
28
+ ? 'PINCH'
29
+ : raw.fatigueBand === 'NEVER'
30
+ ? 'NEVER'
31
+ : raw.fatigueBand != null || 'isPinchServer' in raw ? 'FATIGUE' : null);
32
+ if (mode == null)
33
+ return raw;
34
+ const { mode: _m, fatigueBand, isPinchServer: _p, ...rest } = raw;
35
+ if (mode === 'NEVER') {
36
+ // Old NEVER dropped every other field; an explicit empty condition list = never substituted.
37
+ return { conditions: [] };
38
+ }
39
+ if (mode === 'FATIGUE') {
40
+ return {
41
+ ...rest,
42
+ conditions: [fatigueCondition(fatigueBand)],
43
+ conditionLogic: 'ALL',
44
+ pinchServer: false,
45
+ subBackMode: rest.subBackMode ?? 'FATIGUE'
46
+ };
47
+ }
48
+ if (mode === 'PINCH') {
49
+ // A legacy PINCH row without the flag is a serve-and-return pinch server (the documented default).
50
+ return { ...rest, pinchServer: rest.pinchServer ?? true };
51
+ }
52
+ // Unknown legacy mode: strip it and let validation judge the rest.
53
+ return rest;
54
+ }
55
+ /** Convert a legacy DesignatedSub (flat fields + per-set entries); unified shapes pass through. */
56
+ function convertLegacyDesignatedSub(raw) {
57
+ if (!isRecord(raw))
58
+ return raw;
59
+ const { starter, sets, ...cfg } = raw;
60
+ const flat = convertLegacySetSubConfig(cfg);
61
+ const out = { ...(isRecord(flat) ? flat : {}), starter };
62
+ if (Array.isArray(sets))
63
+ out.sets = sets.map(convertLegacySetSubConfig);
64
+ return out;
65
+ }
66
+ /** Convert ONE legacy libero set-sub config; unified shapes pass through. */
67
+ function convertLegacyLiberoSetSubConfig(raw) {
68
+ if (!isRecord(raw) || raw.mode == null)
69
+ return raw;
70
+ const { mode, fatigueBand, ...rest } = raw;
71
+ if (mode === 'NEVER')
72
+ return { conditions: [] };
73
+ if (mode === 'ALWAYS')
74
+ return { ...rest, conditions: [{ type: pinch_condition_1.PinchConditionType.ASAP }], conditionLogic: 'ALL' };
75
+ if (mode === 'FATIGUE')
76
+ return { ...rest, conditions: [fatigueCondition(fatigueBand)], conditionLogic: 'ALL' };
77
+ if (mode === 'CONDITIONS')
78
+ return rest;
79
+ return rest;
80
+ }
81
+ /** Convert a legacy LiberoSubConfig (flat fields + per-set entries); unified shapes pass through. */
82
+ function convertLegacyLiberoSubConfig(raw) {
83
+ if (!isRecord(raw))
84
+ return raw;
85
+ const { sets, ...cfg } = raw;
86
+ const flat = convertLegacyLiberoSetSubConfig(cfg);
87
+ const out = { ...(isRecord(flat) ? flat : {}) };
88
+ if (Array.isArray(sets))
89
+ out.sets = sets.map(convertLegacyLiberoSetSubConfig);
90
+ return out;
91
+ }
@@ -0,0 +1,84 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const globals_1 = require("@jest/globals");
4
+ const energy_band_1 = require("./energy-band");
5
+ const pinch_condition_1 = require("./pinch-condition");
6
+ const sub_config_convert_1 = require("./sub-config-convert");
7
+ // The conversion matrix for the unified condition engine: every legacy generation must land on the exact
8
+ // unified shape (these functions are the single conversion authority used by the schema preprocess, the
9
+ // transformers, and the API repo's read path).
10
+ (0, globals_1.describe)('convertLegacySetSubConfig', () => {
11
+ (0, globals_1.it)('passes an already-unified config through untouched', () => {
12
+ const unified = { bench: 'X', conditions: [{ type: pinch_condition_1.PinchConditionType.ASAP }], pinchServer: true };
13
+ (0, globals_1.expect)((0, sub_config_convert_1.convertLegacySetSubConfig)(unified)).toBe(unified);
14
+ });
15
+ (0, globals_1.it)('converts mode NEVER to an explicit empty condition list, dropping every other field', () => {
16
+ (0, globals_1.expect)((0, sub_config_convert_1.convertLegacySetSubConfig)({ mode: 'NEVER', bench: 'X' })).toEqual({ conditions: [] });
17
+ });
18
+ (0, globals_1.it)('converts mode FATIGUE with a band to a FATIGUE condition + FATIGUE sub-back', () => {
19
+ (0, globals_1.expect)((0, sub_config_convert_1.convertLegacySetSubConfig)({ mode: 'FATIGUE', fatigueBand: energy_band_1.EnergyBand.WORN, bench: 'X', benchFatigueBand: energy_band_1.EnergyBand.TIRED })).toEqual({
20
+ bench: 'X',
21
+ benchFatigueBand: energy_band_1.EnergyBand.TIRED,
22
+ conditions: [{ type: pinch_condition_1.PinchConditionType.FATIGUE, band: energy_band_1.EnergyBand.WORN }],
23
+ conditionLogic: 'ALL',
24
+ pinchServer: false,
25
+ subBackMode: 'FATIGUE'
26
+ });
27
+ });
28
+ (0, globals_1.it)('converts mode FATIGUE without a band to a bandless FATIGUE condition (team default)', () => {
29
+ const out = (0, sub_config_convert_1.convertLegacySetSubConfig)({ mode: 'FATIGUE' });
30
+ (0, globals_1.expect)(out.conditions).toEqual([{ type: pinch_condition_1.PinchConditionType.FATIGUE }]);
31
+ });
32
+ (0, globals_1.it)('converts mode PINCH keeping conditions and defaulting a missing pinchServer flag to true', () => {
33
+ (0, globals_1.expect)((0, sub_config_convert_1.convertLegacySetSubConfig)({ mode: 'PINCH', bench: 'X', conditions: [{ type: pinch_condition_1.PinchConditionType.ASAP }] })).toEqual({
34
+ bench: 'X',
35
+ conditions: [{ type: pinch_condition_1.PinchConditionType.ASAP }],
36
+ pinchServer: true
37
+ });
38
+ });
39
+ (0, globals_1.it)('keeps an explicit pinchServer false on a legacy PINCH regular conditional sub', () => {
40
+ const out = (0, sub_config_convert_1.convertLegacySetSubConfig)({ mode: 'PINCH', bench: 'X', conditions: [{ type: pinch_condition_1.PinchConditionType.ASAP }], pinchServer: false, subBackMode: 'CONDITIONS', subBackConditions: [{ type: pinch_condition_1.PinchConditionType.ASAP }] });
41
+ (0, globals_1.expect)(out.pinchServer).toBe(false);
42
+ (0, globals_1.expect)(out.subBackMode).toBe('CONDITIONS');
43
+ });
44
+ (0, globals_1.it)('converts the oldest isPinchServer-era row to a pinch server', () => {
45
+ (0, globals_1.expect)((0, sub_config_convert_1.convertLegacySetSubConfig)({ isPinchServer: true, bench: 'X', conditions: [{ type: pinch_condition_1.PinchConditionType.ASAP }] })).toEqual({
46
+ bench: 'X',
47
+ conditions: [{ type: pinch_condition_1.PinchConditionType.ASAP }],
48
+ pinchServer: true
49
+ });
50
+ });
51
+ (0, globals_1.it)("converts the oldest 'NEVER' fatigueBand row to never (empty conditions)", () => {
52
+ (0, globals_1.expect)((0, sub_config_convert_1.convertLegacySetSubConfig)({ fatigueBand: 'NEVER' })).toEqual({ conditions: [] });
53
+ });
54
+ (0, globals_1.it)('converts the oldest band-only row to a FATIGUE condition', () => {
55
+ const out = (0, sub_config_convert_1.convertLegacySetSubConfig)({ fatigueBand: energy_band_1.EnergyBand.EXHAUSTED });
56
+ (0, globals_1.expect)(out.conditions).toEqual([{ type: pinch_condition_1.PinchConditionType.FATIGUE, band: energy_band_1.EnergyBand.EXHAUSTED }]);
57
+ });
58
+ (0, globals_1.it)('strips an unknown legacy mode and keeps the rest (lenient heal)', () => {
59
+ (0, globals_1.expect)((0, sub_config_convert_1.convertLegacySetSubConfig)({ mode: 'SOMETIMES', bench: 'X' })).toEqual({ bench: 'X' });
60
+ });
61
+ });
62
+ (0, globals_1.describe)('convertLegacyDesignatedSub', () => {
63
+ (0, globals_1.it)('converts the flat fields and every per-set entry, keeping the starter', () => {
64
+ const out = (0, sub_config_convert_1.convertLegacyDesignatedSub)({
65
+ starter: 'S',
66
+ mode: 'FATIGUE',
67
+ fatigueBand: energy_band_1.EnergyBand.TIRED,
68
+ sets: [{ mode: 'NEVER' }, { mode: 'PINCH', bench: 'X', conditions: [{ type: pinch_condition_1.PinchConditionType.ASAP }] }]
69
+ });
70
+ (0, globals_1.expect)(out.starter).toBe('S');
71
+ (0, globals_1.expect)(out.conditions).toEqual([{ type: pinch_condition_1.PinchConditionType.FATIGUE, band: energy_band_1.EnergyBand.TIRED }]);
72
+ (0, globals_1.expect)(out.sets[0]).toEqual({ conditions: [] });
73
+ (0, globals_1.expect)(out.sets[1].pinchServer).toBe(true);
74
+ });
75
+ });
76
+ (0, globals_1.describe)('convertLegacyLiberoSetSubConfig', () => {
77
+ (0, globals_1.it)('maps each legacy libero mode to its unified condition list', () => {
78
+ (0, globals_1.expect)((0, sub_config_convert_1.convertLegacyLiberoSetSubConfig)({ mode: 'NEVER' })).toEqual({ conditions: [] });
79
+ (0, globals_1.expect)((0, sub_config_convert_1.convertLegacyLiberoSetSubConfig)({ mode: 'ALWAYS' })).toEqual({ conditions: [{ type: pinch_condition_1.PinchConditionType.ASAP }], conditionLogic: 'ALL' });
80
+ (0, globals_1.expect)((0, sub_config_convert_1.convertLegacyLiberoSetSubConfig)({ mode: 'FATIGUE', fatigueBand: energy_band_1.EnergyBand.WORN })).toEqual({ conditions: [{ type: pinch_condition_1.PinchConditionType.FATIGUE, band: energy_band_1.EnergyBand.WORN }], conditionLogic: 'ALL' });
81
+ const conditions = [{ type: pinch_condition_1.PinchConditionType.PHASE, phase: 'RECEIVING' }];
82
+ (0, globals_1.expect)((0, sub_config_convert_1.convertLegacyLiberoSetSubConfig)({ mode: 'CONDITIONS', conditions, conditionLogic: 'ANY' })).toEqual({ conditions, conditionLogic: 'ANY' });
83
+ });
84
+ });
@@ -1,4 +1,4 @@
1
- import { BoxScore, DataProps, DeclineStatus, Division as _Division, League as _League, Match as _Match, MatchSet as _MatchSet, Player as _Player, PlayerPosition, Qualifier as _Qualifier, QualifierMatch as _QualifierMatch, Rally as _Rally, RallyEvent as _RallyEvent, PointBreakdown, RallyMetrics, Season as _Season, Standing as _Standing, Team as _Team, Tournament as _Tournament, TournamentMatch as _TournamentMatch, National as _National, NationalMatch as _NationalMatch, Country as _Country, CourtPosition, ConditionLogic, EnergyBand, LiberoSubMode, MatchForfeitType, PerformanceStats, PinchCondition, RoleEnum, RotationSystemEnum, SubBackMode, SubBand, SubMode } from '../service';
1
+ import { BoxScore, DataProps, DeclineStatus, Division as _Division, League as _League, Match as _Match, MatchSet as _MatchSet, Player as _Player, PlayerPosition, Qualifier as _Qualifier, QualifierMatch as _QualifierMatch, Rally as _Rally, RallyEvent as _RallyEvent, PointBreakdown, RallyMetrics, Season as _Season, Standing as _Standing, Team as _Team, Tournament as _Tournament, TournamentMatch as _TournamentMatch, National as _National, NationalMatch as _NationalMatch, Country as _Country, CourtPosition, ConditionLogic, EnergyBand, MatchForfeitType, PerformanceStats, PinchCondition, RoleEnum, RotationSystemEnum, SubBackMode, SubBand } from '../service';
2
2
  export type Rally = DataProps<_Rally> & {
3
3
  homePlayerPosition: PlayerPosition[];
4
4
  awayPlayerPosition: PlayerPosition[];
@@ -81,29 +81,17 @@ export interface StartingLineup {
81
81
  bench: string[];
82
82
  }
83
83
  export interface ApiSetSubConfig {
84
- mode: SubMode;
85
84
  benchId?: string;
86
- fatigueBand?: EnergyBand;
87
- benchFatigueBand?: EnergyBand;
88
85
  conditions?: PinchCondition[];
89
86
  conditionLogic?: ConditionLogic;
90
87
  pinchServer?: boolean;
88
+ benchFatigueBand?: EnergyBand;
91
89
  subBackMode?: SubBackMode;
92
90
  subBackConditions?: PinchCondition[];
93
91
  subBackConditionLogic?: ConditionLogic;
94
92
  }
95
- export interface ApiDesignatedSub {
93
+ export interface ApiDesignatedSub extends ApiSetSubConfig {
96
94
  starterId: string;
97
- benchId?: string;
98
- mode: SubMode;
99
- fatigueBand?: EnergyBand;
100
- benchFatigueBand?: EnergyBand;
101
- conditions?: PinchCondition[];
102
- conditionLogic?: ConditionLogic;
103
- pinchServer?: boolean;
104
- subBackMode?: SubBackMode;
105
- subBackConditions?: PinchCondition[];
106
- subBackConditionLogic?: ConditionLogic;
107
95
  sets?: ApiSetSubConfig[];
108
96
  }
109
97
  export interface ApiOffensivePreference {
@@ -130,8 +118,6 @@ export interface ApiLiberoInjury {
130
118
  inMatchDesignateId?: string;
131
119
  }
132
120
  export interface ApiLiberoSetSubConfig {
133
- mode: LiberoSubMode;
134
- fatigueBand?: EnergyBand;
135
121
  conditions?: PinchCondition[];
136
122
  conditionLogic?: ConditionLogic;
137
123
  }
@@ -1,7 +1,7 @@
1
1
  import * as Sequelize from 'sequelize';
2
2
  import { Model, Optional } from 'sequelize';
3
3
  import { PlayerId, TeamId, TeamModel } from '.';
4
- import { ConditionLogic, CourtPosition, EnergyBand, LiberoSubMode, PinchCondition, RoleEnum, RotationSystemEnum, SubBackMode, SubBand, SubMode } from '../../service';
4
+ import { ConditionLogic, CourtPosition, EnergyBand, PinchCondition, RoleEnum, RotationSystemEnum, SubBackMode, SubBand, SubMode } from '../../service';
5
5
  export interface TacticsLineupAttributes {
6
6
  [CourtPosition.LIBERO_ZONE]?: PlayerId;
7
7
  [CourtPosition.LEFT_BACK]: PlayerId;
@@ -13,30 +13,20 @@ export interface TacticsLineupAttributes {
13
13
  bench: PlayerId[];
14
14
  }
15
15
  export interface SetSubConfigAttributes {
16
- mode: SubMode;
17
16
  benchId?: PlayerId;
18
- fatigueBand?: EnergyBand;
19
- benchFatigueBand?: EnergyBand;
20
17
  conditions?: PinchCondition[];
21
18
  conditionLogic?: ConditionLogic;
22
19
  pinchServer?: boolean;
20
+ benchFatigueBand?: EnergyBand;
23
21
  subBackMode?: SubBackMode;
24
22
  subBackConditions?: PinchCondition[];
25
23
  subBackConditionLogic?: ConditionLogic;
24
+ mode?: SubMode;
25
+ fatigueBand?: SubBand;
26
26
  }
27
- export interface DesignatedSubAttributes {
27
+ export interface DesignatedSubAttributes extends SetSubConfigAttributes {
28
28
  starterId: PlayerId;
29
- benchId?: PlayerId;
30
- mode?: SubMode;
31
29
  isPinchServer?: boolean;
32
- fatigueBand?: SubBand;
33
- benchFatigueBand?: EnergyBand;
34
- conditions?: PinchCondition[];
35
- conditionLogic?: ConditionLogic;
36
- pinchServer?: boolean;
37
- subBackMode?: SubBackMode;
38
- subBackConditions?: PinchCondition[];
39
- subBackConditionLogic?: ConditionLogic;
40
30
  sets?: SetSubConfigAttributes[];
41
31
  }
42
32
  export interface OffensivePreferenceAttributes {
@@ -63,10 +53,10 @@ export interface LiberoInjuryAttributes {
63
53
  inMatchDesignateId?: PlayerId;
64
54
  }
65
55
  export interface LiberoSetSubConfigAttributes {
66
- mode: LiberoSubMode;
67
- fatigueBand?: EnergyBand;
68
56
  conditions?: PinchCondition[];
69
57
  conditionLogic?: ConditionLogic;
58
+ mode?: string;
59
+ fatigueBand?: EnergyBand;
70
60
  }
71
61
  export interface LiberoSubConfigAttributes extends LiberoSetSubConfigAttributes {
72
62
  sets?: LiberoSetSubConfigAttributes[];
@@ -148,7 +148,16 @@ describe('apiTacticsConfigToTacticsAttributes()', () => {
148
148
  expect(back.lineup).toEqual(full.lineup);
149
149
  expect(back.libero_replacements).toEqual(full.libero_replacements);
150
150
  expect(back.receive_rotation_offset).toBe(true);
151
- expect(back.designated_subs).toEqual(full.designated_subs);
151
+ // The dto path converts legacy mode-shaped subs to the unified condition engine, so the round-trip lands
152
+ // in the converted (unified) shape rather than the legacy input.
153
+ expect(back.designated_subs).toEqual([{
154
+ starterId: ids[0],
155
+ conditions: [{ type: 'FATIGUE', band: 'WORN' }],
156
+ conditionLogic: 'ALL',
157
+ pinchServer: false,
158
+ subBackMode: 'FATIGUE',
159
+ sets: [{ conditions: [] }]
160
+ }]);
152
161
  expect(back.libero_replacement_sets).toEqual(full.libero_replacement_sets);
153
162
  expect(back.rotation_system).toBe('5-1');
154
163
  expect(back.designated_setters).toEqual(full.designated_setters);
@@ -1,4 +1,4 @@
1
- import { Block, LiberoReplacement, RallyEvent, Reception, Serve, Set, Spike, Substitution } from '../../service';
1
+ import { Block, FreeBall, LiberoReplacement, RallyEvent, Reception, Serve, Set, Spike, Substitution } from '../../service';
2
2
  export interface CompactEvent {
3
3
  p: number;
4
4
  e: number;
@@ -21,4 +21,5 @@ export declare function transformToReception(event: CompactEvent, roster: string
21
21
  export declare function transformToServe(event: CompactEvent, roster: string[]): Serve;
22
22
  export declare function transformToSet(event: CompactEvent, roster: string[]): Set;
23
23
  export declare function transformToSpike(event: CompactEvent, roster: string[]): Spike;
24
+ export declare function transformToFreeBall(event: CompactEvent, roster: string[]): FreeBall;
24
25
  export declare function transformToSubstitution(event: CompactEvent, roster: string[]): Substitution;
@@ -1,4 +1,4 @@
1
- import { Block, InPlayEvent, LiberoReplacement, Reception, Serve, Set, Spike, Substitution } from '../../service';
1
+ import { Block, FreeBall, InPlayEvent, LiberoReplacement, Reception, Serve, Set, Spike, Substitution } from '../../service';
2
2
  // Incident packing for the compact key 'i': values below 10 are injury severities, 10+ are knock tiers.
3
3
  function incidentToCompact(incident) {
4
4
  if (incident == null)
@@ -108,6 +108,17 @@ export function transformToSpike(event, roster) {
108
108
  incident: incidentFromCompact(event.i, event.h, roster)
109
109
  });
110
110
  }
111
+ // FreeBall (Last Resort save) is a plain InPlayEvent with no incident of its own, so it decodes from f/t/a/s
112
+ // exactly like a spike minus the incident. Must stay in lockstep with the case 7 branch in rally.ts.
113
+ export function transformToFreeBall(event, roster) {
114
+ return FreeBall.create({
115
+ failure: event.f,
116
+ type: event.t,
117
+ playerId: roster[event.p],
118
+ target: event.a,
119
+ score: event.s
120
+ });
121
+ }
111
122
  export function transformToSubstitution(event, roster) {
112
123
  return Substitution.create({
113
124
  playerId: roster[event.p],