@truelab/trueserver 0.0.4 → 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 (137) hide show
  1. package/CHANGELOG.md +67 -0
  2. package/MIGRATION.md +508 -0
  3. package/README.md +114 -94
  4. package/dist/application.d.ts.map +1 -1
  5. package/dist/application.js +9 -5
  6. package/dist/application.js.map +1 -1
  7. package/dist/examples.d.ts +1 -0
  8. package/dist/examples.d.ts.map +1 -1
  9. package/dist/examples.js +3 -1
  10. package/dist/examples.js.map +1 -1
  11. package/dist/helpers/config.helper.d.ts +9 -1
  12. package/dist/helpers/config.helper.d.ts.map +1 -1
  13. package/dist/helpers/config.helper.js +29 -0
  14. package/dist/helpers/config.helper.js.map +1 -1
  15. package/dist/helpers/errors.helper.d.ts +19 -0
  16. package/dist/helpers/errors.helper.d.ts.map +1 -0
  17. package/dist/helpers/errors.helper.js +21 -0
  18. package/dist/helpers/errors.helper.js.map +1 -0
  19. package/dist/helpers/fastify.helper.d.ts +5 -1
  20. package/dist/helpers/fastify.helper.d.ts.map +1 -1
  21. package/dist/helpers/fastify.helper.js +3 -1
  22. package/dist/helpers/fastify.helper.js.map +1 -1
  23. package/dist/helpers/handlers.helper.d.ts +10 -1
  24. package/dist/helpers/handlers.helper.d.ts.map +1 -1
  25. package/dist/helpers/handlers.helper.js +2 -1
  26. package/dist/helpers/handlers.helper.js.map +1 -1
  27. package/dist/helpers/money.helper.d.ts +13 -0
  28. package/dist/helpers/money.helper.d.ts.map +1 -0
  29. package/dist/helpers/money.helper.js +29 -0
  30. package/dist/helpers/money.helper.js.map +1 -0
  31. package/dist/index.d.ts +7 -4
  32. package/dist/index.d.ts.map +1 -1
  33. package/dist/index.js +11 -4
  34. package/dist/index.js.map +1 -1
  35. package/dist/plugins/404.plugin.d.ts.map +1 -1
  36. package/dist/plugins/404.plugin.js +2 -1
  37. package/dist/plugins/404.plugin.js.map +1 -1
  38. package/dist/plugins/apikey.plugin.d.ts.map +1 -1
  39. package/dist/plugins/apikey.plugin.js +6 -7
  40. package/dist/plugins/apikey.plugin.js.map +1 -1
  41. package/dist/plugins/error.plugin.d.ts.map +1 -1
  42. package/dist/plugins/error.plugin.js +6 -2
  43. package/dist/plugins/error.plugin.js.map +1 -1
  44. package/dist/plugins/sentry.plugin.d.ts.map +1 -1
  45. package/dist/plugins/sentry.plugin.js +1 -2
  46. package/dist/plugins/sentry.plugin.js.map +1 -1
  47. package/dist/plugins/swagger.plugin.d.ts.map +1 -1
  48. package/dist/plugins/swagger.plugin.js +5 -1
  49. package/dist/plugins/swagger.plugin.js.map +1 -1
  50. package/dist/routes/checksum/checksum.route.d.ts +3 -0
  51. package/dist/routes/checksum/checksum.route.d.ts.map +1 -0
  52. package/dist/routes/checksum/checksum.route.js +21 -0
  53. package/dist/routes/checksum/checksum.route.js.map +1 -0
  54. package/dist/routes/checksum/index.d.ts +3 -0
  55. package/dist/routes/checksum/index.d.ts.map +1 -0
  56. package/dist/routes/checksum/index.js +12 -0
  57. package/dist/routes/checksum/index.js.map +1 -0
  58. package/dist/routes/client/client.route.d.ts +3 -0
  59. package/dist/routes/client/client.route.d.ts.map +1 -0
  60. package/dist/routes/{client.route.js → client/client.route.js} +5 -5
  61. package/dist/routes/client/client.route.js.map +1 -0
  62. package/dist/routes/client/index.d.ts +3 -0
  63. package/dist/routes/client/index.d.ts.map +1 -0
  64. package/dist/routes/client/index.js +12 -0
  65. package/dist/routes/client/index.js.map +1 -0
  66. package/dist/routes/config/config.route.js +1 -1
  67. package/dist/routes/config/config.route.js.map +1 -1
  68. package/dist/routes/config/config.route.validate.plugin.d.ts.map +1 -1
  69. package/dist/routes/config/config.route.validate.plugin.js +48 -17
  70. package/dist/routes/config/config.route.validate.plugin.js.map +1 -1
  71. package/dist/routes/play/play.route.validate.plugin.d.ts +10 -9
  72. package/dist/routes/play/play.route.validate.plugin.d.ts.map +1 -1
  73. package/dist/routes/play/play.route.validate.plugin.js +90 -39
  74. package/dist/routes/play/play.route.validate.plugin.js.map +1 -1
  75. package/dist/routes/validate/validate.route.validate.plugin.d.ts.map +1 -1
  76. package/dist/routes/validate/validate.route.validate.plugin.js +5 -4
  77. package/dist/routes/validate/validate.route.validate.plugin.js.map +1 -1
  78. package/dist/routes-public/healthcheck.route.d.ts.map +1 -0
  79. package/dist/{routes → routes-public}/healthcheck.route.js +4 -10
  80. package/dist/routes-public/healthcheck.route.js.map +1 -0
  81. package/dist/schemas/checksum.schema.d.ts +11 -0
  82. package/dist/schemas/checksum.schema.d.ts.map +1 -0
  83. package/dist/schemas/checksum.schema.js +26 -0
  84. package/dist/schemas/checksum.schema.js.map +1 -0
  85. package/dist/schemas/client.schema.d.ts +1 -1
  86. package/dist/schemas/client.schema.js +4 -4
  87. package/dist/schemas/client.schema.js.map +1 -1
  88. package/dist/schemas/config.schema.d.ts +17 -20
  89. package/dist/schemas/config.schema.d.ts.map +1 -1
  90. package/dist/schemas/config.schema.js +42 -43
  91. package/dist/schemas/config.schema.js.map +1 -1
  92. package/dist/schemas/examples/checksum.schema.examples.d.ts +3 -0
  93. package/dist/schemas/examples/checksum.schema.examples.d.ts.map +1 -0
  94. package/dist/schemas/examples/checksum.schema.examples.js +26 -0
  95. package/dist/schemas/examples/checksum.schema.examples.js.map +1 -0
  96. package/dist/schemas/examples/client.schema.examples.d.ts.map +1 -1
  97. package/dist/schemas/examples/client.schema.examples.js +15 -18
  98. package/dist/schemas/examples/client.schema.examples.js.map +1 -1
  99. package/dist/schemas/examples/config.schema.examples.d.ts.map +1 -1
  100. package/dist/schemas/examples/config.schema.examples.js +65 -33
  101. package/dist/schemas/examples/config.schema.examples.js.map +1 -1
  102. package/dist/schemas/examples/healthcheck.schema.examples.d.ts.map +1 -1
  103. package/dist/schemas/examples/healthcheck.schema.examples.js +3 -14
  104. package/dist/schemas/examples/healthcheck.schema.examples.js.map +1 -1
  105. package/dist/schemas/examples/play.schema.examples.d.ts.map +1 -1
  106. package/dist/schemas/examples/play.schema.examples.js +94 -130
  107. package/dist/schemas/examples/play.schema.examples.js.map +1 -1
  108. package/dist/schemas/examples/validate.schema.examples.d.ts.map +1 -1
  109. package/dist/schemas/examples/validate.schema.examples.js +51 -41
  110. package/dist/schemas/examples/validate.schema.examples.js.map +1 -1
  111. package/dist/schemas/healthcheck.schema.d.ts +2 -20
  112. package/dist/schemas/healthcheck.schema.d.ts.map +1 -1
  113. package/dist/schemas/healthcheck.schema.js +10 -41
  114. package/dist/schemas/healthcheck.schema.js.map +1 -1
  115. package/dist/schemas/play.schema.d.ts +22 -17
  116. package/dist/schemas/play.schema.d.ts.map +1 -1
  117. package/dist/schemas/play.schema.js +37 -45
  118. package/dist/schemas/play.schema.js.map +1 -1
  119. package/dist/schemas/shared.d.ts +13 -21
  120. package/dist/schemas/shared.d.ts.map +1 -1
  121. package/dist/schemas/shared.js +56 -72
  122. package/dist/schemas/shared.js.map +1 -1
  123. package/dist/schemas/validate.schema.d.ts +11 -7
  124. package/dist/schemas/validate.schema.d.ts.map +1 -1
  125. package/dist/schemas/validate.schema.js +14 -16
  126. package/dist/schemas/validate.schema.js.map +1 -1
  127. package/package.json +5 -2
  128. package/dist/plugins/metrics.plugin.d.ts +0 -9
  129. package/dist/plugins/metrics.plugin.d.ts.map +0 -1
  130. package/dist/plugins/metrics.plugin.js +0 -24
  131. package/dist/plugins/metrics.plugin.js.map +0 -1
  132. package/dist/routes/client.route.d.ts +0 -3
  133. package/dist/routes/client.route.d.ts.map +0 -1
  134. package/dist/routes/client.route.js.map +0 -1
  135. package/dist/routes/healthcheck.route.d.ts.map +0 -1
  136. package/dist/routes/healthcheck.route.js.map +0 -1
  137. /package/dist/{routes → routes-public}/healthcheck.route.d.ts +0 -0
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 ready-to-use HTTP routes, request/response validation, shared TypeBox schemas, API key checks, Sentry integration, and Swagger UI for local development. A game server only needs to register game metadata and route handlers.
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+ (uses `require()` of an ESM-only `typebox` dependency)
10
- - npm package access for `@truelab/trueserver`
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,63 @@ const stakeModes = [{
44
41
  }]
