@indiegems/gem-web-sdk 13.0.0 → 13.2.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.
Files changed (40) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/README.md +4 -3
  3. package/dist/function.d.ts +2 -1
  4. package/dist/game.d.ts +37 -2
  5. package/dist/gem-web-sdk.bridge.js +1 -1
  6. package/dist/gem-web-sdk.esm.js +2 -2
  7. package/dist/gem-web-sdk.function.js +2 -2
  8. package/dist/gem-web-sdk.game.js +2 -2
  9. package/dist/gem-web-sdk.min.js +2 -2
  10. package/dist/gem-web-sdk.sdk.js +2 -2
  11. package/dist/gem-web-sdk.server-api.js +1 -1
  12. package/dist/gem-web-sdk.server.js +2 -2
  13. package/dist/index.d.ts +39 -5
  14. package/dist/sdk.d.ts +37 -2
  15. package/dist/server-api.d.ts +1 -0
  16. package/dist/server.d.ts +1 -0
  17. package/docs/analytics.md +6 -3
  18. package/docs/api/api-reference-function.md +38 -7
  19. package/docs/api/api-reference-game.md +381 -33
  20. package/docs/api/api-reference-server.md +23 -2
  21. package/docs/api/reference.json +401 -25
  22. package/docs/arcade-avatars.md +194 -26
  23. package/docs/brand-art.md +3 -3
  24. package/docs/changelog.md +30 -0
  25. package/docs/deprecation-policy.md +6 -0
  26. package/docs/getting-started.md +9 -5
  27. package/docs/inventory-and-catalogs.md +1 -1
  28. package/docs/manifest.json +29 -29
  29. package/docs/platform-moments.md +266 -0
  30. package/docs/rewarded-ads.md +26 -9
  31. package/docs/server-bundles.md +2 -2
  32. package/docs/serverless-functions-js.md +17 -1
  33. package/docs/serverless-functions.md +13 -7
  34. package/docs/what-a-game-must-do.md +126 -70
  35. package/package.json +6 -2
  36. package/rigs/adult_human.glb +0 -0
  37. package/schema/bridge.v1.json +160 -7
  38. package/schema/capabilities.public.json +16 -0
  39. package/schema/moment-log-line.json +109 -0
  40. package/docs/cross-promotion.md +0 -232
@@ -1,6 +1,6 @@
1
1
  # The browser surface — API reference
2
2
 
3
- Generated from `@indiegems/gem-web-sdk/sdk` v13.0.0, the version of the package this file ships in. Every symbol below carries an explicit anchor, its `anchor` in `reference.json` beside this file.
3
+ Generated from `@indiegems/gem-web-sdk/sdk` v13.2.0, the version of the package this file ships in. Every symbol below carries an explicit anchor, its `anchor` in `reference.json` beside this file.
4
4
 
5
5
  `@indiegems/gem-web-sdk/sdk` is the one import a game writes, wherever its code runs. `@indiegems/gem-web-sdk/game` exports exactly the symbols on this page and nothing else, for a build that wants only this half.
6
6
 
