@clovnet/casino-sdk 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +148 -0
- package/dist/client-B3-Y0Xan.d.cts +2167 -0
- package/dist/client-BxHPrkxp.d.ts +2167 -0
- package/dist/contract-DtFe4bRy.d.cts +205 -0
- package/dist/contract-DtFe4bRy.d.ts +205 -0
- package/dist/index.cjs +2337 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +56 -0
- package/dist/index.d.ts +56 -0
- package/dist/index.js +2298 -0
- package/dist/index.js.map +1 -0
- package/dist/react/index.cjs +382 -0
- package/dist/react/index.cjs.map +1 -0
- package/dist/react/index.d.cts +118 -0
- package/dist/react/index.d.ts +118 -0
- package/dist/react/index.js +370 -0
- package/dist/react/index.js.map +1 -0
- package/dist/realtime/index.cjs +407 -0
- package/dist/realtime/index.cjs.map +1 -0
- package/dist/realtime/index.d.cts +6 -0
- package/dist/realtime/index.d.ts +6 -0
- package/dist/realtime/index.js +403 -0
- package/dist/realtime/index.js.map +1 -0
- package/package.json +111 -0
|
@@ -0,0 +1,2167 @@
|
|
|
1
|
+
import { j as WebSocketImpl, a as ConnectionState, d as RealtimeChannel, C as ChannelEventMap, U as Unsubscribe, e as RealtimeError } from './contract-DtFe4bRy.cjs';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Plugin-actions (ext) transport contract — mirrored verbatim from the runtime's
|
|
5
|
+
* frozen plugin-actions wire contract (see `docs/CASINO_SDK_EXT_BUILD_PROMPT.md` §2,
|
|
6
|
+
* built in the runtime repo from `PLUGIN_ACTIONS_RUNTIME_BUILD_PROMPT.md`).
|
|
7
|
+
*
|
|
8
|
+
* Discovery endpoints:
|
|
9
|
+
*
|
|
10
|
+
* ```
|
|
11
|
+
* GET /api/ext/_catalog → ExtCatalog (ETag + Cache-Control: max-age=60)
|
|
12
|
+
* GET /api/ext/_catalog/:pluginKey → ExtCatalogPlugin (404 if plugin not enabled for tenant)
|
|
13
|
+
* ```
|
|
14
|
+
*
|
|
15
|
+
* Action calls are plain HTTP to `PluginActionDescriptor.path` over the SDK's
|
|
16
|
+
* standard transport. Plugin payloads are opaque to the SDK — no minor-unit money
|
|
17
|
+
* conversion happens on `ext` responses; consult each plugin's docs for its shapes.
|
|
18
|
+
*
|
|
19
|
+
* This file also hosts the typed-client machinery ({@link ExtRegistry} and friends)
|
|
20
|
+
* used by generated `@cwe-plugins/<key>-client` packages. It is types-only so the
|
|
21
|
+
* error model can reference {@link ExtPluginClient} without import cycles.
|
|
22
|
+
*/
|
|
23
|
+
interface ExtCatalog {
|
|
24
|
+
catalogVersion: 1;
|
|
25
|
+
tenantId: string;
|
|
26
|
+
plugins: ExtCatalogPlugin[];
|
|
27
|
+
}
|
|
28
|
+
interface ExtCatalogPlugin {
|
|
29
|
+
pluginKey: string;
|
|
30
|
+
/** Installed plugin semver. */
|
|
31
|
+
version: string;
|
|
32
|
+
title: string;
|
|
33
|
+
actions: PluginActionDescriptor[];
|
|
34
|
+
/** Realtime channels the plugin may publish, e.g. `["ext.cashback"]`. */
|
|
35
|
+
channels: string[];
|
|
36
|
+
/** Optional widget descriptors. */
|
|
37
|
+
frontend?: PluginFrontendDecl;
|
|
38
|
+
}
|
|
39
|
+
interface PluginActionDescriptor {
|
|
40
|
+
key: string;
|
|
41
|
+
title: string;
|
|
42
|
+
description?: string;
|
|
43
|
+
kind: "query" | "mutation";
|
|
44
|
+
/** Mutation requires an `Idempotency-Key` header (auto-generated if absent). */
|
|
45
|
+
idempotent?: boolean;
|
|
46
|
+
deprecated?: string;
|
|
47
|
+
method: "GET" | "POST" | "PUT" | "DELETE";
|
|
48
|
+
/** Absolute, e.g. `"/api/ext/cashback/claim"`. */
|
|
49
|
+
path: string;
|
|
50
|
+
/** `"player"` ⇒ session cookie required (the SDK handles this as usual). */
|
|
51
|
+
auth: "public" | "player";
|
|
52
|
+
/** JSON Schema draft-07 for params/query/body. */
|
|
53
|
+
input?: unknown;
|
|
54
|
+
/** JSON Schema, when declared. */
|
|
55
|
+
output?: unknown;
|
|
56
|
+
/** Plugin event types this action may cause. */
|
|
57
|
+
emits?: string[];
|
|
58
|
+
}
|
|
59
|
+
interface PluginFrontendDecl {
|
|
60
|
+
widgets?: Array<{
|
|
61
|
+
key: string;
|
|
62
|
+
title: string;
|
|
63
|
+
slot: "lobby" | "account" | "cashier" | "game-sidebar";
|
|
64
|
+
/** A `kind: "query"` action key. */
|
|
65
|
+
dataAction: string;
|
|
66
|
+
/** Mutation action keys. */
|
|
67
|
+
actions?: string[];
|
|
68
|
+
}>;
|
|
69
|
+
}
|
|
70
|
+
/** HTTP methods a plugin action may declare. */
|
|
71
|
+
type ExtMethod = "GET" | "POST" | "PUT" | "DELETE";
|
|
72
|
+
interface ExtCallOpts {
|
|
73
|
+
/** Use this idempotency key instead of an auto-generated one. */
|
|
74
|
+
idempotencyKey?: string;
|
|
75
|
+
/** Abort signal. */
|
|
76
|
+
signal?: AbortSignal;
|
|
77
|
+
}
|
|
78
|
+
/** Init for the {@link ExtPluginClient.request} escape hatch. */
|
|
79
|
+
interface ExtRequestInit extends ExtCallOpts {
|
|
80
|
+
/** Query params; `undefined`/`null` are dropped, arrays repeat the key. */
|
|
81
|
+
query?: Record<string, unknown>;
|
|
82
|
+
/** JSON body (plugins validate `.strict()` — send exactly the declared keys). */
|
|
83
|
+
body?: unknown;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Lazy per-plugin handle returned by `sdk.ext(pluginKey)`. Nothing is fetched
|
|
87
|
+
* until `call()`/`request()` is used.
|
|
88
|
+
*/
|
|
89
|
+
interface ExtPluginClient {
|
|
90
|
+
/** The plugin this handle targets. */
|
|
91
|
+
readonly pluginKey: string;
|
|
92
|
+
/**
|
|
93
|
+
* Call a catalog-declared action. Resolves the action descriptor from the
|
|
94
|
+
* (cached) catalog, routes `input` per the action's method, and auto-generates
|
|
95
|
+
* an `Idempotency-Key` for idempotent mutations.
|
|
96
|
+
*/
|
|
97
|
+
call<TResult = unknown>(actionKey: string, input?: unknown, opts?: ExtCallOpts): Promise<TResult>;
|
|
98
|
+
/**
|
|
99
|
+
* Escape hatch for declared routes not in the catalog (same transport, no
|
|
100
|
+
* descriptor lookup). `path` is relative to the plugin mount:
|
|
101
|
+
* `request("GET", "/games/starburst")` → `GET /api/ext/<pluginKey>/games/starburst`.
|
|
102
|
+
*/
|
|
103
|
+
request<TResult = unknown>(method: ExtMethod, path: string, init?: ExtRequestInit): Promise<TResult>;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Per-action shape a generated client declares: `input` (omit for none) and
|
|
107
|
+
* `output` (omit for `unknown`).
|
|
108
|
+
*/
|
|
109
|
+
interface ExtActionShape {
|
|
110
|
+
input?: unknown;
|
|
111
|
+
output?: unknown;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Augmentation point for generated plugin clients. Empty by default; a generated
|
|
115
|
+
* package adds its plugin under its key:
|
|
116
|
+
*
|
|
117
|
+
* ```ts
|
|
118
|
+
* declare module "@clovnet/casino-sdk" {
|
|
119
|
+
* interface ExtRegistry {
|
|
120
|
+
* cashback: {
|
|
121
|
+
* getSummary: { output: CashbackSummary };
|
|
122
|
+
* claim: { input: { periodId: string }; output: ClaimResult };
|
|
123
|
+
* };
|
|
124
|
+
* }
|
|
125
|
+
* }
|
|
126
|
+
* ```
|
|
127
|
+
*
|
|
128
|
+
* With that in scope, `sdk.ext("cashback")` returns a {@link TypedExtPluginClient}
|
|
129
|
+
* whose `call()` narrows action keys, input, and result. Unregistered plugin keys
|
|
130
|
+
* keep the loosely-typed {@link ExtPluginClient}.
|
|
131
|
+
*/
|
|
132
|
+
interface ExtRegistry {
|
|
133
|
+
}
|
|
134
|
+
/** The declared input type of an action shape (`undefined` when it takes none; an optional `input?:` admits `undefined`). */
|
|
135
|
+
type ExtActionInput<S> = "input" extends keyof S ? S extends {
|
|
136
|
+
input?: infer I;
|
|
137
|
+
} ? {} extends Pick<S, "input" & keyof S> ? I | undefined : I : never : undefined;
|
|
138
|
+
/** The declared output type of an action shape (`unknown` when undeclared). */
|
|
139
|
+
type ExtActionOutput<S> = "output" extends keyof S ? S extends {
|
|
140
|
+
output?: infer O;
|
|
141
|
+
} ? O : never : unknown;
|
|
142
|
+
/** `call()` argument tuple for an action shape: input is optional only when it may be omitted. */
|
|
143
|
+
type ExtCallArgs<S> = [ExtActionInput<S>] extends [undefined] ? [input?: undefined, opts?: ExtCallOpts] : undefined extends ExtActionInput<S> ? [input?: ExtActionInput<S>, opts?: ExtCallOpts] : [input: ExtActionInput<S>, opts?: ExtCallOpts];
|
|
144
|
+
/**
|
|
145
|
+
* The narrowed handle `sdk.ext(pluginKey)` returns when `pluginKey` is registered
|
|
146
|
+
* in {@link ExtRegistry}. Transport is identical to {@link ExtPluginClient} — this
|
|
147
|
+
* is typing only.
|
|
148
|
+
*/
|
|
149
|
+
interface TypedExtPluginClient<A> {
|
|
150
|
+
readonly pluginKey: string;
|
|
151
|
+
call<K extends keyof A & string>(actionKey: K, ...args: ExtCallArgs<A[K]>): Promise<ExtActionOutput<A[K]>>;
|
|
152
|
+
request<TResult = unknown>(method: ExtMethod, path: string, init?: ExtRequestInit): Promise<TResult>;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Error model.
|
|
157
|
+
*
|
|
158
|
+
* The runtime returns a single envelope shape on failure:
|
|
159
|
+
*
|
|
160
|
+
* ```json
|
|
161
|
+
* { "error": { "code": "INSUFFICIENT_FUNDS", "message": "...", "details": { } } }
|
|
162
|
+
* ```
|
|
163
|
+
*
|
|
164
|
+
* `mapEnvelope` turns that (plus the HTTP status) into a typed {@link CasinoSdkError}
|
|
165
|
+
* subclass so app code can `instanceof`-branch instead of string-matching codes.
|
|
166
|
+
*/
|
|
167
|
+
|
|
168
|
+
/** The runtime's `AppError` envelope, as seen on the wire. */
|
|
169
|
+
interface AppErrorEnvelope {
|
|
170
|
+
error: {
|
|
171
|
+
code: string;
|
|
172
|
+
message: string;
|
|
173
|
+
details?: unknown;
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
/** Type guard for the runtime error envelope. */
|
|
177
|
+
declare function isAppErrorEnvelope(value: unknown): value is AppErrorEnvelope;
|
|
178
|
+
/**
|
|
179
|
+
* Base class for every error the SDK throws. Carries the stable machine-readable
|
|
180
|
+
* `code`, the HTTP `status` (0 for transport/client-side failures), and the
|
|
181
|
+
* runtime's `details` payload when present.
|
|
182
|
+
*/
|
|
183
|
+
declare class CasinoSdkError extends Error {
|
|
184
|
+
readonly code: string;
|
|
185
|
+
readonly status: number;
|
|
186
|
+
readonly details?: unknown;
|
|
187
|
+
constructor(code: string, message: string, status?: number, details?: unknown);
|
|
188
|
+
}
|
|
189
|
+
/** 401 — session missing/invalid/expired. Often triggers an auto-refresh + retry. */
|
|
190
|
+
declare class AuthError extends CasinoSdkError {
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* 403 — authenticated but not allowed. This includes CSRF double-submit failures:
|
|
194
|
+
* the runtime throws them as a plain `FORBIDDEN` (see `packages/core/src/csrf.ts`
|
|
195
|
+
* in the runtime), so there is deliberately no separate CSRF error class here.
|
|
196
|
+
*/
|
|
197
|
+
declare class ForbiddenError extends CasinoSdkError {
|
|
198
|
+
}
|
|
199
|
+
/** 400 — request failed validation. `details.issues` lists the per-field problems. */
|
|
200
|
+
declare class ValidationError extends CasinoSdkError {
|
|
201
|
+
}
|
|
202
|
+
/** 404 — resource not found. */
|
|
203
|
+
declare class NotFoundError extends CasinoSdkError {
|
|
204
|
+
}
|
|
205
|
+
/** 409 — conflict or invalid state transition. */
|
|
206
|
+
declare class ConflictError extends CasinoSdkError {
|
|
207
|
+
}
|
|
208
|
+
/** 422 — wallet balance too low for the requested debit (`INSUFFICIENT_FUNDS`). */
|
|
209
|
+
declare class InsufficientFundsError extends CasinoSdkError {
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* 422 — the request was semantically rejected and no more-specific domain code
|
|
213
|
+
* matched. The neutral fallback for unknown 422s: a future non-money 422 must not
|
|
214
|
+
* `instanceof`-match app branches written for insufficient funds.
|
|
215
|
+
*/
|
|
216
|
+
declare class UnprocessableError extends CasinoSdkError {
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* 422 — a cashier gate plugin denied the operation (`DEPOSIT_DENIED`,
|
|
220
|
+
* `WITHDRAWAL_DENIED`, `DEPOSIT_LIMIT_EXCEEDED`, `WITHDRAWAL_LIMIT_EXCEEDED`).
|
|
221
|
+
* `details.pluginId` names the deciding plugin.
|
|
222
|
+
*/
|
|
223
|
+
declare class OperationDeniedError extends CasinoSdkError {
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* 429 — rate limited. On verification-request cooldowns `details.resendIn`
|
|
227
|
+
* gives the seconds to wait before retrying.
|
|
228
|
+
*/
|
|
229
|
+
declare class RateLimitError extends CasinoSdkError {
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* 409 `FLOW_DELEGATED` — this tenant delegates the flow (signup/deposit/
|
|
233
|
+
* withdrawal/KYC) to a plugin; drive the plugin's actions via `sdk.ext`
|
|
234
|
+
* instead. `details: { flow, pluginKey }`.
|
|
235
|
+
*/
|
|
236
|
+
declare class FlowDelegatedError extends ConflictError {
|
|
237
|
+
/** The plugin that owns the delegated flow (from `details.pluginKey`). */
|
|
238
|
+
readonly pluginKey: string;
|
|
239
|
+
/** The delegated flow kind (from `details.flow`), e.g. `"deposit"`. */
|
|
240
|
+
readonly flow: string | undefined;
|
|
241
|
+
/**
|
|
242
|
+
* Set (best-effort, by the SDK) when the ext catalog shows an action named
|
|
243
|
+
* after the flow — `sdk.ext(pluginKey).call(continueWith.actionKey, …)` is
|
|
244
|
+
* then the way to continue. Left `undefined` when the catalog lookup fails;
|
|
245
|
+
* the original error is never masked.
|
|
246
|
+
*/
|
|
247
|
+
continueWith?: {
|
|
248
|
+
actionKey: string;
|
|
249
|
+
};
|
|
250
|
+
constructor(code: string, message: string, status?: number, details?: unknown);
|
|
251
|
+
/** Sugar for `sdk.ext(this.pluginKey)` — the plugin client to continue the flow with. */
|
|
252
|
+
resolve(sdk: {
|
|
253
|
+
ext: (pluginKey: string) => ExtPluginClient;
|
|
254
|
+
}): ExtPluginClient;
|
|
255
|
+
}
|
|
256
|
+
/**
|
|
257
|
+
* 409 `RG_BLOCKED` — a responsible-gaming state blocks the operation.
|
|
258
|
+
* `details: { reason: "cool_off" | "self_excluded", until: string | null }`.
|
|
259
|
+
*/
|
|
260
|
+
declare class RgBlockedError extends CasinoSdkError {
|
|
261
|
+
}
|
|
262
|
+
/**
|
|
263
|
+
* 409 `LIMIT_EXCEEDED` — a responsible-gaming limit blocks the operation.
|
|
264
|
+
* `details: { kind, period, remainingMinor, resetsAt }`.
|
|
265
|
+
*/
|
|
266
|
+
declare class LimitExceededError extends CasinoSdkError {
|
|
267
|
+
}
|
|
268
|
+
/** 5xx — the runtime failed internally. */
|
|
269
|
+
declare class ServerError extends CasinoSdkError {
|
|
270
|
+
}
|
|
271
|
+
/** No HTTP response (DNS/offline/abort/CORS). `status` is 0. */
|
|
272
|
+
declare class NetworkError extends CasinoSdkError {
|
|
273
|
+
}
|
|
274
|
+
/**
|
|
275
|
+
* A capability the SDK exposes for surface-completeness, but the runtime does not
|
|
276
|
+
* implement yet. See the roadmap in `docs/SDK_BIBLE.md`.
|
|
277
|
+
*/
|
|
278
|
+
declare class NotImplementedError extends CasinoSdkError {
|
|
279
|
+
constructor(message: string);
|
|
280
|
+
}
|
|
281
|
+
/**
|
|
282
|
+
* The plugin is not installed/enabled for this tenant — either a 404 from an
|
|
283
|
+
* `/api/ext/*` route (the runtime makes disabled and not-installed
|
|
284
|
+
* indistinguishable) or the plugin is absent from a fresh catalog (then thrown
|
|
285
|
+
* client-side with `status` 0 and code `PLUGIN_NOT_ENABLED`, no network call).
|
|
286
|
+
*/
|
|
287
|
+
declare class PluginNotEnabledError extends NotFoundError {
|
|
288
|
+
readonly pluginKey: string;
|
|
289
|
+
constructor(pluginKey: string, options?: {
|
|
290
|
+
code?: string;
|
|
291
|
+
message?: string;
|
|
292
|
+
status?: number;
|
|
293
|
+
details?: unknown;
|
|
294
|
+
});
|
|
295
|
+
}
|
|
296
|
+
/**
|
|
297
|
+
* The action key is not among the plugin's catalog descriptors. Thrown
|
|
298
|
+
* client-side (status 0) without a network call when the catalog is fresh.
|
|
299
|
+
*/
|
|
300
|
+
declare class PluginActionNotFoundError extends NotFoundError {
|
|
301
|
+
readonly pluginKey: string;
|
|
302
|
+
readonly actionKey: string;
|
|
303
|
+
constructor(pluginKey: string, actionKey: string);
|
|
304
|
+
}
|
|
305
|
+
/**
|
|
306
|
+
* The plugin handler timed out or crashed — a 504 from an `/api/ext/*` route or
|
|
307
|
+
* the envelope code `PLUGIN_ROUTE_ERROR` (also the legacy `PLUGIN_ROUTE_TIMEOUT`).
|
|
308
|
+
*/
|
|
309
|
+
declare class PluginUnavailableError extends ServerError {
|
|
310
|
+
}
|
|
311
|
+
/**
|
|
312
|
+
* A generated plugin client's expected major version does not match the
|
|
313
|
+
* installed plugin's version in the catalog. Thrown client-side by
|
|
314
|
+
* `assertPluginVersion` (status 0).
|
|
315
|
+
*/
|
|
316
|
+
declare class PluginVersionMismatchError extends ConflictError {
|
|
317
|
+
readonly pluginKey: string;
|
|
318
|
+
readonly expectedRange: string;
|
|
319
|
+
readonly actualVersion: string;
|
|
320
|
+
constructor(pluginKey: string, expectedRange: string, actualVersion: string);
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
/** Minimal cookie store used when no browser cookie jar exists. */
|
|
324
|
+
declare class CookieJar {
|
|
325
|
+
private readonly store;
|
|
326
|
+
/** Read a cookie value by name, or undefined. */
|
|
327
|
+
get(name: string): string | undefined;
|
|
328
|
+
/** Serialize all cookies into a `Cookie` request-header value. */
|
|
329
|
+
header(): string | undefined;
|
|
330
|
+
/** Clear the jar (e.g. on logout). */
|
|
331
|
+
clear(): void;
|
|
332
|
+
/**
|
|
333
|
+
* Ingest `set-cookie` header(s) from a response. Only the `name=value` pair is
|
|
334
|
+
* retained (attributes like Path/HttpOnly are ignored — this jar is a dev/SSR
|
|
335
|
+
* convenience, not a spec-complete cookie store). A `Max-Age<=0` or an `Expires`
|
|
336
|
+
* in the past deletes the cookie; `Max-Age` wins when both are present.
|
|
337
|
+
*/
|
|
338
|
+
ingest(setCookie: string | string[] | null | undefined): void;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
/**
|
|
342
|
+
* Client configuration.
|
|
343
|
+
*/
|
|
344
|
+
|
|
345
|
+
/** A `fetch`-compatible function. Defaults to the platform global. */
|
|
346
|
+
type FetchLike = typeof fetch;
|
|
347
|
+
/** Cookie names the runtime sets. Stable; surfaced for advanced use. */
|
|
348
|
+
declare const COOKIE: {
|
|
349
|
+
readonly access: "cwe_access_token";
|
|
350
|
+
readonly refresh: "cwe_refresh_token";
|
|
351
|
+
readonly csrf: "cwe_csrf";
|
|
352
|
+
};
|
|
353
|
+
/** Header names the SDK sends. */
|
|
354
|
+
declare const HEADER: {
|
|
355
|
+
readonly tenant: "x-tenant-id";
|
|
356
|
+
readonly brand: "x-brand-id";
|
|
357
|
+
readonly region: "x-region";
|
|
358
|
+
readonly csrf: "x-csrf-token";
|
|
359
|
+
readonly idempotency: "idempotency-key";
|
|
360
|
+
};
|
|
361
|
+
interface CasinoClientConfig {
|
|
362
|
+
/** Base URL of the runtime HTTP API, e.g. `https://api.staging.example`. */
|
|
363
|
+
baseUrl: string;
|
|
364
|
+
/**
|
|
365
|
+
* WebSocket URL of the realtime gateway, e.g. `wss://api.staging.example/realtime`.
|
|
366
|
+
* Optional — only needed if you use `sdk.realtime`. Defaults to `baseUrl` with
|
|
367
|
+
* the protocol swapped to ws(s) and `/realtime` appended.
|
|
368
|
+
*/
|
|
369
|
+
wsUrl?: string;
|
|
370
|
+
/**
|
|
371
|
+
* Tenant id sent as `x-tenant-id` on every request — as *declared intent*,
|
|
372
|
+
* not as the selector. Since the runtime's H1 trusted tenant resolution, the
|
|
373
|
+
* tenant is determined by the request host (`baseUrl`'s origin → the
|
|
374
|
+
* runtime's trusted host→tenant map, else its env base); an untrusted
|
|
375
|
+
* `x-tenant-id` is ignored. The header is only honored when a trusted
|
|
376
|
+
* internal proxy forwards it with `x-internal-tenant-token`. Point `baseUrl`
|
|
377
|
+
* at the correct brand host; multi-brand apps need one client per brand
|
|
378
|
+
* base URL.
|
|
379
|
+
*/
|
|
380
|
+
tenantId: string;
|
|
381
|
+
/** Optional brand id sent as `x-brand-id`. */
|
|
382
|
+
brandId?: string;
|
|
383
|
+
/** Optional region sent as `x-region`. */
|
|
384
|
+
region?: string;
|
|
385
|
+
/** Default fiat currency for wallet/cashier/launch calls (ISO 4217). Default `EUR`. */
|
|
386
|
+
defaultCurrency?: string;
|
|
387
|
+
/** Injected fetch (Node < 18, tests, custom agents). Defaults to global `fetch`. */
|
|
388
|
+
fetch?: FetchLike;
|
|
389
|
+
/**
|
|
390
|
+
* Called when a request fails auth even after one refresh attempt. Use it to
|
|
391
|
+
* route the user to login / clear app state. Receives the originating error.
|
|
392
|
+
*/
|
|
393
|
+
onAuthError?: (error: CasinoSdkError) => void;
|
|
394
|
+
/**
|
|
395
|
+
* Disable the automatic 401 → refresh → retry behavior (default enabled).
|
|
396
|
+
* Turn off if you manage refresh yourself.
|
|
397
|
+
*/
|
|
398
|
+
autoRefresh?: boolean;
|
|
399
|
+
/**
|
|
400
|
+
* Node/SSR only: the cookie jar this client reads and writes (the browser manages
|
|
401
|
+
* its own cookies). Defaults to a fresh private jar. On a server that handles many
|
|
402
|
+
* users, never share one jar (or one module-scope client) across requests — use
|
|
403
|
+
* `client.withCookies(new CookieJar())` per incoming request instead.
|
|
404
|
+
*/
|
|
405
|
+
cookieJar?: CookieJar;
|
|
406
|
+
/**
|
|
407
|
+
* Default per-request timeout in milliseconds — a black-holed request rejects
|
|
408
|
+
* with `NetworkError` (code `NETWORK_TIMEOUT`) instead of hanging forever, so
|
|
409
|
+
* `deposit()`/`withdraw()`/`login()` always settle. Default 30 000; `0` disables.
|
|
410
|
+
* Calls that legitimately run long expose a per-call override — e.g.
|
|
411
|
+
* `kyc.uploadDocument(input, { timeoutMs })` for large files on slow uplinks.
|
|
412
|
+
*/
|
|
413
|
+
timeoutMs?: number;
|
|
414
|
+
}
|
|
415
|
+
/** Internal, fully-resolved config with defaults applied. */
|
|
416
|
+
interface ResolvedConfig extends Required<Omit<CasinoClientConfig, "brandId" | "region" | "onAuthError" | "cookieJar">> {
|
|
417
|
+
brandId: string | undefined;
|
|
418
|
+
region: string | undefined;
|
|
419
|
+
onAuthError: ((error: CasinoSdkError) => void) | undefined;
|
|
420
|
+
cookieJar: CookieJar | undefined;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
/**
|
|
424
|
+
* The isomorphic HTTP layer every module calls through.
|
|
425
|
+
*
|
|
426
|
+
* Responsibilities (so modules and app code never deal with them):
|
|
427
|
+
* - inject `x-tenant-id` (+ optional brand/region) from config — declared intent
|
|
428
|
+
* only: the runtime resolves the tenant from the request host (H1) and ignores
|
|
429
|
+
* untrusted `x-tenant-*` headers,
|
|
430
|
+
* - `credentials: "include"` for the cookie session; in Node, replay/ingest a
|
|
431
|
+
* {@link CookieJar},
|
|
432
|
+
* - double-submit CSRF: read `cwe_csrf`, send `x-csrf-token` on mutations,
|
|
433
|
+
* - auto idempotency keys for money ops (body field + header),
|
|
434
|
+
* - decode the `AppError` envelope into a typed {@link CasinoSdkError},
|
|
435
|
+
* - on 401, attempt a single refresh + retry, then fire `onAuthError` — the refresh
|
|
436
|
+
* is single-flight across concurrent 401s and never refreshes itself,
|
|
437
|
+
* - apply the default request timeout (`timeoutMs`, 30s) → `NETWORK_TIMEOUT`.
|
|
438
|
+
*/
|
|
439
|
+
|
|
440
|
+
type Method = "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
|
|
441
|
+
interface RequestOptions {
|
|
442
|
+
/** Query params; `undefined`/`null` are dropped, arrays repeat the key. */
|
|
443
|
+
query?: Record<string, string | number | boolean | undefined | null | readonly string[]>;
|
|
444
|
+
/**
|
|
445
|
+
* Request body. A plain value is JSON-encoded; a `FormData` is sent as
|
|
446
|
+
* multipart (content-type left to fetch so the boundary is set) — used by
|
|
447
|
+
* KYC document uploads.
|
|
448
|
+
*/
|
|
449
|
+
body?: unknown;
|
|
450
|
+
/**
|
|
451
|
+
* Mark this a money operation: the SDK injects an idempotency key into both the
|
|
452
|
+
* body (`idempotencyKey`) and the `idempotency-key` header.
|
|
453
|
+
*/
|
|
454
|
+
idempotent?: boolean;
|
|
455
|
+
/** Provide your own idempotency key instead of an auto-generated one. */
|
|
456
|
+
idempotencyKey?: string;
|
|
457
|
+
/**
|
|
458
|
+
* With `idempotent`, send the key as the `idempotency-key` header only and leave
|
|
459
|
+
* the body untouched. Plugin ext routes validate bodies `.strict()` and read the
|
|
460
|
+
* key from the header, so an injected body field would be rejected.
|
|
461
|
+
*/
|
|
462
|
+
idempotencyHeaderOnly?: boolean;
|
|
463
|
+
/** Force-enable/disable the CSRF header (defaults: on for mutations). */
|
|
464
|
+
csrf?: boolean;
|
|
465
|
+
/** Extra request headers, merged after the SDK's own (advanced; e.g. `if-none-match`). */
|
|
466
|
+
headers?: Record<string, string>;
|
|
467
|
+
/** Abort signal. */
|
|
468
|
+
signal?: AbortSignal;
|
|
469
|
+
/** Per-request timeout in ms, overriding `config.timeoutMs` (`0` disables). */
|
|
470
|
+
timeoutMs?: number;
|
|
471
|
+
/** Internal: prevents infinite refresh recursion. */
|
|
472
|
+
_isRetry?: boolean;
|
|
473
|
+
/**
|
|
474
|
+
* Internal: this request must never enter the 401 → refresh → retry path, and a
|
|
475
|
+
* 401 on it does not fire `onAuthError` itself. Set on the refresh POST: when
|
|
476
|
+
* refresh 401s (fully logged out), the *originating* request reports the auth
|
|
477
|
+
* failure exactly once instead of the refresh request recursing into itself.
|
|
478
|
+
*/
|
|
479
|
+
_noRefresh?: boolean;
|
|
480
|
+
/**
|
|
481
|
+
* Internal: skip the error enricher for this request. Set on the ext catalog
|
|
482
|
+
* reads the enricher itself performs, so an erroring catalog can never
|
|
483
|
+
* re-enter its own error path.
|
|
484
|
+
*/
|
|
485
|
+
_noEnrich?: boolean;
|
|
486
|
+
}
|
|
487
|
+
/** Result of a {@link HttpClient.conditionalGet} (ETag revalidation). */
|
|
488
|
+
interface ConditionalGetResult<T> {
|
|
489
|
+
/** True when the server answered `304 Not Modified` — keep your cached copy. */
|
|
490
|
+
notModified: boolean;
|
|
491
|
+
/** The parsed body (present on 2xx, absent on 304). */
|
|
492
|
+
data?: T;
|
|
493
|
+
/** The response `ETag`, to send back as `etag` on the next call. */
|
|
494
|
+
etag?: string;
|
|
495
|
+
/** Parsed `Cache-Control: max-age=N`, in seconds. */
|
|
496
|
+
maxAgeSeconds?: number;
|
|
497
|
+
}
|
|
498
|
+
declare class HttpClient {
|
|
499
|
+
private readonly config;
|
|
500
|
+
readonly jar: CookieJar;
|
|
501
|
+
private refresh;
|
|
502
|
+
private refreshInflight;
|
|
503
|
+
private enrich;
|
|
504
|
+
constructor(config: ResolvedConfig);
|
|
505
|
+
/** Wired by the client after the auth module exists (avoids a circular import). */
|
|
506
|
+
setRefreshHandler(fn: () => Promise<void>): void;
|
|
507
|
+
/**
|
|
508
|
+
* Wired by the client: a best-effort pass over every decoded error before it is
|
|
509
|
+
* thrown (e.g. populating `FlowDelegatedError.continueWith` from the ext
|
|
510
|
+
* catalog). Whatever it does or throws, the original error is what propagates.
|
|
511
|
+
*/
|
|
512
|
+
setErrorEnricher(fn: (error: CasinoSdkError) => Promise<void> | void): void;
|
|
513
|
+
get<T>(path: string, options?: Omit<RequestOptions, "body">): Promise<T>;
|
|
514
|
+
post<T>(path: string, options?: RequestOptions): Promise<T>;
|
|
515
|
+
put<T>(path: string, options?: RequestOptions): Promise<T>;
|
|
516
|
+
patch<T>(path: string, options?: RequestOptions): Promise<T>;
|
|
517
|
+
delete<T>(path: string, options?: RequestOptions): Promise<T>;
|
|
518
|
+
request<T>(method: Method, path: string, options?: RequestOptions): Promise<T>;
|
|
519
|
+
/**
|
|
520
|
+
* GET with ETag revalidation: sends `If-None-Match` when `etag` is given and
|
|
521
|
+
* treats `304 Not Modified` as success (empty `data`). Used for endpoints that
|
|
522
|
+
* declare HTTP caching, e.g. the ext plugin catalog.
|
|
523
|
+
*/
|
|
524
|
+
conditionalGet<T>(path: string, options?: {
|
|
525
|
+
etag?: string;
|
|
526
|
+
signal?: AbortSignal;
|
|
527
|
+
_noEnrich?: boolean;
|
|
528
|
+
}): Promise<ConditionalGetResult<T>>;
|
|
529
|
+
/** The shared pipeline: headers, CSRF, idempotency, cookies, fetch, 401→refresh→retry. */
|
|
530
|
+
private performFetch;
|
|
531
|
+
/**
|
|
532
|
+
* Run the wired refresh handler with single-flight dedupe (same pattern as the
|
|
533
|
+
* ext catalog): concurrent 401s all await ONE in-flight `POST /auth/player/refresh`.
|
|
534
|
+
* With rotating refresh tokens, parallel refreshes would present an already-rotated
|
|
535
|
+
* token and get the whole session revoked.
|
|
536
|
+
*/
|
|
537
|
+
private runRefresh;
|
|
538
|
+
private parseBody;
|
|
539
|
+
private toError;
|
|
540
|
+
private buildUrl;
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
/**
|
|
544
|
+
* Money.
|
|
545
|
+
*
|
|
546
|
+
* The runtime keeps money to **4 decimal places** (`@cwe/shared/money.ts`, `SCALE=4`).
|
|
547
|
+
* Three encodings appear on the wire and the SDK reconciles them to ONE:
|
|
548
|
+
*
|
|
549
|
+
* - REST **responses** carry 4-dp **decimal strings** — `"12.5000"`.
|
|
550
|
+
* - REST **request bodies** take plain JSON **numbers** in major units — `12.5`
|
|
551
|
+
* (the runtime's Zod schemas are `z.number()`; it normalizes to 4-dp internally).
|
|
552
|
+
* - Realtime carries the same value as a **scale-4 integer** — `125000`.
|
|
553
|
+
*
|
|
554
|
+
* The SDK's public surface always uses {@link MinorUnits} (the scale-4 integer),
|
|
555
|
+
* matching `docs/realtime-sdk-contract.ts`. Convert at the REST boundary with
|
|
556
|
+
* {@link decimalToMinor} (read) / {@link minorToAmount} (write), and display with
|
|
557
|
+
* {@link formatMoney}. Never do money math beyond display.
|
|
558
|
+
*/
|
|
559
|
+
/** Integer minor units at scale 4 (i.e. major × 10_000). `125000` ⇒ `12.5000`. */
|
|
560
|
+
type MinorUnits = number;
|
|
561
|
+
/** Decimal places the runtime stores. Do not change without the runtime changing. */
|
|
562
|
+
declare const MONEY_SCALE = 4;
|
|
563
|
+
/**
|
|
564
|
+
* Convert a runtime decimal string (or number) to scale-4 minor units.
|
|
565
|
+
* `"12.5000"` ⇒ `125000`. Throws on non-numeric input.
|
|
566
|
+
*/
|
|
567
|
+
declare function decimalToMinor(value: string | number): MinorUnits;
|
|
568
|
+
/**
|
|
569
|
+
* Parse a runtime **minor-unit integer string** (`"1000000"` ⇒ `1000000`) — the
|
|
570
|
+
* encoding the player history surfaces (`/player/cashier/*`, `/player/bets`,
|
|
571
|
+
* `/player/game-sessions`, `/player/limits`) use, unlike the 4-dp decimal
|
|
572
|
+
* strings elsewhere (use {@link decimalToMinor} for those).
|
|
573
|
+
*
|
|
574
|
+
* Strict by design: only canonical digit strings (`/^-?\d+$/`) within the safe
|
|
575
|
+
* integer range are accepted. A 4-dp decimal that happens to be integral
|
|
576
|
+
* (`"50.0000"`) is a WRONG-SURFACE value — accepting it would misread €50.00 as
|
|
577
|
+
* 50 minor units (a 10 000× error) — and an over-`MAX_SAFE_INTEGER` string would
|
|
578
|
+
* silently lose minor units. Both throw so contract drift is loud.
|
|
579
|
+
*/
|
|
580
|
+
declare function minorStringToMinor(value: string): MinorUnits;
|
|
581
|
+
/**
|
|
582
|
+
* Convert scale-4 minor units to the plain major-unit **number** the runtime's
|
|
583
|
+
* write schemas expect (`amount: z.number()`). `125000` ⇒ `12.5`. Safe for the
|
|
584
|
+
* 4-dp range: a scale-4 integer divided by 10⁴ round-trips exactly through JSON.
|
|
585
|
+
*/
|
|
586
|
+
declare function minorToAmount(minor: MinorUnits): number;
|
|
587
|
+
/**
|
|
588
|
+
* Convert scale-4 minor units back to a canonical 4-dp decimal string
|
|
589
|
+
* (`125000` ⇒ `"12.5000"`) — the format REST **responses** use. Kept public for
|
|
590
|
+
* symmetry/display; note the runtime's write paths take numbers, not strings
|
|
591
|
+
* (use {@link minorToAmount} there).
|
|
592
|
+
*/
|
|
593
|
+
declare function minorToDecimal(minor: MinorUnits): string;
|
|
594
|
+
/**
|
|
595
|
+
* Format minor units for display in the currency's natural precision.
|
|
596
|
+
* `formatMoney(125000, "EUR")` ⇒ `"€12.50"` (en locale). Uses `Intl.NumberFormat`;
|
|
597
|
+
* pass `locale` to localize, or `{ minimumFractionDigits }` to show all 4 dp.
|
|
598
|
+
*/
|
|
599
|
+
declare function formatMoney(minor: MinorUnits, currency: string, options?: {
|
|
600
|
+
locale?: string;
|
|
601
|
+
minimumFractionDigits?: number;
|
|
602
|
+
maximumFractionDigits?: number;
|
|
603
|
+
}): string;
|
|
604
|
+
|
|
605
|
+
/**
|
|
606
|
+
* Transport types — the player-facing HTTP contract, hand-authored from the
|
|
607
|
+
* runtime route files + Zod schemas (the "manual fallback" of contract sync;
|
|
608
|
+
* see `docs/SDK_BIBLE.md` → Versioning for the swagger follow-up).
|
|
609
|
+
*
|
|
610
|
+
* Money fields that the runtime serializes as 4-dp decimal strings are typed as
|
|
611
|
+
* `MinorUnits` here because the SDK converts them at the transport boundary
|
|
612
|
+
* (`src/modules/*`), so app code only ever sees scale-4 integers.
|
|
613
|
+
*
|
|
614
|
+
* Targets runtime API: see `RUNTIME_API_VERSION` in `src/index.ts`.
|
|
615
|
+
*/
|
|
616
|
+
|
|
617
|
+
type PlayerStatus = "active" | "suspended" | "closed" | "pending_verification" | "self_excluded";
|
|
618
|
+
/** Shared pagination block on the new player-facing history endpoints. */
|
|
619
|
+
interface PageInfo {
|
|
620
|
+
limit: number;
|
|
621
|
+
offset: number;
|
|
622
|
+
/** Total rows for the player, across all pages. */
|
|
623
|
+
total: number;
|
|
624
|
+
/** True when `offset + returned < total`. */
|
|
625
|
+
hasMore: boolean;
|
|
626
|
+
}
|
|
627
|
+
interface Player {
|
|
628
|
+
id: string;
|
|
629
|
+
tenantId: string;
|
|
630
|
+
externalId: string | null;
|
|
631
|
+
username: string | null;
|
|
632
|
+
email: string | null;
|
|
633
|
+
phone: string | null;
|
|
634
|
+
status: PlayerStatus;
|
|
635
|
+
createdAt: string;
|
|
636
|
+
updatedAt: string;
|
|
637
|
+
}
|
|
638
|
+
interface PlayerProfile {
|
|
639
|
+
firstName?: string | null;
|
|
640
|
+
lastName?: string | null;
|
|
641
|
+
birthDate?: string | null;
|
|
642
|
+
gender?: string | null;
|
|
643
|
+
country?: string | null;
|
|
644
|
+
city?: string | null;
|
|
645
|
+
address?: string | null;
|
|
646
|
+
language?: string | null;
|
|
647
|
+
currency?: string | null;
|
|
648
|
+
timezone?: string | null;
|
|
649
|
+
avatarUrl?: string | null;
|
|
650
|
+
}
|
|
651
|
+
/** Three wallet buckets, all in scale-4 minor units. */
|
|
652
|
+
interface WalletBuckets {
|
|
653
|
+
cash: MinorUnits;
|
|
654
|
+
bonus: MinorUnits;
|
|
655
|
+
locked: MinorUnits;
|
|
656
|
+
}
|
|
657
|
+
interface Paginated<T> {
|
|
658
|
+
rows: T[];
|
|
659
|
+
total: number;
|
|
660
|
+
limit: number;
|
|
661
|
+
offset: number;
|
|
662
|
+
}
|
|
663
|
+
/**
|
|
664
|
+
* Attribution hints carried from the affiliate click chain into signup. Optional
|
|
665
|
+
* and best-effort — signup never fails on them — but they can only be captured
|
|
666
|
+
* here: a click chain not carried at signup is unrecoverable. `clickId` is the
|
|
667
|
+
* value returned by {@link TrackClickResult} (persist it client-side after
|
|
668
|
+
* `affiliate.trackClick` and pass it through here).
|
|
669
|
+
*/
|
|
670
|
+
interface SignupAttribution {
|
|
671
|
+
/** The `clickId` from `affiliate.trackClick` — the click → registration join key. */
|
|
672
|
+
clickId?: string;
|
|
673
|
+
/** Affiliate public tracking code, when the `clickId` was not captured. */
|
|
674
|
+
affiliateCode?: string;
|
|
675
|
+
deviceFingerprint?: string;
|
|
676
|
+
}
|
|
677
|
+
interface SignupInput {
|
|
678
|
+
email: string;
|
|
679
|
+
password: string;
|
|
680
|
+
username?: string;
|
|
681
|
+
profile?: {
|
|
682
|
+
firstName?: string;
|
|
683
|
+
lastName?: string;
|
|
684
|
+
country?: string;
|
|
685
|
+
currency?: string;
|
|
686
|
+
language?: string;
|
|
687
|
+
};
|
|
688
|
+
/** Affiliate attribution captured at click time; see {@link SignupAttribution}. */
|
|
689
|
+
attribution?: SignupAttribution;
|
|
690
|
+
}
|
|
691
|
+
interface LoginInput {
|
|
692
|
+
email: string;
|
|
693
|
+
password: string;
|
|
694
|
+
}
|
|
695
|
+
/** Response of signup/login/refresh/social-callback. Cookies are set by the runtime. */
|
|
696
|
+
interface AuthSession {
|
|
697
|
+
player: Player;
|
|
698
|
+
csrfToken: string;
|
|
699
|
+
}
|
|
700
|
+
interface MeResponse {
|
|
701
|
+
player: Player;
|
|
702
|
+
profile: PlayerProfile | null;
|
|
703
|
+
}
|
|
704
|
+
type SocialProvider = "google" | "discord" | "apple" | "telegram" | "facebook" | "twitter" | "github" | "custom";
|
|
705
|
+
interface SocialAccount {
|
|
706
|
+
id: string;
|
|
707
|
+
tenantId: string;
|
|
708
|
+
playerId: string;
|
|
709
|
+
provider: string;
|
|
710
|
+
providerUserId: string;
|
|
711
|
+
providerEmail?: string | null;
|
|
712
|
+
providerUsername?: string | null;
|
|
713
|
+
providerAvatarUrl?: string | null;
|
|
714
|
+
/** Raw provider profile payload as stored by the runtime. */
|
|
715
|
+
providerData?: unknown;
|
|
716
|
+
linkedAt: string;
|
|
717
|
+
}
|
|
718
|
+
/**
|
|
719
|
+
* Player-facing view of a login session — the "your devices" surface. Omits the
|
|
720
|
+
* refresh-token hash and rotation linkage (server-internal). Dates are ISO strings
|
|
721
|
+
* on the wire.
|
|
722
|
+
*/
|
|
723
|
+
interface SafeSession {
|
|
724
|
+
id: string;
|
|
725
|
+
/** True for the session this request is authenticated with ("this device"). */
|
|
726
|
+
current: boolean;
|
|
727
|
+
/** Not revoked and not expired. */
|
|
728
|
+
active: boolean;
|
|
729
|
+
device: {
|
|
730
|
+
type: string | null;
|
|
731
|
+
os: string | null;
|
|
732
|
+
browser: string | null;
|
|
733
|
+
model: string | null;
|
|
734
|
+
/** Human label, e.g. "Chrome on macOS". */
|
|
735
|
+
label: string;
|
|
736
|
+
};
|
|
737
|
+
location: {
|
|
738
|
+
city: string | null;
|
|
739
|
+
region: string | null;
|
|
740
|
+
country: string | null;
|
|
741
|
+
};
|
|
742
|
+
ip: string | null;
|
|
743
|
+
createdAt: string;
|
|
744
|
+
/** Refreshed on token rotation (throttled server-side); null for old sessions. */
|
|
745
|
+
lastSeenAt: string | null;
|
|
746
|
+
expiresAt: string;
|
|
747
|
+
revokedAt: string | null;
|
|
748
|
+
}
|
|
749
|
+
interface SessionHistoryQuery {
|
|
750
|
+
/** Page size, 1–100. Runtime default 50. */
|
|
751
|
+
limit?: number;
|
|
752
|
+
/** Runtime default 0. */
|
|
753
|
+
offset?: number;
|
|
754
|
+
}
|
|
755
|
+
/** One page of session history (`GET /auth/player/sessions/history`). */
|
|
756
|
+
interface SessionHistoryPage {
|
|
757
|
+
sessions: SafeSession[];
|
|
758
|
+
pagination: {
|
|
759
|
+
limit: number;
|
|
760
|
+
offset: number;
|
|
761
|
+
/** Total sessions for the player, across all pages. */
|
|
762
|
+
total: number;
|
|
763
|
+
/** True when `offset + returned < total`. */
|
|
764
|
+
hasMore: boolean;
|
|
765
|
+
};
|
|
766
|
+
}
|
|
767
|
+
/**
|
|
768
|
+
* One login attempt (`GET /auth/player/login-history`). `failReason` values
|
|
769
|
+
* observed from the runtime: `invalid_credentials`, `account_inactive`,
|
|
770
|
+
* `account_locked`; null on success.
|
|
771
|
+
*/
|
|
772
|
+
interface LoginAttempt {
|
|
773
|
+
id: string;
|
|
774
|
+
at: string;
|
|
775
|
+
ip: string | null;
|
|
776
|
+
device: {
|
|
777
|
+
type: string | null;
|
|
778
|
+
os: string | null;
|
|
779
|
+
browser: string | null;
|
|
780
|
+
label: string;
|
|
781
|
+
};
|
|
782
|
+
location: {
|
|
783
|
+
country: string | null;
|
|
784
|
+
city: string | null;
|
|
785
|
+
};
|
|
786
|
+
method: "password" | "social" | "refresh";
|
|
787
|
+
success: boolean;
|
|
788
|
+
failReason: string | null;
|
|
789
|
+
}
|
|
790
|
+
interface LoginHistoryPage {
|
|
791
|
+
attempts: LoginAttempt[];
|
|
792
|
+
pagination: PageInfo;
|
|
793
|
+
}
|
|
794
|
+
/**
|
|
795
|
+
* `200` of email/phone verification request. `expiresIn` is the credential TTL
|
|
796
|
+
* in seconds; `resendIn` is the cooldown before another request is accepted
|
|
797
|
+
* (a premature retry returns 429 `RATE_LIMITED` with `details.resendIn`).
|
|
798
|
+
*/
|
|
799
|
+
interface VerificationRequestResult {
|
|
800
|
+
success: true;
|
|
801
|
+
expiresIn: number;
|
|
802
|
+
resendIn: number;
|
|
803
|
+
}
|
|
804
|
+
/**
|
|
805
|
+
* The full profile row as `GET /player/profile` serializes it (unlike the
|
|
806
|
+
* partial {@link PlayerProfile} on `/auth/player/me`, every field is present,
|
|
807
|
+
* nullable). `birthDate` is a `YYYY-MM-DD` string.
|
|
808
|
+
*/
|
|
809
|
+
interface PlayerProfileRecord {
|
|
810
|
+
playerId: string;
|
|
811
|
+
tenantId: string;
|
|
812
|
+
firstName: string | null;
|
|
813
|
+
lastName: string | null;
|
|
814
|
+
birthDate: string | null;
|
|
815
|
+
gender: string | null;
|
|
816
|
+
country: string | null;
|
|
817
|
+
city: string | null;
|
|
818
|
+
address: string | null;
|
|
819
|
+
language: string | null;
|
|
820
|
+
currency: string | null;
|
|
821
|
+
timezone: string | null;
|
|
822
|
+
avatarUrl: string | null;
|
|
823
|
+
createdAt: string;
|
|
824
|
+
updatedAt: string;
|
|
825
|
+
}
|
|
826
|
+
interface ProfileResponse {
|
|
827
|
+
/** Null until the player has a profile row. */
|
|
828
|
+
profile: PlayerProfileRecord | null;
|
|
829
|
+
/**
|
|
830
|
+
* Identity fields locked after KYC approval (`firstName`/`lastName`/
|
|
831
|
+
* `birthDate`/`country`); patching them fails 409 `PROFILE_FIELD_LOCKED`.
|
|
832
|
+
*/
|
|
833
|
+
lockedFields: string[];
|
|
834
|
+
}
|
|
835
|
+
/**
|
|
836
|
+
* Body of `PATCH /player/profile`. All fields optional, at least one required;
|
|
837
|
+
* `null` clears a field. Identity fields are rejected after KYC approval.
|
|
838
|
+
*/
|
|
839
|
+
interface UpdateProfileInput {
|
|
840
|
+
firstName?: string | null;
|
|
841
|
+
lastName?: string | null;
|
|
842
|
+
/** `YYYY-MM-DD`. */
|
|
843
|
+
birthDate?: string | null;
|
|
844
|
+
gender?: string | null;
|
|
845
|
+
/** ISO 3166-1 alpha-2. */
|
|
846
|
+
country?: string | null;
|
|
847
|
+
city?: string | null;
|
|
848
|
+
address?: string | null;
|
|
849
|
+
language?: string | null;
|
|
850
|
+
timezone?: string | null;
|
|
851
|
+
avatarUrl?: string | null;
|
|
852
|
+
}
|
|
853
|
+
interface UpdateProfileResult {
|
|
854
|
+
profile: PlayerProfileRecord;
|
|
855
|
+
/** Keys whose value actually changed (may be empty for a no-op patch). */
|
|
856
|
+
changedFields: string[];
|
|
857
|
+
}
|
|
858
|
+
type OddsFormat = "decimal" | "fractional" | "american";
|
|
859
|
+
interface PlayerPreferences {
|
|
860
|
+
language: string | null;
|
|
861
|
+
timezone: string | null;
|
|
862
|
+
displayCurrency: string | null;
|
|
863
|
+
oddsFormat: string | null;
|
|
864
|
+
marketing: {
|
|
865
|
+
email: boolean;
|
|
866
|
+
sms: boolean;
|
|
867
|
+
push: boolean;
|
|
868
|
+
};
|
|
869
|
+
/** Reality-check interval in minutes (5–240); null = disabled. */
|
|
870
|
+
realityCheckMinutes: number | null;
|
|
871
|
+
}
|
|
872
|
+
/** Body of `PUT /player/preferences` — all optional, at least one required. */
|
|
873
|
+
interface SetPreferencesInput {
|
|
874
|
+
language?: string;
|
|
875
|
+
timezone?: string;
|
|
876
|
+
/** ISO 4217. */
|
|
877
|
+
displayCurrency?: string;
|
|
878
|
+
oddsFormat?: OddsFormat;
|
|
879
|
+
marketing?: {
|
|
880
|
+
email?: boolean;
|
|
881
|
+
sms?: boolean;
|
|
882
|
+
push?: boolean;
|
|
883
|
+
};
|
|
884
|
+
/** 5–240 minutes, or null to disable the reality check. */
|
|
885
|
+
realityCheckMinutes?: number | null;
|
|
886
|
+
}
|
|
887
|
+
/** One append-only consent-trail entry. Keys today: `marketing.{email,sms,push}`. */
|
|
888
|
+
interface ConsentEntry {
|
|
889
|
+
consentKey: string;
|
|
890
|
+
granted: boolean;
|
|
891
|
+
at: string;
|
|
892
|
+
source: string;
|
|
893
|
+
}
|
|
894
|
+
interface ConsentsPage {
|
|
895
|
+
consents: ConsentEntry[];
|
|
896
|
+
pagination: PageInfo;
|
|
897
|
+
}
|
|
898
|
+
/**
|
|
899
|
+
* GDPR export request state. `POST /player/account/export` answers `202` with
|
|
900
|
+
* `status: "pending"`; `GET /player/account/export` returns the latest request,
|
|
901
|
+
* or the bare `{ status: "none" }` when none was ever requested. The download
|
|
902
|
+
* link is delivered out-of-band, not through these endpoints.
|
|
903
|
+
*/
|
|
904
|
+
type DataExportStatus = {
|
|
905
|
+
status: "none";
|
|
906
|
+
} | {
|
|
907
|
+
id: string;
|
|
908
|
+
status: "pending" | "processing" | "completed" | "failed";
|
|
909
|
+
requestedAt: string;
|
|
910
|
+
finishedAt: string | null;
|
|
911
|
+
expiresAt: string | null;
|
|
912
|
+
};
|
|
913
|
+
type LimitKind = "deposit" | "loss" | "wager" | "session_time";
|
|
914
|
+
/** UTC windows; `week` starts ISO Monday 00:00 UTC. */
|
|
915
|
+
type LimitPeriod = "day" | "week" | "month";
|
|
916
|
+
/**
|
|
917
|
+
* One active limit (`GET /player/limits`). `value` is scale-4 minor units for
|
|
918
|
+
* the money kinds (`deposit`/`loss`/`wager`) and **whole minutes** for
|
|
919
|
+
* `session_time`. Ratchet semantics: a new limit or a decrease applies
|
|
920
|
+
* immediately (`pending: null`); an increase or removal is stored in `pending`
|
|
921
|
+
* and applied at `pending.activeAt` (24h cooldown).
|
|
922
|
+
*/
|
|
923
|
+
interface PlayerLimit {
|
|
924
|
+
kind: LimitKind;
|
|
925
|
+
period: LimitPeriod;
|
|
926
|
+
value: MinorUnits;
|
|
927
|
+
currency: string | null;
|
|
928
|
+
activeFrom: string;
|
|
929
|
+
pending: {
|
|
930
|
+
/** The value that will apply (null for a pending removal). */
|
|
931
|
+
value: MinorUnits | null;
|
|
932
|
+
removal: boolean;
|
|
933
|
+
activeAt: string;
|
|
934
|
+
} | null;
|
|
935
|
+
}
|
|
936
|
+
/**
|
|
937
|
+
* One entry of `PUT /player/limits`. `value` in scale-4 minor units (minutes
|
|
938
|
+
* for `session_time`); `null` requests removal of the existing limit.
|
|
939
|
+
*/
|
|
940
|
+
interface SetLimitInput {
|
|
941
|
+
kind: LimitKind;
|
|
942
|
+
period: LimitPeriod;
|
|
943
|
+
value: MinorUnits | null;
|
|
944
|
+
/** ISO 4217, for money kinds. */
|
|
945
|
+
currency?: string;
|
|
946
|
+
}
|
|
947
|
+
/** Cool-off blocks gameplay + deposits (login/withdrawal stay); extend-only. */
|
|
948
|
+
type CoolOffPeriod = "24h" | "48h" | "7d" | "30d";
|
|
949
|
+
/** Self-exclusion is irreversible player-side; `permanent` has no end date. */
|
|
950
|
+
type SelfExclusionPeriod = "6m" | "1y" | "5y" | "permanent";
|
|
951
|
+
type KycTrigger = "manual" | "signup" | "deposit_total_threshold" | "withdrawal_amount_threshold" | "withdrawal_total_threshold" | "risk_flag";
|
|
952
|
+
type KycRequestStatus = "draft" | "submitted" | "in_review" | "approved" | "rejected" | "needs_more";
|
|
953
|
+
type KycDocumentStatus = "uploaded" | "in_review" | "approved" | "rejected";
|
|
954
|
+
type PlayerKycStatus = "none" | "pending" | "approved" | "rejected" | "expired";
|
|
955
|
+
/** `GET /player/kyc/status`. */
|
|
956
|
+
interface KycStatusResponse {
|
|
957
|
+
/** Verification level 0–3. */
|
|
958
|
+
level: number;
|
|
959
|
+
status: PlayerKycStatus;
|
|
960
|
+
approvedAt: string | null;
|
|
961
|
+
emailVerified: boolean;
|
|
962
|
+
phoneVerified: boolean;
|
|
963
|
+
gates: {
|
|
964
|
+
withdrawal: {
|
|
965
|
+
requiredLevel: number;
|
|
966
|
+
met: boolean;
|
|
967
|
+
};
|
|
968
|
+
};
|
|
969
|
+
}
|
|
970
|
+
/** Latest upload for a checklist item (metadata only — players cannot download). */
|
|
971
|
+
interface KycItemDocument {
|
|
972
|
+
id: string;
|
|
973
|
+
status: KycDocumentStatus;
|
|
974
|
+
fileName: string;
|
|
975
|
+
uploadedAt: string;
|
|
976
|
+
rejectionReason: string | null;
|
|
977
|
+
}
|
|
978
|
+
interface KycChecklistItem {
|
|
979
|
+
/** e.g. `passport`, `id_front`, `proof_of_address`, `selfie`. */
|
|
980
|
+
documentTypeKey: string;
|
|
981
|
+
title: string;
|
|
982
|
+
required: boolean;
|
|
983
|
+
sort: number;
|
|
984
|
+
source: "set" | "needs_more" | "plugin";
|
|
985
|
+
document: KycItemDocument | null;
|
|
986
|
+
}
|
|
987
|
+
/** One open KYC request (`GET /player/kyc/requirements`). */
|
|
988
|
+
interface KycRequest {
|
|
989
|
+
id: string;
|
|
990
|
+
trigger: KycTrigger;
|
|
991
|
+
status: KycRequestStatus;
|
|
992
|
+
targetLevel: number | null;
|
|
993
|
+
createdAt: string;
|
|
994
|
+
items: KycChecklistItem[];
|
|
995
|
+
}
|
|
996
|
+
interface KycHistoryEntry {
|
|
997
|
+
id: string;
|
|
998
|
+
trigger: KycTrigger;
|
|
999
|
+
status: KycRequestStatus;
|
|
1000
|
+
targetLevel: number | null;
|
|
1001
|
+
decisionReasonCode: string | null;
|
|
1002
|
+
createdAt: string;
|
|
1003
|
+
reviewedAt: string | null;
|
|
1004
|
+
}
|
|
1005
|
+
interface KycHistoryPage {
|
|
1006
|
+
requests: KycHistoryEntry[];
|
|
1007
|
+
pagination: PageInfo;
|
|
1008
|
+
}
|
|
1009
|
+
/**
|
|
1010
|
+
* A KYC document upload. Accepted content: JPEG/PNG/PDF, sniffed server-side
|
|
1011
|
+
* from magic bytes (the declared content-type is ignored); 25 MB transport cap,
|
|
1012
|
+
* per-type cap (default 10 MB) enforced at 422 `KYC_DOCUMENT_INVALID`.
|
|
1013
|
+
*/
|
|
1014
|
+
interface KycUploadInput {
|
|
1015
|
+
/** Attach to an open request (`draft`/`needs_more`); omit for a proactive upload. */
|
|
1016
|
+
requestId?: string;
|
|
1017
|
+
documentTypeKey: string;
|
|
1018
|
+
/** The file content. In Node pass a `Blob`/`File` (both global on Node ≥20). */
|
|
1019
|
+
file: Blob;
|
|
1020
|
+
/** Filename sent in the multipart part; defaults to `File.name` or `document`. */
|
|
1021
|
+
fileName?: string;
|
|
1022
|
+
/** `YYYY-MM-DD`; required for document types with `requiresExpiryDate` (e.g. passport). */
|
|
1023
|
+
expiryDate?: string;
|
|
1024
|
+
}
|
|
1025
|
+
/** `201` of a KYC document upload. */
|
|
1026
|
+
interface KycUploadResult {
|
|
1027
|
+
documentId: string;
|
|
1028
|
+
status: "uploaded";
|
|
1029
|
+
}
|
|
1030
|
+
interface DepositInput {
|
|
1031
|
+
/** Defaults to the authenticated player. */
|
|
1032
|
+
playerId?: string;
|
|
1033
|
+
/** Amount in scale-4 minor units. */
|
|
1034
|
+
amount: MinorUnits;
|
|
1035
|
+
/** Defaults to the client/profile currency. */
|
|
1036
|
+
currency?: string;
|
|
1037
|
+
providerKey?: string;
|
|
1038
|
+
methodKey?: string;
|
|
1039
|
+
instrumentId?: string;
|
|
1040
|
+
/** FX snapshot currency (ISO 4217) for base-currency analytics. */
|
|
1041
|
+
baseCurrency?: string;
|
|
1042
|
+
returnUrl?: string;
|
|
1043
|
+
/** Auto-generated if omitted. */
|
|
1044
|
+
idempotencyKey?: string;
|
|
1045
|
+
metadata?: Record<string, unknown>;
|
|
1046
|
+
}
|
|
1047
|
+
type DepositTxStatus = "initiated" | "pending_provider" | "authorized" | "captured" | "completed" | "failed" | "refunded" | "chargeback";
|
|
1048
|
+
interface DepositResult {
|
|
1049
|
+
depositId: string;
|
|
1050
|
+
status: DepositTxStatus;
|
|
1051
|
+
providerKey?: string;
|
|
1052
|
+
sourceTxId?: string;
|
|
1053
|
+
/** PSP redirect/3DS/QR instruction; provider-specific. Render or follow as told. */
|
|
1054
|
+
nextAction?: unknown;
|
|
1055
|
+
walletTransactionId?: string;
|
|
1056
|
+
balances?: WalletBuckets;
|
|
1057
|
+
idempotent: boolean;
|
|
1058
|
+
/** Present when a gate plugin requires a step-up (e.g. KYC) before the deposit proceeds. */
|
|
1059
|
+
challenge?: {
|
|
1060
|
+
challenge: string;
|
|
1061
|
+
reason?: string;
|
|
1062
|
+
};
|
|
1063
|
+
}
|
|
1064
|
+
interface WithdrawalInput {
|
|
1065
|
+
playerId?: string;
|
|
1066
|
+
amount: MinorUnits;
|
|
1067
|
+
currency?: string;
|
|
1068
|
+
providerKey?: string;
|
|
1069
|
+
methodKey?: string;
|
|
1070
|
+
instrumentId?: string;
|
|
1071
|
+
/** FX snapshot currency (ISO 4217) for base-currency analytics. */
|
|
1072
|
+
baseCurrency?: string;
|
|
1073
|
+
idempotencyKey?: string;
|
|
1074
|
+
metadata?: Record<string, unknown>;
|
|
1075
|
+
}
|
|
1076
|
+
type WithdrawalTxStatus = "requested" | "pending_review" | "approved" | "processing" | "paid" | "rejected" | "failed" | "cancelled";
|
|
1077
|
+
interface WithdrawalResult {
|
|
1078
|
+
withdrawalId: string;
|
|
1079
|
+
status: WithdrawalTxStatus;
|
|
1080
|
+
reviewVerdict?: string;
|
|
1081
|
+
providerKey?: string;
|
|
1082
|
+
sourceTxId?: string;
|
|
1083
|
+
balances?: WalletBuckets;
|
|
1084
|
+
idempotent: boolean;
|
|
1085
|
+
}
|
|
1086
|
+
/** Shared filter block on the cashier/gaming history endpoints. */
|
|
1087
|
+
interface HistoryRangeQuery {
|
|
1088
|
+
/** ISO date(-time) or `Date`. */
|
|
1089
|
+
from?: string | Date;
|
|
1090
|
+
to?: string | Date;
|
|
1091
|
+
/** 1–100, runtime default 50. */
|
|
1092
|
+
limit?: number;
|
|
1093
|
+
offset?: number;
|
|
1094
|
+
}
|
|
1095
|
+
interface DepositHistoryQuery extends HistoryRangeQuery {
|
|
1096
|
+
status?: DepositTxStatus;
|
|
1097
|
+
providerKey?: string;
|
|
1098
|
+
}
|
|
1099
|
+
/** One deposit (`GET /player/cashier/deposits`), amounts in minor units. */
|
|
1100
|
+
interface DepositSummary {
|
|
1101
|
+
id: string;
|
|
1102
|
+
amount: MinorUnits;
|
|
1103
|
+
currency: string;
|
|
1104
|
+
status: DepositTxStatus;
|
|
1105
|
+
providerKey: string;
|
|
1106
|
+
methodKey: string | null;
|
|
1107
|
+
isFirstDeposit: boolean;
|
|
1108
|
+
createdAt: string;
|
|
1109
|
+
updatedAt: string;
|
|
1110
|
+
}
|
|
1111
|
+
/** `GET /player/cashier/deposits/:id`. */
|
|
1112
|
+
interface DepositDetail extends DepositSummary {
|
|
1113
|
+
walletTransactionId: string | null;
|
|
1114
|
+
failureReason: string | null;
|
|
1115
|
+
/** Only non-null while the deposit is `initiated`/`pending_provider`. */
|
|
1116
|
+
nextAction: unknown | null;
|
|
1117
|
+
}
|
|
1118
|
+
interface DepositHistoryPage {
|
|
1119
|
+
deposits: DepositSummary[];
|
|
1120
|
+
pagination: PageInfo;
|
|
1121
|
+
}
|
|
1122
|
+
interface WithdrawalHistoryQuery extends HistoryRangeQuery {
|
|
1123
|
+
status?: WithdrawalTxStatus;
|
|
1124
|
+
}
|
|
1125
|
+
/** One withdrawal (`GET /player/cashier/withdrawals`), amounts in minor units. */
|
|
1126
|
+
interface WithdrawalSummary {
|
|
1127
|
+
id: string;
|
|
1128
|
+
amount: MinorUnits;
|
|
1129
|
+
currency: string;
|
|
1130
|
+
status: WithdrawalTxStatus;
|
|
1131
|
+
providerKey: string | null;
|
|
1132
|
+
methodKey: string | null;
|
|
1133
|
+
reviewVerdict: string | null;
|
|
1134
|
+
createdAt: string;
|
|
1135
|
+
updatedAt: string;
|
|
1136
|
+
}
|
|
1137
|
+
/** `GET /player/cashier/withdrawals/:id`. */
|
|
1138
|
+
interface WithdrawalDetail extends WithdrawalSummary {
|
|
1139
|
+
failureReason: string | null;
|
|
1140
|
+
/** Wallet transaction ids of the reserve/settle/release legs. */
|
|
1141
|
+
walletTransactionIds: {
|
|
1142
|
+
reserve: string | null;
|
|
1143
|
+
settle: string | null;
|
|
1144
|
+
release: string | null;
|
|
1145
|
+
};
|
|
1146
|
+
}
|
|
1147
|
+
interface WithdrawalHistoryPage {
|
|
1148
|
+
withdrawals: WithdrawalSummary[];
|
|
1149
|
+
pagination: PageInfo;
|
|
1150
|
+
}
|
|
1151
|
+
/**
|
|
1152
|
+
* A payment method available to the player (`GET /player/cashier/methods`),
|
|
1153
|
+
* sorted by `sort` ascending. `limits` are converted to minor units.
|
|
1154
|
+
*/
|
|
1155
|
+
interface PaymentMethod {
|
|
1156
|
+
methodKey: string;
|
|
1157
|
+
providerKey: string;
|
|
1158
|
+
title: string;
|
|
1159
|
+
kind: "card" | "bank" | "ewallet" | "crypto" | "voucher";
|
|
1160
|
+
direction: "deposit" | "withdrawal" | "both";
|
|
1161
|
+
limits: {
|
|
1162
|
+
min: MinorUnits;
|
|
1163
|
+
max: MinorUnits;
|
|
1164
|
+
currency: string;
|
|
1165
|
+
};
|
|
1166
|
+
/** Present when the method is gated behind a KYC level. */
|
|
1167
|
+
requiresKycLevel?: number;
|
|
1168
|
+
/** Extra inputs the method needs (e.g. IBAN); render as a form. */
|
|
1169
|
+
fields?: Array<{
|
|
1170
|
+
key: string;
|
|
1171
|
+
label: string;
|
|
1172
|
+
type: "text" | "number" | "select";
|
|
1173
|
+
required?: boolean;
|
|
1174
|
+
options?: string[];
|
|
1175
|
+
}>;
|
|
1176
|
+
sort: number;
|
|
1177
|
+
}
|
|
1178
|
+
interface BalanceResponse {
|
|
1179
|
+
playerId: string;
|
|
1180
|
+
currency: string;
|
|
1181
|
+
cash: MinorUnits;
|
|
1182
|
+
bonus: MinorUnits;
|
|
1183
|
+
locked: MinorUnits;
|
|
1184
|
+
total: MinorUnits;
|
|
1185
|
+
/** `closed` is terminal; `frozen` is reversible. See the runtime wallet state machine. */
|
|
1186
|
+
status: "active" | "frozen" | "closed";
|
|
1187
|
+
walletId: string | null;
|
|
1188
|
+
}
|
|
1189
|
+
type TransactionType = "credit" | "debit" | "deposit" | "withdrawal" | "bet" | "win" | "bonus" | "rollback" | "transfer" | "adjustment";
|
|
1190
|
+
type TransactionStatus = "pending" | "processing" | "completed" | "failed" | "cancelled" | "reversed";
|
|
1191
|
+
interface WalletTransaction {
|
|
1192
|
+
id: string;
|
|
1193
|
+
walletId: string;
|
|
1194
|
+
playerId: string;
|
|
1195
|
+
externalTransactionId: string | null;
|
|
1196
|
+
type: TransactionType;
|
|
1197
|
+
amount: MinorUnits;
|
|
1198
|
+
currency: string;
|
|
1199
|
+
source: string;
|
|
1200
|
+
gameId: string | null;
|
|
1201
|
+
/** The replay-guard key the write was made with. */
|
|
1202
|
+
idempotencyKey: string;
|
|
1203
|
+
metadata: Record<string, unknown> | null;
|
|
1204
|
+
status: TransactionStatus;
|
|
1205
|
+
createdAt: string;
|
|
1206
|
+
updatedAt: string;
|
|
1207
|
+
}
|
|
1208
|
+
interface TransactionsQuery {
|
|
1209
|
+
playerId?: string;
|
|
1210
|
+
walletId?: string;
|
|
1211
|
+
type?: TransactionType;
|
|
1212
|
+
status?: TransactionStatus;
|
|
1213
|
+
currency?: string;
|
|
1214
|
+
limit?: number;
|
|
1215
|
+
offset?: number;
|
|
1216
|
+
}
|
|
1217
|
+
/** The three wallet buckets a ledger leg can move. */
|
|
1218
|
+
type WalletBucketName = "cash" | "bonus" | "locked";
|
|
1219
|
+
/**
|
|
1220
|
+
* One append-only ledger leg (`GET /wallet/ledger`) — a single bucket movement
|
|
1221
|
+
* with its before/after balance snapshot. Amounts in scale-4 minor units.
|
|
1222
|
+
*/
|
|
1223
|
+
interface LedgerEntry {
|
|
1224
|
+
id: string;
|
|
1225
|
+
walletId: string;
|
|
1226
|
+
transactionId: string;
|
|
1227
|
+
playerId: string;
|
|
1228
|
+
walletType: WalletBucketName;
|
|
1229
|
+
direction: "credit" | "debit";
|
|
1230
|
+
amount: MinorUnits;
|
|
1231
|
+
currency: string;
|
|
1232
|
+
balanceBefore: MinorUnits;
|
|
1233
|
+
balanceAfter: MinorUnits;
|
|
1234
|
+
createdAt: string;
|
|
1235
|
+
}
|
|
1236
|
+
interface LedgerQuery {
|
|
1237
|
+
playerId?: string;
|
|
1238
|
+
walletId?: string;
|
|
1239
|
+
walletType?: WalletBucketName;
|
|
1240
|
+
transactionId?: string;
|
|
1241
|
+
limit?: number;
|
|
1242
|
+
offset?: number;
|
|
1243
|
+
}
|
|
1244
|
+
interface GameFeatures {
|
|
1245
|
+
hasDemo?: boolean;
|
|
1246
|
+
isVirtual?: boolean;
|
|
1247
|
+
bonusBuy?: boolean;
|
|
1248
|
+
megaways?: boolean;
|
|
1249
|
+
[key: string]: unknown;
|
|
1250
|
+
}
|
|
1251
|
+
interface Game {
|
|
1252
|
+
id: string;
|
|
1253
|
+
slug: string | null;
|
|
1254
|
+
title: string | null;
|
|
1255
|
+
name?: unknown;
|
|
1256
|
+
providerId: string;
|
|
1257
|
+
launchCode: string | null;
|
|
1258
|
+
imageUrl: string | null;
|
|
1259
|
+
rtp: string | null;
|
|
1260
|
+
minBet: string | null;
|
|
1261
|
+
maxBet: string | null;
|
|
1262
|
+
featured: boolean;
|
|
1263
|
+
features: GameFeatures;
|
|
1264
|
+
}
|
|
1265
|
+
interface GamePage {
|
|
1266
|
+
games: Game[];
|
|
1267
|
+
page: {
|
|
1268
|
+
limit: number;
|
|
1269
|
+
offset: number;
|
|
1270
|
+
count: number;
|
|
1271
|
+
};
|
|
1272
|
+
}
|
|
1273
|
+
interface CategoryNode {
|
|
1274
|
+
id: string;
|
|
1275
|
+
slug: string;
|
|
1276
|
+
title: string | null;
|
|
1277
|
+
kind: string;
|
|
1278
|
+
sort: number;
|
|
1279
|
+
children: CategoryNode[];
|
|
1280
|
+
}
|
|
1281
|
+
interface Provider {
|
|
1282
|
+
id: string;
|
|
1283
|
+
externalId: string;
|
|
1284
|
+
name: string;
|
|
1285
|
+
alias: string | null;
|
|
1286
|
+
logoUrl: string | null;
|
|
1287
|
+
logoPath: string | null;
|
|
1288
|
+
weight: number;
|
|
1289
|
+
parentProviderId: string | null;
|
|
1290
|
+
}
|
|
1291
|
+
interface GeoQuery {
|
|
1292
|
+
country?: string;
|
|
1293
|
+
/** Sub-national code, e.g. `"CA-QC"`. */
|
|
1294
|
+
subdivision?: string;
|
|
1295
|
+
currency?: string;
|
|
1296
|
+
locale?: string;
|
|
1297
|
+
device?: string;
|
|
1298
|
+
}
|
|
1299
|
+
interface LobbyQuery extends GeoQuery {
|
|
1300
|
+
limit?: number;
|
|
1301
|
+
offset?: number;
|
|
1302
|
+
}
|
|
1303
|
+
interface SearchQuery extends LobbyQuery {
|
|
1304
|
+
q?: string;
|
|
1305
|
+
/** Provider id, name, or alias; expands to its sub-providers server-side. */
|
|
1306
|
+
provider?: string;
|
|
1307
|
+
/** Category id or slug (e.g. `"slots"`); includes the category subtree. */
|
|
1308
|
+
category?: string;
|
|
1309
|
+
hasDemo?: boolean;
|
|
1310
|
+
isVirtual?: boolean;
|
|
1311
|
+
bonusBuy?: boolean;
|
|
1312
|
+
megaways?: boolean;
|
|
1313
|
+
}
|
|
1314
|
+
/**
|
|
1315
|
+
* @deprecated Input for the removed SlotServ aggregation route (`POST
|
|
1316
|
+
* /plugins/slotserv/launch`); `games.launch()` is a stub now. The live launch
|
|
1317
|
+
* path — `games.providerSession()` — takes `playerId`/`currency` options only.
|
|
1318
|
+
*/
|
|
1319
|
+
interface LaunchInput {
|
|
1320
|
+
playerId?: string;
|
|
1321
|
+
currency?: string;
|
|
1322
|
+
device?: string;
|
|
1323
|
+
locale?: string;
|
|
1324
|
+
mode?: "real" | "demo";
|
|
1325
|
+
country?: string;
|
|
1326
|
+
}
|
|
1327
|
+
/** Response of `POST /providers/:provider/session`. */
|
|
1328
|
+
interface LaunchResult {
|
|
1329
|
+
provider: string;
|
|
1330
|
+
/** The game session id. */
|
|
1331
|
+
sessionId: string;
|
|
1332
|
+
/** Fully-formed URL to render in the game iframe. */
|
|
1333
|
+
launchUrl: string;
|
|
1334
|
+
}
|
|
1335
|
+
type BetStatus = "placed" | "settled" | "rolled_back";
|
|
1336
|
+
interface BetsQuery extends HistoryRangeQuery {
|
|
1337
|
+
gameId?: string;
|
|
1338
|
+
providerKey?: string;
|
|
1339
|
+
status?: BetStatus;
|
|
1340
|
+
}
|
|
1341
|
+
/** One bet (`GET /player/bets`), amounts in minor units. */
|
|
1342
|
+
interface Bet {
|
|
1343
|
+
betId: string;
|
|
1344
|
+
roundId: string;
|
|
1345
|
+
gameId: string | null;
|
|
1346
|
+
gameTitle: string | null;
|
|
1347
|
+
providerKey: string;
|
|
1348
|
+
betAmount: MinorUnits;
|
|
1349
|
+
/** Null until settled. */
|
|
1350
|
+
winAmount: MinorUnits | null;
|
|
1351
|
+
currency: string;
|
|
1352
|
+
status: BetStatus;
|
|
1353
|
+
placedAt: string;
|
|
1354
|
+
settledAt: string | null;
|
|
1355
|
+
}
|
|
1356
|
+
/** `GET /player/bets/:betId` — the bet plus its round-linked wallet transactions. */
|
|
1357
|
+
interface BetDetail extends Bet {
|
|
1358
|
+
walletTransactions: Array<{
|
|
1359
|
+
id: string;
|
|
1360
|
+
type: string;
|
|
1361
|
+
}>;
|
|
1362
|
+
}
|
|
1363
|
+
interface BetsPage {
|
|
1364
|
+
bets: Bet[];
|
|
1365
|
+
pagination: PageInfo;
|
|
1366
|
+
}
|
|
1367
|
+
/** One game session (`GET /player/game-sessions`), totals in minor units. */
|
|
1368
|
+
interface GameSession {
|
|
1369
|
+
id: string;
|
|
1370
|
+
providerKey: string;
|
|
1371
|
+
gameId: string | null;
|
|
1372
|
+
currency: string | null;
|
|
1373
|
+
status: "active" | "ended";
|
|
1374
|
+
betCount: number;
|
|
1375
|
+
betTotal: MinorUnits;
|
|
1376
|
+
winTotal: MinorUnits;
|
|
1377
|
+
startedAt: string;
|
|
1378
|
+
lastActivityAt: string | null;
|
|
1379
|
+
endedAt: string | null;
|
|
1380
|
+
}
|
|
1381
|
+
interface GameSessionsPage {
|
|
1382
|
+
sessions: GameSession[];
|
|
1383
|
+
pagination: PageInfo;
|
|
1384
|
+
}
|
|
1385
|
+
/** Up to five free-form sub-tracking ids an affiliate appends to their link. */
|
|
1386
|
+
interface AffiliateSubIds {
|
|
1387
|
+
subId1?: string;
|
|
1388
|
+
subId2?: string;
|
|
1389
|
+
subId3?: string;
|
|
1390
|
+
subId4?: string;
|
|
1391
|
+
subId5?: string;
|
|
1392
|
+
}
|
|
1393
|
+
/** Standard UTM parameters captured from the landing URL. */
|
|
1394
|
+
interface UtmParams {
|
|
1395
|
+
source?: string;
|
|
1396
|
+
medium?: string;
|
|
1397
|
+
campaign?: string;
|
|
1398
|
+
term?: string;
|
|
1399
|
+
content?: string;
|
|
1400
|
+
}
|
|
1401
|
+
/**
|
|
1402
|
+
* Body of `POST /affiliate/track/click`. The runtime validates `.strict()` —
|
|
1403
|
+
* unknown keys are rejected — and captures `ip`/`user-agent` server-side.
|
|
1404
|
+
* `referrer` falls back to the `Referer` header when omitted.
|
|
1405
|
+
*/
|
|
1406
|
+
interface TrackClickInput {
|
|
1407
|
+
/** The affiliate's public tracking code from the landing link (required). */
|
|
1408
|
+
affiliateCode: string;
|
|
1409
|
+
campaignId?: string;
|
|
1410
|
+
creativeId?: string;
|
|
1411
|
+
subIds?: AffiliateSubIds;
|
|
1412
|
+
/** Google Ads click id. */
|
|
1413
|
+
gclid?: string;
|
|
1414
|
+
/** Meta click id. */
|
|
1415
|
+
fbclid?: string;
|
|
1416
|
+
/** TikTok click id. */
|
|
1417
|
+
ttclid?: string;
|
|
1418
|
+
/** Microsoft Ads click id. */
|
|
1419
|
+
msclkid?: string;
|
|
1420
|
+
utm?: UtmParams;
|
|
1421
|
+
landingPage?: string;
|
|
1422
|
+
referrer?: string;
|
|
1423
|
+
deviceFingerprint?: string;
|
|
1424
|
+
}
|
|
1425
|
+
/**
|
|
1426
|
+
* `201` response of the click capture. Store `clickId` client-side
|
|
1427
|
+
* (cookie/localStorage) and pass it through signup — it is the
|
|
1428
|
+
* click → registration join key.
|
|
1429
|
+
*/
|
|
1430
|
+
interface TrackClickResult {
|
|
1431
|
+
clickId: string;
|
|
1432
|
+
}
|
|
1433
|
+
|
|
1434
|
+
/**
|
|
1435
|
+
* `sdk.affiliate` — affiliate click tracking.
|
|
1436
|
+
*
|
|
1437
|
+
* Maps to the runtime's one public affiliate endpoint, `POST /affiliate/track/click`.
|
|
1438
|
+
* Call it on the landing page; anything not captured at click time (gclid/fbclid/…)
|
|
1439
|
+
* is unrecoverable. The endpoint is rate-limited per IP (`RateLimitError` on 429).
|
|
1440
|
+
*/
|
|
1441
|
+
|
|
1442
|
+
declare class AffiliateModule {
|
|
1443
|
+
private readonly http;
|
|
1444
|
+
constructor(http: HttpClient);
|
|
1445
|
+
/**
|
|
1446
|
+
* Record an affiliate click. Returns the `clickId` join key — persist it
|
|
1447
|
+
* (cookie/localStorage) and carry it into signup so the registration can be
|
|
1448
|
+
* attributed to the click.
|
|
1449
|
+
*/
|
|
1450
|
+
trackClick(input: TrackClickInput): Promise<TrackClickResult>;
|
|
1451
|
+
}
|
|
1452
|
+
|
|
1453
|
+
/**
|
|
1454
|
+
* Session state held client-side.
|
|
1455
|
+
*
|
|
1456
|
+
* Several runtime endpoints need the player's id and/or currency that the SDK can
|
|
1457
|
+
* supply automatically once the player has authenticated:
|
|
1458
|
+
*
|
|
1459
|
+
* - cashier `deposits`/`withdrawals` take `playerId` in the body,
|
|
1460
|
+
* - `GET /wallet/balance` and `/wallet/transactions` take `playerId` + `currency`,
|
|
1461
|
+
* - provider game sessions (`providerSession`) take `playerId` + `currency`.
|
|
1462
|
+
*
|
|
1463
|
+
* Auth responses (`signup`/`login`/`me`) populate this cache so callers don't have
|
|
1464
|
+
* to thread `playerId` through every call. Every method still accepts an explicit
|
|
1465
|
+
* override.
|
|
1466
|
+
*/
|
|
1467
|
+
|
|
1468
|
+
interface SessionSnapshot {
|
|
1469
|
+
player: Player | null;
|
|
1470
|
+
profile: PlayerProfile | null;
|
|
1471
|
+
/** Default currency: profile currency, else the client's configured default. */
|
|
1472
|
+
currency: string;
|
|
1473
|
+
}
|
|
1474
|
+
declare class Session {
|
|
1475
|
+
private player;
|
|
1476
|
+
private profile;
|
|
1477
|
+
private readonly fallbackCurrency;
|
|
1478
|
+
constructor(fallbackCurrency: string);
|
|
1479
|
+
setPlayer(player: Player | null, profile?: PlayerProfile | null): void;
|
|
1480
|
+
clear(): void;
|
|
1481
|
+
get playerId(): string | undefined;
|
|
1482
|
+
/** Resolve a playerId, preferring an explicit override; throws if neither exists. */
|
|
1483
|
+
requirePlayerId(override?: string): string;
|
|
1484
|
+
/** Resolve a currency: override → profile → configured default. */
|
|
1485
|
+
resolveCurrency(override?: string): string;
|
|
1486
|
+
snapshot(): SessionSnapshot;
|
|
1487
|
+
}
|
|
1488
|
+
|
|
1489
|
+
/**
|
|
1490
|
+
* `sdk.auth` — signup, login, logout, refresh, me, social OAuth, and sessions
|
|
1491
|
+
* ("your devices").
|
|
1492
|
+
*
|
|
1493
|
+
* Maps to the runtime's `/auth/player/*` routes. Auth is cookie-based: the runtime
|
|
1494
|
+
* sets `cwe_access_token` / `cwe_refresh_token` (HttpOnly) and `cwe_csrf` (readable);
|
|
1495
|
+
* the SDK's HTTP layer carries the session and CSRF automatically. After a
|
|
1496
|
+
* successful auth call the {@link Session} cache is populated so wallet/cashier/launch
|
|
1497
|
+
* calls can auto-fill `playerId` + `currency`.
|
|
1498
|
+
*/
|
|
1499
|
+
|
|
1500
|
+
interface SocialStartOptions {
|
|
1501
|
+
/**
|
|
1502
|
+
* Where the runtime should send the player back after the provider flow.
|
|
1503
|
+
* Use `https:` URLs only — a non-https `redirectUri` (or worse, a scheme like
|
|
1504
|
+
* `javascript:`) must never reach a browser navigation. The runtime validates
|
|
1505
|
+
* it against the tenant's allowlist; keep your allowlist https-only too.
|
|
1506
|
+
*/
|
|
1507
|
+
redirectUri?: string;
|
|
1508
|
+
}
|
|
1509
|
+
declare class AuthModule {
|
|
1510
|
+
private readonly http;
|
|
1511
|
+
private readonly session;
|
|
1512
|
+
private readonly baseUrl;
|
|
1513
|
+
constructor(http: HttpClient, session: Session, baseUrl: string);
|
|
1514
|
+
/** Create an account and start a session. */
|
|
1515
|
+
signup(input: SignupInput): Promise<AuthSession>;
|
|
1516
|
+
/** Log in with email + password and start a session. */
|
|
1517
|
+
login(input: LoginInput): Promise<AuthSession>;
|
|
1518
|
+
/** End the session and clear the cached player. */
|
|
1519
|
+
logout(): Promise<void>;
|
|
1520
|
+
/**
|
|
1521
|
+
* Rotate the session using the refresh cookie. Called automatically by the HTTP
|
|
1522
|
+
* layer on a 401; you rarely call it directly.
|
|
1523
|
+
*
|
|
1524
|
+
* The request is marked `_noRefresh`: a 401 here (fully logged out) must throw
|
|
1525
|
+
* immediately instead of re-entering the 401 → refresh path it is part of.
|
|
1526
|
+
*/
|
|
1527
|
+
refresh(): Promise<AuthSession>;
|
|
1528
|
+
/** Current player + profile. Refreshes the session cache (incl. default currency). */
|
|
1529
|
+
me(): Promise<MeResponse>;
|
|
1530
|
+
readonly social: {
|
|
1531
|
+
/**
|
|
1532
|
+
* Build the URL that begins a social login. Send the browser here (full
|
|
1533
|
+
* navigation, not fetch) so the runtime can redirect to the provider.
|
|
1534
|
+
*/
|
|
1535
|
+
startUrl: (provider: SocialProvider, options?: SocialStartOptions) => string;
|
|
1536
|
+
/**
|
|
1537
|
+
* Complete the OAuth callback (provider → your app) by exchanging `code`+`state`.
|
|
1538
|
+
* Use this if your app handles the callback route itself; otherwise the runtime
|
|
1539
|
+
* handles `/auth/player/social/:provider/callback` directly.
|
|
1540
|
+
*/
|
|
1541
|
+
callback: (provider: SocialProvider, params: {
|
|
1542
|
+
code?: string;
|
|
1543
|
+
state?: string;
|
|
1544
|
+
hash?: string;
|
|
1545
|
+
}) => Promise<AuthSession>;
|
|
1546
|
+
/** Link a social account to the logged-in player. */
|
|
1547
|
+
link: (provider: SocialProvider, code: string) => Promise<{
|
|
1548
|
+
account: SocialAccount;
|
|
1549
|
+
}>;
|
|
1550
|
+
/** Unlink a previously-linked social account. */
|
|
1551
|
+
unlink: (provider: SocialProvider, socialAccountId: string) => Promise<{
|
|
1552
|
+
success: true;
|
|
1553
|
+
}>;
|
|
1554
|
+
};
|
|
1555
|
+
/**
|
|
1556
|
+
* Session management — the "your devices" surface. The runtime scopes every call
|
|
1557
|
+
* to the authenticated player (the id comes from the verified principal, never a
|
|
1558
|
+
* path), so a player can only see and revoke their own sessions.
|
|
1559
|
+
*/
|
|
1560
|
+
readonly sessions: {
|
|
1561
|
+
/** Active sessions — the player's currently signed-in devices. */
|
|
1562
|
+
list: () => Promise<SafeSession[]>;
|
|
1563
|
+
/**
|
|
1564
|
+
* Full session history — active plus revoked/expired, newest first. Paginated:
|
|
1565
|
+
* `limit` 1–100 (default 50), `offset` ≥ 0; `pagination.hasMore` says whether
|
|
1566
|
+
* another page exists.
|
|
1567
|
+
*/
|
|
1568
|
+
history: (query?: SessionHistoryQuery) => Promise<SessionHistoryPage>;
|
|
1569
|
+
/** Disconnect (revoke) one of the player's own sessions. */
|
|
1570
|
+
revoke: (sessionId: string) => Promise<{
|
|
1571
|
+
success: true;
|
|
1572
|
+
}>;
|
|
1573
|
+
/**
|
|
1574
|
+
* Revoke every session. By default the current one survives ("sign out
|
|
1575
|
+
* everywhere else"); pass `{ exceptCurrent: false }` to revoke this device
|
|
1576
|
+
* too (the runtime then also clears the auth cookies).
|
|
1577
|
+
*/
|
|
1578
|
+
revokeAll: (options?: {
|
|
1579
|
+
exceptCurrent?: boolean;
|
|
1580
|
+
}) => Promise<{
|
|
1581
|
+
revoked: number;
|
|
1582
|
+
}>;
|
|
1583
|
+
};
|
|
1584
|
+
/**
|
|
1585
|
+
* Login history (successful and failed attempts, newest first). Paginated:
|
|
1586
|
+
* `limit` 1–100 (default 50), `offset` ≥ 0.
|
|
1587
|
+
*/
|
|
1588
|
+
loginHistory(query?: SessionHistoryQuery): Promise<LoginHistoryPage>;
|
|
1589
|
+
/** Password management. The reset flow is anonymous; `change` needs a session. */
|
|
1590
|
+
readonly password: {
|
|
1591
|
+
/**
|
|
1592
|
+
* Change the password (requires the current one). The runtime revokes every
|
|
1593
|
+
* OTHER session; this one survives. Rate-limited 10/h per player.
|
|
1594
|
+
*/
|
|
1595
|
+
change: (input: {
|
|
1596
|
+
currentPassword: string;
|
|
1597
|
+
newPassword: string;
|
|
1598
|
+
}) => Promise<{
|
|
1599
|
+
success: true;
|
|
1600
|
+
}>;
|
|
1601
|
+
/**
|
|
1602
|
+
* Request a password-reset email. Always resolves `{ success: true }`
|
|
1603
|
+
* (anti-enumeration) — even for unknown emails or while rate-limited.
|
|
1604
|
+
*/
|
|
1605
|
+
requestReset: (email: string) => Promise<{
|
|
1606
|
+
success: true;
|
|
1607
|
+
}>;
|
|
1608
|
+
/**
|
|
1609
|
+
* Complete a reset with the emailed token. Revokes ALL sessions — the
|
|
1610
|
+
* player must log in again.
|
|
1611
|
+
*/
|
|
1612
|
+
confirmReset: (input: {
|
|
1613
|
+
token: string;
|
|
1614
|
+
newPassword: string;
|
|
1615
|
+
}) => Promise<{
|
|
1616
|
+
success: true;
|
|
1617
|
+
}>;
|
|
1618
|
+
};
|
|
1619
|
+
/**
|
|
1620
|
+
* Email/phone verification. `request` sends the token/OTP (429 with
|
|
1621
|
+
* `details.resendIn` inside the cooldown); `confirm` verifies it.
|
|
1622
|
+
*/
|
|
1623
|
+
readonly verification: {
|
|
1624
|
+
email: {
|
|
1625
|
+
request: () => Promise<VerificationRequestResult>;
|
|
1626
|
+
/** Confirm with the emailed link `token` XOR the 6-digit `code` (code needs a session). */
|
|
1627
|
+
confirm: (input: {
|
|
1628
|
+
token?: string;
|
|
1629
|
+
code?: string;
|
|
1630
|
+
}) => Promise<{
|
|
1631
|
+
verified: true;
|
|
1632
|
+
}>;
|
|
1633
|
+
};
|
|
1634
|
+
phone: {
|
|
1635
|
+
/** Pass `phone` to verify a NEW number (applied on confirm). */
|
|
1636
|
+
request: (phone?: string) => Promise<VerificationRequestResult>;
|
|
1637
|
+
confirm: (code: string) => Promise<{
|
|
1638
|
+
verified: true;
|
|
1639
|
+
}>;
|
|
1640
|
+
};
|
|
1641
|
+
};
|
|
1642
|
+
}
|
|
1643
|
+
|
|
1644
|
+
/**
|
|
1645
|
+
* `sdk.cashier` — deposits & withdrawals.
|
|
1646
|
+
*
|
|
1647
|
+
* Maps to `POST /cashier/deposits` and `POST /cashier/withdrawals`. These are money
|
|
1648
|
+
* operations: the HTTP layer auto-generates an idempotency key (body + header), and
|
|
1649
|
+
* the SDK auto-fills `playerId` + `currency` from the session. Amounts are passed in
|
|
1650
|
+
* minor units and converted to the plain major-unit number the runtime's write
|
|
1651
|
+
* schemas require; any `balances` in the response (4-dp decimal strings) are
|
|
1652
|
+
* converted back to minor units.
|
|
1653
|
+
*/
|
|
1654
|
+
|
|
1655
|
+
declare class CashierModule {
|
|
1656
|
+
private readonly http;
|
|
1657
|
+
private readonly session;
|
|
1658
|
+
constructor(http: HttpClient, session: Session);
|
|
1659
|
+
/**
|
|
1660
|
+
* Initiate a deposit. Follow `result.nextAction` for any PSP redirect/3DS step;
|
|
1661
|
+
* a `result.challenge` means a gate plugin requires a step-up (e.g. KYC) first.
|
|
1662
|
+
*/
|
|
1663
|
+
deposit(input: DepositInput): Promise<DepositResult>;
|
|
1664
|
+
/** Request a withdrawal. May land in `pending_review` depending on risk rules. */
|
|
1665
|
+
withdraw(input: WithdrawalInput): Promise<WithdrawalResult>;
|
|
1666
|
+
/**
|
|
1667
|
+
* The payment methods available to the player (`GET /player/cashier/methods`),
|
|
1668
|
+
* sorted for display; `limits` in minor units. Cached 60s server-side.
|
|
1669
|
+
*/
|
|
1670
|
+
listMethods(options?: {
|
|
1671
|
+
currency?: string;
|
|
1672
|
+
}): Promise<PaymentMethod[]>;
|
|
1673
|
+
/** Deposit history, newest first, amounts in minor units. */
|
|
1674
|
+
deposits(query?: DepositHistoryQuery): Promise<DepositHistoryPage>;
|
|
1675
|
+
/**
|
|
1676
|
+
* One deposit with its wallet linkage and any pending `nextAction`
|
|
1677
|
+
* (`GET /player/cashier/deposits/:id`). Realtime `wallet.deposit` remains the
|
|
1678
|
+
* push channel; this is the authoritative re-read.
|
|
1679
|
+
*/
|
|
1680
|
+
depositStatus(depositId: string): Promise<DepositDetail>;
|
|
1681
|
+
/** Withdrawal history, newest first, amounts in minor units. */
|
|
1682
|
+
withdrawals(query?: WithdrawalHistoryQuery): Promise<WithdrawalHistoryPage>;
|
|
1683
|
+
/** One withdrawal with its reserve/settle/release wallet-transaction legs. */
|
|
1684
|
+
withdrawalStatus(withdrawalId: string): Promise<WithdrawalDetail>;
|
|
1685
|
+
/**
|
|
1686
|
+
* Cancel a withdrawal that hasn't been picked up yet (`requested`/
|
|
1687
|
+
* `pending_review` only — anything later is a `ConflictError` with code
|
|
1688
|
+
* `INVALID_WITHDRAWAL_STATE`). Releases the reserved funds; idempotent
|
|
1689
|
+
* (a replay returns `idempotent: true` without balances).
|
|
1690
|
+
*/
|
|
1691
|
+
cancelWithdrawal(withdrawalId: string): Promise<WithdrawalResult>;
|
|
1692
|
+
}
|
|
1693
|
+
|
|
1694
|
+
/**
|
|
1695
|
+
* `sdk.wallet` — balance buckets, transaction history, and the append-only ledger.
|
|
1696
|
+
*
|
|
1697
|
+
* Maps to `GET /wallet/balance`, `GET /wallet/transactions`, and `GET /wallet/ledger`.
|
|
1698
|
+
* All take the player's id (auto-filled from the session); balance also takes a
|
|
1699
|
+
* currency (defaults to the profile/config currency). All money is converted to
|
|
1700
|
+
* scale-4 minor units here.
|
|
1701
|
+
*/
|
|
1702
|
+
|
|
1703
|
+
declare class WalletModule {
|
|
1704
|
+
private readonly http;
|
|
1705
|
+
private readonly session;
|
|
1706
|
+
constructor(http: HttpClient, session: Session);
|
|
1707
|
+
/** Current balance across the cash/bonus/locked buckets, in minor units. */
|
|
1708
|
+
getBalance(options?: {
|
|
1709
|
+
playerId?: string;
|
|
1710
|
+
currency?: string;
|
|
1711
|
+
}): Promise<BalanceResponse>;
|
|
1712
|
+
/** Paginated transaction history (newest first), amounts in minor units. */
|
|
1713
|
+
transactions(query?: TransactionsQuery): Promise<Paginated<WalletTransaction>>;
|
|
1714
|
+
/**
|
|
1715
|
+
* Paginated append-only ledger (newest first) — one row per bucket movement,
|
|
1716
|
+
* with before/after balance snapshots, amounts in minor units. Filter by
|
|
1717
|
+
* `transactionId` to see the legs of a single transaction.
|
|
1718
|
+
*/
|
|
1719
|
+
ledger(query?: LedgerQuery): Promise<Paginated<LedgerEntry>>;
|
|
1720
|
+
/**
|
|
1721
|
+
* Fetch a single transaction by id.
|
|
1722
|
+
*
|
|
1723
|
+
* @remarks Not available to players yet — the runtime route
|
|
1724
|
+
* `GET /wallet/transactions/:id` requires staff permission. Tracked on the
|
|
1725
|
+
* roadmap; use {@link transactions} to page history in the meantime.
|
|
1726
|
+
*/
|
|
1727
|
+
getTransaction(_id: string): Promise<WalletTransaction>;
|
|
1728
|
+
}
|
|
1729
|
+
|
|
1730
|
+
/**
|
|
1731
|
+
* `sdk.catalog` — lobby, search, categories tree, providers, game detail,
|
|
1732
|
+
* suggested, and trending. All endpoints are public and geo/currency-aware via
|
|
1733
|
+
* query params (`country`, `currency`, `locale`, `device`).
|
|
1734
|
+
*
|
|
1735
|
+
* Maps to the runtime's `/catalog/*` routes.
|
|
1736
|
+
*/
|
|
1737
|
+
|
|
1738
|
+
declare class CatalogModule {
|
|
1739
|
+
private readonly http;
|
|
1740
|
+
constructor(http: HttpClient);
|
|
1741
|
+
/** The default lobby, filtered by geo/currency/device. */
|
|
1742
|
+
lobby(query?: LobbyQuery): Promise<GamePage>;
|
|
1743
|
+
/** Search/filter games by text, provider, category, and feature flags. */
|
|
1744
|
+
searchGames(query?: SearchQuery): Promise<GamePage>;
|
|
1745
|
+
/** Trending games over a window (`"all"` by default), most popular first. */
|
|
1746
|
+
trending(query?: GeoQuery & {
|
|
1747
|
+
window?: string;
|
|
1748
|
+
limit?: number;
|
|
1749
|
+
}): Promise<Game[]>;
|
|
1750
|
+
/** The localized category tree (recursive `children`). */
|
|
1751
|
+
categories(options?: {
|
|
1752
|
+
locale?: string;
|
|
1753
|
+
}): Promise<CategoryNode[]>;
|
|
1754
|
+
/** All enabled providers for this brand/region. */
|
|
1755
|
+
providers(query?: GeoQuery): Promise<Provider[]>;
|
|
1756
|
+
/** A single game by internal id or slug. */
|
|
1757
|
+
game(idOrSlug: string, query?: GeoQuery): Promise<Game>;
|
|
1758
|
+
/**
|
|
1759
|
+
* Games suggested alongside the given game. Takes the internal game **id** only
|
|
1760
|
+
* (an unknown id — including a slug — yields an empty list, not a 404).
|
|
1761
|
+
*/
|
|
1762
|
+
suggested(gameId: string, query?: GeoQuery & {
|
|
1763
|
+
limit?: number;
|
|
1764
|
+
}): Promise<Game[]>;
|
|
1765
|
+
}
|
|
1766
|
+
|
|
1767
|
+
/**
|
|
1768
|
+
* `sdk.ext` — plugin actions: player-facing features that tenant-enabled plugins
|
|
1769
|
+
* expose on the casino API.
|
|
1770
|
+
*
|
|
1771
|
+
* ```ts
|
|
1772
|
+
* // Discovery
|
|
1773
|
+
* const catalog = await sdk.ext.catalog();
|
|
1774
|
+
* const cashback = await sdk.ext.plugin("cashback");
|
|
1775
|
+
*
|
|
1776
|
+
* // Generic — works for any enabled plugin, no build-time knowledge:
|
|
1777
|
+
* const summary = await sdk.ext("cashback").call("getSummary");
|
|
1778
|
+
* await sdk.ext("cashback").call("claim", { periodId });
|
|
1779
|
+
*
|
|
1780
|
+
* // Typed — when the plugin's generated client package is installed, the
|
|
1781
|
+
* // ExtRegistry augmentation narrows call() keys/input/result (see types/ext.ts).
|
|
1782
|
+
* ```
|
|
1783
|
+
*
|
|
1784
|
+
* `call()` resolves the action descriptor from the cached catalog
|
|
1785
|
+
* (`GET /api/ext/_catalog`, ETag-revalidated, `Cache-Control: max-age` honored),
|
|
1786
|
+
* routes input per the action's method (GET/DELETE ⇒ query, POST/PUT ⇒ JSON body,
|
|
1787
|
+
* `:name` path params substituted from matching top-level input keys), and
|
|
1788
|
+
* auto-generates an `Idempotency-Key` for idempotent mutations. The key travels
|
|
1789
|
+
* as a header only — plugin bodies validate `.strict()`.
|
|
1790
|
+
*
|
|
1791
|
+
* Dev-mode only: input is best-effort checked against the action's JSON schema
|
|
1792
|
+
* (`console.warn`, never a client-side throw — the server's Zod is
|
|
1793
|
+
* authoritative), and `deprecated` actions warn once per action per client.
|
|
1794
|
+
*
|
|
1795
|
+
* Money note: plugin payloads are opaque to the SDK, so no minor-unit conversion
|
|
1796
|
+
* happens here — consult the plugin's own docs for its money shapes.
|
|
1797
|
+
*/
|
|
1798
|
+
|
|
1799
|
+
interface ExtCatalogOptions {
|
|
1800
|
+
/** Bypass the cache and refetch unconditionally. */
|
|
1801
|
+
force?: boolean;
|
|
1802
|
+
}
|
|
1803
|
+
/**
|
|
1804
|
+
* The `sdk.ext` surface: callable for a per-plugin handle, plus catalog
|
|
1805
|
+
* discovery. Fully lazy — nothing is fetched until an action is called or the
|
|
1806
|
+
* catalog is requested, and a broken catalog endpoint never affects non-ext SDK
|
|
1807
|
+
* usage.
|
|
1808
|
+
*/
|
|
1809
|
+
interface ExtModule {
|
|
1810
|
+
/** Typed handle — `pluginKey` is registered in {@link ExtRegistry} (generated client installed). */
|
|
1811
|
+
<K extends keyof ExtRegistry & string>(pluginKey: K): TypedExtPluginClient<ExtRegistry[K]>;
|
|
1812
|
+
/** Generic handle for any enabled plugin; no build-time knowledge needed. */
|
|
1813
|
+
(pluginKey: string): ExtPluginClient;
|
|
1814
|
+
/**
|
|
1815
|
+
* The tenant's plugin catalog. Cached in-memory per `Cache-Control: max-age`
|
|
1816
|
+
* (default 60s) and revalidated via ETag once stale; `{ force: true }`
|
|
1817
|
+
* refetches unconditionally. Concurrent callers share one in-flight fetch.
|
|
1818
|
+
*/
|
|
1819
|
+
catalog(opts?: ExtCatalogOptions): Promise<ExtCatalog>;
|
|
1820
|
+
/**
|
|
1821
|
+
* One plugin's catalog entry, straight from
|
|
1822
|
+
* `GET /api/ext/_catalog/:pluginKey`. Rejects with
|
|
1823
|
+
* {@link PluginNotEnabledError} when the plugin is not enabled (404).
|
|
1824
|
+
*/
|
|
1825
|
+
plugin(pluginKey: string): Promise<ExtCatalogPlugin>;
|
|
1826
|
+
}
|
|
1827
|
+
/**
|
|
1828
|
+
* Runtime guard for generated plugin clients: throws
|
|
1829
|
+
* {@link PluginVersionMismatchError} when the installed plugin's **major**
|
|
1830
|
+
* version differs from the expected range's major. `expectedRange` accepts the
|
|
1831
|
+
* common single-major forms a generator emits: `"1"`, `"1.x"`, `"1.2.3"`,
|
|
1832
|
+
* `"^1.2.3"`, `"~1.2"`, `"=1.0.0"`, `"v1"`.
|
|
1833
|
+
*/
|
|
1834
|
+
declare function assertPluginVersion(catalogEntry: ExtCatalogPlugin, expectedRange: string): void;
|
|
1835
|
+
|
|
1836
|
+
/**
|
|
1837
|
+
* `sdk.games` — launch a game session.
|
|
1838
|
+
*
|
|
1839
|
+
* `providerSession` maps to the generic `POST /providers/:provider/session` and
|
|
1840
|
+
* returns a fully-formed `launchUrl` to render in the game iframe plus a
|
|
1841
|
+
* `sessionId`. Provider adapters are registered per tenant in the runtime
|
|
1842
|
+
* (built-in `"fake"` for dev, or plugin-contributed adapters). The SDK never
|
|
1843
|
+
* sees provider secrets; all money flows server-side through the provider →
|
|
1844
|
+
* wallet (and pushes back over the realtime `wallet.balance` channel).
|
|
1845
|
+
*
|
|
1846
|
+
* `launch` is a stub: the runtime removed its route (`POST
|
|
1847
|
+
* /plugins/slotserv/launch`) together with the baked-in SlotServ plugin
|
|
1848
|
+
* (runtime `16d4e5d`); it throws `NotImplementedError` until an aggregation
|
|
1849
|
+
* plugin ships a launch surface again.
|
|
1850
|
+
*/
|
|
1851
|
+
|
|
1852
|
+
/**
|
|
1853
|
+
* Assert a game `launchUrl` is safe to render in an iframe: `https:` only
|
|
1854
|
+
* (`http:` tolerated for loopback hosts so local dev runtimes keep working).
|
|
1855
|
+
* `javascript:`, `data:`, and every other scheme are rejected — a compromised or
|
|
1856
|
+
* misconfigured provider must not be able to hand the frontend a script URL.
|
|
1857
|
+
*
|
|
1858
|
+
* The SDK applies this to every launch result; it is exported so app code can
|
|
1859
|
+
* re-validate URLs it stored or received elsewhere. Also render the iframe with
|
|
1860
|
+
* `sandbox="allow-scripts allow-same-origin allow-forms"` (see the games guide).
|
|
1861
|
+
*
|
|
1862
|
+
* @returns the URL, unchanged, for fluent use.
|
|
1863
|
+
* @throws ValidationError (`LAUNCH_URL_INVALID`, status 0) when the URL is unsafe.
|
|
1864
|
+
*/
|
|
1865
|
+
declare function assertLaunchUrl(url: string): string;
|
|
1866
|
+
declare class GamesModule {
|
|
1867
|
+
private readonly http;
|
|
1868
|
+
private readonly session;
|
|
1869
|
+
constructor(http: HttpClient, session: Session);
|
|
1870
|
+
/**
|
|
1871
|
+
* @deprecated The runtime removed the SlotServ aggregation route
|
|
1872
|
+
* (`POST /plugins/slotserv/launch`) along with its baked-in plugins
|
|
1873
|
+
* (runtime `16d4e5d`, 2026-07-10) — this now always throws
|
|
1874
|
+
* {@link NotImplementedError}. Launch through a tenant-registered provider
|
|
1875
|
+
* adapter instead: {@link providerSession}. It returns the same
|
|
1876
|
+
* `{ launchUrl, sessionId }` shape.
|
|
1877
|
+
*/
|
|
1878
|
+
launch(gameId: string, _input?: LaunchInput): Promise<LaunchResult>;
|
|
1879
|
+
/**
|
|
1880
|
+
* Open a game session with a specific provider adapter via the generic
|
|
1881
|
+
* `POST /providers/:provider/session` route (provider-agnostic counterpart of
|
|
1882
|
+
* {@link launch}). Currency defaults from the session.
|
|
1883
|
+
*/
|
|
1884
|
+
providerSession(provider: string, gameId: string, options?: {
|
|
1885
|
+
playerId?: string;
|
|
1886
|
+
currency?: string;
|
|
1887
|
+
}): Promise<LaunchResult>;
|
|
1888
|
+
/** Bet history (`GET /player/bets`), newest first, amounts in minor units. */
|
|
1889
|
+
bets(query?: BetsQuery): Promise<BetsPage>;
|
|
1890
|
+
/** One bet plus its round-linked wallet transactions (`GET /player/bets/:betId`). */
|
|
1891
|
+
bet(betId: string): Promise<BetDetail>;
|
|
1892
|
+
/** Game-session history with per-session bet/win totals in minor units. */
|
|
1893
|
+
gameSessions(query?: {
|
|
1894
|
+
from?: string | Date;
|
|
1895
|
+
to?: string | Date;
|
|
1896
|
+
limit?: number;
|
|
1897
|
+
offset?: number;
|
|
1898
|
+
}): Promise<GameSessionsPage>;
|
|
1899
|
+
}
|
|
1900
|
+
|
|
1901
|
+
/**
|
|
1902
|
+
* `sdk.kyc` — the player-side KYC surface (`/player/kyc/*`).
|
|
1903
|
+
*
|
|
1904
|
+
* Flow: {@link status} says where the player stands and whether the withdrawal
|
|
1905
|
+
* gate is met; {@link requirements} lists the open requests with their document
|
|
1906
|
+
* checklists; {@link uploadDocument} sends files (multipart; JPEG/PNG/PDF,
|
|
1907
|
+
* sniffed server-side); {@link submit} hands a completed request to review.
|
|
1908
|
+
* Players can never download their documents — item `document` objects are
|
|
1909
|
+
* metadata only. Live status pushes arrive on the realtime `player` channel
|
|
1910
|
+
* (`kyc_status_changed` / `kyc_documents_requested`).
|
|
1911
|
+
*
|
|
1912
|
+
* If the tenant delegates the KYC flow to a plugin, mutations throw
|
|
1913
|
+
* {@link FlowDelegatedError} — drive the plugin's `/api/ext` routes via
|
|
1914
|
+
* `sdk.ext` instead.
|
|
1915
|
+
*/
|
|
1916
|
+
|
|
1917
|
+
declare class KycModule {
|
|
1918
|
+
private readonly http;
|
|
1919
|
+
constructor(http: HttpClient);
|
|
1920
|
+
/** Verification level, per-gate requirements, and email/phone verification flags. */
|
|
1921
|
+
status(): Promise<KycStatusResponse>;
|
|
1922
|
+
/** Open KYC requests (draft/submitted/in_review/needs_more) with their checklists. */
|
|
1923
|
+
requirements(): Promise<KycRequest[]>;
|
|
1924
|
+
/** All requests, newest first. `limit` 1–100 (default 50). */
|
|
1925
|
+
history(query?: {
|
|
1926
|
+
limit?: number;
|
|
1927
|
+
offset?: number;
|
|
1928
|
+
}): Promise<KycHistoryPage>;
|
|
1929
|
+
/**
|
|
1930
|
+
* Upload a document. With `requestId` it attaches to that request (which must
|
|
1931
|
+
* be `draft`/`needs_more`); without, it is a proactive re-verification
|
|
1932
|
+
* upload. Rejections are `ValidationError` with code `KYC_DOCUMENT_INVALID`
|
|
1933
|
+
* (format/size/expiry — see `details`).
|
|
1934
|
+
*
|
|
1935
|
+
* Large files on slow uplinks can outlive the client's default request
|
|
1936
|
+
* timeout (30s) — raise or disable it per call via `options.timeoutMs`
|
|
1937
|
+
* (`0` disables).
|
|
1938
|
+
*/
|
|
1939
|
+
uploadDocument(input: KycUploadInput, options?: {
|
|
1940
|
+
timeoutMs?: number;
|
|
1941
|
+
signal?: AbortSignal;
|
|
1942
|
+
}): Promise<KycUploadResult>;
|
|
1943
|
+
/**
|
|
1944
|
+
* Submit a request for review. Every `required` checklist item needs an
|
|
1945
|
+
* uploaded/approved document first — otherwise `ValidationError` with
|
|
1946
|
+
* `details.missing` listing the absent `documentTypeKey`s.
|
|
1947
|
+
*/
|
|
1948
|
+
submit(requestId: string): Promise<{
|
|
1949
|
+
status: "submitted";
|
|
1950
|
+
}>;
|
|
1951
|
+
}
|
|
1952
|
+
|
|
1953
|
+
/**
|
|
1954
|
+
* `sdk.player` — profile, preferences, consents, responsible-gaming limits, and
|
|
1955
|
+
* account lifecycle (close / GDPR export).
|
|
1956
|
+
*
|
|
1957
|
+
* Maps to the runtime's `/player/*` account surface. Responsible-gaming limit
|
|
1958
|
+
* values ride the wire as scale-4 minor-unit integer *strings* (minutes for
|
|
1959
|
+
* `session_time`); the SDK converts them to `MinorUnits` numbers at the
|
|
1960
|
+
* boundary. KYC lives at `sdk.kyc`.
|
|
1961
|
+
*/
|
|
1962
|
+
|
|
1963
|
+
declare class PlayerModule {
|
|
1964
|
+
private readonly http;
|
|
1965
|
+
private readonly auth;
|
|
1966
|
+
constructor(http: HttpClient, auth: AuthModule);
|
|
1967
|
+
/** The logged-in player's identity + profile (via `/auth/player/me`). */
|
|
1968
|
+
getProfile(): Promise<MeResponse>;
|
|
1969
|
+
/**
|
|
1970
|
+
* The full profile row plus which identity fields are KYC-locked
|
|
1971
|
+
* (`GET /player/profile`). `profile` is null until one exists.
|
|
1972
|
+
*/
|
|
1973
|
+
profile(): Promise<ProfileResponse>;
|
|
1974
|
+
/**
|
|
1975
|
+
* Patch the profile (`PATCH /player/profile`). At least one field; `null`
|
|
1976
|
+
* clears. Identity fields (`firstName`/`lastName`/`birthDate`/`country`)
|
|
1977
|
+
* throw `ConflictError` (`PROFILE_FIELD_LOCKED`) after KYC approval.
|
|
1978
|
+
*/
|
|
1979
|
+
updateProfile(patch: UpdateProfileInput): Promise<UpdateProfileResult>;
|
|
1980
|
+
/** Preferences (locale, display currency, marketing consents, reality check). */
|
|
1981
|
+
getPreferences(): Promise<PlayerPreferences>;
|
|
1982
|
+
/**
|
|
1983
|
+
* Update preferences (`PUT /player/preferences`, partial — at least one
|
|
1984
|
+
* field). Marketing changes are recorded on the consent trail; set
|
|
1985
|
+
* `realityCheckMinutes: null` to disable the reality check.
|
|
1986
|
+
*/
|
|
1987
|
+
setPreferences(patch: SetPreferencesInput): Promise<PlayerPreferences>;
|
|
1988
|
+
/** The append-only marketing-consent trail, newest first. */
|
|
1989
|
+
consents(query?: {
|
|
1990
|
+
limit?: number;
|
|
1991
|
+
offset?: number;
|
|
1992
|
+
}): Promise<ConsentsPage>;
|
|
1993
|
+
/**
|
|
1994
|
+
* Active responsible-gaming limits, values in minor units (minutes for
|
|
1995
|
+
* `session_time`). `pending` carries a ratcheted increase/removal and when it
|
|
1996
|
+
* applies.
|
|
1997
|
+
*/
|
|
1998
|
+
getLimits(): Promise<PlayerLimit[]>;
|
|
1999
|
+
/**
|
|
2000
|
+
* Set/remove limits (`PUT /player/limits`). `value` in minor units (minutes
|
|
2001
|
+
* for `session_time`); `null` removes. New limits and decreases apply
|
|
2002
|
+
* immediately; increases/removals apply after the 24h ratchet cooldown
|
|
2003
|
+
* (returned in `pending`).
|
|
2004
|
+
*/
|
|
2005
|
+
setLimits(limits: SetLimitInput[]): Promise<PlayerLimit[]>;
|
|
2006
|
+
/** Responsible-gaming actions beyond limits. */
|
|
2007
|
+
readonly rg: {
|
|
2008
|
+
/**
|
|
2009
|
+
* Start a cooling-off period (blocks gameplay + deposits; login and
|
|
2010
|
+
* withdrawals stay). Extend-only — a shorter period than the active one is
|
|
2011
|
+
* a `ConflictError`.
|
|
2012
|
+
*/
|
|
2013
|
+
coolOff: (period: CoolOffPeriod) => Promise<{
|
|
2014
|
+
success: true;
|
|
2015
|
+
until: string;
|
|
2016
|
+
}>;
|
|
2017
|
+
/**
|
|
2018
|
+
* Self-exclude. Irreversible player-side; revokes every session (including
|
|
2019
|
+
* this one). `until` is null for `"permanent"`.
|
|
2020
|
+
*/
|
|
2021
|
+
selfExclude: (period: SelfExclusionPeriod) => Promise<{
|
|
2022
|
+
success: true;
|
|
2023
|
+
until: string | null;
|
|
2024
|
+
}>;
|
|
2025
|
+
/** Acknowledge a reality-check prompt (audit trail only). */
|
|
2026
|
+
acknowledgeRealityCheck: (gameSessionId?: string) => Promise<{
|
|
2027
|
+
success: true;
|
|
2028
|
+
}>;
|
|
2029
|
+
};
|
|
2030
|
+
/** Presence signals. */
|
|
2031
|
+
readonly presence: {
|
|
2032
|
+
/**
|
|
2033
|
+
* Tell the runtime this player is still here (`POST
|
|
2034
|
+
* /player/presence/heartbeat`, 204). Any authenticated request already
|
|
2035
|
+
* refreshes presence server-side — call this only for clients that idle
|
|
2036
|
+
* while "in play" (e.g. a game iframe generates provider callbacks, not
|
|
2037
|
+
* player API traffic). Best-effort on the server: a storage blip never
|
|
2038
|
+
* fails the request; presence decays via TTL and heals on the next call.
|
|
2039
|
+
*/
|
|
2040
|
+
heartbeat: () => Promise<void>;
|
|
2041
|
+
};
|
|
2042
|
+
/** Account lifecycle. */
|
|
2043
|
+
readonly account: {
|
|
2044
|
+
/**
|
|
2045
|
+
* Close the account (idempotent). Requires zero balances and no pending
|
|
2046
|
+
* withdrawal — otherwise `ConflictError` with code `BALANCE_REMAINING` or
|
|
2047
|
+
* `WITHDRAWAL_PENDING`. Revokes every session.
|
|
2048
|
+
*/
|
|
2049
|
+
close: (reason?: string) => Promise<{
|
|
2050
|
+
success: true;
|
|
2051
|
+
}>;
|
|
2052
|
+
/**
|
|
2053
|
+
* Request a GDPR data export (202; one active at a time — a second request
|
|
2054
|
+
* while pending/processing is a `ConflictError`). The download link is
|
|
2055
|
+
* delivered out-of-band.
|
|
2056
|
+
*/
|
|
2057
|
+
requestDataExport: () => Promise<DataExportStatus>;
|
|
2058
|
+
/** Latest export request, or `{ status: "none" }` if never requested. */
|
|
2059
|
+
dataExportStatus: () => Promise<DataExportStatus>;
|
|
2060
|
+
};
|
|
2061
|
+
}
|
|
2062
|
+
|
|
2063
|
+
/**
|
|
2064
|
+
* `sdk.realtime` — the realtime gateway, pre-wired with the ticket handshake.
|
|
2065
|
+
*
|
|
2066
|
+
* Flow: `POST /realtime/ticket` (cookie-authenticated, via the HTTP layer) →
|
|
2067
|
+
* `{ ticket }` → open the WS → send `{ type: "auth", token }`. In the browser the
|
|
2068
|
+
* same-site HttpOnly cookie can authenticate the upgrade directly; the ticket path
|
|
2069
|
+
* is used everywhere for portability and to survive access-token expiry.
|
|
2070
|
+
*
|
|
2071
|
+
* This is a thin delegate over the standalone {@link createRealtimeClient} so it
|
|
2072
|
+
* shares the SDK's session/config and you don't pass `url`/`getAuthCredential`.
|
|
2073
|
+
*/
|
|
2074
|
+
|
|
2075
|
+
/** Realtime options you can pass to `createCasinoClient({ realtime })`. */
|
|
2076
|
+
interface RealtimeOptions {
|
|
2077
|
+
/** Reconcile missed state after every (re)connect; the SDK calls this for you. */
|
|
2078
|
+
resync?: (ctx: {
|
|
2079
|
+
since?: string;
|
|
2080
|
+
}) => Promise<void>;
|
|
2081
|
+
backoff?: {
|
|
2082
|
+
baseMs?: number;
|
|
2083
|
+
maxMs?: number;
|
|
2084
|
+
factor?: number;
|
|
2085
|
+
};
|
|
2086
|
+
heartbeatMs?: number;
|
|
2087
|
+
dedupeWindow?: number;
|
|
2088
|
+
/** Node only: pass `ws` (`import WebSocket from "ws"`). */
|
|
2089
|
+
WebSocketImpl?: WebSocketImpl;
|
|
2090
|
+
}
|
|
2091
|
+
declare class RealtimeModule {
|
|
2092
|
+
private readonly http;
|
|
2093
|
+
private readonly wsUrl;
|
|
2094
|
+
private readonly options;
|
|
2095
|
+
private client;
|
|
2096
|
+
constructor(http: HttpClient, wsUrl: string, options?: RealtimeOptions);
|
|
2097
|
+
/** Fetch a single-use socket ticket from the gateway. */
|
|
2098
|
+
ticket(): Promise<{
|
|
2099
|
+
ticket: string;
|
|
2100
|
+
expiresIn: number;
|
|
2101
|
+
}>;
|
|
2102
|
+
private ensure;
|
|
2103
|
+
get state(): ConnectionState;
|
|
2104
|
+
/** Open the socket and authenticate. Call once after login. */
|
|
2105
|
+
connect(): Promise<void>;
|
|
2106
|
+
/** Close intentionally (call on logout). Stops auto-reconnect. */
|
|
2107
|
+
disconnect(): Promise<void>;
|
|
2108
|
+
/** Subscribe to a channel with a typed handler. Returns an unsubscribe fn. */
|
|
2109
|
+
on<C extends RealtimeChannel>(channel: C, handler: (event: ChannelEventMap[C]) => void): Unsubscribe;
|
|
2110
|
+
subscribe(channels: readonly RealtimeChannel[]): Promise<void>;
|
|
2111
|
+
unsubscribe(channels: readonly RealtimeChannel[]): Promise<void>;
|
|
2112
|
+
withSubscription<T>(channels: readonly RealtimeChannel[], scope: () => Promise<T>): Promise<T>;
|
|
2113
|
+
activeChannels(): readonly RealtimeChannel[];
|
|
2114
|
+
onStateChange(handler: (state: ConnectionState) => void): Unsubscribe;
|
|
2115
|
+
onError(handler: (error: RealtimeError) => void): Unsubscribe;
|
|
2116
|
+
}
|
|
2117
|
+
|
|
2118
|
+
/**
|
|
2119
|
+
* `createCasinoClient` — the framework-agnostic entry point.
|
|
2120
|
+
*
|
|
2121
|
+
* Composes the HTTP layer + session cache + every module into one object:
|
|
2122
|
+
* `{ auth, cashier, wallet, catalog, games, player, realtime, ... }`. Construct it
|
|
2123
|
+
* once and share it (e.g. via the React `CasinoProvider`).
|
|
2124
|
+
*/
|
|
2125
|
+
|
|
2126
|
+
interface CreateCasinoClientConfig extends CasinoClientConfig {
|
|
2127
|
+
/** Options for `sdk.realtime` (resync hook, backoff, Node WebSocket, …). */
|
|
2128
|
+
realtime?: RealtimeOptions;
|
|
2129
|
+
}
|
|
2130
|
+
interface CasinoClient {
|
|
2131
|
+
readonly affiliate: AffiliateModule;
|
|
2132
|
+
readonly auth: AuthModule;
|
|
2133
|
+
readonly cashier: CashierModule;
|
|
2134
|
+
readonly wallet: WalletModule;
|
|
2135
|
+
readonly catalog: CatalogModule;
|
|
2136
|
+
/**
|
|
2137
|
+
* Plugin actions: `sdk.ext(pluginKey).call(actionKey, input)` plus catalog
|
|
2138
|
+
* discovery (`sdk.ext.catalog()` / `sdk.ext.plugin(key)`).
|
|
2139
|
+
*/
|
|
2140
|
+
readonly ext: ExtModule;
|
|
2141
|
+
readonly games: GamesModule;
|
|
2142
|
+
/** Player-side KYC: status, requirements, document upload, submit. */
|
|
2143
|
+
readonly kyc: KycModule;
|
|
2144
|
+
readonly player: PlayerModule;
|
|
2145
|
+
readonly realtime: RealtimeModule;
|
|
2146
|
+
/** A snapshot of the cached player/profile/currency (no network call). */
|
|
2147
|
+
getSession(): SessionSnapshot;
|
|
2148
|
+
/**
|
|
2149
|
+
* A fully isolated scope of this client bound to `jar`: same configuration, fresh
|
|
2150
|
+
* HTTP pipeline, fresh session cache — nothing (cookies, CSRF, cached player) is
|
|
2151
|
+
* shared with this client or any other scope.
|
|
2152
|
+
*
|
|
2153
|
+
* THE way to use the SDK server-side (Next.js route handlers, RSC, API routes):
|
|
2154
|
+
* a module-scope client is safe in the browser (one user per tab) but on a server
|
|
2155
|
+
* it is shared across every user's requests, so its jar would replay user A's
|
|
2156
|
+
* auth cookies on user B's request. Create one scope per incoming request:
|
|
2157
|
+
*
|
|
2158
|
+
* ```ts
|
|
2159
|
+
* const scoped = client.withCookies(new CookieJar());
|
|
2160
|
+
* await scoped.auth.login(credentials); // cookies stay inside this scope
|
|
2161
|
+
* ```
|
|
2162
|
+
*/
|
|
2163
|
+
withCookies(jar: CookieJar): CasinoClient;
|
|
2164
|
+
}
|
|
2165
|
+
declare function createCasinoClient(config: CreateCasinoClientConfig): CasinoClient;
|
|
2166
|
+
|
|
2167
|
+
export { InsufficientFundsError as $, type AffiliateSubIds as A, type BalanceResponse as B, COOKIE as C, type DataExportStatus as D, type ExtActionInput as E, type ExtActionShape as F, type ExtCallArgs as G, type ExtCallOpts as H, type ExtCatalog as I, type ExtCatalogOptions as J, type ExtCatalogPlugin as K, type ExtMethod as L, type ExtModule as M, type ExtPluginClient as N, type ExtRegistry as O, type ExtRequestInit as P, type FetchLike as Q, FlowDelegatedError as R, ForbiddenError as S, type Game as T, type GameFeatures as U, type GamePage as V, type GameSession as W, type GameSessionsPage as X, type GeoQuery as Y, HEADER as Z, type HistoryRangeQuery as _, type AppErrorEnvelope as a, type SocialProvider as a$, type KycChecklistItem as a0, type KycDocumentStatus as a1, type KycHistoryEntry as a2, type KycHistoryPage as a3, type KycItemDocument as a4, type KycRequest as a5, type KycRequestStatus as a6, type KycStatusResponse as a7, type KycTrigger as a8, type KycUploadInput as a9, type PlayerPreferences as aA, type PlayerProfile as aB, type PlayerProfileRecord as aC, type PlayerStatus as aD, type PluginActionDescriptor as aE, PluginActionNotFoundError as aF, type PluginFrontendDecl as aG, PluginNotEnabledError as aH, PluginUnavailableError as aI, PluginVersionMismatchError as aJ, type ProfileResponse as aK, type Provider as aL, RateLimitError as aM, type RealtimeOptions as aN, RgBlockedError as aO, type SafeSession as aP, type SearchQuery as aQ, type SelfExclusionPeriod as aR, ServerError as aS, type SessionHistoryPage as aT, type SessionHistoryQuery as aU, type SessionSnapshot as aV, type SetLimitInput as aW, type SetPreferencesInput as aX, type SignupAttribution as aY, type SignupInput as aZ, type SocialAccount as a_, type KycUploadResult as aa, type LaunchInput as ab, type LaunchResult as ac, type LedgerEntry as ad, type LedgerQuery as ae, LimitExceededError as af, type LimitKind as ag, type LimitPeriod as ah, type LobbyQuery as ai, type LoginAttempt as aj, type LoginHistoryPage as ak, type LoginInput as al, MONEY_SCALE as am, type MeResponse as an, type MinorUnits as ao, NetworkError as ap, NotFoundError as aq, NotImplementedError as ar, type OddsFormat as as, OperationDeniedError as at, type PageInfo as au, type Paginated as av, type PaymentMethod as aw, type Player as ax, type PlayerKycStatus as ay, type PlayerLimit as az, AuthError as b, type SocialStartOptions as b0, type TrackClickInput as b1, type TrackClickResult as b2, type TransactionStatus as b3, type TransactionType as b4, type TransactionsQuery as b5, type TypedExtPluginClient as b6, UnprocessableError as b7, type UpdateProfileInput as b8, type UpdateProfileResult as b9, type UtmParams as ba, ValidationError as bb, type VerificationRequestResult as bc, type WalletBucketName as bd, type WalletBuckets as be, type WalletTransaction as bf, type WithdrawalDetail as bg, type WithdrawalHistoryPage as bh, type WithdrawalHistoryQuery as bi, type WithdrawalInput as bj, type WithdrawalResult as bk, type WithdrawalSummary as bl, type WithdrawalTxStatus as bm, assertLaunchUrl as bn, assertPluginVersion as bo, createCasinoClient as bp, decimalToMinor as bq, formatMoney as br, isAppErrorEnvelope as bs, minorStringToMinor as bt, minorToAmount as bu, minorToDecimal as bv, type AuthSession as c, type Bet as d, type BetDetail as e, type BetStatus as f, type BetsPage as g, type BetsQuery as h, type CasinoClient as i, type CasinoClientConfig as j, CasinoSdkError as k, type CategoryNode as l, ConflictError as m, type ConsentEntry as n, type ConsentsPage as o, CookieJar as p, type CoolOffPeriod as q, type CreateCasinoClientConfig as r, type DepositDetail as s, type DepositHistoryPage as t, type DepositHistoryQuery as u, type DepositInput as v, type DepositResult as w, type DepositSummary as x, type DepositTxStatus as y, type ExtActionOutput as z };
|