@clocktone/game-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/README.md +402 -0
- package/android/build.gradle +69 -0
- package/android/src/main/kotlin/com/playloop/plugins/applovinmax/PlayLoopAppLovinMaxPlugin.kt +316 -0
- package/android/src/main/kotlin/com/playloop/plugins/deviceid/PlayLoopDeviceIdPlugin.kt +30 -0
- package/dist/loader/cjs/index.js +72 -0
- package/dist/loader/esm/index.js +69 -0
- package/dist/types/CashableSDK.d.ts +46 -0
- package/dist/types/adapters/CapacitorAdsAdapter.d.ts +25 -0
- package/dist/types/adapters/WebViewAdsAdapter.d.ts +13 -0
- package/dist/types/babylon/BabylonPlugin.d.ts +17 -0
- package/dist/types/babylon/index.d.ts +17 -0
- package/dist/types/index.d.ts +481 -0
- package/dist/types/loader/index.d.ts +300 -0
- package/dist/types/modules/AnalyticsModule.d.ts +17 -0
- package/dist/types/modules/EmbeddedAdsModule.d.ts +30 -0
- package/dist/types/modules/FeatureFlagModule.d.ts +36 -0
- package/dist/types/modules/RewardsModule.d.ts +19 -0
- package/dist/types/modules/SessionModule.d.ts +16 -0
- package/dist/types/modules/StandaloneAdsModule.d.ts +51 -0
- package/dist/types/transport/HttpTransport.d.ts +19 -0
- package/dist/types/transport/WebViewTransport.d.ts +17 -0
- package/dist/types/types.d.ts +147 -0
- package/dist/types/ui/UIModule.d.ts +64 -0
- package/docs/ads.md +210 -0
- package/docs/analytics.md +45 -0
- package/docs/babylon.md +88 -0
- package/docs/feature-flags.md +109 -0
- package/docs/game-integration-guide.md +449 -0
- package/docs/loader.md +113 -0
- package/docs/rewards.md +57 -0
- package/docs/session.md +43 -0
- package/docs/ui.md +248 -0
- package/docs/wire-protocol.md +194 -0
- package/package.json +81 -0
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
type PlayLoopAction = 'ads.showRewarded' | 'ads.showInterstitial' | 'ads.isInterstitialAvailable' | 'ads.showBanner' | 'ads.hideBanner' | 'ads.destroyBanner' | 'rewards.awardCoins' | 'rewards.reserveSponsoredVideo' | 'rewards.claimSponsoredVideo' | 'rewards.bonusOffer' | 'rewards.awardBonus' | 'session.getBalance' | 'session.getWallet' | 'games.init' | 'analytics.playTick' | 'analytics.themeColor' | 'lifecycle.ready' | 'lifecycle.progress' | 'tier.claimCelebration' | 'games.list' | 'ui.closeGame' | 'ui.openMenu';
|
|
2
|
+
type PlayLoopEventName = 'lifecycle.freeze' | 'lifecycle.resume' | 'rewards.coinsAwarded';
|
|
3
|
+
interface PlayLoopSDKConfig {
|
|
4
|
+
gameId: string;
|
|
5
|
+
mode?: 'embedded' | 'standalone';
|
|
6
|
+
apiBaseUrl?: string;
|
|
7
|
+
requestTimeoutMs?: number;
|
|
8
|
+
debug?: boolean;
|
|
9
|
+
/** Client ID for embedded mode (host passes it for direct HTTP auth) */
|
|
10
|
+
clientId?: string;
|
|
11
|
+
/** AppLovin MAX config (required for standalone mode ads) */
|
|
12
|
+
appLovin?: {
|
|
13
|
+
/** SDK key from AppLovin dashboard (Account → General → Keys) */
|
|
14
|
+
sdkKey: string;
|
|
15
|
+
rewardedAdUnitId: string;
|
|
16
|
+
interstitialAdUnitId: string;
|
|
17
|
+
/** Ad unit ID for banner ads (optional - banners disabled if omitted) */
|
|
18
|
+
bannerAdUnitId?: string;
|
|
19
|
+
};
|
|
20
|
+
/** Auto-show a banner ad at this position after init completes. */
|
|
21
|
+
banner?: BannerPosition;
|
|
22
|
+
/** Called when SDK needs the game to pause (e.g. ad overlay). */
|
|
23
|
+
onPause?: () => void;
|
|
24
|
+
/** Called when SDK signals the game can resume. */
|
|
25
|
+
onResume?: () => void;
|
|
26
|
+
/** UI config (top bar, sponsored modal, close/menu buttons) */
|
|
27
|
+
ui?: UIConfig;
|
|
28
|
+
}
|
|
29
|
+
interface UIConfig {
|
|
30
|
+
/** Called when close button is tapped */
|
|
31
|
+
onClose?: () => void;
|
|
32
|
+
/** Called when menu button is tapped (optional) */
|
|
33
|
+
onMenuOpen?: () => void;
|
|
34
|
+
/** Show the floating PlayLoop button (default: false) */
|
|
35
|
+
showPlayLoopButton?: boolean;
|
|
36
|
+
/** Show close button on the PlayLoopButton trigger (default: true in embedded) */
|
|
37
|
+
showClose?: boolean;
|
|
38
|
+
/** Show menu button on the PlayLoopButton trigger (default: true in embedded) */
|
|
39
|
+
showMenu?: boolean;
|
|
40
|
+
/** Position and style config for the PlayLoop button */
|
|
41
|
+
PlayLoopButton?: {
|
|
42
|
+
position?: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
interface AdConfig {
|
|
46
|
+
fullScreenIntervalMs: number;
|
|
47
|
+
initialFullScreenMs: number;
|
|
48
|
+
optToFullGapMs: number;
|
|
49
|
+
fullToOptGapMs: number;
|
|
50
|
+
actionFullScreenMs: number;
|
|
51
|
+
rewardMediaDelayMs: number;
|
|
52
|
+
arcadeBonusIntervalMs: number;
|
|
53
|
+
arcadeBoostIntervalMs: number;
|
|
54
|
+
}
|
|
55
|
+
type EventHandler = (event: PlayLoopEventName, data?: unknown) => void;
|
|
56
|
+
interface SendOptions {
|
|
57
|
+
timeoutMs?: number;
|
|
58
|
+
}
|
|
59
|
+
interface ITransport {
|
|
60
|
+
send<T = unknown>(action: PlayLoopAction, payload?: Record<string, unknown>, opts?: SendOptions): Promise<T>;
|
|
61
|
+
onEvent(handler: EventHandler): () => void;
|
|
62
|
+
dispose(): void;
|
|
63
|
+
}
|
|
64
|
+
type BannerPosition = 'top' | 'bottom';
|
|
65
|
+
interface IAds {
|
|
66
|
+
showRewardedVideo(): Promise<boolean>;
|
|
67
|
+
showInterstitial(): Promise<boolean>;
|
|
68
|
+
showBanner(position?: BannerPosition): Promise<boolean>;
|
|
69
|
+
hideBanner(): Promise<void>;
|
|
70
|
+
destroyBanner(): Promise<void>;
|
|
71
|
+
onBeforeAd(handler: () => void): () => void;
|
|
72
|
+
onAfterAd(handler: (result: {
|
|
73
|
+
success: boolean;
|
|
74
|
+
}) => void): () => void;
|
|
75
|
+
dispose(): void;
|
|
76
|
+
/** Whether an interstitial can be shown (cooldown check). Always true without cooldowns. */
|
|
77
|
+
isInterstitialAvailable(): boolean;
|
|
78
|
+
/** Whether a rewarded ad can be shown (cooldown check). Always true without cooldowns. */
|
|
79
|
+
isRewardedAvailable(): boolean;
|
|
80
|
+
/** Update ad cooldown config at runtime (standalone mode only, no-op in embedded). */
|
|
81
|
+
updateAdConfig?(overrides: Partial<AdConfig>): void;
|
|
82
|
+
/** Reset all cooldown timers so ads can be shown immediately (standalone only, no-op in embedded). */
|
|
83
|
+
resetCooldowns?(): void;
|
|
84
|
+
/** Current ad config snapshot (standalone only). */
|
|
85
|
+
getAdConfig?(): Readonly<AdConfig>;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
declare class UIModule {
|
|
89
|
+
private transport;
|
|
90
|
+
private nativeTransport;
|
|
91
|
+
private ads;
|
|
92
|
+
private config;
|
|
93
|
+
private adConfig;
|
|
94
|
+
private debug;
|
|
95
|
+
private mode;
|
|
96
|
+
private rootEl;
|
|
97
|
+
private playLoopButton;
|
|
98
|
+
private wallet;
|
|
99
|
+
private scheduler;
|
|
100
|
+
private modals;
|
|
101
|
+
constructor(transport: ITransport, ads: IAds, config: UIConfig, debug: boolean, adConfig: AdConfig, mode?: 'embedded' | 'standalone', nativeTransport?: ITransport);
|
|
102
|
+
/**
|
|
103
|
+
* Mount the UI shell (styles + root + PlayLoopButton in loading state).
|
|
104
|
+
* Called early before auth completes so the pill is visible immediately.
|
|
105
|
+
*/
|
|
106
|
+
mountShell(): void;
|
|
107
|
+
updateAds(ads: IAds, adConfig?: AdConfig): void;
|
|
108
|
+
initialize(): Promise<void>;
|
|
109
|
+
getTopBarHeight(): number;
|
|
110
|
+
showSponsoredInterstitial(): Promise<boolean>;
|
|
111
|
+
showCoinAward(newTotalCoins: number): void;
|
|
112
|
+
showCoinToast(amount: number, note?: string): void;
|
|
113
|
+
updateCoins(coins: number): void;
|
|
114
|
+
refreshWallet(): Promise<void>;
|
|
115
|
+
triggerBonusOffer(): Promise<void>;
|
|
116
|
+
triggerBoosterOffer(): Promise<void>;
|
|
117
|
+
setVisible(visible: boolean): void;
|
|
118
|
+
dispose(): void;
|
|
119
|
+
private initPlayLoopButton;
|
|
120
|
+
private handleCloseGame;
|
|
121
|
+
private handleOpenMenu;
|
|
122
|
+
private log;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
declare class SessionModule {
|
|
126
|
+
private transport;
|
|
127
|
+
private mode;
|
|
128
|
+
private appSetId;
|
|
129
|
+
private clientId;
|
|
130
|
+
constructor(transport: ITransport | null, mode: 'embedded' | 'standalone', clientId?: string);
|
|
131
|
+
setTransport(transport: ITransport): void;
|
|
132
|
+
initialize(): Promise<void>;
|
|
133
|
+
getAuthHeaders(): Record<string, string>;
|
|
134
|
+
getBalance(): Promise<{
|
|
135
|
+
coins: number;
|
|
136
|
+
}>;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
declare class RewardsModule {
|
|
140
|
+
private transport;
|
|
141
|
+
private emitter;
|
|
142
|
+
private getMultiplier;
|
|
143
|
+
private lastAwardTime;
|
|
144
|
+
private static readonly AWARD_COOLDOWN_MS;
|
|
145
|
+
constructor(transport: ITransport, getMultiplier?: () => number);
|
|
146
|
+
awardCoins(opts?: {
|
|
147
|
+
multiplier?: number;
|
|
148
|
+
}): Promise<{
|
|
149
|
+
coins: number;
|
|
150
|
+
}>;
|
|
151
|
+
onCoinsAwarded(handler: (data: {
|
|
152
|
+
coins: number;
|
|
153
|
+
}) => void): () => void;
|
|
154
|
+
dispose(): void;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
declare class AnalyticsModule {
|
|
158
|
+
private transport;
|
|
159
|
+
private gameId;
|
|
160
|
+
private tickTimer;
|
|
161
|
+
private secondsAccumulated;
|
|
162
|
+
constructor(transport: ITransport, gameId: string);
|
|
163
|
+
startAutoTracking(): void;
|
|
164
|
+
stopAutoTracking(): void;
|
|
165
|
+
reportThemeColor(hex: string): void;
|
|
166
|
+
dispose(): void;
|
|
167
|
+
private flush;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
declare class FeatureFlagModule {
|
|
171
|
+
private client;
|
|
172
|
+
private _adConfig;
|
|
173
|
+
private initialized;
|
|
174
|
+
private debug;
|
|
175
|
+
constructor(debug: boolean);
|
|
176
|
+
/**
|
|
177
|
+
* Initialize Statsig with bootstrap values from the backend init endpoint.
|
|
178
|
+
* Mirrors the frontend StatsigAdapter.initialize() pattern.
|
|
179
|
+
* Returns the userId from the init response.
|
|
180
|
+
*/
|
|
181
|
+
initialize(transport: ITransport, appSetId: string | null, gameId: string, mode: 'embedded' | 'standalone'): Promise<string | null>;
|
|
182
|
+
/** Get the current ad configuration (Statsig overrides merged with defaults). */
|
|
183
|
+
get adConfig(): Readonly<AdConfig>;
|
|
184
|
+
/** Check if the adapter is initialized. */
|
|
185
|
+
isInitialized(): boolean;
|
|
186
|
+
/**
|
|
187
|
+
* Read a dynamic config value by name.
|
|
188
|
+
* Mirrors frontend StatsigAdapter.getConfig().
|
|
189
|
+
*/
|
|
190
|
+
getConfig(configName: string): {
|
|
191
|
+
get: <V>(key: string, defaultValue: V) => V;
|
|
192
|
+
value: Record<string, unknown>;
|
|
193
|
+
};
|
|
194
|
+
/** Check a feature gate. Returns false if Statsig is not initialized. */
|
|
195
|
+
checkGate(name: string): boolean;
|
|
196
|
+
/** Log an event to Statsig. */
|
|
197
|
+
logEvent(eventName: string, value?: number, metadata?: Record<string, string>): void;
|
|
198
|
+
dispose(): void;
|
|
199
|
+
/** Read `media_settings` dynamic config and merge with defaults. */
|
|
200
|
+
private readAdConfig;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
type BabylonEngine = {
|
|
204
|
+
stopRenderLoop(renderFunction?: () => void): void;
|
|
205
|
+
runRenderLoop(renderFunction: () => void): void;
|
|
206
|
+
};
|
|
207
|
+
type BabylonScene = {
|
|
208
|
+
render(): void;
|
|
209
|
+
};
|
|
210
|
+
declare class BabylonPlugin {
|
|
211
|
+
private engine;
|
|
212
|
+
private scene;
|
|
213
|
+
bindScene(scene: BabylonScene, engine?: BabylonEngine): void;
|
|
214
|
+
freeze(): void;
|
|
215
|
+
resume(): void;
|
|
216
|
+
dispose(): void;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
declare class PlayLoopSDK {
|
|
220
|
+
private config;
|
|
221
|
+
private transport;
|
|
222
|
+
private nativeTransport;
|
|
223
|
+
private emitter;
|
|
224
|
+
private unsubTransportEvents;
|
|
225
|
+
private unsubNativeEvents;
|
|
226
|
+
private unsubRewardsCoins;
|
|
227
|
+
private _session;
|
|
228
|
+
private _rewards;
|
|
229
|
+
private _ads;
|
|
230
|
+
private _analytics;
|
|
231
|
+
private _featureFlags;
|
|
232
|
+
private _babylon;
|
|
233
|
+
private _ui;
|
|
234
|
+
private _isAuthenticated;
|
|
235
|
+
readonly mode: 'embedded' | 'standalone';
|
|
236
|
+
static getHostConfig(): {
|
|
237
|
+
clientId?: string;
|
|
238
|
+
apiBaseUrl?: string;
|
|
239
|
+
debug?: boolean;
|
|
240
|
+
} | null;
|
|
241
|
+
static getWidgetHeight(): number;
|
|
242
|
+
constructor(config: PlayLoopSDKConfig);
|
|
243
|
+
init(): Promise<void>;
|
|
244
|
+
private initEmbeddedMode;
|
|
245
|
+
private initStandaloneMode;
|
|
246
|
+
private initRewards;
|
|
247
|
+
private finalizeUI;
|
|
248
|
+
private subscribeTransportEvents;
|
|
249
|
+
private autoDetectBabylon;
|
|
250
|
+
private handleTransportEvent;
|
|
251
|
+
get session(): SessionModule;
|
|
252
|
+
get rewards(): RewardsModule;
|
|
253
|
+
get ads(): IAds;
|
|
254
|
+
get analytics(): AnalyticsModule;
|
|
255
|
+
get isAuthenticated(): boolean;
|
|
256
|
+
get featureFlags(): FeatureFlagModule | null;
|
|
257
|
+
getTopBarHeight(): number;
|
|
258
|
+
getTopBarHeightAsync(): Promise<number>;
|
|
259
|
+
get adConfig(): Readonly<AdConfig>;
|
|
260
|
+
get ui(): UIModule | null;
|
|
261
|
+
get babylon(): BabylonPlugin;
|
|
262
|
+
onFreeze(handler: () => void): () => void;
|
|
263
|
+
onResume(handler: () => void): () => void;
|
|
264
|
+
onWidgetReady(handler: (data: {
|
|
265
|
+
height: number;
|
|
266
|
+
}) => void): () => void;
|
|
267
|
+
dispose(): void;
|
|
268
|
+
private detectMode;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* PlayLoop SDK Loader — thin bootstrap for CDN-delivered SDK.
|
|
273
|
+
*
|
|
274
|
+
* Games bundle this loader via their build tool (Vite/Webpack).
|
|
275
|
+
* It shims CapacitorCore, fetches the SDK version from the backend,
|
|
276
|
+
* loads the IIFE script from CDN, and returns the typed constructor.
|
|
277
|
+
*/
|
|
278
|
+
|
|
279
|
+
/** Compute widget top-bar height from window.innerWidth — no SDK load required. */
|
|
280
|
+
declare function getWidgetHeight(): number;
|
|
281
|
+
interface LoaderOptions {
|
|
282
|
+
/** Backend base URL (e.g. "https://api.playloopapi.com") */
|
|
283
|
+
apiBaseUrl: string;
|
|
284
|
+
/** Hardcoded fallback CDN URL if version fetch fails */
|
|
285
|
+
fallbackUrl?: string;
|
|
286
|
+
/** Timeout for the version-fetch request (default: 5000ms) */
|
|
287
|
+
timeoutMs?: number;
|
|
288
|
+
}
|
|
289
|
+
/**
|
|
290
|
+
* Load the PlayLoop SDK from CDN and return the constructor.
|
|
291
|
+
*
|
|
292
|
+
* 1. Shims `window.CapacitorCore` so the IIFE can resolve `@capacitor/core`
|
|
293
|
+
* 2. Fetches `/s/arcade/sdk-ver` to get the latest CDN URL
|
|
294
|
+
* 3. Injects a `<script>` tag for the IIFE bundle
|
|
295
|
+
* 4. Returns the typed `PlayLoopSDK` constructor
|
|
296
|
+
*/
|
|
297
|
+
declare function loadPlayLoopSDK(options: LoaderOptions): Promise<typeof PlayLoopSDK>;
|
|
298
|
+
|
|
299
|
+
export { PlayLoopSDK, getWidgetHeight, loadPlayLoopSDK };
|
|
300
|
+
export type { LoaderOptions, PlayLoopSDKConfig, UIConfig };
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { ITransport } from '../types.js';
|
|
2
|
+
|
|
3
|
+
declare class AnalyticsModule {
|
|
4
|
+
private transport;
|
|
5
|
+
private gameId;
|
|
6
|
+
private tickTimer;
|
|
7
|
+
private secondsAccumulated;
|
|
8
|
+
constructor(transport: ITransport, gameId: string);
|
|
9
|
+
startAutoTracking(): void;
|
|
10
|
+
stopAutoTracking(): void;
|
|
11
|
+
logEvent(eventName: string, eventValue?: number, properties?: Record<string, unknown>): void;
|
|
12
|
+
reportThemeColor(hex: string): void;
|
|
13
|
+
dispose(): void;
|
|
14
|
+
private flush;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export { AnalyticsModule };
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { IAds, IAdsAdapter, BannerPosition } from '../types.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Ads module for embedded mode (game hosted in Cashable WebView).
|
|
5
|
+
* Thin pass-through — the host app manages cooldowns.
|
|
6
|
+
*/
|
|
7
|
+
declare class EmbeddedAdsModule implements IAds {
|
|
8
|
+
private adapter;
|
|
9
|
+
private emitter;
|
|
10
|
+
constructor(adapter: IAdsAdapter);
|
|
11
|
+
/** Always available — host app manages cooldowns. */
|
|
12
|
+
isInterstitialAvailable(): boolean;
|
|
13
|
+
/** Always available — host app manages cooldowns. */
|
|
14
|
+
isRewardedAvailable(): boolean;
|
|
15
|
+
showRewardedVideo(): Promise<boolean>;
|
|
16
|
+
showInterstitial(): Promise<boolean>;
|
|
17
|
+
/** Show a banner ad at the given screen position (default: bottom). */
|
|
18
|
+
showBanner(position?: BannerPosition): Promise<boolean>;
|
|
19
|
+
/** Hide the banner ad (keeps it loaded for fast re-show). */
|
|
20
|
+
hideBanner(): Promise<void>;
|
|
21
|
+
/** Destroy the banner ad and free resources. */
|
|
22
|
+
destroyBanner(): Promise<void>;
|
|
23
|
+
onBeforeAd(handler: () => void): () => void;
|
|
24
|
+
onAfterAd(handler: (result: {
|
|
25
|
+
success: boolean;
|
|
26
|
+
}) => void): () => void;
|
|
27
|
+
dispose(): void;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export { EmbeddedAdsModule };
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { ITransport, AdConfig } from '../types.js';
|
|
2
|
+
|
|
3
|
+
declare class FeatureFlagModule {
|
|
4
|
+
private client;
|
|
5
|
+
private _adConfig;
|
|
6
|
+
private initialized;
|
|
7
|
+
private debug;
|
|
8
|
+
constructor(debug: boolean);
|
|
9
|
+
/**
|
|
10
|
+
* Initialize Statsig with bootstrap values from the backend init endpoint.
|
|
11
|
+
* Mirrors the frontend StatsigAdapter.initialize() pattern.
|
|
12
|
+
* Returns the userId from the init response.
|
|
13
|
+
*/
|
|
14
|
+
initialize(transport: ITransport, appSetId: string | null, gameId: string, mode: 'embedded' | 'standalone'): Promise<string | null>;
|
|
15
|
+
/** Get the current ad configuration (Statsig overrides merged with defaults). */
|
|
16
|
+
get adConfig(): Readonly<AdConfig>;
|
|
17
|
+
/** Check if the adapter is initialized. */
|
|
18
|
+
isInitialized(): boolean;
|
|
19
|
+
/**
|
|
20
|
+
* Read a dynamic config value by name.
|
|
21
|
+
* Mirrors frontend StatsigAdapter.getConfig().
|
|
22
|
+
*/
|
|
23
|
+
getConfig(configName: string): {
|
|
24
|
+
get: <V>(key: string, defaultValue: V) => V;
|
|
25
|
+
value: Record<string, unknown>;
|
|
26
|
+
};
|
|
27
|
+
/** Check a feature gate. Returns false if Statsig is not initialized. */
|
|
28
|
+
checkGate(name: string): boolean;
|
|
29
|
+
/** Log an event to Statsig. */
|
|
30
|
+
logEvent(eventName: string, value?: number, metadata?: Record<string, string>): void;
|
|
31
|
+
dispose(): void;
|
|
32
|
+
/** Read ad_config dynamic config and merge with defaults. Mirrors frontend useAdConfigSync. */
|
|
33
|
+
private readAdConfig;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export { FeatureFlagModule };
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { ITransport } from '../types.js';
|
|
2
|
+
|
|
3
|
+
declare class RewardsModule {
|
|
4
|
+
private transport;
|
|
5
|
+
private emitter;
|
|
6
|
+
private getMultiplier;
|
|
7
|
+
constructor(transport: ITransport, getMultiplier?: () => number);
|
|
8
|
+
awardCoins(opts?: {
|
|
9
|
+
multiplier?: number;
|
|
10
|
+
}): Promise<{
|
|
11
|
+
coins: number;
|
|
12
|
+
}>;
|
|
13
|
+
onCoinsAwarded(handler: (data: {
|
|
14
|
+
coins: number;
|
|
15
|
+
}) => void): () => void;
|
|
16
|
+
dispose(): void;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export { RewardsModule };
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { ITransport } from '../types.js';
|
|
2
|
+
|
|
3
|
+
declare class SessionModule {
|
|
4
|
+
private transport;
|
|
5
|
+
private mode;
|
|
6
|
+
private appSetId;
|
|
7
|
+
constructor(transport: ITransport | null, mode: 'embedded' | 'standalone');
|
|
8
|
+
setTransport(transport: ITransport): void;
|
|
9
|
+
initialize(): Promise<void>;
|
|
10
|
+
getAuthHeaders(): Record<string, string>;
|
|
11
|
+
getBalance(): Promise<{
|
|
12
|
+
coins: number;
|
|
13
|
+
}>;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export { SessionModule };
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { IAds, AdConfig, BannerPosition } from '../types.js';
|
|
2
|
+
import { EmbeddedAdsModule } from './EmbeddedAdsModule.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Ads module for standalone mode (Capacitor game running independently).
|
|
6
|
+
* Wraps EmbeddedAdsModule with cooldown enforcement since there is no host app
|
|
7
|
+
* to manage ad pacing.
|
|
8
|
+
*
|
|
9
|
+
* Cooldown rules (matching frontend/src/lib/ads.ts):
|
|
10
|
+
* - First interstitial delayed by `firstInterstitialCooldownMs`
|
|
11
|
+
* - After interstitial: next interstitial in `interstitialCooldownMs`,
|
|
12
|
+
* next rewarded in `interstitialToRewardedCooldownMs`
|
|
13
|
+
* - After rewarded: next interstitial in `rewardedToInterstitialCooldownMs`
|
|
14
|
+
* - Rewarded ads are never blocked (user-initiated), but record cross-cooldowns
|
|
15
|
+
*/
|
|
16
|
+
declare class StandaloneAdsModule implements IAds {
|
|
17
|
+
private inner;
|
|
18
|
+
private config;
|
|
19
|
+
private _nextInterstitialAt;
|
|
20
|
+
private _nextRewardedAt;
|
|
21
|
+
private _hasShownFullScreenAd;
|
|
22
|
+
constructor(inner: EmbeddedAdsModule, config: AdConfig);
|
|
23
|
+
/** Whether an interstitial can be shown right now (cooldown elapsed). */
|
|
24
|
+
isInterstitialAvailable(): boolean;
|
|
25
|
+
/** Whether a rewarded ad can be shown right now (cooldown elapsed). */
|
|
26
|
+
isRewardedAvailable(): boolean;
|
|
27
|
+
/** Whether any full-screen ad has been shown this session. */
|
|
28
|
+
get hasShownFullScreenAd(): boolean;
|
|
29
|
+
showRewardedVideo(): Promise<boolean>;
|
|
30
|
+
showInterstitial(): Promise<boolean>;
|
|
31
|
+
showBanner(position?: BannerPosition): Promise<boolean>;
|
|
32
|
+
hideBanner(): Promise<void>;
|
|
33
|
+
destroyBanner(): Promise<void>;
|
|
34
|
+
onBeforeAd(handler: () => void): () => void;
|
|
35
|
+
onAfterAd(handler: (result: {
|
|
36
|
+
success: boolean;
|
|
37
|
+
}) => void): () => void;
|
|
38
|
+
/** Update ad cooldown config at runtime (partial merge). */
|
|
39
|
+
updateAdConfig(overrides: Partial<AdConfig>): void;
|
|
40
|
+
/** Reset all cooldown timers so ads can be shown immediately. */
|
|
41
|
+
resetCooldowns(): void;
|
|
42
|
+
/** Current ad config (read-only snapshot). */
|
|
43
|
+
getAdConfig(): Readonly<AdConfig>;
|
|
44
|
+
dispose(): void;
|
|
45
|
+
/** Lazy-init: first access starts the first-interstitial cooldown. */
|
|
46
|
+
private getNextInterstitialAt;
|
|
47
|
+
private recordInterstitialShown;
|
|
48
|
+
private recordRewardedShown;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export { StandaloneAdsModule };
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { ITransport, CashableAction, SendOptions, EventHandler } from '../types.js';
|
|
2
|
+
|
|
3
|
+
interface HttpTransportConfig {
|
|
4
|
+
apiBaseUrl: string;
|
|
5
|
+
gameId: string;
|
|
6
|
+
getAuthHeaders: () => Record<string, string>;
|
|
7
|
+
defaultTimeoutMs?: number;
|
|
8
|
+
}
|
|
9
|
+
declare class HttpTransport implements ITransport {
|
|
10
|
+
private config;
|
|
11
|
+
private activeControllers;
|
|
12
|
+
private disposed;
|
|
13
|
+
constructor(config: HttpTransportConfig);
|
|
14
|
+
send<T = unknown>(action: CashableAction, payload?: Record<string, unknown>, opts?: SendOptions): Promise<T>;
|
|
15
|
+
onEvent(_handler: EventHandler): () => void;
|
|
16
|
+
dispose(): void;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export { HttpTransport };
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { ITransport, CashableAction, SendOptions, EventHandler } from '../types.js';
|
|
2
|
+
|
|
3
|
+
declare class WebViewTransport implements ITransport {
|
|
4
|
+
private pending;
|
|
5
|
+
private eventHandlers;
|
|
6
|
+
private messageListener;
|
|
7
|
+
private defaultTimeoutMs;
|
|
8
|
+
constructor(opts?: {
|
|
9
|
+
defaultTimeoutMs?: number;
|
|
10
|
+
});
|
|
11
|
+
send<T = unknown>(action: CashableAction, payload?: Record<string, unknown>, opts?: SendOptions): Promise<T>;
|
|
12
|
+
onEvent(handler: EventHandler): () => void;
|
|
13
|
+
dispose(): void;
|
|
14
|
+
private handleMessage;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export { WebViewTransport };
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
interface CashableRequest {
|
|
2
|
+
type: 'cashable:request';
|
|
3
|
+
action: CashableAction;
|
|
4
|
+
requestId: string;
|
|
5
|
+
payload?: Record<string, unknown>;
|
|
6
|
+
}
|
|
7
|
+
interface CashableResponse {
|
|
8
|
+
type: 'cashable:response';
|
|
9
|
+
requestId: string;
|
|
10
|
+
data?: unknown;
|
|
11
|
+
error?: {
|
|
12
|
+
code: CashableErrorCode;
|
|
13
|
+
message: string;
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
interface CashableEvent {
|
|
17
|
+
type: 'cashable:event';
|
|
18
|
+
event: CashableEventName;
|
|
19
|
+
data?: unknown;
|
|
20
|
+
}
|
|
21
|
+
interface CashableReady {
|
|
22
|
+
type: 'cashable:ready';
|
|
23
|
+
version: string;
|
|
24
|
+
}
|
|
25
|
+
type CashableMessage = CashableRequest | CashableResponse | CashableEvent | CashableReady;
|
|
26
|
+
type CashableAction = 'ads.showRewarded' | 'ads.showInterstitial' | 'ads.showBanner' | 'ads.hideBanner' | 'ads.destroyBanner' | 'rewards.awardCoins' | 'rewards.reserveSponsoredVideo' | 'rewards.claimSponsoredVideo' | 'rewards.bonusOffer' | 'rewards.awardBonus' | 'session.getBalance' | 'session.getWallet' | 'games.init' | 'analytics.playTick' | 'analytics.logEvent' | 'analytics.themeColor' | 'lifecycle.ready' | 'lifecycle.progress' | 'tier.claimCelebration' | 'games.list';
|
|
27
|
+
type CashableEventName = 'lifecycle.freeze' | 'lifecycle.resume' | 'rewards.coinsAwarded';
|
|
28
|
+
type CashableErrorCode = 'TIMEOUT' | 'TRANSPORT_ERROR' | 'AD_NOT_AVAILABLE' | 'NETWORK_ERROR' | 'USER_NOT_LINKED';
|
|
29
|
+
interface CashableSDKConfig {
|
|
30
|
+
gameId: string;
|
|
31
|
+
mode?: 'embedded' | 'standalone';
|
|
32
|
+
apiBaseUrl?: string;
|
|
33
|
+
requestTimeoutMs?: number;
|
|
34
|
+
debug?: boolean;
|
|
35
|
+
/** AppLovin MAX config (required for standalone mode ads) */
|
|
36
|
+
appLovin?: {
|
|
37
|
+
/** SDK key from AppLovin dashboard (Account → General → Keys) */
|
|
38
|
+
sdkKey: string;
|
|
39
|
+
rewardedAdUnitId: string;
|
|
40
|
+
interstitialAdUnitId: string;
|
|
41
|
+
/** Ad unit ID for banner ads (optional - banners disabled if omitted) */
|
|
42
|
+
bannerAdUnitId?: string;
|
|
43
|
+
};
|
|
44
|
+
/** UI config for standalone mode (top bar, sponsored modal) */
|
|
45
|
+
ui?: UIConfig;
|
|
46
|
+
}
|
|
47
|
+
interface UIConfig {
|
|
48
|
+
/** Called when close button is tapped */
|
|
49
|
+
onClose?: () => void;
|
|
50
|
+
/** Called when menu button is tapped (optional) */
|
|
51
|
+
onMenuOpen?: () => void;
|
|
52
|
+
/** Show the floating Cashable button (default: false) */
|
|
53
|
+
showCashableButton?: boolean;
|
|
54
|
+
/** Position and style config for the Cashable button */
|
|
55
|
+
cashableButton?: {
|
|
56
|
+
position?: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';
|
|
57
|
+
/** Auto-open the panel briefly on first wallet data (default: true) */
|
|
58
|
+
autoPeek?: boolean;
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
interface TierInfo {
|
|
62
|
+
tier: number;
|
|
63
|
+
name: string;
|
|
64
|
+
target: number;
|
|
65
|
+
}
|
|
66
|
+
interface WalletData {
|
|
67
|
+
coins: number;
|
|
68
|
+
tier: {
|
|
69
|
+
currentTier: number;
|
|
70
|
+
currentTierName: string;
|
|
71
|
+
nextTier?: {
|
|
72
|
+
name: string;
|
|
73
|
+
target: number;
|
|
74
|
+
};
|
|
75
|
+
};
|
|
76
|
+
tierDefinitions: TierInfo[];
|
|
77
|
+
coinGoal: number;
|
|
78
|
+
coinsEarnedThisPeriod: number;
|
|
79
|
+
earnings: number;
|
|
80
|
+
cashoutPeriodEndsAt: number;
|
|
81
|
+
}
|
|
82
|
+
interface ActiveOffer {
|
|
83
|
+
offerId: string;
|
|
84
|
+
amount?: number;
|
|
85
|
+
type: 'bonus' | 'booster';
|
|
86
|
+
}
|
|
87
|
+
interface InitResponse {
|
|
88
|
+
userId: string;
|
|
89
|
+
featureFlags?: {
|
|
90
|
+
clientKey: string;
|
|
91
|
+
bootstrapValues?: string;
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
interface AdConfig {
|
|
95
|
+
interstitialCooldownMs: number;
|
|
96
|
+
firstInterstitialCooldownMs: number;
|
|
97
|
+
rewardedToInterstitialCooldownMs: number;
|
|
98
|
+
interstitialToRewardedCooldownMs: number;
|
|
99
|
+
playClickInterstitialCooldownMs: number;
|
|
100
|
+
earnBeforePlayingAdCooldownMs: number;
|
|
101
|
+
gamesBonusOfferCadenceMs: number;
|
|
102
|
+
gamesCoinBoosterCadenceMs: number;
|
|
103
|
+
}
|
|
104
|
+
/** Default ad config values (matches Cashable app defaults). */
|
|
105
|
+
declare const DEFAULT_AD_CONFIG: AdConfig;
|
|
106
|
+
type EventHandler = (event: CashableEventName, data?: unknown) => void;
|
|
107
|
+
interface SendOptions {
|
|
108
|
+
timeoutMs?: number;
|
|
109
|
+
}
|
|
110
|
+
interface ITransport {
|
|
111
|
+
send<T = unknown>(action: CashableAction, payload?: Record<string, unknown>, opts?: SendOptions): Promise<T>;
|
|
112
|
+
onEvent(handler: EventHandler): () => void;
|
|
113
|
+
dispose(): void;
|
|
114
|
+
}
|
|
115
|
+
type BannerPosition = 'top' | 'bottom';
|
|
116
|
+
interface IAdsAdapter {
|
|
117
|
+
showRewardedVideo(): Promise<boolean>;
|
|
118
|
+
showInterstitial(): Promise<boolean>;
|
|
119
|
+
showBanner(position?: BannerPosition): Promise<boolean>;
|
|
120
|
+
hideBanner(): Promise<void>;
|
|
121
|
+
destroyBanner(): Promise<void>;
|
|
122
|
+
}
|
|
123
|
+
interface IAds {
|
|
124
|
+
showRewardedVideo(): Promise<boolean>;
|
|
125
|
+
showInterstitial(): Promise<boolean>;
|
|
126
|
+
showBanner(position?: BannerPosition): Promise<boolean>;
|
|
127
|
+
hideBanner(): Promise<void>;
|
|
128
|
+
destroyBanner(): Promise<void>;
|
|
129
|
+
onBeforeAd(handler: () => void): () => void;
|
|
130
|
+
onAfterAd(handler: (result: {
|
|
131
|
+
success: boolean;
|
|
132
|
+
}) => void): () => void;
|
|
133
|
+
dispose(): void;
|
|
134
|
+
/** Whether an interstitial can be shown (cooldown check). Always true without cooldowns. */
|
|
135
|
+
isInterstitialAvailable(): boolean;
|
|
136
|
+
/** Whether a rewarded ad can be shown (cooldown check). Always true without cooldowns. */
|
|
137
|
+
isRewardedAvailable(): boolean;
|
|
138
|
+
/** Update ad cooldown config at runtime (standalone mode only, no-op in embedded). */
|
|
139
|
+
updateAdConfig?(overrides: Partial<AdConfig>): void;
|
|
140
|
+
/** Reset all cooldown timers so ads can be shown immediately (standalone only, no-op in embedded). */
|
|
141
|
+
resetCooldowns?(): void;
|
|
142
|
+
/** Current ad config snapshot (standalone only). */
|
|
143
|
+
getAdConfig?(): Readonly<AdConfig>;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export { DEFAULT_AD_CONFIG };
|
|
147
|
+
export type { ActiveOffer, AdConfig, BannerPosition, CashableAction, CashableErrorCode, CashableEvent, CashableEventName, CashableMessage, CashableReady, CashableRequest, CashableResponse, CashableSDKConfig, EventHandler, IAds, IAdsAdapter, ITransport, InitResponse, SendOptions, TierInfo, UIConfig, WalletData };
|