@truelab/trueserver 0.0.6 → 0.1.1
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/CHANGELOG.md +34 -0
- package/MIGRATION.md +206 -1
- package/README.md +38 -16
- package/dist/helpers/config.helper.d.ts +8 -3
- package/dist/helpers/config.helper.d.ts.map +1 -1
- package/dist/helpers/config.helper.js +11 -8
- package/dist/helpers/config.helper.js.map +1 -1
- package/dist/helpers/errors.helper.d.ts +1 -0
- package/dist/helpers/errors.helper.d.ts.map +1 -1
- package/dist/helpers/errors.helper.js +1 -0
- package/dist/helpers/errors.helper.js.map +1 -1
- package/dist/helpers/money.helper.d.ts +13 -0
- package/dist/helpers/money.helper.d.ts.map +1 -0
- package/dist/helpers/money.helper.js +29 -0
- package/dist/helpers/money.helper.js.map +1 -0
- package/dist/helpers/pkg-version.helper.d.ts +1 -1
- package/dist/helpers/pkg-version.helper.js +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -1
- package/dist/index.js.map +1 -1
- package/dist/plugins/sentry.plugin.d.ts.map +1 -1
- package/dist/plugins/sentry.plugin.js +0 -1
- package/dist/plugins/sentry.plugin.js.map +1 -1
- package/dist/plugins/swagger.plugin.js +1 -1
- package/dist/plugins/swagger.plugin.js.map +1 -1
- package/dist/routes/config/config.route.validate.plugin.d.ts.map +1 -1
- package/dist/routes/config/config.route.validate.plugin.js +15 -10
- package/dist/routes/config/config.route.validate.plugin.js.map +1 -1
- package/dist/routes/play/play.route.validate.plugin.d.ts +5 -4
- package/dist/routes/play/play.route.validate.plugin.d.ts.map +1 -1
- package/dist/routes/play/play.route.validate.plugin.js +21 -14
- package/dist/routes/play/play.route.validate.plugin.js.map +1 -1
- package/dist/routes/validate/validate.route.validate.plugin.d.ts.map +1 -1
- package/dist/routes/validate/validate.route.validate.plugin.js +2 -7
- package/dist/routes/validate/validate.route.validate.plugin.js.map +1 -1
- package/dist/schemas/config.schema.d.ts +3 -3
- package/dist/schemas/config.schema.js +8 -8
- package/dist/schemas/config.schema.js.map +1 -1
- package/dist/schemas/examples/config.schema.examples.d.ts.map +1 -1
- package/dist/schemas/examples/config.schema.examples.js +48 -12
- package/dist/schemas/examples/config.schema.examples.js.map +1 -1
- package/dist/schemas/examples/play.schema.examples.d.ts.map +1 -1
- package/dist/schemas/examples/play.schema.examples.js +41 -13
- package/dist/schemas/examples/play.schema.examples.js.map +1 -1
- package/dist/schemas/examples/validate.schema.examples.d.ts.map +1 -1
- package/dist/schemas/examples/validate.schema.examples.js +51 -39
- package/dist/schemas/examples/validate.schema.examples.js.map +1 -1
- package/dist/schemas/play.schema.d.ts +11 -11
- package/dist/schemas/play.schema.d.ts.map +1 -1
- package/dist/schemas/play.schema.js +22 -21
- package/dist/schemas/play.schema.js.map +1 -1
- package/dist/schemas/shared.d.ts +1 -1
- package/dist/schemas/shared.js +4 -4
- package/dist/schemas/shared.js.map +1 -1
- package/dist/schemas/validate.schema.d.ts +12 -7
- package/dist/schemas/validate.schema.d.ts.map +1 -1
- package/dist/schemas/validate.schema.js +17 -16
- package/dist/schemas/validate.schema.js.map +1 -1
- package/dist/test-utils.d.ts +1 -1
- package/dist/test-utils.js +1 -1
- package/package.json +7 -9
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,40 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [0.1.0] - Unreleased
|
|
10
|
+
|
|
11
|
+
### Breaking
|
|
12
|
+
|
|
13
|
+
- `/play` ответ **уплощён**: убрана обёртка `results`. Все поля теперь на верхнем уровне.
|
|
14
|
+
- `/play` ответ: `results.cashWin` → `winCash`; `results.coins` → `winCoins`.
|
|
15
|
+
- `/play` ответ: добавлено обязательное поле `hasMaxWin: boolean`. При достижении max-win обязательно `hasMaxWin: true` вместе с `finished: true`.
|
|
16
|
+
- `/play` ответ: `results.choices?: integer[]` → `choices?: (number | string)[]` — тип элемента расширен до `number | string`.
|
|
17
|
+
- `/play` ответ: добавлено поле `choice?: number | string` — эхо `choice` с continuation-шагов.
|
|
18
|
+
- `/play` запрос: `choice` — тип `integer` → `number | string`.
|
|
19
|
+
- `/config` ответ: удалено поле `gameTitle`.
|
|
20
|
+
- `/config` ответ: `hasVariableLines` → `hasVariableLevels`.
|
|
21
|
+
- `/config` ответ: добавлено обязательное поле `hasValidate: boolean`.
|
|
22
|
+
- `/config` инварианты: при `hasMultiStake === true` обязательно `freeroundsAllowed === false` и `hasValidate === true`.
|
|
23
|
+
- `TGameConfig` (`registerGameConfig`): удалено поле `gameTitle`; `hasVariableLines` → `hasVariableLevels`; `hasValidation` → `hasValidate` (обязательное при `hasMultiStake === true`).
|
|
24
|
+
- `/validate` запрос: `demo` → `isDemo`; `metaPublic`/`metaPrivate` (строки) удалены — заменены на `meta: { public, private }` (объект); `currencyDecimals` и `stakes` стали обязательными.
|
|
25
|
+
- `/validate` ответ: пустой `{}` → `{ valid: boolean, reason?: string }`. Хендлер игры теперь возвращает вердикт, а не пустой объект. `valid: false` — это `200` с отказом (раунд не открывается), не ошибка.
|
|
26
|
+
- `StakeMode.parametric` — стало опциональным (отсутствие эквивалентно `false`).
|
|
27
|
+
|
|
28
|
+
### Added
|
|
29
|
+
|
|
30
|
+
- Money-хелперы: `Big`, `roundMoney`, `isExactDecimal` — экспортируются из `@truelab/trueserver`. Позволяют считать денежные значения в decimal-арифметике без подключения отдельной библиотеки.
|
|
31
|
+
- Зависимость `big.js` добавлена в `dependencies` пакета (передаётся транзитивно потребителям).
|
|
32
|
+
|
|
33
|
+
### Changed
|
|
34
|
+
|
|
35
|
+
- Включает все изменения из 0.0.7 (multi-RTP режим: `rtpVersion` в `registerGameConfig` стал опциональным; сверка `rtpVersion` ответа с path-сегментом выполняется всегда).
|
|
36
|
+
|
|
37
|
+
## [0.0.7] - 2026-06-06
|
|
38
|
+
|
|
39
|
+
### Changed
|
|
40
|
+
- `TGameConfig.rtpVersion` теперь **опциональное**. Поддержан режим «один инстанс — все RTP»: игра не регистрирует `rtpVersion`, а берёт его из сегмента `{rtpVersion}` пути на каждый запрос.
|
|
41
|
+
- `config.route.validate.plugin`: сверка `rtpVersion` ответа с `gameConfig.rtpVersion` выполняется **только если** он задан (single-RTP). Сверка с path-сегментом `{rtpVersion}` выполняется всегда. См. [MIGRATION.md](MIGRATION.md).
|
|
42
|
+
|
|
9
43
|
## [0.0.6] - 2026-06-03
|
|
10
44
|
|
|
11
45
|
### Breaking
|
package/MIGRATION.md
CHANGED
|
@@ -1,10 +1,215 @@
|
|
|
1
1
|
# Migration
|
|
2
2
|
|
|
3
|
-
- [0.0.
|
|
3
|
+
- [0.0.7 → 0.1.0](#migration-007--010) (последнее)
|
|
4
|
+
- [0.0.6 → 0.0.7](#migration-006--007)
|
|
5
|
+
- [0.0.5 → 0.0.6](#migration-005--006)
|
|
4
6
|
- [0.0.4 → 0.0.5](#migration-004--005)
|
|
5
7
|
|
|
6
8
|
---
|
|
7
9
|
|
|
10
|
+
## Migration: 0.0.7 → 0.1.0
|
|
11
|
+
|
|
12
|
+
Ломающий релиз. Приводит пакет в полное соответствие контракту Game Server API v1.0.0.
|
|
13
|
+
|
|
14
|
+
> **TL;DR что менять в игре:**
|
|
15
|
+
> 1. `/play` хендлер: убрать обёртку `results`, переименовать поля, добавить `hasMaxWin`.
|
|
16
|
+
> 2. `/config` хендлер: убрать `gameTitle`, заменить `hasVariableLines` на `hasVariableLevels`, добавить `hasValidate`.
|
|
17
|
+
> 3. `registerGameConfig`: убрать `gameTitle`, `hasVariableLines` → `hasVariableLevels`, `hasValidation` → `hasValidate`.
|
|
18
|
+
> 4. `/validate` хендлер: обновить запрос и вернуть `{ valid: boolean, reason?: string }`.
|
|
19
|
+
|
|
20
|
+
### 1. `/play` ответ — уплощение и переименования
|
|
21
|
+
|
|
22
|
+
Обёртка `results` удалена. Все поля возвращаются на верхнем уровне.
|
|
23
|
+
|
|
24
|
+
```diff
|
|
25
|
+
// handlers.register('play', async (request): Promise<TPlayReply> => {
|
|
26
|
+
return {
|
|
27
|
+
gameResponse: {},
|
|
28
|
+
rng: [],
|
|
29
|
+
- results: {
|
|
30
|
+
- coins: 250,
|
|
31
|
+
- cashWin: 2.5,
|
|
32
|
+
- finished: true,
|
|
33
|
+
- },
|
|
34
|
+
+ winCoins: 250,
|
|
35
|
+
+ winCash: 2.5,
|
|
36
|
+
+ finished: true,
|
|
37
|
+
+ hasMaxWin: false,
|
|
38
|
+
}
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
| Старое | Новое |
|
|
42
|
+
|---|---|
|
|
43
|
+
| `results.cashWin` | `winCash` |
|
|
44
|
+
| `results.coins` | `winCoins` |
|
|
45
|
+
| `results.finished` | `finished` |
|
|
46
|
+
| `results.choices` | `choices` |
|
|
47
|
+
| `results.meta` | `meta` |
|
|
48
|
+
| — | `hasMaxWin` (обязательное) |
|
|
49
|
+
| — | `choice` (эхо choice на continuation-шагах) |
|
|
50
|
+
|
|
51
|
+
**`hasMaxWin`** — обязательное поле в каждом ответе. При достижении max-win: `hasMaxWin: true` + `finished: true` одновременно. Незавершённый раунд с `hasMaxWin: true` является ошибкой.
|
|
52
|
+
|
|
53
|
+
### 2. `/play` запрос — тип `choice`
|
|
54
|
+
|
|
55
|
+
`choice` расширен до `number | string` (раньше был только `integer`). Аналогично `choices` в ответе стал `(number | string)[]`.
|
|
56
|
+
|
|
57
|
+
### 3. `/config` ответ
|
|
58
|
+
|
|
59
|
+
```diff
|
|
60
|
+
{
|
|
61
|
+
- gameTitle: 'My Game',
|
|
62
|
+
gameId: 'my-game',
|
|
63
|
+
...
|
|
64
|
+
- hasVariableLines: false,
|
|
65
|
+
+ hasVariableLevels: false,
|
|
66
|
+
coinRatio: 10,
|
|
67
|
+
...
|
|
68
|
+
+ hasValidate: false,
|
|
69
|
+
}
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
| Старое | Новое |
|
|
73
|
+
|---|---|
|
|
74
|
+
| `gameTitle` | удалено |
|
|
75
|
+
| `hasVariableLines` | `hasVariableLevels` |
|
|
76
|
+
| — | `hasValidate` (обязательное) |
|
|
77
|
+
|
|
78
|
+
**Новые инварианты** при `hasMultiStake === true`:
|
|
79
|
+
- `freeroundsAllowed` должен быть `false`
|
|
80
|
+
- `hasValidate` должен быть `true`
|
|
81
|
+
|
|
82
|
+
### 4. `registerGameConfig`
|
|
83
|
+
|
|
84
|
+
```diff
|
|
85
|
+
registerGameConfig({
|
|
86
|
+
- gameTitle: 'My Game',
|
|
87
|
+
gameId: 'my-game',
|
|
88
|
+
...
|
|
89
|
+
- hasVariableLines: false,
|
|
90
|
+
+ hasVariableLevels: false,
|
|
91
|
+
coinRatio: 10,
|
|
92
|
+
- hasValidation: true, // legacy
|
|
93
|
+
+ hasValidate: true,
|
|
94
|
+
})
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
| Старое | Новое |
|
|
98
|
+
|---|---|
|
|
99
|
+
| `gameTitle` | удалено |
|
|
100
|
+
| `hasVariableLines` | `hasVariableLevels` |
|
|
101
|
+
| `hasValidation` | `hasValidate` (обязательное при `hasMultiStake === true`) |
|
|
102
|
+
|
|
103
|
+
### 5. `/validate` запрос и ответ
|
|
104
|
+
|
|
105
|
+
**Запрос** — поля `metaPublic`/`metaPrivate` (строки) удалены, заменены на `meta: { public, private }` (объект); `demo` → `isDemo`; `currencyDecimals` и `stakes` стали обязательными.
|
|
106
|
+
|
|
107
|
+
```diff
|
|
108
|
+
-{
|
|
109
|
+
- demo: true,
|
|
110
|
+
- currencyDecimals: 2,
|
|
111
|
+
- stakes: [{ name: 'default', amount: 3 }],
|
|
112
|
+
- maxExposure: 100000,
|
|
113
|
+
- metaPublic: '{"someProgress": 15}',
|
|
114
|
+
- metaPrivate: '{"seed": "..."}',
|
|
115
|
+
-}
|
|
116
|
+
+{
|
|
117
|
+
+ isDemo: true,
|
|
118
|
+
+ currencyDecimals: 2,
|
|
119
|
+
+ stakes: [{ name: 'default', amount: 3 }],
|
|
120
|
+
+ maxExposure: 100000,
|
|
121
|
+
+ meta: {
|
|
122
|
+
+ public: { someProgress: 15 },
|
|
123
|
+
+ private: { seed: '...' },
|
|
124
|
+
+ },
|
|
125
|
+
+}
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
**Ответ** — хендлер теперь возвращает вердикт, а не пустой объект:
|
|
129
|
+
|
|
130
|
+
```diff
|
|
131
|
+
-handlers.register('validate', async (): Promise<TValidateReply> => ({}))
|
|
132
|
+
+handlers.register('validate', async (request): Promise<TValidateReply> => {
|
|
133
|
+
+ // Validate stake set — e.g. check that max win cannot exceed maxExposure
|
|
134
|
+
+ const isValid = checkStakes(request.body.stakes, request.body.maxExposure)
|
|
135
|
+
+ if (!isValid) {
|
|
136
|
+
+ return { valid: false, reason: 'potential win exceeds maxExposure' }
|
|
137
|
+
+ }
|
|
138
|
+
+ return { valid: true }
|
|
139
|
+
+})
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
`valid: false` возвращается со статусом `200` — это деловой вердикт, не ошибка. RGS не откроет раунд при `valid: false`. Некорректный запрос (отсутствуют обязательные поля) — по-прежнему `400`.
|
|
143
|
+
|
|
144
|
+
### 6. `StakeMode.parametric` — стало опциональным
|
|
145
|
+
|
|
146
|
+
```diff
|
|
147
|
+
-{ name: 'default', type: 'default', bets: 1, parametric: false }
|
|
148
|
+
+{ name: 'default', type: 'default', bets: 1 }
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
Отсутствие `parametric` эквивалентно `false`. Существующий код, передающий `parametric: false`, продолжает работать.
|
|
152
|
+
|
|
153
|
+
### 7. Money-хелперы (новое)
|
|
154
|
+
|
|
155
|
+
Пакет теперь экспортирует decimal-хелперы. Используйте их для вычисления `winCash`/`winCoins` вместо нативной float-арифметики:
|
|
156
|
+
|
|
157
|
+
```typescript
|
|
158
|
+
import { Big, roundMoney, isExactDecimal } from '@truelab/trueserver'
|
|
159
|
+
|
|
160
|
+
const winCash = roundMoney(new Big(coins).times(coinValue), currencyDecimals)
|
|
161
|
+
// winCash — точное decimal-число без float-дрейфа
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
### Чек-лист
|
|
165
|
+
|
|
166
|
+
1. `npm install @truelab/trueserver@0.1.0`.
|
|
167
|
+
2. `/play` хендлер: убрать `results`, переименовать `cashWin`→`winCash`, `coins`→`winCoins`, добавить `hasMaxWin`.
|
|
168
|
+
3. `/config` хендлер: убрать `gameTitle`, `hasVariableLines`→`hasVariableLevels`, добавить `hasValidate`.
|
|
169
|
+
4. `registerGameConfig`: убрать `gameTitle`, `hasVariableLines`→`hasVariableLevels`, `hasValidation`→`hasValidate`.
|
|
170
|
+
5. `/validate` хендлер: обновить форму запроса (убрать `metaPublic`/`metaPrivate`, добавить `meta`; `demo`→`isDemo`); вернуть `{ valid: boolean, reason?: string }`.
|
|
171
|
+
6. Проверить инварианты: если `hasMultiStake: true` — `freeroundsAllowed: false` и `hasValidate: true`.
|
|
172
|
+
7. Обновить тесты: плоский ответ `/play`, новые поля, новый контракт `/validate`.
|
|
173
|
+
8. Прогнать тесты.
|
|
174
|
+
|
|
175
|
+
---
|
|
176
|
+
|
|
177
|
+
## Migration: 0.0.6 → 0.0.7
|
|
178
|
+
|
|
179
|
+
Не ломающее изменение. `rtpVersion` в `registerGameConfig` стал опциональным, чтобы один инстанс мог обслуживать все RTP-версии, выбирая их по сегменту `{rtpVersion}` пути.
|
|
180
|
+
|
|
181
|
+
> **TL;DR:**
|
|
182
|
+
> - Хотите как раньше (один инстанс = один RTP) — ничего менять не нужно, `rtpVersion` в `registerGameConfig` продолжает работать и сверяется.
|
|
183
|
+
> - Хотите один инстанс на все RTP — **не передавайте** `rtpVersion` в `registerGameConfig`, а в reply `handlers.register('config', ...)` возвращайте `rtpVersion` из `request.params.rtpVersion`.
|
|
184
|
+
|
|
185
|
+
### Режим «один инстанс — все RTP»
|
|
186
|
+
|
|
187
|
+
```diff
|
|
188
|
+
registerGameConfig({
|
|
189
|
+
gameTitle: 'My Game',
|
|
190
|
+
gameId: 'my-game',
|
|
191
|
+
- rtpVersion: '96',
|
|
192
|
+
...
|
|
193
|
+
})
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
```diff
|
|
197
|
+
handlers.register('config', async (request) => ({
|
|
198
|
+
...
|
|
199
|
+
- rtpVersion: '96',
|
|
200
|
+
+ rtpVersion: request.params.rtpVersion,
|
|
201
|
+
...
|
|
202
|
+
}))
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
Правила валидации `/config`:
|
|
206
|
+
- `rtpVersion` ответа **всегда** сверяется с сегментом `{rtpVersion}` пути (несовпадение → `500` `code=1006`);
|
|
207
|
+
- с `gameConfig.rtpVersion` — **только если** он задан (single-RTP режим).
|
|
208
|
+
|
|
209
|
+
Все остальные RTP-зависимые поля (`rtp`, `volatility`, при необходимости `coinRatio`/`variableLevels`) тоже выбирайте по `request.params.rtpVersion`. Помните, что `coinRatio`/`variableLevels`/`defaultLevel` по-прежнему сверяются с `gameConfig`, поэтому при их различии между RTP single-config сверка не подойдёт.
|
|
210
|
+
|
|
211
|
+
---
|
|
212
|
+
|
|
8
213
|
## Migration: 0.0.5 → 0.0.6
|
|
9
214
|
|
|
10
215
|
Документация GS Documentation обновлена после релиза 0.0.5. Изменения локальные, но breaking: новое обязательное поле в `/config` и опциональное в `Stake`.
|
package/README.md
CHANGED
|
@@ -41,7 +41,6 @@ const stakeModes = [{
|
|
|
41
41
|
}]
|
|
42
42
|
|
|
43
43
|
registerGameConfig({
|
|
44
|
-
gameTitle: 'My Game',
|
|
45
44
|
gameId: 'my-game',
|
|
46
45
|
rtpVersion: '96',
|
|
47
46
|
stakeModes,
|
|
@@ -49,12 +48,12 @@ registerGameConfig({
|
|
|
49
48
|
hasChoice: false,
|
|
50
49
|
hasRestore: false,
|
|
51
50
|
hasMultiStake: false,
|
|
52
|
-
|
|
51
|
+
hasVariableLevels: false,
|
|
53
52
|
coinRatio: 10,
|
|
53
|
+
hasValidate: false,
|
|
54
54
|
})
|
|
55
55
|
|
|
56
56
|
handlers.register('config', async (): Promise<TConfigReply> => ({
|
|
57
|
-
gameTitle: 'My Game',
|
|
58
57
|
gameId: 'my-game',
|
|
59
58
|
volatility: 2.5,
|
|
60
59
|
rtp: '96.17',
|
|
@@ -63,11 +62,12 @@ handlers.register('config', async (): Promise<TConfigReply> => ({
|
|
|
63
62
|
hasChoice: false,
|
|
64
63
|
hasRestore: false,
|
|
65
64
|
hasMultiStake: false,
|
|
66
|
-
|
|
65
|
+
hasVariableLevels: false,
|
|
67
66
|
coinRatio: 10,
|
|
68
67
|
minWin: 0.1,
|
|
69
68
|
maxWin: 5000,
|
|
70
69
|
freeroundsAllowed: false,
|
|
70
|
+
hasValidate: false,
|
|
71
71
|
stakeModes,
|
|
72
72
|
}))
|
|
73
73
|
|
|
@@ -80,10 +80,9 @@ handlers.register('play', async (request): Promise<TPlayReply> => {
|
|
|
80
80
|
return {
|
|
81
81
|
gameResponse: {},
|
|
82
82
|
rng: [],
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
},
|
|
83
|
+
winCash: 0,
|
|
84
|
+
finished: true,
|
|
85
|
+
hasMaxWin: false,
|
|
87
86
|
}
|
|
88
87
|
})
|
|
89
88
|
|
|
@@ -93,8 +92,11 @@ handlers.register('checksum', async (): Promise<TChecksumReply> => ({
|
|
|
93
92
|
],
|
|
94
93
|
}))
|
|
95
94
|
|
|
96
|
-
// Optional.
|
|
97
|
-
handlers.register('validate', async (): Promise<TValidateReply> =>
|
|
95
|
+
// Optional. Enabled when hasValidate=true in registerGameConfig.
|
|
96
|
+
handlers.register('validate', async (request): Promise<TValidateReply> => {
|
|
97
|
+
// Return a verdict: valid=true allows the round; valid=false rejects it (HTTP 200, no round opened).
|
|
98
|
+
return { valid: true }
|
|
99
|
+
})
|
|
98
100
|
|
|
99
101
|
async function main() {
|
|
100
102
|
const app = new Application()
|
|
@@ -112,7 +114,7 @@ main().catch((error) => {
|
|
|
112
114
|
})
|
|
113
115
|
```
|
|
114
116
|
|
|
115
|
-
`registerGameConfig` must be called before `app.build()` — validation plugins read the config while the server is being built. The function throws if invariants are violated (e.g. `hasChoice=true` without `hasMeta=true`,
|
|
117
|
+
`registerGameConfig` must be called before `app.build()` — validation plugins read the config while the server is being built. The function throws if invariants are violated (e.g. `hasChoice=true` without `hasMeta=true`, `hasVariableLevels=true` without `variableLevels`/`defaultLevel`, or `hasMultiStake=true` without `hasValidate=true`).
|
|
116
118
|
|
|
117
119
|
## Runtime API
|
|
118
120
|
|
|
@@ -125,7 +127,7 @@ main().catch((error) => {
|
|
|
125
127
|
| `GET` | `/:rtpVersion/client` | `handlers.register('client', ...)` | Client-side payload |
|
|
126
128
|
| `GET` | `/:rtpVersion/checksum` | `handlers.register('checksum', ...)` | File hashes for integrity check |
|
|
127
129
|
| `POST` | `/:rtpVersion/play` | `handlers.register('play', ...)` | One step of a round |
|
|
128
|
-
| `POST` | `/:rtpVersion/validate` | `handlers.register('validate', ...)` |
|
|
130
|
+
| `POST` | `/:rtpVersion/validate` | `handlers.register('validate', ...)` | Pre-round stake validation — returns `{ valid, reason? }` (enabled when `hasValidate=true`) |
|
|
129
131
|
|
|
130
132
|
Every request must carry the API key header. By default it is `x-api-key: test`; override with `API_HEADER` and `API_KEY`.
|
|
131
133
|
|
|
@@ -162,7 +164,7 @@ import {
|
|
|
162
164
|
} from '@truelab/trueserver/test-utils'
|
|
163
165
|
```
|
|
164
166
|
|
|
165
|
-
`test-utils` uses
|
|
167
|
+
`test-utils` uses a global `expect`, so consumers should run it under a test runner that provides one (e.g. Vitest or Jest) with globals enabled.
|
|
166
168
|
|
|
167
169
|
## Environment variables
|
|
168
170
|
|
|
@@ -194,11 +196,31 @@ Swagger UI is enabled only for `APP_ENV=dev`.
|
|
|
194
196
|
|
|
195
197
|
The package validates both requests and replies via TypeBox schemas and runs game-specific checks based on `registerGameConfig`:
|
|
196
198
|
|
|
197
|
-
- `/config` checks stake modes, the `hasChoice ⇒ hasMeta` invariant
|
|
198
|
-
- `/play` checks stakes (uniqueness, qty, level in `variableLevels`, parametric/params), `stepIndex` consistency (opening vs continuation), `choice`/`meta` presence per flags, and reply shape (`finished`, `choices`, `meta`).
|
|
199
|
-
- `/validate` is available only when `
|
|
199
|
+
- `/config` checks stake modes, the `hasChoice ⇒ hasMeta` invariant, the `hasVariableLevels ⇔ {variableLevels, defaultLevel}` pair, and `hasMultiStake=true ⇒ freeroundsAllowed=false` and `hasValidate=true`.
|
|
200
|
+
- `/play` checks stakes (uniqueness, qty, level in `variableLevels`, parametric/params), `stepIndex` consistency (opening vs continuation), `choice`/`meta` presence per flags, and reply shape (`finished`, `choices`, `meta`, `hasMaxWin`).
|
|
201
|
+
- `/validate` is available only when `hasValidate=true`. Returns `{ valid: boolean, reason?: string }`.
|
|
200
202
|
- `test` field is rejected on production (`APP_ENV=master` or unset).
|
|
201
203
|
|
|
204
|
+
## Money-value precision
|
|
205
|
+
|
|
206
|
+
All monetary values on the wire — `winCash`, `winCoins`, `coinValue`, `minWin`, `maxWin`, `amount` — must be **exact decimal representations**. «Exact» means the value round-trips through `JSON.parse → number → toString` to itself. For example, `0.3` is exact; `0.30000000000000004` (the IEEE 754 artefact of `0.1 + 0.2` in native JavaScript) is not.
|
|
207
|
+
|
|
208
|
+
RGS compares all incoming money values with **strict equality** (no epsilon tolerance). Any float drift in upstream computation will cause RGS to reject the value as invalid.
|
|
209
|
+
|
|
210
|
+
**What this means in practice:**
|
|
211
|
+
|
|
212
|
+
- Compute `winCash`, `winCoins` and any derived monetary values in decimal arithmetic — avoid `+ - × ÷` on native floating-point types for money-valued outputs.
|
|
213
|
+
- Use a decimal library. The package exports `Big`, `roundMoney` and `isExactDecimal` from `big.js` so you do not need to add a separate dependency.
|
|
214
|
+
|
|
215
|
+
```ts
|
|
216
|
+
import { Big, roundMoney, isExactDecimal } from '@truelab/trueserver'
|
|
217
|
+
|
|
218
|
+
const winCash = roundMoney(new Big(coins).times(coinValue), currencyDecimals) // exact-decimal number
|
|
219
|
+
isExactDecimal(winCash, currencyDecimals) // true
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
`roundMoney(value, decimals)` returns a `number` rounded to `decimals` decimal places using decimal arithmetic (half-up by default). `isExactDecimal(value, decimals)` checks that the number has at most `decimals` fractional digits — useful as a self-check in tests.
|
|
223
|
+
|
|
202
224
|
## Migration
|
|
203
225
|
|
|
204
226
|
This release is breaking. See [MIGRATION.md](MIGRATION.md) for a full mapping of old fields/paths to new ones.
|
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
import { TConfigReply } from '../schemas/config.schema';
|
|
2
|
-
export type TGameConfig = Pick<TConfigReply, 'gameId' | '
|
|
3
|
-
/**
|
|
4
|
-
|
|
2
|
+
export type TGameConfig = Pick<TConfigReply, 'gameId' | 'stakeModes' | 'hasMeta' | 'hasChoice' | 'hasRestore' | 'hasMultiStake' | 'hasVariableLevels' | 'hasValidate' | 'variableLevels' | 'defaultLevel' | 'coinRatio'> & {
|
|
3
|
+
/**
|
|
4
|
+
* RTP version label. Optional: set it only for single-RTP deployments.
|
|
5
|
+
* Multi-RTP servers omit it and take rtpVersion from the {rtpVersion} path
|
|
6
|
+
* segment per request — in that case /config validates the reply against the
|
|
7
|
+
* path segment only.
|
|
8
|
+
*/
|
|
9
|
+
rtpVersion?: TConfigReply['rtpVersion'];
|
|
5
10
|
};
|
|
6
11
|
export declare function registerGameConfig(config: TGameConfig): void;
|
|
7
12
|
export declare function getGameConfig(): TGameConfig;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.helper.d.ts","sourceRoot":"","sources":["../../helpers/config.helper.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAA;AAGvD,MAAM,MAAM,WAAW,GAAG,IAAI,CAAC,YAAY,EAC3C,QAAQ,GACR,
|
|
1
|
+
{"version":3,"file":"config.helper.d.ts","sourceRoot":"","sources":["../../helpers/config.helper.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAA;AAGvD,MAAM,MAAM,WAAW,GAAG,IAAI,CAAC,YAAY,EAC3C,QAAQ,GACR,YAAY,GACZ,SAAS,GACT,WAAW,GACX,YAAY,GACZ,eAAe,GACf,mBAAmB,GACnB,aAAa,GACb,gBAAgB,GAChB,cAAc,GACd,WAAW,CACV,GAAG;IACA;;;;;OAKG;IACH,UAAU,CAAC,EAAE,YAAY,CAAC,YAAY,CAAC,CAAC;CAC3C,CAAA;AAID,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,WAAW,QAiCrD;AAED,wBAAgB,aAAa,IAAI,WAAW,CAE3C"}
|
|
@@ -5,29 +5,32 @@ exports.getGameConfig = getGameConfig;
|
|
|
5
5
|
const fastify_helper_1 = require("./fastify.helper");
|
|
6
6
|
let gameConfig;
|
|
7
7
|
function registerGameConfig(config) {
|
|
8
|
-
if (typeof config.rtpVersion !== 'string' || config.rtpVersion.length === 0) {
|
|
9
|
-
throw (0, fastify_helper_1.httpError)('Game config: rtpVersion must be a non-empty string', 500);
|
|
8
|
+
if (config.rtpVersion !== undefined && (typeof config.rtpVersion !== 'string' || config.rtpVersion.length === 0)) {
|
|
9
|
+
throw (0, fastify_helper_1.httpError)('Game config: rtpVersion must be a non-empty string when provided', 500);
|
|
10
10
|
}
|
|
11
11
|
if (config.hasChoice && !config.hasMeta) {
|
|
12
12
|
throw (0, fastify_helper_1.httpError)('Game config: hasChoice=true requires hasMeta=true', 500);
|
|
13
13
|
}
|
|
14
|
-
if (config.
|
|
14
|
+
if (config.hasMultiStake && config.hasValidate !== true) {
|
|
15
|
+
throw (0, fastify_helper_1.httpError)('Game config: hasValidate must be true when hasMultiStake=true', 500);
|
|
16
|
+
}
|
|
17
|
+
if (config.hasVariableLevels) {
|
|
15
18
|
if (!Array.isArray(config.variableLevels) || config.variableLevels.length === 0) {
|
|
16
|
-
throw (0, fastify_helper_1.httpError)('Game config: variableLevels must be a non-empty array when
|
|
19
|
+
throw (0, fastify_helper_1.httpError)('Game config: variableLevels must be a non-empty array when hasVariableLevels=true', 500);
|
|
17
20
|
}
|
|
18
21
|
if (config.defaultLevel === undefined || !config.variableLevels.includes(config.defaultLevel)) {
|
|
19
|
-
throw (0, fastify_helper_1.httpError)('Game config: defaultLevel must be one of variableLevels when
|
|
22
|
+
throw (0, fastify_helper_1.httpError)('Game config: defaultLevel must be one of variableLevels when hasVariableLevels=true', 500);
|
|
20
23
|
}
|
|
21
24
|
if (config.coinRatio !== undefined) {
|
|
22
|
-
throw (0, fastify_helper_1.httpError)('Game config: coinRatio must not be set when
|
|
25
|
+
throw (0, fastify_helper_1.httpError)('Game config: coinRatio must not be set when hasVariableLevels=true', 500);
|
|
23
26
|
}
|
|
24
27
|
}
|
|
25
28
|
else {
|
|
26
29
|
if (config.coinRatio === undefined) {
|
|
27
|
-
throw (0, fastify_helper_1.httpError)('Game config: coinRatio is required when
|
|
30
|
+
throw (0, fastify_helper_1.httpError)('Game config: coinRatio is required when hasVariableLevels=false', 500);
|
|
28
31
|
}
|
|
29
32
|
if (config.variableLevels !== undefined || config.defaultLevel !== undefined) {
|
|
30
|
-
throw (0, fastify_helper_1.httpError)('Game config: variableLevels/defaultLevel must not be set when
|
|
33
|
+
throw (0, fastify_helper_1.httpError)('Game config: variableLevels/defaultLevel must not be set when hasVariableLevels=false', 500);
|
|
31
34
|
}
|
|
32
35
|
}
|
|
33
36
|
gameConfig = config;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.helper.js","sourceRoot":"","sources":["../../helpers/config.helper.ts"],"names":[],"mappings":";;
|
|
1
|
+
{"version":3,"file":"config.helper.js","sourceRoot":"","sources":["../../helpers/config.helper.ts"],"names":[],"mappings":";;AA2BA,gDAiCC;AAED,sCAEC;AA/DD,qDAA4C;AAwB5C,IAAI,UAAuB,CAAA;AAE3B,SAAgB,kBAAkB,CAAC,MAAmB;IAClD,IAAI,MAAM,CAAC,UAAU,KAAK,SAAS,IAAI,CAAC,OAAO,MAAM,CAAC,UAAU,KAAK,QAAQ,IAAI,MAAM,CAAC,UAAU,CAAC,MAAM,KAAK,CAAC,CAAC,EAAE,CAAC;QAC/G,MAAM,IAAA,0BAAS,EAAC,kEAAkE,EAAE,GAAG,CAAC,CAAA;IAC5F,CAAC;IAED,IAAI,MAAM,CAAC,SAAS,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QACtC,MAAM,IAAA,0BAAS,EAAC,mDAAmD,EAAE,GAAG,CAAC,CAAA;IAC7E,CAAC;IAED,IAAI,MAAM,CAAC,aAAa,IAAI,MAAM,CAAC,WAAW,KAAK,IAAI,EAAE,CAAC;QACtD,MAAM,IAAA,0BAAS,EAAC,+DAA+D,EAAE,GAAG,CAAC,CAAA;IACzF,CAAC;IAED,IAAI,MAAM,CAAC,iBAAiB,EAAE,CAAC;QAC3B,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,cAAc,CAAC,IAAI,MAAM,CAAC,cAAc,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC9E,MAAM,IAAA,0BAAS,EAAC,mFAAmF,EAAE,GAAG,CAAC,CAAA;QAC7G,CAAC;QACD,IAAI,MAAM,CAAC,YAAY,KAAK,SAAS,IAAI,CAAC,MAAM,CAAC,cAAc,CAAC,QAAQ,CAAC,MAAM,CAAC,YAAY,CAAC,EAAE,CAAC;YAC5F,MAAM,IAAA,0BAAS,EAAC,qFAAqF,EAAE,GAAG,CAAC,CAAA;QAC/G,CAAC;QACD,IAAI,MAAM,CAAC,SAAS,KAAK,SAAS,EAAE,CAAC;YACjC,MAAM,IAAA,0BAAS,EAAC,oEAAoE,EAAE,GAAG,CAAC,CAAA;QAC9F,CAAC;IACL,CAAC;SAAM,CAAC;QACJ,IAAI,MAAM,CAAC,SAAS,KAAK,SAAS,EAAE,CAAC;YACjC,MAAM,IAAA,0BAAS,EAAC,iEAAiE,EAAE,GAAG,CAAC,CAAA;QAC3F,CAAC;QACD,IAAI,MAAM,CAAC,cAAc,KAAK,SAAS,IAAI,MAAM,CAAC,YAAY,KAAK,SAAS,EAAE,CAAC;YAC3E,MAAM,IAAA,0BAAS,EAAC,uFAAuF,EAAE,GAAG,CAAC,CAAA;QACjH,CAAC;IACL,CAAC;IAED,UAAU,GAAG,MAAM,CAAA;AACvB,CAAC;AAED,SAAgB,aAAa;IACzB,OAAO,UAAU,CAAA;AACrB,CAAC"}
|
|
@@ -11,6 +11,7 @@ export declare const EErrorCode: {
|
|
|
11
11
|
readonly BusinessMetaShape: 4002;
|
|
12
12
|
readonly BusinessFinishedMismatch: 4003;
|
|
13
13
|
readonly BusinessChoicesMismatch: 4004;
|
|
14
|
+
readonly BusinessMaxWinMismatch: 4005;
|
|
14
15
|
readonly Unhandled: 9001;
|
|
15
16
|
readonly NotFound: 9404;
|
|
16
17
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.helper.d.ts","sourceRoot":"","sources":["../../helpers/errors.helper.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,UAAU
|
|
1
|
+
{"version":3,"file":"errors.helper.d.ts","sourceRoot":"","sources":["../../helpers/errors.helper.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,UAAU;;;;;;;;;;;;;;;;CAgBb,CAAA;AAEV,MAAM,MAAM,UAAU,GAAG,CAAC,OAAO,UAAU,CAAC,CAAC,MAAM,OAAO,UAAU,CAAC,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.helper.js","sourceRoot":"","sources":["../../helpers/errors.helper.ts"],"names":[],"mappings":";;;AAAa,QAAA,UAAU,GAAG;IACtB,cAAc,EAAE,IAAI;IACpB,gBAAgB,EAAE,IAAI;IACtB,cAAc,EAAE,IAAI;IACpB,gBAAgB,EAAE,IAAI;IACtB,mBAAmB,EAAE,IAAI;IACzB,gBAAgB,EAAE,IAAI;IACtB,cAAc,EAAE,IAAI;IACpB,MAAM,EAAE,IAAI;IACZ,qBAAqB,EAAE,IAAI;IAC3B,iBAAiB,EAAE,IAAI;IACvB,wBAAwB,EAAE,IAAI;IAC9B,uBAAuB,EAAE,IAAI;IAC7B,SAAS,EAAE,IAAI;IACf,QAAQ,EAAE,IAAI;CACR,CAAA"}
|
|
1
|
+
{"version":3,"file":"errors.helper.js","sourceRoot":"","sources":["../../helpers/errors.helper.ts"],"names":[],"mappings":";;;AAAa,QAAA,UAAU,GAAG;IACtB,cAAc,EAAE,IAAI;IACpB,gBAAgB,EAAE,IAAI;IACtB,cAAc,EAAE,IAAI;IACpB,gBAAgB,EAAE,IAAI;IACtB,mBAAmB,EAAE,IAAI;IACzB,gBAAgB,EAAE,IAAI;IACtB,cAAc,EAAE,IAAI;IACpB,MAAM,EAAE,IAAI;IACZ,qBAAqB,EAAE,IAAI;IAC3B,iBAAiB,EAAE,IAAI;IACvB,wBAAwB,EAAE,IAAI;IAC9B,uBAAuB,EAAE,IAAI;IAC7B,sBAAsB,EAAE,IAAI;IAC5B,SAAS,EAAE,IAAI;IACf,QAAQ,EAAE,IAAI;CACR,CAAA"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import Big from 'big.js';
|
|
2
|
+
export { default as Big } from 'big.js';
|
|
3
|
+
/**
|
|
4
|
+
* Rounds a monetary value to `decimals` places using exact decimal arithmetic.
|
|
5
|
+
* Returns an exact-decimal number safe to put on the wire (default mode: round half up).
|
|
6
|
+
*/
|
|
7
|
+
export declare function roundMoney(value: number | string, decimals: number, mode?: Big.RoundingMode): number;
|
|
8
|
+
/**
|
|
9
|
+
* true if `value` is finite and has no more than `decimals` decimal places
|
|
10
|
+
* (i.e. it is an exact representation for a currency with `decimals` precision).
|
|
11
|
+
*/
|
|
12
|
+
export declare function isExactDecimal(value: number, decimals: number): boolean;
|
|
13
|
+
//# sourceMappingURL=money.helper.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"money.helper.d.ts","sourceRoot":"","sources":["../../helpers/money.helper.ts"],"names":[],"mappings":"AAAA,OAAO,GAAG,MAAM,QAAQ,CAAA;AAExB,OAAO,EAAE,OAAO,IAAI,GAAG,EAAE,MAAM,QAAQ,CAAA;AAEvC;;;GAGG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,GAAE,GAAG,CAAC,YAA8B,GAAG,MAAM,CAErH;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAIvE"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.Big = void 0;
|
|
7
|
+
exports.roundMoney = roundMoney;
|
|
8
|
+
exports.isExactDecimal = isExactDecimal;
|
|
9
|
+
const big_js_1 = __importDefault(require("big.js"));
|
|
10
|
+
var big_js_2 = require("big.js");
|
|
11
|
+
Object.defineProperty(exports, "Big", { enumerable: true, get: function () { return __importDefault(big_js_2).default; } });
|
|
12
|
+
/**
|
|
13
|
+
* Rounds a monetary value to `decimals` places using exact decimal arithmetic.
|
|
14
|
+
* Returns an exact-decimal number safe to put on the wire (default mode: round half up).
|
|
15
|
+
*/
|
|
16
|
+
function roundMoney(value, decimals, mode = big_js_1.default.roundHalfUp) {
|
|
17
|
+
return Number(new big_js_1.default(value).round(decimals, mode).toString());
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* true if `value` is finite and has no more than `decimals` decimal places
|
|
21
|
+
* (i.e. it is an exact representation for a currency with `decimals` precision).
|
|
22
|
+
*/
|
|
23
|
+
function isExactDecimal(value, decimals) {
|
|
24
|
+
if (!Number.isFinite(value))
|
|
25
|
+
return false;
|
|
26
|
+
const big = new big_js_1.default(value.toString());
|
|
27
|
+
return big.round(decimals, big_js_1.default.roundHalfUp).eq(big);
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=money.helper.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"money.helper.js","sourceRoot":"","sources":["../../helpers/money.helper.ts"],"names":[],"mappings":";;;;;;AAQA,gCAEC;AAMD,wCAIC;AApBD,oDAAwB;AAExB,iCAAuC;AAA9B,8GAAA,OAAO,OAAO;AAEvB;;;GAGG;AACH,SAAgB,UAAU,CAAC,KAAsB,EAAE,QAAgB,EAAE,OAAyB,gBAAG,CAAC,WAAW;IACzG,OAAO,MAAM,CAAC,IAAI,gBAAG,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAA;AAClE,CAAC;AAED;;;GAGG;AACH,SAAgB,cAAc,CAAC,KAAa,EAAE,QAAgB;IAC1D,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAA;IACzC,MAAM,GAAG,GAAG,IAAI,gBAAG,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC,CAAA;IACrC,OAAO,GAAG,CAAC,KAAK,CAAC,QAAQ,EAAE,gBAAG,CAAC,WAAW,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,CAAA;AACvD,CAAC"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Resolve package.json from either compiled output (`dist/helpers/…`) or source (`helpers
|
|
2
|
+
* Resolve package.json from either compiled output (`dist/helpers/…`) or TypeScript source (`helpers/…`, e.g. under Vitest).
|
|
3
3
|
*/
|
|
4
4
|
export declare function getPackageVersion(): string;
|
|
5
5
|
//# sourceMappingURL=pkg-version.helper.d.ts.map
|
|
@@ -4,7 +4,7 @@ exports.getPackageVersion = getPackageVersion;
|
|
|
4
4
|
const fs_1 = require("fs");
|
|
5
5
|
const path_1 = require("path");
|
|
6
6
|
/**
|
|
7
|
-
* Resolve package.json from either compiled output (`dist/helpers/…`) or source (`helpers
|
|
7
|
+
* Resolve package.json from either compiled output (`dist/helpers/…`) or TypeScript source (`helpers/…`, e.g. under Vitest).
|
|
8
8
|
*/
|
|
9
9
|
function getPackageVersion() {
|
|
10
10
|
const candidates = [
|
package/dist/index.d.ts
CHANGED
|
@@ -5,6 +5,7 @@ export { EErrorCode, type TErrorCode, } from './helpers/errors.helper';
|
|
|
5
5
|
export { type FastifyTypebox, type FastifyError$Coded, getRoutePrefix, httpError, registerPlugin, registerRoute, } from './helpers/fastify.helper';
|
|
6
6
|
export { type RouteHandler, type TRtpParams, HandlersHelper, handlers, } from './helpers/handlers.helper';
|
|
7
7
|
export { logger, loggerOptions } from './helpers/logger.helper';
|
|
8
|
+
export { Big, roundMoney, isExactDecimal } from './helpers/money.helper';
|
|
8
9
|
export { getByChance, random, randomOf } from './helpers/random.helper';
|
|
9
10
|
export { ChecksumReplySchema, ChecksumRequestSchema, type TChecksumReply, type TChecksumRequest, } from './schemas/checksum.schema';
|
|
10
11
|
export { ClientReplySchema, ClientRequestSchema, type TClientReply, type TClientRequest, } from './schemas/client.schema';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,eAAe,CAAA;AACtD,OAAO,EACH,aAAa,EACb,kBAAkB,EAClB,KAAK,WAAW,GACnB,MAAM,yBAAyB,CAAA;AAChC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAA;AACxD,OAAO,EACH,UAAU,EACV,KAAK,UAAU,GAClB,MAAM,yBAAyB,CAAA;AAChC,OAAO,EACH,KAAK,cAAc,EACnB,KAAK,kBAAkB,EACvB,cAAc,EACd,SAAS,EACT,cAAc,EACd,aAAa,GAChB,MAAM,0BAA0B,CAAA;AACjC,OAAO,EACH,KAAK,YAAY,EACjB,KAAK,UAAU,EACf,cAAc,EACd,QAAQ,GACX,MAAM,2BAA2B,CAAA;AAClC,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAA;AAC/D,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAA;AAEvE,OAAO,EACH,mBAAmB,EACnB,qBAAqB,EACrB,KAAK,cAAc,EACnB,KAAK,gBAAgB,GACxB,MAAM,2BAA2B,CAAA;AAClC,OAAO,EACH,iBAAiB,EACjB,mBAAmB,EACnB,KAAK,YAAY,EACjB,KAAK,cAAc,GACtB,MAAM,yBAAyB,CAAA;AAChC,OAAO,EACH,iBAAiB,EACjB,mBAAmB,EACnB,KAAK,YAAY,EACjB,KAAK,cAAc,GACtB,MAAM,yBAAyB,CAAA;AAChC,OAAO,EACH,sBAAsB,EACtB,wBAAwB,EACxB,KAAK,iBAAiB,EACtB,KAAK,mBAAmB,GAC3B,MAAM,8BAA8B,CAAA;AACrC,OAAO,EACH,eAAe,EACf,iBAAiB,EACjB,KAAK,UAAU,EACf,KAAK,YAAY,GACpB,MAAM,uBAAuB,CAAA;AAC9B,OAAO,EACH,iBAAiB,EACjB,YAAY,EACZ,UAAU,EACV,aAAa,EACb,mBAAmB,EACnB,UAAU,EACV,kBAAkB,EAClB,eAAe,EACf,WAAW,EACX,KAAK,YAAY,EACjB,KAAK,SAAS,EACd,KAAK,cAAc,EACnB,KAAK,KAAK,EACV,KAAK,aAAa,EAClB,KAAK,MAAM,EACX,KAAK,UAAU,GAClB,MAAM,kBAAkB,CAAA;AACzB,OAAO,EACH,mBAAmB,EACnB,qBAAqB,EACrB,KAAK,cAAc,EACnB,KAAK,gBAAgB,GACxB,MAAM,2BAA2B,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,eAAe,CAAA;AACtD,OAAO,EACH,aAAa,EACb,kBAAkB,EAClB,KAAK,WAAW,GACnB,MAAM,yBAAyB,CAAA;AAChC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAA;AACxD,OAAO,EACH,UAAU,EACV,KAAK,UAAU,GAClB,MAAM,yBAAyB,CAAA;AAChC,OAAO,EACH,KAAK,cAAc,EACnB,KAAK,kBAAkB,EACvB,cAAc,EACd,SAAS,EACT,cAAc,EACd,aAAa,GAChB,MAAM,0BAA0B,CAAA;AACjC,OAAO,EACH,KAAK,YAAY,EACjB,KAAK,UAAU,EACf,cAAc,EACd,QAAQ,GACX,MAAM,2BAA2B,CAAA;AAClC,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAA;AAC/D,OAAO,EAAE,GAAG,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAA;AACxE,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAA;AAEvE,OAAO,EACH,mBAAmB,EACnB,qBAAqB,EACrB,KAAK,cAAc,EACnB,KAAK,gBAAgB,GACxB,MAAM,2BAA2B,CAAA;AAClC,OAAO,EACH,iBAAiB,EACjB,mBAAmB,EACnB,KAAK,YAAY,EACjB,KAAK,cAAc,GACtB,MAAM,yBAAyB,CAAA;AAChC,OAAO,EACH,iBAAiB,EACjB,mBAAmB,EACnB,KAAK,YAAY,EACjB,KAAK,cAAc,GACtB,MAAM,yBAAyB,CAAA;AAChC,OAAO,EACH,sBAAsB,EACtB,wBAAwB,EACxB,KAAK,iBAAiB,EACtB,KAAK,mBAAmB,GAC3B,MAAM,8BAA8B,CAAA;AACrC,OAAO,EACH,eAAe,EACf,iBAAiB,EACjB,KAAK,UAAU,EACf,KAAK,YAAY,GACpB,MAAM,uBAAuB,CAAA;AAC9B,OAAO,EACH,iBAAiB,EACjB,YAAY,EACZ,UAAU,EACV,aAAa,EACb,mBAAmB,EACnB,UAAU,EACV,kBAAkB,EAClB,eAAe,EACf,WAAW,EACX,KAAK,YAAY,EACjB,KAAK,SAAS,EACd,KAAK,cAAc,EACnB,KAAK,KAAK,EACV,KAAK,aAAa,EAClB,KAAK,MAAM,EACX,KAAK,UAAU,GAClB,MAAM,kBAAkB,CAAA;AACzB,OAAO,EACH,mBAAmB,EACnB,qBAAqB,EACrB,KAAK,cAAc,EACnB,KAAK,gBAAgB,GACxB,MAAM,2BAA2B,CAAA"}
|
package/dist/index.js
CHANGED
|
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.ValidateRequestSchema = exports.ValidateReplySchema = exports.StakeSchema = exports.StakeModeSchema = exports.RequestErrorSchema = exports.MetaSchema = exports.InternalErrorSchema = exports.HeadersSchema = exports.EStakeType = exports.EEnvironment = exports.AccessErrorSchema = exports.PlayRequestSchema = exports.PlayReplySchema = exports.HealthcheckRequestSchema = exports.HealthcheckReplySchema = exports.ConfigRequestSchema = exports.ConfigReplySchema = exports.ClientRequestSchema = exports.ClientReplySchema = exports.ChecksumRequestSchema = exports.ChecksumReplySchema = exports.randomOf = exports.random = exports.getByChance = exports.loggerOptions = exports.logger = exports.handlers = exports.HandlersHelper = exports.registerRoute = exports.registerPlugin = exports.httpError = exports.getRoutePrefix = exports.EErrorCode = exports.variation = exports.getenv = exports.registerGameConfig = exports.getGameConfig = exports.Application = void 0;
|
|
6
|
+
exports.ValidateRequestSchema = exports.ValidateReplySchema = exports.StakeSchema = exports.StakeModeSchema = exports.RequestErrorSchema = exports.MetaSchema = exports.InternalErrorSchema = exports.HeadersSchema = exports.EStakeType = exports.EEnvironment = exports.AccessErrorSchema = exports.PlayRequestSchema = exports.PlayReplySchema = exports.HealthcheckRequestSchema = exports.HealthcheckReplySchema = exports.ConfigRequestSchema = exports.ConfigReplySchema = exports.ClientRequestSchema = exports.ClientReplySchema = exports.ChecksumRequestSchema = exports.ChecksumReplySchema = exports.randomOf = exports.random = exports.getByChance = exports.isExactDecimal = exports.roundMoney = exports.Big = exports.loggerOptions = exports.logger = exports.handlers = exports.HandlersHelper = exports.registerRoute = exports.registerPlugin = exports.httpError = exports.getRoutePrefix = exports.EErrorCode = exports.variation = exports.getenv = exports.registerGameConfig = exports.getGameConfig = exports.Application = void 0;
|
|
7
7
|
var application_1 = require("./application");
|
|
8
8
|
Object.defineProperty(exports, "Application", { enumerable: true, get: function () { return __importDefault(application_1).default; } });
|
|
9
9
|
var config_helper_1 = require("./helpers/config.helper");
|
|
@@ -25,6 +25,10 @@ Object.defineProperty(exports, "handlers", { enumerable: true, get: function ()
|
|
|
25
25
|
var logger_helper_1 = require("./helpers/logger.helper");
|
|
26
26
|
Object.defineProperty(exports, "logger", { enumerable: true, get: function () { return logger_helper_1.logger; } });
|
|
27
27
|
Object.defineProperty(exports, "loggerOptions", { enumerable: true, get: function () { return logger_helper_1.loggerOptions; } });
|
|
28
|
+
var money_helper_1 = require("./helpers/money.helper");
|
|
29
|
+
Object.defineProperty(exports, "Big", { enumerable: true, get: function () { return money_helper_1.Big; } });
|
|
30
|
+
Object.defineProperty(exports, "roundMoney", { enumerable: true, get: function () { return money_helper_1.roundMoney; } });
|
|
31
|
+
Object.defineProperty(exports, "isExactDecimal", { enumerable: true, get: function () { return money_helper_1.isExactDecimal; } });
|
|
28
32
|
var random_helper_1 = require("./helpers/random.helper");
|
|
29
33
|
Object.defineProperty(exports, "getByChance", { enumerable: true, get: function () { return random_helper_1.getByChance; } });
|
|
30
34
|
Object.defineProperty(exports, "random", { enumerable: true, get: function () { return random_helper_1.random; } });
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":";;;;;;AAAA,6CAAsD;AAA7C,2HAAA,OAAO,OAAe;AAC/B,yDAIgC;AAH5B,8GAAA,aAAa,OAAA;AACb,mHAAA,kBAAkB,OAAA;AAGtB,mDAAwD;AAA/C,oGAAA,MAAM,OAAA;AAAE,uGAAA,SAAS,OAAA;AAC1B,yDAGgC;AAF5B,2GAAA,UAAU,OAAA;AAGd,2DAOiC;AAJ7B,gHAAA,cAAc,OAAA;AACd,2GAAA,SAAS,OAAA;AACT,gHAAA,cAAc,OAAA;AACd,+GAAA,aAAa,OAAA;AAEjB,6DAKkC;AAF9B,iHAAA,cAAc,OAAA;AACd,2GAAA,QAAQ,OAAA;AAEZ,yDAA+D;AAAtD,uGAAA,MAAM,OAAA;AAAE,8GAAA,aAAa,OAAA;AAC9B,yDAAuE;AAA9D,4GAAA,WAAW,OAAA;AAAE,uGAAA,MAAM,OAAA;AAAE,yGAAA,QAAQ,OAAA;AAEtC,6DAKkC;AAJ9B,sHAAA,mBAAmB,OAAA;AACnB,wHAAA,qBAAqB,OAAA;AAIzB,yDAKgC;AAJ5B,kHAAA,iBAAiB,OAAA;AACjB,oHAAA,mBAAmB,OAAA;AAIvB,yDAKgC;AAJ5B,kHAAA,iBAAiB,OAAA;AACjB,oHAAA,mBAAmB,OAAA;AAIvB,mEAKqC;AAJjC,4HAAA,sBAAsB,OAAA;AACtB,8HAAA,wBAAwB,OAAA;AAI5B,qDAK8B;AAJ1B,8GAAA,eAAe,OAAA;AACf,gHAAA,iBAAiB,OAAA;AAIrB,2CAiByB;AAhBrB,2GAAA,iBAAiB,OAAA;AACjB,sGAAA,YAAY,OAAA;AACZ,oGAAA,UAAU,OAAA;AACV,uGAAA,aAAa,OAAA;AACb,6GAAA,mBAAmB,OAAA;AACnB,oGAAA,UAAU,OAAA;AACV,4GAAA,kBAAkB,OAAA;AAClB,yGAAA,eAAe,OAAA;AACf,qGAAA,WAAW,OAAA;AASf,6DAKkC;AAJ9B,sHAAA,mBAAmB,OAAA;AACnB,wHAAA,qBAAqB,OAAA"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":";;;;;;AAAA,6CAAsD;AAA7C,2HAAA,OAAO,OAAe;AAC/B,yDAIgC;AAH5B,8GAAA,aAAa,OAAA;AACb,mHAAA,kBAAkB,OAAA;AAGtB,mDAAwD;AAA/C,oGAAA,MAAM,OAAA;AAAE,uGAAA,SAAS,OAAA;AAC1B,yDAGgC;AAF5B,2GAAA,UAAU,OAAA;AAGd,2DAOiC;AAJ7B,gHAAA,cAAc,OAAA;AACd,2GAAA,SAAS,OAAA;AACT,gHAAA,cAAc,OAAA;AACd,+GAAA,aAAa,OAAA;AAEjB,6DAKkC;AAF9B,iHAAA,cAAc,OAAA;AACd,2GAAA,QAAQ,OAAA;AAEZ,yDAA+D;AAAtD,uGAAA,MAAM,OAAA;AAAE,8GAAA,aAAa,OAAA;AAC9B,uDAAwE;AAA/D,mGAAA,GAAG,OAAA;AAAE,0GAAA,UAAU,OAAA;AAAE,8GAAA,cAAc,OAAA;AACxC,yDAAuE;AAA9D,4GAAA,WAAW,OAAA;AAAE,uGAAA,MAAM,OAAA;AAAE,yGAAA,QAAQ,OAAA;AAEtC,6DAKkC;AAJ9B,sHAAA,mBAAmB,OAAA;AACnB,wHAAA,qBAAqB,OAAA;AAIzB,yDAKgC;AAJ5B,kHAAA,iBAAiB,OAAA;AACjB,oHAAA,mBAAmB,OAAA;AAIvB,yDAKgC;AAJ5B,kHAAA,iBAAiB,OAAA;AACjB,oHAAA,mBAAmB,OAAA;AAIvB,mEAKqC;AAJjC,4HAAA,sBAAsB,OAAA;AACtB,8HAAA,wBAAwB,OAAA;AAI5B,qDAK8B;AAJ1B,8GAAA,eAAe,OAAA;AACf,gHAAA,iBAAiB,OAAA;AAIrB,2CAiByB;AAhBrB,2GAAA,iBAAiB,OAAA;AACjB,sGAAA,YAAY,OAAA;AACZ,oGAAA,UAAU,OAAA;AACV,uGAAA,aAAa,OAAA;AACb,6GAAA,mBAAmB,OAAA;AACnB,oGAAA,UAAU,OAAA;AACV,4GAAA,kBAAkB,OAAA;AAClB,yGAAA,eAAe,OAAA;AACf,qGAAA,WAAW,OAAA;AASf,6DAKkC;AAJ9B,sHAAA,mBAAmB,OAAA;AACnB,wHAAA,qBAAqB,OAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sentry.plugin.d.ts","sourceRoot":"","sources":["../../plugins/sentry.plugin.ts"],"names":[],"mappings":"AAMA,QAAA,MAAM,YAAY,
|
|
1
|
+
{"version":3,"file":"sentry.plugin.d.ts","sourceRoot":"","sources":["../../plugins/sentry.plugin.ts"],"names":[],"mappings":"AAMA,QAAA,MAAM,YAAY,gFAahB,CAAA;AAEF,eAAe,YAAY,CAAA"}
|
|
@@ -16,7 +16,6 @@ const sentryPlugin = (0, fastify_helper_1.registerPlugin)(async (fastify) => {
|
|
|
16
16
|
setErrorHandler: false,
|
|
17
17
|
});
|
|
18
18
|
fastify.addHook('onReady', () => {
|
|
19
|
-
fastify.Sentry.setTag('game.title', gameConfig.gameTitle);
|
|
20
19
|
fastify.Sentry.setTag('game.id', gameConfig.gameId);
|
|
21
20
|
});
|
|
22
21
|
});
|