@truelab/trueserver 0.0.6 → 0.1.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.
Files changed (58) hide show
  1. package/CHANGELOG.md +34 -0
  2. package/MIGRATION.md +206 -1
  3. package/README.md +37 -15
  4. package/dist/helpers/config.helper.d.ts +8 -3
  5. package/dist/helpers/config.helper.d.ts.map +1 -1
  6. package/dist/helpers/config.helper.js +11 -8
  7. package/dist/helpers/config.helper.js.map +1 -1
  8. package/dist/helpers/errors.helper.d.ts +1 -0
  9. package/dist/helpers/errors.helper.d.ts.map +1 -1
  10. package/dist/helpers/errors.helper.js +1 -0
  11. package/dist/helpers/errors.helper.js.map +1 -1
  12. package/dist/helpers/money.helper.d.ts +13 -0
  13. package/dist/helpers/money.helper.d.ts.map +1 -0
  14. package/dist/helpers/money.helper.js +29 -0
  15. package/dist/helpers/money.helper.js.map +1 -0
  16. package/dist/index.d.ts +1 -0
  17. package/dist/index.d.ts.map +1 -1
  18. package/dist/index.js +5 -1
  19. package/dist/index.js.map +1 -1
  20. package/dist/plugins/sentry.plugin.d.ts.map +1 -1
  21. package/dist/plugins/sentry.plugin.js +0 -1
  22. package/dist/plugins/sentry.plugin.js.map +1 -1
  23. package/dist/plugins/swagger.plugin.js +1 -1
  24. package/dist/plugins/swagger.plugin.js.map +1 -1
  25. package/dist/routes/config/config.route.validate.plugin.d.ts.map +1 -1
  26. package/dist/routes/config/config.route.validate.plugin.js +15 -10
  27. package/dist/routes/config/config.route.validate.plugin.js.map +1 -1
  28. package/dist/routes/play/play.route.validate.plugin.d.ts +5 -4
  29. package/dist/routes/play/play.route.validate.plugin.d.ts.map +1 -1
  30. package/dist/routes/play/play.route.validate.plugin.js +21 -14
  31. package/dist/routes/play/play.route.validate.plugin.js.map +1 -1
  32. package/dist/routes/validate/validate.route.validate.plugin.d.ts.map +1 -1
  33. package/dist/routes/validate/validate.route.validate.plugin.js +2 -7
  34. package/dist/routes/validate/validate.route.validate.plugin.js.map +1 -1
  35. package/dist/schemas/config.schema.d.ts +3 -3
  36. package/dist/schemas/config.schema.js +8 -8
  37. package/dist/schemas/config.schema.js.map +1 -1
  38. package/dist/schemas/examples/config.schema.examples.d.ts.map +1 -1
  39. package/dist/schemas/examples/config.schema.examples.js +48 -12
  40. package/dist/schemas/examples/config.schema.examples.js.map +1 -1
  41. package/dist/schemas/examples/play.schema.examples.d.ts.map +1 -1
  42. package/dist/schemas/examples/play.schema.examples.js +41 -13
  43. package/dist/schemas/examples/play.schema.examples.js.map +1 -1
  44. package/dist/schemas/examples/validate.schema.examples.d.ts.map +1 -1
  45. package/dist/schemas/examples/validate.schema.examples.js +51 -39
  46. package/dist/schemas/examples/validate.schema.examples.js.map +1 -1
  47. package/dist/schemas/play.schema.d.ts +11 -11
  48. package/dist/schemas/play.schema.d.ts.map +1 -1
  49. package/dist/schemas/play.schema.js +22 -21
  50. package/dist/schemas/play.schema.js.map +1 -1
  51. package/dist/schemas/shared.d.ts +1 -1
  52. package/dist/schemas/shared.js +4 -4
  53. package/dist/schemas/shared.js.map +1 -1
  54. package/dist/schemas/validate.schema.d.ts +12 -7
  55. package/dist/schemas/validate.schema.d.ts.map +1 -1
  56. package/dist/schemas/validate.schema.js +17 -16
  57. package/dist/schemas/validate.schema.js.map +1 -1
  58. package/package.json +3 -1
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.5 → 0.0.6](#migration-005--006) (последнее)
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
- hasVariableLines: false,
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
- hasVariableLines: false,
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
- results: {
84
- cashWin: 0,
85
- finished: true,
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. `hasValidation` is a legacy flag — kept for backward compatibility.
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`, or `hasVariableLines=true` without `variableLevels`/`defaultLevel`).
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', ...)` | Legacy stake/meta pre-validation (off by default) |
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
 
@@ -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 and the `hasVariableLines ⇔ {variableLevels, defaultLevel}` pair.
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 `hasValidation=true`.
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' | 'gameTitle' | 'rtpVersion' | 'stakeModes' | 'hasMeta' | 'hasChoice' | 'hasRestore' | 'hasMultiStake' | 'hasVariableLines' | 'variableLevels' | 'defaultLevel' | 'coinRatio'> & {
3
- /** Kept for /validate endpoint (not part of GS Documentation spec) */
4
- hasValidation?: boolean;
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,WAAW,GACX,YAAY,GACZ,YAAY,GACZ,SAAS,GACT,WAAW,GACX,YAAY,GACZ,eAAe,GACf,kBAAkB,GAClB,gBAAgB,GAChB,cAAc,GACd,WAAW,CACV,GAAG;IACA,sEAAsE;IACtE,aAAa,CAAC,EAAE,OAAO,CAAC;CAC3B,CAAA;AAID,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,WAAW,QA6BrD;AAED,wBAAgB,aAAa,IAAI,WAAW,CAE3C"}
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.hasVariableLines) {
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 hasVariableLines=true', 500);
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 hasVariableLines=true', 500);
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 hasVariableLines=true', 500);
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 hasVariableLines=false', 500);
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 hasVariableLines=false', 500);
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":";;AAuBA,gDA6BC;AAED,sCAEC;AAvDD,qDAA4C;AAoB5C,IAAI,UAAuB,CAAA;AAE3B,SAAgB,kBAAkB,CAAC,MAAmB;IAClD,IAAI,OAAO,MAAM,CAAC,UAAU,KAAK,QAAQ,IAAI,MAAM,CAAC,UAAU,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC1E,MAAM,IAAA,0BAAS,EAAC,oDAAoD,EAAE,GAAG,CAAC,CAAA;IAC9E,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,gBAAgB,EAAE,CAAC;QAC1B,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,kFAAkF,EAAE,GAAG,CAAC,CAAA;QAC5G,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,oFAAoF,EAAE,GAAG,CAAC,CAAA;QAC9G,CAAC;QACD,IAAI,MAAM,CAAC,SAAS,KAAK,SAAS,EAAE,CAAC;YACjC,MAAM,IAAA,0BAAS,EAAC,mEAAmE,EAAE,GAAG,CAAC,CAAA;QAC7F,CAAC;IACL,CAAC;SAAM,CAAC;QACJ,IAAI,MAAM,CAAC,SAAS,KAAK,SAAS,EAAE,CAAC;YACjC,MAAM,IAAA,0BAAS,EAAC,gEAAgE,EAAE,GAAG,CAAC,CAAA;QAC1F,CAAC;QACD,IAAI,MAAM,CAAC,cAAc,KAAK,SAAS,IAAI,MAAM,CAAC,YAAY,KAAK,SAAS,EAAE,CAAC;YAC3E,MAAM,IAAA,0BAAS,EAAC,sFAAsF,EAAE,GAAG,CAAC,CAAA;QAChH,CAAC;IACL,CAAC;IAED,UAAU,GAAG,MAAM,CAAA;AACvB,CAAC;AAED,SAAgB,aAAa;IACzB,OAAO,UAAU,CAAA;AACrB,CAAC"}
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;;;;;;;;;;;;;;;CAeb,CAAA;AAEV,MAAM,MAAM,UAAU,GAAG,CAAC,OAAO,UAAU,CAAC,CAAC,MAAM,OAAO,UAAU,CAAC,CAAA"}
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"}
@@ -14,6 +14,7 @@ exports.EErrorCode = {
14
14
  BusinessMetaShape: 4002,
15
15
  BusinessFinishedMismatch: 4003,
16
16
  BusinessChoicesMismatch: 4004,
17
+ BusinessMaxWinMismatch: 4005,
17
18
  Unhandled: 9001,
18
19
  NotFound: 9404,
19
20
  };
@@ -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"}
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';
@@ -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,gFAchB,CAAA;AAEF,eAAe,YAAY,CAAA"}
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
  });
