@indiegems/gem-web-sdk 7.9.1 → 8.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +30 -0
- package/README.md +1 -0
- package/dist/function.d.ts +26 -1
- package/dist/game.d.ts +32 -1
- package/dist/gem-web-sdk.bridge.js +1 -1
- package/dist/gem-web-sdk.esm.js +2 -2
- package/dist/gem-web-sdk.function.js +2 -2
- package/dist/gem-web-sdk.game.js +2 -2
- package/dist/gem-web-sdk.min.js +2 -2
- package/dist/gem-web-sdk.sdk.js +2 -2
- package/dist/gem-web-sdk.server.js +1 -1
- package/dist/index.d.ts +12 -3
- package/dist/sdk.d.ts +32 -1
- package/docs/api/api-reference-game.md +424 -8
- package/docs/api/api-reference-server.md +1 -1
- package/docs/api/reference.json +441 -5
- package/docs/changelog.md +30 -0
- package/docs/friends.md +86 -11
- package/docs/getting-started.md +10 -6
- package/docs/manifest.json +21 -16
- package/docs/saving-player-progress.md +41 -0
- package/docs/server-bundles.md +2 -2
- package/docs/serverless-functions-js.md +3 -2
- package/docs/serverless-functions.md +53 -7
- package/docs/vibration.md +141 -0
- package/docs/what-a-game-must-do.md +12 -0
- package/package.json +1 -1
- package/schema/bridge.v1.json +60 -0
- package/schema/capabilities.public.json +9 -0
package/docs/api/reference.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"schema_version": 1,
|
|
3
3
|
"source": {
|
|
4
4
|
"repo": "IndieGems/gem-web-sdk",
|
|
5
|
-
"version": "
|
|
5
|
+
"version": "8.0.0"
|
|
6
6
|
},
|
|
7
7
|
"entries": [
|
|
8
8
|
{
|
|
@@ -61,6 +61,10 @@
|
|
|
61
61
|
"key": "share",
|
|
62
62
|
"title": "Sharing a link to your game"
|
|
63
63
|
},
|
|
64
|
+
{
|
|
65
|
+
"key": "vibration",
|
|
66
|
+
"title": "Vibrating the device"
|
|
67
|
+
},
|
|
64
68
|
{
|
|
65
69
|
"key": "config",
|
|
66
70
|
"title": "Your published config"
|
|
@@ -6184,6 +6188,30 @@
|
|
|
6184
6188
|
"defaultValue": null,
|
|
6185
6189
|
"guides": []
|
|
6186
6190
|
},
|
|
6191
|
+
{
|
|
6192
|
+
"id": "GemFrame.requestVibrate",
|
|
6193
|
+
"name": "requestVibrate",
|
|
6194
|
+
"anchor": "gemframe-requestvibrate",
|
|
6195
|
+
"group": "frame",
|
|
6196
|
+
"url": "https://gemarcade.com/developer/api/game#gemframe-requestvibrate",
|
|
6197
|
+
"signature": "requestVibrate(pattern: readonly number[]): boolean",
|
|
6198
|
+
"documented": true,
|
|
6199
|
+
"summary": "Ask the arcade to play a vibration pattern where this frame has no\n`navigator.vibrate` of its own. Nothing comes back.\n\nPrefer `gem.vibration.vibrate`, which calls `navigator.vibrate` itself\nwhere it exists, normalizes the pattern and caps its total length. This one\nchecks only the message's bounds, sends only while connected, and only to\nan arcade that handles vibration.",
|
|
6200
|
+
"deprecated": null,
|
|
6201
|
+
"params": [
|
|
6202
|
+
{
|
|
6203
|
+
"name": "pattern",
|
|
6204
|
+
"type": "readonly number[]",
|
|
6205
|
+
"optional": false,
|
|
6206
|
+
"summary": null
|
|
6207
|
+
}
|
|
6208
|
+
],
|
|
6209
|
+
"returns": "whether the message left the frame — `false` before the bridge is\n up, for an arcade that does not handle vibration, and past this frame's\n own limit of 600 a minute.",
|
|
6210
|
+
"throws": null,
|
|
6211
|
+
"example": null,
|
|
6212
|
+
"defaultValue": null,
|
|
6213
|
+
"guides": []
|
|
6214
|
+
},
|
|
6187
6215
|
{
|
|
6188
6216
|
"id": "GemFrame.reportFrameStats",
|
|
6189
6217
|
"name": "reportFrameStats",
|
|
@@ -6444,7 +6472,7 @@
|
|
|
6444
6472
|
"url": "https://gemarcade.com/developer/api/game#listfriendsoptions-limit",
|
|
6445
6473
|
"signature": "readonly limit?: number",
|
|
6446
6474
|
"documented": true,
|
|
6447
|
-
"summary": "Friends per page — default 50, at most 200. A player holds at most 1000\nfriends, so five max-size pages is the whole list.",
|
|
6475
|
+
"summary": "Friends per page — default 50, at most 200. A player holds at most 1000\nfriends, so five max-size pages is the whole list. A larger value is\nclamped to 200; a value that is not a non-negative integer is refused with\n`invalid`.",
|
|
6448
6476
|
"deprecated": null,
|
|
6449
6477
|
"params": [],
|
|
6450
6478
|
"returns": null,
|
|
@@ -6487,6 +6515,261 @@
|
|
|
6487
6515
|
"defaultValue": null,
|
|
6488
6516
|
"guides": []
|
|
6489
6517
|
},
|
|
6518
|
+
{
|
|
6519
|
+
"id": "FriendChallenge",
|
|
6520
|
+
"name": "FriendChallenge",
|
|
6521
|
+
"anchor": "friendchallenge",
|
|
6522
|
+
"group": "friends",
|
|
6523
|
+
"url": "https://gemarcade.com/developer/api/game#friendchallenge",
|
|
6524
|
+
"signature": "interface FriendChallenge",
|
|
6525
|
+
"documented": true,
|
|
6526
|
+
"summary": "One friend challenge this player received, in THIS game on THIS channel.\n\nWhat the platform holds, not what the card said: there is no rendered line,\nbecause your game renders its own copy from [FriendChallenge.template](https://gemarcade.com/developer/api/game#friendchallenge-template)\nand [FriendChallenge.params](https://gemarcade.com/developer/api/game#friendchallenge-params). Who sent it is\n[FriendChallenge.from](https://gemarcade.com/developer/api/game#friendchallenge-from), resolved by the platform from its own records.",
|
|
6527
|
+
"deprecated": null,
|
|
6528
|
+
"params": [],
|
|
6529
|
+
"returns": null,
|
|
6530
|
+
"throws": null,
|
|
6531
|
+
"example": null,
|
|
6532
|
+
"defaultValue": null,
|
|
6533
|
+
"guides": []
|
|
6534
|
+
},
|
|
6535
|
+
{
|
|
6536
|
+
"id": "FriendChallenge.id",
|
|
6537
|
+
"name": "id",
|
|
6538
|
+
"anchor": "friendchallenge-id",
|
|
6539
|
+
"group": "friends",
|
|
6540
|
+
"url": "https://gemarcade.com/developer/api/game#friendchallenge-id",
|
|
6541
|
+
"signature": "readonly id: string",
|
|
6542
|
+
"documented": true,
|
|
6543
|
+
"summary": "The challenge's id — the value the Arcade puts in the `gemChallenge`\nlaunch key, and what [FriendsApi.markAnswered](https://gemarcade.com/developer/api/game#friendsapi-markanswered) takes. Opaque: compare\nit, store it, never parse it.",
|
|
6544
|
+
"deprecated": null,
|
|
6545
|
+
"params": [],
|
|
6546
|
+
"returns": null,
|
|
6547
|
+
"throws": null,
|
|
6548
|
+
"example": null,
|
|
6549
|
+
"defaultValue": null,
|
|
6550
|
+
"guides": []
|
|
6551
|
+
},
|
|
6552
|
+
{
|
|
6553
|
+
"id": "FriendChallenge.from",
|
|
6554
|
+
"name": "from",
|
|
6555
|
+
"anchor": "friendchallenge-from",
|
|
6556
|
+
"group": "friends",
|
|
6557
|
+
"url": "https://gemarcade.com/developer/api/game#friendchallenge-from",
|
|
6558
|
+
"signature": "readonly from: Friend",
|
|
6559
|
+
"documented": true,
|
|
6560
|
+
"summary": "Who sent it, with the same display identity a [Friend](https://gemarcade.com/developer/api/game#friend) row carries.\n\nThe ONLY name to show. Never read a name out of\n[FriendChallenge.params](https://gemarcade.com/developer/api/game#friendchallenge-params): a name in a param is one nobody checked.",
|
|
6561
|
+
"deprecated": null,
|
|
6562
|
+
"params": [],
|
|
6563
|
+
"returns": null,
|
|
6564
|
+
"throws": null,
|
|
6565
|
+
"example": null,
|
|
6566
|
+
"defaultValue": null,
|
|
6567
|
+
"guides": []
|
|
6568
|
+
},
|
|
6569
|
+
{
|
|
6570
|
+
"id": "FriendChallenge.template",
|
|
6571
|
+
"name": "template",
|
|
6572
|
+
"anchor": "friendchallenge-template",
|
|
6573
|
+
"group": "friends",
|
|
6574
|
+
"url": "https://gemarcade.com/developer/api/game#friendchallenge-template",
|
|
6575
|
+
"signature": "readonly template: string",
|
|
6576
|
+
"documented": true,
|
|
6577
|
+
"summary": "The `friend_challenge_templates[].key` it was sent with — which of your\ntemplates, so your game knows what was asked.",
|
|
6578
|
+
"deprecated": null,
|
|
6579
|
+
"params": [],
|
|
6580
|
+
"returns": null,
|
|
6581
|
+
"throws": null,
|
|
6582
|
+
"example": null,
|
|
6583
|
+
"defaultValue": null,
|
|
6584
|
+
"guides": []
|
|
6585
|
+
},
|
|
6586
|
+
{
|
|
6587
|
+
"id": "FriendChallenge.params",
|
|
6588
|
+
"name": "params",
|
|
6589
|
+
"anchor": "friendchallenge-params",
|
|
6590
|
+
"group": "friends",
|
|
6591
|
+
"url": "https://gemarcade.com/developer/api/game#friendchallenge-params",
|
|
6592
|
+
"signature": "readonly params: Readonly<Record<string, string | number>>",
|
|
6593
|
+
"documented": true,
|
|
6594
|
+
"summary": "The params exactly as sent: integers as numbers, date, enum and code\nparams as strings. The template constrained them when the challenge was\nsent, but the config may have changed since, so check each one against\nwhat your game expects before it does anything — as you would a launch\nkey.",
|
|
6595
|
+
"deprecated": null,
|
|
6596
|
+
"params": [],
|
|
6597
|
+
"returns": null,
|
|
6598
|
+
"throws": null,
|
|
6599
|
+
"example": null,
|
|
6600
|
+
"defaultValue": null,
|
|
6601
|
+
"guides": []
|
|
6602
|
+
},
|
|
6603
|
+
{
|
|
6604
|
+
"id": "FriendChallenge.createdAt",
|
|
6605
|
+
"name": "createdAt",
|
|
6606
|
+
"anchor": "friendchallenge-createdat",
|
|
6607
|
+
"group": "friends",
|
|
6608
|
+
"url": "https://gemarcade.com/developer/api/game#friendchallenge-createdat",
|
|
6609
|
+
"signature": "readonly createdAt: number",
|
|
6610
|
+
"documented": true,
|
|
6611
|
+
"summary": "When it was sent, in Unix seconds.",
|
|
6612
|
+
"deprecated": null,
|
|
6613
|
+
"params": [],
|
|
6614
|
+
"returns": null,
|
|
6615
|
+
"throws": null,
|
|
6616
|
+
"example": null,
|
|
6617
|
+
"defaultValue": null,
|
|
6618
|
+
"guides": []
|
|
6619
|
+
},
|
|
6620
|
+
{
|
|
6621
|
+
"id": "FriendChallenge.openedAt",
|
|
6622
|
+
"name": "openedAt",
|
|
6623
|
+
"anchor": "friendchallenge-openedat",
|
|
6624
|
+
"group": "friends",
|
|
6625
|
+
"url": "https://gemarcade.com/developer/api/game#friendchallenge-openedat",
|
|
6626
|
+
"signature": "readonly openedAt?: number",
|
|
6627
|
+
"documented": true,
|
|
6628
|
+
"summary": "When the player pressed Play on it in the Arcade, in Unix seconds. Absent\nuntil they have.",
|
|
6629
|
+
"deprecated": null,
|
|
6630
|
+
"params": [],
|
|
6631
|
+
"returns": null,
|
|
6632
|
+
"throws": null,
|
|
6633
|
+
"example": null,
|
|
6634
|
+
"defaultValue": null,
|
|
6635
|
+
"guides": []
|
|
6636
|
+
},
|
|
6637
|
+
{
|
|
6638
|
+
"id": "FriendChallenge.answeredAt",
|
|
6639
|
+
"name": "answeredAt",
|
|
6640
|
+
"anchor": "friendchallenge-answeredat",
|
|
6641
|
+
"group": "friends",
|
|
6642
|
+
"url": "https://gemarcade.com/developer/api/game#friendchallenge-answeredat",
|
|
6643
|
+
"signature": "readonly answeredAt?: number",
|
|
6644
|
+
"documented": true,
|
|
6645
|
+
"summary": "When a game marked it answered with [FriendsApi.markAnswered](https://gemarcade.com/developer/api/game#friendsapi-markanswered), in\nUnix seconds. Absent until one has — so present on a relaunch of a\nchallenge already played, which is how a replay is told from a first\nattempt.",
|
|
6646
|
+
"deprecated": null,
|
|
6647
|
+
"params": [],
|
|
6648
|
+
"returns": null,
|
|
6649
|
+
"throws": null,
|
|
6650
|
+
"example": null,
|
|
6651
|
+
"defaultValue": null,
|
|
6652
|
+
"guides": []
|
|
6653
|
+
},
|
|
6654
|
+
{
|
|
6655
|
+
"id": "FriendChallengesPage",
|
|
6656
|
+
"name": "FriendChallengesPage",
|
|
6657
|
+
"anchor": "friendchallengespage",
|
|
6658
|
+
"group": "friends",
|
|
6659
|
+
"url": "https://gemarcade.com/developer/api/game#friendchallengespage",
|
|
6660
|
+
"signature": "interface FriendChallengesPage",
|
|
6661
|
+
"documented": true,
|
|
6662
|
+
"summary": "One page of the friend challenges this player holds in this game.",
|
|
6663
|
+
"deprecated": null,
|
|
6664
|
+
"params": [],
|
|
6665
|
+
"returns": null,
|
|
6666
|
+
"throws": null,
|
|
6667
|
+
"example": null,
|
|
6668
|
+
"defaultValue": null,
|
|
6669
|
+
"guides": []
|
|
6670
|
+
},
|
|
6671
|
+
{
|
|
6672
|
+
"id": "FriendChallengesPage.challenges",
|
|
6673
|
+
"name": "challenges",
|
|
6674
|
+
"anchor": "friendchallengespage-challenges",
|
|
6675
|
+
"group": "friends",
|
|
6676
|
+
"url": "https://gemarcade.com/developer/api/game#friendchallengespage-challenges",
|
|
6677
|
+
"signature": "readonly challenges: readonly FriendChallenge[]",
|
|
6678
|
+
"documented": true,
|
|
6679
|
+
"summary": "The page's challenges, newest first, answered ones last.",
|
|
6680
|
+
"deprecated": null,
|
|
6681
|
+
"params": [],
|
|
6682
|
+
"returns": null,
|
|
6683
|
+
"throws": null,
|
|
6684
|
+
"example": null,
|
|
6685
|
+
"defaultValue": null,
|
|
6686
|
+
"guides": []
|
|
6687
|
+
},
|
|
6688
|
+
{
|
|
6689
|
+
"id": "FriendChallengesPage.nextCursor",
|
|
6690
|
+
"name": "nextCursor",
|
|
6691
|
+
"anchor": "friendchallengespage-nextcursor",
|
|
6692
|
+
"group": "friends",
|
|
6693
|
+
"url": "https://gemarcade.com/developer/api/game#friendchallengespage-nextcursor",
|
|
6694
|
+
"signature": "readonly nextCursor?: string",
|
|
6695
|
+
"documented": true,
|
|
6696
|
+
"summary": "Present exactly when another page exists — pass it back as\n[ListFriendChallengesOptions.cursor](https://gemarcade.com/developer/api/game#listfriendchallengesoptions-cursor) to continue. Opaque: mint\nnothing, store nothing, compare nothing.",
|
|
6697
|
+
"deprecated": null,
|
|
6698
|
+
"params": [],
|
|
6699
|
+
"returns": null,
|
|
6700
|
+
"throws": null,
|
|
6701
|
+
"example": null,
|
|
6702
|
+
"defaultValue": null,
|
|
6703
|
+
"guides": []
|
|
6704
|
+
},
|
|
6705
|
+
{
|
|
6706
|
+
"id": "ListFriendChallengesOptions",
|
|
6707
|
+
"name": "ListFriendChallengesOptions",
|
|
6708
|
+
"anchor": "listfriendchallengesoptions",
|
|
6709
|
+
"group": "friends",
|
|
6710
|
+
"url": "https://gemarcade.com/developer/api/game#listfriendchallengesoptions",
|
|
6711
|
+
"signature": "interface ListFriendChallengesOptions",
|
|
6712
|
+
"documented": true,
|
|
6713
|
+
"summary": "What [FriendsApi.inbox](https://gemarcade.com/developer/api/game#friendsapi-inbox) takes.",
|
|
6714
|
+
"deprecated": null,
|
|
6715
|
+
"params": [],
|
|
6716
|
+
"returns": null,
|
|
6717
|
+
"throws": null,
|
|
6718
|
+
"example": null,
|
|
6719
|
+
"defaultValue": null,
|
|
6720
|
+
"guides": []
|
|
6721
|
+
},
|
|
6722
|
+
{
|
|
6723
|
+
"id": "ListFriendChallengesOptions.limit",
|
|
6724
|
+
"name": "limit",
|
|
6725
|
+
"anchor": "listfriendchallengesoptions-limit",
|
|
6726
|
+
"group": "friends",
|
|
6727
|
+
"url": "https://gemarcade.com/developer/api/game#listfriendchallengesoptions-limit",
|
|
6728
|
+
"signature": "readonly limit?: number",
|
|
6729
|
+
"documented": true,
|
|
6730
|
+
"summary": "Challenges per page — default 50, at most 200. The platform keeps at most\n200 per player, so one max-size page is the whole inbox. A larger value is\nclamped to 200; a value that is not a non-negative integer is refused with\n`invalid`.",
|
|
6731
|
+
"deprecated": null,
|
|
6732
|
+
"params": [],
|
|
6733
|
+
"returns": null,
|
|
6734
|
+
"throws": null,
|
|
6735
|
+
"example": null,
|
|
6736
|
+
"defaultValue": null,
|
|
6737
|
+
"guides": []
|
|
6738
|
+
},
|
|
6739
|
+
{
|
|
6740
|
+
"id": "ListFriendChallengesOptions.cursor",
|
|
6741
|
+
"name": "cursor",
|
|
6742
|
+
"anchor": "listfriendchallengesoptions-cursor",
|
|
6743
|
+
"group": "friends",
|
|
6744
|
+
"url": "https://gemarcade.com/developer/api/game#listfriendchallengesoptions-cursor",
|
|
6745
|
+
"signature": "readonly cursor?: string",
|
|
6746
|
+
"documented": true,
|
|
6747
|
+
"summary": "The previous page's [FriendChallengesPage.nextCursor](https://gemarcade.com/developer/api/game#friendchallengespage-nextcursor), verbatim. A\ncursor the platform did not mint is refused with `invalid`.",
|
|
6748
|
+
"deprecated": null,
|
|
6749
|
+
"params": [],
|
|
6750
|
+
"returns": null,
|
|
6751
|
+
"throws": null,
|
|
6752
|
+
"example": null,
|
|
6753
|
+
"defaultValue": null,
|
|
6754
|
+
"guides": []
|
|
6755
|
+
},
|
|
6756
|
+
{
|
|
6757
|
+
"id": "ListFriendChallengesOptions.signal",
|
|
6758
|
+
"name": "signal",
|
|
6759
|
+
"anchor": "listfriendchallengesoptions-signal",
|
|
6760
|
+
"group": "friends",
|
|
6761
|
+
"url": "https://gemarcade.com/developer/api/game#listfriendchallengesoptions-signal",
|
|
6762
|
+
"signature": "readonly signal?: AbortSignal",
|
|
6763
|
+
"documented": true,
|
|
6764
|
+
"summary": "Abort the read.",
|
|
6765
|
+
"deprecated": null,
|
|
6766
|
+
"params": [],
|
|
6767
|
+
"returns": null,
|
|
6768
|
+
"throws": null,
|
|
6769
|
+
"example": null,
|
|
6770
|
+
"defaultValue": null,
|
|
6771
|
+
"guides": []
|
|
6772
|
+
},
|
|
6490
6773
|
{
|
|
6491
6774
|
"id": "FriendChallengeRequest",
|
|
6492
6775
|
"name": "FriendChallengeRequest",
|
|
@@ -6801,7 +7084,7 @@
|
|
|
6801
7084
|
"url": "https://gemarcade.com/developer/api/game#friendsapi",
|
|
6802
7085
|
"signature": "class FriendsApi",
|
|
6803
7086
|
"documented": true,
|
|
6804
|
-
"summary": "Reading the signed-in player's accepted friends,
|
|
7087
|
+
"summary": "Reading the signed-in player's accepted friends, asking the Arcade to send\none of them a friend challenge, and reading the friend challenges this\nplayer received in this game.\n\nReached as `gem.friends`. Display identity only, and every accepted friend\nappears whether or not they have played this game — an invite-a-friend flow\nneeds exactly the ones who have not. The graph itself — requests, accepts,\nblocks — is managed on the Arcade, not here. So is a friend challenge's\nsend: [FriendsApi.challenge](https://gemarcade.com/developer/api/game#friendsapi-challenge) asks, and the Arcade confirms with the\nplayer and sends. The receiving side is [FriendsApi.launchedChallenge](https://gemarcade.com/developer/api/game#friendsapi-launchedchallenge)\n(the challenge this frame was launched with), [FriendsApi.inbox](https://gemarcade.com/developer/api/game#friendsapi-inbox) and\n[FriendsApi.markAnswered](https://gemarcade.com/developer/api/game#friendsapi-markanswered).\n\n```js\nlet page = await gem.friends.list();\nrender(page.friends);\nwhile (page.nextCursor) {\n page = await gem.friends.list({ cursor: page.nextCursor });\n render(page.friends);\n}\n```",
|
|
6805
7088
|
"deprecated": null,
|
|
6806
7089
|
"params": [],
|
|
6807
7090
|
"returns": null,
|
|
@@ -6829,7 +7112,7 @@
|
|
|
6829
7112
|
}
|
|
6830
7113
|
],
|
|
6831
7114
|
"returns": null,
|
|
6832
|
-
"throws": "`invalid` when the cursor is not one a previous page minted,\n `forbidden` in a context with no player — this is a player surface, and\n a game server has no friend list.",
|
|
7115
|
+
"throws": "`invalid` when the cursor is not one a previous page minted or\n `limit` is not a non-negative integer,\n `forbidden` in a context with no player — this is a player surface, and\n a game server has no friend list.",
|
|
6833
7116
|
"example": null,
|
|
6834
7117
|
"defaultValue": null,
|
|
6835
7118
|
"guides": []
|
|
@@ -6858,6 +7141,78 @@
|
|
|
6858
7141
|
"defaultValue": null,
|
|
6859
7142
|
"guides": []
|
|
6860
7143
|
},
|
|
7144
|
+
{
|
|
7145
|
+
"id": "FriendsApi.inbox",
|
|
7146
|
+
"name": "inbox",
|
|
7147
|
+
"anchor": "friendsapi-inbox",
|
|
7148
|
+
"group": "friends",
|
|
7149
|
+
"url": "https://gemarcade.com/developer/api/game#friendsapi-inbox",
|
|
7150
|
+
"signature": "inbox(options?: ListFriendChallengesOptions): Promise<FriendChallengesPage>",
|
|
7151
|
+
"documented": true,
|
|
7152
|
+
"summary": "Read one page of the friend challenges this player holds in THIS game on\nTHIS channel — in every state. Answered challenges are INCLUDED, and sort\nafter the rest; within each part, newest first.\n\nPass the result's `nextCursor` back as `cursor` until it stops arriving.\nA guest holds no challenges, so a guest's page is empty rather than a\nrefusal — build the screen for the empty state, not around an error.\n\nFor the one challenge this frame was launched with, use\n[FriendsApi.launchedChallenge](https://gemarcade.com/developer/api/game#friendsapi-launchedchallenge) rather than searching this list.",
|
|
7153
|
+
"deprecated": null,
|
|
7154
|
+
"params": [
|
|
7155
|
+
{
|
|
7156
|
+
"name": "options",
|
|
7157
|
+
"type": "ListFriendChallengesOptions",
|
|
7158
|
+
"optional": false,
|
|
7159
|
+
"summary": null
|
|
7160
|
+
}
|
|
7161
|
+
],
|
|
7162
|
+
"returns": null,
|
|
7163
|
+
"throws": "`invalid` when the cursor is not one a previous page minted or\n `limit` is not a non-negative integer,\n `forbidden` in a context with no player of this game — a game server,\n or a token that is not a game-scoped player token.",
|
|
7164
|
+
"example": "```ts\nconst page = await gem.friends.inbox();\nfor (const c of page.challenges) {\n if (c.template !== 'beat_code_jar') continue;\n renderChallengeRow(c.from, c.params, c.answeredAt !== undefined);\n}\n```",
|
|
7165
|
+
"defaultValue": null,
|
|
7166
|
+
"guides": []
|
|
7167
|
+
},
|
|
7168
|
+
{
|
|
7169
|
+
"id": "FriendsApi.launchedChallenge",
|
|
7170
|
+
"name": "launchedChallenge",
|
|
7171
|
+
"anchor": "friendsapi-launchedchallenge",
|
|
7172
|
+
"group": "friends",
|
|
7173
|
+
"url": "https://gemarcade.com/developer/api/game#friendsapi-launchedchallenge",
|
|
7174
|
+
"signature": "launchedChallenge(options?: { signal?: AbortSignal }): Promise<FriendChallenge | null>",
|
|
7175
|
+
"documented": true,
|
|
7176
|
+
"summary": "The friend challenge this frame was launched with, or `null`.\n\nWhen a player opens a friend challenge in the Arcade, the Arcade launches\nyour game with the challenge's params as launch keys plus its own\n`gemChallenge` key. This reads that key — never parse it yourself — and\nlooks the challenge up, so you get the sender from the platform's records\nand the params as they were sent.\n\nThe key is read ONCE, when `createGem` runs, so cleaning your URL with\n`history.replaceState` afterwards changes nothing here.\n\nRESOLVES `null`, never rejects, for every launch that is not this\nplayer's challenge in this game: no `gemChallenge` key, a value that is\nnot a challenge id, a challenge that is not this player's (a link\nsomeone shared), one from another game or channel, and every guest\nlaunch. Treat `null` as an ordinary launch.\n\nA RELAUNCH REPLAYS. Pressing Play again in the Arcade relaunches the\nsame challenge, so the same one comes back; it carries\n[FriendChallenge.answeredAt](https://gemarcade.com/developer/api/game#friendchallenge-answeredat) once a game has called\n[FriendsApi.markAnswered](https://gemarcade.com/developer/api/game#friendsapi-markanswered), which is how a replay is told from a\nfirst attempt. Asks the platform on every call, so `answeredAt` is\ncurrent.",
|
|
7177
|
+
"deprecated": null,
|
|
7178
|
+
"params": [
|
|
7179
|
+
{
|
|
7180
|
+
"name": "options",
|
|
7181
|
+
"type": "{ signal?: AbortSignal }",
|
|
7182
|
+
"optional": false,
|
|
7183
|
+
"summary": null
|
|
7184
|
+
}
|
|
7185
|
+
],
|
|
7186
|
+
"returns": null,
|
|
7187
|
+
"throws": "What any platform read throws: `forbidden` in a context with no\n player of this game, `unavailable` and `rate_limited` when the platform\n cannot answer now — and `not_found` for a 404 that is NOT the platform's\n uniform challenge answer (no `inbox_item_not_found` code), which is a\n platform that does not serve this read rather than an ordinary launch.",
|
|
7188
|
+
"example": "```ts\nconst challenge = await gem.friends.launchedChallenge();\nif (challenge?.template === 'beat_code_jar') {\n // Check every param before it does anything: the config may have changed since.\n const jar = challenge.params.jar;\n if (typeof jar === 'string' && /^[A-Z0-9]{7}$/.test(jar)) {\n startJar(jar, { from: challenge.from, replay: challenge.answeredAt !== undefined });\n }\n}\n```",
|
|
7189
|
+
"defaultValue": null,
|
|
7190
|
+
"guides": []
|
|
7191
|
+
},
|
|
7192
|
+
{
|
|
7193
|
+
"id": "FriendsApi.markAnswered",
|
|
7194
|
+
"name": "markAnswered",
|
|
7195
|
+
"anchor": "friendsapi-markanswered",
|
|
7196
|
+
"group": "friends",
|
|
7197
|
+
"url": "https://gemarcade.com/developer/api/game#friendsapi-markanswered",
|
|
7198
|
+
"signature": "markAnswered(id: string): Promise<void>",
|
|
7199
|
+
"documented": true,
|
|
7200
|
+
"summary": "Mark one of this player's friend challenges answered — the player has\nplayed it. Call it when your game has finished the attempt the challenge\nasked for, win or lose.\n\nIdempotent: a second call changes nothing, and a challenge keeps the\nfirst time it was answered. The sender is told nothing — there is no read\nreceipt — so this is your game's own record, which\n[FriendChallenge.answeredAt](https://gemarcade.com/developer/api/game#friendchallenge-answeredat) reads back.",
|
|
7201
|
+
"deprecated": null,
|
|
7202
|
+
"params": [
|
|
7203
|
+
{
|
|
7204
|
+
"name": "id",
|
|
7205
|
+
"type": "string",
|
|
7206
|
+
"optional": false,
|
|
7207
|
+
"summary": "A [FriendChallenge.id](https://gemarcade.com/developer/api/game#friendchallenge-id), from\n [FriendsApi.launchedChallenge](https://gemarcade.com/developer/api/game#friendsapi-launchedchallenge) or [FriendsApi.inbox](https://gemarcade.com/developer/api/game#friendsapi-inbox)."
|
|
7208
|
+
}
|
|
7209
|
+
],
|
|
7210
|
+
"returns": null,
|
|
7211
|
+
"throws": "`not_found` when no challenge of this player's in this game and\n channel has that id — one answer for unknown and someone else's;\n `invalid` when `id` cannot be a challenge id at all, before anything is\n sent; `forbidden` in a context with no player of this game;\n `rate_limited` past the platform's per-player budget.",
|
|
7212
|
+
"example": null,
|
|
7213
|
+
"defaultValue": null,
|
|
7214
|
+
"guides": []
|
|
7215
|
+
},
|
|
6861
7216
|
{
|
|
6862
7217
|
"id": "FunctionInvokeOptions",
|
|
6863
7218
|
"name": "FunctionInvokeOptions",
|
|
@@ -8895,6 +9250,29 @@
|
|
|
8895
9250
|
}
|
|
8896
9251
|
]
|
|
8897
9252
|
},
|
|
9253
|
+
{
|
|
9254
|
+
"id": "Gem.vibration",
|
|
9255
|
+
"name": "vibration",
|
|
9256
|
+
"anchor": "gem-vibration",
|
|
9257
|
+
"group": "start",
|
|
9258
|
+
"url": "https://gemarcade.com/developer/api/game#gem-vibration",
|
|
9259
|
+
"signature": "readonly vibration: VibrationApi",
|
|
9260
|
+
"documented": true,
|
|
9261
|
+
"summary": "Vibrate the player's device — call this instead of `navigator.vibrate`,\nwhich does nothing on any iPhone. It takes the same pattern and returns the\nsame boolean, calls `navigator.vibrate` itself where that works, and asks\nthe arcade to vibrate where it does not.\n\nRead [VibrationApi.available](https://gemarcade.com/developer/api/game#vibrationapi-available) live, after `awaitBridge()`, to decide\nwhether to show your game's vibration toggle — on an iPhone it is `false`\nuntil the bridge connects — and check that toggle before every call:\n\n```js\nawait gem.awaitBridge();\nvibrationRow.hidden = !gem.vibration.available;\n\nfunction onHit() {\n if (settings.vibration) gem.vibration.vibrate(30);\n}\n```\n\n- **Never call `navigator.vibrate`**, guarded or not, and never fall back\n to it: this already does.\n- **Nothing vibrates before the player's first tap** in your game; the\n call returns `false` until then.\n- **Stop on pause** with `gem.vibration.vibrate(0)`.",
|
|
9262
|
+
"deprecated": null,
|
|
9263
|
+
"params": [],
|
|
9264
|
+
"returns": null,
|
|
9265
|
+
"throws": null,
|
|
9266
|
+
"example": null,
|
|
9267
|
+
"defaultValue": null,
|
|
9268
|
+
"guides": [
|
|
9269
|
+
{
|
|
9270
|
+
"slug": "vibration",
|
|
9271
|
+
"anchor": null,
|
|
9272
|
+
"url": "https://gemarcade.com/developer/docs/vibration"
|
|
9273
|
+
}
|
|
9274
|
+
]
|
|
9275
|
+
},
|
|
8898
9276
|
{
|
|
8899
9277
|
"id": "Gem.analytics",
|
|
8900
9278
|
"name": "analytics",
|
|
@@ -8983,7 +9361,7 @@
|
|
|
8983
9361
|
"url": "https://gemarcade.com/developer/api/game#gem-friends",
|
|
8984
9362
|
"signature": "readonly friends: FriendsApi",
|
|
8985
9363
|
"documented": true,
|
|
8986
|
-
"summary": "The signed-in player's accepted friends — display identity only,\nread-only, paginated — and the one ask that reaches past them:\n[FriendsApi.challenge](https://gemarcade.com/developer/api/game#friendsapi-challenge), which asks the Arcade to send a friend a\nchallenge after the player confirms it in the Arcade's own sheet.\n\nEvery accepted friend appears whether or not they have played this game;\nan invite-a-friend flow needs exactly the ones who have not. The graph\nitself — requests, accepts, blocks, presence — stays on the Arcade: there\nis nothing here to mutate and nothing
|
|
9364
|
+
"summary": "The signed-in player's accepted friends — display identity only,\nread-only, paginated — and the one ask that reaches past them:\n[FriendsApi.challenge](https://gemarcade.com/developer/api/game#friendsapi-challenge), which asks the Arcade to send a friend a\nchallenge after the player confirms it in the Arcade's own sheet. The\nreceiving side reads the challenges this player holds in this game:\n[FriendsApi.launchedChallenge](https://gemarcade.com/developer/api/game#friendsapi-launchedchallenge) for the one this frame was launched\nwith, [FriendsApi.inbox](https://gemarcade.com/developer/api/game#friendsapi-inbox) for all of them, and\n[FriendsApi.markAnswered](https://gemarcade.com/developer/api/game#friendsapi-markanswered) once one has been played.\n\nEvery accepted friend appears whether or not they have played this game;\nan invite-a-friend flow needs exactly the ones who have not. The graph\nitself — requests, accepts, blocks, presence — stays on the Arcade: there\nis nothing here to mutate but a challenge's own answered mark, and nothing\nthat says who is online.\n\nA player surface only. In a context with no player — a game server — the\nplatform refuses the read; a server has no friend list.",
|
|
8987
9365
|
"deprecated": null,
|
|
8988
9366
|
"params": [],
|
|
8989
9367
|
"returns": null,
|
|
@@ -13221,6 +13599,64 @@
|
|
|
13221
13599
|
"defaultValue": null,
|
|
13222
13600
|
"guides": []
|
|
13223
13601
|
},
|
|
13602
|
+
{
|
|
13603
|
+
"id": "VibrationApi",
|
|
13604
|
+
"name": "VibrationApi",
|
|
13605
|
+
"anchor": "vibrationapi",
|
|
13606
|
+
"group": "vibration",
|
|
13607
|
+
"url": "https://gemarcade.com/developer/api/game#vibrationapi",
|
|
13608
|
+
"signature": "interface VibrationApi",
|
|
13609
|
+
"documented": true,
|
|
13610
|
+
"summary": "`gem.vibration` — vibrate the player's device, on every platform that can.\nCall it instead of `navigator.vibrate`, which does nothing on any iPhone.",
|
|
13611
|
+
"deprecated": null,
|
|
13612
|
+
"params": [],
|
|
13613
|
+
"returns": null,
|
|
13614
|
+
"throws": null,
|
|
13615
|
+
"example": null,
|
|
13616
|
+
"defaultValue": null,
|
|
13617
|
+
"guides": []
|
|
13618
|
+
},
|
|
13619
|
+
{
|
|
13620
|
+
"id": "VibrationApi.available",
|
|
13621
|
+
"name": "available",
|
|
13622
|
+
"anchor": "vibrationapi-available",
|
|
13623
|
+
"group": "vibration",
|
|
13624
|
+
"url": "https://gemarcade.com/developer/api/game#vibrationapi-available",
|
|
13625
|
+
"signature": "readonly available: boolean",
|
|
13626
|
+
"documented": true,
|
|
13627
|
+
"summary": "Whether this device can vibrate for your game: decide from this whether to\nshow your game's vibration toggle. Read it live, every time — after\n`awaitBridge()`, and again when your settings screen opens — never once at\nboot: on an iPhone it is `false` until the bridge connects.\n\n`false` on a desktop with a mouse, in iOS Safari, in Firefox, before the\nbridge is up where vibration goes through the arcade, and after the\nsession ends there.",
|
|
13628
|
+
"deprecated": null,
|
|
13629
|
+
"params": [],
|
|
13630
|
+
"returns": null,
|
|
13631
|
+
"throws": null,
|
|
13632
|
+
"example": null,
|
|
13633
|
+
"defaultValue": null,
|
|
13634
|
+
"guides": []
|
|
13635
|
+
},
|
|
13636
|
+
{
|
|
13637
|
+
"id": "VibrationApi.vibrate",
|
|
13638
|
+
"name": "vibrate",
|
|
13639
|
+
"anchor": "vibrationapi-vibrate",
|
|
13640
|
+
"group": "vibration",
|
|
13641
|
+
"url": "https://gemarcade.com/developer/api/game#vibrationapi-vibrate",
|
|
13642
|
+
"signature": "vibrate(pattern: number | readonly number[]): boolean",
|
|
13643
|
+
"documented": true,
|
|
13644
|
+
"summary": "Vibrate: takes what `navigator.vibrate` takes and returns what it returns.\n`pattern` is a duration in milliseconds, or an array of alternating\nvibration and pause durations, starting with a vibration. `0` or `[]`\nstops a pattern that is still playing, and every call replaces it.\n\nThe pattern is capped at 99 entries of at most 10 000 ms each, and at\n10 000 ms in all: a longer one is cut short, not refused.\n\nCheck your game's own vibration toggle before every call. Returns `false`,\nand vibrates nothing, when [VibrationApi.available](https://gemarcade.com/developer/api/game#vibrationapi-available) is false, before\nthe player's first tap or key press in your game, for a pattern with a\nnegative or non-finite entry, and past 600 calls a minute; each of the last\nthree says so once a session through your `log`. `true` means the pattern\nwas accepted, never that a motor moved.",
|
|
13645
|
+
"deprecated": null,
|
|
13646
|
+
"params": [
|
|
13647
|
+
{
|
|
13648
|
+
"name": "pattern",
|
|
13649
|
+
"type": "number | readonly number[]",
|
|
13650
|
+
"optional": false,
|
|
13651
|
+
"summary": null
|
|
13652
|
+
}
|
|
13653
|
+
],
|
|
13654
|
+
"returns": null,
|
|
13655
|
+
"throws": null,
|
|
13656
|
+
"example": null,
|
|
13657
|
+
"defaultValue": null,
|
|
13658
|
+
"guides": []
|
|
13659
|
+
},
|
|
13224
13660
|
{
|
|
13225
13661
|
"id": "LeaderboardPeriod.id",
|
|
13226
13662
|
"name": "id",
|
package/docs/changelog.md
CHANGED
|
@@ -16,6 +16,36 @@ Its entries are grouped under up to five headings:
|
|
|
16
16
|
- **Rebuild**: a type changed. Recompile against the new version; code that compiles today keeps compiling unless the entry says otherwise.
|
|
17
17
|
- **New capability**: something a game can now do. Nothing changes unless you use it.
|
|
18
18
|
|
|
19
|
+
## 8.0.0
|
|
20
|
+
|
|
21
|
+
*Written from the API facts alone; no summary was generated for this release.*
|
|
22
|
+
|
|
23
|
+
Upgrading from 7.9.1. Impact: required.
|
|
24
|
+
|
|
25
|
+
### Must change
|
|
26
|
+
|
|
27
|
+
- BucketConfig changed its signature.
|
|
28
|
+
- FRAME_TO_PARENT changed its signature.
|
|
29
|
+
- FrameMessage changed its signature.
|
|
30
|
+
- GemCapability changed its signature.
|
|
31
|
+
|
|
32
|
+
### New capability
|
|
33
|
+
|
|
34
|
+
- FriendChallenge was added.
|
|
35
|
+
- FriendChallengesPage was added.
|
|
36
|
+
- FriendsApi.inbox was added.
|
|
37
|
+
- FriendsApi.launchedChallenge was added.
|
|
38
|
+
- FriendsApi.markAnswered was added.
|
|
39
|
+
- FriendsApiDependencies.launchedChallengeId was added.
|
|
40
|
+
- ListFriendChallengesOptions was added.
|
|
41
|
+
- Gem.vibration was added.
|
|
42
|
+
- GemFrame.requestVibrate was added.
|
|
43
|
+
- VibrationApi was added.
|
|
44
|
+
- GemVibrate was added.
|
|
45
|
+
- HostCallbacks.onVibrate was added.
|
|
46
|
+
- HostOptions.onVibrate was added.
|
|
47
|
+
- validateGemVibrate was added.
|
|
48
|
+
|
|
19
49
|
## 7.9.1
|
|
20
50
|
|
|
21
51
|
Upgrading from 7.9.0. Impact: none.
|