@truelab/trueserver 0.0.4 → 0.0.6
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 +33 -0
- package/MIGRATION.md +303 -0
- package/README.md +96 -98
- package/dist/application.d.ts.map +1 -1
- package/dist/application.js +9 -5
- package/dist/application.js.map +1 -1
- package/dist/examples.d.ts +1 -0
- package/dist/examples.d.ts.map +1 -1
- package/dist/examples.js +3 -1
- package/dist/examples.js.map +1 -1
- package/dist/helpers/config.helper.d.ts +4 -1
- package/dist/helpers/config.helper.d.ts.map +1 -1
- package/dist/helpers/config.helper.js +26 -0
- package/dist/helpers/config.helper.js.map +1 -1
- package/dist/helpers/errors.helper.d.ts +18 -0
- package/dist/helpers/errors.helper.d.ts.map +1 -0
- package/dist/helpers/errors.helper.js +20 -0
- package/dist/helpers/errors.helper.js.map +1 -0
- package/dist/helpers/fastify.helper.d.ts +5 -1
- package/dist/helpers/fastify.helper.d.ts.map +1 -1
- package/dist/helpers/fastify.helper.js +3 -1
- package/dist/helpers/fastify.helper.js.map +1 -1
- package/dist/helpers/handlers.helper.d.ts +10 -1
- package/dist/helpers/handlers.helper.d.ts.map +1 -1
- package/dist/helpers/handlers.helper.js +2 -1
- package/dist/helpers/handlers.helper.js.map +1 -1
- package/dist/index.d.ts +6 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -4
- package/dist/index.js.map +1 -1
- package/dist/plugins/404.plugin.d.ts.map +1 -1
- package/dist/plugins/404.plugin.js +2 -1
- package/dist/plugins/404.plugin.js.map +1 -1
- package/dist/plugins/apikey.plugin.d.ts.map +1 -1
- package/dist/plugins/apikey.plugin.js +6 -7
- package/dist/plugins/apikey.plugin.js.map +1 -1
- package/dist/plugins/error.plugin.d.ts.map +1 -1
- package/dist/plugins/error.plugin.js +6 -2
- package/dist/plugins/error.plugin.js.map +1 -1
- package/dist/plugins/sentry.plugin.js +2 -2
- package/dist/plugins/sentry.plugin.js.map +1 -1
- package/dist/plugins/swagger.plugin.d.ts.map +1 -1
- package/dist/plugins/swagger.plugin.js +5 -1
- package/dist/plugins/swagger.plugin.js.map +1 -1
- package/dist/routes/checksum/checksum.route.d.ts +3 -0
- package/dist/routes/checksum/checksum.route.d.ts.map +1 -0
- package/dist/routes/checksum/checksum.route.js +21 -0
- package/dist/routes/checksum/checksum.route.js.map +1 -0
- package/dist/routes/checksum/index.d.ts +3 -0
- package/dist/routes/checksum/index.d.ts.map +1 -0
- package/dist/routes/checksum/index.js +12 -0
- package/dist/routes/checksum/index.js.map +1 -0
- package/dist/routes/client/client.route.d.ts +3 -0
- package/dist/routes/client/client.route.d.ts.map +1 -0
- package/dist/routes/{client.route.js → client/client.route.js} +5 -5
- package/dist/routes/client/client.route.js.map +1 -0
- package/dist/routes/client/index.d.ts +3 -0
- package/dist/routes/client/index.d.ts.map +1 -0
- package/dist/routes/client/index.js +12 -0
- package/dist/routes/client/index.js.map +1 -0
- package/dist/routes/config/config.route.js +1 -1
- package/dist/routes/config/config.route.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 +43 -17
- package/dist/routes/config/config.route.validate.plugin.js.map +1 -1
- package/dist/routes/play/play.route.validate.plugin.d.ts +9 -9
- package/dist/routes/play/play.route.validate.plugin.d.ts.map +1 -1
- package/dist/routes/play/play.route.validate.plugin.js +84 -40
- 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 +9 -3
- package/dist/routes/validate/validate.route.validate.plugin.js.map +1 -1
- package/dist/routes-public/healthcheck.route.d.ts.map +1 -0
- package/dist/{routes → routes-public}/healthcheck.route.js +4 -10
- package/dist/routes-public/healthcheck.route.js.map +1 -0
- package/dist/schemas/checksum.schema.d.ts +11 -0
- package/dist/schemas/checksum.schema.d.ts.map +1 -0
- package/dist/schemas/checksum.schema.js +26 -0
- package/dist/schemas/checksum.schema.js.map +1 -0
- package/dist/schemas/client.schema.d.ts +1 -1
- package/dist/schemas/client.schema.js +4 -4
- package/dist/schemas/client.schema.js.map +1 -1
- package/dist/schemas/config.schema.d.ts +17 -20
- package/dist/schemas/config.schema.d.ts.map +1 -1
- package/dist/schemas/config.schema.js +41 -42
- package/dist/schemas/config.schema.js.map +1 -1
- package/dist/schemas/examples/checksum.schema.examples.d.ts +3 -0
- package/dist/schemas/examples/checksum.schema.examples.d.ts.map +1 -0
- package/dist/schemas/examples/checksum.schema.examples.js +26 -0
- package/dist/schemas/examples/checksum.schema.examples.js.map +1 -0
- package/dist/schemas/examples/client.schema.examples.d.ts.map +1 -1
- package/dist/schemas/examples/client.schema.examples.js +15 -18
- package/dist/schemas/examples/client.schema.examples.js.map +1 -1
- package/dist/schemas/examples/config.schema.examples.d.ts.map +1 -1
- package/dist/schemas/examples/config.schema.examples.js +30 -34
- package/dist/schemas/examples/config.schema.examples.js.map +1 -1
- package/dist/schemas/examples/healthcheck.schema.examples.d.ts.map +1 -1
- package/dist/schemas/examples/healthcheck.schema.examples.js +3 -14
- package/dist/schemas/examples/healthcheck.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 +66 -130
- 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 +1 -3
- package/dist/schemas/examples/validate.schema.examples.js.map +1 -1
- package/dist/schemas/healthcheck.schema.d.ts +2 -20
- package/dist/schemas/healthcheck.schema.d.ts.map +1 -1
- package/dist/schemas/healthcheck.schema.js +10 -41
- package/dist/schemas/healthcheck.schema.js.map +1 -1
- package/dist/schemas/play.schema.d.ts +21 -16
- package/dist/schemas/play.schema.d.ts.map +1 -1
- package/dist/schemas/play.schema.js +36 -45
- package/dist/schemas/play.schema.js.map +1 -1
- package/dist/schemas/shared.d.ts +13 -21
- package/dist/schemas/shared.d.ts.map +1 -1
- package/dist/schemas/shared.js +56 -72
- package/dist/schemas/shared.js.map +1 -1
- package/dist/schemas/validate.schema.d.ts +2 -3
- package/dist/schemas/validate.schema.d.ts.map +1 -1
- package/dist/schemas/validate.schema.js +2 -5
- package/dist/schemas/validate.schema.js.map +1 -1
- package/package.json +3 -2
- package/dist/plugins/metrics.plugin.d.ts +0 -9
- package/dist/plugins/metrics.plugin.d.ts.map +0 -1
- package/dist/plugins/metrics.plugin.js +0 -24
- package/dist/plugins/metrics.plugin.js.map +0 -1
- package/dist/routes/client.route.d.ts +0 -3
- package/dist/routes/client.route.d.ts.map +0 -1
- package/dist/routes/client.route.js.map +0 -1
- package/dist/routes/healthcheck.route.d.ts.map +0 -1
- package/dist/routes/healthcheck.route.js.map +0 -1
- /package/dist/{routes → routes-public}/healthcheck.route.d.ts +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,39 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [0.0.6] - 2026-06-03
|
|
10
|
+
|
|
11
|
+
### Breaking
|
|
12
|
+
- `/config` reply: добавлено обязательное поле `rtpVersion: string` (лейбл RTP-бакета, совпадает с сегментом `{rtpVersion}` пути). См. [MIGRATION.md](MIGRATION.md).
|
|
13
|
+
- `TGameConfig.rtpVersion` — теперь обязательное поле для `registerGameConfig`.
|
|
14
|
+
- `Stake.amount` — семантика изменилась: теперь это **полная сумма ставки**, уже умноженная на `StakeMode.bets` и `qty`. Формула: `amount = baseBetAmount × StakeMode.bets × (qty ?? 1)`. Не валидируется пакетом — обновлены только описания.
|
|
15
|
+
|
|
16
|
+
### Added
|
|
17
|
+
- `Stake.coinValue?: number` — опциональное поле для игр с coin-механикой (для рулеток и подобных — отсутствует).
|
|
18
|
+
- `config.route.validate.plugin` сверяет `rtpVersion` в трёх источниках: `request.params.rtpVersion`, `payload.rtpVersion`, `gameConfig.rtpVersion`. Несовпадение → 500 с `code=1006`.
|
|
19
|
+
|
|
20
|
+
## [0.0.5] - 2026-05-29
|
|
21
|
+
|
|
22
|
+
### Breaking
|
|
23
|
+
- Контракт API приведён к [GS Documentation 1.0.0](https://www.notion.so/truelab/GS-Documentation-36dd90f8c00680d485d4e4e3eba928f0). Полный список изменений и инструкция по переходу — в [MIGRATION.md](MIGRATION.md).
|
|
24
|
+
- Гейм-плей роуты под версионным префиксом `/:rtpVersion` (`/config`, `/client`, `/play`, `/validate`, `/checksum`). `/healthcheck` остаётся без версии.
|
|
25
|
+
- HTTP-методы: `GET /healthcheck`, `GET /:rtpVersion/config`, `GET /:rtpVersion/client`, `GET /:rtpVersion/checksum`. `/play` и `/validate` — `POST`.
|
|
26
|
+
- Обёртка ошибок: `{ status: false, code, message }` (вместо `{ ok: false, message }`).
|
|
27
|
+
- API-ключ проверяется на **всех** запросах (включая `GET`).
|
|
28
|
+
- `/play`: переименование полей (`demo` → `isDemo`, `winCash` → `cashWin`, `winCoins` → `coins`); `meta` — единый объект `{ public, private }`; `gameResponse` и `rng` в новом формате; `finished/choices/meta` перенесены в `results`. Добавлено обязательное `stepIndex`.
|
|
29
|
+
- `/config`: новая форма (`gameTitle`, `gameId`, `volatility: number`, `rtp: string`, `hasRestore`, `hasVariableLines`, `variableLevels: number[]`, `defaultLevel`). Удалены `category`, `hitrate`, `apiVersion`, `hasBuy`, `hasGamble`, `ECategory`, `EVolatility`.
|
|
30
|
+
- `/healthcheck`: ответ упрощён до `{ ok, gameId, environment, uptime, timestamp }`. Поле `service` → `gameId`. Удалены `metrics`, `services`, `code`, `status`, `message`.
|
|
31
|
+
- `/client`: убрано `ok`, добавлено опциональное поле `test` для cheat-tool.
|
|
32
|
+
- Удалён плагин `metrics`.
|
|
33
|
+
|
|
34
|
+
### Added
|
|
35
|
+
- Новый эндпоинт `GET /:rtpVersion/checksum` для проверки целостности файлов.
|
|
36
|
+
- Хелпер `EErrorCode` + типизированный `httpError(message, statusCode, code)`.
|
|
37
|
+
- Инвариант `hasChoice ⇒ hasMeta` и `hasVariableLines ⇔ {variableLevels, defaultLevel}` проверяются в `registerGameConfig`.
|
|
38
|
+
- Экспорт `MetaSchema` / `TMeta`.
|
|
39
|
+
|
|
40
|
+
## [Unreleased archive]
|
|
41
|
+
|
|
9
42
|
### Added
|
|
10
43
|
- NPM-пакет `@truelab/trueserver` (exports, dist, prepack, publishConfig)
|
|
11
44
|
- Bitbucket Pipelines: test / build / publish
|
package/MIGRATION.md
ADDED
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
# Migration
|
|
2
|
+
|
|
3
|
+
- [0.0.5 → 0.0.6](#migration-005--006) (последнее)
|
|
4
|
+
- [0.0.4 → 0.0.5](#migration-004--005)
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Migration: 0.0.5 → 0.0.6
|
|
9
|
+
|
|
10
|
+
Документация GS Documentation обновлена после релиза 0.0.5. Изменения локальные, но breaking: новое обязательное поле в `/config` и опциональное в `Stake`.
|
|
11
|
+
|
|
12
|
+
> **TL;DR что менять в игре:**
|
|
13
|
+
> 1. В `registerGameConfig(...)` добавить `rtpVersion: '96'` (или ваш лейбл).
|
|
14
|
+
> 2. В `handlers.register('config', ...)` добавить `rtpVersion` в reply (тот же, что в `gameConfig`, и совпадающий с path-сегментом).
|
|
15
|
+
> 3. Опционально: добавить `coinValue` в `Stake` для игр с coin-механикой (запросы продолжают приниматься и без него).
|
|
16
|
+
> 4. Перепроверить `Stake.amount`: теперь это **полная сумма** (включая `StakeMode.bets` и `qty`), а не «на одну ставку».
|
|
17
|
+
|
|
18
|
+
### 1. `/config` reply → новое обязательное `rtpVersion`
|
|
19
|
+
|
|
20
|
+
```diff
|
|
21
|
+
{
|
|
22
|
+
"gameTitle": "Book of Odin",
|
|
23
|
+
"gameId": "book-of-odin",
|
|
24
|
+
"volatility": 2.5,
|
|
25
|
+
"rtp": "96.17",
|
|
26
|
+
+ "rtpVersion": "96",
|
|
27
|
+
"hasMeta": true,
|
|
28
|
+
...
|
|
29
|
+
}
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
`rtpVersion` — лейбл RTP-бакета (`"96"`, `"97"`, …). Он:
|
|
33
|
+
- совпадает с сегментом `{rtpVersion}` пути, под которым смонтирован endpoint;
|
|
34
|
+
- 1:1 с `rtp` (одной RTP-версии — один точный `rtp` и один `rtpVersion`);
|
|
35
|
+
- сверяется с `gameConfig.rtpVersion` и с `request.params.rtpVersion`. Несовпадение → `500` с `code=1006`.
|
|
36
|
+
|
|
37
|
+
### 2. `TGameConfig.rtpVersion`
|
|
38
|
+
|
|
39
|
+
```diff
|
|
40
|
+
registerGameConfig({
|
|
41
|
+
gameTitle: 'My Game',
|
|
42
|
+
gameId: 'my-game',
|
|
43
|
+
+ rtpVersion: '96',
|
|
44
|
+
...
|
|
45
|
+
})
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
`registerGameConfig` падает на старте, если `rtpVersion` пустой или не строка.
|
|
49
|
+
|
|
50
|
+
### 3. `Stake.coinValue` (опционально)
|
|
51
|
+
|
|
52
|
+
```diff
|
|
53
|
+
{
|
|
54
|
+
"name": "regular",
|
|
55
|
+
"amount": 1.0,
|
|
56
|
+
+ "coinValue": 0.1,
|
|
57
|
+
"level": 9
|
|
58
|
+
}
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Игры без coin-механики (рулетки и т.п.) **не передают** это поле. Пакет не проверяет его наличие/отсутствие — формат остаётся на стороне игры.
|
|
62
|
+
|
|
63
|
+
### 4. `Stake.amount` — новая семантика
|
|
64
|
+
|
|
65
|
+
Ничего в схеме менять не нужно, но **читать значение** теперь надо по-другому:
|
|
66
|
+
|
|
67
|
+
```
|
|
68
|
+
amount = baseBetAmount × StakeMode.bets × (qty ?? 1)
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
`baseBetAmount` — стоимость одной базовой ставки в валюте (`coinValue × coinRatio` для fixed-line; `coinValue × level` для variable-line).
|
|
72
|
+
|
|
73
|
+
`qty` **уже учтён** в `amount` — нельзя умножать ещё раз.
|
|
74
|
+
|
|
75
|
+
### Чек-лист
|
|
76
|
+
|
|
77
|
+
1. `npm install @truelab/trueserver@0.0.6`.
|
|
78
|
+
2. Добавить `rtpVersion` в `registerGameConfig({...})`.
|
|
79
|
+
3. Добавить `rtpVersion` в reply `handlers.register('config', ...)` — должен совпадать с тем, что в `gameConfig`, и с path-сегментом.
|
|
80
|
+
4. Перепроверить расчёт `Stake.amount` в обработчике `/play`.
|
|
81
|
+
5. Если используется coin-механика — добавить `coinValue` в формирование тестовых запросов / mocks.
|
|
82
|
+
6. Прогнать тесты.
|
|
83
|
+
|
|
84
|
+
---
|
|
85
|
+
|
|
86
|
+
## Migration: 0.0.4 → 0.0.5
|
|
87
|
+
|
|
88
|
+
Этот документ описывает все изменения контракта между 0.0.4 и 0.0.5.
|
|
89
|
+
0.0.5 приводит пакет в соответствие со спецификацией [GS Documentation 1.0.0](https://www.notion.so/truelab/GS-Documentation-36dd90f8c00680d485d4e4e3eba928f0).
|
|
90
|
+
|
|
91
|
+
Релиз помечен как **patch**, но **является ломающим**: меняются методы и пути роутов, формат ответов, имена и формы полей, удаляются enum'ы и флаги. Игры, использующие 0.0.4, не запустятся на 0.0.5 без правок.
|
|
92
|
+
|
|
93
|
+
> **TL;DR что менять в игре:**
|
|
94
|
+
> 1. `registerGameConfig(...)` — новые имена/набор полей.
|
|
95
|
+
> 2. `handlers.register('config', ...)` — новый ответ.
|
|
96
|
+
> 3. `handlers.register('play', ...)` — новый `request` и `reply`; в реквесте `request.params.rtpVersion` доступен.
|
|
97
|
+
> 4. `handlers.register('client', ...)` — убрать `ok: true`, добавить `test` если нужен cheat-tool.
|
|
98
|
+
> 5. Добавить `handlers.register('checksum', ...)`.
|
|
99
|
+
> 6. Везде, где раньше возвращали ошибку — теперь формат `{ status, code, message }`.
|
|
100
|
+
|
|
101
|
+
## 1. Маршруты и методы
|
|
102
|
+
|
|
103
|
+
| Старое | Новое |
|
|
104
|
+
|---|---|
|
|
105
|
+
| `POST /healthcheck` | **`GET`** `/healthcheck` (без `:rtpVersion`) |
|
|
106
|
+
| `POST /config` | **`GET`** `/:rtpVersion/config` |
|
|
107
|
+
| `POST /client` | **`GET`** `/:rtpVersion/client` |
|
|
108
|
+
| — | **`GET`** `/:rtpVersion/checksum` (новый) |
|
|
109
|
+
| `POST /play` | `POST` `/:rtpVersion/play` |
|
|
110
|
+
| `POST /validate` | `POST` `/:rtpVersion/validate` |
|
|
111
|
+
|
|
112
|
+
- `{rtpVersion}` — путь-сегмент (`96`, `97`, `98` и т. п.). Игра получает его из `request.params.rtpVersion` (`TRtpParams` — см. `helpers/handlers.helper.ts`).
|
|
113
|
+
- `ROUTE_PREFIX` оборачивает оба варианта. Например, `ROUTE_PREFIX=/api/v1` даёт `GET /api/v1/healthcheck` и `POST /api/v1/96/play`.
|
|
114
|
+
|
|
115
|
+
## 2. Аутентификация
|
|
116
|
+
|
|
117
|
+
API-ключ теперь проверяется на **всех** HTTP-методах (включая `GET`). Раньше — только на `POST`. Если у внешней инфраструктуры есть GET-пробы без заголовка `X-API-KEY`, их нужно обновить.
|
|
118
|
+
|
|
119
|
+
## 3. Формат ошибок
|
|
120
|
+
|
|
121
|
+
Старое:
|
|
122
|
+
|
|
123
|
+
```json
|
|
124
|
+
{ "ok": false, "message": "...", "custom": null }
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
Новое:
|
|
128
|
+
|
|
129
|
+
```json
|
|
130
|
+
{ "status": false, "code": 4001, "message": "..." }
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
`code` — целое число. Диапазоны:
|
|
134
|
+
|
|
135
|
+
| Диапазон | Категория |
|
|
136
|
+
|---|---|
|
|
137
|
+
| `1xxx` | Валидация |
|
|
138
|
+
| `2xxx` | Аутентификация |
|
|
139
|
+
| `4xxx` | Бизнес-правила |
|
|
140
|
+
| `9xxx` | Internal / unhandled |
|
|
141
|
+
|
|
142
|
+
Константы — в `EErrorCode` (экспорт из корня пакета). Для собственных ошибок используйте `httpError(message, statusCode, code)`:
|
|
143
|
+
|
|
144
|
+
```typescript
|
|
145
|
+
import { EErrorCode, httpError } from '@truelab/trueserver'
|
|
146
|
+
|
|
147
|
+
throw httpError('Unknown choice', 400, EErrorCode.BusinessChoiceUnknown)
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
## 4. `TGameConfig` (вход `registerGameConfig`)
|
|
151
|
+
|
|
152
|
+
| Старое | Новое |
|
|
153
|
+
|---|---|
|
|
154
|
+
| `name` | `gameTitle` |
|
|
155
|
+
| — | `gameId` (новое, общий ID для всех RTP) |
|
|
156
|
+
| `hasMeta` | `hasMeta` |
|
|
157
|
+
| `hasChoice` | `hasChoice` (теперь требует `hasMeta=true`) |
|
|
158
|
+
| `hasBuy` | — (удалено) |
|
|
159
|
+
| `hasGamble` | — (удалено) |
|
|
160
|
+
| — | `hasRestore` (новое) |
|
|
161
|
+
| `hasMultiStake` | `hasMultiStake` |
|
|
162
|
+
| `hasValidation` | `hasValidation` (опциональное, оставлено для `/validate`) |
|
|
163
|
+
| — | `hasVariableLines` (новое; раньше определялось по наличию `variableLevels`) |
|
|
164
|
+
| `variableLevels: number` (целое) | `variableLevels: number[]` (массив доступных уровней) |
|
|
165
|
+
| — | `defaultLevel: number` (обязателен при `hasVariableLines=true`) |
|
|
166
|
+
| `coinRatio?` | `coinRatio` (required при `hasVariableLines=false`) |
|
|
167
|
+
|
|
168
|
+
`registerGameConfig` теперь бросает при нарушении:
|
|
169
|
+
- `hasChoice=true` без `hasMeta=true`;
|
|
170
|
+
- `hasVariableLines=true` без непустого `variableLevels` или с `defaultLevel ∉ variableLevels`;
|
|
171
|
+
- `hasVariableLines=false` без `coinRatio`.
|
|
172
|
+
|
|
173
|
+
## 5. `/config` reply
|
|
174
|
+
|
|
175
|
+
| Старое | Новое |
|
|
176
|
+
|---|---|
|
|
177
|
+
| `ok: true` | — (убрать, успех — plain JSON без обёртки) |
|
|
178
|
+
| `name` | `gameTitle` |
|
|
179
|
+
| — | `gameId` |
|
|
180
|
+
| `category: ECategory` | — (удалено) |
|
|
181
|
+
| `rtp: number` | `rtp: string` (десятичная строка, например `"96.17"`) |
|
|
182
|
+
| `volatility: EVolatility` (enum-строка, опционально) | `volatility: number` из `{0.5, 1, 1.5, …, 5}` (обязательное) |
|
|
183
|
+
| `hitrate?: number` | — (удалено) |
|
|
184
|
+
| `apiVersion: number` | — (удалено) |
|
|
185
|
+
| `hasBuy`, `hasGamble`, `hasValidation` | — (удалены из reply; `hasValidation` остался в `TGameConfig`) |
|
|
186
|
+
| — | `hasRestore: boolean` |
|
|
187
|
+
| — | `hasVariableLines: boolean` |
|
|
188
|
+
| `variableLevels: number` | `variableLevels?: number[]` |
|
|
189
|
+
| — | `defaultLevel?: number` |
|
|
190
|
+
| `minWin?: number` | `minWin: number` (обязательное, множитель ставки) |
|
|
191
|
+
| `maxWin: number` | `maxWin: number` (множитель ставки) |
|
|
192
|
+
| `coinRatio?: number` | `coinRatio?: number` |
|
|
193
|
+
| `stakeModes: StakeMode[]` | без изменений |
|
|
194
|
+
|
|
195
|
+
Удалены enum'ы: `ECategory`, `EVolatility`.
|
|
196
|
+
|
|
197
|
+
## 6. `/play` request
|
|
198
|
+
|
|
199
|
+
| Старое | Новое |
|
|
200
|
+
|---|---|
|
|
201
|
+
| `demo: boolean` | `isDemo: boolean` |
|
|
202
|
+
| `currencyDecimals?: number` | `currencyDecimals: integer` (обязательное) |
|
|
203
|
+
| — | `stepIndex: integer` (новое; `0` для opening step, инкремент на каждое продолжение) |
|
|
204
|
+
| `stakes?: Stake[]` | `stakes?: Stake[]` — только на opening step; уникальные `name` |
|
|
205
|
+
| `choice?: integer` | `choice?: integer` — обязателен на continuation step при `hasChoice=true` |
|
|
206
|
+
| `metaPrivate?: string`, `metaPublic?: string` | `meta?: { public, private }` — один объект |
|
|
207
|
+
| `isGambleAllowed?: boolean` | — (удалено) |
|
|
208
|
+
| `test?: string` | `test?: string` — игнорируется в production |
|
|
209
|
+
|
|
210
|
+
## 7. `/play` reply
|
|
211
|
+
|
|
212
|
+
| Старое | Новое |
|
|
213
|
+
|---|---|
|
|
214
|
+
| `ok: true` | — (убрать) |
|
|
215
|
+
| `rng: string` (stringified JSON) | `rng: Array<{ value: number, ... }>` |
|
|
216
|
+
| `finished: boolean` (top-level) | `results.finished: boolean` |
|
|
217
|
+
| `results.response: string` (stringified JSON) | `gameResponse: object` (top-level) |
|
|
218
|
+
| `results.winCash` | `results.cashWin` |
|
|
219
|
+
| `results.winCoins?` | `results.coins?` |
|
|
220
|
+
| `metaPrivate?`, `metaPublic?` (top-level, strings) | `results.meta?: { public, private }` |
|
|
221
|
+
| `choices?: number[]` (top-level) | `results.choices?: integer[]` (non-empty, не `null`/`[]`) |
|
|
222
|
+
| `analytics?` | — (удалено) |
|
|
223
|
+
|
|
224
|
+
Контракт `choices`: должен присутствовать тогда и только тогда, когда `hasChoice=true` и `results.finished=false`.
|
|
225
|
+
|
|
226
|
+
## 8. `/client` reply
|
|
227
|
+
|
|
228
|
+
| Старое | Новое |
|
|
229
|
+
|---|---|
|
|
230
|
+
| `ok: true` | — (убрать) |
|
|
231
|
+
| `data: object` | `data: object` |
|
|
232
|
+
| — | `test?: object` (опциональное; RGS фильтрует для не-test-сессий) |
|
|
233
|
+
|
|
234
|
+
## 9. `/healthcheck` reply
|
|
235
|
+
|
|
236
|
+
| Старое | Новое |
|
|
237
|
+
|---|---|
|
|
238
|
+
| `code: 200` | — |
|
|
239
|
+
| `status: true` | — |
|
|
240
|
+
| `service: string` | `gameId: string` |
|
|
241
|
+
| `environment` | `environment` |
|
|
242
|
+
| `uptime: number` | `uptime: number` |
|
|
243
|
+
| `message: 'OK'` | — |
|
|
244
|
+
| `timestamp: integer` | `timestamp: integer` (ms) |
|
|
245
|
+
| `metrics: {...}` | — (плагин `metrics` удалён) |
|
|
246
|
+
| `services: { sentry }` | — |
|
|
247
|
+
| — | `ok: true` (новое) |
|
|
248
|
+
|
|
249
|
+
## 10. `/checksum` (новый эндпоинт)
|
|
250
|
+
|
|
251
|
+
`GET /:rtpVersion/checksum`:
|
|
252
|
+
|
|
253
|
+
```json
|
|
254
|
+
{
|
|
255
|
+
"files": [
|
|
256
|
+
{ "path": "src/math/probabilities.json", "hash": "9f86..." },
|
|
257
|
+
{ "path": "src/math/paytable.json", "hash": "6c1b..." }
|
|
258
|
+
]
|
|
259
|
+
}
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
Алгоритм хэширования и набор файлов согласуются с RGS-командой при деплое.
|
|
263
|
+
|
|
264
|
+
## 11. `/validate` (без изменений, но патч)
|
|
265
|
+
|
|
266
|
+
`/validate` сохранён как был (вне новой спеки) для обратной совместимости. Изменилось только:
|
|
267
|
+
- Reply теперь пустой объект `{}` (раньше `{ ok: true }`).
|
|
268
|
+
- Endpoint, как и остальные версионированные, доступен под `/:rtpVersion/validate`.
|
|
269
|
+
|
|
270
|
+
`hasValidation` остался во входе `registerGameConfig` как опциональный флаг. Если `false`, плагин валидации `/validate` отбивает запросы 400-кодом.
|
|
271
|
+
|
|
272
|
+
## 12. Удалённые экспорты
|
|
273
|
+
|
|
274
|
+
- `ECategory`, `EVolatility` — удалены полностью.
|
|
275
|
+
- `TMetrics`, `MetricsSchema`, `metricsPlugin` — удалены.
|
|
276
|
+
- `winCash`/`winCoins`/`metaPrivate`/`metaPublic` в `/play` — удалены (см. п. 7).
|
|
277
|
+
- `apiVersion`, `hitrate`, `category`, `hasBuy`, `hasGamble` — удалены из `/config`.
|
|
278
|
+
|
|
279
|
+
## 13. Что появилось
|
|
280
|
+
|
|
281
|
+
- `EErrorCode`, `TErrorCode` — нумерация ошибок.
|
|
282
|
+
- `MetaSchema`, `TMeta` — схема `{ public, private }`.
|
|
283
|
+
- `ChecksumReplySchema`, `ChecksumRequestSchema`, `TChecksumReply`, `TChecksumRequest`.
|
|
284
|
+
- `TRtpParams = { rtpVersion: string }` — тип path-параметра.
|
|
285
|
+
- `FastifyError$Coded` — расширение `FastifyError` с полем `errorCode`.
|
|
286
|
+
- `httpError(message, statusCode, code)` — третий параметр для машинного кода ошибки.
|
|
287
|
+
|
|
288
|
+
## 14. Чек-лист обновления игры
|
|
289
|
+
|
|
290
|
+
1. `npm install @truelab/trueserver@0.0.5`.
|
|
291
|
+
2. Привести `registerGameConfig({...})` к новой форме (см. п. 4).
|
|
292
|
+
3. Переписать `handlers.register('config', ...)` под новый reply (см. п. 5).
|
|
293
|
+
4. Переписать `handlers.register('play', ...)` — request/reply (см. пп. 6, 7). `meta` теперь объект, а не два string'а.
|
|
294
|
+
5. В `handlers.register('client', ...)` убрать `ok: true`. При необходимости добавить поле `test`.
|
|
295
|
+
6. Зарегистрировать `handlers.register('checksum', ...)` (если интегрируется проверка целостности).
|
|
296
|
+
7. Если был `handlers.register('validate', ...)` с `ok: true` — теперь возвращать `{}`.
|
|
297
|
+
8. Сменить все собственные ошибки на формат `{ status, code, message }` или использовать `httpError(..., code)`.
|
|
298
|
+
9. Обновить тесты: пути с `/:rtpVersion`, методы GET для `/config`/`/client`/`/checksum`, GET для `/healthcheck`, новые имена полей.
|
|
299
|
+
10. Обновить URL-ы на стороне RGS-конфига для каждой игры.
|
|
300
|
+
|
|
301
|
+
## Вопросы
|
|
302
|
+
|
|
303
|
+
Регрессии и нерешённые ситуации — в Slack `#truelab-games-platform` или к командe RGS.
|
package/README.md
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
# @truelab/trueserver
|
|
2
2
|
|
|
3
|
-
Fastify-based server package for TrueLab games.
|
|
3
|
+
Fastify-based server package for TrueLab games. Implements the **Game Server** contract described in [GS Documentation](https://www.notion.so/truelab/GS-Documentation-36dd90f8c00680d485d4e4e3eba928f0).
|
|
4
4
|
|
|
5
|
-
The package provides
|
|
5
|
+
The package provides HTTP routes, TypeBox request/response schemas, API-key authentication, error envelope, Sentry integration and Swagger UI. A game team only needs to call `registerGameConfig`, register handlers and start the server.
|
|
6
6
|
|
|
7
7
|
## Requirements
|
|
8
8
|
|
|
9
|
-
- Node.js 22.12+
|
|
10
|
-
- npm
|
|
9
|
+
- Node.js 22.12+
|
|
10
|
+
- npm access to `@truelab/trueserver`
|
|
11
11
|
|
|
12
12
|
## Installation
|
|
13
13
|
|
|
@@ -17,18 +17,15 @@ npm install @truelab/trueserver
|
|
|
17
17
|
|
|
18
18
|
## Quick Start
|
|
19
19
|
|
|
20
|
-
Register game config and handlers before building the Fastify app:
|
|
21
|
-
|
|
22
20
|
```typescript
|
|
23
21
|
import {
|
|
24
22
|
Application,
|
|
25
|
-
ECategory,
|
|
26
23
|
EStakeType,
|
|
27
|
-
EVolatility,
|
|
28
24
|
handlers,
|
|
29
25
|
getenv,
|
|
30
26
|
logger,
|
|
31
27
|
registerGameConfig,
|
|
28
|
+
type TChecksumReply,
|
|
32
29
|
type TClientReply,
|
|
33
30
|
type TConfigReply,
|
|
34
31
|
type TPlayReply,
|
|
@@ -44,57 +41,61 @@ const stakeModes = [{
|
|
|
44
41
|
}]
|
|
45
42
|
|
|
46
43
|
registerGameConfig({
|
|
47
|
-
|
|
44
|
+
gameTitle: 'My Game',
|
|
45
|
+
gameId: 'my-game',
|
|
46
|
+
rtpVersion: '96',
|
|
48
47
|
stakeModes,
|
|
49
|
-
hasBuy: false,
|
|
50
|
-
hasChoice: false,
|
|
51
|
-
hasGamble: false,
|
|
52
48
|
hasMeta: false,
|
|
49
|
+
hasChoice: false,
|
|
50
|
+
hasRestore: false,
|
|
53
51
|
hasMultiStake: false,
|
|
54
|
-
|
|
52
|
+
hasVariableLines: false,
|
|
55
53
|
coinRatio: 10,
|
|
56
54
|
})
|
|
57
55
|
|
|
58
|
-
handlers.register('client', async (): Promise<TClientReply> => ({
|
|
59
|
-
ok: true,
|
|
60
|
-
data: {},
|
|
61
|
-
}))
|
|
62
|
-
|
|
63
56
|
handlers.register('config', async (): Promise<TConfigReply> => ({
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
hitrate: 25,
|
|
70
|
-
volatility: EVolatility.med,
|
|
57
|
+
gameTitle: 'My Game',
|
|
58
|
+
gameId: 'my-game',
|
|
59
|
+
volatility: 2.5,
|
|
60
|
+
rtp: '96.17',
|
|
61
|
+
rtpVersion: '96',
|
|
71
62
|
hasMeta: false,
|
|
72
63
|
hasChoice: false,
|
|
73
|
-
|
|
64
|
+
hasRestore: false,
|
|
74
65
|
hasMultiStake: false,
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
freeroundsAllowed: false,
|
|
66
|
+
hasVariableLines: false,
|
|
67
|
+
coinRatio: 10,
|
|
78
68
|
minWin: 0.1,
|
|
79
69
|
maxWin: 5000,
|
|
80
|
-
|
|
81
|
-
|
|
70
|
+
freeroundsAllowed: false,
|
|
71
|
+
stakeModes,
|
|
82
72
|
}))
|
|
83
73
|
|
|
84
|
-
handlers.register('
|
|
85
|
-
|
|
86
|
-
rng: '[]',
|
|
87
|
-
finished: true,
|
|
88
|
-
results: {
|
|
89
|
-
response: '{}',
|
|
90
|
-
winCash: 0,
|
|
91
|
-
},
|
|
74
|
+
handlers.register('client', async (): Promise<TClientReply> => ({
|
|
75
|
+
data: {},
|
|
92
76
|
}))
|
|
93
77
|
|
|
94
|
-
handlers.register('
|
|
95
|
-
|
|
78
|
+
handlers.register('play', async (request): Promise<TPlayReply> => {
|
|
79
|
+
// request.params.rtpVersion is available for routing math by RTP
|
|
80
|
+
return {
|
|
81
|
+
gameResponse: {},
|
|
82
|
+
rng: [],
|
|
83
|
+
results: {
|
|
84
|
+
cashWin: 0,
|
|
85
|
+
finished: true,
|
|
86
|
+
},
|
|
87
|
+
}
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
handlers.register('checksum', async (): Promise<TChecksumReply> => ({
|
|
91
|
+
files: [
|
|
92
|
+
{ path: 'src/math/spin.js', hash: '...' },
|
|
93
|
+
],
|
|
96
94
|
}))
|
|
97
95
|
|
|
96
|
+
// Optional. `hasValidation` is a legacy flag — kept for backward compatibility.
|
|
97
|
+
handlers.register('validate', async (): Promise<TValidateReply> => ({}))
|
|
98
|
+
|
|
98
99
|
async function main() {
|
|
99
100
|
const app = new Application()
|
|
100
101
|
const server = await app.build()
|
|
@@ -111,63 +112,74 @@ main().catch((error) => {
|
|
|
111
112
|
})
|
|
112
113
|
```
|
|
113
114
|
|
|
114
|
-
`registerGameConfig` must be called before `app.build()
|
|
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`).
|
|
115
116
|
|
|
116
117
|
## Runtime API
|
|
117
118
|
|
|
118
|
-
|
|
119
|
+
`{rtpVersion}` is a path segment (e.g. `96`, `97`, `98`) — RGS picks the correct RTP per call. Game Server receives it as `request.params.rtpVersion`.
|
|
119
120
|
|
|
120
121
|
| Method | Path | Handler | Description |
|
|
121
122
|
| ------ | ---- | ------- | ----------- |
|
|
122
|
-
| `
|
|
123
|
-
| `
|
|
124
|
-
| `
|
|
125
|
-
| `
|
|
126
|
-
| `POST` |
|
|
123
|
+
| `GET` | `/healthcheck` | built-in | Liveness/readiness probe |
|
|
124
|
+
| `GET` | `/:rtpVersion/config` | `handlers.register('config', ...)` | Game config (RTP-specific) |
|
|
125
|
+
| `GET` | `/:rtpVersion/client` | `handlers.register('client', ...)` | Client-side payload |
|
|
126
|
+
| `GET` | `/:rtpVersion/checksum` | `handlers.register('checksum', ...)` | File hashes for integrity check |
|
|
127
|
+
| `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) |
|
|
127
129
|
|
|
128
|
-
|
|
130
|
+
Every request must carry the API key header. By default it is `x-api-key: test`; override with `API_HEADER` and `API_KEY`.
|
|
129
131
|
|
|
130
|
-
`ROUTE_PREFIX` is
|
|
132
|
+
`ROUTE_PREFIX` is prepended to every route. For `ROUTE_PREFIX=/api/v1`, the play endpoint becomes `POST /api/v1/96/play`. `/healthcheck` is always mounted directly under the prefix (no `{rtpVersion}` segment).
|
|
131
133
|
|
|
132
|
-
##
|
|
134
|
+
## Error envelope
|
|
133
135
|
|
|
134
|
-
|
|
136
|
+
Non-2xx responses follow:
|
|
137
|
+
|
|
138
|
+
```json
|
|
139
|
+
{
|
|
140
|
+
"status": false,
|
|
141
|
+
"code": 4001,
|
|
142
|
+
"message": "Invalid choice value"
|
|
143
|
+
}
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
Recommended code ranges: `1xxx` validation, `2xxx` auth, `4xxx` business, `9xxx` internal. The `EErrorCode` enum exports the codes used by this package.
|
|
135
147
|
|
|
136
148
|
```typescript
|
|
137
|
-
import {
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
PlayRequestSchema,
|
|
141
|
-
handlers,
|
|
142
|
-
registerGameConfig,
|
|
143
|
-
type TPlayReply,
|
|
144
|
-
} from '@truelab/trueserver'
|
|
149
|
+
import { EErrorCode, httpError } from '@truelab/trueserver'
|
|
150
|
+
|
|
151
|
+
throw httpError('Invalid choice value', 400, EErrorCode.BusinessChoiceUnknown)
|
|
145
152
|
```
|
|
146
153
|
|
|
147
|
-
Test
|
|
154
|
+
## Test utilities
|
|
148
155
|
|
|
149
156
|
```typescript
|
|
150
|
-
import {
|
|
157
|
+
import {
|
|
158
|
+
checkSchemaPropertyDescriptions,
|
|
159
|
+
testBuild,
|
|
160
|
+
testHeaders,
|
|
161
|
+
validator,
|
|
162
|
+
} from '@truelab/trueserver/test-utils'
|
|
151
163
|
```
|
|
152
164
|
|
|
153
|
-
|
|
165
|
+
`test-utils` uses Jest globals (`expect`), so consumers should keep `jest` and usually `@types/jest` in `devDependencies`.
|
|
154
166
|
|
|
155
|
-
## Environment
|
|
167
|
+
## Environment variables
|
|
156
168
|
|
|
157
169
|
| Variable | Default | Description |
|
|
158
170
|
| -------- | ------- | ----------- |
|
|
159
|
-
| `HOST` | `localhost` | Listen host
|
|
160
|
-
| `PORT` | `2980` | Listen port
|
|
161
|
-
| `ROUTE_PREFIX` | empty | Prefix for all
|
|
162
|
-
| `API_HEADER` | `x-api-key` | Header name used by
|
|
163
|
-
| `API_KEY` | `test` | Expected API key
|
|
171
|
+
| `HOST` | `localhost` | Listen host |
|
|
172
|
+
| `PORT` | `2980` | Listen port |
|
|
173
|
+
| `ROUTE_PREFIX` | empty | Prefix for all routes |
|
|
174
|
+
| `API_HEADER` | `x-api-key` | Header name used by API-key plugin |
|
|
175
|
+
| `API_KEY` | `test` | Expected API key value |
|
|
164
176
|
| `LOG_LEVEL` | `error` | Pino log level |
|
|
165
|
-
| `APP_ENV` | unset | `dev`, `release`,
|
|
177
|
+
| `APP_ENV` | unset | `dev`, `release`, `master`; controls Swagger and environment mapping |
|
|
166
178
|
| `SENTRY_DSN` | unset | Sentry DSN |
|
|
167
179
|
| `SENTRY_RATE` | `0.01` | Sentry traces sample rate |
|
|
168
|
-
| `GAME_ID` | `0001` | Sentry `game.id` tag |
|
|
180
|
+
| `GAME_ID` | `0001` | Sentry `game.id` tag fallback |
|
|
169
181
|
|
|
170
|
-
The environment helper imports `dotenv/config`, so `.env` is loaded
|
|
182
|
+
The environment helper imports `dotenv/config`, so `.env` is loaded automatically.
|
|
171
183
|
|
|
172
184
|
## Swagger
|
|
173
185
|
|
|
@@ -175,45 +187,31 @@ Swagger UI is enabled only for `APP_ENV=dev`.
|
|
|
175
187
|
|
|
176
188
|
- `GET /` redirects to `/docs`
|
|
177
189
|
- `GET /docs` serves Swagger UI
|
|
178
|
-
- `release` and `master` disable Swagger UI
|
|
179
190
|
|
|
180
|
-
|
|
191
|
+
`release` and `master` disable Swagger UI.
|
|
181
192
|
|
|
182
193
|
## Advanced Validation
|
|
183
194
|
|
|
184
|
-
The package validates both requests and
|
|
195
|
+
The package validates both requests and replies via TypeBox schemas and runs game-specific checks based on `registerGameConfig`:
|
|
185
196
|
|
|
186
|
-
- `/config` checks stake modes
|
|
187
|
-
- `/play` checks stakes,
|
|
188
|
-
- `/validate` is available only when `hasValidation
|
|
189
|
-
- `test`
|
|
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`.
|
|
200
|
+
- `test` field is rejected on production (`APP_ENV=master` or unset).
|
|
190
201
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
## Test Utilities
|
|
194
|
-
|
|
195
|
-
Integration tests in consuming game servers can import Jest helpers from:
|
|
196
|
-
|
|
197
|
-
```typescript
|
|
198
|
-
import {
|
|
199
|
-
checkSchemaPropertyDescriptions,
|
|
200
|
-
testBuild,
|
|
201
|
-
testHeaders,
|
|
202
|
-
validator,
|
|
203
|
-
} from '@truelab/trueserver/test-utils'
|
|
204
|
-
```
|
|
202
|
+
## Migration
|
|
205
203
|
|
|
206
|
-
|
|
204
|
+
This release is breaking. See [MIGRATION.md](MIGRATION.md) for a full mapping of old fields/paths to new ones.
|
|
207
205
|
|
|
208
206
|
## Package Development
|
|
209
207
|
|
|
210
208
|
```bash
|
|
211
|
-
npm test
|
|
212
|
-
npm run build
|
|
213
|
-
npm pack
|
|
209
|
+
npm test
|
|
210
|
+
npm run build
|
|
211
|
+
npm pack
|
|
214
212
|
```
|
|
215
213
|
|
|
216
|
-
`bitbucket-pipelines.yml` runs tests and build on the default branch.
|
|
214
|
+
`bitbucket-pipelines.yml` runs tests and build on the default branch.
|
|
217
215
|
|
|
218
216
|
## License
|
|
219
217
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"application.d.ts","sourceRoot":"","sources":["../application.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,cAAc,EAAkB,MAAM,0BAA0B,CAAA;AAGzE,MAAM,CAAC,OAAO,OAAO,WAAW;IAC5B,MAAM,EAAE,cAAc,CAAA;;IAchB,KAAK,IAAI,OAAO,CAAC,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"application.d.ts","sourceRoot":"","sources":["../application.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,cAAc,EAAkB,MAAM,0BAA0B,CAAA;AAGzE,MAAM,CAAC,OAAO,OAAO,WAAW;IAC5B,MAAM,EAAE,cAAc,CAAA;;IAchB,KAAK,IAAI,OAAO,CAAC,cAAc,CAAC;CAuBzC"}
|