@@ -1 +1 @@
1
- {"version":3,"file":"sentry.plugin.js","sourceRoot":"","sources":["../../plugins/sentry.plugin.ts"],"names":[],"mappings":";;;;;AAAA,qFAA2D;AAE3D,4DAAwD;AACxD,sDAAyD;AACzD,8DAA0D;AAE1D,MAAM,YAAY,GAAG,IAAA,+BAAc,EAAC,KAAK,EAAE,OAAO,EAAE,EAAE;IAClD,MAAM,UAAU,GAAG,IAAA,6BAAa,GAAE,CAAA;IAElC,OAAO,CAAC,QAAQ,CAAC,wBAAa,EAAE;QAC5B,GAAG,EAAE,IAAA,mBAAM,EAAC,YAAY,EAAE,SAAS,CAAC;QACpC,WAAW,EAAE,IAAA,sBAAS,EAAC,aAAa,EAAE,SAAS,EAAE,YAAY,CAAC;QAC9D,gBAAgB,EAAE,UAAU,CAAC,IAAA,mBAAM,EAAC,aAAa,EAAE,MAAM,CAAC,CAAC,EAAE,gBAAgB;QAC7E,eAAe,EAAE,KAAK;KACzB,CAAC,CAAA;IAEF,OAAO,CAAC,OAAO,CAAC,SAAS,EAAE,GAAG,EAAE;QAC5B,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,YAAY,EAAE,UAAU,CAAC,SAAS,CAAC,CAAA;QACzD,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,SAAS,EAAE,UAAU,CAAC,MAAM,CAAC,CAAA;IACvD,CAAC,CAAC,CAAA;AACN,CAAC,CAAC,CAAA;AAEF,kBAAe,YAAY,CAAA"}
1
+ {"version":3,"file":"sentry.plugin.js","sourceRoot":"","sources":["../../plugins/sentry.plugin.ts"],"names":[],"mappings":";;;;;AAAA,qFAA2D;AAE3D,4DAAwD;AACxD,sDAAyD;AACzD,8DAA0D;AAE1D,MAAM,YAAY,GAAG,IAAA,+BAAc,EAAC,KAAK,EAAE,OAAO,EAAE,EAAE;IAClD,MAAM,UAAU,GAAG,IAAA,6BAAa,GAAE,CAAA;IAElC,OAAO,CAAC,QAAQ,CAAC,wBAAa,EAAE;QAC5B,GAAG,EAAE,IAAA,mBAAM,EAAC,YAAY,EAAE,SAAS,CAAC;QACpC,WAAW,EAAE,IAAA,sBAAS,EAAC,aAAa,EAAE,SAAS,EAAE,YAAY,CAAC;QAC9D,gBAAgB,EAAE,UAAU,CAAC,IAAA,mBAAM,EAAC,aAAa,EAAE,MAAM,CAAC,CAAC,EAAE,gBAAgB;QAC7E,eAAe,EAAE,KAAK;KACzB,CAAC,CAAA;IAEF,OAAO,CAAC,OAAO,CAAC,SAAS,EAAE,GAAG,EAAE;QAC5B,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,SAAS,EAAE,UAAU,CAAC,MAAM,CAAC,CAAA;IACvD,CAAC,CAAC,CAAA;AACN,CAAC,CAAC,CAAA;AAEF,kBAAe,YAAY,CAAA"}
@@ -38,7 +38,7 @@ const swaggerPlugin = (0, fastify_helper_1.registerPlugin)(async (fastify) => {
38
38
  deepLinking: false,
39
39
  },
40
40
  theme: {
41
- title: `Swagger - ${gameConfig.gameTitle}`,
41
+ title: `Swagger - ${gameConfig.gameId}`,
42
42
  },
43
43
  });
