@indiegems/gem-web-sdk 7.5.0 → 7.7.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.
@@ -1,6 +1,6 @@
1
1
  # The browser surface — API reference
2
2
 
3
- Generated from `@indiegems/gem-web-sdk/sdk` v7.5.0. Every symbol below links to its page on the Gem Arcade developer site.
3
+ Generated from `@indiegems/gem-web-sdk/sdk` v7.7.0. Every symbol below links to its page on the Gem Arcade developer site.
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
 
@@ -4613,11 +4613,11 @@ type FrameState = "waiting" | "connected" | "refused" | "closed"
4613
4613
 
4614
4614
  Where the bridge to the arcade stands.
4615
4615
 
4616
- `waiting` is the state a game boots in and the one it may sit in forever —
4617
- outside the arcade there is no parent to answer, and that is not an error.
4618
- `connected` is the only state in which the arcade is listening. `refused`
4619
- means a page tried to embed this game and its origin was not in
4620
- `allowedEmbedders`; `closed` means a connection existed and ended.
4616
+ `waiting` is the state a game boots in: framed, and its parent has not
4617
+ answered yet. `connected` is the only state in which the arcade is
4618
+ listening. `refused` is final: the page that framed this game is not one it
4619
+ accepts (its origin is not in `allowedEmbedders`), or it speaks no protocol
4620
+ version in common. `closed` means a connection existed and ended.
4621
4621
 
4622
4622
  Received by `onStateChange` and resolved by `awaitBridge`, both of which are
4623
4623
  documented — so without a name for this union a game could observe the value
@@ -4655,15 +4655,15 @@ Where the bridge is right now, without waiting.
4655
4655
 
4656
4656
  - `waiting` — the arcade has not connected yet. Normal at startup.
4657
4657
  - `connected` — the bridge is up and everything works.
4658
- - `refused` — the arcade declined the connection.
4658
+ - `refused` — final: the page that framed the game is not one it accepts,
4659
+ or no protocol version is shared.
4659
4660
  - `closed` — the session has ended.
4660
4661
 
4661
4662
  A snapshot, so it can change immediately after you read it. To wait for the