45
42
 
46
43
  registerGameConfig({
47
- name: 'My Game',
44
+ gameId: 'my-game',
45
+ rtpVersion: '96',
48
46
  stakeModes,
49
- hasBuy: false,
50
- hasChoice: false,
51
- hasGamble: false,
52
47
  hasMeta: false,
48
+ hasChoice: false,
49
+ hasRestore: false,
53
50
  hasMultiStake: false,
54
- hasValidation: false,
51
+ hasVariableLevels: false,
55
52
  coinRatio: 10,
53
+ hasValidate: false,
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
- ok: true,
65
- name: 'My Game',
66
- category: ECategory.slots,
67
- stakeModes,
68
- rtp: 96,
69
- hitrate: 25,
70
- volatility: EVolatility.med,
57
+ gameId: 'my-game',
58
+ volatility: 2.5,
59
+ rtp: '96.17',
60
+ rtpVersion: '96',
71
61
  hasMeta: false,
72
62
  hasChoice: false,
73
- hasBuy: false,
63
+ hasRestore: false,
74
64
  hasMultiStake: false,
75
- hasValidation: false,
76
- hasGamble: false,
77
- freeroundsAllowed: false,
65
+ hasVariableLevels: false,
66
+ coinRatio: 10,
78
67
  minWin: 0.1,
79
68
  maxWin: 5000,
80
- apiVersion: 0,
81
- coinRatio: 10,
69
+ freeroundsAllowed: false,
70
+ hasValidate: false,
71
+ stakeModes,
82
72
  }))
83
73
 
84
- handlers.register('play', async (): Promise<TPlayReply> => ({
85
- ok: true,
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('validate', async (): Promise<TValidateReply> => ({
95
- ok: true,
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
+ winCash: 0,
84
+ finished: true,
85
+ hasMaxWin: false,
86
+ }
87
+ })
88
+
89
+ handlers.register('checksum', async (): Promise<TChecksumReply> => ({
90
+ files: [
91
+ { path: 'src/math/spin.js', hash: '...' },
92
+ ],
96
93
  }))
97
94
 
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
+ })
100
+
98
101
  async function main() {
99
102
  const app = new Application()
100
103
  const server = await app.build()
@@ -111,63 +114,74 @@ main().catch((error) => {
111
114
  })
112
115
  ```
113
116
 
114
- `registerGameConfig` must be called before `app.build()`: validation plugins read this config while the server is being built.
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`).
115
118
 
116
119
  ## Runtime API
117
120
 
118
- The server registers these routes:
121
+ `{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
122
 
120
123
  | Method | Path | Handler | Description |
121
124
  | ------ | ---- | ------- | ----------- |
122
- | `POST` | `/client` | `handlers.register('client', ...)` | Client settings payload |
123
- | `POST` | `/config` | `handlers.register('config', ...)` | Public game config |
124
- | `POST` | `/play` | `handlers.register('play', ...)` | Game round calculation |
125
- | `POST` | `/validate` | `handlers.register('validate', ...)` | Optional stake/meta validation |
126
- | `POST` | `/healthcheck` | built in | Service status and metrics |
125
+ | `GET` | `/healthcheck` | built-in | Liveness/readiness probe |
126
+ | `GET` | `/:rtpVersion/config` | `handlers.register('config', ...)` | Game config (RTP-specific) |
127
+ | `GET` | `/:rtpVersion/client` | `handlers.register('client', ...)` | Client-side payload |
128
+ | `GET` | `/:rtpVersion/checksum` | `handlers.register('checksum', ...)` | File hashes for integrity check |
129
+ | `POST` | `/:rtpVersion/play` | `handlers.register('play', ...)` | One step of a round |
130
+ | `POST` | `/:rtpVersion/validate` | `handlers.register('validate', ...)` | Pre-round stake validation — returns `{ valid, reason? }` (enabled when `hasValidate=true`) |
131
+
132
+ Every request must carry the API key header. By default it is `x-api-key: test`; override with `API_HEADER` and `API_KEY`.
133
+
134
+ `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).
127
135
 