44
44
  fastify.addSchema(shared_1.StakeSchema);
@@ -1 +1 @@
1
- {"version":3,"file":"swagger.plugin.js","sourceRoot":"","sources":["../../plugins/swagger.plugin.ts"],"names":[],"mappings":";;;;;AAAA,+DAA6C;AAC7C,qEAAkD;AAElD,4DAAwD;AACxD,sDAAiD;AACjD,8DAA0D;AAC1D,sEAAiE;AACjE,gEAAuF;AACvF,4DAAiF;AACjF,4DAAiF;AACjF,wDAA2E;AAC3E,8CAE0B;AAC1B,gEAAuF;AAEvF,MAAM,aAAa,GAAG,IAAA,+BAAc,EAAC,KAAK,EAAE,OAAO,EAAE,EAAE;IACnD,IAAI,IAAA,sBAAS,EAAC,KAAK,EAAE,IAAI,EAAE,IAAI,CAAC;QAAE,OAAM;IAExC,MAAM,UAAU,GAAG,IAAA,6BAAa,GAAE,CAAA;IAClC,MAAM,WAAW,GAAG,OAAO,CAAA;IAE3B,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,KAAK,EAAE,EAAE;QACjC,KAAK,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAA;IAC/B,CAAC,CAAC,CAAA;IAEF,OAAO,CAAC,QAAQ,CAAC,iBAAc,EAAE;QAC7B,OAAO,EAAE;YACL,IAAI,EAAE;gBACF,KAAK,EAAE,qBAAqB;gBAC5B,OAAO,EAAE,IAAA,sCAAiB,GAAE;aAC/B;SACJ;KACJ,CAAC,CAAA;IAEF,OAAO,CAAC,QAAQ,CAAC,oBAAgB,EAAE;QAC/B,WAAW;QACX,QAAQ,EAAE;YACN,YAAY,EAAE,MAAM;YACpB,WAAW,EAAE,KAAK;SACrB;QACD,KAAK,EAAE;YACH,KAAK,EAAE,aAAa,UAAU,CAAC,SAAS,EAAE;SAC7C;KACJ,CAAC,CAAA;IAEF,OAAO,CAAC,SAAS,CAAC,oBAAW,CAAC,CAAA;IAC9B,OAAO,CAAC,SAAS,CAAC,wBAAe,CAAC,CAAA;IAClC,OAAO,CAAC,SAAS,CAAC,mBAAU,CAAC,CAAA;IAC7B,OAAO,CAAC,SAAS,CAAC,2BAAkB,CAAC,CAAA;IACrC,OAAO,CAAC,SAAS,CAAC,0BAAiB,CAAC,CAAA;IACpC,OAAO,CAAC,SAAS,CAAC,4BAAmB,CAAC,CAAA;IACtC,OAAO,CAAC,SAAS,CAAC,+BAAiB,CAAC,CAAA;IACpC,OAAO,CAAC,SAAS,CAAC,6BAAe,CAAC,CAAA;IAClC,OAAO,CAAC,SAAS,CAAC,uCAAqB,CAAC,CAAA;IACxC,OAAO,CAAC,SAAS,CAAC,qCAAmB,CAAC,CAAA;IACtC,OAAO,CAAC,SAAS,CAAC,mCAAmB,CAAC,CAAA;IACtC,OAAO,CAAC,SAAS,CAAC,iCAAiB,CAAC,CAAA;IACpC,OAAO,CAAC,SAAS,CAAC,mCAAmB,CAAC,CAAA;IACtC,OAAO,CAAC,SAAS,CAAC,iCAAiB,CAAC,CAAA;IACpC,OAAO,CAAC,SAAS,CAAC,uCAAqB,CAAC,CAAA;IACxC,OAAO,CAAC,SAAS,CAAC,qCAAmB,CAAC,CAAA;AAC1C,CAAC,CAAC,CAAA;AAEF,kBAAe,aAAa,CAAA"}
1
+ {"version":3,"file":"swagger.plugin.js","sourceRoot":"","sources":["../../plugins/swagger.plugin.ts"],"names":[],"mappings":";;;;;AAAA,+DAA6C;AAC7C,qEAAkD;AAElD,4DAAwD;AACxD,sDAAiD;AACjD,8DAA0D;AAC1D,sEAAiE;AACjE,gEAAuF;AACvF,4DAAiF;AACjF,4DAAiF;AACjF,wDAA2E;AAC3E,8CAE0B;AAC1B,gEAAuF;AAEvF,MAAM,aAAa,GAAG,IAAA,+BAAc,EAAC,KAAK,EAAE,OAAO,EAAE,EAAE;IACnD,IAAI,IAAA,sBAAS,EAAC,KAAK,EAAE,IAAI,EAAE,IAAI,CAAC;QAAE,OAAM;IAExC,MAAM,UAAU,GAAG,IAAA,6BAAa,GAAE,CAAA;IAClC,MAAM,WAAW,GAAG,OAAO,CAAA;IAE3B,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,KAAK,EAAE,EAAE;QACjC,KAAK,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAA;IAC/B,CAAC,CAAC,CAAA;IAEF,OAAO,CAAC,QAAQ,CAAC,iBAAc,EAAE;QAC7B,OAAO,EAAE;YACL,IAAI,EAAE;gBACF,KAAK,EAAE,qBAAqB;gBAC5B,OAAO,EAAE,IAAA,sCAAiB,GAAE;aAC/B;SACJ;KACJ,CAAC,CAAA;IAEF,OAAO,CAAC,QAAQ,CAAC,oBAAgB,EAAE;QAC/B,WAAW;QACX,QAAQ,EAAE;YACN,YAAY,EAAE,MAAM;YACpB,WAAW,EAAE,KAAK;SACrB;QACD,KAAK,EAAE;YACH,KAAK,EAAE,aAAa,UAAU,CAAC,MAAM,EAAE;SAC1C;KACJ,CAAC,CAAA;IAEF,OAAO,CAAC,SAAS,CAAC,oBAAW,CAAC,CAAA;IAC9B,OAAO,CAAC,SAAS,CAAC,wBAAe,CAAC,CAAA;IAClC,OAAO,CAAC,SAAS,CAAC,mBAAU,CAAC,CAAA;IAC7B,OAAO,CAAC,SAAS,CAAC,2BAAkB,CAAC,CAAA;IACrC,OAAO,CAAC,SAAS,CAAC,0BAAiB,CAAC,CAAA;IACpC,OAAO,CAAC,SAAS,CAAC,4BAAmB,CAAC,CAAA;IACtC,OAAO,CAAC,SAAS,CAAC,+BAAiB,CAAC,CAAA;IACpC,OAAO,CAAC,SAAS,CAAC,6BAAe,CAAC,CAAA;IAClC,OAAO,CAAC,SAAS,CAAC,uCAAqB,CAAC,CAAA;IACxC,OAAO,CAAC,SAAS,CAAC,qCAAmB,CAAC,CAAA;IACtC,OAAO,CAAC,SAAS,CAAC,mCAAmB,CAAC,CAAA;IACtC,OAAO,CAAC,SAAS,CAAC,iCAAiB,CAAC,CAAA;IACpC,OAAO,CAAC,SAAS,CAAC,mCAAmB,CAAC,CAAA;IACtC,OAAO,CAAC,SAAS,CAAC,iCAAiB,CAAC,CAAA;IACpC,OAAO,CAAC,SAAS,CAAC,uCAAqB,CAAC,CAAA;IACxC,OAAO,CAAC,SAAS,CAAC,qCAAmB,CAAC,CAAA;AAC1C,CAAC,CAAC,CAAA;AAEF,kBAAe,aAAa,CAAA"}