@indiegems/gem-web-sdk 7.4.2 → 7.6.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.4.2. Every symbol below links to its page on the Gem Arcade developer site.
3
+ Generated from `@indiegems/gem-web-sdk/sdk` v7.6.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
@@ -11011,6 +11013,31 @@ theirs. Use this for display-level comparisons, not for identity.
11011
11013
  | `a` | `string \| PlayerText` |
11012
11014
  | `b` | `string \| PlayerText` |
11013
11015
 
11016
+ ## GemUnframedError
11017
+
11018
+ [GemUnframedError](https://gemarcade.com/developer/api/game#gemunframederror)
11019
+
11020
+ ```ts
11021
+ class GemUnframedError
11022
+ ```
11023
+
11024
+ Your game was opened without a Gem frame around it.
11025
+
11026
+ Thrown by `createGem` when the page is the top-level document: nothing
11027
+ framed it, so nothing can hand it a player, saves, config or rooms. The SDK
11028
+ needs that frame — the Gem Arcade, the Gem Editor, or the Gem dev host on
11029
+ your own machine — and refuses to start without one rather than running a
11030
+ game that silently has no player.
11031
+
11032
+ You do not construct this. Run the game framed instead: `npm run dev`
11033
+ (`gemctl serve start . --dev`) while you edit, `gemctl serve start .` for a
11034
+ build, signed in to `gemctl` either way. Never open the address the dev
11035
+ server itself prints.
11036
+
11037
+ A unit test that constructs the SDK in a simulated DOM has to give its
11038
+ window a parent of its own, because a simulated top-level window is exactly
11039
+ what this refuses.
11040
+
11014
11041
  ## LeaderboardEntry.playerId
11015
11042
 
11016
11043
  [LeaderboardEntry.playerId](https://gemarcade.com/developer/api/game#leaderboardentry-playerid)
@@ -1,6 +1,6 @@
1
1
  # The server surface — API reference
2
2
 
3
- Generated from `@indiegems/gem-web-sdk/sdk` v7.4.2. Every symbol below links to its page on the Gem Arcade developer site.
3
+ Generated from `@indiegems/gem-web-sdk/sdk` v7.6.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
 
@@ -2,7 +2,7 @@
2
2
  "schema_version": 1,
3
3
  "source": {
4
4
  "repo": "IndieGems/gem-web-sdk",
5
- "version": "7.4.2"
5
+ "version": "7.6.0"
6
6
  },
7
7
  "entries": [
8
8
  {
@@ -5836,7 +5836,7 @@
5836
5836
  "url": "https://gemarcade.com/developer/api/game#framestate",
5837
5837
  "signature": "type FrameState = \"waiting\" | \"connected\" | \"refused\" | \"closed\"",
5838
5838
  "documented": true,
5839
- "summary": "Where the bridge to the arcade stands.\n\n`waiting` is the state a game boots in and the one it may sit in forever —\noutside the arcade there is no parent to answer, and that is not an error.\n`connected` is the only state in which the arcade is listening. `refused`\nmeans a page tried to embed this game and its origin was not in\n`allowedEmbedders`; `closed` means a connection existed and ended.\n\nReceived by `onStateChange` and resolved by `awaitBridge`, both of which are\ndocumented — so without a name for this union a game could observe the value\nand not be able to declare a variable holding it.",
5839
+ "summary": "Where the bridge to the arcade stands.\n\n`waiting` is the state a game boots in: framed, and its parent has not\nanswered yet. `connected` is the only state in which the arcade is\nlistening. `refused` is final: the page that framed this game is not one it\naccepts (its origin is not in `allowedEmbedders`), or it speaks no protocol\nversion in common. `closed` means a connection existed and ended.\n\nReceived by `onStateChange` and resolved by `awaitBridge`, both of which are\ndocumented — so without a name for this union a game could observe the value\nand not be able to declare a variable holding it.",
5840
5840
  "deprecated": null,
5841
5841
  "params": [],
5842
5842
  "returns": null,
@@ -5876,7 +5876,7 @@
5876
5876
  "url": "https://gemarcade.com/developer/api/game#gemframe-currentstate",
5877
5877
  "signature": "get currentState(): FrameState",
5878
5878
  "documented": true,
5879
- "summary": "Where the bridge is right now, without waiting.\n\n- `waiting` — the arcade has not connected yet. Normal at startup.\n- `connected` — the bridge is up and everything works.\n- `refused` — the arcade declined the connection.\n- `closed` — the session has ended.\n\nA snapshot, so it can change immediately after you read it. To wait for the\nquestion to be settled once, use [GemFrame.settled](https://gemarcade.com/developer/api/game#gemframe-settled).\n\nA game outside the arcade — opened directly during development — stays\n`waiting` forever, which is the case worth handling: boot anyway, without a\nbridge.",
5879
+ "summary": "Where the bridge is right now, without waiting.\n\n- `waiting` — the arcade has not connected yet. Normal at startup.\n- `connected` — the bridge is up and everything works.\n- `refused` — final: the page that framed the game is not one it accepts,\n or no protocol version is shared.\n- `closed` — the session has ended.\n\nA snapshot, so it can change immediately after you read it. To wait for the\nquestion to be settled once, use [GemFrame.settled](https://gemarcade.com/developer/api/game#gemframe-settled).\n\nA page that frames the game and never answers leaves it `waiting`, which\nis the case worth handling: boot anyway, and say there is no session.",
5880
5880
  "deprecated": null,
5881
5881
  "params": [],
5882
5882
  "returns": null,
@@ -5934,7 +5934,7 @@
5934
5934
  "url": "https://gemarcade.com/developer/api/game#gemframe-settled",
5935
5935
  "signature": "settled(timeoutMs?: number): Promise<FrameState>",
5936
5936
  "documented": true,
5937
- "summary": "Wait until it is known whether there is a bridge, then carry on either way.\n\nResolves as soon as the connection is settled — connected, refused or\nclosed — or when the timeout runs out, with whichever state it is in.\n\nTHE GATE TO AWAIT BEFORE LOADING A HEAVY BUNDLE. The connection offer is\ndelivered to your game rather than polled, so if your main thread is busy\nparsing several megabytes of JavaScript, the offer waits behind that work —\nand by the time it arrives the connection it carried may already be gone.\nAwaiting this first keeps the thread free long enough to accept.\n\nTHE WAIT IS BOUNDED ON PURPOSE, because an embedder is optional: your game\nmust still start when nothing ever answers, just without a bridge. Proceed\non either outcome and read the resolved state to know which kind of boot\nthis is.\n\n```js\nconst state = await gem.frame.settled();\nconst game = await import('./game-engine.js');\ngame.start({ online: state === 'connected' });\n```",
5937
+ "summary": "Wait until it is known whether there is a bridge, then carry on either way.\n\nResolves as soon as the connection is settled — connected, refused or\nclosed — or when the timeout runs out, with whichever state it is in.\n\nTHE GATE TO AWAIT BEFORE LOADING A HEAVY BUNDLE. The connection offer is\ndelivered to your game rather than polled, so if your main thread is busy\nparsing several megabytes of JavaScript, the offer waits behind that work —\nand by the time it arrives the connection it carried may already be gone.\nAwaiting this first keeps the thread free long enough to accept.\n\nTHE WAIT IS BOUNDED ON PURPOSE, because the page framing your game may\nnever answer, and your game must still start and say so. Proceed on either\noutcome and read the resolved state to know which kind of boot this is.\n\n```js\nconst state = await gem.frame.settled();\nconst game = await import('./game-engine.js');\ngame.start({ online: state === 'connected' });\n```",
5938
5938
  "deprecated": null,
5939
5939
  "params": [
5940
5940
  {
@@ -8171,7 +8171,7 @@
8171
8171
  "url": "https://gemarcade.com/developer/api/game#gemviewport-reserved",
8172
8172
  "signature": "readonly reserved: readonly GemReservedArea[]",
8173
8173
  "documented": true,
8174
- "summary": "Rectangles of your frame the arcade's own controls cover — its Back tab in\nthe top-left corner, and the wallets beside it while they are shown (see\n`setWalletVisible`). Keep essential UI and touch targets out of them. Empty\nwhen the arcade covers nothing.\n\nA stylesheet reads the per-corner custom properties the SDK stamps from\nthese instead — `--gem-reserved-tl-height` and friends.",
8174
+ "summary": "Rectangles of your frame the arcade's own controls cover — its Back tab in\nthe top-left corner, and the wallets in the top-right while they are shown (see\n`setWalletVisible`). Keep essential UI and touch targets out of them. Empty\nwhen the arcade covers nothing.\n\nA stylesheet reads the per-corner custom properties the SDK stamps from\nthese instead — `--gem-reserved-tl-height` and friends.",
8175
8175
  "deprecated": null,
8176
8176
  "params": [],
8177
8177
  "returns": null,
@@ -8495,7 +8495,7 @@
8495
8495
  "url": "https://gemarcade.com/developer/api/game#gem-awaitidentity",
8496
8496
  "signature": "awaitIdentity(timeoutMs?: number): Promise<GemIdentity>",
8497
8497
  "documented": true,
8498
- "summary": "The player's identity, waiting for sign-in if it has not finished.\n\nAwait this when you need the ids themselves. Rooms are scoped to your game,\nand your game's id comes from sign-in — but a room call made before it\nlands performs this same wait on your behalf, so awaiting it first is a\nchoice rather than a requirement. In a real session the wait is normally a\nsingle round trip to the page hosting your game.\n\nRejects rather than resolving `null` if the wait runs out: a session that\ncannot sign in cannot do anything else either, and a `null` you might not\ncheck is how a game carries on into a failure it could have reported.",
8498
+ "summary": "The player's identity, waiting for sign-in if it has not finished.\n\nAwait this when you need the ids themselves. Rooms are scoped to your game,\nand your game's id comes from sign-in — but a room call made before it\nlands performs this same wait on your behalf, so awaiting it first is a\nchoice rather than a requirement. In a real session the wait is normally a\nsingle round trip to the page hosting your game.\n\nRejects rather than resolving `null` if the wait runs out: a session that\ncannot sign in cannot do anything else either, and a `null` you might not\ncheck is how a game carries on into a failure it could have reported. It\nrejects at once, without waiting, when the page that framed the game was\nrefused: no token can arrive from it.",
8499
8499
  "deprecated": null,
8500
8500
  "params": [
8501
8501
  {
@@ -8519,7 +8519,7 @@
8519
8519
  "url": "https://gemarcade.com/developer/api/game#gem-awaitbridge",
8520
8520
  "signature": "awaitBridge(timeoutMs?: number): Promise<FrameState | null>",
8521
8521
  "documented": true,
8522
- "summary": "Waits for the connection to the arcade to settle, one way or the other.\n\nResolves with the state it settled into — connected, refused or closed — or\nwith whatever it had reached by `timeoutMs`, which defaults to 10 seconds.\n\nOUTSIDE THE ARCADE IT WAITS THE FULL TIMEOUT and then resolves `waiting`,\nbecause nothing ever answers. It does not return early. That is ten seconds\non every local run, so pass a shorter `timeoutMs` during development if the\nwait gets in your way.\n\n**Await this in your boot entry before importing your main bundle.** The\narcade hands your game its connection as a one-off delivery rather than\nsomething you can ask for later, so if the browser's main thread is busy\nparsing several megabytes of JavaScript when it arrives, your game can miss\nit. Awaiting this keeps the thread free through that window; the moment it\nresolves, load everything.\n\nCarry on whatever it resolves to. `connected` means you are in the arcade;\nanything else means you are running without it, which your game has to\nsurvive anyway — during local development, that is the normal case.",
8522
+ "summary": "Waits for the connection to the arcade to settle, one way or the other.\n\nResolves with the state it settled into — connected, refused or closed — or\nwith whatever it had reached by `timeoutMs`, which defaults to 10 seconds.\n\nA game is always framed (`createGem` throws [GemUnframedError](https://gemarcade.com/developer/api/game#gemunframederror)\notherwise), so this normally resolves `connected` within a moment. It\nresolves `refused` at once when the page that framed the game is not one it\naccepts, and `waiting` only when the page framing it never answers.\n\n**Await this in your boot entry before importing your main bundle.** The\narcade hands your game its connection as a one-off delivery rather than\nsomething you can ask for later, so if the browser's main thread is busy\nparsing several megabytes of JavaScript when it arrives, your game can miss\nit. Awaiting this keeps the thread free through that window; the moment it\nresolves, load everything.\n\nCarry on whatever it resolves to. `connected` means a Gem host is\nlistening — the Arcade, the Editor or the dev host on your machine;\nanything else means no session, which your game has to survive with a\nmessage rather than a hang.",
8523
8523
  "deprecated": null,
8524
8524
  "params": [
8525
8525
  {
@@ -8543,7 +8543,7 @@
8543
8543
  "url": "https://gemarcade.com/developer/api/game#gem-player",
8544
8544
  "signature": "readonly player: GemPlayer | null",
8545
8545
  "documented": true,
8546
- "summary": "The player's display identity — the name, handle and avatar you show them.\n\n`null` until the arcade sends it, and always `null` outside the arcade.\nNothing obliges it to be sent at all, so treat its absence as normal and\nhave a fallback: a game with no display name is fine, a game that waits\nforever for one is not.\n\nThe name and handle are player-authored, so they arrive wrapped rather than\nas plain strings — assigning one to `innerHTML` will not compile. Unwrap at\nthe point you render.\n\nThis is distinct from [Gem.identity](https://gemarcade.com/developer/api/game#gem-identity), which is who the platform says\nthey are, and from [Gem.me](https://gemarcade.com/developer/api/game#gem-me), which is their data in your game.",
8546
+ "summary": "The player's display identity — the name, handle and avatar you show them.\n\n`null` until the arcade sends it. Nothing obliges it to be sent at all, so treat its absence as normal and\nhave a fallback: a game with no display name is fine, a game that waits\nforever for one is not.\n\nThe name and handle are player-authored, so they arrive wrapped rather than\nas plain strings — assigning one to `innerHTML` will not compile. Unwrap at\nthe point you render.\n\nThis is distinct from [Gem.identity](https://gemarcade.com/developer/api/game#gem-identity), which is who the platform says\nthey are, and from [Gem.me](https://gemarcade.com/developer/api/game#gem-me), which is their data in your game.",
8547
8547
  "deprecated": null,
8548
8548
  "params": [],
8549
8549
  "returns": null,
@@ -8960,7 +8960,7 @@
8960
8960
  "url": "https://gemarcade.com/developer/api/game#gem-awaitcohorts",
8961
8961
  "signature": "awaitCohorts(timeoutMs?: number): Promise<GemCohorts | null>",
8962
8962
  "documented": true,
8963
- "summary": "The experiment cohorts, waiting up to `timeoutMs` for them.\n\nResolves `null` rather than rejecting — outside the arcade too, once the\nwait is out, because no parent is there to answer. Use it at the ONE point\nyour game branches on a cohort — typically\nthe first run's opening — and give it a short budget: the answer normally\narrives within the first second, and a player kept on a splash screen for a\nvalue that changes a tutorial's wording has been made to pay for an\nexperiment they cannot see.",
8963
+ "summary": "The experiment cohorts, waiting up to `timeoutMs` for them.\n\nResolves `null` rather than rejecting once the wait is out, or at once if\nthe page that framed the game was refused. Use it at the ONE point\nyour game branches on a cohort — typically\nthe first run's opening — and give it a short budget: the answer normally\narrives within the first second, and a player kept on a splash screen for a\nvalue that changes a tutorial's wording has been made to pay for an\nexperiment they cannot see.",
8964
8964
  "deprecated": null,
8965
8965
  "params": [
8966
8966
  {
@@ -9032,7 +9032,7 @@
9032
9032
  "url": "https://gemarcade.com/developer/api/game#gem-setwalletvisible",
9033
9033
  "signature": "setWalletVisible(visible: boolean, options?: WalletVisibleOptions): boolean",
9034
9034
  "documented": true,
9035
- "summary": "Show or hide the player's wallets — Gems, Tickets, the achievement score —\nthat the arcade draws over your game. **They are shown until you say\notherwise**: hide them during gameplay, show them again on your menus.\n\n```js\nlevel.onStart = () => gem.setWalletVisible(false);\nmenu.onOpen = () => gem.setWalletVisible(true);\n// A screen that sells in one currency may narrow it:\nticketStore.onOpen = () => gem.setWalletVisible(true, { only: ['ARCADE_TICKETS'] });\n```\n\n**It is screen state, not a toggle.** Each call is the whole answer and\nreplaces the last. While hidden, the arcade still pops a wallet up for a\nmoment when something happens to it — a reward you cued, a spend, a\nlanding — and your state never cancels that. Shown wallets cover part of\nyour frame beside the Back tab, which arrives as a rectangle in\n`gem.viewport.reserved`; hiding them gives that space back. A wallet the\narcade pops up for a moment floats over your game and moves nothing.\n\n**Never draw a platform balance yourself**, and never show a pending state\nfor one: the arcade takes every figure from the platform. If this returns\n`false`, do nothing in its place.",
9035
+ "summary": "Show or hide the player's wallets — Gems, Tickets, the achievement score —\nthat the arcade draws over your game. **They are shown until you say\notherwise**: hide them during gameplay, show them again on your menus.\n\n```js\nlevel.onStart = () => gem.setWalletVisible(false);\nmenu.onOpen = () => gem.setWalletVisible(true);\n// A screen that sells in one currency may narrow it:\nticketStore.onOpen = () => gem.setWalletVisible(true, { only: ['ARCADE_TICKETS'] });\n```\n\n**It is screen state, not a toggle.** Each call is the whole answer and\nreplaces the last. While hidden, the arcade still pops a wallet up for a\nmoment when something happens to it — a reward you cued, a spend, a\nlanding — and your state never cancels that. Shown wallets cover part of\nyour frame's top-right corner, which arrives as a rectangle in\n`gem.viewport.reserved`; hiding them gives that space back. A wallet the\narcade pops up for a moment floats over your game and moves nothing.\n\n**Never draw a platform balance yourself**, and never show a pending state\nfor one: the arcade takes every figure from the platform. If this returns\n`false`, do nothing in its place.",
9036
9036
  "deprecated": null,
9037
9037
  "params": [
9038
9038
  {
@@ -9103,7 +9103,7 @@
9103
9103
  "url": "https://gemarcade.com/developer/api/game#gem-frame",
9104
9104
  "signature": "readonly frame: GemFrame",
9105
9105
  "documented": true,
9106
- "summary": "Your game's connection to the arcade.\n\nALWAYS PRESENT, even when nothing is on the other end. Running outside the\narcade does not make this `null` — the frame simply stays in `waiting`\nforever, which is the state to read when you want to know whether anyone\never answered. Do not write a `=== null` branch; it can never run.\n\n**This is where the loading contract lives.** Call `progress()` as your\ngame loads and `ready()` when it is playable, so the arcade can show a real\nloading state instead of a blank frame. Both are on the platform's release\nchecklist.",
9106
+ "summary": "Your game's connection to the arcade.\n\nALWAYS PRESENT, even when nothing is on the other end yet: a page that\nnever answers leaves the frame in `waiting`, and a refused one ends it in\n`refused`, which is the state to read when you want to know whether anyone\never answered. Do not write a `=== null` branch; it can never run.\n\n**This is where the loading contract lives.** Call `progress()` as your\ngame loads and `ready()` when it is playable, so the arcade can show a real\nloading state instead of a blank frame. Both are on the platform's release\nchecklist.",
9107
9107
  "deprecated": null,
9108
9108
  "params": [],
9109
9109
  "returns": null,
@@ -9241,7 +9241,7 @@
9241
9241
  "url": "https://gemarcade.com/developer/api/game#creategem",
9242
9242
  "signature": "function createGem(options?: GemOptions): Gem",
9243
9243
  "documented": true,
9244
- "summary": "Connect your game to the Gem Arcade. This is the one call every game makes.\n\nIt assembles everything and connects to the arcade that framed your game. The\nsame code runs on your machine and in production without a flag — but it needs\na parent either way: run it framed, and until one answers the SDK says so\nrather than letting you assume a session you do not have.\n\nOne dependency, imported like anything else. The install is at\nhttps://gemarcade.com/developer/docs/getting-started#3-add-the-sdk\n\n```js\nimport { createGem } from '@indiegems/gem-web-sdk/sdk';\n\nconst gem = createGem();\n\nawait gem.awaitBridge(); // before loading your main bundle\nconst me = await gem.awaitIdentity(); // who is playing, once sign-in completes\n```\n\nNo options are needed. The Gem Arcade's website and app are always allowed to\nembed your game ([ARCADE_EMBEDDERS](https://gemarcade.com/developer/api/game#arcade_embedders)), and the arcade tells your game\nwhich build it is.\n\nA dev host on your own machine (`gemctl serve`, the Gem Editor) needs no\nentry either: it frames a locally-served game from a loopback origin whose\nport changes every run, and the SDK admits it by rule — only while the game\nitself is served from loopback, so nothing changes once you publish.\n\nPass `allowedEmbedders` only when a site of your own frames your game. It\nadds to the arcade's origins; see [GemOptions.allowedEmbedders](https://gemarcade.com/developer/api/game#gemoptions-allowedembedders).",
9244
+ "summary": "Connect your game to the Gem Arcade. This is the one call every game makes.\n\nIt assembles everything and connects to the arcade that framed your game. The\nsame code runs on your machine and in production without a flag — but it needs\na frame either way. Opened at the top level it throws\n[GemUnframedError](https://gemarcade.com/developer/api/game#gemunframederror), which says how to run it: in the Arcade, the Editor,\nor the Gem dev host on your machine (`npm run dev`, signed in to `gemctl`).\n\nOne dependency, imported like anything else. The install is at\nhttps://gemarcade.com/developer/docs/getting-started#3-add-the-sdk\n\n```js\nimport { createGem } from '@indiegems/gem-web-sdk/sdk';\n\nconst gem = createGem();\n\nawait gem.awaitBridge(); // before loading your main bundle\nconst me = await gem.awaitIdentity(); // who is playing, once sign-in completes\n```\n\nNo options are needed. The Gem Arcade's website and app are always allowed to\nembed your game ([ARCADE_EMBEDDERS](https://gemarcade.com/developer/api/game#arcade_embedders)), and the arcade tells your game\nwhich build it is.\n\nA dev host on your own machine (`gemctl serve`, the Gem Editor) needs no\nentry either: it frames a locally-served game from a loopback origin whose\nport changes every run, and the SDK admits it by rule — only while the game\nitself is served from loopback, so nothing changes once you publish.\n\nPass `allowedEmbedders` only when a site of your own frames your game. It\nadds to the arcade's origins; see [GemOptions.allowedEmbedders](https://gemarcade.com/developer/api/game#gemoptions-allowedembedders).",
9245
9245
  "deprecated": null,
9246
9246
  "params": [
9247
9247
  {
@@ -12980,6 +12980,23 @@
12980
12980
  "defaultValue": null,
12981
12981
  "guides": []
12982
12982
  },
12983
+ {
12984
+ "id": "GemUnframedError",
12985
+ "name": "GemUnframedError",
12986
+ "anchor": "gemunframederror",
12987
+ "group": "failures",
12988
+ "url": "https://gemarcade.com/developer/api/game#gemunframederror",
12989
+ "signature": "class GemUnframedError",
12990
+ "documented": true,
12991
+ "summary": "Your game was opened without a Gem frame around it.\n\nThrown by `createGem` when the page is the top-level document: nothing\nframed it, so nothing can hand it a player, saves, config or rooms. The SDK\nneeds that frame — the Gem Arcade, the Gem Editor, or the Gem dev host on\nyour own machine — and refuses to start without one rather than running a\ngame that silently has no player.\n\nYou do not construct this. Run the game framed instead: `npm run dev`\n(`gemctl serve start . --dev`) while you edit, `gemctl serve start .` for a\nbuild, signed in to `gemctl` either way. Never open the address the dev\nserver itself prints.\n\nA unit test that constructs the SDK in a simulated DOM has to give its\nwindow a parent of its own, because a simulated top-level window is exactly\nwhat this refuses.",
12992
+ "deprecated": null,
12993
+ "params": [],
12994
+ "returns": null,
12995
+ "throws": null,
12996
+ "example": null,
12997
+ "defaultValue": null,
12998
+ "guides": []
12999
+ },
12983
13000
  {
12984
13001
  "id": "LeaderboardEntry.playerId",
12985
13002
  "name": "playerId",