128
- All `POST` routes require an API key header. By default the server expects `x-api-key: test`; configure it with `API_HEADER` and `API_KEY`.
136
+ ## Error envelope
129
137
 
130
- `ROUTE_PREFIX` is applied to all routes. For example, `ROUTE_PREFIX=/api/game` turns `/play` into `/api/game/play`.
138
+ Non-2xx responses follow:
131
139
 
132
- ## Imports
140
+ ```json
141
+ {
142
+ "status": false,
143
+ "code": 4001,
144
+ "message": "Invalid choice value"
145
+ }
146
+ ```
133
147
 
134
- Use the root entry for runtime code, schemas, enums, helpers, and public types:
148
+ Recommended code ranges: `1xxx` validation, `2xxx` auth, `4xxx` business, `9xxx` internal. The `EErrorCode` enum exports the codes used by this package.
135
149
 
136
150
  ```typescript
137
- import {
138
- Application,
139
- EStakeType,
140
- PlayRequestSchema,
141
- handlers,
142
- registerGameConfig,
143
- type TPlayReply,
144
- } from '@truelab/trueserver'
151
+ import { EErrorCode, httpError } from '@truelab/trueserver'
152
+
153
+ throw httpError('Invalid choice value', 400, EErrorCode.BusinessChoiceUnknown)
145
154
  ```
146
155
 
147
- Test helpers are intentionally published through a separate entry:
156
+ ## Test utilities
148
157
 
149
158
  ```typescript
150
- import { testBuild } from '@truelab/trueserver/test-utils'
159
+ import {
160
+ checkSchemaPropertyDescriptions,
161
+ testBuild,
162
+ testHeaders,
163
+ validator,
164
+ } from '@truelab/trueserver/test-utils'
151
165
  ```
152
166
 
153
- Published code is compiled into `dist/`; package `exports` exposes only the public root entry and test utilities.
167
+ `test-utils` uses Jest globals (`expect`), so consumers should keep `jest` and usually `@types/jest` in `devDependencies`.
154
168
 
155
- ## Environment Variables
169
+ ## Environment variables
156
170
 
157
171
  | Variable | Default | Description |
158
172
  | -------- | ------- | ----------- |
159
- | `HOST` | `localhost` | Listen host used by the consuming game server |
160
- | `PORT` | `2980` | Listen port used by the consuming game server |
161
- | `ROUTE_PREFIX` | empty | Prefix for all autoloaded routes |
162
- | `API_HEADER` | `x-api-key` | Header name used by the API key plugin |
163
- | `API_KEY` | `test` | Expected API key for `POST` requests |
173
+ | `HOST` | `localhost` | Listen host |
174
+ | `PORT` | `2980` | Listen port |
175
+ | `ROUTE_PREFIX` | empty | Prefix for all routes |
176
+ | `API_HEADER` | `x-api-key` | Header name used by API-key plugin |
177
+ | `API_KEY` | `test` | Expected API key value |
164
178
  | `LOG_LEVEL` | `error` | Pino log level |
165
- | `APP_ENV` | unset | `dev`, `release`, or `master`; controls Swagger and environment mapping |
179
+ | `APP_ENV` | unset | `dev`, `release`, `master`; controls Swagger and environment mapping |
166
180
  | `SENTRY_DSN` | unset | Sentry DSN |
167
181
  | `SENTRY_RATE` | `0.01` | Sentry traces sample rate |
168
- | `GAME_ID` | `0001` | Sentry `game.id` tag |
182
+ | `GAME_ID` | `0001` | Sentry `game.id` tag fallback |
169
183
 
170
- The environment helper imports `dotenv/config`, so `.env` is loaded when `@truelab/trueserver` is imported.
184
+ The environment helper imports `dotenv/config`, so `.env` is loaded automatically.
171
185
 
172
186
  ## Swagger
173
187
 
@@ -175,45 +189,51 @@ Swagger UI is enabled only for `APP_ENV=dev`.
175
189
 
176
190
  - `GET /` redirects to `/docs`
177
191
  - `GET /docs` serves Swagger UI
178
- - `release` and `master` disable Swagger UI
179
192
 
180
- When `ROUTE_PREFIX` is set, it affects game routes, not the Swagger UI route.
193
+ `release` and `master` disable Swagger UI.
181
194
 
182
195
  ## Advanced Validation
183
196
 
184
- The package validates both requests and successful replies with TypeBox schemas. It also performs game-specific checks based on `registerGameConfig`.
197
+ The package validates both requests and replies via TypeBox schemas and runs game-specific checks based on `registerGameConfig`:
185
198
 
186
- - `/config` checks stake modes and requires exactly one of `coinRatio` or `variableLevels`.
187
- - `/play` checks stakes, choices, meta data, gamble flag, and reply consistency.
188
- - `/validate` is available only when `hasValidation` is `true`.
189
- - `test` data is rejected in production mapping (`APP_ENV=master` or unset).
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 }`.
202
+ - `test` field is rejected on production (`APP_ENV=master` or unset).
190
203
 
191
- For stepped games, set `hasChoice: true`; for games with meta state, set `hasMeta: true`; for line/table variants, use `variableLevels` instead of `coinRatio`.
204
+ ## Money-value precision
192
205
 
193
- ## Test Utilities
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.
194
207
 
195
- Integration tests in consuming game servers can import Jest helpers from:
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.
196
209
 
197
- ```typescript
198
- import {
199
- checkSchemaPropertyDescriptions,
200
- testBuild,
201
- testHeaders,
202
- validator,
203
- } from '@truelab/trueserver/test-utils'
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
204
220
  ```
205
221
 
206
- `test-utils` uses Jest globals (`expect`), so the consuming project should keep `jest` and usually `@types/jest` in `devDependencies`.
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
+
224
+ ## Migration
225
+
226
+ This release is breaking. See [MIGRATION.md](MIGRATION.md) for a full mapping of old fields/paths to new ones.
207
227
 
208
228
  ## Package Development
209
229
 
210
230
  ```bash
