@indiegems/gem-web-sdk 21.0.0 → 21.1.1
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 +29 -0
- package/README.md +1 -0
- package/dist/conformance.d.ts +1 -1
- package/dist/dev-tools.d.ts +1 -0
- package/dist/function.d.ts +10 -2
- package/dist/game.d.ts +78 -11
- package/dist/gem-web-sdk.bridge.js +2 -2
- package/dist/gem-web-sdk.conformance.js +3 -3
- package/dist/gem-web-sdk.dev-tools.js +2 -0
- package/dist/gem-web-sdk.esm.js +3 -3
- package/dist/gem-web-sdk.function.js +3 -3
- package/dist/gem-web-sdk.game.js +3 -3
- package/dist/gem-web-sdk.min.js +3 -3
- package/dist/gem-web-sdk.sdk.js +3 -3
- package/dist/gem-web-sdk.server.js +2 -2
- package/dist/index.d.ts +103 -48
- package/dist/sdk.d.ts +78 -11
- package/docs/analytics.md +3 -2
- package/docs/api/api-reference-function.md +11 -4
- package/docs/api/api-reference-game.md +354 -23
- package/docs/api/api-reference-server.md +1 -1
- package/docs/api/reference.json +411 -14
- package/docs/arcade-economy.md +2 -2
- package/docs/changelog.md +29 -0
- package/docs/game-economy-tutorial.md +6 -3
- package/docs/getting-started.md +126 -31
- package/docs/graphics-route.md +188 -0
- package/docs/inventory-and-catalogs.md +69 -27
- package/docs/manifest.json +21 -16
- package/docs/server-bundles.md +3 -3
- package/docs/what-a-game-must-do.md +33 -2
- package/package.json +5 -1
- package/schema/bridge.v1.json +263 -12
- package/schema/capabilities.public.json +26 -0
package/docs/api/reference.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"schema_version": 2,
|
|
3
3
|
"source": {
|
|
4
4
|
"repo": "IndieGems/gem-web-sdk",
|
|
5
|
-
"version": "21.
|
|
5
|
+
"version": "21.1.1"
|
|
6
6
|
},
|
|
7
7
|
"entries": [
|
|
8
8
|
{
|
|
@@ -85,6 +85,10 @@
|
|
|
85
85
|
"key": "vibration",
|
|
86
86
|
"title": "Vibrating the device"
|
|
87
87
|
},
|
|
88
|
+
{
|
|
89
|
+
"key": "graphics",
|
|
90
|
+
"title": "WebGL or WebGPU"
|
|
91
|
+
},
|
|
88
92
|
{
|
|
89
93
|
"key": "controls",
|
|
90
94
|
"title": "Camera and look controls"
|
|
@@ -2564,7 +2568,7 @@
|
|
|
2564
2568
|
"group": "saves",
|
|
2565
2569
|
"signature": "purchase(request: ConfiguredPurchaseRequest, options?: PurchaseOptions): Promise<ConfiguredPurchaseResult>",
|
|
2566
2570
|
"documented": true,
|
|
2567
|
-
"summary": "Purchase an item or recipe through the game's authenticated SDK transport.\n\nOne call covers both direct payment types:\n\n- `payment: { kind: 'configured' }` runs a recipe and charges the costs in\n your game config.\n- `payment: { kind: 'platform_currency', currencySlug: 'GEMS' }` resolves\n the target's published platform price and spends the player's platform\n balance directly. It does not send a bridge message or open the platform\n purchase sheet; your game owns any confirmation UI.\n\nThe game supplies no amount, config version or nonce. The SDK resolves the\ncurrent published price and config version, mints the nonce, and the\nplatform checks all of them again before it debits anything.\n\nA REFUSAL CARRIES A `code` — branch on `err.code`, in the vocabulary\n`gem.purchases.request` answers in (`PurchaseRefusalCode`). Before\nsending a `platform_currency` payment the SDK itself throws a\n`PurchaseRefusal` (a `RangeError`) for `unknown_currency`,\n`not_purchasable`, `no_price_in_currency` or `quantity_invalid`, with a\nmessage naming the active slugs or what the target is priced in. The\nplatform's own refusals arrive as a `GemApiError` whose `code` is its wire\ncode — `insufficient_funds`, `recipe_locked`, `price_mismatch`, … Never\ndraw a top-up or payment screen of your own for either.",
|
|
2571
|
+
"summary": "Purchase an item or recipe through the game's authenticated SDK transport.\n\nOne call covers both direct payment types:\n\n- `payment: { kind: 'configured' }` runs a recipe and charges the costs in\n your game config.\n- `payment: { kind: 'platform_currency', currencySlug: 'GEMS' }` resolves\n the target's published platform price and spends the player's platform\n balance directly. It does not send a bridge message or open the platform\n purchase sheet; your game owns any confirmation UI.\n\nThe game supplies no amount, config version or nonce. The SDK resolves the\ncurrent published price and config version, mints the nonce, and the\nplatform checks all of them again before it debits anything.\n\nA REFUSAL CARRIES A `code` — branch on `err.code`, in the vocabulary\n`gem.purchases.request` answers in (`PurchaseRefusalCode`). Before\nsending a `platform_currency` payment the SDK itself throws a\n`PurchaseRefusal` (a `RangeError`) for `unknown_currency`,\n`not_purchasable`, `no_price_in_currency` or `quantity_invalid`, with a\nmessage naming the active slugs or what the target is priced in. The\nplatform's own refusals arrive as a `GemApiError` whose `code` is its wire\ncode — `insufficient_funds`, `recipe_locked`, `price_mismatch`, … Never\ndraw a top-up or payment screen of your own for either.\n\n`guest_upgrade_required` means the player is a guest, and the platform\nlets only an account spend. TREAT IT LIKE A DECLINE AND DRAW NOTHING: the\nSDK has already handed the attempt to the arcade, which offers the player\nan account over your game. The call rejects at once rather than waiting\nfor them; if they make one and buy, [PurchaseApi.onSettled](api-reference-game.md#purchaseapi-onsettled) hears it.",
|
|
2568
2572
|
"deprecated": null,
|
|
2569
2573
|
"params": [
|
|
2570
2574
|
{
|
|
@@ -4735,7 +4739,7 @@
|
|
|
4735
4739
|
"group": "analytics",
|
|
4736
4740
|
"signature": "type PlatformEventName = \"gem_session_start\" | \"gem_session_end\" | \"gem_pause\" | \"gem_resume\" | \"gem_boot_ready\" | \"gem_match_started\" | \"gem_match_ended\" | \"gem_auth_lost\" | \"gem_graphics_context_lost\"",
|
|
4737
4741
|
"documented": true,
|
|
4738
|
-
"summary": "Every event the SDK emits by itself.\n\nA game never sends these — `gem.analytics.track` refuses the whole\n`gem_` namespace — but the names are published because a game reading its\nown funnel needs the vocabulary, and because a funnel declared in the game\nconfig names them as steps.\n\n| Event | Dimensions | Measures |\n|---|---|---|\n| `gem_session_start` | — | — |\n| `gem_session_end` | `reason` ([SessionEndReason](api-reference-game.md#sessionendreason)) | `duration_ms` |\n| `gem_pause` / `gem_resume` | — | — |\n| `gem_boot_ready` | — | `boot_ms` |\n| `gem_match_started` / `gem_match_ended` | `transport` ([MatchTransport](api-reference-game.md#matchtransport)) | — |\n| `gem_auth_lost` | — | — |\n| `gem_graphics_context_lost` | `gpu`, `host`, `route` | `context_ms`, `hidden`, `nth_loss` |\n\n`gem_graphics_context_lost` is the browser taking a WebGL context away
|
|
4742
|
+
"summary": "Every event the SDK emits by itself.\n\nA game never sends these — `gem.analytics.track` refuses the whole\n`gem_` namespace — but the names are published because a game reading its\nown funnel needs the vocabulary, and because a funnel declared in the game\nconfig names them as steps.\n\n| Event | Dimensions | Measures |\n|---|---|---|\n| `gem_session_start` | — | — |\n| `gem_session_end` | `reason` ([SessionEndReason](api-reference-game.md#sessionendreason)) | `duration_ms` |\n| `gem_pause` / `gem_resume` | — | — |\n| `gem_boot_ready` | — | `boot_ms` |\n| `gem_match_started` / `gem_match_ended` | `transport` ([MatchTransport](api-reference-game.md#matchtransport)) | — |\n| `gem_auth_lost` | — | — |\n| `gem_graphics_context_lost` | `gpu`, `host`, `route` | `context_ms`, `hidden`, `nth_loss` |\n\n`gem_graphics_context_lost` is the browser taking a WebGL context away, or a\nWebGPU device (`route` is then `webgpu`) — usually a GPU driver fault, sized\nper device across every game (`analytics/graphics.ts` lists the dimension\nvalues).",
|
|
4739
4743
|
"deprecated": null,
|
|
4740
4744
|
"params": [],
|
|
4741
4745
|
"returns": null,
|
|
@@ -9088,9 +9092,9 @@
|
|
|
9088
9092
|
"name": "requestShare",
|
|
9089
9093
|
"anchor": "gemframe-requestshare",
|
|
9090
9094
|
"group": "frame",
|
|
9091
|
-
"signature": "requestShare(params: readonly { key: string; value: string }[]): boolean",
|
|
9095
|
+
"signature": "requestShare(params: readonly { key: string; value: string }[], requestId?: string): boolean",
|
|
9092
9096
|
"documented": true,
|
|
9093
|
-
"summary": "Ask the arcade to share a link to your game, opened with these launch\nparameters. The arcade builds the link and shows its own share sheet, copy\nor Share panel; nothing comes back, not even whether
|
|
9097
|
+
"summary": "Ask the arcade to share a link to your game, opened with these launch\nparameters. The arcade builds the link and shows its own share sheet, copy\nor Share panel; nothing about the player comes back, not even whether they\nshared.\n\nPrefer `gem.share.request`, which checks each key and value against the\narcade's rules, throws on the one that would be refused, and rejects with\nthe arcade's own refusal. This one checks only the message's bounds, sends\nonly while connected, and only to an arcade that handles share.",
|
|
9094
9098
|
"deprecated": null,
|
|
9095
9099
|
"params": [
|
|
9096
9100
|
{
|
|
@@ -9098,6 +9102,12 @@
|
|
|
9098
9102
|
"type": "readonly { key: string; value: string }[]",
|
|
9099
9103
|
"optional": false,
|
|
9100
9104
|
"summary": null
|
|
9105
|
+
},
|
|
9106
|
+
{
|
|
9107
|
+
"name": "requestId",
|
|
9108
|
+
"type": "string",
|
|
9109
|
+
"optional": true,
|
|
9110
|
+
"summary": "Pass one only to an arcade that advertised\n `shareResult`, which answers it with one `gem.shareResult` through\n `onShareResult`; any other arcade ignores it. Omit it and nothing\n answers."
|
|
9101
9111
|
}
|
|
9102
9112
|
],
|
|
9103
9113
|
"returns": "whether the message left the frame — `false` before the bridge is\n up, for an arcade that does not handle share, and past this frame's own\n limit of six a minute.",
|
|
@@ -13116,6 +13126,27 @@
|
|
|
13116
13126
|
"defaultValue": null,
|
|
13117
13127
|
"guides": []
|
|
13118
13128
|
},
|
|
13129
|
+
{
|
|
13130
|
+
"id": "Gem.graphics",
|
|
13131
|
+
"name": "graphics",
|
|
13132
|
+
"anchor": "gem-graphics",
|
|
13133
|
+
"group": "start",
|
|
13134
|
+
"signature": "readonly graphics: GraphicsApi",
|
|
13135
|
+
"documented": true,
|
|
13136
|
+
"summary": "Which renderer to make — WebGPU or WebGL — on this device, and whether\nthe device is listed as broken on WebGL. Ask\n`gem.graphics.preferredRoute()` just before you make your first renderer.\n\nWebGL is the answer almost everywhere; `webgpu` only on a listed device,\nwhere WebGPU is available. A listed device that ends up on WebGL draws the\ngame's safe WebGL recipe. The list is the platform's, so a new entry\nreaches your live game without a new build.",
|
|
13137
|
+
"deprecated": null,
|
|
13138
|
+
"params": [],
|
|
13139
|
+
"returns": null,
|
|
13140
|
+
"throws": null,
|
|
13141
|
+
"example": null,
|
|
13142
|
+
"defaultValue": null,
|
|
13143
|
+
"guides": [
|
|
13144
|
+
{
|
|
13145
|
+
"slug": "graphics-route",
|
|
13146
|
+
"anchor": null
|
|
13147
|
+
}
|
|
13148
|
+
]
|
|
13149
|
+
},
|
|
13119
13150
|
{
|
|
13120
13151
|
"id": "Gem.moments",
|
|
13121
13152
|
"name": "moments",
|
|
@@ -13186,7 +13217,7 @@
|
|
|
13186
13217
|
"group": "start",
|
|
13187
13218
|
"signature": "readonly share: ShareApi",
|
|
13188
13219
|
"documented": true,
|
|
13189
|
-
"summary": "Ask the arcade to share a link to your game — opened at the puzzle, board\nor level your launch parameters name — from your own Share button.\n\nThe arcade builds the link, which unfurls with your game's card, and shares\nit through its own share sheet, a copy, or its own Share panel. Show your\nbutton only where [ShareApi.available](api-reference-game.md#shareapi-available) is true, and read it again in\nthe click handler, because `request` throws once it has gone false:\n\n```js\nshareButton.hidden = !gem.share.available;\nshareButton.onclick = () => {\n if (!gem.share.available) return;\n gem.share.request({ params: { jar: 'JAR-K7P3QXM', n: '2' } });\n};\n```\n\n- **Call it synchronously from the click** — never from a timer, after an\n `await`, or on a game event.\n- **Never gate progress or a reward on sharing.**
|
|
13220
|
+
"summary": "Ask the arcade to share a link to your game — opened at the puzzle, board\nor level your launch parameters name — from your own Share button.\n\nThe arcade builds the link, which unfurls with your game's card, and shares\nit through its own share sheet, a copy, or its own Share panel. Show your\nbutton only where [ShareApi.available](api-reference-game.md#shareapi-available) is true, and read it again in\nthe click handler, because `request` throws once it has gone false:\n\n```js\nshareButton.hidden = !gem.share.available;\nshareButton.onclick = () => {\n if (!gem.share.available) return;\n gem.share.request({ params: { jar: 'JAR-K7P3QXM', n: '2' } }).catch((err) => {\n // A ShareRefusal: err.reason ('reserved_key', 'round_trip', 'too_long')\n // and err.key say what to fix. Log it; show the player nothing.\n console.warn(err);\n });\n};\n```\n\n- **Call it synchronously from the click** — never from a timer, after an\n `await`, or on a game event.\n- **Never gate progress or a reward on sharing.** Nothing about the player\n comes back, not even whether they shared. The promise rejects only when\n the arcade refused the share, with a [ShareRefusal](api-reference-game.md#sharerefusal) naming why.\n- **No personal identifiers in `params`.** The link is public.\n- **The share text is the arcade's own**; your game supplies none.",
|
|
13190
13221
|
"deprecated": null,
|
|
13191
13222
|
"params": [],
|
|
13192
13223
|
"returns": null,
|
|
@@ -14361,6 +14392,205 @@
|
|
|
14361
14392
|
}
|
|
14362
14393
|
]
|
|
14363
14394
|
},
|
|
14395
|
+
{
|
|
14396
|
+
"id": "GemGraphicsRoute",
|
|
14397
|
+
"name": "GemGraphicsRoute",
|
|
14398
|
+
"anchor": "gemgraphicsroute",
|
|
14399
|
+
"group": "graphics",
|
|
14400
|
+
"signature": "type GemGraphicsRoute = \"webgpu\" | \"webgl\"",
|
|
14401
|
+
"documented": true,
|
|
14402
|
+
"summary": "Which renderer to make: `webgpu` or `webgl`.",
|
|
14403
|
+
"deprecated": null,
|
|
14404
|
+
"params": [],
|
|
14405
|
+
"returns": null,
|
|
14406
|
+
"throws": null,
|
|
14407
|
+
"example": null,
|
|
14408
|
+
"defaultValue": null,
|
|
14409
|
+
"guides": []
|
|
14410
|
+
},
|
|
14411
|
+
{
|
|
14412
|
+
"id": "GemPreferredRoute",
|
|
14413
|
+
"name": "GemPreferredRoute",
|
|
14414
|
+
"anchor": "gempreferredroute",
|
|
14415
|
+
"group": "graphics",
|
|
14416
|
+
"signature": "interface GemPreferredRoute",
|
|
14417
|
+
"documented": true,
|
|
14418
|
+
"summary": "What `gem.graphics.preferredRoute()` answers.",
|
|
14419
|
+
"deprecated": null,
|
|
14420
|
+
"params": [],
|
|
14421
|
+
"returns": null,
|
|
14422
|
+
"throws": null,
|
|
14423
|
+
"example": null,
|
|
14424
|
+
"defaultValue": null,
|
|
14425
|
+
"guides": []
|
|
14426
|
+
},
|
|
14427
|
+
{
|
|
14428
|
+
"id": "GemPreferredRoute.route",
|
|
14429
|
+
"name": "route",
|
|
14430
|
+
"anchor": "gempreferredroute-route",
|
|
14431
|
+
"group": "graphics",
|
|
14432
|
+
"signature": "readonly route: GemGraphicsRoute",
|
|
14433
|
+
"documented": true,
|
|
14434
|
+
"summary": "Which renderer to make. `webgpu` only when `listed` is true and the\nbrowser hands out a WebGPU adapter; `webgl` everywhere else.",
|
|
14435
|
+
"deprecated": null,
|
|
14436
|
+
"params": [],
|
|
14437
|
+
"returns": null,
|
|
14438
|
+
"throws": null,
|
|
14439
|
+
"example": null,
|
|
14440
|
+
"defaultValue": null,
|
|
14441
|
+
"guides": []
|
|
14442
|
+
},
|
|
14443
|
+
{
|
|
14444
|
+
"id": "GemPreferredRoute.listed",
|
|
14445
|
+
"name": "listed",
|
|
14446
|
+
"anchor": "gempreferredroute-listed",
|
|
14447
|
+
"group": "graphics",
|
|
14448
|
+
"signature": "readonly listed: boolean",
|
|
14449
|
+
"documented": true,
|
|
14450
|
+
"summary": "Whether the platform lists this device as broken on WebGL. When true and\n`route` is `webgl` (no WebGPU here), or your WebGPU renderer fails to\nstart, draw your safe WebGL recipe — for every listed device today, no\nshadow maps — on every WebGL canvas the page makes, menus included.",
|
|
14451
|
+
"deprecated": null,
|
|
14452
|
+
"params": [],
|
|
14453
|
+
"returns": null,
|
|
14454
|
+
"throws": null,
|
|
14455
|
+
"example": null,
|
|
14456
|
+
"defaultValue": null,
|
|
14457
|
+
"guides": []
|
|
14458
|
+
},
|
|
14459
|
+
{
|
|
14460
|
+
"id": "GemGraphicsRouteEntry",
|
|
14461
|
+
"name": "GemGraphicsRouteEntry",
|
|
14462
|
+
"anchor": "gemgraphicsrouteentry",
|
|
14463
|
+
"group": "graphics",
|
|
14464
|
+
"signature": "interface GemGraphicsRouteEntry",
|
|
14465
|
+
"documented": true,
|
|
14466
|
+
"summary": "One device that should draw with WebGPU, as the platform's list names it.\nAn absent `route` or `host` matches any.",
|
|
14467
|
+
"deprecated": null,
|
|
14468
|
+
"params": [],
|
|
14469
|
+
"returns": null,
|
|
14470
|
+
"throws": null,
|
|
14471
|
+
"example": null,
|
|
14472
|
+
"defaultValue": null,
|
|
14473
|
+
"guides": []
|
|
14474
|
+
},
|
|
14475
|
+
{
|
|
14476
|
+
"id": "GemGraphicsRouteEntry.gpu",
|
|
14477
|
+
"name": "gpu",
|
|
14478
|
+
"anchor": "gemgraphicsrouteentry-gpu",
|
|
14479
|
+
"group": "graphics",
|
|
14480
|
+
"signature": "readonly gpu: string",
|
|
14481
|
+
"documented": true,
|
|
14482
|
+
"summary": "The GPU family, e.g. `powervr_d_series`, `adreno`, `mali`.",
|
|
14483
|
+
"deprecated": null,
|
|
14484
|
+
"params": [],
|
|
14485
|
+
"returns": null,
|
|
14486
|
+
"throws": null,
|
|
14487
|
+
"example": null,
|
|
14488
|
+
"defaultValue": null,
|
|
14489
|
+
"guides": []
|
|
14490
|
+
},
|
|
14491
|
+
{
|
|
14492
|
+
"id": "GemGraphicsRouteEntry.route",
|
|
14493
|
+
"name": "route",
|
|
14494
|
+
"anchor": "gemgraphicsrouteentry-route",
|
|
14495
|
+
"group": "graphics",
|
|
14496
|
+
"signature": "readonly route?: string",
|
|
14497
|
+
"documented": true,
|
|
14498
|
+
"summary": "The route WebGL takes there, e.g. `angle_gles` (OpenGL ES),\n`native_gl` (no ANGLE, as the Arcade's Android app reports) or\n`angle_vulkan`. Absent matches any route.",
|
|
14499
|
+
"deprecated": null,
|
|
14500
|
+
"params": [],
|
|
14501
|
+
"returns": null,
|
|
14502
|
+
"throws": null,
|
|
14503
|
+
"example": null,
|
|
14504
|
+
"defaultValue": null,
|
|
14505
|
+
"guides": []
|
|
14506
|
+
},
|
|
14507
|
+
{
|
|
14508
|
+
"id": "GemGraphicsRouteEntry.host",
|
|
14509
|
+
"name": "host",
|
|
14510
|
+
"anchor": "gemgraphicsrouteentry-host",
|
|
14511
|
+
"group": "graphics",
|
|
14512
|
+
"signature": "readonly host?: string",
|
|
14513
|
+
"documented": true,
|
|
14514
|
+
"summary": "`android_webview` (an Android app's WebView, the Arcade app's included) or\n`other`. Absent matches either.",
|
|
14515
|
+
"deprecated": null,
|
|
14516
|
+
"params": [],
|
|
14517
|
+
"returns": null,
|
|
14518
|
+
"throws": null,
|
|
14519
|
+
"example": null,
|
|
14520
|
+
"defaultValue": null,
|
|
14521
|
+
"guides": []
|
|
14522
|
+
},
|
|
14523
|
+
{
|
|
14524
|
+
"id": "PreferredRouteOptions",
|
|
14525
|
+
"name": "PreferredRouteOptions",
|
|
14526
|
+
"anchor": "preferredrouteoptions",
|
|
14527
|
+
"group": "graphics",
|
|
14528
|
+
"signature": "interface PreferredRouteOptions",
|
|
14529
|
+
"documented": true,
|
|
14530
|
+
"summary": "Options for [GraphicsApi.preferredRoute](api-reference-game.md#graphicsapi-preferredroute).",
|
|
14531
|
+
"deprecated": null,
|
|
14532
|
+
"params": [],
|
|
14533
|
+
"returns": null,
|
|
14534
|
+
"throws": null,
|
|
14535
|
+
"example": null,
|
|
14536
|
+
"defaultValue": null,
|
|
14537
|
+
"guides": []
|
|
14538
|
+
},
|
|
14539
|
+
{
|
|
14540
|
+
"id": "PreferredRouteOptions.timeoutMs",
|
|
14541
|
+
"name": "timeoutMs",
|
|
14542
|
+
"anchor": "preferredrouteoptions-timeoutms",
|
|
14543
|
+
"group": "graphics",
|
|
14544
|
+
"signature": "readonly timeoutMs?: number",
|
|
14545
|
+
"documented": true,
|
|
14546
|
+
"summary": "The longest the answer may take, in milliseconds. Default 1000, at most\n5000. When it runs out, the answer comes from what is known by then.",
|
|
14547
|
+
"deprecated": null,
|
|
14548
|
+
"params": [],
|
|
14549
|
+
"returns": null,
|
|
14550
|
+
"throws": null,
|
|
14551
|
+
"example": null,
|
|
14552
|
+
"defaultValue": null,
|
|
14553
|
+
"guides": []
|
|
14554
|
+
},
|
|
14555
|
+
{
|
|
14556
|
+
"id": "GraphicsApi",
|
|
14557
|
+
"name": "GraphicsApi",
|
|
14558
|
+
"anchor": "graphicsapi",
|
|
14559
|
+
"group": "graphics",
|
|
14560
|
+
"signature": "interface GraphicsApi",
|
|
14561
|
+
"documented": true,
|
|
14562
|
+
"summary": "Which renderer to make. Reached as `gem.graphics`.",
|
|
14563
|
+
"deprecated": null,
|
|
14564
|
+
"params": [],
|
|
14565
|
+
"returns": null,
|
|
14566
|
+
"throws": null,
|
|
14567
|
+
"example": null,
|
|
14568
|
+
"defaultValue": null,
|
|
14569
|
+
"guides": []
|
|
14570
|
+
},
|
|
14571
|
+
{
|
|
14572
|
+
"id": "GraphicsApi.preferredRoute",
|
|
14573
|
+
"name": "preferredRoute",
|
|
14574
|
+
"anchor": "graphicsapi-preferredroute",
|
|
14575
|
+
"group": "graphics",
|
|
14576
|
+
"signature": "preferredRoute(options?: PreferredRouteOptions): Promise<GemPreferredRoute>",
|
|
14577
|
+
"documented": true,
|
|
14578
|
+
"summary": "Which renderer to make, and whether this device is listed. Ask once, just\nbefore you make your first renderer, and follow it for every canvas.\n\n`route` is `webgl` almost everywhere. It is `webgpu` only on a device the\nplatform lists as broken on WebGL (today the PowerVR D-Series GPU in the\nPixel 10, on every route but Vulkan), and only when WebGPU is available\nthere. `listed` is true on such a device: when `route` is `webgl` there,\nor making the WebGPU renderer fails, draw your safe WebGL recipe.\n\nResolves within `timeoutMs` (default 1000 ms) and never rejects; normally\nat once after `gem.awaitBridge()`. The launch key `gemGraphics=webgpu`,\n`gemGraphics=safe` or `gemGraphics=webgl` forces the answer, for testing.",
|
|
14579
|
+
"deprecated": null,
|
|
14580
|
+
"params": [
|
|
14581
|
+
{
|
|
14582
|
+
"name": "options",
|
|
14583
|
+
"type": "PreferredRouteOptions",
|
|
14584
|
+
"optional": true,
|
|
14585
|
+
"summary": "See [PreferredRouteOptions](api-reference-game.md#preferredrouteoptions)."
|
|
14586
|
+
}
|
|
14587
|
+
],
|
|
14588
|
+
"returns": "`{ route, listed }`.",
|
|
14589
|
+
"throws": null,
|
|
14590
|
+
"example": null,
|
|
14591
|
+
"defaultValue": null,
|
|
14592
|
+
"guides": []
|
|
14593
|
+
},
|
|
14364
14594
|
{
|
|
14365
14595
|
"id": "GemMatchServerModule",
|
|
14366
14596
|
"name": "GemMatchServerModule",
|
|
@@ -16712,6 +16942,125 @@
|
|
|
16712
16942
|
"defaultValue": null,
|
|
16713
16943
|
"guides": []
|
|
16714
16944
|
},
|
|
16945
|
+
{
|
|
16946
|
+
"id": "PurchaseApi.onSettled",
|
|
16947
|
+
"name": "onSettled",
|
|
16948
|
+
"anchor": "purchaseapi-onsettled",
|
|
16949
|
+
"group": "purchases",
|
|
16950
|
+
"signature": "onSettled(listener: (settlement: PurchaseSettlement) => void): () => void",
|
|
16951
|
+
"documented": true,
|
|
16952
|
+
"summary": "Hear about a purchase of yours that completed AFTER its own call answered\n— returns a function that stops listening.\n\nThe case it exists for: a guest's `gem.me.mutations.purchase` is refused\n`guest_upgrade_required` (your call rejects at once, and you show\nnothing), the arcade offers the player an account, and they make one and\nconfirm the arcade's sheet a minute later — or after a reload, when the\ncall that asked is gone. This is how your game hears that it happened.\n\nTHE SDK CONFIRMS IT FIRST. A listener is called only once the platform\nitself, asked with your game's own token, says the purchase was granted;\na claim it cannot confirm is never reported. The player's inventory is\nre-read either way, so `gem.me.inventory.onChanged` fires too. Treat it\nlike an `ok`: grant feedback, then re-read inventory rather than assuming\nwhat landed.\n\nHears nothing on an arcade that does not support it (the\n`purchaseSettled` capability), and never for a purchase an `ok` already\nreported.",
|
|
16953
|
+
"deprecated": null,
|
|
16954
|
+
"params": [
|
|
16955
|
+
{
|
|
16956
|
+
"name": "listener",
|
|
16957
|
+
"type": "(settlement: PurchaseSettlement) => void",
|
|
16958
|
+
"optional": false,
|
|
16959
|
+
"summary": null
|
|
16960
|
+
}
|
|
16961
|
+
],
|
|
16962
|
+
"returns": null,
|
|
16963
|
+
"throws": null,
|
|
16964
|
+
"example": null,
|
|
16965
|
+
"defaultValue": null,
|
|
16966
|
+
"guides": []
|
|
16967
|
+
},
|
|
16968
|
+
{
|
|
16969
|
+
"id": "PurchaseSettlement",
|
|
16970
|
+
"name": "PurchaseSettlement",
|
|
16971
|
+
"anchor": "purchasesettlement",
|
|
16972
|
+
"group": "purchases",
|
|
16973
|
+
"signature": "interface PurchaseSettlement",
|
|
16974
|
+
"documented": true,
|
|
16975
|
+
"summary": "A purchase of one of your game's own items or recipes that completed after\nthe call that asked for it had answered. What [PurchaseApi.onSettled](api-reference-game.md#purchaseapi-onsettled)\nhears.",
|
|
16976
|
+
"deprecated": null,
|
|
16977
|
+
"params": [],
|
|
16978
|
+
"returns": null,
|
|
16979
|
+
"throws": null,
|
|
16980
|
+
"example": null,
|
|
16981
|
+
"defaultValue": null,
|
|
16982
|
+
"guides": []
|
|
16983
|
+
},
|
|
16984
|
+
{
|
|
16985
|
+
"id": "PurchaseSettlement.kind",
|
|
16986
|
+
"name": "kind",
|
|
16987
|
+
"anchor": "purchasesettlement-kind",
|
|
16988
|
+
"group": "purchases",
|
|
16989
|
+
"signature": "readonly kind: \"item\" | \"recipe\"",
|
|
16990
|
+
"documented": true,
|
|
16991
|
+
"summary": "`item` or `recipe`.",
|
|
16992
|
+
"deprecated": null,
|
|
16993
|
+
"params": [],
|
|
16994
|
+
"returns": null,
|
|
16995
|
+
"throws": null,
|
|
16996
|
+
"example": null,
|
|
16997
|
+
"defaultValue": null,
|
|
16998
|
+
"guides": []
|
|
16999
|
+
},
|
|
17000
|
+
{
|
|
17001
|
+
"id": "PurchaseSettlement.targetId",
|
|
17002
|
+
"name": "targetId",
|
|
17003
|
+
"anchor": "purchasesettlement-targetid",
|
|
17004
|
+
"group": "purchases",
|
|
17005
|
+
"signature": "readonly targetId: string",
|
|
17006
|
+
"documented": true,
|
|
17007
|
+
"summary": "The item-definition or recipe id that was bought.",
|
|
17008
|
+
"deprecated": null,
|
|
17009
|
+
"params": [],
|
|
17010
|
+
"returns": null,
|
|
17011
|
+
"throws": null,
|
|
17012
|
+
"example": null,
|
|
17013
|
+
"defaultValue": null,
|
|
17014
|
+
"guides": []
|
|
17015
|
+
},
|
|
17016
|
+
{
|
|
17017
|
+
"id": "PurchaseSettlement.catalogId",
|
|
17018
|
+
"name": "catalogId",
|
|
17019
|
+
"anchor": "purchasesettlement-catalogid",
|
|
17020
|
+
"group": "purchases",
|
|
17021
|
+
"signature": "readonly catalogId?: string",
|
|
17022
|
+
"documented": true,
|
|
17023
|
+
"summary": "The recipe's catalog; absent for an item.",
|
|
17024
|
+
"deprecated": null,
|
|
17025
|
+
"params": [],
|
|
17026
|
+
"returns": null,
|
|
17027
|
+
"throws": null,
|
|
17028
|
+
"example": null,
|
|
17029
|
+
"defaultValue": null,
|
|
17030
|
+
"guides": []
|
|
17031
|
+
},
|
|
17032
|
+
{
|
|
17033
|
+
"id": "PurchaseSettlement.quantity",
|
|
17034
|
+
"name": "quantity",
|
|
17035
|
+
"anchor": "purchasesettlement-quantity",
|
|
17036
|
+
"group": "purchases",
|
|
17037
|
+
"signature": "readonly quantity: number",
|
|
17038
|
+
"documented": true,
|
|
17039
|
+
"summary": "How many were bought. Always 1 for a recipe.",
|
|
17040
|
+
"deprecated": null,
|
|
17041
|
+
"params": [],
|
|
17042
|
+
"returns": null,
|
|
17043
|
+
"throws": null,
|
|
17044
|
+
"example": null,
|
|
17045
|
+
"defaultValue": null,
|
|
17046
|
+
"guides": []
|
|
17047
|
+
},
|
|
17048
|
+
{
|
|
17049
|
+
"id": "PurchaseSettlement.purchaseId",
|
|
17050
|
+
"name": "purchaseId",
|
|
17051
|
+
"anchor": "purchasesettlement-purchaseid",
|
|
17052
|
+
"group": "purchases",
|
|
17053
|
+
"signature": "readonly purchaseId: string",
|
|
17054
|
+
"documented": true,
|
|
17055
|
+
"summary": "The platform's own id for this purchase, which the SDK confirmed with the\nplatform before calling you. A handle for logs and support, as on\n[PurchaseOutcome](api-reference-game.md#purchaseoutcome)'s `ok`; nothing reads it back.",
|
|
17056
|
+
"deprecated": null,
|
|
17057
|
+
"params": [],
|
|
17058
|
+
"returns": null,
|
|
17059
|
+
"throws": null,
|
|
17060
|
+
"example": null,
|
|
17061
|
+
"defaultValue": null,
|
|
17062
|
+
"guides": []
|
|
17063
|
+
},
|
|
16715
17064
|
{
|
|
16716
17065
|
"id": "Avatar3dPurchaseRequest",
|
|
16717
17066
|
"name": "Avatar3dPurchaseRequest",
|
|
@@ -16765,9 +17114,9 @@
|
|
|
16765
17114
|
"name": "PurchaseRefusalCode",
|
|
16766
17115
|
"anchor": "purchaserefusalcode",
|
|
16767
17116
|
"group": "purchases",
|
|
16768
|
-
"signature": "type PurchaseRefusalCode = \"unknown_currency\" | \"currency_not_in_game_payable\" | \"not_purchasable\" | \"no_price_in_currency\" | \"busy\" | \"cancelled\" | \"insufficient_funds\" | \"price_mismatch\" | \"config_version_stale\" | \"platform_currency_unavailable\" | \"not_available\" | \"recipe_locked\" | \"already_owned\" | \"recipe_on_cooldown\" | \"recipe_max_uses_exceeded\" | \"item_max_stack_exceeded\" | \"recipe_costs_unmet\" | \"quantity_invalid\" | \"error\"",
|
|
17117
|
+
"signature": "type PurchaseRefusalCode = \"unknown_currency\" | \"currency_not_in_game_payable\" | \"not_purchasable\" | \"no_price_in_currency\" | \"busy\" | \"cancelled\" | \"insufficient_funds\" | \"price_mismatch\" | \"config_version_stale\" | \"platform_currency_unavailable\" | \"not_available\" | \"recipe_locked\" | \"already_owned\" | \"guest_upgrade_required\" | \"recipe_on_cooldown\" | \"recipe_max_uses_exceeded\" | \"item_max_stack_exceeded\" | \"recipe_costs_unmet\" | \"quantity_invalid\" | \"error\"",
|
|
16769
17118
|
"documented": true,
|
|
16770
|
-
"summary": "Every reason a purchase can be refused, on either purchase path.\n\n- `unknown_currency` — the slug names no active platform currency. There are\n no aliases: `TICKETS` is the retired name for Gems and is refused, never\n resolved. Write `GEMS` or `ARCADE_TICKETS`.\n- `currency_not_in_game_payable` — the currency exists but cannot pay for game\n items.\n- `not_purchasable` — the target is not in the purchase catalog, or cannot be\n bought (not published on this channel, inactive, or its config does not\n convert).\n- `no_price_in_currency` — the target has prices, but none in that currency.\n- `busy` — another purchase is in flight, a sheet is already open, the\n platform is backing off after a cancel, or the ask came without a player's\n click or key press.\n- `cancelled` — the player dismissed the platform sheet.\n- `insufficient_funds` — the player cannot afford it.\n- `price_mismatch`, `config_version_stale` — the price or config changed\n between reading the catalog and buying; nothing was charged. Re-read.\n- `platform_currency_unavailable` — the currency is not available to this\n purchase right now.\n- `not_available` — the recipe is outside its availability window, or\n inactive; nothing was charged. Hide or disable it until its window opens.\n- `recipe_locked` — the recipe's prerequisites are not met.\n- `already_owned` — the player already owns the 3D avatar\n `gem.avatars3d.purchase` asked for; nothing was charged. Only that call\n answers it.\n- `recipe_on_cooldown`, `recipe_max_uses_exceeded`, `item_max_stack_exceeded`,\n `recipe_costs_unmet`, `quantity_invalid` — the recipe or item refused this\n purchase, before any charge.\n- `error` — anything else, including a reason this SDK version does not know\n and an answer that never arrived.\n\nFOR YOUR LOGS AND YOUR SHOP'S STATE, NEVER FOR A PAYMENT SCREEN. Grey out a\nlocked recipe; fix a slug. Never draw a top-up, \"buy more\" or payment UI of\nyour own — only the arcade may, and a game screen that looks like one is an\nimitation of platform payment chrome.",
|
|
17119
|
+
"summary": "Every reason a purchase can be refused, on either purchase path.\n\n- `unknown_currency` — the slug names no active platform currency. There are\n no aliases: `TICKETS` is the retired name for Gems and is refused, never\n resolved. Write `GEMS` or `ARCADE_TICKETS`.\n- `currency_not_in_game_payable` — the currency exists but cannot pay for game\n items.\n- `not_purchasable` — the target is not in the purchase catalog, or cannot be\n bought (not published on this channel, inactive, or its config does not\n convert).\n- `no_price_in_currency` — the target has prices, but none in that currency.\n- `busy` — another purchase is in flight, a sheet is already open, the\n platform is backing off after a cancel, or the ask came without a player's\n click or key press.\n- `cancelled` — the player dismissed the platform sheet.\n- `insufficient_funds` — the player cannot afford it.\n- `price_mismatch`, `config_version_stale` — the price or config changed\n between reading the catalog and buying; nothing was charged. Re-read.\n- `platform_currency_unavailable` — the currency is not available to this\n purchase right now.\n- `not_available` — the recipe is outside its availability window, or\n inactive; nothing was charged. Hide or disable it until its window opens.\n- `recipe_locked` — the recipe's prerequisites are not met.\n- `already_owned` — the player already owns the 3D avatar\n `gem.avatars3d.purchase` asked for; nothing was charged. Only that call\n answers it.\n- `guest_upgrade_required` — the player is playing as a guest, and the\n platform lets only an account spend. Nothing was charged. TREAT IT EXACTLY\n LIKE A DECLINE, AND DRAW NOTHING: the arcade offers the player an account\n itself, over your game, and a sign-up or \"log in to buy\" screen of your own\n would be a second one. A game on `gem.me.mutations.purchase` gets it as a\n `GemApiError` after the SDK has already handed the attempt to the arcade.\n- `recipe_on_cooldown`, `recipe_max_uses_exceeded`, `item_max_stack_exceeded`,\n `recipe_costs_unmet`, `quantity_invalid` — the recipe or item refused this\n purchase, before any charge.\n- `error` — anything else, including a reason this SDK version does not know\n and an answer that never arrived.\n\nFOR YOUR LOGS AND YOUR SHOP'S STATE, NEVER FOR A PAYMENT SCREEN. Grey out a\nlocked recipe; fix a slug. Never draw a top-up, \"buy more\" or payment UI of\nyour own — only the arcade may, and a game screen that looks like one is an\nimitation of platform payment chrome.",
|
|
16771
17120
|
"deprecated": null,
|
|
16772
17121
|
"params": [],
|
|
16773
17122
|
"returns": null,
|
|
@@ -17720,6 +18069,54 @@
|
|
|
17720
18069
|
"defaultValue": null,
|
|
17721
18070
|
"guides": []
|
|
17722
18071
|
},
|
|
18072
|
+
{
|
|
18073
|
+
"id": "ShareRefusal",
|
|
18074
|
+
"name": "ShareRefusal",
|
|
18075
|
+
"anchor": "sharerefusal",
|
|
18076
|
+
"group": "share",
|
|
18077
|
+
"signature": "class ShareRefusal",
|
|
18078
|
+
"documented": true,
|
|
18079
|
+
"summary": "The arcade refused a share, and why. `gem.share.request`'s promise rejects\nwith it. Fix the params it names: the same share is refused again.",
|
|
18080
|
+
"deprecated": null,
|
|
18081
|
+
"params": [],
|
|
18082
|
+
"returns": null,
|
|
18083
|
+
"throws": null,
|
|
18084
|
+
"example": null,
|
|
18085
|
+
"defaultValue": null,
|
|
18086
|
+
"guides": []
|
|
18087
|
+
},
|
|
18088
|
+
{
|
|
18089
|
+
"id": "ShareRefusal.reason",
|
|
18090
|
+
"name": "reason",
|
|
18091
|
+
"anchor": "sharerefusal-reason",
|
|
18092
|
+
"group": "share",
|
|
18093
|
+
"signature": "readonly reason: string",
|
|
18094
|
+
"documented": true,
|
|
18095
|
+
"summary": "Why, as a machine code. OPEN-ENDED, so keep a `default` arm:\n\n- `reserved_key` — [ShareRefusal.key](api-reference-game.md#sharerefusal-key) is a key the arcade keeps for\n itself and does not carry in a share link.\n- `round_trip` — the link the arcade built does not read back as what you\n asked for, or [ShareRefusal.key](api-reference-game.md#sharerefusal-key) is too long to carry.\n- `too_long` — the whole link is over 2 KB. Share fewer or shorter params.\n- `error` — the arcade refused without saying why.",
|
|
18096
|
+
"deprecated": null,
|
|
18097
|
+
"params": [],
|
|
18098
|
+
"returns": null,
|
|
18099
|
+
"throws": null,
|
|
18100
|
+
"example": null,
|
|
18101
|
+
"defaultValue": null,
|
|
18102
|
+
"guides": []
|
|
18103
|
+
},
|
|
18104
|
+
{
|
|
18105
|
+
"id": "ShareRefusal.key",
|
|
18106
|
+
"name": "key",
|
|
18107
|
+
"anchor": "sharerefusal-key",
|
|
18108
|
+
"group": "share",
|
|
18109
|
+
"signature": "readonly key?: string",
|
|
18110
|
+
"documented": true,
|
|
18111
|
+
"summary": "The param key that caused it, exactly as you passed it, when one key did.",
|
|
18112
|
+
"deprecated": null,
|
|
18113
|
+
"params": [],
|
|
18114
|
+
"returns": null,
|
|
18115
|
+
"throws": null,
|
|
18116
|
+
"example": null,
|
|
18117
|
+
"defaultValue": null,
|
|
18118
|
+
"guides": []
|
|
18119
|
+
},
|
|
17723
18120
|
{
|
|
17724
18121
|
"id": "ShareApi",
|
|
17725
18122
|
"name": "ShareApi",
|
|
@@ -17757,9 +18154,9 @@
|
|
|
17757
18154
|
"name": "request",
|
|
17758
18155
|
"anchor": "shareapi-request",
|
|
17759
18156
|
"group": "share",
|
|
17760
|
-
"signature": "request(request?: ShareRequest): void",
|
|
18157
|
+
"signature": "request(request?: ShareRequest): Promise<void>",
|
|
17761
18158
|
"documented": true,
|
|
17762
|
-
"summary": "Ask the arcade to share a link that opens your game with `params`. Call it\nsynchronously from your own Share button's click handler.
|
|
18159
|
+
"summary": "Ask the arcade to share a link that opens your game with `params`. Call it\nsynchronously from your own Share button's click handler. Nothing about the\nplayer ever comes back — not even whether they shared — so never gate\nprogress or a reward on it.",
|
|
17763
18160
|
"deprecated": null,
|
|
17764
18161
|
"params": [
|
|
17765
18162
|
{
|
|
@@ -17769,8 +18166,8 @@
|
|
|
17769
18166
|
"summary": null
|
|
17770
18167
|
}
|
|
17771
18168
|
],
|
|
17772
|
-
"returns":
|
|
17773
|
-
"throws": "`Error` when [ShareApi.available](api-reference-game.md#shareapi-available) is false, and `TypeError`,\n naming the parameter, for one the arcade would refuse: more than 16, a\n key that is not a letter followed by up to 63 letters, digits, `_` or\n `-`, a key the arcade
|
|
18169
|
+
"returns": "a promise that REJECTS with a [ShareRefusal](api-reference-game.md#sharerefusal) when the arcade\n refused the share, naming the reason and the param, and otherwise\n resolves with nothing. Resolving does not mean the player shared, or saw\n a sheet at all. An older arcade never refuses out loud, so there it\n resolves at once.",
|
|
18170
|
+
"throws": "`Error` when [ShareApi.available](api-reference-game.md#shareapi-available) is false, and `TypeError`,\n naming the parameter, for one the arcade would refuse: more than 16, a\n key that is not a letter followed by up to 63 letters, digits, `_` or\n `-`, a key the arcade keeps for itself, or a value that is not a string,\n is over 512 characters, or holds a control, format or unpaired surrogate\n character. Nothing is sent when it throws. Which keys the arcade keeps\n depends on the arcade: a current one carries reserved keys such as\n `invite` and refuses only `embedder` (and `_s`, which is not a valid key\n anyway); an older one refuses all of them.",
|
|
17774
18171
|
"example": null,
|
|
17775
18172
|
"defaultValue": null,
|
|
17776
18173
|
"guides": []
|
|
@@ -30373,7 +30770,7 @@
|
|
|
30373
30770
|
"group": "analytics",
|
|
30374
30771
|
"signature": "type PlatformEventName = \"gem_session_start\" | \"gem_session_end\" | \"gem_pause\" | \"gem_resume\" | \"gem_boot_ready\" | \"gem_match_started\" | \"gem_match_ended\" | \"gem_auth_lost\" | \"gem_graphics_context_lost\"",
|
|
30375
30772
|
"documented": true,
|
|
30376
|
-
"summary": "Every event the SDK emits by itself.\n\nA game never sends these — `gem.analytics.track` refuses the whole\n`gem_` namespace — but the names are published because a game reading its\nown funnel needs the vocabulary, and because a funnel declared in the game\nconfig names them as steps.\n\n| Event | Dimensions | Measures |\n|---|---|---|\n| `gem_session_start` | — | — |\n| `gem_session_end` | `reason` ([SessionEndReason](api-reference-function.md#sessionendreason)) | `duration_ms` |\n| `gem_pause` / `gem_resume` | — | — |\n| `gem_boot_ready` | — | `boot_ms` |\n| `gem_match_started` / `gem_match_ended` | `transport` (`MatchTransport`) | — |\n| `gem_auth_lost` | — | — |\n| `gem_graphics_context_lost` | `gpu`, `host`, `route` | `context_ms`, `hidden`, `nth_loss` |\n\n`gem_graphics_context_lost` is the browser taking a WebGL context away
|
|
30773
|
+
"summary": "Every event the SDK emits by itself.\n\nA game never sends these — `gem.analytics.track` refuses the whole\n`gem_` namespace — but the names are published because a game reading its\nown funnel needs the vocabulary, and because a funnel declared in the game\nconfig names them as steps.\n\n| Event | Dimensions | Measures |\n|---|---|---|\n| `gem_session_start` | — | — |\n| `gem_session_end` | `reason` ([SessionEndReason](api-reference-function.md#sessionendreason)) | `duration_ms` |\n| `gem_pause` / `gem_resume` | — | — |\n| `gem_boot_ready` | — | `boot_ms` |\n| `gem_match_started` / `gem_match_ended` | `transport` (`MatchTransport`) | — |\n| `gem_auth_lost` | — | — |\n| `gem_graphics_context_lost` | `gpu`, `host`, `route` | `context_ms`, `hidden`, `nth_loss` |\n\n`gem_graphics_context_lost` is the browser taking a WebGL context away, or a\nWebGPU device (`route` is then `webgpu`) — usually a GPU driver fault, sized\nper device across every game (`analytics/graphics.ts` lists the dimension\nvalues).",
|
|
30377
30774
|
"deprecated": null,
|
|
30378
30775
|
"params": [],
|
|
30379
30776
|
"returns": null,
|
|
@@ -33417,7 +33814,7 @@
|
|
|
33417
33814
|
"group": "inventory",
|
|
33418
33815
|
"signature": "purchase(request: ConfiguredPurchaseRequest, options?: PurchaseOptions): Promise<ConfiguredPurchaseResult>",
|
|
33419
33816
|
"documented": true,
|
|
33420
|
-
"summary": "Purchase an item or recipe through the game's authenticated SDK transport.\n\nOne call covers both direct payment types:\n\n- `payment: { kind: 'configured' }` runs a recipe and charges the costs in\n your game config.\n- `payment: { kind: 'platform_currency', currencySlug: 'GEMS' }` resolves\n the target's published platform price and spends the player's platform\n balance directly. It does not send a bridge message or open the platform\n purchase sheet; your game owns any confirmation UI.\n\nThe game supplies no amount, config version or nonce. The SDK resolves the\ncurrent published price and config version, mints the nonce, and the\nplatform checks all of them again before it debits anything.\n\nA REFUSAL CARRIES A `code` — branch on `err.code`, in the vocabulary\n`gem.purchases.request` answers in (`PurchaseRefusalCode`). Before\nsending a `platform_currency` payment the SDK itself throws a\n`PurchaseRefusal` (a `RangeError`) for `unknown_currency`,\n`not_purchasable`, `no_price_in_currency` or `quantity_invalid`, with a\nmessage naming the active slugs or what the target is priced in. The\nplatform's own refusals arrive as a `GemApiError` whose `code` is its wire\ncode — `insufficient_funds`, `recipe_locked`, `price_mismatch`, … Never\ndraw a top-up or payment screen of your own for either.",
|
|
33817
|
+
"summary": "Purchase an item or recipe through the game's authenticated SDK transport.\n\nOne call covers both direct payment types:\n\n- `payment: { kind: 'configured' }` runs a recipe and charges the costs in\n your game config.\n- `payment: { kind: 'platform_currency', currencySlug: 'GEMS' }` resolves\n the target's published platform price and spends the player's platform\n balance directly. It does not send a bridge message or open the platform\n purchase sheet; your game owns any confirmation UI.\n\nThe game supplies no amount, config version or nonce. The SDK resolves the\ncurrent published price and config version, mints the nonce, and the\nplatform checks all of them again before it debits anything.\n\nA REFUSAL CARRIES A `code` — branch on `err.code`, in the vocabulary\n`gem.purchases.request` answers in (`PurchaseRefusalCode`). Before\nsending a `platform_currency` payment the SDK itself throws a\n`PurchaseRefusal` (a `RangeError`) for `unknown_currency`,\n`not_purchasable`, `no_price_in_currency` or `quantity_invalid`, with a\nmessage naming the active slugs or what the target is priced in. The\nplatform's own refusals arrive as a `GemApiError` whose `code` is its wire\ncode — `insufficient_funds`, `recipe_locked`, `price_mismatch`, … Never\ndraw a top-up or payment screen of your own for either.\n\n`guest_upgrade_required` means the player is a guest, and the platform\nlets only an account spend. TREAT IT LIKE A DECLINE AND DRAW NOTHING: the\nSDK has already handed the attempt to the arcade, which offers the player\nan account over your game. The call rejects at once rather than waiting\nfor them; if they make one and buy, `PurchaseApi.onSettled` hears it.",
|
|
33421
33818
|
"deprecated": null,
|
|
33422
33819
|
"params": [
|
|
33423
33820
|
{
|
package/docs/arcade-economy.md
CHANGED
|
@@ -116,8 +116,8 @@ them only for fraud or moderation ([achievements.md](achievements.md)).
|
|
|
116
116
|
## 5. Spending: price each item in the currency it deserves
|
|
117
117
|
|
|
118
118
|
Any game may sell an item or a recipe for a platform currency, with no server and no approval: the
|
|
119
|
-
item or recipe carries `platform_prices`, and the player pays through the arcade's purchase sheet
|
|
120
|
-
|
|
119
|
+
item or recipe carries `platform_prices`, and the player pays through the arcade's purchase sheet,
|
|
120
|
+
`gem.purchases.request`
|
|
121
121
|
([inventory-and-catalogs.md](inventory-and-catalogs.md#16-selling-for-a-platform-currency)). What a
|
|
122
122
|
currency may price is a platform setting on that currency, its **spend scope**:
|
|
123
123
|
|
package/docs/changelog.md
CHANGED
|
@@ -16,6 +16,35 @@ 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
|
+
## 21.1.1
|
|
20
|
+
|
|
21
|
+
Upgrading from 21.1.0. Impact: none.
|
|
22
|
+
|
|
23
|
+
Nothing in this release asks anything of a game.
|
|
24
|
+
|
|
25
|
+
## 21.1.0
|
|
26
|
+
|
|
27
|
+
Upgrading from 21.0.0. Impact: required.
|
|
28
|
+
|
|
29
|
+
### Must change
|
|
30
|
+
|
|
31
|
+
- ShareApi.request now returns a promise instead of nothing. When the Arcade refuses a share, the promise rejects with a ShareRefusal that names the reason and the offending param. Await the call and handle the rejection, for example by telling the player the share could not be sent. With an older Arcade the promise resolves at once, as before. The SDK also refuses, before sending, share params whose keys the Arcade reserves for its own play links, and the error names the key. If your game uses such a name as its own share param, rename it. A current Arcade carries most reserved keys through a share link and returns them unchanged, but an older one still refuses them.
|
|
32
|
+
- PurchaseRefusalCode has a new value, guest_upgrade_required, returned when a web guest tries to spend platform currency directly. Treat it like any other decline: draw no purchase, and do not retry. If you switch over the refusal codes exhaustively, add a case for it or your code will stop compiling.
|
|
33
|
+
|
|
34
|
+
### Should change
|
|
35
|
+
|
|
36
|
+
- Spend Gems and Coins through the Arcade's purchase sheet, not through a direct spend on the entity mutations. A web guest's direct spend is now refused. The SDK hands that one refusal to the Arcade as a safety net and rejects your call with the original error straight away, without waiting for an answer or retrying. Move any direct platform-currency spend to the purchase sheet flow. See [the deprecation policy](deprecation-policy.md#4-what-is-deprecated-today).
|
|
37
|
+
|
|
38
|
+
### Rebuild
|
|
39
|
+
|
|
40
|
+
- Several types and signatures changed in compatible ways: GemFrame.requestPurchase gained an optional origin argument, GemFrame.requestShare gained an optional requestId argument, the MutationsApi constructor gained an optional hand-off callback, and GemCapability, ParentMessage and PARENT_TO_FRAME gained new capabilities and message kinds for purchase settlement, graphics routes and share results. Recompiling is enough. If you test against your own stand-in for the Arcade, rebuild it against this release and bring it up to the newer parent conformance version.
|
|
41
|
+
|
|
42
|
+
### New capability
|
|
43
|
+
|
|
44
|
+
- PurchaseApi.onSettled lets a game hear a purchase that completed after its original call had already been answered, for example one finished through the Arcade after a guest's refused spend. The game is told once the platform confirms the purchase, as a PurchaseSettlement, and the player's data is refreshed. Subscribe to it so you can grant the purchase when it lands. It only fires on an Arcade that supports it.
|
|
45
|
+
- Gem.graphics lets a game ask which renderer to create just before it creates one. The answer says the route to use and whether the device is on the platform's list of devices known to draw wrongly on WebGL. The answer is WebGL everywhere except on a listed device, and there WebGPU only if it hands out an adapter. A listed device with no working WebGPU gets your safe WebGL recipe. The call is time-bounded and never rejects. The list ships built in, and the Arcade can replace it for live games without a rebuild. Call it before creating a renderer and make every canvas follow the answer.
|
|
46
|
+
- A new dev-tools entry point is available for a game's dev-only code, such as a cheat panel, level skipper or debug menu. Importing it from that code marks the build as carrying dev-only code. Builds so marked can be uploaded and played on the dev channel but are refused on the test and release channels. Keep the import behind a build-time switch that is off for test and release builds so the marker is absent from them.
|
|
47
|
+
|
|
19
48
|
## 21.0.0
|
|
20
49
|
|
|
21
50
|
Upgrading from 20.1.0. Impact: required.
|
|
@@ -268,7 +268,7 @@ if (av.locked) {
|
|
|
268
268
|
The platform checks the prerequisite **before** it takes any Coins: a locked player who buys
|
|
269
269
|
anyway is refused `recipe_locked` and charged nothing. See
|
|
270
270
|
[`inventory-and-catalogs.md`](inventory-and-catalogs.md#16-selling-for-a-platform-currency) for the
|
|
271
|
-
|
|
271
|
+
full rule, and why platform currency is spent through the arcade's sheet.
|
|
272
272
|
|
|
273
273
|
### Buying it, and reading a refusal
|
|
274
274
|
|
|
@@ -291,8 +291,11 @@ if (res.outcome === 'ok' || res.outcome === 'unavailable') {
|
|
|
291
291
|
}
|
|
292
292
|
```
|
|
293
293
|
|
|
294
|
-
|
|
295
|
-
|
|
294
|
+
Spend Coins and Gems through this sheet, never `gem.me.mutations.purchase`: on gemarcade.com a web
|
|
295
|
+
guest is refused a direct spend (`guest_upgrade_required`), and the sheet is where the arcade offers
|
|
296
|
+
them an account. A recipe your game charges in its own currency (`payment: { kind: 'configured' }`)
|
|
297
|
+
reports the same strings as `err.code` — see
|
|
298
|
+
[`inventory-and-catalogs.md`](inventory-and-catalogs.md#why-a-purchase-was-refused).
|
|
296
299
|
|
|
297
300
|
**Never draw a top-up or payment screen of your own** — no "get more Coins", no checkout, nothing
|
|
298
301
|
shaped like the arcade's purchase sheet. A player short of Coins earns more by playing; your shop
|