@@ -286,7 +286,7 @@ the button could pay right now is [AdsApi.rewardedStatus](#adsapi-rewardedstatus
286
286
 
287
287
  Absent when your game declares none.
288
288
 
289
- **Guides.** [cross-promotion](../cross-promotion.md)
289
+ **Guides.** [rewarded-ads](../rewarded-ads.md)
290
290
 
291
291
  <a id="gameconfig-version"></a>
292
292
 
@@ -2902,6 +2902,9 @@ type AdFormat = "interstitial" | "rewarded"
2902
2902
  Which kind of break this is.
2903
2903
 
2904
2904
  `interstitial` is a panel the player dismisses and nothing follows from.
2905
+ **Deprecated:** signal a platform moment with `gem.moments.signal` instead.
2906
+ It keeps working — the arcade treats it as a platform moment — and `14.0.0`
2907
+ is the earliest release that may remove it (`docs/deprecation-policy.md`).
2905
2908
 
2906
2909
  `rewarded` is a button the player pressed for a reward, such as a revive or
2907
2910
  an extra attempt, that your game config declares for the placement in
@@ -3021,7 +3024,7 @@ Which of the three answers this is.
3021
3024
  interface AdsApi
3022
3025
  ```
3023
3026
 
3024
- The cross-promotion surface. `gem.ads` is one of these.
3027
+ Rewarded ads, and the deprecated interstitial ask. `gem.ads` is one of these.
3025
3028
 
3026
3029
  <a id="adsapi-requestad"></a>
3027
3030
 
@@ -3033,6 +3036,10 @@ requestAd(req: AdRequest): Promise<AdOutcome>
3033
3036
 
3034
3037
  Ask whether the arcade has anything to show at this break in play.
3035
3038
 
3039
+ **For a break in play, signal a platform moment with
3040
+ `gem.moments.signal` instead: `format: 'interstitial'` is deprecated.** It
3041
+ keeps working until at least `14.0.0`. A `rewarded` ask is unchanged.
3042
+
3036
3043
  NOTHING REJECTS, and the answer is usually `no_ad`. Every local refusal —
3037
3044
  an arcade that does not do this at all, a call while another is still in
3038
3045
  flight, a `placement` this module will not send — resolves `no_ad` rather
@@ -3070,16 +3077,6 @@ An `interstitial` needs no press.
3070
3077
 
3071
3078
  **Returns.** The arcade's answer. Always resolves; never rejects.
3072
3079
 
3073
- ```ts
3074
- const { outcome } = await gem.ads.requestAd({
3075
- placement: 'level-complete',
3076
- format: 'interstitial',
3077
- });
3078
- // `no_ad` is the ordinary answer. There is nothing to show and nothing
3079
- // to draw; carry straight on to the next level.
3080
- startNextLevel();
3081
- ```
3082
-
3083
3080
  ```ts
3084
3081
  // A rewarded ask, from the button's own click: the ask comes first, and
3085
3082
  // the inventory read comes after it.
@@ -3142,7 +3139,7 @@ if (status.nextEligibleAt !== undefined) {
3142
3139
  }
3143
3140
  ```
3144
3141
 
3145
- **Guides.** [cross-promotion](../cross-promotion.md)
3142
+ **Guides.** [rewarded-ads](../rewarded-ads.md)
3146
3143
 
3147
3144
  <a id="rewardedstatus"></a>
3148
3145
 
@@ -3482,7 +3479,7 @@ is what says which events exist, which dimensions each has and which values
3482
3479
  each dimension may take. An undeclared name or key is not stored — it comes
3483
3480
  back as a rejection, which the SDK prints to the console during development.
3484
3481
 
3485
- ⚠️ **Detail older than 30 days is gone.** Raw events live 30 days; the daily
3482
+ ⚠️ **Detail older than 60 days is gone.** Raw events live 60 days; the daily
3486
3483
  counters live 400. Anything you want to be able to ask about a year from now
3487
3484
  has to be a declared dimension today.
3488
3485
 
@@ -3594,9 +3591,14 @@ the page.
3594
3591
  A refusal that could clear on its own — a 429 from the rate limiter, a 503
3595
3592
  while the platform's idempotency store is unreachable — KEEPS the queue
3596
3593
  and rejects, so the next flush sends the same events rather than losing
3597
- them. A refusal that cannot clear discards the batch it was sent for,
3594
+ them. The SDK's own flushes then hold off for the longer of the server's
3595
+ `Retry-After` and a cooldown that doubles from 10 seconds, up to five
3596
+ minutes. A refusal that cannot clear discards the batch it was sent for,
3598
3597
  because resending it can only fail identically.
3599
3598
 
3599
+ One flush at a time: called while another is still on the wire, this
3600
+ resolves at once with nothing sent, and what is queued goes out after it.
3601
+
3600
3602
  | Parameter | Type |
3601
3603
  | --- | --- |
3602
3604
  | `options` | `AnalyticsFlushOptions` |
@@ -3925,6 +3927,100 @@ for the avatars already worn when you subscribe: read
3925
3927
 
3926
3928
  **Returns.** a function that stops the calls. Calling it twice is harmless.
3927
3929
 
3930
+ <a id="avatarrignode"></a>
3931
+
3932
+ ## AvatarRigNode
3933
+
3934
+ ```ts
3935
+ interface AvatarRigNode
3936
+ ```
3937
+
3938
+ One node of a rig family's tree: its exact, case-sensitive name, and the
3939
+ name of the node it is a DIRECT child of. `parent` is `null` for the rig's
3940
+ root, which is a root node of the scene.
3941
+
3942
+ <a id="avatarrignode-node"></a>
3943
+
3944
+ ## AvatarRigNode.node
3945
+
3946
+ ```ts
3947
+ readonly node: string
3948
+ ```
3949
+
3950
+ The node's name, exact and case-sensitive.
3951
+
3952
+ <a id="avatarrignode-parent"></a>
3953
+
3954
+ ## AvatarRigNode.parent
3955
+
3956
+ ```ts
3957
+ readonly parent: string | null
3958
+ ```
3959
+
3960
+ The node it is a direct child of; `null` for the rig's root.
3961
+
3962
+ <a id="avatarrigs"></a>
3963
+
3964
+ ## AvatarRigs
3965
+
3966
+ ```ts
3967
+ interface AvatarRigs
3968
+ ```
3969
+
3970
+ The rig families this SDK knows, by name.
3971
+
3972
+ <a id="avatarrigs-small_human"></a>
3973
+
3974
+ ## AvatarRigs.small_human
3975
+
3976
+ ```ts
3977
+ readonly small_human: readonly AvatarRigNode[]
3978
+ ```
3979
+
3980
+ The toy rig: rigid limbs on a body, eight nodes.
3981
+
3982
+ <a id="avatarrigs-adult_human"></a>
3983
+
3984
+ ## AvatarRigs.adult_human
3985
+
3986
+ ```ts
3987
+ readonly adult_human: readonly AvatarRigNode[]
3988
+ ```
3989
+
3990
+ The adult humanoid with fingers: 56 nodes, world-aligned at rest.
3991
+
3992
+ <a id="avatar_rigs"></a>
3993
+
3994
+ ## AVATAR_RIGS
3995
+
3996
+ ```ts
3997
+ AVATAR_RIGS: AvatarRigs
3998
+ ```
3999
+
4000
+ Every node each rig family requires, with the node it is a direct child of,
4001
+ parents before children. Frozen: read it, never change it.
4002
+
4003
+ The platform refuses an upload whose model is missing one of these, has one
4004
+ under another parent, or has one twice (`rig_mismatch`). Extra nodes are
4005
+ allowed anywhere. `adult_human` also requires every one of its nodes to be
4006
+ world-aligned at rest; that rule is not in this list, see the guide.
4007
+
4008
+ ```js
4009
+ import { AVATAR_RIGS } from '@indiegems/gem-web-sdk/sdk';
4010
+
4011
+ // Every joint of the worn avatar, by name.
4012
+ const joints = new Map(
4013
+ AVATAR_RIGS.adult_human.map(({ node }) => [node, gltf.scene.getObjectByName(node)]),
4014
+ );
4015
+
4016
+ // A test that one of your own models is on the rig.
4017
+ for (const { node, parent } of AVATAR_RIGS.adult_human) {
4018
+ const found = gltf.scene.getObjectByName(node);
4019
+ if (!found) throw new Error(`missing ${node}`);
4020
+ if (parent !== null && found.parent?.name !== parent) throw new Error(`${node} is not under ${parent}`);
4021
+ }
4022
+ ```
4023
+
3928
4024
  <a id="gemapierror"></a>
3929
4025
 
3930
4026
  ## GemApiError
@@ -5347,7 +5443,7 @@ interface RewardCue
5347
5443
  ```
5348
5444
 
5349
5445
  A platform-currency reward on its way to the arcade — in the Gem Arcade, a
5350
- flight into the Gem button at the top-left of your frame — what
5446
+ flight into the Gem button, in whichever corner of your frame it sits — what
5351
5447
  `gem.cueReward` takes.
5352
5448
 
5353
5449
  <a id="rewardcue-currencyslug"></a>
@@ -5408,6 +5504,18 @@ y: number
5408
5504
 
5409
5505
  From the top edge of your viewport.
5410
5506
 
5507
+ <a id="gemcorner"></a>
5508
+
5509
+ ## GemCorner
5510
+
5511
+ ```ts
5512
+ type GemCorner = "top-left" | "top-right" | "bottom-left" | "bottom-right"
5513
+ ```
5514
+
5515
+ A corner of your frame, for `gem.setGemCorner`: where you would like the
5516
+ arcade's Gem button to sit. `'top-left'` is where it sits unless you ask.
5517
+ Physical corners: `'top-left'` is the top-left in every language.
5518
+
5411
5519
  <a id="framestate"></a>
5412
5520
 
5413
5521
  ## FrameState
@@ -5737,6 +5845,30 @@ connected, and only to an arcade that draws a menu.
5737
5845
  **Returns.** whether the message left the frame — `false` before the bridge is
5738
5846
  up, for an arcade with no menu, and past this frame's own limit.
5739
5847
 
5848
+ <a id="gemframe-setgemcorner"></a>
5849
+
5850
+ ## GemFrame.setGemCorner
5851
+
5852
+ ```ts
5853
+ setGemCorner(corner: GemCorner): boolean
5854
+ ```
5855
+
5856
+ Ask the arcade to put its own control over your game — the Gem button —
5857
+ in this corner of your frame: the whole answer each time, the latest one
5858
+ winning.
5859
+
5860
+ Prefer `gem.setGemCorner`, which holds the corner across a reconnect.
5861
+ This one sends only while connected, and only to an arcade that can move
5862
+ its control.
5863
+
5864
+ | Parameter | Type |
5865
+ | --- | --- |
5866
+ | `corner` | `GemCorner` |
5867
+
5868
+ **Returns.** whether the message left the frame — `false` before the bridge is
5869
+ up, for an arcade that cannot move its control, for a value that is not
5870
+ one of the four corners, and past this frame's own limit.
5871
+
5740
5872
  <a id="gemframe-requestshare"></a>
5741
5873
 
5742
5874
  ## GemFrame.requestShare
@@ -8303,7 +8435,7 @@ readonly reserved: readonly GemReservedArea[]
8303
8435
  ```
8304
8436
 
8305
8437
  Rectangles of your frame the arcade's own controls cover — in the Gem
8306
- Arcade, its Gem button in the top-left corner, and the frame-rate readout
8438
+ Arcade, its Gem button in whichever corner it sits, and the frame-rate readout
8307
8439
  beside it while the player shows it; an embedder that draws wallets over
8308
8440
  your game adds theirs (see `setWalletVisible`). Keep essential UI and touch
8309
8441
  targets out of them. Empty when the arcade covers nothing.
@@ -8821,6 +8953,25 @@ every account on the device shares it, and it can vanish at any time. A
8821
8953
  game must boot correctly from `null`. Progress belongs in `gem.me.saves`;
8822
8954
  a setting that must follow the player belongs in `gem.me.settings`.
8823
8955
 
8956
+ <a id="gem-moments"></a>
8957
+
8958
+ ## Gem.moments
8959
+
8960
+ ```ts
8961
+ readonly moments: MomentsApi
8962
+ ```
8963
+
8964
+ Platform moments — **every game, at every break.** Signal one from the
8965
+ player's press that leaves a natural break in play (Next, Retry, Back to
8966
+ menu), and the arcade may show one of its own offers over your paused
8967
+ game. It usually shows nothing.
8968
+
8969
+ Required by the release checklist at every important break, whether or
8970
+ not your game has a shop of its own. Never resolves a reason, never
8971
+ rejects, and `shown` grants nothing.
8972
+
8973
+ **Guides.** [platform-moments](../platform-moments.md)
8974
+
8824
8975
  <a id="gem-ads"></a>
8825
8976
 
8826
8977
  ## Gem.ads
@@ -8829,13 +8980,15 @@ a setting that must follow the player belongs in `gem.me.settings`.
8829
8980
  readonly ads: AdsApi
8830
8981
  ```
8831
8982
 
8832
- One full-screen cross-promotion at a natural break in play, rendered by
8833
- the arcade over your paused game.
8983
+ Rewarded ads, and the deprecated interstitial ask.
8834
8984
 
8835
- Ask at a break the player already expects — a level ending, a round
8836
- finishing — never mid-input. **The answer is usually no, and that is
8837
- ordinary rather than a failure:** carry straight on, and draw no
8838
- placeholder while you wait.
8985
+ **`format: 'interstitial'` is deprecated: signal a platform moment with
8986
+ `gem.moments.signal` instead.** It keeps working until at least `14.0.0`
8987
+ (`docs/deprecation-policy.md`); the arcade treats it as a platform moment.
8988
+
8989
+ A rewarded ask comes from the player's press on your own button. **The
8990
+ answer is usually no, and that is ordinary rather than a failure:** carry
8991
+ straight on, and draw no placeholder while you wait.
8839
8992
 
8840
8993
  Nothing here rejects. The arcade pauses your game while a panel is up and
8841
8994
  resumes it when the promise settles, so you do not pause anything yourself.
@@ -8848,7 +9001,7 @@ about the player. What you can read is your own economy:
8848
9001
  `rewardedStatus(placement)` says whether a rewarded placement could pay
8849
9002
  right now under your own declared caps — never whether an ad will play.
8850
9003
 
8851
- **Guides.** [cross-promotion](../cross-promotion.md)
9004
+ **Guides.** [rewarded-ads](../rewarded-ads.md)
8852
9005
 
8853
9006
  <a id="gem-account"></a>
8854
9007
 
@@ -9430,7 +9583,7 @@ cueReward(cue: RewardCue): boolean
9430
9583
 
9431
9584
  Tell the arcade a platform-currency reward is on its way, so it **shows it
9432
9585
  landing**: in the Gem Arcade the currency flies from `origin` into the Gem
9433
- button at the top-left of your frame, which pops as each mark lands, and
9586
+ button, wherever on your frame it sits, which pops as each mark lands, and
9434
9587
  the player's total moves once the platform confirms the grant. The Gem
9435
9588
  Arcade draws no wallet over your game: the player sees their Gems and
9436
9589
  Tickets in the Gem menu. A payout you do not cue still flies, from the
@@ -9532,8 +9685,9 @@ gem.setMenu({
9532
9685
  ```
9533
9686
 
9534
9687
  **Your settings are not in it: draw your own settings button, always** —
9535
- top-right, clear of `gem.viewport.reserved`. The Arcade's menu never opens
9536
- your settings screen.
9688
+ top-right, with your Back button just to its left, clear of
9689
+ `gem.viewport.reserved`. The Arcade's menu never opens your settings
9690
+ screen.
9537
9691
 
9538
9692
  **It is screen state, not a toggle.** Each call is the whole answer and
9539
9693
  replaces the last, so call it again when the entries change — a new
@@ -9553,6 +9707,46 @@ should not assume the game was paused by you.
9553
9707
  up (the menu is held and sent when it connects) and under an embedder
9554
9708
  that draws no menu.
9555
9709
 
9710
+ <a id="gem-setgemcorner"></a>
9711
+
9712
+ ## Gem.setGemCorner
9713
+
9714
+ ```ts
9715
+ setGemCorner(corner: GemCorner): boolean
9716
+ ```
9717
+
9718
+ Ask the arcade to put its Gem button — its one control over your game,
9719
+ top-left unless you ask — in another corner of your frame.
9720
+
9721
+ ```js
9722
+ // This game's own HUD needs the top-left, so the Gem goes top-right.
9723
+ gem.setGemCorner('top-right');
9724
+ ```
9725
+
9726
+ **Leave it top-left unless your own HUD needs that corner**, and then pick
9727
+ one corner for the whole game — at most one per orientation. A Gem that
9728
+ moves between screens is a way out the player has to look for. In
9729
+ landscape the bottom corners sit where a player's thumbs hold the sticks.
9730
+
9731
+ **Lay out around `gem.viewport.reserved`, never around the corner you
9732
+ asked for.** The arcade moves the Gem on its own terms: not while the
9733
+ player is using it or its menu is open, not more often than every few
9734
+ seconds, and never over a corner the player chose for themselves. The
9735
+ rectangles say where it actually is, and the `--gem-reserved-*` CSS
9736
+ properties follow them.
9737
+
9738
+ **It is screen state, not a toggle.** Each call is the whole answer and
9739
+ replaces the last. A value that is not one of the four corners is ignored,
9740
+ with a warning on the console, and changes nothing.
9741
+
9742
+ | Parameter | Type | Description |
9743
+ | --- | --- | --- |
9744
+ | `corner` | `GemCorner` | `'top-left'`, `'top-right'`, `'bottom-left'` or `'bottom-right'`. |
9745
+
9746
+ **Returns.** `true` when the arcade was told now; `false` before the bridge is
9747
+ up (the corner is held and sent when it connects), under an embedder that
9748
+ cannot move its control, and for a value that is not a corner.
9749
+
9556
9750
  <a id="gem-exit"></a>
9557
9751
 
9558
9752
  ## Gem.exit
@@ -10352,7 +10546,7 @@ The match this connection belongs to.
10352
10546
  ## MatchConnection.protocol
10353
10547
 
10354
10548
  ```ts
10355
- readonly protocol: "dedicated" | "p2p" | "loopback"
10549
+ readonly protocol: "p2p" | "dedicated" | "loopback"
10356
10550
  ```
10357
10551
 
10358
10552
  What is on the other end: `dedicated` (the platform's server), `p2p` (the
@@ -10544,13 +10738,21 @@ putting it in an `<img src>`.
10544
10738
  ## Avatar3dFamily
10545
10739
 
10546
10740
  ```ts
10547
- type Avatar3dFamily = "small_human" | string & object
10741
+ type Avatar3dFamily = "small_human" | "adult_human" | string & object
10548
10742
  ```
10549
10743
 
10550
10744
  A rig family — the skeleton contract a 3D avatar is built on, and what a
10551
- game declares it can animate. `small_human` is the first: a `root` node
10552
- with a `body` child, which holds `head`, `arm_l`, `arm_r`, `leg_l` and
10553
- `leg_r`, and a `hand_socket` under `arm_r`.
10745
+ game declares it can animate. Two today:
10746
+
10747
+ - `small_human`, the toy rig: a `root` node with a `body` child, which holds
10748
+ `head`, `arm_l`, `arm_r`, `leg_l` and `leg_r`, and a `hand_socket` under
10749
+ `arm_r`.
10750
+ - `adult_human`, a full humanoid with fingers: 56 nodes from `root` and
10751
+ `hips` down to `toes_r`, every one world-aligned at rest, so one clip plays
10752
+ on every avatar in the family.
10753
+
10754
+ `AVATAR_RIGS` (from `@indiegems/gem-web-sdk/sdk`) lists every node of
10755
+ both, with its parent.
10554
10756
 
10555
10757
  Open-ended (`| (string & {})`) so a family the platform adds later does not
10556
10758
  break a `switch` you compiled against this version: keep a `default` arm and
@@ -10853,6 +11055,152 @@ so portraits appear only against production and loopback.
10853
11055
  | --- | --- |
10854
11056
  | `url` | `string \| undefined` |
10855
11057
 
11058
+ <a id="momentkind"></a>
11059
+
11060
+ ## MomentKind
11061
+
11062
+ ```ts
11063
+ type MomentKind = "level_end" | "run_end" | "match_end" | "menu_return" | "other_break"
11064
+ ```
11065
+
11066
+ Which kind of break a platform moment is. A closed set — there will not be a
11067
+ sixth:
11068
+
11069
+ - `level_end` — a level, stage, wave or puzzle finished, won or lost.
11070
+ - `run_end` — game over, death, or a run banked, in an endless, arcade or
11071
+ roguelike game.
11072
+ - `match_end` — a multiplayer round or match finished and the player is
11073
+ leaving its results.
11074
+ - `menu_return` — the player chose to go back to the title, a main menu or
11075
+ level select. A return, never the first arrival.
11076
+ - `other_break` — any other natural stop: a story chapter, a between-waves
11077
+ shop.
11078
+
11079
+ It is a label, for the arcade's analytics, the dev host's log and the release
11080
+ checklist. It never changes what the arcade decides for your game.
11081
+
11082
+ **Guides.** [platform-moments](../platform-moments.md)
11083
+
11084
+ <a id="momentsignal"></a>
11085
+
11086
+ ## MomentSignal
11087
+
11088
+ ```ts
11089
+ interface MomentSignal
11090
+ ```
11091
+
11092
+ A platform moment: which kind of break the player is leaving, and optionally
11093
+ your own name for the screen.
11094
+
11095
+ <a id="momentsignal-kind"></a>
11096
+
11097
+ ## MomentSignal.kind
11098
+
11099
+ ```ts
11100
+ kind: MomentKind
11101
+ ```
11102
+
11103
+ Which kind of break this is. Required.
11104
+
11105
+ <a id="momentsignal-placement"></a>
11106
+
11107
+ ## MomentSignal.placement
11108
+
11109
+ ```ts
11110
+ placement?: string
11111
+ ```
11112
+
11113
+ Optional: your own name for the screen the player is leaving —
11114
+ `world-1-3`, `results`. **1 to 32 characters of `a-z`, `0-9`, `-` and
11115
+ `_`.** It appears only in the dev host's log, so a reviewer can map each
11116
+ moment to a screen. It is never sent to the platform, never stored, never
11117
+ shown to a player and never sent to an ad network.
11118
+
11119
+ <a id="momentresult"></a>
11120
+
11121
+ ## MomentResult
11122
+
11123
+ ```ts
11124
+ interface MomentResult
11125
+ ```
11126
+
11127
+ What the arcade answers at a platform moment.
11128
+
11129
+ <a id="momentresult-shown"></a>
11130
+
11131
+ ## MomentResult.shown
11132
+
11133
+ ```ts
11134
+ readonly shown: boolean
11135
+ ```
11136
+
11137
+ Whether the arcade showed anything of its own at this break. Usually
11138
+ `false`. Either way, carry on exactly as you would have. **It grants
11139
+ nothing**, and it never says what was shown or why nothing was.
11140
+
11141
+ <a id="momentsapi"></a>
11142
+
11143
+ ## MomentsApi
11144
+
11145
+ ```ts
11146
+ interface MomentsApi
11147
+ ```
11148
+
11149
+ Platform moments. `gem.moments` is one of these.
11150
+
11151
+ <a id="momentsapi-signal"></a>
11152
+
11153
+ ## MomentsApi.signal
11154
+
11155
+ ```ts
11156
+ signal(moment: MomentSignal): Promise<MomentResult>
11157
+ ```
11158
+
11159
+ Signal a platform moment: the player is leaving a natural break in play.
11160
+ The arcade may show one of its own offers over your paused game, and
11161
+ usually shows nothing.
11162
+
11163
+ **Call it from the player's press that LEAVES the break** — Next, Retry,
11164
+ Back to menu — once per break. Start your own transition in parallel, and
11165
+ start play when this settles. **Never** mid-input, during a timed
11166
+ sequence, on a timer, on boot or the first arrival at the title, or from a
11167
+ player-opened pause menu (choosing Quit to menu from it is a
11168
+ `menu_return`). No press is required by the SDK; the rule is about when a
11169
+ break really is one.
11170
+
11171
+ NOTHING REJECTS. It resolves `{ shown: false }` without asking for an
11172
+ arcade that does not do platform moments, a `kind` or `placement` outside
11173
+ the grammar, a call before `gem.frame.ready()`, a call while another is in
11174
+ flight, a call within 10 seconds of the last answer, or no answer within
11175
+ three minutes — and each of those, except an arcade that does not do it
11176
+ (said once a session), is reported to your `onError` handler with code
11177
+ `'internal'` and a message beginning with a stable token:
11178
+ `moments_unsupported`, `moments_invalid_argument`, `moments_before_ready`,
11179
+ `moments_in_flight`, `moments_called_repeatedly` or `moments_timeout`.
11180
+
11181
+ The arcade pauses and resumes your game through the ordinary
11182
+ `gem.pause`, so you do not pause anything yourself.
11183
+
11184
+ ⚠️ `shown` grants nothing. Never hand out a reward, an unlock or a currency
11185
+ on it, and never pop your own offer at a break that settled `shown: true`.
11186
+
11187
+ | Parameter | Type | Description |
11188
+ | --- | --- | --- |
11189
+ | `moment` | `MomentSignal` | Which kind of break, and optionally your name for the screen. |
11190
+
11191
+ **Returns.** Whether the arcade showed anything. Always resolves; never rejects.
11192
+
11193
+ ```ts
11194
+ // From the press that LEAVES the break — Next, Retry, Back to menu.
11195
+ async function onNextPressed() {
11196
+ startLevelTransition(); // your fade or loading beat, in parallel
11197
+ await gem.moments.signal({ kind: 'level_end', placement: 'world-1-3' });
11198
+ startNextLevel(); // settled either way; never grant anything on `shown`
11199
+ }
11200
+ ```
11201
+
11202
+ **Guides.** [platform-moments](../platform-moments.md)
11203
+
10856
11204
  <a id="dedicatedserverfailure"></a>
10857
11205
 
10858
11206
  ## DedicatedServerFailure
@@ -1,6 +1,6 @@
1
1
  # The server surface — API reference
2
2
 
3
- Generated from `@indiegems/gem-web-sdk/sdk` v13.0.0, the version of the package this file ships in. Every symbol below carries an explicit anchor, its `anchor` in `reference.json` beside this file.
3
+ Generated from `@indiegems/gem-web-sdk/sdk` v13.2.0, the version of the package this file ships in. Every symbol below carries an explicit anchor, its `anchor` in `reference.json` beside this file.
4
4
 
5
5
  `@indiegems/gem-web-sdk/sdk` is the one import a game writes, wherever its code runs. `@indiegems/gem-web-sdk/server` exports exactly the symbols on this page and nothing else, for a build that wants only this half.
6
6
 
@@ -440,6 +440,22 @@ It buys the same budget a read gets — see [ClientOptions.maxRetries](#clientop
440
440
  final: only the failures [GemApiError.retryable](#gemapierror-retryable) describes, and a
441
441
  connection that died with no answer, are attempted again.
442
442
 
443
+ <a id="requestoptions-retryratelimited"></a>
444
+
445
+ ## RequestOptions.retryRateLimited
446
+
447
+ ```ts
448
+ readonly retryRateLimited?: boolean
449
+ ```
450
+
451
+ Set `false` to make a 429 final for this call instead of waiting out
452
+ `Retry-After` and trying again.
453
+
454
+ For a caller that keeps its own queue and its own cooldown, such as a
455
+ batch sender on a timer: retrying inside the call only spends more of the
456
+ same limit, and holds the caller's queue while it does. Every other
457
+ failure is retried exactly as before. Defaults to `true`.
458
+
443
459
  <a id="versionedgetoptions"></a>
444
460
 
445
461
  ## VersionedGetOptions
@@ -2523,9 +2539,14 @@ Send what is queued now, and find out what happened to it.
2523
2539
 
2524
2540
  Resolves with the platform's own answer, INCLUDING per-event rejections, which
2525
2541
  arrive inside a 200 rather than as an error. A refusal that could clear on
2526
- its own — a 429, a 503 — keeps the queue and rejects; one that cannot
2542
+ its own — a 429, a 503 — keeps the queue and rejects, and the SDK's own
2543
+ flushes then hold off for the longer of the server's `Retry-After` and a
2544
+ cooldown that doubles from 10 seconds, up to five minutes; one that cannot
2527
2545
  discards the batch, because resending it can only fail the same way.
2528
2546
 
2547
+ One flush at a time: called while another is on the wire, this waits for
2548
+ it and then sends what is left.
2549
+
2529
2550
  | Parameter | Type | Description |
2530
2551
  | --- | --- | --- |
2531
2552
  | `signal` | `AbortSignal` | Cancels the call — not the queue. |