@stakeplate/core 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +71 -0
- package/dist/MockNetworkManager-cj8WT5v0.js +126 -0
- package/dist/MockNetworkManager-cj8WT5v0.js.map +1 -0
- package/dist/audio.d.ts +2 -0
- package/dist/audio.js +112 -0
- package/dist/audio.js.map +1 -0
- package/dist/bind-BBRxizWR.js +43 -0
- package/dist/bind-BBRxizWR.js.map +1 -0
- package/dist/hud-port-DNfotn4U.d.ts +49 -0
- package/dist/index-BBlN97RY.d.ts +58 -0
- package/dist/index-BcTRfis9.d.ts +276 -0
- package/dist/index.d.ts +96 -0
- package/dist/index.js +561 -0
- package/dist/index.js.map +1 -0
- package/dist/network-pBW8CX1o.d.ts +120 -0
- package/dist/protocol-C1KamF3t.d.ts +112 -0
- package/dist/rgs.d.ts +25 -0
- package/dist/rgs.js +119 -0
- package/dist/rgs.js.map +1 -0
- package/dist/rules.d.ts +68 -0
- package/dist/rules.js +250 -0
- package/dist/rules.js.map +1 -0
- package/dist/stores.d.ts +2 -0
- package/dist/stores.js +93 -0
- package/dist/stores.js.map +1 -0
- package/dist/testing.d.ts +18 -0
- package/dist/testing.js +38 -0
- package/dist/testing.js.map +1 -0
- package/dist/ticker-A-XgayZv.js +26 -0
- package/dist/ticker-A-XgayZv.js.map +1 -0
- package/package.json +118 -0
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { c as JurisdictionConfig, d as ReplayParams, m as Round, r as AuthenticateResponse, s as EndRoundResponse, u as PlayResponse } from "./protocol-C1KamF3t.js";
|
|
2
|
+
//#region src/rgs/runtime.d.ts
|
|
3
|
+
/**
|
|
4
|
+
* Stake launch parameters (mirrors `StakeEngine/web-sdk`
|
|
5
|
+
* `packages/state-shared/src/stateUrl`): sessionID, rgs_url, lang, currency, device,
|
|
6
|
+
* social, demo — plus the REPLAY set: replay, amount, game, version, mode, event.
|
|
7
|
+
* `lang` is REQUIRED by `/wallet/authenticate` (its absence 400s the RGS).
|
|
8
|
+
*/
|
|
9
|
+
interface ReplayLaunch {
|
|
10
|
+
/** `?replay=true` — launch into replay (fetch + play back a recorded round). */
|
|
11
|
+
active: boolean;
|
|
12
|
+
amount: number;
|
|
13
|
+
game: string;
|
|
14
|
+
version: string;
|
|
15
|
+
mode: string;
|
|
16
|
+
event: string;
|
|
17
|
+
}
|
|
18
|
+
interface RuntimeConfig {
|
|
19
|
+
rgsUrl: string;
|
|
20
|
+
sessionId: string;
|
|
21
|
+
/** BCP-47-ish language for `/wallet/authenticate` + the HUD locale ('br' → 'pt'). */
|
|
22
|
+
language: string;
|
|
23
|
+
/** `?currency=` — currency code (used for replay, where there's no wallet to read it). */
|
|
24
|
+
currency: string;
|
|
25
|
+
/** `?social=true` — force Stake US social/sweepstakes wording. */
|
|
26
|
+
social: boolean;
|
|
27
|
+
/** `?device=` — Stake's viewport hint ('' | 'desktop' | 'mobile' | a small popout). */
|
|
28
|
+
device: string;
|
|
29
|
+
/** `?demo=true` — run against the built-in mock RGS, no real backend. */
|
|
30
|
+
demo: boolean;
|
|
31
|
+
replay: ReplayLaunch;
|
|
32
|
+
}
|
|
33
|
+
type ParamSource = (name: string) => string | undefined;
|
|
34
|
+
/** Read a URL param from `window.location.search` (undefined off-DOM). */
|
|
35
|
+
declare function urlParam(name: string): string | undefined;
|
|
36
|
+
/**
|
|
37
|
+
* Parse the Stake launch params into a {@link RuntimeConfig}. Pass `overrides` (or a
|
|
38
|
+
* custom `param` reader) for tests/embedding; otherwise reads the page URL. `rgsUrl`
|
|
39
|
+
* defaults to a local emulator so `pnpm dev` works out of the box.
|
|
40
|
+
*/
|
|
41
|
+
declare function readRuntime(opts?: {
|
|
42
|
+
param?: ParamSource;
|
|
43
|
+
overrides?: Partial<RuntimeConfig>;
|
|
44
|
+
defaultRgsUrl?: string;
|
|
45
|
+
}): RuntimeConfig;
|
|
46
|
+
//#endregion
|
|
47
|
+
//#region src/rgs/MockNetworkManager.d.ts
|
|
48
|
+
interface MockOptions {
|
|
49
|
+
/** Starting balance in MAJOR units (default 1000). */
|
|
50
|
+
balance?: number;
|
|
51
|
+
currency?: string;
|
|
52
|
+
betLevels?: number[];
|
|
53
|
+
defaultBet?: number;
|
|
54
|
+
rtp?: number;
|
|
55
|
+
jurisdiction?: JurisdictionConfig;
|
|
56
|
+
/** Mode key → cost multiplier, so the mock charges bet × cost like the platform. */
|
|
57
|
+
modes?: Record<string, number>;
|
|
58
|
+
/** An incomplete round present at authenticate — for testing mid-spin resume. */
|
|
59
|
+
activeRound?: Round | null;
|
|
60
|
+
}
|
|
61
|
+
/** A scripted round. `payoutMultiplier` is BOOK units (×100); `win` (major) is a shortcut. */
|
|
62
|
+
interface ScriptedRound {
|
|
63
|
+
mode?: string;
|
|
64
|
+
/** BOOK units (100 = 1×). Provide this OR `win`. */
|
|
65
|
+
payoutMultiplier?: number;
|
|
66
|
+
/** Convenience: the win in MAJOR units for the given bet (sets payoutMultiplier). */
|
|
67
|
+
win?: number;
|
|
68
|
+
/** The stake charged, MAJOR units (default = the bet passed to play). */
|
|
69
|
+
stake?: number;
|
|
70
|
+
/** The book events the game's `interpretBook` will parse. */
|
|
71
|
+
events?: unknown[];
|
|
72
|
+
/** Leave the round open (needs `end-round`) — for exercising the settle path. */
|
|
73
|
+
active?: boolean;
|
|
74
|
+
}
|
|
75
|
+
declare class MockNetworkManager implements NetworkManager {
|
|
76
|
+
private balanceApi;
|
|
77
|
+
private currency;
|
|
78
|
+
private readonly config;
|
|
79
|
+
private active;
|
|
80
|
+
private readonly modes;
|
|
81
|
+
private readonly queue;
|
|
82
|
+
/** The last round returned but not yet end-round'd (its win is applied on settle). */
|
|
83
|
+
private pendingWinApi;
|
|
84
|
+
constructor(opts?: MockOptions);
|
|
85
|
+
/** Queue the next round `play()` will return (call repeatedly to queue several). */
|
|
86
|
+
forceRound(round: ScriptedRound): this;
|
|
87
|
+
/** Set the current balance (MAJOR units). */
|
|
88
|
+
setBalance(major: number): this;
|
|
89
|
+
private balance;
|
|
90
|
+
authenticate(): Promise<AuthenticateResponse>;
|
|
91
|
+
play(args: PlayArgs): Promise<PlayResponse>;
|
|
92
|
+
endRound(): Promise<EndRoundResponse>;
|
|
93
|
+
replay(_p: ReplayParams): Promise<Round>;
|
|
94
|
+
}
|
|
95
|
+
//#endregion
|
|
96
|
+
//#region src/rgs/network.d.ts
|
|
97
|
+
interface PlayArgs {
|
|
98
|
+
/** Base bet in MAJOR units (the transport converts to API units). */
|
|
99
|
+
bet: number;
|
|
100
|
+
/** Mode key — base / a boost / a bonus buy. The platform applies its cost. */
|
|
101
|
+
mode: string;
|
|
102
|
+
/** Currency code; defaults to the one learned at authenticate. */
|
|
103
|
+
currency?: string;
|
|
104
|
+
}
|
|
105
|
+
interface NetworkManager {
|
|
106
|
+
authenticate(): Promise<AuthenticateResponse>;
|
|
107
|
+
play(args: PlayArgs): Promise<PlayResponse>;
|
|
108
|
+
endRound(): Promise<EndRoundResponse>;
|
|
109
|
+
/** Optional — fetch a recorded round for read-only replay. */
|
|
110
|
+
replay?(params: ReplayParams): Promise<Round>;
|
|
111
|
+
dispose?(): void;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Pick the transport from the runtime: `?demo=true` (or a supplied `mock`) → the
|
|
115
|
+
* in-process mock RGS; otherwise the real Stake transport.
|
|
116
|
+
*/
|
|
117
|
+
declare function createNetwork(runtime: RuntimeConfig, mock?: MockNetworkManager): NetworkManager;
|
|
118
|
+
//#endregion
|
|
119
|
+
export { MockOptions as a, RuntimeConfig as c, MockNetworkManager as i, readRuntime as l, PlayArgs as n, ScriptedRound as o, createNetwork as r, ReplayLaunch as s, NetworkManager as t, urlParam as u };
|
|
120
|
+
//# sourceMappingURL=network-pBW8CX1o.d.ts.map
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
//#region src/rgs/protocol.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Stake Engine wire protocol — the GAME-AGNOSTIC shapes every Stake RGS speaks.
|
|
4
|
+
* (A game's own book/event types live in the game and are parsed by its
|
|
5
|
+
* `interpretBook`; here a round's events are opaque `unknown[]`.)
|
|
6
|
+
*
|
|
7
|
+
* Two money scales (Stake convention):
|
|
8
|
+
* - API units : value × 1_000_000 (six implied decimals). Every RGS endpoint.
|
|
9
|
+
* - BOOK units: value × 100. A book's `payoutMultiplier` + its event amounts.
|
|
10
|
+
* So: win_api = round(betAmount_api × payoutMultiplier_book / 100).
|
|
11
|
+
*
|
|
12
|
+
* @see https://github.com/StakeEngine/web-sdk — packages/rgs-requests, rgs-fetcher/schema
|
|
13
|
+
*/
|
|
14
|
+
declare const API_AMOUNT_MULTIPLIER = 1000000;
|
|
15
|
+
declare const BOOK_AMOUNT_MULTIPLIER = 100;
|
|
16
|
+
interface Balance {
|
|
17
|
+
amount: number;
|
|
18
|
+
currency: string;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Stake Engine's per-player jurisdiction config, delivered at `/wallet/authenticate`
|
|
22
|
+
* as `config.jurisdiction`. Structurally compatible with `@open-slot-ui`'s
|
|
23
|
+
* `JurisdictionConfig`, so it feeds straight into `hud.applyJurisdiction`.
|
|
24
|
+
*/
|
|
25
|
+
interface JurisdictionConfig {
|
|
26
|
+
socialCasino?: boolean;
|
|
27
|
+
disabledFullscreen?: boolean;
|
|
28
|
+
disabledTurbo?: boolean;
|
|
29
|
+
disabledSuperTurbo?: boolean;
|
|
30
|
+
disabledAutoplay?: boolean;
|
|
31
|
+
disabledSlamstop?: boolean;
|
|
32
|
+
disabledSpacebar?: boolean;
|
|
33
|
+
disabledBuyFeature?: boolean;
|
|
34
|
+
displayNetPosition?: boolean;
|
|
35
|
+
displayRTP?: boolean;
|
|
36
|
+
displaySessionTimer?: boolean;
|
|
37
|
+
/** Minimum ms a round must take — enforced by the core, not the platform. */
|
|
38
|
+
minimumRoundDuration?: number;
|
|
39
|
+
/** Buy-feature confirm threshold (× base bet) — fed to the HUD's confirm gate. */
|
|
40
|
+
confirmBuyAboveCost?: number;
|
|
41
|
+
}
|
|
42
|
+
interface RgsConfig {
|
|
43
|
+
minBet: number;
|
|
44
|
+
maxBet: number;
|
|
45
|
+
stepBet: number;
|
|
46
|
+
betLevels: number[];
|
|
47
|
+
defaultBetLevel: number;
|
|
48
|
+
/** Theoretical RTP percentage (e.g. 96.0) for the RTP readout. */
|
|
49
|
+
rtp?: number;
|
|
50
|
+
jurisdiction?: JurisdictionConfig;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* One RGS round. `E` is the game's book-event type (opaque to the core — the game's
|
|
54
|
+
* `interpretBook` narrows it). The real Stake RGS returns the event list under
|
|
55
|
+
* `state`; some emulators use `events`; read via {@link roundEvents}.
|
|
56
|
+
*/
|
|
57
|
+
interface Round<E = unknown> {
|
|
58
|
+
betID: string | number;
|
|
59
|
+
mode: string;
|
|
60
|
+
amount: number;
|
|
61
|
+
payout?: number;
|
|
62
|
+
payoutMultiplier: number;
|
|
63
|
+
state?: E[];
|
|
64
|
+
events?: E[];
|
|
65
|
+
/** True while a (winning) round still awaits `/wallet/end-round`. */
|
|
66
|
+
active?: boolean;
|
|
67
|
+
}
|
|
68
|
+
/** The book events for a round, whichever field the RGS used (`state` or `events`). */
|
|
69
|
+
declare function roundEvents<E>(round: Round<E>): E[];
|
|
70
|
+
interface AuthenticateRequest {
|
|
71
|
+
sessionID: string;
|
|
72
|
+
/** REQUIRED by the real RGS — omitting it 400s `ERR_VAL "could not parse request json"`. */
|
|
73
|
+
language: string;
|
|
74
|
+
}
|
|
75
|
+
interface AuthenticateResponse {
|
|
76
|
+
balance: Balance;
|
|
77
|
+
config: RgsConfig;
|
|
78
|
+
round: Round | null;
|
|
79
|
+
}
|
|
80
|
+
interface PlayRequest {
|
|
81
|
+
sessionID: string;
|
|
82
|
+
amount: number;
|
|
83
|
+
mode: string;
|
|
84
|
+
currency: string;
|
|
85
|
+
}
|
|
86
|
+
interface PlayResponse {
|
|
87
|
+
round: Round;
|
|
88
|
+
balance: Balance;
|
|
89
|
+
}
|
|
90
|
+
interface EndRoundRequest {
|
|
91
|
+
sessionID: string;
|
|
92
|
+
}
|
|
93
|
+
interface EndRoundResponse {
|
|
94
|
+
balance: Balance;
|
|
95
|
+
}
|
|
96
|
+
/** `GET /bet/replay/{game}/{version}/{mode}/{event}` params (from `?replay=…` launch). */
|
|
97
|
+
interface ReplayParams {
|
|
98
|
+
game: string;
|
|
99
|
+
version: string;
|
|
100
|
+
mode: string;
|
|
101
|
+
event: string;
|
|
102
|
+
/** The bet amount for the replayed round (API units are NOT used here; major units). */
|
|
103
|
+
amount: number;
|
|
104
|
+
}
|
|
105
|
+
/** RGS error body (`{ error: 'ERR_VAL', message }`) — codes map to `hud.showRgsError`. */
|
|
106
|
+
interface RgsError {
|
|
107
|
+
error: string;
|
|
108
|
+
message?: string;
|
|
109
|
+
}
|
|
110
|
+
//#endregion
|
|
111
|
+
export { Balance as a, JurisdictionConfig as c, ReplayParams as d, RgsConfig as f, roundEvents as h, BOOK_AMOUNT_MULTIPLIER as i, PlayRequest as l, Round as m, AuthenticateRequest as n, EndRoundRequest as o, RgsError as p, AuthenticateResponse as r, EndRoundResponse as s, API_AMOUNT_MULTIPLIER as t, PlayResponse as u };
|
|
112
|
+
//# sourceMappingURL=protocol-C1KamF3t.d.ts.map
|
package/dist/rgs.d.ts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { a as Balance, c as JurisdictionConfig, d as ReplayParams, f as RgsConfig, h as roundEvents, i as BOOK_AMOUNT_MULTIPLIER, l as PlayRequest, m as Round, n as AuthenticateRequest, o as EndRoundRequest, p as RgsError, r as AuthenticateResponse, s as EndRoundResponse, t as API_AMOUNT_MULTIPLIER, u as PlayResponse } from "./protocol-C1KamF3t.js";
|
|
2
|
+
import { a as MockOptions, c as RuntimeConfig, i as MockNetworkManager, l as readRuntime, n as PlayArgs, o as ScriptedRound, r as createNetwork, s as ReplayLaunch, t as NetworkManager, u as urlParam } from "./network-pBW8CX1o.js";
|
|
3
|
+
//#region src/rgs/StakeNetworkManager.d.ts
|
|
4
|
+
interface StakeNetworkOptions {
|
|
5
|
+
rgsUrl: string;
|
|
6
|
+
sessionId: string;
|
|
7
|
+
/** REQUIRED — `/wallet/authenticate` 400s without it. */
|
|
8
|
+
language: string;
|
|
9
|
+
}
|
|
10
|
+
declare class StakeNetworkManager implements NetworkManager {
|
|
11
|
+
private readonly opts;
|
|
12
|
+
private readonly base;
|
|
13
|
+
/** Currency learned at authenticate; sent on every `/wallet/play`. */
|
|
14
|
+
private currency;
|
|
15
|
+
constructor(opts: StakeNetworkOptions);
|
|
16
|
+
authenticate(): Promise<AuthenticateResponse>;
|
|
17
|
+
play(args: PlayArgs): Promise<PlayResponse>;
|
|
18
|
+
endRound(): Promise<EndRoundResponse>;
|
|
19
|
+
replay(p: ReplayParams): Promise<Round>;
|
|
20
|
+
private post;
|
|
21
|
+
private get;
|
|
22
|
+
}
|
|
23
|
+
//#endregion
|
|
24
|
+
export { API_AMOUNT_MULTIPLIER, AuthenticateRequest, AuthenticateResponse, BOOK_AMOUNT_MULTIPLIER, Balance, EndRoundRequest, EndRoundResponse, JurisdictionConfig, MockNetworkManager, MockOptions, NetworkManager, PlayArgs, PlayRequest, PlayResponse, ReplayLaunch, ReplayParams, RgsConfig, RgsError, Round, RuntimeConfig, ScriptedRound, StakeNetworkManager, StakeNetworkOptions, createNetwork, readRuntime, roundEvents, urlParam };
|
|
25
|
+
//# sourceMappingURL=rgs.d.ts.map
|
package/dist/rgs.js
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { i as roundEvents, n as API_AMOUNT_MULTIPLIER, r as BOOK_AMOUNT_MULTIPLIER, t as MockNetworkManager } from "./MockNetworkManager-cj8WT5v0.js";
|
|
2
|
+
//#region src/rgs/runtime.ts
|
|
3
|
+
/** Read a URL param from `window.location.search` (undefined off-DOM). */
|
|
4
|
+
function urlParam(name) {
|
|
5
|
+
if (typeof window === "undefined") return void 0;
|
|
6
|
+
return new URLSearchParams(window.location.search).get(name) ?? void 0;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Parse the Stake launch params into a {@link RuntimeConfig}. Pass `overrides` (or a
|
|
10
|
+
* custom `param` reader) for tests/embedding; otherwise reads the page URL. `rgsUrl`
|
|
11
|
+
* defaults to a local emulator so `pnpm dev` works out of the box.
|
|
12
|
+
*/
|
|
13
|
+
function readRuntime(opts = {}) {
|
|
14
|
+
const param = opts.param ?? urlParam;
|
|
15
|
+
const rawLang = param("lang");
|
|
16
|
+
const language = rawLang === "br" ? "pt" : rawLang || "en";
|
|
17
|
+
const base = {
|
|
18
|
+
rgsUrl: param("rgs_url") ?? param("rgsUrl") ?? opts.defaultRgsUrl ?? "http://localhost:4758",
|
|
19
|
+
sessionId: param("sessionID") ?? param("sessionId") ?? "dev",
|
|
20
|
+
language,
|
|
21
|
+
currency: param("currency") ?? "USD",
|
|
22
|
+
social: param("social") === "true",
|
|
23
|
+
device: param("device") ?? "",
|
|
24
|
+
demo: param("demo") === "true",
|
|
25
|
+
replay: {
|
|
26
|
+
active: param("replay") === "true",
|
|
27
|
+
amount: Number(param("amount")) || 0,
|
|
28
|
+
game: param("game") ?? "",
|
|
29
|
+
version: param("version") ?? "",
|
|
30
|
+
mode: param("mode") ?? "",
|
|
31
|
+
event: param("event") ?? ""
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
return {
|
|
35
|
+
...base,
|
|
36
|
+
...opts.overrides,
|
|
37
|
+
replay: {
|
|
38
|
+
...base.replay,
|
|
39
|
+
...opts.overrides?.replay
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
//#endregion
|
|
44
|
+
//#region src/rgs/StakeNetworkManager.ts
|
|
45
|
+
var StakeNetworkManager = class {
|
|
46
|
+
opts;
|
|
47
|
+
base;
|
|
48
|
+
/** Currency learned at authenticate; sent on every `/wallet/play`. */
|
|
49
|
+
currency = "USD";
|
|
50
|
+
constructor(opts) {
|
|
51
|
+
this.opts = opts;
|
|
52
|
+
this.base = /^https?:\/\//.test(opts.rgsUrl) ? opts.rgsUrl : `https://${opts.rgsUrl}`;
|
|
53
|
+
}
|
|
54
|
+
async authenticate() {
|
|
55
|
+
const res = await this.post("/wallet/authenticate", {
|
|
56
|
+
sessionID: this.opts.sessionId,
|
|
57
|
+
language: this.opts.language
|
|
58
|
+
});
|
|
59
|
+
this.currency = res.balance.currency;
|
|
60
|
+
return res;
|
|
61
|
+
}
|
|
62
|
+
async play(args) {
|
|
63
|
+
return this.post("/wallet/play", {
|
|
64
|
+
sessionID: this.opts.sessionId,
|
|
65
|
+
currency: args.currency ?? this.currency,
|
|
66
|
+
amount: Math.round(args.bet * API_AMOUNT_MULTIPLIER),
|
|
67
|
+
mode: args.mode
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
async endRound() {
|
|
71
|
+
return this.post("/wallet/end-round", { sessionID: this.opts.sessionId });
|
|
72
|
+
}
|
|
73
|
+
async replay(p) {
|
|
74
|
+
const path = `/bet/replay/${encodeURIComponent(p.game)}/${encodeURIComponent(p.version)}/${encodeURIComponent(p.mode)}/${encodeURIComponent(p.event)}`;
|
|
75
|
+
const res = await this.get(path);
|
|
76
|
+
return res.round ?? res;
|
|
77
|
+
}
|
|
78
|
+
async post(path, body) {
|
|
79
|
+
const res = await fetch(this.base + path, {
|
|
80
|
+
method: "POST",
|
|
81
|
+
headers: { "content-type": "application/json" },
|
|
82
|
+
body: JSON.stringify(body)
|
|
83
|
+
});
|
|
84
|
+
if (!res.ok) {
|
|
85
|
+
const text = await res.text().catch(() => "");
|
|
86
|
+
throw new Error(`[StakeNetworkManager] ${path} -> ${res.status} ${text}`);
|
|
87
|
+
}
|
|
88
|
+
return await res.json();
|
|
89
|
+
}
|
|
90
|
+
async get(path) {
|
|
91
|
+
const res = await fetch(this.base + path, {
|
|
92
|
+
method: "GET",
|
|
93
|
+
headers: { accept: "application/json" }
|
|
94
|
+
});
|
|
95
|
+
if (!res.ok) {
|
|
96
|
+
const text = await res.text().catch(() => "");
|
|
97
|
+
throw new Error(`[StakeNetworkManager] ${path} -> ${res.status} ${text}`);
|
|
98
|
+
}
|
|
99
|
+
return await res.json();
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
//#endregion
|
|
103
|
+
//#region src/rgs/network.ts
|
|
104
|
+
/**
|
|
105
|
+
* Pick the transport from the runtime: `?demo=true` (or a supplied `mock`) → the
|
|
106
|
+
* in-process mock RGS; otherwise the real Stake transport.
|
|
107
|
+
*/
|
|
108
|
+
function createNetwork(runtime, mock) {
|
|
109
|
+
if (runtime.demo || mock) return mock ?? new MockNetworkManager();
|
|
110
|
+
return new StakeNetworkManager({
|
|
111
|
+
rgsUrl: runtime.rgsUrl,
|
|
112
|
+
sessionId: runtime.sessionId,
|
|
113
|
+
language: runtime.language
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
//#endregion
|
|
117
|
+
export { API_AMOUNT_MULTIPLIER, BOOK_AMOUNT_MULTIPLIER, MockNetworkManager, StakeNetworkManager, createNetwork, readRuntime, roundEvents, urlParam };
|
|
118
|
+
|
|
119
|
+
//# sourceMappingURL=rgs.js.map
|
package/dist/rgs.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rgs.js","names":[],"sources":["../src/rgs/runtime.ts","../src/rgs/StakeNetworkManager.ts","../src/rgs/network.ts"],"sourcesContent":["/**\n * Stake launch parameters (mirrors `StakeEngine/web-sdk`\n * `packages/state-shared/src/stateUrl`): sessionID, rgs_url, lang, currency, device,\n * social, demo — plus the REPLAY set: replay, amount, game, version, mode, event.\n * `lang` is REQUIRED by `/wallet/authenticate` (its absence 400s the RGS).\n */\n\nexport interface ReplayLaunch {\n /** `?replay=true` — launch into replay (fetch + play back a recorded round). */\n active: boolean;\n amount: number;\n game: string;\n version: string;\n mode: string;\n event: string;\n}\n\nexport interface RuntimeConfig {\n rgsUrl: string;\n sessionId: string;\n /** BCP-47-ish language for `/wallet/authenticate` + the HUD locale ('br' → 'pt'). */\n language: string;\n /** `?currency=` — currency code (used for replay, where there's no wallet to read it). */\n currency: string;\n /** `?social=true` — force Stake US social/sweepstakes wording. */\n social: boolean;\n /** `?device=` — Stake's viewport hint ('' | 'desktop' | 'mobile' | a small popout). */\n device: string;\n /** `?demo=true` — run against the built-in mock RGS, no real backend. */\n demo: boolean;\n replay: ReplayLaunch;\n}\n\ntype ParamSource = (name: string) => string | undefined;\n\n/** Read a URL param from `window.location.search` (undefined off-DOM). */\nexport function urlParam(name: string): string | undefined {\n if (typeof window === 'undefined') return undefined;\n return new URLSearchParams(window.location.search).get(name) ?? undefined;\n}\n\n/**\n * Parse the Stake launch params into a {@link RuntimeConfig}. Pass `overrides` (or a\n * custom `param` reader) for tests/embedding; otherwise reads the page URL. `rgsUrl`\n * defaults to a local emulator so `pnpm dev` works out of the box.\n */\nexport function readRuntime(opts: { param?: ParamSource; overrides?: Partial<RuntimeConfig>; defaultRgsUrl?: string } = {}): RuntimeConfig {\n const param = opts.param ?? urlParam;\n const rawLang = param('lang');\n const language = rawLang === 'br' ? 'pt' : rawLang || 'en';\n const base: RuntimeConfig = {\n rgsUrl: param('rgs_url') ?? param('rgsUrl') ?? opts.defaultRgsUrl ?? 'http://localhost:4758',\n sessionId: param('sessionID') ?? param('sessionId') ?? 'dev',\n language,\n currency: param('currency') ?? 'USD',\n social: param('social') === 'true',\n device: param('device') ?? '',\n demo: param('demo') === 'true',\n replay: {\n active: param('replay') === 'true',\n amount: Number(param('amount')) || 0,\n game: param('game') ?? '',\n version: param('version') ?? '',\n mode: param('mode') ?? '',\n event: param('event') ?? '',\n },\n };\n return { ...base, ...opts.overrides, replay: { ...base.replay, ...opts.overrides?.replay } };\n}\n","// StakeNetworkManager — the ONLY thing that speaks the Stake RGS wire. Thin: it\n// returns RAW protocol shapes; the engine (SpinPhase/boot) owns the end-round settle\n// + active-round resume. Money: RGS = API units (×1e6); the game's book = BOOK units.\n\nimport {\n API_AMOUNT_MULTIPLIER,\n type AuthenticateResponse,\n type EndRoundResponse,\n type PlayResponse,\n type ReplayParams,\n type Round,\n} from './protocol';\nimport type { NetworkManager, PlayArgs } from './network';\n\nexport interface StakeNetworkOptions {\n rgsUrl: string;\n sessionId: string;\n /** REQUIRED — `/wallet/authenticate` 400s without it. */\n language: string;\n}\n\nexport class StakeNetworkManager implements NetworkManager {\n private readonly base: string;\n /** Currency learned at authenticate; sent on every `/wallet/play`. */\n private currency = 'USD';\n\n constructor(private readonly opts: StakeNetworkOptions) {\n this.base = /^https?:\\/\\//.test(opts.rgsUrl) ? opts.rgsUrl : `https://${opts.rgsUrl}`;\n }\n\n async authenticate(): Promise<AuthenticateResponse> {\n const res = await this.post<AuthenticateResponse>('/wallet/authenticate', {\n sessionID: this.opts.sessionId,\n language: this.opts.language,\n });\n this.currency = res.balance.currency;\n return res;\n }\n\n async play(args: PlayArgs): Promise<PlayResponse> {\n return this.post<PlayResponse>('/wallet/play', {\n sessionID: this.opts.sessionId,\n currency: args.currency ?? this.currency,\n amount: Math.round(args.bet * API_AMOUNT_MULTIPLIER),\n mode: args.mode,\n });\n }\n\n async endRound(): Promise<EndRoundResponse> {\n return this.post<EndRoundResponse>('/wallet/end-round', { sessionID: this.opts.sessionId });\n }\n\n async replay(p: ReplayParams): Promise<Round> {\n const path = `/bet/replay/${encodeURIComponent(p.game)}/${encodeURIComponent(p.version)}/${encodeURIComponent(p.mode)}/${encodeURIComponent(p.event)}`;\n const res = await this.get<Record<string, unknown>>(path);\n // The RGS may return `{ round }` or the round object directly.\n return (res.round ?? res) as Round;\n }\n\n private async post<T>(path: string, body: unknown): Promise<T> {\n const res = await fetch(this.base + path, {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify(body),\n });\n if (!res.ok) {\n const text = await res.text().catch(() => '');\n throw new Error(`[StakeNetworkManager] ${path} -> ${res.status} ${text}`);\n }\n return (await res.json()) as T;\n }\n\n private async get<T>(path: string): Promise<T> {\n const res = await fetch(this.base + path, { method: 'GET', headers: { accept: 'application/json' } });\n if (!res.ok) {\n const text = await res.text().catch(() => '');\n throw new Error(`[StakeNetworkManager] ${path} -> ${res.status} ${text}`);\n }\n return (await res.json()) as T;\n }\n}\n","// The transport seam. Adapters return RAW protocol shapes; the engine applies the\n// game's `interpretBook` and owns the round lifecycle (settle / resume).\n\nimport type { AuthenticateResponse, EndRoundResponse, PlayResponse, ReplayParams, Round } from './protocol';\nimport type { RuntimeConfig } from './runtime';\nimport { StakeNetworkManager } from './StakeNetworkManager';\nimport { MockNetworkManager } from './MockNetworkManager';\n\nexport interface PlayArgs {\n /** Base bet in MAJOR units (the transport converts to API units). */\n bet: number;\n /** Mode key — base / a boost / a bonus buy. The platform applies its cost. */\n mode: string;\n /** Currency code; defaults to the one learned at authenticate. */\n currency?: string;\n}\n\nexport interface NetworkManager {\n authenticate(): Promise<AuthenticateResponse>;\n play(args: PlayArgs): Promise<PlayResponse>;\n endRound(): Promise<EndRoundResponse>;\n /** Optional — fetch a recorded round for read-only replay. */\n replay?(params: ReplayParams): Promise<Round>;\n dispose?(): void;\n}\n\n/**\n * Pick the transport from the runtime: `?demo=true` (or a supplied `mock`) → the\n * in-process mock RGS; otherwise the real Stake transport.\n */\nexport function createNetwork(runtime: RuntimeConfig, mock?: MockNetworkManager): NetworkManager {\n if (runtime.demo || mock) return mock ?? new MockNetworkManager();\n return new StakeNetworkManager({\n rgsUrl: runtime.rgsUrl,\n sessionId: runtime.sessionId,\n language: runtime.language,\n });\n}\n"],"mappings":"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"}
|
package/dist/rules.d.ts
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { BlockSpec, MenuSpec } from "@open-slot-ui/core";
|
|
2
|
+
//#region src/rules/social.d.ts
|
|
3
|
+
/** Restricted → replacement, ORDERED longest-phrase-first (so multi-word phrases win). */
|
|
4
|
+
declare const SOCIAL_REPLACEMENTS: ReadonlyArray<readonly [string, string]>;
|
|
5
|
+
/**
|
|
6
|
+
* Rewrite an English string into its social/sweepstakes wording — restricted phrases swapped
|
|
7
|
+
* for their alternatives (word-boundary safe, longest-phrase-first, case-preserving). Idempotent
|
|
8
|
+
* enough for authoring: run it over your menu/feature copy to auto-generate social variants.
|
|
9
|
+
*/
|
|
10
|
+
declare function toSocial(text: string): string;
|
|
11
|
+
/**
|
|
12
|
+
* AGGRESSIVELY find restricted phrases in an English string — matches even as part of a word
|
|
13
|
+
* (so `paytable`, `betting`, `payout` are all flagged). Returns the offending terms. Use it at
|
|
14
|
+
* launch/build to fail on any social-copy that would still surface gambling wording.
|
|
15
|
+
*/
|
|
16
|
+
declare function findRestricted(text: string): string[];
|
|
17
|
+
//#endregion
|
|
18
|
+
//#region src/rules/index.d.ts
|
|
19
|
+
/** RTP / volatility / max-win etc. — shown as a stat grid. Values are display strings. */
|
|
20
|
+
interface RulesStats {
|
|
21
|
+
rtp?: string;
|
|
22
|
+
volatility?: string;
|
|
23
|
+
maxWin?: string;
|
|
24
|
+
lines?: string;
|
|
25
|
+
/** Any extra label → value rows (e.g. per-mode RTP/Max win). */
|
|
26
|
+
extra?: Array<{
|
|
27
|
+
label: string;
|
|
28
|
+
value: string;
|
|
29
|
+
}>;
|
|
30
|
+
}
|
|
31
|
+
interface BuildRulesOptions {
|
|
32
|
+
/** How Settings exposes sound: a `'toggle'` only · `'master'` slider · `'sliders'`
|
|
33
|
+
* (Music + Effects). Default `'sliders'`. */
|
|
34
|
+
sound?: 'toggle' | 'master' | 'sliders';
|
|
35
|
+
/** An "About the game" intro paragraph. */
|
|
36
|
+
about?: string;
|
|
37
|
+
/** "How to play" steps. */
|
|
38
|
+
howToPlay?: string[];
|
|
39
|
+
/** Feature cards (Wild / Scatter / Bonus …). */
|
|
40
|
+
features?: Array<{
|
|
41
|
+
icon?: string;
|
|
42
|
+
title: string;
|
|
43
|
+
text?: string;
|
|
44
|
+
}>;
|
|
45
|
+
/** The game's paytable blocks (usually one `{ kind: 'paytable' }`). */
|
|
46
|
+
paytable?: BlockSpec[];
|
|
47
|
+
/** RTP / volatility / max-win — the Game info grid. Must match the certified report. */
|
|
48
|
+
stats?: RulesStats;
|
|
49
|
+
/** Extra rules blocks appended before the disclaimer. */
|
|
50
|
+
extra?: BlockSpec[];
|
|
51
|
+
/** Override the built-in per-control guide lines (each: `**Name** — what it does.`). */
|
|
52
|
+
controlGuide?: string[];
|
|
53
|
+
/** Include the Stake disclaimer (default `true`). */
|
|
54
|
+
disclaimer?: boolean;
|
|
55
|
+
}
|
|
56
|
+
interface BuiltRules {
|
|
57
|
+
menu: MenuSpec;
|
|
58
|
+
/** { normal → social } for the strings buildRules authored — merge into `config.socialMessages.en`. */
|
|
59
|
+
socialEn: Record<string, string>;
|
|
60
|
+
}
|
|
61
|
+
/** Every interactive control described (Stake: the Info/Help must explain each button). */
|
|
62
|
+
declare const DEFAULT_CONTROL_GUIDE: string[];
|
|
63
|
+
/** The EXACT Stake Engine disclaimer — never reworded, never given a social override. */
|
|
64
|
+
declare const STAKE_DISCLAIMER = "Malfunction voids all wins and plays. A consistent internet connection is required. In the event of a disconnection, reload the game to finish any uncompleted rounds. The expected return is calculated over many plays. The game display is not representative of any physical device and is for illustrative purposes only. Winnings are settled according to the amount received from the Remote Game Server and not from events within the web browser. TM and © 2026 Stake Engine.";
|
|
65
|
+
declare function buildRules(opts?: BuildRulesOptions): BuiltRules;
|
|
66
|
+
//#endregion
|
|
67
|
+
export { BuildRulesOptions, BuiltRules, DEFAULT_CONTROL_GUIDE, RulesStats, SOCIAL_REPLACEMENTS, STAKE_DISCLAIMER, buildRules, findRestricted, toSocial };
|
|
68
|
+
//# sourceMappingURL=rules.d.ts.map
|