@battle-racing/br-common-lib 6.0.0-rc.0 → 6.1.0-rc.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/RELEASING.md +111 -0
- package/dist/domains/hardware/KartHardwareState.schema.d.ts +4 -3
- package/dist/domains/hardware/ModuleStatus.schema.d.ts +4 -3
- package/dist/domains/hardware/screen/ScreenDebug.schema.d.ts +4 -3
- package/dist/domains/hardware/steering-wheel/SteeringWheel.const.d.ts +18 -7
- package/dist/domains/hardware/steering-wheel/SteeringWheel.const.js +18 -7
- package/dist/domains/hardware/steering-wheel/SteeringWheel.schema.d.ts +36 -10
- package/dist/domains/hardware/steering-wheel/SteeringWheel.schema.js +3 -0
- package/dist/domains/kart/Kart.schema.d.ts +4 -3
- package/package.json +1 -1
- package/src/domains/hardware/steering-wheel/SteeringWheel.const.ts +18 -7
- package/src/domains/hardware/steering-wheel/SteeringWheel.schema.ts +3 -0
package/RELEASING.md
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# Publicar versiones de `br-common-lib`
|
|
2
|
+
|
|
3
|
+
Esta librería la consumen cuatro repositorios (`game-manager-backend`, `kart-manager`,
|
|
4
|
+
`webapp-battle-racing`, `screen-web-RPI`). Un cambio incompatible los rompe a todos, así que
|
|
5
|
+
todo cambio se prueba primero como **release candidate (RC)** y solo se publica como versión
|
|
6
|
+
oficial cuando ya se validó el cambio.
|
|
7
|
+
|
|
8
|
+
Hay dos roles:
|
|
9
|
+
|
|
10
|
+
| Rol | Qué publica | Cómo |
|
|
11
|
+
| :---------------------- | :----------------- | :----------------------------------- |
|
|
12
|
+
| Cualquier desarrollador | RCs (`X.Y.Z-rc.N`) | GitHub Actions → **Publish RC** |
|
|
13
|
+
| Mantenedor | Versión oficial | `npm run release:*` desde su máquina |
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## Elegir el bump: la tabla que evita el error
|
|
18
|
+
|
|
19
|
+
Esta es la parte donde se equivoca todo el mundo. La pregunta no es "qué tan grande es mi
|
|
20
|
+
cambio", sino **"¿es la primera RC de este cambio, o ya venía iterando?"**
|
|
21
|
+
|
|
22
|
+
| Situación | Bump | Ejemplo desde `6.0.0-rc.0` |
|
|
23
|
+
| :------------------------------------------------------- | :----------- | :------------------------- |
|
|
24
|
+
| **Primera** RC de un cambio incompatible | `premajor` | `7.0.0-rc.0` |
|
|
25
|
+
| **Primera** RC de una funcionalidad retrocompatible | `preminor` | `6.1.0-rc.0` |
|
|
26
|
+
| **Primera** RC de un arreglo menor | `prepatch` | `6.0.1-rc.0` |
|
|
27
|
+
| **Otra** RC del mismo cambio (corregir algo, reintentar) | `prerelease` | `6.0.0-rc.1` |
|
|
28
|
+
|
|
29
|
+
> **La regla:** `premajor` / `preminor` / `prepatch` se usan **una sola vez por cambio**.
|
|
30
|
+
> A partir de ahí, siempre `prerelease`.
|
|
31
|
+
|
|
32
|
+
Si te equivocas y usas `premajor` dos veces, terminas con una versión mayor que nunca existió
|
|
33
|
+
como release (`5.0.0` saltando a `6.0.0`). El error que cometió el bruto de JD **No es grave y no hay que arreglarlo:** `latest` no
|
|
34
|
+
se movió y nadie consumió esa versión. Sigue con `prerelease` desde donde quedaste. Las
|
|
35
|
+
versiones publicadas en npm no se pueden reutilizar ni borrar, así que no intentes "volver atrás".
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## Publicar una RC
|
|
40
|
+
|
|
41
|
+
0. Has push de tu rama.
|
|
42
|
+
1. Repositorio → pestaña **Actions**
|
|
43
|
+
2. En la barra izquierda: **Publish RC**
|
|
44
|
+
3. **Run workflow**
|
|
45
|
+
4. **Branch:** la rama que estás probando
|
|
46
|
+
5. **Version bump:** según la tabla de arriba
|
|
47
|
+
6. **Run workflow**
|
|
48
|
+
|
|
49
|
+
Al terminar, abrí el run → **Summary**. Ahí está la línea lista para pegar en el consumidor.
|
|
50
|
+
|
|
51
|
+
### Probarla en un consumidor
|
|
52
|
+
|
|
53
|
+
Versión **exacta**, sin `^`:
|
|
54
|
+
|
|
55
|
+
```json
|
|
56
|
+
"@battle-racing/br-common-lib": "6.0.0-rc.1"
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
El caret con prereleases se comporta de forma poco intuitiva, y sobre hardware querés saber
|
|
60
|
+
exactamente qué build está corriendo.
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
npm install @battle-racing/br-common-lib@6.0.0-rc.1
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### Verificar que salió bien
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
npm view @battle-racing/br-common-lib dist-tags
|
|
70
|
+
# esperado: { latest: '<la oficial>', rc: '6.0.0-rc.1' }
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Si `latest` se movió, algo está mal en el workflow. Pará y avisá antes de seguir.
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
## Publicar la versión oficial
|
|
78
|
+
|
|
79
|
+
Solo el mantenedor, y solo cuando la RC ya se validó en pista.
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
git checkout main && git pull
|
|
83
|
+
npm run release:major # o release:minor / release:patch
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Desde `6.0.0-rc.3`, `release:major` publica `6.0.0` — quita el sufijo de RC, no salta a `7.0.0`.
|
|
87
|
+
|
|
88
|
+
Después, repinear los consumidores a la versión final y quitar el pin exacto de la RC.
|
|
89
|
+
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
## Qué protege a `latest`
|
|
93
|
+
|
|
94
|
+
- El workflow tiene `--tag rc` **hardcodeado**, sin input. Desde Actions no se puede mover `latest`.
|
|
95
|
+
- Los rangos de semver nunca resuelven a prereleases salvo que se pidan explícitamente: un
|
|
96
|
+
consumidor en `^4.9.1` no va a agarrar una `6.0.0-rc.0` ni por accidente.
|
|
97
|
+
- La publicación usa **trusted publishing (OIDC)**: no hay token de npm en ningún lado.
|
|
98
|
+
|
|
99
|
+
**Límite conocido:** al despachar el workflow se ejecuta la copia del `.yml` **de la rama
|
|
100
|
+
seleccionada**. Quien tenga permiso de escritura puede editar ese archivo en su rama y quitar
|
|
101
|
+
el `--tag rc`. La protección real contra eso es un Environment con revisor obligatorio, que
|
|
102
|
+
requiere repo público o plan de pago.
|
|
103
|
+
|
|
104
|
+
---
|
|
105
|
+
|
|
106
|
+
## Notas
|
|
107
|
+
|
|
108
|
+
- El commit y el tag de versión quedan en la rama desde la que publicaste. Al mergear a `main`
|
|
109
|
+
puede haber conflicto en el `package.json` si `main` avanzó por otro lado.
|
|
110
|
+
- Las RC publicadas son públicas y permanentes (se pueden deprecar, pero solo desaparecen si se
|
|
111
|
+
despublican dentro de las primeras 72 horas).
|
|
@@ -52,10 +52,11 @@ export declare const kartHardwareStateSchema: z.ZodObject<{
|
|
|
52
52
|
steeringWheel: z.ZodNullable<z.ZodObject<{
|
|
53
53
|
ledStatus: z.ZodEnum<{
|
|
54
54
|
readonly OFF: "Off";
|
|
55
|
-
readonly
|
|
56
|
-
readonly
|
|
57
|
-
readonly EMERGENCY: "Emergency";
|
|
55
|
+
readonly COLOR: "Color";
|
|
56
|
+
readonly EFFECT: "Effect";
|
|
58
57
|
readonly CONFIG: "Config";
|
|
58
|
+
readonly ERROR: "Error";
|
|
59
|
+
readonly UNKNOWN: "Unknown";
|
|
59
60
|
}>;
|
|
60
61
|
buttons: z.ZodObject<{
|
|
61
62
|
powerRightPressed: z.ZodBoolean;
|
|
@@ -109,10 +109,11 @@ export declare const moduleStatusSchema: z.ZodUnion<readonly [z.ZodObject<{
|
|
|
109
109
|
}, z.core.$strip>, z.ZodObject<{
|
|
110
110
|
ledStatus: z.ZodEnum<{
|
|
111
111
|
readonly OFF: "Off";
|
|
112
|
-
readonly
|
|
113
|
-
readonly
|
|
114
|
-
readonly EMERGENCY: "Emergency";
|
|
112
|
+
readonly COLOR: "Color";
|
|
113
|
+
readonly EFFECT: "Effect";
|
|
115
114
|
readonly CONFIG: "Config";
|
|
115
|
+
readonly ERROR: "Error";
|
|
116
|
+
readonly UNKNOWN: "Unknown";
|
|
116
117
|
}>;
|
|
117
118
|
buttons: z.ZodObject<{
|
|
118
119
|
powerRightPressed: z.ZodBoolean;
|
|
@@ -52,10 +52,11 @@ export declare const screenDebugViewInputSchema: z.ZodObject<{
|
|
|
52
52
|
steeringWheel: z.ZodNullable<z.ZodObject<{
|
|
53
53
|
ledStatus: z.ZodEnum<{
|
|
54
54
|
readonly OFF: "Off";
|
|
55
|
-
readonly
|
|
56
|
-
readonly
|
|
57
|
-
readonly EMERGENCY: "Emergency";
|
|
55
|
+
readonly COLOR: "Color";
|
|
56
|
+
readonly EFFECT: "Effect";
|
|
58
57
|
readonly CONFIG: "Config";
|
|
58
|
+
readonly ERROR: "Error";
|
|
59
|
+
readonly UNKNOWN: "Unknown";
|
|
59
60
|
}>;
|
|
60
61
|
buttons: z.ZodObject<{
|
|
61
62
|
powerRightPressed: z.ZodBoolean;
|
|
@@ -6,15 +6,26 @@ export declare const STEERING_WHEEL_ACTION_COMMAND: {
|
|
|
6
6
|
readonly CONFIGURE_LEDS: "ConfigureLeds";
|
|
7
7
|
};
|
|
8
8
|
export declare const STEERING_WHEEL_SHOW_LED_EFFECT_OPTIONS: {
|
|
9
|
-
readonly
|
|
10
|
-
readonly
|
|
11
|
-
readonly
|
|
12
|
-
readonly
|
|
9
|
+
readonly SHOW_COLOR: "ShowColor";
|
|
10
|
+
readonly STAR: "Star";
|
|
11
|
+
readonly RAY: "Ray";
|
|
12
|
+
readonly RAY_HIT: "RayHit";
|
|
13
|
+
readonly SPEED_BOOST: "SpeedBoost";
|
|
14
|
+
readonly CHARGED_POWER: "ChargedPower";
|
|
15
|
+
readonly ROULETTE: "Roulette";
|
|
16
|
+
readonly SHIELD: "Shield";
|
|
17
|
+
readonly BROKEN_SHIELD: "BrokenShield";
|
|
18
|
+
readonly WAIT: "Wait";
|
|
19
|
+
readonly STANDARD: "Standard";
|
|
20
|
+
readonly POWER_UP: "PowerUp";
|
|
21
|
+
readonly EMERGENCY: "Emergency";
|
|
22
|
+
readonly TRAFFIC_LIGHT: "TrafficLight";
|
|
13
23
|
};
|
|
14
24
|
export declare const STEERING_WHEEL_LED_STATUS: {
|
|
15
25
|
readonly OFF: "Off";
|
|
16
|
-
readonly
|
|
17
|
-
readonly
|
|
18
|
-
readonly EMERGENCY: "Emergency";
|
|
26
|
+
readonly COLOR: "Color";
|
|
27
|
+
readonly EFFECT: "Effect";
|
|
19
28
|
readonly CONFIG: "Config";
|
|
29
|
+
readonly ERROR: "Error";
|
|
30
|
+
readonly UNKNOWN: "Unknown";
|
|
20
31
|
};
|
|
@@ -9,15 +9,26 @@ exports.STEERING_WHEEL_ACTION_COMMAND = {
|
|
|
9
9
|
CONFIGURE_LEDS: 'ConfigureLeds',
|
|
10
10
|
};
|
|
11
11
|
exports.STEERING_WHEEL_SHOW_LED_EFFECT_OPTIONS = {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
12
|
+
SHOW_COLOR: 'ShowColor',
|
|
13
|
+
STAR: 'Star',
|
|
14
|
+
RAY: 'Ray',
|
|
15
|
+
RAY_HIT: 'RayHit',
|
|
16
|
+
SPEED_BOOST: 'SpeedBoost',
|
|
17
|
+
CHARGED_POWER: 'ChargedPower',
|
|
18
|
+
ROULETTE: 'Roulette',
|
|
19
|
+
SHIELD: 'Shield',
|
|
20
|
+
BROKEN_SHIELD: 'BrokenShield',
|
|
21
|
+
WAIT: 'Wait',
|
|
22
|
+
STANDARD: 'Standard',
|
|
23
|
+
POWER_UP: 'PowerUp',
|
|
24
|
+
EMERGENCY: 'Emergency',
|
|
25
|
+
TRAFFIC_LIGHT: 'TrafficLight',
|
|
16
26
|
};
|
|
17
27
|
exports.STEERING_WHEEL_LED_STATUS = {
|
|
18
28
|
OFF: 'Off',
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
EMERGENCY: 'Emergency',
|
|
29
|
+
COLOR: 'Color',
|
|
30
|
+
EFFECT: 'Effect',
|
|
22
31
|
CONFIG: 'Config',
|
|
32
|
+
ERROR: 'Error',
|
|
33
|
+
UNKNOWN: 'Unknown',
|
|
23
34
|
};
|
|
@@ -7,18 +7,20 @@ export declare const steeringWheelActionCommandSchema: z.ZodEnum<{
|
|
|
7
7
|
}>;
|
|
8
8
|
export declare const steeringWheelLedStatusSchema: z.ZodEnum<{
|
|
9
9
|
readonly OFF: "Off";
|
|
10
|
-
readonly
|
|
11
|
-
readonly
|
|
12
|
-
readonly EMERGENCY: "Emergency";
|
|
10
|
+
readonly COLOR: "Color";
|
|
11
|
+
readonly EFFECT: "Effect";
|
|
13
12
|
readonly CONFIG: "Config";
|
|
13
|
+
readonly ERROR: "Error";
|
|
14
|
+
readonly UNKNOWN: "Unknown";
|
|
14
15
|
}>;
|
|
15
16
|
export declare const steeringWheelModuleStatusSchema: z.ZodObject<{
|
|
16
17
|
ledStatus: z.ZodEnum<{
|
|
17
18
|
readonly OFF: "Off";
|
|
18
|
-
readonly
|
|
19
|
-
readonly
|
|
20
|
-
readonly EMERGENCY: "Emergency";
|
|
19
|
+
readonly COLOR: "Color";
|
|
20
|
+
readonly EFFECT: "Effect";
|
|
21
21
|
readonly CONFIG: "Config";
|
|
22
|
+
readonly ERROR: "Error";
|
|
23
|
+
readonly UNKNOWN: "Unknown";
|
|
22
24
|
}>;
|
|
23
25
|
buttons: z.ZodObject<{
|
|
24
26
|
powerRightPressed: z.ZodBoolean;
|
|
@@ -30,12 +32,36 @@ export declare const steeringWheelModuleStatusSchema: z.ZodObject<{
|
|
|
30
32
|
}, z.core.$strip>;
|
|
31
33
|
}, z.core.$strip>;
|
|
32
34
|
export declare const steeringWheelShowLedsEffectOptionsSchema: z.ZodEnum<{
|
|
33
|
-
readonly
|
|
34
|
-
readonly
|
|
35
|
-
readonly
|
|
36
|
-
readonly
|
|
35
|
+
readonly SHOW_COLOR: "ShowColor";
|
|
36
|
+
readonly STAR: "Star";
|
|
37
|
+
readonly RAY: "Ray";
|
|
38
|
+
readonly RAY_HIT: "RayHit";
|
|
39
|
+
readonly SPEED_BOOST: "SpeedBoost";
|
|
40
|
+
readonly CHARGED_POWER: "ChargedPower";
|
|
41
|
+
readonly ROULETTE: "Roulette";
|
|
42
|
+
readonly SHIELD: "Shield";
|
|
43
|
+
readonly BROKEN_SHIELD: "BrokenShield";
|
|
44
|
+
readonly WAIT: "Wait";
|
|
45
|
+
readonly STANDARD: "Standard";
|
|
46
|
+
readonly POWER_UP: "PowerUp";
|
|
47
|
+
readonly EMERGENCY: "Emergency";
|
|
48
|
+
readonly TRAFFIC_LIGHT: "TrafficLight";
|
|
37
49
|
}>;
|
|
38
50
|
export declare const steeringWheelConfigureLedsOptionsSchema: z.ZodObject<{
|
|
39
51
|
brightness: z.ZodNumber;
|
|
40
52
|
durationSeconds: z.ZodNumber;
|
|
53
|
+
colorBody: z.ZodDefault<z.ZodOptional<z.ZodEnum<{
|
|
54
|
+
readonly Black: "Black";
|
|
55
|
+
readonly Red: "Red";
|
|
56
|
+
readonly Blue: "Blue";
|
|
57
|
+
readonly Green: "Green";
|
|
58
|
+
readonly Yellow: "Yellow";
|
|
59
|
+
readonly Violet: "Violet";
|
|
60
|
+
readonly Cyan: "Cyan";
|
|
61
|
+
readonly Magenta: "Magenta";
|
|
62
|
+
readonly Orange: "Orange";
|
|
63
|
+
readonly White: "White";
|
|
64
|
+
readonly GreenYellow: "GreenYellow";
|
|
65
|
+
readonly SaddleBrown: "SaddleBrown";
|
|
66
|
+
}>>>;
|
|
41
67
|
}, z.core.$strip>;
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.steeringWheelConfigureLedsOptionsSchema = exports.steeringWheelShowLedsEffectOptionsSchema = exports.steeringWheelModuleStatusSchema = exports.steeringWheelLedStatusSchema = exports.steeringWheelActionCommandSchema = void 0;
|
|
4
4
|
const zod_1 = require("zod");
|
|
5
|
+
const Color_schema_1 = require("../../color/Color.schema");
|
|
6
|
+
const Color_const_1 = require("../../color/Color.const");
|
|
5
7
|
const SteeringWheel_const_1 = require("./SteeringWheel.const");
|
|
6
8
|
exports.steeringWheelActionCommandSchema = zod_1.z.enum(SteeringWheel_const_1.STEERING_WHEEL_ACTION_COMMAND);
|
|
7
9
|
exports.steeringWheelLedStatusSchema = zod_1.z.enum(SteeringWheel_const_1.STEERING_WHEEL_LED_STATUS);
|
|
@@ -20,4 +22,5 @@ exports.steeringWheelShowLedsEffectOptionsSchema = zod_1.z.enum(SteeringWheel_co
|
|
|
20
22
|
exports.steeringWheelConfigureLedsOptionsSchema = zod_1.z.object({
|
|
21
23
|
brightness: zod_1.z.number().min(0).max(100),
|
|
22
24
|
durationSeconds: zod_1.z.number().min(1).max(25),
|
|
25
|
+
colorBody: Color_schema_1.colorNameSchema.optional().default(Color_const_1.COLOR_NAME.Red),
|
|
23
26
|
});
|
|
@@ -116,10 +116,11 @@ export declare const kartSchema: z.ZodObject<{
|
|
|
116
116
|
steeringWheel: z.ZodNullable<z.ZodObject<{
|
|
117
117
|
ledStatus: z.ZodEnum<{
|
|
118
118
|
readonly OFF: "Off";
|
|
119
|
-
readonly
|
|
120
|
-
readonly
|
|
121
|
-
readonly EMERGENCY: "Emergency";
|
|
119
|
+
readonly COLOR: "Color";
|
|
120
|
+
readonly EFFECT: "Effect";
|
|
122
121
|
readonly CONFIG: "Config";
|
|
122
|
+
readonly ERROR: "Error";
|
|
123
|
+
readonly UNKNOWN: "Unknown";
|
|
123
124
|
}>;
|
|
124
125
|
buttons: z.ZodObject<{
|
|
125
126
|
powerRightPressed: z.ZodBoolean;
|
package/package.json
CHANGED
|
@@ -7,16 +7,27 @@ export const STEERING_WHEEL_ACTION_COMMAND = {
|
|
|
7
7
|
} as const;
|
|
8
8
|
|
|
9
9
|
export const STEERING_WHEEL_SHOW_LED_EFFECT_OPTIONS = {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
10
|
+
SHOW_COLOR: 'ShowColor',
|
|
11
|
+
STAR: 'Star',
|
|
12
|
+
RAY: 'Ray',
|
|
13
|
+
RAY_HIT: 'RayHit',
|
|
14
|
+
SPEED_BOOST: 'SpeedBoost',
|
|
15
|
+
CHARGED_POWER: 'ChargedPower',
|
|
16
|
+
ROULETTE: 'Roulette',
|
|
17
|
+
SHIELD: 'Shield',
|
|
18
|
+
BROKEN_SHIELD: 'BrokenShield',
|
|
19
|
+
WAIT: 'Wait',
|
|
20
|
+
STANDARD: 'Standard',
|
|
21
|
+
POWER_UP: 'PowerUp',
|
|
22
|
+
EMERGENCY: 'Emergency',
|
|
23
|
+
TRAFFIC_LIGHT: 'TrafficLight',
|
|
14
24
|
} as const;
|
|
15
25
|
|
|
16
26
|
export const STEERING_WHEEL_LED_STATUS = {
|
|
17
27
|
OFF: 'Off',
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
EMERGENCY: 'Emergency',
|
|
28
|
+
COLOR: 'Color',
|
|
29
|
+
EFFECT: 'Effect',
|
|
21
30
|
CONFIG: 'Config',
|
|
31
|
+
ERROR: 'Error',
|
|
32
|
+
UNKNOWN: 'Unknown',
|
|
22
33
|
} as const;
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
+
import { colorNameSchema } from '../../color/Color.schema';
|
|
3
|
+
import { COLOR_NAME } from '../../color/Color.const';
|
|
2
4
|
import {
|
|
3
5
|
STEERING_WHEEL_ACTION_COMMAND,
|
|
4
6
|
STEERING_WHEEL_LED_STATUS,
|
|
@@ -25,4 +27,5 @@ export const steeringWheelShowLedsEffectOptionsSchema = z.enum(STEERING_WHEEL_SH
|
|
|
25
27
|
export const steeringWheelConfigureLedsOptionsSchema = z.object({
|
|
26
28
|
brightness: z.number().min(0).max(100),
|
|
27
29
|
durationSeconds: z.number().min(1).max(25),
|
|
30
|
+
colorBody: colorNameSchema.optional().default(COLOR_NAME.Red),
|
|
28
31
|
});
|