211
- npm test # run Jest tests
212
- npm run build # compile TypeScript to dist/
213
- npm pack # build and inspect the npm tarball
231
+ npm test
232
+ npm run build
233
+ npm pack
214
234
  ```
215
235
 
216
- `bitbucket-pipelines.yml` runs tests and build on the default branch. The custom `publish` pipeline publishes to npm with `NPM_PUBLISH_TOKEN`.
236
+ `bitbucket-pipelines.yml` runs tests and build on the default branch.
217
237
 
218
238
  ## License
219
239
 
@@ -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;CAiBzC"}
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"}
@@ -22,16 +22,20 @@ class Application {
22
22
  }).withTypeProvider();
23
23
  }
24
24
  async build() {
25
- // Load plugins
26
25
  await this.server.register(autoload_1.default, {
27
26
  dir: path_1.default.join(__dirname, 'plugins'),
28
27
  });
29
- // Load routes
28
+ const prefix = (0, fastify_helper_1.getRoutePrefix)();
29
+ // Version-agnostic routes (healthcheck)
30
+ await this.server.register(autoload_1.default, {
31
+ dir: path_1.default.join(__dirname, 'routes-public'),
32
+ options: { prefix },
33
+ dirNameRoutePrefix: false,
34
+ });
35
+ // Version-scoped routes (config, client, play, validate, checksum)
30
36
  await this.server.register(autoload_1.default, {
31
37
  dir: path_1.default.join(__dirname, 'routes'),
32
- options: {
33
- prefix: (0, fastify_helper_1.getRoutePrefix)(),
34
- },
38
+ options: { prefix: `${prefix}/:rtpVersion` },
35
39
  dirNameRoutePrefix: false,
36
40
  });
37
41
  return this.server;
@@ -1 +1 @@
1
- {"version":3,"file":"application.js","sourceRoot":"","sources":["../application.ts"],"names":[],"mappings":";;;;;AAAA,iEAA+C;AAE/C,sDAA6B;AAC7B,gDAAuB;AAEvB,6DAAyE;AACzE,2DAAuD;AAEvD,MAAqB,WAAW;IAC5B,MAAM,CAAgB;IAEtB;QACI,IAAI,CAAC,MAAM,GAAG,IAAA,iBAAO,EAAC;YAClB,GAAG,EAAE;gBACD,aAAa,EAAE;oBACX,QAAQ,EAAE,CAAC,YAAY,CAAC;iBAC3B;aACJ;YACD,MAAM,EAAE,6BAAa;YACrB,qBAAqB,EAAE,IAAI;SAC9B,CAAC,CAAC,gBAAgB,EAAuB,CAAA;IAC9C,CAAC;IAED,KAAK,CAAC,KAAK;QACX,eAAe;QACX,MAAM,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,kBAAe,EAAE;YACxC,GAAG,EAAE,cAAI,CAAC,IAAI,CAAC,SAAS,EAAE,SAAS,CAAC;SACvC,CAAC,CAAA;QAEF,cAAc;QACd,MAAM,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,kBAAe,EAAE;YACxC,GAAG,EAAE,cAAI,CAAC,IAAI,CAAC,SAAS,EAAE,QAAQ,CAAC;YACnC,OAAO,EAAE;gBACL,MAAM,EAAE,IAAA,+BAAc,GAAE;aAC3B;YACD,kBAAkB,EAAE,KAAK;SAC5B,CAAC,CAAA;QAEF,OAAO,IAAI,CAAC,MAAM,CAAA;IACtB,CAAC;CACJ;AAhCD,8BAgCC"}
1
+ {"version":3,"file":"application.js","sourceRoot":"","sources":["../application.ts"],"names":[],"mappings":";;;;;AAAA,iEAA+C;AAE/C,sDAA6B;AAC7B,gDAAuB;AAEvB,6DAAyE;AACzE,2DAAuD;AAEvD,MAAqB,WAAW;IAC5B,MAAM,CAAgB;IAEtB;QACI,IAAI,CAAC,MAAM,GAAG,IAAA,iBAAO,EAAC;YAClB,GAAG,EAAE;gBACD,aAAa,EAAE;oBACX,QAAQ,EAAE,CAAC,YAAY,CAAC;iBAC3B;aACJ;YACD,MAAM,EAAE,6BAAa;YACrB,qBAAqB,EAAE,IAAI;SAC9B,CAAC,CAAC,gBAAgB,EAAuB,CAAA;IAC9C,CAAC;IAED,KAAK,CAAC,KAAK;QACP,MAAM,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,kBAAe,EAAE;YACxC,GAAG,EAAE,cAAI,CAAC,IAAI,CAAC,SAAS,EAAE,SAAS,CAAC;SACvC,CAAC,CAAA;QAEF,MAAM,MAAM,GAAG,IAAA,+BAAc,GAAE,CAAA;QAE/B,wCAAwC;QACxC,MAAM,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,kBAAe,EAAE;YACxC,GAAG,EAAE,cAAI,CAAC,IAAI,CAAC,SAAS,EAAE,eAAe,CAAC;YAC1C,OAAO,EAAE,EAAE,MAAM,EAAE;YACnB,kBAAkB,EAAE,KAAK;SAC5B,CAAC,CAAA;QAEF,mEAAmE;QACnE,MAAM,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,kBAAe,EAAE;YACxC,GAAG,EAAE,cAAI,CAAC,IAAI,CAAC,SAAS,EAAE,QAAQ,CAAC;YACnC,OAAO,EAAE,EAAE,MAAM,EAAE,GAAG,MAAM,cAAc,EAAE;YAC5C,kBAAkB,EAAE,KAAK;SAC5B,CAAC,CAAA;QAEF,OAAO,IAAI,CAAC,MAAM,CAAA;IACtB,CAAC;CACJ;AAtCD,8BAsCC"}
@@ -1,3 +1,4 @@
1
+ export { checksumReplyExamples } from './schemas/examples/checksum.schema.examples';
1
2
  export { clientReplyExamples } from './schemas/examples/client.schema.examples';
2
3
  export { configReplyExamples } from './schemas/examples/config.schema.examples';
3
4
  export { healthcheckReplyExamples } from './schemas/examples/healthcheck.schema.examples';
@@ -1 +1 @@
1
- {"version":3,"file":"examples.d.ts","sourceRoot":"","sources":["../examples.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,2CAA2C,CAAA;AAC/E,OAAO,EAAE,mBAAmB,EAAE,MAAM,2CAA2C,CAAA;AAC/E,OAAO,EAAE,wBAAwB,EAAE,MAAM,gDAAgD,CAAA;AACzF,OAAO,EACH,iBAAiB,EACjB,mBAAmB,GACtB,MAAM,yCAAyC,CAAA;AAChD,OAAO,EACH,qBAAqB,EACrB,uBAAuB,GAC1B,MAAM,6CAA6C,CAAA"}
1
+ {"version":3,"file":"examples.d.ts","sourceRoot":"","sources":["../examples.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,6CAA6C,CAAA;AACnF,OAAO,EAAE,mBAAmB,EAAE,MAAM,2CAA2C,CAAA;AAC/E,OAAO,EAAE,mBAAmB,EAAE,MAAM,2CAA2C,CAAA;AAC/E,OAAO,EAAE,wBAAwB,EAAE,MAAM,gDAAgD,CAAA;AACzF,OAAO,EACH,iBAAiB,EACjB,mBAAmB,GACtB,MAAM,yCAAyC,CAAA;AAChD,OAAO,EACH,qBAAqB,EACrB,uBAAuB,GAC1B,MAAM,6CAA6C,CAAA"}
package/dist/examples.js CHANGED
@@ -1,6 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.validateRequestExamples = exports.validateReplyExamples = exports.playRequestExamples = exports.playReplyExamples = exports.healthcheckReplyExamples = exports.configReplyExamples = exports.clientReplyExamples = void 0;
3
+ exports.validateRequestExamples = exports.validateReplyExamples = exports.playRequestExamples = exports.playReplyExamples = exports.healthcheckReplyExamples = exports.configReplyExamples = exports.clientReplyExamples = exports.checksumReplyExamples = void 0;
4
+ var checksum_schema_examples_1 = require("./schemas/examples/checksum.schema.examples");
5
+ Object.defineProperty(exports, "checksumReplyExamples", { enumerable: true, get: function () { return checksum_schema_examples_1.checksumReplyExamples; } });
4
6
  var client_schema_examples_1 = require("./schemas/examples/client.schema.examples");
5
7
  Object.defineProperty(exports, "clientReplyExamples", { enumerable: true, get: function () { return client_schema_examples_1.clientReplyExamples; } });
6
8
  var config_schema_examples_1 = require("./schemas/examples/config.schema.examples");
@@ -1 +1 @@
1
- {"version":3,"file":"examples.js","sourceRoot":"","sources":["../examples.ts"],"names":[],"mappings":";;;AAAA,oFAA+E;AAAtE,6HAAA,mBAAmB,OAAA;AAC5B,oFAA+E;AAAtE,6HAAA,mBAAmB,OAAA;AAC5B,8FAAyF;AAAhF,uIAAA,wBAAwB,OAAA;AACjC,gFAGgD;AAF5C,yHAAA,iBAAiB,OAAA;AACjB,2HAAA,mBAAmB,OAAA;AAEvB,wFAGoD;AAFhD,iIAAA,qBAAqB,OAAA;AACrB,mIAAA,uBAAuB,OAAA"}
1
+ {"version":3,"file":"examples.js","sourceRoot":"","sources":["../examples.ts"],"names":[],"mappings":";;;AAAA,wFAAmF;AAA1E,iIAAA,qBAAqB,OAAA;AAC9B,oFAA+E;AAAtE,6HAAA,mBAAmB,OAAA;AAC5B,oFAA+E;AAAtE,6HAAA,mBAAmB,OAAA;AAC5B,8FAAyF;AAAhF,uIAAA,wBAAwB,OAAA;AACjC,gFAGgD;AAF5C,yHAAA,iBAAiB,OAAA;AACjB,2HAAA,mBAAmB,OAAA;AAEvB,wFAGoD;AAFhD,iIAAA,qBAAqB,OAAA;AACrB,mIAAA,uBAAuB,OAAA"}
@@ -1,5 +1,13 @@
1
1
  import { TConfigReply } from '../schemas/config.schema';
2
- export type TGameConfig = Pick<TConfigReply, 'name' | 'stakeModes' | 'hasBuy' | 'hasChoice' | 'hasGamble' | 'hasMeta' | 'hasMultiStake' | 'hasValidation' | 'variableLevels' | 'coinRatio'>;
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'];
10
+ };
3
11
  export declare function registerGameConfig(config: TGameConfig): void;
4
12
  export declare function getGameConfig(): TGameConfig;
5
13
  //# sourceMappingURL=config.helper.d.ts.map
@@ -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;AAEvD,MAAM,MAAM,WAAW,GAAG,IAAI,CAAC,YAAY,EAC3C,MAAM,GACN,YAAY,GACZ,QAAQ,GACR,WAAW,GACX,WAAW,GACX,SAAS,GACT,eAAe,GACf,eAAe,GACf,gBAAgB,GAChB,WAAW,CACV,CAAA;AAID,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,WAAW,QAErD;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"}
@@ -2,8 +2,37 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.registerGameConfig = registerGameConfig;
4
4
  exports.getGameConfig = getGameConfig;
5
+ const fastify_helper_1 = require("./fastify.helper");
5
6
  let gameConfig;
6
7
  function registerGameConfig(config) {
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
+ }
11
+ if (config.hasChoice && !config.hasMeta) {
12
+ throw (0, fastify_helper_1.httpError)('Game config: hasChoice=true requires hasMeta=true', 500);
13
+ }
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) {
18
+ if (!Array.isArray(config.variableLevels) || config.variableLevels.length === 0) {
19
+ throw (0, fastify_helper_1.httpError)('Game config: variableLevels must be a non-empty array when hasVariableLevels=true', 500);
20
+ }
21
+ if (config.defaultLevel === undefined || !config.variableLevels.includes(config.defaultLevel)) {
22
+ throw (0, fastify_helper_1.httpError)('Game config: defaultLevel must be one of variableLevels when hasVariableLevels=true', 500);
23
+ }
24
+ if (config.coinRatio !== undefined) {
25
+ throw (0, fastify_helper_1.httpError)('Game config: coinRatio must not be set when hasVariableLevels=true', 500);
26
+ }
27
+ }
28
+ else {
29
+ if (config.coinRatio === undefined) {
30
+ throw (0, fastify_helper_1.httpError)('Game config: coinRatio is required when hasVariableLevels=false', 500);
31
+ }
32
+ if (config.variableLevels !== undefined || config.defaultLevel !== undefined) {
33
+ throw (0, fastify_helper_1.httpError)('Game config: variableLevels/defaultLevel must not be set when hasVariableLevels=false', 500);
34
+ }
35
+ }
7
36
  gameConfig = config;
8
37
  }
9
38
  function getGameConfig() {
@@ -1 +1 @@
1
- {"version":3,"file":"config.helper.js","sourceRoot":"","sources":["../../helpers/config.helper.ts"],"names":[],"mappings":";;AAiBA,gDAEC;AAED,sCAEC;AARD,IAAI,UAAuB,CAAA;AAE3B,SAAgB,kBAAkB,CAAC,MAAmB;IAClD,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"}
@@ -0,0 +1,19 @@
1
+ export declare const EErrorCode: {
2
+ readonly ValidationBody: 1001;
3
+ readonly ValidationStakes: 1002;
4
+ readonly ValidationMeta: 1003;
5
+ readonly ValidationChoice: 1004;
6
+ readonly ValidationStepIndex: 1005;
7
+ readonly ValidationConfig: 1006;
8
+ readonly ValidationTest: 1007;
9
+ readonly ApiKey: 2001;
10
+ readonly BusinessChoiceUnknown: 4001;
11
+ readonly BusinessMetaShape: 4002;
12
+ readonly BusinessFinishedMismatch: 4003;
13
+ readonly BusinessChoicesMismatch: 4004;
14
+ readonly BusinessMaxWinMismatch: 4005;
15
+ readonly Unhandled: 9001;
16
+ readonly NotFound: 9404;
17
+ };
18
+ export type TErrorCode = (typeof EErrorCode)[keyof typeof EErrorCode];
19
+ //# sourceMappingURL=errors.helper.d.ts.map
@@ -0,0 +1 @@
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"}
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.EErrorCode = void 0;
4
+ exports.EErrorCode = {
5
+ ValidationBody: 1001,
6
+ ValidationStakes: 1002,
7
+ ValidationMeta: 1003,
8
+ ValidationChoice: 1004,
9
+ ValidationStepIndex: 1005,
10
+ ValidationConfig: 1006,
11
+ ValidationTest: 1007,
12
+ ApiKey: 2001,
13
+ BusinessChoiceUnknown: 4001,
14
+ BusinessMetaShape: 4002,
15
+ BusinessFinishedMismatch: 4003,
16
+ BusinessChoicesMismatch: 4004,
17
+ BusinessMaxWinMismatch: 4005,
18
+ Unhandled: 9001,
19
+ NotFound: 9404,
20
+ };
21
+ //# sourceMappingURL=errors.helper.js.map
@@ -0,0 +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,sBAAsB,EAAE,IAAI;IAC5B,SAAS,EAAE,IAAI;IACf,QAAQ,EAAE,IAAI;CACR,CAAA"}
@@ -1,9 +1,13 @@
1
1
  import { TypeBoxTypeProvider } from '@fastify/type-provider-typebox';
2
2
  import { FastifyBaseLogger, FastifyError, FastifyInstance, RawReplyDefaultExpression, RawRequestDefaultExpression, RawServerDefault } from 'fastify';
3
+ import { TErrorCode } from './errors.helper';
3
4
  export type FastifyTypebox = FastifyInstance<RawServerDefault, RawRequestDefaultExpression<RawServerDefault>, RawReplyDefaultExpression<RawServerDefault>, FastifyBaseLogger, TypeBoxTypeProvider>;
5
+ export type FastifyError$Coded = FastifyError & {
6
+ errorCode?: TErrorCode;
7
+ };
4
8
  export declare function registerRoute(callback: (fastify: FastifyTypebox) => Promise<void>): (fastify: FastifyTypebox) => Promise<void>;
5
9
  export declare function registerPlugin(callback: (fastify: FastifyTypebox) => Promise<void>): (fastify: FastifyTypebox) => Promise<void>;
6
10
  export declare function getRoutePrefix(): string;
7
11
  /** This is handled in error.plugin.ts */
8
- export declare function httpError(message: string, statusCode?: number): FastifyError;
12
+ export declare function httpError(message: string, statusCode?: number, code?: TErrorCode): FastifyError$Coded;
9
13
  //# sourceMappingURL=fastify.helper.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"fastify.helper.d.ts","sourceRoot":"","sources":["../../helpers/fastify.helper.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,gCAAgC,CAAA;AACpE,OAAO,EACH,iBAAiB,EAAE,YAAY,EAAE,eAAe,EAChD,yBAAyB,EAAE,2BAA2B,EAAE,gBAAgB,EAC3E,MAAM,SAAS,CAAA;AAKhB,MAAM,MAAM,cAAc,GAAG,eAAe,CAC5C,gBAAgB,EAChB,2BAA2B,CAAC,gBAAgB,CAAC,EAC7C,yBAAyB,CAAC,gBAAgB,CAAC,EAC3C,iBAAiB,EACjB,mBAAmB,CAClB,CAAA;AAED,wBAAgB,aAAa,CAAC,QAAQ,EAAE,CAAC,OAAO,EAAE,cAAc,KAAK,OAAO,CAAE,IAAI,CAAC,aAAjC,cAAc,KAAK,OAAO,CAAE,IAAI,CAAC,CAElF;AAED,wBAAgB,cAAc,CAAC,QAAQ,EAAE,CAAC,OAAO,EAAE,cAAc,KAAK,OAAO,CAAE,IAAI,CAAC,aAAjC,cAAc,KAAK,OAAO,CAAE,IAAI,CAAC,CAEnF;AAED,wBAAgB,cAAc,IAAI,MAAM,CAEvC;AAED,yCAAyC;AACzC,wBAAgB,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,GAAE,MAAY,GAAG,YAAY,CAKjF"}
1
+ {"version":3,"file":"fastify.helper.d.ts","sourceRoot":"","sources":["../../helpers/fastify.helper.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,gCAAgC,CAAA;AACpE,OAAO,EACH,iBAAiB,EAAE,YAAY,EAAE,eAAe,EAChD,yBAAyB,EAAE,2BAA2B,EAAE,gBAAgB,EAC3E,MAAM,SAAS,CAAA;AAIhB,OAAO,EAAc,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAExD,MAAM,MAAM,cAAc,GAAG,eAAe,CAC5C,gBAAgB,EAChB,2BAA2B,CAAC,gBAAgB,CAAC,EAC7C,yBAAyB,CAAC,gBAAgB,CAAC,EAC3C,iBAAiB,EACjB,mBAAmB,CAClB,CAAA;AAED,MAAM,MAAM,kBAAkB,GAAG,YAAY,GAAG;IAAE,SAAS,CAAC,EAAE,UAAU,CAAA;CAAE,CAAA;AAE1E,wBAAgB,aAAa,CAAC,QAAQ,EAAE,CAAC,OAAO,EAAE,cAAc,KAAK,OAAO,CAAE,IAAI,CAAC,aAAjC,cAAc,KAAK,OAAO,CAAE,IAAI,CAAC,CAElF;AAED,wBAAgB,cAAc,CAAC,QAAQ,EAAE,CAAC,OAAO,EAAE,cAAc,KAAK,OAAO,CAAE,IAAI,CAAC,aAAjC,cAAc,KAAK,OAAO,CAAE,IAAI,CAAC,CAEnF;AAED,wBAAgB,cAAc,IAAI,MAAM,CAEvC;AAED,yCAAyC;AACzC,wBAAgB,SAAS,CACrB,OAAO,EAAE,MAAM,EACf,UAAU,GAAE,MAAY,EACxB,IAAI,GAAE,UAAiC,GACxC,kBAAkB,CAMpB"}
@@ -9,6 +9,7 @@ exports.getRoutePrefix = getRoutePrefix;
9
9
  exports.httpError = httpError;
10
10
  const fastify_plugin_1 = __importDefault(require("fastify-plugin"));
11
11
  const env_helper_1 = require("./env.helper");
12
+ const errors_helper_1 = require("./errors.helper");
12
13
  function registerRoute(callback) {
13
14
  return callback;
14
15
  }
@@ -19,9 +20,10 @@ function getRoutePrefix() {
19
20
  return (0, env_helper_1.getenv)('ROUTE_PREFIX', '');
20
21
  }
21
22
  /** This is handled in error.plugin.ts */
22
- function httpError(message, statusCode = 500) {
23
+ function httpError(message, statusCode = 500, code = errors_helper_1.EErrorCode.Unhandled) {
23
24
  const error = new Error(message);
24
25
  error.statusCode = statusCode;
26
+ error.errorCode = code;
25
27
  return error;
26
28
  }
27
29
  //# sourceMappingURL=fastify.helper.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"fastify.helper.js","sourceRoot":"","sources":["../../helpers/fastify.helper.ts"],"names":[],"mappings":";;;;;AAiBA,sCAEC;AAED,wCAEC;AAED,wCAEC;AAGD,8BAKC;AA9BD,oEAA0C;AAE1C,6CAAqC;AAUrC,SAAgB,aAAa,CAAC,QAAqD;IAC/E,OAAO,QAAQ,CAAA;AACnB,CAAC;AAED,SAAgB,cAAc,CAAC,QAAqD;IAChF,OAAO,IAAA,wBAAa,EAAC,QAAQ,CAAC,CAAA;AAClC,CAAC;AAED,SAAgB,cAAc;IAC1B,OAAO,IAAA,mBAAM,EAAC,cAAc,EAAE,EAAE,CAAC,CAAA;AACrC,CAAC;AAED,yCAAyC;AACzC,SAAgB,SAAS,CAAC,OAAe,EAAE,aAAqB,GAAG;IAC/D,MAAM,KAAK,GAAG,IAAI,KAAK,CAAC,OAAO,CAAiB,CAAA;IAChD,KAAK,CAAC,UAAU,GAAG,UAAU,CAAA;IAE7B,OAAO,KAAK,CAAA;AAChB,CAAC"}
1
+ {"version":3,"file":"fastify.helper.js","sourceRoot":"","sources":["../../helpers/fastify.helper.ts"],"names":[],"mappings":";;;;;AAoBA,sCAEC;AAED,wCAEC;AAED,wCAEC;AAGD,8BAUC;AAtCD,oEAA0C;AAE1C,6CAAqC;AACrC,mDAAwD;AAYxD,SAAgB,aAAa,CAAC,QAAqD;IAC/E,OAAO,QAAQ,CAAA;AACnB,CAAC;AAED,SAAgB,cAAc,CAAC,QAAqD;IAChF,OAAO,IAAA,wBAAa,EAAC,QAAQ,CAAC,CAAA;AAClC,CAAC;AAED,SAAgB,cAAc;IAC1B,OAAO,IAAA,mBAAM,EAAC,cAAc,EAAE,EAAE,CAAC,CAAA;AACrC,CAAC;AAED,yCAAyC;AACzC,SAAgB,SAAS,CACrB,OAAe,EACf,aAAqB,GAAG,EACxB,OAAmB,0BAAU,CAAC,SAAS;IAEvC,MAAM,KAAK,GAAG,IAAI,KAAK,CAAC,OAAO,CAAuB,CAAA;IACtD,KAAK,CAAC,UAAU,GAAG,UAAU,CAAA;IAC7B,KAAK,CAAC,SAAS,GAAG,IAAI,CAAA;IAEtB,OAAO,KAAK,CAAA;AAChB,CAAC"}
@@ -1,11 +1,16 @@
1
1
  import { RawReplyDefaultExpression, RawRequestDefaultExpression, RawServerDefault, RouteHandlerMethod } from 'fastify';
2
+ import { TChecksumReply, TChecksumRequest } from '../schemas/checksum.schema';
2
3
  import { TClientReply, TClientRequest } from '../schemas/client.schema';
3
4
  import { TConfigReply, TConfigRequest } from '../schemas/config.schema';
4
5
  import { TPlayReply, TPlayRequest } from '../schemas/play.schema';
5
6
  import { TValidateReply, TValidateRequest } from '../schemas/validate.schema';
6
- export type RouteHandler<TRequest, TReply> = RouteHandlerMethod<RawServerDefault, RawRequestDefaultExpression, RawReplyDefaultExpression, {
7
+ export type TRtpParams = {
8
+ rtpVersion: string;
9
+ };
10
+ export type RouteHandler<TRequest, TReply, TParams = TRtpParams> = RouteHandlerMethod<RawServerDefault, RawRequestDefaultExpression, RawReplyDefaultExpression, {
7
11
  Body: TRequest;
8
12
  Reply: TReply;
13
+ Params: TParams;
9
14
  }>;
10
15
  export declare class HandlersHelper<IHandlers extends {
11
16
  [key: string]: {
@@ -35,5 +40,9 @@ export declare const handlers: HandlersHelper<{
35
40
  request: TClientRequest;
36
41
  reply: TClientReply;
37
42
  };
43
+ checksum: {
44
+ request: TChecksumRequest;
45
+ reply: TChecksumReply;
46
+ };
38
47
  }>;
39
48
  //# sourceMappingURL=handlers.helper.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"handlers.helper.d.ts","sourceRoot":"","sources":["../../helpers/handlers.helper.ts"],"names":[],"mappings":"AAAA,OAAO,EACH,yBAAyB,EAAE,2BAA2B,EAAE,gBAAgB,EAAE,kBAAkB,EAC/F,MAAM,SAAS,CAAA;AAEhB,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAA;AACvE,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAA;AACvE,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAA;AACjE,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAA;AAG7E,MAAM,MAAM,YAAY,CAAC,QAAQ,EAAE,MAAM,IAAI,kBAAkB,CAC/D,gBAAgB,EAChB,2BAA2B,EAC3B,yBAAyB,EACzB;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAChC,CAAA;AAED,qBAAa,cAAc,CAAC,SAAS,SAAS;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG;QAAE,OAAO,EAAE,GAAG,CAAC;QAAC,KAAK,EAAE,GAAG,CAAA;KAAE,CAAA;CAAE;IACzF,SAAS,CAAC,QAAQ,EAAE,GAAG,CAAE,MAAM,EAAE,YAAY,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,CAAY;IAEpE,QAAQ,CAAC,KAAK,SAAS,MAAM,SAAS,EAClC,IAAI,EAAE,KAAK,EACX,OAAO,EAAE,YAAY,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,SAAS,CAAC,EAAE,SAAS,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,CAAC;IAMjF,GAAG,CAAC,KAAK,SAAS,MAAM,SAAS,EAAE,IAAI,EAAE,KAAK,GAAG,YAAY,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,SAAS,CAAC,EAAE,SAAS,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,CAAC;IASrH,GAAG,CAAC,KAAK,SAAS,MAAM,SAAS,EAAE,IAAI,EAAE,KAAK,GAAG,OAAO;CAI3D;AAED,eAAO,MAAM,QAAQ;UACT;QAAE,OAAO,EAAE,YAAY,CAAC;QAAC,KAAK,EAAE,UAAU,CAAA;KAAE;cACxC;QAAE,OAAO,EAAE,gBAAgB,CAAC;QAAC,KAAK,EAAE,cAAc,CAAA;KAAE;YACtD;QAAE,OAAO,EAAE,cAAc,CAAC;QAAC,KAAK,EAAE,YAAY,CAAA;KAAE;YAChD;QAAE,OAAO,EAAE,cAAc,CAAC;QAAC,KAAK,EAAE,YAAY,CAAA;KAAE;EAC1D,CAAA"}
1
+ {"version":3,"file":"handlers.helper.d.ts","sourceRoot":"","sources":["../../helpers/handlers.helper.ts"],"names":[],"mappings":"AAAA,OAAO,EACH,yBAAyB,EAAE,2BAA2B,EAAE,gBAAgB,EAAE,kBAAkB,EAC/F,MAAM,SAAS,CAAA;AAEhB,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAA;AAC7E,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAA;AACvE,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAA;AACvE,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAA;AACjE,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAA;AAI7E,MAAM,MAAM,UAAU,GAAG;IAAE,UAAU,EAAE,MAAM,CAAA;CAAE,CAAA;AAE/C,MAAM,MAAM,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,GAAG,UAAU,IAAI,kBAAkB,CACrF,gBAAgB,EAChB,2BAA2B,EAC3B,yBAAyB,EACzB;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,OAAO,CAAA;CAAE,CACjD,CAAA;AAED,qBAAa,cAAc,CAAC,SAAS,SAAS;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG;QAAE,OAAO,EAAE,GAAG,CAAC;QAAC,KAAK,EAAE,GAAG,CAAA;KAAE,CAAA;CAAE;IACzF,SAAS,CAAC,QAAQ,EAAE,GAAG,CAAE,MAAM,EAAE,YAAY,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,CAAY;IAEpE,QAAQ,CAAC,KAAK,SAAS,MAAM,SAAS,EAClC,IAAI,EAAE,KAAK,EACX,OAAO,EAAE,YAAY,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,SAAS,CAAC,EAAE,SAAS,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,CAAC;IAMjF,GAAG,CAAC,KAAK,SAAS,MAAM,SAAS,EAAE,IAAI,EAAE,KAAK,GAAG,YAAY,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,SAAS,CAAC,EAAE,SAAS,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,CAAC;IASrH,GAAG,CAAC,KAAK,SAAS,MAAM,SAAS,EAAE,IAAI,EAAE,KAAK,GAAG,OAAO;CAI3D;AAED,eAAO,MAAM,QAAQ;UACT;QAAE,OAAO,EAAE,YAAY,CAAC;QAAC,KAAK,EAAE,UAAU,CAAA;KAAE;cACxC;QAAE,OAAO,EAAE,gBAAgB,CAAC;QAAC,KAAK,EAAE,cAAc,CAAA;KAAE;YACtD;QAAE,OAAO,EAAE,cAAc,CAAC;QAAC,KAAK,EAAE,YAAY,CAAA;KAAE;YAChD;QAAE,OAAO,EAAE,cAAc,CAAC;QAAC,KAAK,EAAE,YAAY,CAAA;KAAE;cAC9C;QAAE,OAAO,EAAE,gBAAgB,CAAC;QAAC,KAAK,EAAE,cAAc,CAAA;KAAE;EAChE,CAAA"}
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.handlers = exports.HandlersHelper = void 0;
4
+ const errors_helper_1 = require("./errors.helper");
4
5
  const fastify_helper_1 = require("./fastify.helper");
5
6
  class HandlersHelper {
6
7
  handlers = new Map();
@@ -13,7 +14,7 @@ class HandlersHelper {
13
14
  const handlerName = name;
14
15
  return this.handlers.get(handlerName);
15
16
  }
16
- throw (0, fastify_helper_1.httpError)(`Handler "${String(name)}" is not registered`);
17
+ throw (0, fastify_helper_1.httpError)(`Handler "${String(name)}" is not registered`, 500, errors_helper_1.EErrorCode.Unhandled);
17
18
  }
18
19
  has(name) {
19
20
  const handlerName = name;
@@ -1 +1 @@
1
- {"version":3,"file":"handlers.helper.js","sourceRoot":"","sources":["../../helpers/handlers.helper.ts"],"names":[],"mappings":";;;AAQA,qDAA4C;AAS5C,MAAa,cAAc;IACb,QAAQ,GAAyC,IAAI,GAAG,EAAE,CAAA;IAEpE,QAAQ,CACJ,IAAW,EACX,OAA6E;QAE7E,MAAM,WAAW,GAAG,IAAc,CAAA;QAClC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,WAAW,EAAE,OAAO,CAAC,CAAA;IAC3C,CAAC;IAED,GAAG,CAAgC,IAAW;QAC1C,IAAI,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;YACjB,MAAM,WAAW,GAAG,IAAc,CAAA;YAClC,OAAO,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,WAAW,CAAC,CAAA;QACzC,CAAC;QAED,MAAM,IAAA,0BAAS,EAAC,YAAY,MAAM,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAAA;IAClE,CAAC;IAED,GAAG,CAAgC,IAAW;QAC1C,MAAM,WAAW,GAAG,IAAc,CAAA;QAClC,OAAO,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,WAAW,CAAC,CAAA;IACzC,CAAC;CACJ;AAxBD,wCAwBC;AAEY,QAAA,QAAQ,GAAG,IAAI,cAAc,EAKtC,CAAA"}
1
+ {"version":3,"file":"handlers.helper.js","sourceRoot":"","sources":["../../helpers/handlers.helper.ts"],"names":[],"mappings":";;;AASA,mDAA4C;AAC5C,qDAA4C;AAW5C,MAAa,cAAc;IACb,QAAQ,GAAyC,IAAI,GAAG,EAAE,CAAA;IAEpE,QAAQ,CACJ,IAAW,EACX,OAA6E;QAE7E,MAAM,WAAW,GAAG,IAAc,CAAA;QAClC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,WAAW,EAAE,OAAO,CAAC,CAAA;IAC3C,CAAC;IAED,GAAG,CAAgC,IAAW;QAC1C,IAAI,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;YACjB,MAAM,WAAW,GAAG,IAAc,CAAA;YAClC,OAAO,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,WAAW,CAAC,CAAA;QACzC,CAAC;QAED,MAAM,IAAA,0BAAS,EAAC,YAAY,MAAM,CAAC,IAAI,CAAC,qBAAqB,EAAE,GAAG,EAAE,0BAAU,CAAC,SAAS,CAAC,CAAA;IAC7F,CAAC;IAED,GAAG,CAAgC,IAAW;QAC1C,MAAM,WAAW,GAAG,IAAc,CAAA;QAClC,OAAO,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,WAAW,CAAC,CAAA;IACzC,CAAC;CACJ;AAxBD,wCAwBC;AAEY,QAAA,QAAQ,GAAG,IAAI,cAAc,EAMtC,CAAA"}