4662
4663
  question to be settled once, use [GemFrame.settled](https://gemarcade.com/developer/api/game#gemframe-settled).
4663
4664
 
4664
- A game outside the arcade — opened directly during development — stays
4665
- `waiting` forever, which is the case worth handling: boot anyway, without a
4666
- bridge.
4665
+ A page that frames the game and never answers leaves it `waiting`, which
4666
+ is the case worth handling: boot anyway, and say there is no session.
4667
4667
 
4668
4668
  ## GemFrame.parentSupports
4669
4669
 
@@ -4723,10 +4723,9 @@ parsing several megabytes of JavaScript, the offer waits behind that work —
4723
4723
  and by the time it arrives the connection it carried may already be gone.
4724
4724
  Awaiting this first keeps the thread free long enough to accept.
4725
4725
 
4726
- THE WAIT IS BOUNDED ON PURPOSE, because an embedder is optional: your game
4727
- must still start when nothing ever answers, just without a bridge. Proceed
4728
- on either outcome and read the resolved state to know which kind of boot
4729
- this is.
4726
+ THE WAIT IS BOUNDED ON PURPOSE, because the page framing your game may
4727
+ never answer, and your game must still start and say so. Proceed on either
4728
+ outcome and read the resolved state to know which kind of boot this is.
4730
4729
 
4731
4730
  ```js
4732
4731
  const state = await gem.frame.settled();
@@ -6752,7 +6751,7 @@ readonly reserved: readonly GemReservedArea[]
6752
6751
  ```
6753
6752
 
6754
6753
  Rectangles of your frame the arcade's own controls cover — its Back tab in
6755
- the top-left corner, and the wallets beside it while they are shown (see
6754
+ the top-left corner, and the wallets in the top-right while they are shown (see
6756
6755
  `setWalletVisible`). Keep essential UI and touch targets out of them. Empty
6757
6756
  when the arcade covers nothing.
6758
6757
 
@@ -7019,7 +7018,9 @@ single round trip to the page hosting your game.
7019
7018
 
7020
7019
  Rejects rather than resolving `null` if the wait runs out: a session that
7021
7020
  cannot sign in cannot do anything else either, and a `null` you might not
7022
- check is how a game carries on into a failure it could have reported.
7021
+ check is how a game carries on into a failure it could have reported. It
7022
+ rejects at once, without waiting, when the page that framed the game was
7023
+ refused: no token can arrive from it.
7023
7024
 
7024
7025
  | Parameter | Type | Description |
7025
7026
  | --- | --- | --- |
@@ -7042,10 +7043,10 @@ Waits for the connection to the arcade to settle, one way or the other.
7042
7043
  Resolves with the state it settled into — connected, refused or closed — or
7043
7044
  with whatever it had reached by `timeoutMs`, which defaults to 10 seconds.
7044
7045
 
7045
- OUTSIDE THE ARCADE IT WAITS THE FULL TIMEOUT and then resolves `waiting`,
7046
- because nothing ever answers. It does not return early. That is ten seconds
7047
- on every local run, so pass a shorter `timeoutMs` during development if the
7048
- wait gets in your way.
7046
+ A game is always framed (`createGem` throws [GemUnframedError](https://gemarcade.com/developer/api/game#gemunframederror)
7047
+ otherwise), so this normally resolves `connected` within a moment. It
7048
+ resolves `refused` at once when the page that framed the game is not one it
7049
+ accepts, and `waiting` only when the page framing it never answers.
7049
7050
 
7050
7051
  **Await this in your boot entry before importing your main bundle.** The
7051
7052
  arcade hands your game its connection as a one-off delivery rather than
@@ -7054,9 +7055,10 @@ parsing several megabytes of JavaScript when it arrives, your game can miss
7054
7055
  it. Awaiting this keeps the thread free through that window; the moment it
7055
7056
  resolves, load everything.
7056
7057
 
7057
- Carry on whatever it resolves to. `connected` means you are in the arcade;
7058
- anything else means you are running without it, which your game has to
7059
- survive anyway — during local development, that is the normal case.
7058
+ Carry on whatever it resolves to. `connected` means a Gem host is
7059
+ listening — the Arcade, the Editor or the dev host on your machine;
7060
+ anything else means no session, which your game has to survive with a
7061
+ message rather than a hang.
7060
7062
 
7061
7063
  | Parameter | Type | Description |
7062
7064
  | --- | --- | --- |
@@ -7075,8 +7077,7 @@ readonly player: GemPlayer | null
7075
7077
 
7076
7078
  The player's display identity — the name, handle and avatar you show them.
7077
7079
 
7078
- `null` until the arcade sends it, and always `null` outside the arcade.
7079
- Nothing obliges it to be sent at all, so treat its absence as normal and
7080
+ `null` until the arcade sends it. Nothing obliges it to be sent at all, so treat its absence as normal and
7080
7081
  have a fallback: a game with no display name is fine, a game that waits
7081
7082
  forever for one is not.
7082
7083
 
@@ -7523,8 +7524,8 @@ awaitCohorts(timeoutMs?: number): Promise<GemCohorts | null>
7523
7524
 
7524
7525
  The experiment cohorts, waiting up to `timeoutMs` for them.
7525
7526
 
7526
- Resolves `null` rather than rejecting — outside the arcade too, once the
7527
- wait is out, because no parent is there to answer. Use it at the ONE point
7527
+ Resolves `null` rather than rejecting once the wait is out, or at once if
7528
+ the page that framed the game was refused. Use it at the ONE point
7528
7529
  your game branches on a cohort — typically
7529
7530
  the first run's opening — and give it a short budget: the answer normally
7530
7531
  arrives within the first second, and a player kept on a splash screen for a
@@ -7647,7 +7648,7 @@ ticketStore.onOpen = () => gem.setWalletVisible(true, { only: ['ARCADE_TICKETS']
7647
7648
  replaces the last. While hidden, the arcade still pops a wallet up for a
7648
7649
  moment when something happens to it — a reward you cued, a spend, a
7649
7650
  landing — and your state never cancels that. Shown wallets cover part of
7650
- your frame beside the Back tab, which arrives as a rectangle in
7651
+ your frame's top-right corner, which arrives as a rectangle in
7651
7652
  `gem.viewport.reserved`; hiding them gives that space back. A wallet the
7652
7653
  arcade pops up for a moment floats over your game and moves nothing.
7653
7654
 
@@ -7715,9 +7716,9 @@ readonly frame: GemFrame
7715
7716
 
7716
7717
  Your game's connection to the arcade.
7717
7718
 
7718
- ALWAYS PRESENT, even when nothing is on the other end. Running outside the
7719
- arcade does not make this `null` — the frame simply stays in `waiting`
7720
- forever, which is the state to read when you want to know whether anyone
7719
+ ALWAYS PRESENT, even when nothing is on the other end yet: a page that
7720
+ never answers leaves the frame in `waiting`, and a refused one ends it in
7721
+ `refused`, which is the state to read when you want to know whether anyone
7721
7722
  ever answered. Do not write a `=== null` branch; it can never run.
7722
7723
 
7723
7724
  **This is where the loading contract lives.** Call `progress()` as your
@@ -7833,8 +7834,9 @@ Connect your game to the Gem Arcade. This is the one call every game makes.
7833
7834
 
7834
7835
  It assembles everything and connects to the arcade that framed your game. The
7835
7836
  same code runs on your machine and in production without a flag — but it needs
7836
- a parent either way: run it framed, and until one answers the SDK says so
7837
- rather than letting you assume a session you do not have.
7837
+ a frame either way. Opened at the top level it throws
7838
+ [GemUnframedError](https://gemarcade.com/developer/api/game#gemunframederror), which says how to run it: in the Arcade, the Editor,
7839
+ or the Gem dev host on your machine (`npm run dev`, signed in to `gemctl`).
7838
7840
 
7839
7841
  One dependency, imported like anything else. The install is at
7840
7842
  https://gemarcade.com/developer/docs/getting-started#3-add-the-sdk
@@ -7989,6 +7991,43 @@ What [LeaderboardsApi.mine](https://gemarcade.com/developer/api/game#leaderboard
7989
7991
  Separate from [GetLeaderboardOptions](https://gemarcade.com/developer/api/game#getleaderboardoptions) because `limit` has no meaning
7990
7992
  there — the global board arrives whole.
7991
7993
 
7994
+ ## LeaderboardPeriod
7995
+
7996
+ [LeaderboardPeriod](https://gemarcade.com/developer/api/game#leaderboardperiod)
7997
+
7998
+ ```ts
7999
+ interface LeaderboardPeriod
8000
+ ```
8001
+
8002
+ Which period a `per_period` board's result is for.
8003
+
8004
+ A board whose config sets `per_period: true` exists once per period of the
8005
+ challenge its run type names: one board for each day of a daily challenge,
8006
+ each week of a weekly one. Present on a result exactly when the board is one
8007
+ of those; absent — not `null` — on an ordinary board.
8008
+
8009
+ ⚠️ **Players on the same date share a board, whatever their time zone.** On
8010
+ a challenge that follows each player's own time zone (the default), a date
8011
+ begins for each player at their own midnight, so one day's board is open for
8012
+ about 50 hours worldwide and the standings can still change after YOUR day
8013
+ has ended. [LeaderboardPeriod.final](https://gemarcade.com/developer/api/game#leaderboardperiod-final) says when they no longer can.
8014
+
8015
+ ## PeriodOptions
8016
+
8017
+ [PeriodOptions](https://gemarcade.com/developer/api/game#periodoptions)
8018
+
8019
+ ```ts
8020
+ interface PeriodOptions
8021
+ ```
8022
+
8023
+ Which period of a `per_period` board to read. Shared by every read.
8024
+
8025
+ Name at most one of [PeriodOptions.periodsBack](https://gemarcade.com/developer/api/game#periodoptions-periodsback) and
8026
+ [PeriodOptions.period](https://gemarcade.com/developer/api/game#periodoptions-period); with neither, the read answers for the current
8027
+ period. On a board that is not `per_period` both are refused with `invalid`
8028
+ rather than ignored — asking an all-time board for yesterday and being
8029
+ handed all time would be a different answer with nothing saying so.
8030
+
7992
8031
  ## ShowcaseApi
7993
8032
 
7994
8033
  [ShowcaseApi](https://gemarcade.com/developer/api/game#showcaseapi)
@@ -11011,6 +11050,126 @@ theirs. Use this for display-level comparisons, not for identity.
11011
11050
  | `a` | `string \| PlayerText` |
11012
11051
  | `b` | `string \| PlayerText` |
11013
11052
 
11053
+ ## GemUnframedError
11054
+
11055
+ [GemUnframedError](https://gemarcade.com/developer/api/game#gemunframederror)
11056
+
11057
+ ```ts
11058
+ class GemUnframedError
11059
+ ```
11060
+
11061
+ Your game was opened without a Gem frame around it.
11062
+
11063
+ Thrown by `createGem` when the page is the top-level document: nothing
11064
+ framed it, so nothing can hand it a player, saves, config or rooms. The SDK
11065
+ needs that frame — the Gem Arcade, the Gem Editor, or the Gem dev host on
11066
+ your own machine — and refuses to start without one rather than running a
11067
+ game that silently has no player.
11068
+
11069
+ You do not construct this. Run the game framed instead: `npm run dev`
11070
+ (`gemctl serve start . --dev`) while you edit, `gemctl serve start .` for a
11071
+ build, signed in to `gemctl` either way. Never open the address the dev
11072
+ server itself prints.
11073
+
11074
+ A unit test that constructs the SDK in a simulated DOM has to give its
11075
+ window a parent of its own, because a simulated top-level window is exactly
11076
+ what this refuses.
11077
+
11078
+ ## LeaderboardPeriod.id
11079
+
11080
+ [LeaderboardPeriod.id](https://gemarcade.com/developer/api/game#leaderboardperiod-id)
11081
+
11082
+ ```ts
11083
+ readonly id: string
11084
+ ```
11085
+
11086
+ The period this board is for: the date for a daily challenge, the Monday
11087
+ for a weekly, the 1st for a monthly (`2026-10-02`), or a cron challenge's
11088
+ start as `2026-10-02T06:00:00Z`.
11089
+
11090
+ **This is the board's identity.** Pass it back as
11091
+ [GetLeaderboardOptions.period](https://gemarcade.com/developer/api/game#getleaderboardoptions-period) to read this same board again later —
11092
+ from a shared link, or a history screen — and key any cache on it.
11093
+
11094
+ ## LeaderboardPeriod.localDate
11095
+
11096
+ [LeaderboardPeriod.localDate](https://gemarcade.com/developer/api/game#leaderboardperiod-localdate)
11097
+
11098
+ ```ts
11099
+ readonly localDate?: string
11100
+ ```
11101
+
11102
+ The period's calendar date, `YYYY-MM-DD` — the same value
11103
+ `gem.challenges.list()` gives as `periodLocalDate` for the challenge, so
11104
+ the two can be compared. Absent for a cron challenge, which has no date.
11105
+
11106
+ ## LeaderboardPeriod.startAt
11107
+
11108
+ [LeaderboardPeriod.startAt](https://gemarcade.com/developer/api/game#leaderboardperiod-startat)
11109
+
11110
+ ```ts
11111
+ readonly startAt?: number
11112
+ ```
11113
+
11114
+ When this period began for the player the read answered for, in Unix
11115
+ seconds. Absent when the read answered for no player — a server or a
11116
+ function that named none — on a challenge that follows each player's own
11117
+ time zone.
11118
+
11119
+ ## LeaderboardPeriod.endsAt
11120
+
11121
+ [LeaderboardPeriod.endsAt](https://gemarcade.com/developer/api/game#leaderboardperiod-endsat)
11122
+
11123
+ ```ts
11124
+ readonly endsAt?: number
11125
+ ```
11126
+
11127
+ When this period ends for that player, in Unix seconds: their own runs stop
11128
+ counting towards this board then.
11129
+
11130
+ ⚠️ **Not when the board stops changing.** Players in later time zones are
11131
+ still on this date after it; see [LeaderboardPeriod.closesAt](https://gemarcade.com/developer/api/game#leaderboardperiod-closesat) and
11132
+ [LeaderboardPeriod.final](https://gemarcade.com/developer/api/game#leaderboardperiod-final). Absent exactly when
11133
+ [LeaderboardPeriod.startAt](https://gemarcade.com/developer/api/game#leaderboardperiod-startat) is.
11134
+
11135
+ ## LeaderboardPeriod.opensAt
11136
+
11137
+ [LeaderboardPeriod.opensAt](https://gemarcade.com/developer/api/game#leaderboardperiod-opensat)
11138
+
11139
+ ```ts
11140
+ readonly opensAt: number
11141
+ ```
11142
+
11143
+ When this board started accepting runs from anyone, in Unix seconds. `0`
11144
+ when the platform sent something this SDK could not read.
11145
+
11146
+ ## LeaderboardPeriod.closesAt
11147
+
11148
+ [LeaderboardPeriod.closesAt](https://gemarcade.com/developer/api/game#leaderboardperiod-closesat)
11149
+
11150
+ ```ts
11151
+ readonly closesAt: number
11152
+ ```
11153
+
11154
+ When this board stops accepting runs from anyone, in Unix seconds. `0`
11155
+ when the platform sent something this SDK could not read.
11156
+
11157
+ ## LeaderboardPeriod.final
11158
+
11159
+ [LeaderboardPeriod.final](https://gemarcade.com/developer/api/game#leaderboardperiod-final)
11160
+
11161
+ ```ts
11162
+ readonly final: boolean
11163
+ ```
11164
+
11165
+ `true` once no run can still arrive, so the standings will not change
11166
+ again — stop polling, and say "final" if your screen says anything.
11167
+
11168
+ While it is `false` the standings may change, **including after the
11169
+ viewer's own period has ended**: a "you finished 3rd today" shown at the
11170
+ player's midnight can be wrong an hour later. Say "so far" until this is
11171
+ `true`.
11172
+
11014
11173
  ## LeaderboardEntry.playerId
11015
11174
 
11016
11175
  [LeaderboardEntry.playerId](https://gemarcade.com/developer/api/game#leaderboardentry-playerid)
@@ -11108,6 +11267,12 @@ readonly occurredAt: number
11108
11267
 
11109
11268
  When the ranked run occurred — Unix seconds.
11110
11269
 
11270
+ ⚠️ **`0` on a `per_period` board whose challenge follows each player's own
11271
+ time zone** (the default `local` boundary). The platform serves no time
11272
+ there: beside the board's date, the time of a run would show which time
11273
+ zones its player could be in. Render no time for `0` rather than 1970.
11274
+ Ranks are unaffected — ties are still broken by the real time.
11275
+
11111
11276
  ## LeaderboardEntry.entityId
11112
11277
 
11113
11278
  [LeaderboardEntry.entityId](https://gemarcade.com/developer/api/game#leaderboardentry-entityid)
@@ -11251,6 +11416,20 @@ interval: polling faster re-downloads the same rendering.
11251
11416
  Absent if the server did not state one. Absent means *unknown*, not
11252
11417
  "refreshes constantly" — do not substitute `0` and poll on it.
11253
11418
 
11419
+ ## Leaderboard.period
11420
+
11421
+ [Leaderboard.period](https://gemarcade.com/developer/api/game#leaderboard-period)
11422
+
11423
+ ```ts
11424
+ readonly period?: LeaderboardPeriod
11425
+ ```
11426
+
11427
+ Which period this result is for, on a board whose config sets
11428
+ `per_period`. Absent on an ordinary board, which ranks every run.
11429
+
11430
+ Once `period.final` is `true` the board no longer changes and need not be
11431
+ polled.
11432
+
11254
11433
  ## RankedRow.rank
11255
11434
 
11256
11435
  [RankedRow.rank](https://gemarcade.com/developer/api/game#rankedrow-rank)
@@ -11269,7 +11448,9 @@ readonly rank: number
11269
11448
  readonly occurredAt: number
11270
11449
  ```
11271
11450
 
11272
- When the ranked run occurred — Unix seconds.
11451
+ When the ranked run occurred — Unix seconds. `0` on a `per_period` board
11452
+ whose challenge follows each player's own time zone, where the platform
11453
+ serves no time; render nothing for it.
11273
11454
 
11274
11455
  ## RankedRow.stats
11275
11456
 
@@ -11299,7 +11480,9 @@ The run's freeform properties.
11299
11480
  readonly occurredAt: number
11300
11481
  ```
11301
11482
 
11302
- When the ranked run occurred — Unix seconds.
11483
+ When the ranked run occurred — Unix seconds. `0` on a `per_period` board
11484
+ whose challenge follows each player's own time zone, where the platform
11485
+ serves no time; render nothing for it.
11303
11486
 
11304
11487
  ## EntityLeaderboardEntry.properties
11305
11488
 
@@ -11461,6 +11644,107 @@ readonly generatedAt: number
11461
11644
 
11462
11645
  When this rendering was computed — Unix seconds.
11463
11646
 
11647
+ ## EntityLeaderboard.period
11648
+
11649
+ [EntityLeaderboard.period](https://gemarcade.com/developer/api/game#entityleaderboard-period)
11650
+
11651
+ ```ts
11652
+ readonly period?: LeaderboardPeriod
11653
+ ```
11654
+
11655
+ Which period these runs are from, on a board whose config sets
11656
+ `per_period`: `entries`, `personalBest` and `totalRuns`
11657
+ are then that ONE period's, not the entity's whole history. Absent on an
11658
+ ordinary board.
11659
+
11660
+ ## PeriodOptions.periodsBack
11661
+
11662
+ [PeriodOptions.periodsBack](https://gemarcade.com/developer/api/game#periodoptions-periodsback)
11663
+
11664
+ ```ts
11665
+ readonly periodsBack?: number
11666
+ ```
11667
+
11668
+ How many periods back to read. `0` — the default — is the current period;
11669
+ `1` is yesterday's board for a daily challenge, last week's for a weekly.
11670
+
11671
+ An integer from 0 to 400. Anything else is a `TypeError` before the
11672
+ request is made.
11673
+
11674
+ ## PeriodOptions.period
11675
+
11676
+ [PeriodOptions.period](https://gemarcade.com/developer/api/game#periodoptions-period)
11677
+
11678
+ ```ts
11679
+ readonly period?: string
11680
+ ```
11681
+
11682
+ The period to read, as an earlier result's [LeaderboardPeriod.id](https://gemarcade.com/developer/api/game#leaderboardperiod-id) —
11683
+ for a board reached from a shared link or a history screen. At most 400
11684
+ periods back.
11685
+
11686
+ ⚠️ A period that has not begun for the player is refused, even when it has
11687
+ already begun for players in time zones ahead of theirs.
11688
+
11689
+ ## PeriodOptions.playerId
11690
+
11691
+ [PeriodOptions.playerId](https://gemarcade.com/developer/api/game#periodoptions-playerid)
11692
+
11693
+ ```ts
11694
+ readonly playerId?: string
11695
+ ```
11696
+
11697
+ For a serverless function or a dedicated server: the player whose period
11698
+ to answer for. Their credential names no player, so without this a
11699
+ `per_period` board on each player's own time zone is answered in UTC,
11700
+ which is not that player's day.
11701
+
11702
+ Ignored in the browser, where the read is always for the signed-in player.
11703
+
11704
+ ## EntityLeaderboardOptions.periodsBack
11705
+
11706
+ [EntityLeaderboardOptions.periodsBack](https://gemarcade.com/developer/api/game#entityleaderboardoptions-periodsback)
11707
+
11708
+ ```ts
11709
+ readonly periodsBack?: number
11710
+ ```
11711
+
11712
+ How many periods back to read. `0` — the default — is the current period;
11713
+ `1` is yesterday's board for a daily challenge, last week's for a weekly.
11714
+
11715
+ An integer from 0 to 400. Anything else is a `TypeError` before the
11716
+ request is made.
11717
+
11718
+ ## EntityLeaderboardOptions.period
11719
+
11720
+ [EntityLeaderboardOptions.period](https://gemarcade.com/developer/api/game#entityleaderboardoptions-period)
11721
+
11722
+ ```ts
11723
+ readonly period?: string
11724
+ ```
11725
+
11726
+ The period to read, as an earlier result's [LeaderboardPeriod.id](https://gemarcade.com/developer/api/game#leaderboardperiod-id) —
11727
+ for a board reached from a shared link or a history screen. At most 400
11728
+ periods back.
11729
+
11730
+ ⚠️ A period that has not begun for the player is refused, even when it has
11731
+ already begun for players in time zones ahead of theirs.
11732
+
11733
+ ## EntityLeaderboardOptions.playerId
11734
+
11735
+ [EntityLeaderboardOptions.playerId](https://gemarcade.com/developer/api/game#entityleaderboardoptions-playerid)
11736
+
11737
+ ```ts
11738
+ readonly playerId?: string
11739
+ ```
11740
+
11741
+ For a serverless function or a dedicated server: the player whose period
11742
+ to answer for. Their credential names no player, so without this a
11743
+ `per_period` board on each player's own time zone is answered in UTC,
11744
+ which is not that player's day.
11745
+
11746
+ Ignored in the browser, where the read is always for the signed-in player.
11747
+
11464
11748
  ## EntityLeaderboardOptions.bucket1
11465
11749
 
11466
11750
  [EntityLeaderboardOptions.bucket1](https://gemarcade.com/developer/api/game#entityleaderboardoptions-bucket1)
@@ -11507,6 +11791,50 @@ readonly signal?: AbortSignal
11507
11791
 
11508
11792
  Abort the read.
11509
11793
 
11794
+ ## GetLeaderboardOptions.periodsBack
11795
+
11796
+ [GetLeaderboardOptions.periodsBack](https://gemarcade.com/developer/api/game#getleaderboardoptions-periodsback)
11797
+
11798
+ ```ts
11799
+ readonly periodsBack?: number
11800
+ ```
11801
+
11802
+ How many periods back to read. `0` — the default — is the current period;
11803
+ `1` is yesterday's board for a daily challenge, last week's for a weekly.
11804
+
11805
+ An integer from 0 to 400. Anything else is a `TypeError` before the
11806
+ request is made.
11807
+
11808
+ ## GetLeaderboardOptions.period
11809
+
11810
+ [GetLeaderboardOptions.period](https://gemarcade.com/developer/api/game#getleaderboardoptions-period)
11811
+
11812
+ ```ts
11813
+ readonly period?: string
11814
+ ```
11815
+
11816
+ The period to read, as an earlier result's [LeaderboardPeriod.id](https://gemarcade.com/developer/api/game#leaderboardperiod-id) —
11817
+ for a board reached from a shared link or a history screen. At most 400
11818
+ periods back.
11819
+
11820
+ ⚠️ A period that has not begun for the player is refused, even when it has
11821
+ already begun for players in time zones ahead of theirs.
11822
+
11823
+ ## GetLeaderboardOptions.playerId
11824
+
11825
+ [GetLeaderboardOptions.playerId](https://gemarcade.com/developer/api/game#getleaderboardoptions-playerid)
11826
+
11827
+ ```ts
11828
+ readonly playerId?: string
11829
+ ```
11830
+
11831
+ For a serverless function or a dedicated server: the player whose period
11832
+ to answer for. Their credential names no player, so without this a
11833
+ `per_period` board on each player's own time zone is answered in UTC,
11834
+ which is not that player's day.
11835
+
11836
+ Ignored in the browser, where the read is always for the signed-in player.
11837
+
11510
11838
  ## GetLeaderboardOptions.bucket1
11511
11839
 
11512
11840
  [GetLeaderboardOptions.bucket1](https://gemarcade.com/developer/api/game#getleaderboardoptions-bucket1)
@@ -11554,14 +11882,28 @@ A bucketed board (config `bucket_by`) needs exactly its bucket values —
11554
11882
  faster than the result's `updateFrequencySeconds`; the server regenerates
11555
11883
  at that cadence and faster polling re-reads the same rendering.
11556
11884
 
11885
+ A `per_period` board answers for one period — the current one by default:
11886
+
11887
+ ```js
11888
+ const today = await gem.leaderboards.get('daily');
11889
+ const yesterday = await gem.leaderboards.get('daily', { periodsBack: 1 });
11890
+ label(today.period.final ? 'Final standings' : 'Standings so far');
11891
+ ```
11892
+
11557
11893
  | Parameter | Type | Description |
11558
11894
  | --- | --- | --- |
11559
11895
  | `leaderboardType` | `string` | The board's id in your game config. |
11560
11896
  | `options` | `GetLeaderboardOptions` | |
11561
11897
 
11562
- **Throws.** `not_found` when no board with this id is in the channel's active
11898
+ **Throws.** `TypeError` when `periodsBack` is not an integer from 0 to 400,
11899
+ `period` is not a period id, or both are passed — refused before the
11900
+ request.
11901
+
11902
+ `not_found` when no board with this id is in the channel's active
11563
11903
  config, `invalid` when bucket values do not match the board's
11564
- `bucket_by`.
11904
+ `bucket_by`, when a period is asked of a board that is not `per_period`
11905
+ (`code` `invalid_period`), or when the period has not begun for the
11906
+ player (`code` `period_not_begun`).
11565
11907
 
11566
11908
  ## LeaderboardsApi.friends
11567
11909
 
@@ -11592,16 +11934,20 @@ for (const row of board.entries) {
11592
11934
  }
11593
11935
  ```
11594
11936
 
11937
+ On a `per_period` board this takes the same `periodsBack` and `period` as
11938
+ `get`. A friend in another time zone who is on the same date is on
11939
+ the same board.
11940
+
11595
11941
  | Parameter | Type |
11596
11942
  | --- | --- |
11597
11943
  | `leaderboardType` | `string` |
11598
11944
  | `options` | `GetLeaderboardOptions` |
11599
11945
 
11600
11946
  **Throws.** `not_found` when no board with this id is in the channel's active
11601
- config, `invalid` when bucket values do not match the board's
11602
- `bucket_by`, and `forbidden` in a context with no player — the friend
11603
- view is a player surface, and a game server has no friends. (The global
11604
- `get` declares no such refusal: it needs no viewer.)
11947
+ config, `invalid` when bucket values or a period do not match the board
11948
+ (as on `get`), and `forbidden` in a context with no player — the
11949
+ friend view is a player surface, and a game server has no friends. (The
11950
+ global `get` declares no such refusal: it needs no viewer.)
11605
11951
 
11606
11952
  ## LeaderboardsApi.mine
11607
11953
 
@@ -11637,6 +11983,11 @@ a `200` with no entries, not an error. If it is *always* empty, see
11637
11983
  [LeaderboardsApi](https://gemarcade.com/developer/api/game#leaderboardsapi) on where runs come from: a browser cannot submit
11638
11984
  one.
11639
11985
 
11986
+ On a `per_period` board the runs, the personal best and the total are ONE
11987
+ period's — the current one unless `periodsBack` or `period` names another
11988
+ — so `personalBest` is "your best today", and an empty result at the start
11989
+ of a day is the ordinary state.
11990
+
11640
11991
  | Parameter | Type |
11641
11992
  | --- | --- |
11642
11993
  | `leaderboardType` | `string` |
@@ -1,6 +1,6 @@
1
1
  # The server surface — API reference
2
2
 
3
- Generated from `@indiegems/gem-web-sdk/sdk` v7.5.0. Every symbol below links to its page on the Gem Arcade developer site.
3
+ Generated from `@indiegems/gem-web-sdk/sdk` v7.7.0. Every symbol below links to its page on the Gem Arcade developer site.
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