@jest-com/types 1.260826.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +44 -0
  2. package/index.d.ts +1247 -0
  3. package/package.json +30 -0
package/README.md ADDED
@@ -0,0 +1,44 @@
1
+ # @jest-com/types
2
+
3
+ TypeScript types for the [Jest](https://docs.jest.com) HTML5 game SDK.
4
+
5
+ The SDK runtime is loaded from the CDN, not from npm. This package only
6
+ describes the global `JestSDK` object that script creates.
7
+
8
+ ```html
9
+ <script defer src="https://cdn.jest.com/sdk/latest/jestsdk.js"></script>
10
+ ```
11
+
12
+ ```bash
13
+ npm install --save-dev @jest-com/types
14
+ ```
15
+
16
+ ## Usage
17
+
18
+ Import any type from the package, anywhere in your project:
19
+
20
+ ```typescript
21
+ import type { Jest, PlayerProfile, PurchaseData } from "@jest-com/types";
22
+ ```
23
+
24
+ That is the whole setup. `JestSDK` and `window.JestSDK` are typed globals in
25
+ every file, not just the one that imports:
26
+
27
+ ```typescript
28
+ await JestSDK.init();
29
+
30
+ const player = JestSDK.getPlayer();
31
+ ```
32
+
33
+ If your game only touches the global and never annotates a named type, there is
34
+ nothing for it to import. Add a `.d.ts` file instead, for example
35
+ `src/jestsdk.d.ts`:
36
+
37
+ ```typescript
38
+ /// <reference types="@jest-com/types" />
39
+ ```
40
+
41
+ TypeScript auto-loads types only from `node_modules/@types/*`, so one or the
42
+ other is needed.
43
+
44
+ Full docs: https://docs.jest.com/sdk/html5
package/index.d.ts ADDED
@@ -0,0 +1,1247 @@
1
+ export type AvatarSize = 64 | 128 | 256 | 512 | 1000;
2
+ export type PlayerProfile = {
3
+ username: string;
4
+ avatarUrl: string | null;
5
+ };
6
+ /**
7
+ * Data structure for a purchase, documented for game use.
8
+ *
9
+ * Because intended consumer is the game, we use decimal credits.
10
+ */
11
+ export type PurchaseData = {
12
+ purchaseToken: string;
13
+ productSku: string;
14
+ credits: number;
15
+ createdAt: number;
16
+ completedAt: number | null;
17
+ /** @deprecated Always 0. Kept for SDK backwards compatibility. */
18
+ estimatedRevenue: number;
19
+ price: number;
20
+ currency: string;
21
+ /**
22
+ * `true` when no money changed hands: a sandbox user made the purchase (it
23
+ * is then priced at 0), or it came from the Developer Console simulator
24
+ * (which keeps the configured price). Absent on real purchases. Grant the
25
+ * item as usual when testing, but keep these out of anything counting real
26
+ * money.
27
+ */
28
+ sandbox?: boolean;
29
+ };
30
+ /**
31
+ * Data structure for a subscription offer + the wallet's current
32
+ * entitlement for it. Mirrored in the `SubscriptionsSigned` JWS so
33
+ * games can server-verify the same payload they received over the SDK.
34
+ */
35
+ export type SubscriptionData = {
36
+ sku: string;
37
+ displayName: string;
38
+ displayDescription: string | null;
39
+ price: number;
40
+ currency: string;
41
+ billingPeriod: "monthly" | "weekly" | "yearly";
42
+ status: "active" | "inactive";
43
+ /**
44
+ * Whether the wallet is eligible for this subscription's free trial: it has a
45
+ * trial configured and the wallet has never subscribed to it before.
46
+ */
47
+ trialEligible: boolean;
48
+ /**
49
+ * The retention discount this wallet can claim once via
50
+ * `claimRetentionOffer`: a discounted price for the next billing periods of
51
+ * the subscription they already hold, after which the standard `price`
52
+ * returns automatically. Non-null only while the wallet is entitled, the
53
+ * offer is configured, it was never claimed before, and no free trial or
54
+ * introductory offer window is still running.
55
+ */
56
+ retentionOffer: {
57
+ /** Discounted price in the currency specified in `currency`, in decimal. */
58
+ price: number;
59
+ /**
60
+ * Number of billing periods (see `billingPeriod`) the discounted price
61
+ * applies, starting at the next renewal.
62
+ */
63
+ durationPeriods: number;
64
+ } | null;
65
+ /**
66
+ * The introductory offer available to this wallet: a discounted price for
67
+ * the first billing periods of the subscription, after which the standard
68
+ * `price` applies automatically. Non-null only when an intro offer is configured
69
+ * and the wallet has never subscribed to this product before.
70
+ */
71
+ introOffer: {
72
+ /** Discounted price in the currency specified in `currency`, in decimal. */
73
+ price: number;
74
+ /**
75
+ * Number of billing periods (see `billingPeriod`) the discounted price
76
+ * applies, measured from signup.
77
+ */
78
+ durationPeriods: number;
79
+ } | null;
80
+ /**
81
+ * `true` when no money can change hands: the player is a sandbox user (any
82
+ * subscription they start bills 0), or this came from the Developer Console
83
+ * simulator. Absent for real players. `price` still shows the configured
84
+ * amount, but a sandbox user never gets `introOffer` or `retentionOffer` —
85
+ * a checkout already forced to 0 carries no discount.
86
+ */
87
+ sandbox?: boolean;
88
+ /** @deprecated Always 0. Kept for SDK backwards compatibility. */
89
+ estimatedRevenue: number;
90
+ };
91
+
92
+ export type RedirectToTeamGameErrorCode = "game_not_found" | "different_team" | "internal_error";
93
+ export type NotificationPriority = "low" | "medium" | "high" | "critical";
94
+ export type ReferralNotificationVariant = {
95
+ title?: string | null;
96
+ body: string;
97
+ ctaText: string;
98
+ imageReference?: string | null;
99
+ };
100
+ export type ReferralNotificationTemplate = {
101
+ minConversionCount: number;
102
+ variants: ReferralNotificationVariant[];
103
+ };
104
+
105
+ export type LifecycleListener = () => void | PromiseLike<void>;
106
+ export interface LifecycleModule {
107
+ /** Runs when the game document changes from visible to hidden. */
108
+ onHide(listener: LifecycleListener): () => void;
109
+ /** Runs when the game document changes from hidden to visible. */
110
+ onShow(listener: LifecycleListener): () => void;
111
+ /** Runs when the platform begins an exit flow for the game. */
112
+ onExitRequested(listener: LifecycleListener): () => void;
113
+ }
114
+ /**
115
+ * Player data — a simple key-value store for per-player state,
116
+ * persisted across sessions and devices.
117
+ *
118
+ * Use this when your game does not have its own backend. Data is
119
+ * stored alongside the player record on the Jest platform.
120
+ *
121
+ * Constraints:
122
+ * - Values must be JSON-serializable.
123
+ * - Limited to 1 MB per game per player; further writes fail
124
+ * until the stored data size is reduced.
125
+ * - Written directly from the client — do NOT store sensitive
126
+ * information or data requiring strong security guarantees.
127
+ * - Writes are sent as they are made; `flush()` waits for the platform
128
+ * to acknowledge them.
129
+ *
130
+ * Docs: https://docs.jest.com/sdk/html5/player#player-data
131
+ */
132
+ export interface PlayerDataModule {
133
+ /**
134
+ * Returns the value for a key, or `undefined` if not set.
135
+ *
136
+ * @throws {Error} If the SDK is not initialized.
137
+ */
138
+ get(key: string): unknown;
139
+ /**
140
+ * Returns a snapshot of all player data.
141
+ *
142
+ * Modifying the returned object does NOT update stored data;
143
+ * use `set()` to write back.
144
+ *
145
+ * @throws {Error} If the SDK is not initialized.
146
+ */
147
+ getAll(): Record<string, unknown>;
148
+ /**
149
+ * Sets a single key-value pair.
150
+ *
151
+ * @throws {Error} If the SDK is not initialized.
152
+ */
153
+ set(key: string, value: unknown): void;
154
+ /**
155
+ * Shallow-merges multiple key-value pairs into player data.
156
+ *
157
+ * Existing keys not in `partial` are preserved. To remove a key,
158
+ * use `delete()` (or set its value to `undefined`).
159
+ *
160
+ * @throws {Error} If the SDK is not initialized.
161
+ * @example
162
+ * ```typescript
163
+ * JestSDK.data.set({ score: 100, level: 2 });
164
+ * ```
165
+ */
166
+ set(partial: Record<string, unknown>): void;
167
+ /**
168
+ * Deletes a key from player data.
169
+ *
170
+ * @throws {Error} If the SDK is not initialized.
171
+ */
172
+ delete(key: string): void;
173
+ /**
174
+ * Waits for the platform to acknowledge the writes made so far.
175
+ *
176
+ * `set` and `delete` send their update as soon as they are called,
177
+ * unless an earlier update is still unacknowledged, in which case the
178
+ * queued changes coalesce into the next message. This call resolves
179
+ * immediately when nothing is outstanding.
180
+ *
181
+ * The acknowledgement reports that the platform handled the update, not
182
+ * that it stored it: an update the platform rejects because another
183
+ * session advanced the player's state is acknowledged as well.
184
+ *
185
+ * @throws {Error} If the SDK is not initialized.
186
+ */
187
+ flush(): Promise<void>;
188
+ }
189
+ /**
190
+ * Options for {@link NotificationsModule.scheduleNotification}.
191
+ *
192
+ * Provide exactly one of `scheduledAt` (exact time) or `scheduledInDays`
193
+ * (fuzzy timing).
194
+ */
195
+ export type ScheduleNotificationOptions = {
196
+ /**
197
+ * Stable identifier for this notification. Used to replace or
198
+ * unschedule it later. Scheduling with an existing identifier
199
+ * automatically replaces the previous notification.
200
+ */
201
+ identifier?: string;
202
+ /**
203
+ * Main body text of the notification.
204
+ */
205
+ body: string;
206
+ /**
207
+ * Optional title; rendered above the body where supported.
208
+ */
209
+ title?: string;
210
+ /**
211
+ * Call-to-action button label. Must be 1–25 characters.
212
+ */
213
+ ctaText: string;
214
+ /**
215
+ * Higher-priority notifications are weighted more heavily when
216
+ * the platform selects which notification to deliver per day.
217
+ * Defaults to `"low"`.
218
+ */
219
+ priority: NotificationPriority;
220
+ /**
221
+ * Reference to a pre-approved image from the Developer Console's
222
+ * Assets Library. Falls back to the game's Hero image if missing
223
+ * or unapproved.
224
+ */
225
+ assetReference?: string;
226
+ /**
227
+ * @deprecated Use {@link assetReference} instead.
228
+ */
229
+ imageReference?: string;
230
+ /**
231
+ * Optional metadata embedded into the notification's link.
232
+ * Available via `JestSDK.getEntryPayload()` when the player taps
233
+ * the notification.
234
+ */
235
+ entryPayload?: Record<string, unknown>;
236
+ } & ({
237
+ /**
238
+ * Exact scheduled delivery time. Must be within the next 7 days.
239
+ * Use this for fixed events or deadlines.
240
+ */
241
+ scheduledAt: Date;
242
+ scheduledInDays?: never;
243
+ } | {
244
+ scheduledAt?: never;
245
+ /**
246
+ * Days from now to deliver the notification (1–7, inclusive).
247
+ * The platform picks an optimal delivery time within that window
248
+ * for each player.
249
+ */
250
+ scheduledInDays: number;
251
+ });
252
+ /**
253
+ * A single reply option on an interactive notification step. The `key` is
254
+ * returned in the gathered picks; a key matching a message's `key` branches the
255
+ * flow there, otherwise it advances to the next message.
256
+ */
257
+ export type InteractiveNotificationOption = {
258
+ key: string;
259
+ /** Reply-chip label (1–25 characters). */
260
+ label: string;
261
+ };
262
+ /** A non-terminal message: presents 1–6 reply chips. */
263
+ export type InteractiveNotificationStepMessage = {
264
+ /** Stable id; also a branch target for options keyed to it. */
265
+ key: string;
266
+ body: string;
267
+ options: InteractiveNotificationOption[];
268
+ };
269
+ /**
270
+ * A terminal message: ends its branch and is sent as the "continue in browser"
271
+ * link. The last message in a flow must be terminal.
272
+ */
273
+ export type InteractiveNotificationTerminalMessage = {
274
+ key: string;
275
+ body: string;
276
+ /** CTA label for the link. */
277
+ ctaText: string;
278
+ };
279
+ export type InteractiveNotificationMessage = InteractiveNotificationStepMessage | InteractiveNotificationTerminalMessage;
280
+ /**
281
+ * Options for {@link ExperimentalNotificationsModule.scheduleInteractiveNotification}.
282
+ *
283
+ * Provide exactly one of `scheduledAt` (exact time) or `scheduledInDays`
284
+ * (fuzzy timing).
285
+ */
286
+ export type ScheduleInteractiveNotificationOptions = {
287
+ /** Stable identifier used to replace or unschedule this notification. */
288
+ identifier?: string;
289
+ /**
290
+ * Ordered messages sent one after another as the player taps. The last
291
+ * message must be terminal (no options); terminal messages carry the
292
+ * "continue in browser" link.
293
+ */
294
+ messages: InteractiveNotificationMessage[];
295
+ /** Delivery weight. Defaults to `"medium"`. */
296
+ priority?: NotificationPriority;
297
+ /** Pre-approved image reference for the first message. */
298
+ assetReference?: string;
299
+ /**
300
+ * Metadata embedded into the game link. The gathered picks are added
301
+ * under `interactivePicks` and delivered to the game on open.
302
+ */
303
+ entryPayload?: Record<string, unknown>;
304
+ } & ({
305
+ scheduledAt: Date;
306
+ scheduledInDays?: never;
307
+ } | {
308
+ scheduledAt?: never;
309
+ scheduledInDays: number;
310
+ });
311
+ /**
312
+ * Notifications — schedule SMS / RCS / Library re-engagement
313
+ * messages for the current player.
314
+ *
315
+ * Only registered players receive notifications. Check via
316
+ * `getPlayer().registered` before scheduling — calls for guest
317
+ * players are valid but won't be delivered.
318
+ *
319
+ * Delivery: scheduled notifications appear in the platform Library tab.
320
+ * Once per day the platform also selects at most one notification per
321
+ * user across all games to deliver as SMS/RCS, weighted by `priority`.
322
+ * Delivery time is platform-determined per user, respecting compliance
323
+ * (quiet hours, opt-outs).
324
+ *
325
+ * Jest handles consent, opt-outs, and messaging cost subsidization.
326
+ *
327
+ * Docs: https://docs.jest.com/sdk/html5/notifications
328
+ */
329
+ export interface NotificationsModule {
330
+ /**
331
+ * Schedules a notification for the current player.
332
+ *
333
+ * Provide either `scheduledAt` (exact time) or `scheduledInDays`
334
+ * (fuzzy timing — the platform picks an optimal delivery time per
335
+ * user), but not both.
336
+ *
337
+ * Scheduling with an existing `identifier` automatically replaces
338
+ * the previous notification — no need to unschedule first.
339
+ *
340
+ * Constraints (out-of-range values throw `INVALID_ARGUMENTS`):
341
+ * - `scheduledAt` must be within the next 7 days
342
+ * - `scheduledInDays` must be an integer 1–7 (inclusive)
343
+ * - `ctaText` must be 1–25 characters
344
+ *
345
+ * Use exact scheduling for fixed events/deadlines. Use fuzzy
346
+ * scheduling for time-of-day-flexible re-engagement (lets the
347
+ * platform optimize delivery).
348
+ *
349
+ * See {@link ScheduleNotificationOptions} for field-level docs.
350
+ *
351
+ * @throws {Error} If the SDK is not initialized or arguments are invalid.
352
+ * @example
353
+ * ```typescript
354
+ * // Fuzzy: platform picks optimal time within day 2
355
+ * JestSDK.notifications.scheduleNotification({
356
+ * identifier: "retention_d2",
357
+ * scheduledInDays: 2,
358
+ * body: "Your crops are ready to harvest",
359
+ * ctaText: "Play",
360
+ * priority: "medium",
361
+ * entryPayload: { source: "retention_d2" },
362
+ * });
363
+ * ```
364
+ */
365
+ scheduleNotification(options: ScheduleNotificationOptions): void;
366
+ /**
367
+ * Cancels a previously scheduled notification by identifier.
368
+ *
369
+ * Safe to call with an unknown identifier (no-op).
370
+ *
371
+ * @throws {Error} If the SDK is not initialized or arguments are invalid.
372
+ */
373
+ unscheduleNotification(options: {
374
+ identifier: string;
375
+ }): void;
376
+ /** Experimental, unstable notification APIs. */
377
+ experimental: ExperimentalNotificationsModule;
378
+ }
379
+ /**
380
+ * Experimental notification APIs under `JestSDK.notifications.experimental`.
381
+ *
382
+ * These are unstable and may change without a major version bump.
383
+ */
384
+ export interface ExperimentalNotificationsModule {
385
+ /**
386
+ * Schedules an interactive (multi-message) notification: a sequence of
387
+ * messages with reply chips that advance in the messaging thread as the
388
+ * player taps. The gathered picks are delivered to the game via
389
+ * `entryPayload.interactivePicks` when the player opens the game.
390
+ *
391
+ * Provide either `scheduledAt` or `scheduledInDays` (not both). Tapping a
392
+ * chip whose `key` matches a message's `key` branches to that message,
393
+ * otherwise the flow advances to the next message. A message with no options
394
+ * is terminal — it ends the branch and carries the "continue in browser"
395
+ * link (`ctaText`); the last message must be terminal.
396
+ *
397
+ * See {@link ScheduleInteractiveNotificationOptions} for field-level docs.
398
+ *
399
+ * @throws {Error} If the SDK is not initialized or arguments are invalid.
400
+ * @example
401
+ * ```typescript
402
+ * JestSDK.notifications.experimental.scheduleInteractiveNotification({
403
+ * identifier: "tot_daily",
404
+ * scheduledInDays: 1,
405
+ * messages: [
406
+ * {
407
+ * key: "q1",
408
+ * body: "Is it Synthetic or Natural?",
409
+ * options: [
410
+ * { key: "synthetic", label: "Synthetic" },
411
+ * { key: "natural", label: "Natural" },
412
+ * { key: "done", label: "Play in browser" },
413
+ * ],
414
+ * },
415
+ * { key: "done", body: "Keep guessing in the browser", ctaText: "Play" },
416
+ * ],
417
+ * });
418
+ * ```
419
+ */
420
+ scheduleInteractiveNotification(options: ScheduleInteractiveNotificationOptions): void;
421
+ }
422
+ /**
423
+ * Referrals — invite friends via shareable links and track which
424
+ * conversions came from which campaign.
425
+ *
426
+ * Conversions are grouped by `reference`, a stable campaign key
427
+ * you choose (e.g. `"unlock_party_mode_v1"`, `"share_score"`).
428
+ * Only invited players who complete registration count.
429
+ *
430
+ * Both methods work for guest and registered players.
431
+ *
432
+ * For high-stakes rewards (entitlements, currency), verify
433
+ * `referralsSigned` server-side instead of trusting the client.
434
+ *
435
+ * Docs: https://docs.jest.com/sdk/html5/referrals
436
+ */
437
+ export interface ReferralsModule {
438
+ /**
439
+ * Returns all referral conversions for the current player,
440
+ * grouped by `reference`.
441
+ *
442
+ * @returns
443
+ * - `referrals` — map of reference → array of `{ playerId, joinedAt }`
444
+ * (where `joinedAt` is an ISO 8601 timestamp).
445
+ * - `referralsSigned` — HS256 JWS for server-side verification,
446
+ * signed with the game's shared secret. Verified payload shape:
447
+ * `{ referrals, aud: gameId, sub: referrerPlayerId }`.
448
+ */
449
+ listReferrals: () => Promise<{
450
+ referrals: {
451
+ [reference: string]: {
452
+ playerId: string;
453
+ joinedAt: string;
454
+ }[];
455
+ };
456
+ referralsSigned: string;
457
+ }>;
458
+ /**
459
+ * Opens the platform share dialog with a referral link.
460
+ *
461
+ * Only opens the dialog — does NOT guarantee the player completes
462
+ * the share. The promise resolves once the dialog closes.
463
+ *
464
+ * @param opts.reference - Stable campaign key for grouping conversions
465
+ * (e.g. `"unlock_party_mode_v1"`).
466
+ * @param opts.entryPayload - Metadata embedded into the shared link,
467
+ * delivered to the invited player via `getEntryPayload()`.
468
+ * Useful for attribution, custom invite context, etc.
469
+ * @param opts.shareTitle - Title shown in the share sheet (platform-dependent).
470
+ * @param opts.shareText - Body text shown in the share sheet.
471
+ * @param opts.onboardingSlug - Optional game slug to route invited
472
+ * players through an onboarding game first.
473
+ * @param opts.notificationTemplates - Optional templates used to notify
474
+ * the referrer when invited players convert. Each template applies
475
+ * above its `minConversionCount` threshold; the server picks the
476
+ * template with the highest matching threshold and a variant from
477
+ * within it.
478
+ * @param opts.shareImage - Optional **base64 data URL** of an image (e.g.
479
+ * from `canvas.toDataURL("image/png")`) to use as the OG image on the
480
+ * referral's landing page — so messaging-app previews, social cards,
481
+ * etc. show the personalized image instead of the game's static share
482
+ * image. The platform decodes, hashes, deduplicates, hosts the bytes
483
+ * on its CDN, and stores the resulting URL on the referral. Accepted
484
+ * MIME: `image/png`, `image/jpeg`, `image/webp`. The data URL must be
485
+ * at most 2 MB.
486
+ * @returns `canceled: true` if the player dismissed the dialog.
487
+ *
488
+ * @example Share + check conversions later
489
+ * ```typescript
490
+ * // When the player taps "Invite friends":
491
+ * await JestSDK.referrals.shareReferralLink({
492
+ * reference: "unlock_party_mode_v1",
493
+ * shareTitle: "Come play this with me",
494
+ * shareText: "Join me — I want to unlock Party Mode.",
495
+ * });
496
+ *
497
+ * // Later (e.g. on resume): check how many invites converted
498
+ * const { referrals } = await JestSDK.referrals.listReferrals();
499
+ * const inviteCount = (referrals["unlock_party_mode_v1"] ?? []).length;
500
+ * if (inviteCount >= 3) {
501
+ * unlockPartyMode();
502
+ * }
503
+ * ```
504
+ *
505
+ * @throws {Error} If the SDK is not initialized or arguments are invalid.
506
+ */
507
+ shareReferralLink: (opts: {
508
+ reference: string;
509
+ entryPayload?: Record<string, unknown>;
510
+ shareTitle?: string;
511
+ shareText?: string;
512
+ onboardingSlug?: string;
513
+ notificationTemplates?: ReferralNotificationTemplate[];
514
+ shareImage?: string;
515
+ }) => Promise<{
516
+ canceled: boolean;
517
+ }>;
518
+ }
519
+ /**
520
+ * Social - create social interactions between players.
521
+ *
522
+ * Get the profile of the current player if available. Only registered
523
+ * users will have a profile and only after they have set it up.
524
+ *
525
+ * In case you need to fill in gaps with bots, we provide avatars generated by
526
+ * the platform. Those are deterministically picked based on the provided username.
527
+ * The avatars can be scaled to the requested size to reduce network usage.
528
+ *
529
+ * Docs: https://docs.jest.com/sdk/html5/social
530
+ */
531
+ export interface SocialModule {
532
+ /**
533
+ * Returns the profile of the currently logged in player.
534
+ * @param opts - Options for getting the profile
535
+ * @param opts.avatarSize - The size for width and height of the avatar
536
+ * @returns The profile of the currently logged in player
537
+ * @throws {Error} If the SDK is not initialized.
538
+ */
539
+ getProfile: (opts?: {
540
+ avatarSize: AvatarSize;
541
+ }) => PlayerProfile | null;
542
+ /**
543
+ * Gets the avatar URL for a bot given its username
544
+ * @param opts - Options for getting the avatar URL
545
+ * @param opts.size - The size for width and height of the avatar in pixels
546
+ * @param opts.username - The username of the bot used as a seed for the avatar
547
+ * @returns The generated avatar URL
548
+ * @example
549
+ * ```typescript
550
+ * const avatarUrl = sdk.getBotAvatar({ size: 128, username: "bot" });
551
+ * ```
552
+ */
553
+ getBotAvatar(opts: {
554
+ size?: AvatarSize;
555
+ username: string;
556
+ }): string;
557
+ /**
558
+ * Registers a custom screenshot source used when the platform asks the
559
+ * game for a screenshot, replacing the SDK's automatic canvas capture.
560
+ * The provider may be async. Return the screenshot as a base64-encoded PNG
561
+ * (raw or data URL), or null when no screenshot is available (e.g.
562
+ * mid-load or on a sensitive screen). Pass null to unregister the provider
563
+ * and restore automatic capture.
564
+ * @param provider - The screenshot source, or null to unregister.
565
+ * @example
566
+ * ```typescript
567
+ * JestSDK.social.setScreenshotProvider(() => {
568
+ * return myEngine.captureScreenshotAsBase64Png() ?? null;
569
+ * });
570
+ * ```
571
+ */
572
+ setScreenshotProvider(provider: (() => string | null | Promise<string | null>) | null): void;
573
+ }
574
+ /**
575
+ * Payments — sell in-game products in USD.
576
+ *
577
+ * Players purchase products you configure in the Developer Console
578
+ * directly in USD on the Jest platform. Jest handles checkout
579
+ * end-to-end with the player.
580
+ *
581
+ * **Purchase lifecycle:**
582
+ * 1. List products via {@link PaymentsModule.getProducts | getProducts}.
583
+ * 2. Start checkout via {@link PaymentsModule.beginPurchase | beginPurchase}.
584
+ * 3. Grant the item to the player.
585
+ * 4. Confirm via {@link PaymentsModule.completePurchase | completePurchase}.
586
+ * 5. On startup, recover incomplete purchases via {@link PaymentsModule.getIncompletePurchases | getIncompletePurchases}.
587
+ *
588
+ * **Critical: always grant before confirming.** If the game crashes
589
+ * after confirming but before granting, the purchase can't be
590
+ * recovered (it's no longer incomplete).
591
+ *
592
+ * **Server-side verification (recommended):** for any grant that
593
+ * affects entitlements or currency, send `purchaseSigned` /
594
+ * `purchasesSigned` to your backend, verify the HS256 JWS with your
595
+ * shared secret, and use `purchaseToken` as an idempotency key.
596
+ *
597
+ * **Sandbox testing:** sandbox users see real product prices in the
598
+ * game UI, but the platform checkout modal makes clear that no charge
599
+ * will be made and the resulting purchase records 0 credits. Such a
600
+ * purchase carries `sandbox: true` — in the SDK payload and in the
601
+ * signed JWS — so your backend can grant the item while keeping test
602
+ * traffic out of revenue reporting. Purchases driven from the Developer
603
+ * Console simulator carry the same flag, at their configured price.
604
+ *
605
+ * Docs: https://docs.jest.com/sdk/html5/payments
606
+ */
607
+ export interface PaymentsModule {
608
+ /**
609
+ * Lists products available for purchase, configured in the
610
+ * Developer Console.
611
+ *
612
+ * @returns Array of `{ sku, name, description, price, currency }` where
613
+ * `price` is in the specified `currency` (typically USD).
614
+ * @throws {Error} If the SDK is not initialized or the request fails.
615
+ */
616
+ getProducts(): Promise<Array<{
617
+ sku: string;
618
+ name: string;
619
+ description: string | null;
620
+ price: number;
621
+ currency: string;
622
+ }>>;
623
+ /**
624
+ * Starts the platform checkout flow for a product.
625
+ *
626
+ * On success, the returned purchase is **incomplete** — your game
627
+ * must grant the item and then call `completePurchase`.
628
+ *
629
+ * @param options.productSku - SKU from `getProducts()`.
630
+ * @returns One of:
631
+ * - `{ result: "success", purchase, purchaseSigned }` — checkout
632
+ * completed. Use `purchaseSigned` for server-side verification.
633
+ * - `{ result: "cancel" }` — player canceled the flow.
634
+ * - `{ result: "error", error: "internal_error" }` — transient;
635
+ * safe to retry.
636
+ * - `{ result: "error", error: "invalid_product" }` — SKU not
637
+ * available; do NOT retry with the same SKU.
638
+ *
639
+ * May also throw on transient errors (e.g. timeout) — treat
640
+ * thrown errors as retryable.
641
+ *
642
+ * @example Full purchase flow (grant before confirm)
643
+ * ```typescript
644
+ * const result = await JestSDK.payments.beginPurchase({
645
+ * productSku: "powerup_pack_1",
646
+ * });
647
+ *
648
+ * if (result.result !== "success") {
649
+ * if (result.result === "error") {
650
+ * console.error("Purchase failed:", result.error);
651
+ * }
652
+ * return;
653
+ * }
654
+ *
655
+ * // Recommended: send result.purchaseSigned to your backend to verify
656
+ * // and grant. Use result.purchase.purchaseToken as an idempotency key.
657
+ * await grantItem(result.purchase.productSku);
658
+ *
659
+ * // Only confirm AFTER granting succeeded.
660
+ * await JestSDK.payments.completePurchase({
661
+ * purchaseToken: result.purchase.purchaseToken,
662
+ * });
663
+ * ```
664
+ *
665
+ * @throws {Error} If the SDK is not initialized or arguments are invalid.
666
+ */
667
+ beginPurchase(options: {
668
+ productSku: string;
669
+ }): Promise<{
670
+ result: "success";
671
+ purchase: PurchaseData;
672
+ purchaseSigned: string;
673
+ } | {
674
+ result: "cancel";
675
+ } | {
676
+ result: "error";
677
+ error: "internal_error" | "invalid_product";
678
+ }>;
679
+ /**
680
+ * Confirms a purchase, marking it complete on the platform.
681
+ *
682
+ * **Only call this AFTER the item has been durably granted.**
683
+ * If you confirm first and crash before granting, the purchase
684
+ * can't be recovered.
685
+ *
686
+ * @param options.purchaseToken - Token from `beginPurchase` or
687
+ * `getIncompletePurchases`.
688
+ * @returns `{ result: "success" }` or:
689
+ * - `error: "internal_error"` — transient; retry later. Leaving
690
+ * the purchase incomplete is safe; it will reappear in
691
+ * `getIncompletePurchases`.
692
+ * - `error: "invalid_token"` — already confirmed, wrong player,
693
+ * etc. Do NOT retry with the same token.
694
+ *
695
+ * @throws {Error} If the SDK is not initialized or arguments are invalid.
696
+ */
697
+ completePurchase(options: {
698
+ purchaseToken: string;
699
+ }): Promise<{
700
+ result: "success";
701
+ } | {
702
+ result: "error";
703
+ error: "internal_error" | "invalid_token";
704
+ }>;
705
+ /**
706
+ * Starts the platform checkout flow for a subscription.
707
+ *
708
+ * @param options.subscriptionSku - SKU of the subscription to subscribe to.
709
+ * @returns One of:
710
+ * - `{ result: "success", subscription, subscriptionSigned }` — subscribed.
711
+ * - `{ result: "cancel" }` — player canceled the flow.
712
+ * - `{ result: "error", error }` — see error code for details.
713
+ *
714
+ * @throws {Error} If the SDK is not initialized or arguments are invalid.
715
+ */
716
+ beginSubscription(options: {
717
+ subscriptionSku: string;
718
+ }): Promise<{
719
+ result: "success";
720
+ subscription: SubscriptionData;
721
+ subscriptionSigned: string;
722
+ } | {
723
+ result: "cancel";
724
+ } | {
725
+ result: "error";
726
+ error: "internal_error" | "invalid_subscription" | "already_subscribed" | "guest_not_allowed";
727
+ }>;
728
+ /**
729
+ * Returns purchases that started checkout but were never confirmed.
730
+ *
731
+ * **Call this on every startup** to handle purchases that succeeded
732
+ * at checkout but never reached `completePurchase` (e.g. due to a
733
+ * crash, network failure, or app close).
734
+ *
735
+ * For each returned purchase: grant the item (using `productSku`),
736
+ * then call `completePurchase`. The response is capped at 50
737
+ * purchases — if `hasMore` is true, call again until it's false.
738
+ *
739
+ * Use `purchasesSigned` for server-side verification before granting.
740
+ *
741
+ * @example Recovery loop on startup
742
+ * ```typescript
743
+ * let hasMore = true;
744
+ * while (hasMore) {
745
+ * const result = await JestSDK.payments.getIncompletePurchases();
746
+ *
747
+ * for (const purchase of result.purchases) {
748
+ * // Recommended: verify result.purchasesSigned on your backend
749
+ * await grantItem(purchase.productSku);
750
+ *
751
+ * await JestSDK.payments.completePurchase({
752
+ * purchaseToken: purchase.purchaseToken,
753
+ * });
754
+ * }
755
+ *
756
+ * hasMore = result.hasMore;
757
+ * }
758
+ * ```
759
+ */
760
+ getIncompletePurchases(): Promise<{
761
+ hasMore: boolean;
762
+ purchases: Array<PurchaseData>;
763
+ purchasesSigned: string;
764
+ }>;
765
+ /**
766
+ * Lists subscription offers for this game along with the player's
767
+ * current entitlement on each one.
768
+ *
769
+ * Subscriptions are configured per-game in the Developer Console and
770
+ * billed in USD on a recurring cadence (currently monthly only).
771
+ *
772
+ * Each entry's `active` field is `"active"` if the player currently
773
+ * holds the subscription and `"inactive"` otherwise. Grant the
774
+ * entitlement when `active === "active"`.
775
+ *
776
+ * Each entry also carries `trialEligible`: `true` only when the offer
777
+ * has a free trial and this wallet has never subscribed to it, so you
778
+ * can show a "Start free trial" call to action only when it applies.
779
+ *
780
+ * Similarly, `introOffer` is non-null only when the offer has an
781
+ * introductory price and this wallet has never subscribed to it: the
782
+ * player pays `introOffer.price` for the first
783
+ * `introOffer.durationPeriods` billing periods, then the standard
784
+ * `price` applies automatically.
785
+ *
786
+ * **Server-side verification (recommended):** for any grant tied to a
787
+ * subscription, send `signed` to your backend and verify the HS256
788
+ * JWS with your shared game secret. The signed payload carries the
789
+ * same `subscriptions` array.
790
+ *
791
+ * For sandbox users, and in the Developer Console simulator, every
792
+ * entry carries `sandbox: true`: `price` shows as configured, but any
793
+ * subscription started that way bills nothing. A sandbox user never
794
+ * gets `introOffer` or `retentionOffer` — a checkout already forced to
795
+ * 0 carries no discount.
796
+ *
797
+ * For guest players, `subscriptions` is an empty array.
798
+ *
799
+ * @throws {Error} If the SDK is not initialized or the request fails.
800
+ */
801
+ getSubscriptions(): Promise<{
802
+ subscriptions: SubscriptionData[];
803
+ signed: string;
804
+ }>;
805
+ /**
806
+ * Opens a cancellation confirmation dialog for the specified subscription.
807
+ *
808
+ * If the user confirms, the subscription is cancelled at the end of the
809
+ * current billing period (the user retains access until then).
810
+ *
811
+ * @param options.subscriptionSku - The SKU of the subscription to cancel.
812
+ * @returns Result indicating success, user dismissed, or an error.
813
+ * @throws {Error} If the SDK is not initialized or arguments are invalid.
814
+ */
815
+ cancelSubscription(options: {
816
+ subscriptionSku: string;
817
+ }): Promise<{
818
+ result: "success";
819
+ } | {
820
+ result: "cancel";
821
+ } | {
822
+ result: "error";
823
+ error: "internal_error" | "not_found" | "not_active" | "guest_not_allowed";
824
+ }>;
825
+ /**
826
+ * Applies the subscription's configured retention discount to the player's
827
+ * existing subscription — no checkout, applied instantly.
828
+ *
829
+ * Use this for retention flows: when an entitled player asks to cancel,
830
+ * offer the discount your subscription configures (exposed as
831
+ * `retentionOffer` on `SubscriptionData` while the player is eligible).
832
+ * On success the player's next `retentionOffer.durationPeriods` renewals
833
+ * bill at the discounted price, then the standard price returns
834
+ * automatically — same subscription, no interruption.
835
+ *
836
+ * Each player receives a subscription's retention discount only once,
837
+ * and not during a free trial or while an introductory offer window is
838
+ * still running — ineligible claims fail with `not_eligible`. Repeating
839
+ * the call for an already-claimed subscription re-confirms the same
840
+ * discount and succeeds, so retrying after an error is safe. The returned
841
+ * `subscription` reflects the post-claim state (`retentionOffer` becomes
842
+ * `null`).
843
+ *
844
+ * @param options.subscriptionSku - SKU of the subscription the player holds.
845
+ * @returns Result with the refreshed subscription data, or an error.
846
+ * @throws {Error} If the SDK is not initialized or arguments are invalid.
847
+ */
848
+ claimRetentionOffer(options: {
849
+ subscriptionSku: string;
850
+ }): Promise<{
851
+ result: "success";
852
+ subscription: SubscriptionData;
853
+ subscriptionSigned: string;
854
+ } | {
855
+ result: "error";
856
+ error: "internal_error" | "not_eligible" | "guest_not_allowed";
857
+ }>;
858
+ }
859
+ export type Jest = JestSDK & {
860
+ /**
861
+ * Initializes the SDK. Must be called before any other SDK method.
862
+ * Safe to call multiple times — subsequent calls return the same
863
+ * ready promise.
864
+ *
865
+ * @example Typical app startup
866
+ * ```typescript
867
+ * await JestSDK.init();
868
+ *
869
+ * const player = JestSDK.getPlayer();
870
+ * if (!player.registered) {
871
+ * // Optionally prompt registration at the right moment
872
+ * }
873
+ *
874
+ * // Recover any purchases from a previous session that crashed
875
+ * await recoverPurchasesOnStartup();
876
+ *
877
+ * startGame();
878
+ * ```
879
+ */
880
+ init: (opts?: {
881
+ /**
882
+ * When set to false, disables the automatic login reminder popups that appear
883
+ * at escalating intervals for guest users. Manual login via `JestSDK.login()`
884
+ * is unaffected. Defaults to true.
885
+ */
886
+ autoLoginReminders?: boolean;
887
+ /**
888
+ * @deprecated mock is no longer used - it is automatically determined based on whether the app is run inside Jest or as standalone.
889
+ */
890
+ mock?: boolean;
891
+ }) => Promise<void>;
892
+ };
893
+ /**
894
+ * The JestSDK runtime API, exposed as a global when the SDK script is
895
+ * loaded from `https://cdn.jest.com/sdk/latest/jestsdk.js`.
896
+ *
897
+ * Call {@link Jest.init} once on startup, then use the methods below to
898
+ * interact with the player, schedule notifications, sell products, and
899
+ * track referrals. Most methods throw if called before initialization.
900
+ *
901
+ * Docs: https://docs.jest.com/sdk/html5
902
+ */
903
+ export interface JestSDK {
904
+ /**
905
+ * Resolves once the SDK is fully initialized and player data is loaded.
906
+ *
907
+ * `init()` already returns this promise — call `isReady()` only when
908
+ * you need to await initialization from a different code path that
909
+ * doesn't have access to the original `init()` promise.
910
+ *
911
+ * Safe to call any time after `init()` has been invoked, including before
912
+ * `init()` resolves; it chains off the same in-flight initialization.
913
+ *
914
+ * @throws {Error} If initialization times out or `init()` was never called.
915
+ */
916
+ isReady(): Promise<void>;
917
+ /**
918
+ * Returns the entry payload for this game session, or an empty object
919
+ * if no payload was supplied.
920
+ *
921
+ * The entry payload is arbitrary metadata attached to the link the
922
+ * player used to enter the game. Common sources include:
923
+ * - Referral links (from `referrals.shareReferralLink`)
924
+ * - Notification links (from `notifications.scheduleNotification`)
925
+ * - Onboarding game handoffs
926
+ *
927
+ * Typical use cases: difficulty selection, referral attribution,
928
+ * restoring context after registration, A/B test variants.
929
+ *
930
+ * @returns The entry payload as a JSON object (always a `Record`, never null).
931
+ * @throws {Error} If the SDK is not initialized.
932
+ * @example
933
+ * ```typescript
934
+ * const payload = JestSDK.getEntryPayload();
935
+ * const difficulty = payload.difficulty ?? "normal";
936
+ * ```
937
+ */
938
+ getEntryPayload(): Record<string, unknown>;
939
+ /**
940
+ * Returns the current player's identity.
941
+ *
942
+ * Each player has a `playerId` that is stable per-game and persists
943
+ * across sessions and devices, including when a guest later registers.
944
+ * Use this to key your own player state.
945
+ *
946
+ * - `registered: false` — guest player. Cannot receive notifications.
947
+ * Prompt registration via `login()` or `showRegistrationOverlay()`.
948
+ * - `registered: true` — has a Jest account. `username` and `avatarUrl`
949
+ * are platform values you may use in your UI.
950
+ *
951
+ * @returns The player object. `username`/`avatarUrl` are `null` for guests.
952
+ * @throws {Error} If the SDK is not initialized.
953
+ * @example
954
+ * ```typescript
955
+ * const player = JestSDK.getPlayer();
956
+ * if (!player.registered) {
957
+ * // Guest — consider prompting registration
958
+ * }
959
+ * ```
960
+ */
961
+ getPlayer(): {
962
+ playerId: string;
963
+ registered: boolean;
964
+ username: string | null;
965
+ avatarUrl: string | null;
966
+ };
967
+ /**
968
+ * Returns a signed player payload for server-side verification.
969
+ *
970
+ * Use this when your game has a backend and needs to authenticate
971
+ * the player for server requests. `playerSigned` is a JWS (HS256)
972
+ * signed with your game's shared secret (configured in the Developer
973
+ * Console → Games → Secrets).
974
+ *
975
+ * Verify it on your backend with any standard JWT library. The
976
+ * decoded payload has the shape:
977
+ * ```ts
978
+ * {
979
+ * player: { playerId, registered, username, avatarUrl };
980
+ * iat: number; // issued-at timestamp
981
+ * aud: string; // game id
982
+ * sub: string; // player id
983
+ * }
984
+ * ```
985
+ *
986
+ * Jest does not set an explicit expiration; reject tokens older than
987
+ * a chosen threshold (e.g. 24h) and request a new one when needed.
988
+ *
989
+ * Works for both registered and guest players.
990
+ *
991
+ * Docs: https://docs.jest.com/sdk/html5/player#jestsdkgetplayersigned
992
+ *
993
+ * @example Authenticate a backend request
994
+ * ```typescript
995
+ * const { playerSigned } = await JestSDK.getPlayerSigned();
996
+ * await fetch("/api/save-progress", {
997
+ * method: "POST",
998
+ * headers: { authorization: `Bearer ${playerSigned}` },
999
+ * body: JSON.stringify({ score: 1500 }),
1000
+ * });
1001
+ * // Server verifies playerSigned (HS256, game's shared secret)
1002
+ * // before trusting the request.
1003
+ * ```
1004
+ *
1005
+ * @throws {Error} If the SDK is not initialized or the request fails.
1006
+ */
1007
+ getPlayerSigned(): Promise<{
1008
+ player: {
1009
+ playerId: string;
1010
+ registered: boolean;
1011
+ username: string | null;
1012
+ avatarUrl: string | null;
1013
+ };
1014
+ playerSigned: string;
1015
+ }>;
1016
+ /**
1017
+ * Reports loading progress (0–100) to the platform loading overlay.
1018
+ *
1019
+ * Only takes effect when the game's loading screen mode is set to
1020
+ * "Manual" in the Developer Console. In Manual mode, the overlay is
1021
+ * shown automatically on game entry; the game is responsible for
1022
+ * progress and dismissal.
1023
+ *
1024
+ * - Values outside 0–100 are clamped; non-integers are rounded.
1025
+ * - Reaching 100 dismisses the overlay with a fade-out.
1026
+ * - Safety timeout: if no progress update is received for 15 seconds,
1027
+ * the platform exits the player to the home screen. Each call
1028
+ * resets this timer.
1029
+ *
1030
+ * Docs: https://docs.jest.com/sdk/html5/loading-screen
1031
+ *
1032
+ * @param progress - Loading progress from 0 to 100.
1033
+ */
1034
+ setLoadingProgress(progress: number): void;
1035
+ /**
1036
+ * Signals to the Jest platform that the game is ready to be played —
1037
+ * all assets have loaded, initialization is complete, and the player
1038
+ * can now interact.
1039
+ *
1040
+ * For games in the Jest Fund, this is also an important signal that
1041
+ * lets the platform analyze the behavior of the traffic it sends to
1042
+ * the game.
1043
+ *
1044
+ * Call it the moment the player can start playing. Do not wait for
1045
+ * optional or user-gated post-init asset downloads; including that time
1046
+ * would inflate the measurement with the player's own reaction time.
1047
+ * Stream such assets in the background instead.
1048
+ *
1049
+ * Safe to call at any time, regardless of loading-screen mode; calls
1050
+ * after the first are no-ops.
1051
+ *
1052
+ * Also dismisses the manual loading overlay, as if
1053
+ * {@link setLoadingProgress} had been called with `100` — unless the game
1054
+ * already drove progress to 100 itself.
1055
+ */
1056
+ markGameLoaded(): void;
1057
+ /**
1058
+ * Reports that the player reached your game's first meaningful
1059
+ * milestone — the earliest action that demonstrates the player has
1060
+ * experienced the core value of your game.
1061
+ *
1062
+ * You define what the milestone is. Good examples:
1063
+ * - Completing the first-time user experience (FTUE) or tutorial
1064
+ * - Completing Level 1
1065
+ * - Completing the first puzzle, match, or equivalent core gameplay
1066
+ * objective
1067
+ *
1068
+ * Call it every time the milestone is reached — you don't need to track
1069
+ * whether it was already reported. Calls after the first in a session are
1070
+ * no-ops, and the platform measures milestone reach as unique players.
1071
+ *
1072
+ * Implementing this event is required for launch. It gives the platform
1073
+ * a standardized early-engagement signal used to evaluate and support
1074
+ * games.
1075
+ *
1076
+ * Docs: https://docs.jest.com/launch-checklist
1077
+ */
1078
+ markFirstMilestone(): void;
1079
+ /**
1080
+ * Opens the platform's built-in registration popup.
1081
+ *
1082
+ * The flow is completed via SMS/RCS, then the player is redirected
1083
+ * back into the game. Converting guests to registered players is
1084
+ * critical for retention — registered players can receive
1085
+ * notifications and won't lose progress when the session ends.
1086
+ *
1087
+ * Use this for the simplest integration; use
1088
+ * {@link showRegistrationOverlay} for a fully custom UI.
1089
+ *
1090
+ * The platform also triggers automatic registration prompts at
1091
+ * escalating intervals; opt out via `init({ autoLoginReminders: false })`.
1092
+ *
1093
+ * @param opts.entryPayload - Optional metadata embedded in the login link.
1094
+ * Available via {@link getEntryPayload} after the player returns.
1095
+ * Useful for tracking where login was initiated.
1096
+ * @returns A promise that resolves once the login popup is dismissed.
1097
+ * Resolves immediately if the player is already registered.
1098
+ * @throws {Error} If the SDK is not initialized or the entry payload is
1099
+ * invalid.
1100
+ */
1101
+ login(opts?: {
1102
+ entryPayload?: Record<string, unknown>;
1103
+ }): Promise<void>;
1104
+ /**
1105
+ * Shows a minimal registration overlay and returns action handlers
1106
+ * for the game to wire into its own UI.
1107
+ *
1108
+ * This is the customizable alternative to {@link login}. The platform
1109
+ * still renders the required legal text and a close button, but the
1110
+ * game owns the rest of the UI (e.g. positioning, copy, buttons).
1111
+ *
1112
+ * @param opts.theme - "light" or "dark". Defaults to "dark".
1113
+ * @param opts.onClose - Called when the overlay is dismissed
1114
+ * (either via the built-in close button or `closeButtonAction`).
1115
+ * @param opts.entryPayload - Optional metadata embedded in the login
1116
+ * link, available via {@link getEntryPayload} after registration.
1117
+ * @param opts.message - Optional text the player's messaging app is
1118
+ * pre-filled with, in place of the platform's default wording. Must
1119
+ * contain `{{registrationCode}}` exactly once, with a space or
1120
+ * punctuation around it — the platform swaps in a one-time code there,
1121
+ * and that code is how the reply is matched back to this player. Keep the
1122
+ * message under 140 characters so it stays a single text. Emoji and
1123
+ * accented characters cost far more room, so past 70 characters they are
1124
+ * dropped from the pre-filled text rather than splitting it in two.
1125
+ * @returns Two functions to wire into your in-game UI:
1126
+ * - `loginButtonAction()` — starts the platform login flow
1127
+ * - `closeButtonAction()` — closes the overlay
1128
+ *
1129
+ * @example Wire actions to in-game UI
1130
+ * ```typescript
1131
+ * if (!JestSDK.getPlayer().registered) {
1132
+ * const { loginButtonAction, closeButtonAction } =
1133
+ * JestSDK.showRegistrationOverlay({
1134
+ * theme: "light",
1135
+ * message: "Let me into Dungeon Crawl! {{registrationCode}} is my code.",
1136
+ * onClose: () => closeGamePopup(),
1137
+ * });
1138
+ *
1139
+ * myLoginButton.onclick = loginButtonAction;
1140
+ * myCloseButton.onclick = closeButtonAction;
1141
+ * }
1142
+ * ```
1143
+ *
1144
+ * @throws {Error} If the SDK is not initialized or arguments are invalid.
1145
+ */
1146
+ showRegistrationOverlay(opts?: {
1147
+ theme?: "light" | "dark";
1148
+ onClose?: () => void;
1149
+ entryPayload?: Record<string, unknown>;
1150
+ message?: string;
1151
+ }): {
1152
+ loginButtonAction: () => void;
1153
+ closeButtonAction: () => void;
1154
+ };
1155
+ /**
1156
+ * Records a custom analytics event for the current player.
1157
+ *
1158
+ * Events are visible in the Developer Console and can be used to
1159
+ * track in-game milestones, funnel steps, and feature usage.
1160
+ *
1161
+ * Property values should be JSON-serializable primitives or simple
1162
+ * objects. Avoid storing PII or sensitive data in event properties.
1163
+ *
1164
+ * @param eventName - Name of the event (e.g. `"level_complete"`).
1165
+ * Use stable, lowercase, snake_case names.
1166
+ * @param properties - Optional structured data attached to the event
1167
+ * (e.g. `{ level: 5, score: 1200 }`).
1168
+ * @throws {Error} If the SDK is not initialized or arguments are invalid.
1169
+ */
1170
+ captureEvent(eventName: string, properties?: Record<string, unknown>): void;
1171
+ /**
1172
+ * Redirects the player to another game owned by the same team as the
1173
+ * calling game. The platform performs the navigation after verifying
1174
+ * team ownership; redirects to other teams' games are rejected.
1175
+ *
1176
+ * @param options.gameSlug - Slug of the target game (the `/g/<slug>`
1177
+ * part of its URL).
1178
+ * @param options.entryPayload - Optional metadata delivered to the
1179
+ * target game via {@link getEntryPayload}.
1180
+ * @param options.skipGameExitConfirm - When `true`, skips the exit
1181
+ * confirmation dialog. Defaults to `false`.
1182
+ * @returns `{ result: "success" }` once navigation begins, or
1183
+ * `{ result: "error", error }` with `"game_not_found"`,
1184
+ * `"different_team"`, or `"internal_error"` (transient; safe to retry).
1185
+ *
1186
+ * Docs: https://docs.jest.com/sdk/html5/game-redirects
1187
+ *
1188
+ * @example
1189
+ * ```typescript
1190
+ * const result = await JestSDK.redirectToTeamGame({
1191
+ * gameSlug: "trivia-battle",
1192
+ * entryPayload: { source: "cross_promo_button" },
1193
+ * });
1194
+ * if (result.result === "error") {
1195
+ * console.error("Redirect failed:", result.error);
1196
+ * }
1197
+ * ```
1198
+ *
1199
+ * @throws {Error} If the SDK is not initialized or arguments are invalid.
1200
+ */
1201
+ redirectToTeamGame(options: {
1202
+ gameSlug: string;
1203
+ entryPayload?: Record<string, unknown>;
1204
+ skipGameExitConfirm?: boolean;
1205
+ }): Promise<{
1206
+ result: "success";
1207
+ } | {
1208
+ result: "error";
1209
+ error: RedirectToTeamGameErrorCode;
1210
+ }>;
1211
+ /** App visibility and platform exit events. */
1212
+ lifecycle: LifecycleModule;
1213
+ /** Player data — see {@link PlayerDataModule}. */
1214
+ data: PlayerDataModule;
1215
+ /** Notifications — see {@link NotificationsModule}. */
1216
+ notifications: NotificationsModule;
1217
+ /** Referrals — see {@link ReferralsModule}. */
1218
+ referrals: ReferralsModule;
1219
+ /** Payments — see {@link PaymentsModule}. */
1220
+ payments: PaymentsModule;
1221
+ /** Social - see {@link SocialModule} */
1222
+ social: SocialModule;
1223
+ }
1224
+ /**
1225
+ * The global JestSDK singleton — the entry point for all SDK usage in
1226
+ * HTML5 games.
1227
+ *
1228
+ * Available as `window.JestSDK` after loading the SDK script:
1229
+ * ```html
1230
+ * <script src="https://cdn.jest.com/sdk/latest/jestsdk.js"></script>
1231
+ * ```
1232
+ *
1233
+ * Call `JestSDK.init()` first; then use the methods documented on the
1234
+ * {@link JestSDK} interface (e.g. `JestSDK.getPlayer()`,
1235
+ * `JestSDK.notifications.scheduleNotification(...)`).
1236
+ *
1237
+ * The {@link Jest} type describes this singleton's full shape,
1238
+ * including the `init` method.
1239
+ */
1240
+ declare global {
1241
+ const JestSDK: Jest;
1242
+ interface Window {
1243
+ JestSDK: Jest;
1244
+ }
1245
+ }
1246
+
1247
+
package/package.json ADDED
@@ -0,0 +1,30 @@
1
+ {
2
+ "name": "@jest-com/types",
3
+ "version": "1.260826.3",
4
+ "description": "TypeScript types for the Jest HTML5 game SDK global (window.JestSDK).",
5
+ "keywords": [
6
+ "jest",
7
+ "jestsdk",
8
+ "html5",
9
+ "games",
10
+ "types",
11
+ "typescript"
12
+ ],
13
+ "homepage": "https://docs.jest.com/sdk/html5",
14
+ "license": "MIT",
15
+ "type": "module",
16
+ "types": "./index.d.ts",
17
+ "exports": {
18
+ ".": {
19
+ "types": "./index.d.ts"
20
+ }
21
+ },
22
+ "files": [
23
+ "index.d.ts",
24
+ "README.md"
25
+ ],
26
+ "publishConfig": {
27
+ "access": "public",
28
+ "registry": "https://registry.npmjs.org/"
29
+ }
30
+ }