@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.
@@ -0,0 +1,205 @@
1
+ /**
2
+ * Canonical realtime surface, aligned with the runtime's
3
+ * `docs/realtime-sdk-contract.ts` (the agreed contract; verbatim copy kept in
4
+ * `contract.vendored.ts` for diffing — `pnpm sync:contract` refreshes it). We
5
+ * ALIGN with that file — do not fork it. Naming note: the runtime doc calls the
6
+ * core-channel union `RealtimeChannel` and the core+ext union
7
+ * `AnyRealtimeChannel`; here they are {@link CoreRealtimeChannel} and
8
+ * {@link RealtimeChannel} respectively. The plugin `ext.*` channel family
9
+ * ({@link ExtChannel}, {@link ExtPluginEvent}) is part of the runtime contract.
10
+ * SDK-local additions: {@link WebSocketImpl}, `dedupeWindow`,
11
+ * {@link REALTIME_CHANNELS}, {@link isExtChannel}.
12
+ *
13
+ * Money amounts are scale-4 integer minor units (see `core/money.ts`).
14
+ */
15
+ /** Statically-known core channels. Strings are stable, versioned, and authz-checked server-side. */
16
+ type CoreRealtimeChannel = "wallet.balance" | "wallet.deposit" | "wallet.withdrawal" | "gaming" | "bonus" | "player";
17
+ /**
18
+ * Channel family published by tenant-enabled plugins, e.g. `"ext.cashback"`.
19
+ * Which ones exist comes from the ext catalog (`ExtCatalogPlugin.channels`);
20
+ * subscribing for a non-enabled plugin is rejected by the gateway exactly like
21
+ * an unknown channel.
22
+ */
23
+ type ExtChannel = `ext.${string}`;
24
+ /** Logical channels: the static core set plus the dynamic plugin family. */
25
+ type RealtimeChannel = CoreRealtimeChannel | ExtChannel;
26
+ /**
27
+ * Narrow a plain string (e.g. an entry of `ExtCatalogPlugin.channels`, which the
28
+ * wire contract types as `string[]`) to a subscribable {@link ExtChannel}.
29
+ */
30
+ declare function isExtChannel(value: string): value is ExtChannel;
31
+ /**
32
+ * All statically-known subscribable channels, for runtime validation/iteration.
33
+ * Plugin `ext.*` channels are dynamic (discovered via the ext catalog) and
34
+ * intentionally not listed.
35
+ */
36
+ declare const REALTIME_CHANNELS: readonly CoreRealtimeChannel[];
37
+ type WalletBucket = "cash" | "bonus" | "locked";
38
+ /** Minor units (scale-4 integer). Never do money math on the client beyond display. */
39
+ type MinorUnits = number;
40
+ /** Common envelope present on every server → client business event. */
41
+ interface RealtimeEventMeta {
42
+ readonly event: string;
43
+ readonly eventId: string;
44
+ readonly v: number;
45
+ readonly occurredAt: string;
46
+ readonly tenantId: string;
47
+ }
48
+ interface WalletBalanceEvent extends RealtimeEventMeta {
49
+ readonly channel: "wallet.balance";
50
+ readonly data: {
51
+ readonly currency: string;
52
+ readonly balances: Readonly<Record<WalletBucket, MinorUnits>>;
53
+ readonly change: {
54
+ readonly bucket: WalletBucket;
55
+ readonly direction: "credit" | "debit";
56
+ readonly amount: MinorUnits;
57
+ readonly reason: string;
58
+ };
59
+ };
60
+ }
61
+ type DepositStatus = "pending" | "processing" | "completed" | "failed" | "cancelled";
62
+ interface WalletDepositEvent extends RealtimeEventMeta {
63
+ readonly channel: "wallet.deposit";
64
+ readonly data: {
65
+ readonly depositId: string;
66
+ readonly status: DepositStatus;
67
+ readonly amount: MinorUnits;
68
+ readonly currency: string;
69
+ };
70
+ }
71
+ type WithdrawalStatus = "requested" | "approved" | "rejected" | "processing" | "completed" | "reversed" | "cancelled";
72
+ interface WalletWithdrawalEvent extends RealtimeEventMeta {
73
+ readonly channel: "wallet.withdrawal";
74
+ readonly data: {
75
+ readonly withdrawalId: string;
76
+ readonly status: WithdrawalStatus;
77
+ readonly amount: MinorUnits;
78
+ readonly currency: string;
79
+ };
80
+ }
81
+ interface GamingEvent extends RealtimeEventMeta {
82
+ readonly channel: "gaming";
83
+ readonly data: {
84
+ readonly roundId: string;
85
+ readonly betId?: string;
86
+ readonly status: "placed" | "settled" | "rolled_back" | "round_closed";
87
+ /** Internal catalog game id — map to a display name from your catalog. */
88
+ readonly gameId?: string;
89
+ /** Provider key that produced the round (e.g. `"slotserv"`). */
90
+ readonly provider?: string;
91
+ /** Wager amount, present on `placed`. */
92
+ readonly betAmount?: MinorUnits;
93
+ /** Payout, present on `settled`. */
94
+ readonly winAmount?: MinorUnits;
95
+ readonly currency?: string;
96
+ };
97
+ }
98
+ interface BonusEvent extends RealtimeEventMeta {
99
+ readonly channel: "bonus";
100
+ readonly data: {
101
+ readonly bonusId: string;
102
+ readonly status: "granted" | "revoked";
103
+ readonly amount?: MinorUnits;
104
+ readonly currency?: string;
105
+ readonly reason?: string;
106
+ };
107
+ }
108
+ interface PlayerEvent extends RealtimeEventMeta {
109
+ readonly channel: "player";
110
+ readonly data: {
111
+ readonly change: "password_changed" | "social_linked" | "social_unlinked" | "account_locked" | "email_verified" | "phone_verified" | "session_revoked" | "kyc_status_changed" | "kyc_documents_requested" | "limit_changed" | "self_excluded" | "reality_check";
112
+ readonly data?: Readonly<Record<string, string | number | boolean | null>>;
113
+ };
114
+ }
115
+ /**
116
+ * An event published by a plugin on its `ext.<pluginKey>` channel. The standard
117
+ * realtime envelope applies (incl. `eventId` dedupe); `data.type` is the
118
+ * plugin-defined event type (e.g. `"cashback.claimed"`) and `data.payload` is
119
+ * opaque to the SDK.
120
+ */
121
+ interface ExtPluginEvent extends RealtimeEventMeta {
122
+ readonly channel: ExtChannel;
123
+ readonly data: {
124
+ readonly type: string;
125
+ readonly payload: unknown;
126
+ };
127
+ }
128
+ /** Maps a channel name to its event payload type, for type-safe `on(...)`. */
129
+ interface ChannelEventMap {
130
+ "wallet.balance": WalletBalanceEvent;
131
+ "wallet.deposit": WalletDepositEvent;
132
+ "wallet.withdrawal": WalletWithdrawalEvent;
133
+ gaming: GamingEvent;
134
+ bonus: BonusEvent;
135
+ player: PlayerEvent;
136
+ [channel: `ext.${string}`]: ExtPluginEvent;
137
+ }
138
+ type ConnectionState = "idle" | "connecting" | "ready" | "reconnecting" | "closed";
139
+ interface RealtimeError {
140
+ readonly code: string;
141
+ readonly message: string;
142
+ readonly details?: Record<string, unknown>;
143
+ }
144
+ interface RealtimeClientOptions {
145
+ /** wss URL of the gateway, e.g. `wss://grandbet.example/realtime`. */
146
+ readonly url: string;
147
+ /**
148
+ * How the SDK obtains a handshake credential. Return `{ ticket }` (fetched from
149
+ * `POST /realtime/ticket`) or nothing to rely on a same-site HttpOnly cookie.
150
+ * Called again on every reconnect so an expiring credential never drops the socket.
151
+ */
152
+ readonly getAuthCredential?: () => Promise<{
153
+ ticket: string;
154
+ } | void>;
155
+ /**
156
+ * Snapshot read run after every (re)connect to reconcile missed state. The socket
157
+ * accelerates; REST is the truth. The SDK calls this; the app supplies the reads.
158
+ */
159
+ readonly resync?: (ctx: {
160
+ since?: string;
161
+ }) => Promise<void>;
162
+ /** Reconnect backoff. Defaults: base 500ms, factor 2, max 15s, full jitter. */
163
+ readonly backoff?: {
164
+ readonly baseMs?: number;
165
+ readonly maxMs?: number;
166
+ readonly factor?: number;
167
+ };
168
+ /** Override only for testing; the server dictates heartbeat in the `ready` frame. */
169
+ readonly heartbeatMs?: number;
170
+ /** Inject a WebSocket implementation (Node `ws`); defaults to the platform global. */
171
+ readonly WebSocketImpl?: WebSocketImpl;
172
+ /** Max remembered eventIds for dedupe. Default 512. */
173
+ readonly dedupeWindow?: number;
174
+ }
175
+ type Unsubscribe = () => void;
176
+ interface RealtimeClient {
177
+ readonly state: ConnectionState;
178
+ connect(): Promise<void>;
179
+ disconnect(): Promise<void>;
180
+ on<C extends RealtimeChannel>(channel: C, handler: (event: ChannelEventMap[C]) => void): Unsubscribe;
181
+ subscribe(channels: readonly RealtimeChannel[]): Promise<void>;
182
+ unsubscribe(channels: readonly RealtimeChannel[]): Promise<void>;
183
+ withSubscription<T>(channels: readonly RealtimeChannel[], scope: () => Promise<T>): Promise<T>;
184
+ activeChannels(): readonly RealtimeChannel[];
185
+ onStateChange(handler: (state: ConnectionState) => void): Unsubscribe;
186
+ onError(handler: (error: RealtimeError) => void): Unsubscribe;
187
+ }
188
+ type CreateRealtimeClient = (options: RealtimeClientOptions) => RealtimeClient;
189
+ interface WebSocketLike {
190
+ send(data: string): void;
191
+ close(code?: number, reason?: string): void;
192
+ readonly readyState: number;
193
+ onopen: ((ev: unknown) => void) | null;
194
+ onclose: ((ev: {
195
+ code?: number;
196
+ reason?: string;
197
+ }) => void) | null;
198
+ onerror: ((ev: unknown) => void) | null;
199
+ onmessage: ((ev: {
200
+ data: unknown;
201
+ }) => void) | null;
202
+ }
203
+ type WebSocketImpl = new (url: string) => WebSocketLike;
204
+
205
+ export { type BonusEvent as B, type ChannelEventMap as C, type DepositStatus as D, type ExtChannel as E, type GamingEvent as G, type MinorUnits as M, type PlayerEvent as P, REALTIME_CHANNELS as R, type Unsubscribe as U, type WalletBalanceEvent as W, type ConnectionState as a, type CoreRealtimeChannel as b, type ExtPluginEvent as c, type RealtimeChannel as d, type RealtimeError as e, type WalletBucket as f, type WalletDepositEvent as g, type WalletWithdrawalEvent as h, isExtChannel as i, type WebSocketImpl as j, type RealtimeClientOptions as k, type RealtimeClient as l, type CreateRealtimeClient as m, type RealtimeEventMeta as n, type WebSocketLike as o, type WithdrawalStatus as p };
@@ -0,0 +1,205 @@
1
+ /**
2
+ * Canonical realtime surface, aligned with the runtime's
3
+ * `docs/realtime-sdk-contract.ts` (the agreed contract; verbatim copy kept in
4
+ * `contract.vendored.ts` for diffing — `pnpm sync:contract` refreshes it). We
5
+ * ALIGN with that file — do not fork it. Naming note: the runtime doc calls the
6
+ * core-channel union `RealtimeChannel` and the core+ext union
7
+ * `AnyRealtimeChannel`; here they are {@link CoreRealtimeChannel} and
8
+ * {@link RealtimeChannel} respectively. The plugin `ext.*` channel family
9
+ * ({@link ExtChannel}, {@link ExtPluginEvent}) is part of the runtime contract.
10
+ * SDK-local additions: {@link WebSocketImpl}, `dedupeWindow`,
11
+ * {@link REALTIME_CHANNELS}, {@link isExtChannel}.
12
+ *
13
+ * Money amounts are scale-4 integer minor units (see `core/money.ts`).
14
+ */
15
+ /** Statically-known core channels. Strings are stable, versioned, and authz-checked server-side. */
16
+ type CoreRealtimeChannel = "wallet.balance" | "wallet.deposit" | "wallet.withdrawal" | "gaming" | "bonus" | "player";
17
+ /**
18
+ * Channel family published by tenant-enabled plugins, e.g. `"ext.cashback"`.
19
+ * Which ones exist comes from the ext catalog (`ExtCatalogPlugin.channels`);
20
+ * subscribing for a non-enabled plugin is rejected by the gateway exactly like
21
+ * an unknown channel.
22
+ */
23
+ type ExtChannel = `ext.${string}`;
24
+ /** Logical channels: the static core set plus the dynamic plugin family. */
25
+ type RealtimeChannel = CoreRealtimeChannel | ExtChannel;
26
+ /**
27
+ * Narrow a plain string (e.g. an entry of `ExtCatalogPlugin.channels`, which the
28
+ * wire contract types as `string[]`) to a subscribable {@link ExtChannel}.
29
+ */
30
+ declare function isExtChannel(value: string): value is ExtChannel;
31
+ /**
32
+ * All statically-known subscribable channels, for runtime validation/iteration.
33
+ * Plugin `ext.*` channels are dynamic (discovered via the ext catalog) and
34
+ * intentionally not listed.
35
+ */
36
+ declare const REALTIME_CHANNELS: readonly CoreRealtimeChannel[];
37
+ type WalletBucket = "cash" | "bonus" | "locked";
38
+ /** Minor units (scale-4 integer). Never do money math on the client beyond display. */
39
+ type MinorUnits = number;
40
+ /** Common envelope present on every server → client business event. */
41
+ interface RealtimeEventMeta {
42
+ readonly event: string;
43
+ readonly eventId: string;
44
+ readonly v: number;
45
+ readonly occurredAt: string;
46
+ readonly tenantId: string;
47
+ }
48
+ interface WalletBalanceEvent extends RealtimeEventMeta {
49
+ readonly channel: "wallet.balance";
50
+ readonly data: {
51
+ readonly currency: string;
52
+ readonly balances: Readonly<Record<WalletBucket, MinorUnits>>;
53
+ readonly change: {
54
+ readonly bucket: WalletBucket;
55
+ readonly direction: "credit" | "debit";
56
+ readonly amount: MinorUnits;
57
+ readonly reason: string;
58
+ };
59
+ };
60
+ }
61
+ type DepositStatus = "pending" | "processing" | "completed" | "failed" | "cancelled";
62
+ interface WalletDepositEvent extends RealtimeEventMeta {
63
+ readonly channel: "wallet.deposit";
64
+ readonly data: {
65
+ readonly depositId: string;
66
+ readonly status: DepositStatus;
67
+ readonly amount: MinorUnits;
68
+ readonly currency: string;
69
+ };
70
+ }
71
+ type WithdrawalStatus = "requested" | "approved" | "rejected" | "processing" | "completed" | "reversed" | "cancelled";
72
+ interface WalletWithdrawalEvent extends RealtimeEventMeta {
73
+ readonly channel: "wallet.withdrawal";
74
+ readonly data: {
75
+ readonly withdrawalId: string;
76
+ readonly status: WithdrawalStatus;
77
+ readonly amount: MinorUnits;
78
+ readonly currency: string;
79
+ };
80
+ }
81
+ interface GamingEvent extends RealtimeEventMeta {
82
+ readonly channel: "gaming";
83
+ readonly data: {
84
+ readonly roundId: string;
85
+ readonly betId?: string;
86
+ readonly status: "placed" | "settled" | "rolled_back" | "round_closed";
87
+ /** Internal catalog game id — map to a display name from your catalog. */
88
+ readonly gameId?: string;
89
+ /** Provider key that produced the round (e.g. `"slotserv"`). */
90
+ readonly provider?: string;
91
+ /** Wager amount, present on `placed`. */
92
+ readonly betAmount?: MinorUnits;
93
+ /** Payout, present on `settled`. */
94
+ readonly winAmount?: MinorUnits;
95
+ readonly currency?: string;
96
+ };
97
+ }
98
+ interface BonusEvent extends RealtimeEventMeta {
99
+ readonly channel: "bonus";
100
+ readonly data: {
101
+ readonly bonusId: string;
102
+ readonly status: "granted" | "revoked";
103
+ readonly amount?: MinorUnits;
104
+ readonly currency?: string;
105
+ readonly reason?: string;
106
+ };
107
+ }
108
+ interface PlayerEvent extends RealtimeEventMeta {
109
+ readonly channel: "player";
110
+ readonly data: {
111
+ readonly change: "password_changed" | "social_linked" | "social_unlinked" | "account_locked" | "email_verified" | "phone_verified" | "session_revoked" | "kyc_status_changed" | "kyc_documents_requested" | "limit_changed" | "self_excluded" | "reality_check";
112
+ readonly data?: Readonly<Record<string, string | number | boolean | null>>;
113
+ };
114
+ }
115
+ /**
116
+ * An event published by a plugin on its `ext.<pluginKey>` channel. The standard
117
+ * realtime envelope applies (incl. `eventId` dedupe); `data.type` is the
118
+ * plugin-defined event type (e.g. `"cashback.claimed"`) and `data.payload` is
119
+ * opaque to the SDK.
120
+ */
121
+ interface ExtPluginEvent extends RealtimeEventMeta {
122
+ readonly channel: ExtChannel;
123
+ readonly data: {
124
+ readonly type: string;
125
+ readonly payload: unknown;
126
+ };
127
+ }
128
+ /** Maps a channel name to its event payload type, for type-safe `on(...)`. */
129
+ interface ChannelEventMap {
130
+ "wallet.balance": WalletBalanceEvent;
131
+ "wallet.deposit": WalletDepositEvent;
132
+ "wallet.withdrawal": WalletWithdrawalEvent;
133
+ gaming: GamingEvent;
134
+ bonus: BonusEvent;
135
+ player: PlayerEvent;
136
+ [channel: `ext.${string}`]: ExtPluginEvent;
137
+ }
138
+ type ConnectionState = "idle" | "connecting" | "ready" | "reconnecting" | "closed";
139
+ interface RealtimeError {
140
+ readonly code: string;
141
+ readonly message: string;
142
+ readonly details?: Record<string, unknown>;
143
+ }
144
+ interface RealtimeClientOptions {
145
+ /** wss URL of the gateway, e.g. `wss://grandbet.example/realtime`. */
146
+ readonly url: string;
147
+ /**
148
+ * How the SDK obtains a handshake credential. Return `{ ticket }` (fetched from
149
+ * `POST /realtime/ticket`) or nothing to rely on a same-site HttpOnly cookie.
150
+ * Called again on every reconnect so an expiring credential never drops the socket.
151
+ */
152
+ readonly getAuthCredential?: () => Promise<{
153
+ ticket: string;
154
+ } | void>;
155
+ /**
156
+ * Snapshot read run after every (re)connect to reconcile missed state. The socket
157
+ * accelerates; REST is the truth. The SDK calls this; the app supplies the reads.
158
+ */
159
+ readonly resync?: (ctx: {
160
+ since?: string;
161
+ }) => Promise<void>;
162
+ /** Reconnect backoff. Defaults: base 500ms, factor 2, max 15s, full jitter. */
163
+ readonly backoff?: {
164
+ readonly baseMs?: number;
165
+ readonly maxMs?: number;
166
+ readonly factor?: number;
167
+ };
168
+ /** Override only for testing; the server dictates heartbeat in the `ready` frame. */
169
+ readonly heartbeatMs?: number;
170
+ /** Inject a WebSocket implementation (Node `ws`); defaults to the platform global. */
171
+ readonly WebSocketImpl?: WebSocketImpl;
172
+ /** Max remembered eventIds for dedupe. Default 512. */
173
+ readonly dedupeWindow?: number;
174
+ }
175
+ type Unsubscribe = () => void;
176
+ interface RealtimeClient {
177
+ readonly state: ConnectionState;
178
+ connect(): Promise<void>;
179
+ disconnect(): Promise<void>;
180
+ on<C extends RealtimeChannel>(channel: C, handler: (event: ChannelEventMap[C]) => void): Unsubscribe;
181
+ subscribe(channels: readonly RealtimeChannel[]): Promise<void>;
182
+ unsubscribe(channels: readonly RealtimeChannel[]): Promise<void>;
183
+ withSubscription<T>(channels: readonly RealtimeChannel[], scope: () => Promise<T>): Promise<T>;
184
+ activeChannels(): readonly RealtimeChannel[];
185
+ onStateChange(handler: (state: ConnectionState) => void): Unsubscribe;
186
+ onError(handler: (error: RealtimeError) => void): Unsubscribe;
187
+ }
188
+ type CreateRealtimeClient = (options: RealtimeClientOptions) => RealtimeClient;
189
+ interface WebSocketLike {
190
+ send(data: string): void;
191
+ close(code?: number, reason?: string): void;
192
+ readonly readyState: number;
193
+ onopen: ((ev: unknown) => void) | null;
194
+ onclose: ((ev: {
195
+ code?: number;
196
+ reason?: string;
197
+ }) => void) | null;
198
+ onerror: ((ev: unknown) => void) | null;
199
+ onmessage: ((ev: {
200
+ data: unknown;
201
+ }) => void) | null;
202
+ }
203
+ type WebSocketImpl = new (url: string) => WebSocketLike;
204
+
205
+ export { type BonusEvent as B, type ChannelEventMap as C, type DepositStatus as D, type ExtChannel as E, type GamingEvent as G, type MinorUnits as M, type PlayerEvent as P, REALTIME_CHANNELS as R, type Unsubscribe as U, type WalletBalanceEvent as W, type ConnectionState as a, type CoreRealtimeChannel as b, type ExtPluginEvent as c, type RealtimeChannel as d, type RealtimeError as e, type WalletBucket as f, type WalletDepositEvent as g, type WalletWithdrawalEvent as h, isExtChannel as i, type WebSocketImpl as j, type RealtimeClientOptions as k, type RealtimeClient as l, type CreateRealtimeClient as m, type RealtimeEventMeta as n, type WebSocketLike as o, type WithdrawalStatus as p };