@indiegems/gem-web-sdk 13.1.0 → 13.2.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 +22 -0
- package/README.md +4 -3
- package/dist/function.d.ts +1 -0
- package/dist/game.d.ts +23 -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-api.js +1 -1
- package/dist/gem-web-sdk.server.js +2 -2
- package/dist/index.d.ts +27 -4
- package/dist/sdk.d.ts +23 -1
- package/dist/server-api.d.ts +1 -0
- package/dist/server.d.ts +1 -0
- package/docs/analytics.md +6 -3
- package/docs/api/api-reference-function.md +26 -3
- package/docs/api/api-reference-game.md +195 -27
- package/docs/api/api-reference-server.md +23 -2
- package/docs/api/reference.json +220 -18
- package/docs/changelog.md +22 -0
- package/docs/deprecation-policy.md +6 -0
- package/docs/getting-started.md +7 -4
- package/docs/manifest.json +23 -23
- package/docs/platform-moments.md +266 -0
- package/docs/rewarded-ads.md +26 -9
- package/docs/server-bundles.md +2 -2
- package/docs/serverless-functions-js.md +17 -1
- package/docs/serverless-functions.md +67 -7
- package/docs/what-a-game-must-do.md +31 -16
- package/package.json +3 -1
- package/schema/bridge.v1.json +107 -1
- package/schema/capabilities.public.json +10 -0
- package/schema/moment-log-line.json +109 -0
- package/docs/cross-promotion.md +0 -232
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": "13.1
|
|
5
|
+
"version": "13.2.1"
|
|
6
6
|
},
|
|
7
7
|
"entries": [
|
|
8
8
|
{
|
|
@@ -53,9 +53,13 @@
|
|
|
53
53
|
"key": "deviceSettings",
|
|
54
54
|
"title": "Settings kept on this device"
|
|
55
55
|
},
|
|
56
|
+
{
|
|
57
|
+
"key": "moments",
|
|
58
|
+
"title": "Platform moments"
|
|
59
|
+
},
|
|
56
60
|
{
|
|
57
61
|
"key": "ads",
|
|
58
|
-
"title": "
|
|
62
|
+
"title": "Rewarded ads, and the deprecated interstitial"
|
|
59
63
|
},
|
|
60
64
|
{
|
|
61
65
|
"key": "account",
|
|
@@ -382,7 +386,7 @@
|
|
|
382
386
|
"defaultValue": null,
|
|
383
387
|
"guides": [
|
|
384
388
|
{
|
|
385
|
-
"slug": "
|
|
389
|
+
"slug": "rewarded-ads",
|
|
386
390
|
"anchor": null
|
|
387
391
|
}
|
|
388
392
|
]
|
|
@@ -3767,7 +3771,7 @@
|
|
|
3767
3771
|
"group": "ads",
|
|
3768
3772
|
"signature": "type AdFormat = \"interstitial\" | \"rewarded\"",
|
|
3769
3773
|
"documented": true,
|
|
3770
|
-
"summary": "Which kind of break this is.\n\n`interstitial` is a panel the player dismisses and nothing follows from.\n\n`rewarded` is a button the player pressed for a reward, such as a revive or\nan extra attempt, that your game config declares for the placement in\n`rewarded_placements`. The press is the player's opt-in, and the arcade\nstarts the ad straight away, so ask from the button's own handler (see\n[AdsApi.requestAd](api-reference-game.md#adsapi-requestad)). It is served where this arcade serves it, and\nanswered `no_ad` where it does not. ⚠️ The outcome grants nothing: the\nplatform grants the reward, against its own record, never your game reading\n`shown`. It arrives on your game's inventory for this player, written by the\nplatform, so re-read `gem.me.inventory` after a `rewarded` ask settles rather\nthan waiting for a signal on this pair; there will not be one.",
|
|
3774
|
+
"summary": "Which kind of break this is.\n\n`interstitial` is a panel the player dismisses and nothing follows from.\n**Deprecated:** signal a platform moment with `gem.moments.signal` instead.\nIt keeps working — the arcade treats it as a platform moment — and `14.0.0`\nis the earliest release that may remove it (`docs/deprecation-policy.md`).\n\n`rewarded` is a button the player pressed for a reward, such as a revive or\nan extra attempt, that your game config declares for the placement in\n`rewarded_placements`. The press is the player's opt-in, and the arcade\nstarts the ad straight away, so ask from the button's own handler (see\n[AdsApi.requestAd](api-reference-game.md#adsapi-requestad)). It is served where this arcade serves it, and\nanswered `no_ad` where it does not. ⚠️ The outcome grants nothing: the\nplatform grants the reward, against its own record, never your game reading\n`shown`. It arrives on your game's inventory for this player, written by the\nplatform, so re-read `gem.me.inventory` after a `rewarded` ask settles rather\nthan waiting for a signal on this pair; there will not be one.",
|
|
3771
3775
|
"deprecated": null,
|
|
3772
3776
|
"params": [],
|
|
3773
3777
|
"returns": null,
|
|
@@ -3884,7 +3888,7 @@
|
|
|
3884
3888
|
"group": "ads",
|
|
3885
3889
|
"signature": "interface AdsApi",
|
|
3886
3890
|
"documented": true,
|
|
3887
|
-
"summary": "
|
|
3891
|
+
"summary": "Rewarded ads, and the deprecated interstitial ask. `gem.ads` is one of these.",
|
|
3888
3892
|
"deprecated": null,
|
|
3889
3893
|
"params": [],
|
|
3890
3894
|
"returns": null,
|
|
@@ -3900,7 +3904,7 @@
|
|
|
3900
3904
|
"group": "ads",
|
|
3901
3905
|
"signature": "requestAd(req: AdRequest): Promise<AdOutcome>",
|
|
3902
3906
|
"documented": true,
|
|
3903
|
-
"summary": "Ask whether the arcade has anything to show at this break in play.\n\nNOTHING REJECTS, and the answer is usually `no_ad`. Every local refusal —\nan arcade that does not do this at all, a call while another is still in\nflight, a `placement` this module will not send — resolves `no_ad` rather\nthan throwing, so a game needs exactly one branch. Only \"no answer ever\narrived\" resolves `unavailable`.\n\nThe arcade pauses your game while a panel is up and resumes it when this\nsettles, so you do not need to pause anything yourself.\n\n⚠️ `shown` grants nothing. Never hand out a reward, an unlock or a currency\non the strength of it. A `rewarded` placement's reward is granted by the\nplatform into your game's inventory; re-read `gem.me.inventory` once this\nsettles, whatever it settled as.\n\n**A `rewarded` ask must come from the player's press.** The press is the\nplayer's opt-in, and the arcade starts the ad as soon as the ask arrives.\nCall this from your button's `click`, `pointerup`, `touchend` or `keydown`\nhandler, before anything that can outlast the press, such as a slow\n`await` or a timer. **Not from `pointerdown` or `touchstart`:** on a\ntouchscreen they run before the browser counts the tap, so the ask is\nrefused on a phone while the same code works at a desk with a mouse. A\ncontroller press counts when the ask follows it within a second, so ask\nfrom the frame of your input loop that saw the button go down. Where the\nbrowser reports user activation (`navigator.userActivation`), the frame\nholds none, and no controller was used in the last second, this answers\n`no_ad` without asking. It also calls your `onError` handler with\ncode `'internal'` and a message that begins\n`rewarded_requires_user_gesture`, and writes one `[gem]` line a session\nthrough your `log`. Where the browser has no such API, the ask goes ahead.\nAn `interstitial` needs no press.",
|
|
3907
|
+
"summary": "Ask whether the arcade has anything to show at this break in play.\n\n**For a break in play, signal a platform moment with\n`gem.moments.signal` instead: `format: 'interstitial'` is deprecated.** It\nkeeps working until at least `14.0.0`. A `rewarded` ask is unchanged.\n\nNOTHING REJECTS, and the answer is usually `no_ad`. Every local refusal —\nan arcade that does not do this at all, a call while another is still in\nflight, a `placement` this module will not send — resolves `no_ad` rather\nthan throwing, so a game needs exactly one branch. Only \"no answer ever\narrived\" resolves `unavailable`.\n\nThe arcade pauses your game while a panel is up and resumes it when this\nsettles, so you do not need to pause anything yourself.\n\n⚠️ `shown` grants nothing. Never hand out a reward, an unlock or a currency\non the strength of it. A `rewarded` placement's reward is granted by the\nplatform into your game's inventory; re-read `gem.me.inventory` once this\nsettles, whatever it settled as.\n\n**A `rewarded` ask must come from the player's press.** The press is the\nplayer's opt-in, and the arcade starts the ad as soon as the ask arrives.\nCall this from your button's `click`, `pointerup`, `touchend` or `keydown`\nhandler, before anything that can outlast the press, such as a slow\n`await` or a timer. **Not from `pointerdown` or `touchstart`:** on a\ntouchscreen they run before the browser counts the tap, so the ask is\nrefused on a phone while the same code works at a desk with a mouse. A\ncontroller press counts when the ask follows it within a second, so ask\nfrom the frame of your input loop that saw the button go down. Where the\nbrowser reports user activation (`navigator.userActivation`), the frame\nholds none, and no controller was used in the last second, this answers\n`no_ad` without asking. It also calls your `onError` handler with\ncode `'internal'` and a message that begins\n`rewarded_requires_user_gesture`, and writes one `[gem]` line a session\nthrough your `log`. Where the browser has no such API, the ask goes ahead.\nAn `interstitial` needs no press.",
|
|
3904
3908
|
"deprecated": null,
|
|
3905
3909
|
"params": [
|
|
3906
3910
|
{
|
|
@@ -3912,7 +3916,7 @@
|
|
|
3912
3916
|
],
|
|
3913
3917
|
"returns": "The arcade's answer. Always resolves; never rejects.",
|
|
3914
3918
|
"throws": null,
|
|
3915
|
-
"example": "```ts\
|
|
3919
|
+
"example": "```ts\n// A rewarded ask, from the button's own click: the ask comes first, and\n// the inventory read comes after it.\nwatchAdButton.addEventListener('click', async () => {\n watchAdButton.disabled = true; // only between the press and the answer\n await gem.ads.requestAd({ placement: 'dice_rolls', format: 'rewarded' });\n gem.me.inventory.refresh();\n try {\n showDice(await gem.me.inventory.snapshot());\n } finally {\n watchAdButton.disabled = false;\n }\n});\n```",
|
|
3916
3920
|
"defaultValue": null,
|
|
3917
3921
|
"guides": []
|
|
3918
3922
|
},
|
|
@@ -3939,7 +3943,7 @@
|
|
|
3939
3943
|
"defaultValue": null,
|
|
3940
3944
|
"guides": [
|
|
3941
3945
|
{
|
|
3942
|
-
"slug": "
|
|
3946
|
+
"slug": "rewarded-ads",
|
|
3943
3947
|
"anchor": null
|
|
3944
3948
|
}
|
|
3945
3949
|
]
|
|
@@ -4319,7 +4323,7 @@
|
|
|
4319
4323
|
"group": "analytics",
|
|
4320
4324
|
"signature": "class AnalyticsApi",
|
|
4321
4325
|
"documented": true,
|
|
4322
|
-
"summary": "Your game's own analytics — the events you declared in your game config,\nplus the `gem_*` session and match events the SDK emits for you.\n\nReached as `gem.analytics`. [AnalyticsApi.track](api-reference-game.md#analyticsapi-track) is synchronous, never\nthrows and never rejects: it puts an event on an in-memory queue, and the\nSDK posts that queue on its own timer, when it reaches 50 events, and on the\nway out of the page.\n\n```js\ngem.analytics.setContext({ build: 'v1.4.2' });\n\ngem.analytics.track('level_completed', {\n outcome: 'win', // a dimension — a declared, closed set\n difficulty: 'normal', // a dimension\n duration_ms: 48_120, // a measure — summed, averaged, min and max\n score: 3_100, // a measure\n});\n```\n\n⚠️ **Every key has to be declared.** `analytics_events` in your game config\nis what says which events exist, which dimensions each has and which values\neach dimension may take. An undeclared name or key is not stored — it comes\nback as a rejection, which the SDK prints to the console during development.\n\n⚠️ **Detail older than
|
|
4326
|
+
"summary": "Your game's own analytics — the events you declared in your game config,\nplus the `gem_*` session and match events the SDK emits for you.\n\nReached as `gem.analytics`. [AnalyticsApi.track](api-reference-game.md#analyticsapi-track) is synchronous, never\nthrows and never rejects: it puts an event on an in-memory queue, and the\nSDK posts that queue on its own timer, when it reaches 50 events, and on the\nway out of the page.\n\n```js\ngem.analytics.setContext({ build: 'v1.4.2' });\n\ngem.analytics.track('level_completed', {\n outcome: 'win', // a dimension — a declared, closed set\n difficulty: 'normal', // a dimension\n duration_ms: 48_120, // a measure — summed, averaged, min and max\n score: 3_100, // a measure\n});\n```\n\n⚠️ **Every key has to be declared.** `analytics_events` in your game config\nis what says which events exist, which dimensions each has and which values\neach dimension may take. An undeclared name or key is not stored — it comes\nback as a rejection, which the SDK prints to the console during development.\n\n⚠️ **Detail older than 60 days is gone.** Raw events live 60 days; the daily\ncounters live 400. Anything you want to be able to ask about a year from now\nhas to be a declared dimension today.\n\n⚠️ **These are your numbers, not the platform's.** Client events are\ngame-asserted browser facts. They feed your analytics and nothing the\nplatform treats as authoritative — not rewards, not dailies, not store\nranking.",
|
|
4323
4327
|
"deprecated": null,
|
|
4324
4328
|
"params": [],
|
|
4325
4329
|
"returns": null,
|
|
@@ -4408,7 +4412,7 @@
|
|
|
4408
4412
|
"group": "analytics",
|
|
4409
4413
|
"signature": "flush(options?: AnalyticsFlushOptions): Promise<AnalyticsFlushResult>",
|
|
4410
4414
|
"documented": true,
|
|
4411
|
-
"summary": "Send what is queued now, and find out what happened to it.\n\nResolves with the platform's own answer, INCLUDING per-event rejections, which\narrive inside a 200 — see [AnalyticsFlushResult.rejected](api-reference-game.md#analyticsflushresult-rejected). You do\nnot have to call this: the SDK flushes on its own, and on the way out of\nthe page.\n\nA refusal that could clear on its own — a 429 from the rate limiter, a 503\nwhile the platform's idempotency store is unreachable — KEEPS the queue\nand rejects, so the next flush sends the same events rather than losing\nthem. A refusal that cannot clear discards the batch it was sent for,\nbecause resending it can only fail identically.",
|
|
4415
|
+
"summary": "Send what is queued now, and find out what happened to it.\n\nResolves with the platform's own answer, INCLUDING per-event rejections, which\narrive inside a 200 — see [AnalyticsFlushResult.rejected](api-reference-game.md#analyticsflushresult-rejected). You do\nnot have to call this: the SDK flushes on its own, and on the way out of\nthe page.\n\nA refusal that could clear on its own — a 429 from the rate limiter, a 503\nwhile the platform's idempotency store is unreachable — KEEPS the queue\nand rejects, so the next flush sends the same events rather than losing\nthem. The SDK's own flushes then hold off for the longer of the server's\n`Retry-After` and a cooldown that doubles from 10 seconds, up to five\nminutes. A refusal that cannot clear discards the batch it was sent for,\nbecause resending it can only fail identically.\n\nOne flush at a time: called while another is still on the wire, this\nresolves at once with nothing sent, and what is queued goes out after it.",
|
|
4412
4416
|
"deprecated": null,
|
|
4413
4417
|
"params": [
|
|
4414
4418
|
{
|
|
@@ -8338,7 +8342,7 @@
|
|
|
8338
8342
|
"group": "start",
|
|
8339
8343
|
"signature": "ARCADE_EMBEDDERS: readonly string[]",
|
|
8340
8344
|
"documented": true,
|
|
8341
|
-
"summary": "The origins the
|
|
8345
|
+
"summary": "The origins the Gem Arcade embeds a game from, which every game accepts\nwithout naming them: the website, and the Gem Arcade app.\n\nYou do not pass these. They are always allowed, and\n[GemOptions.allowedEmbedders](api-reference-game.md#gemoptions-allowedembedders) adds to them rather than replacing them.",
|
|
8342
8346
|
"deprecated": null,
|
|
8343
8347
|
"params": [],
|
|
8344
8348
|
"returns": null,
|
|
@@ -8370,7 +8374,7 @@
|
|
|
8370
8374
|
"group": "start",
|
|
8371
8375
|
"signature": "readonly allowedEmbedders?: readonly string[]",
|
|
8372
8376
|
"documented": true,
|
|
8373
|
-
"summary": "Extra origins allowed to embed your game, **added to** the ones the Gem\nArcade uses. Most games leave this out.\n\nThe
|
|
8377
|
+
"summary": "Extra origins allowed to embed your game, **added to** the ones the Gem\nArcade uses. Most games leave this out.\n\nThe website and the Gem Arcade app are always allowed — see\n[ARCADE_EMBEDDERS](api-reference-game.md#arcade_embedders) — so do not list them. Set this only when a site\nof your own embeds your game.\n\n```js\ncreateGem({ allowedEmbedders: ['https://my-own-site.example'] })\n```\n\nA page whose origin is not allowed gets no connection, and — deliberately —\nis told nothing about why. That means a typo here looks exactly like a\nhandshake that has not arrived: pass [GemOptions.onRefused](api-reference-game.md#gemoptions-onrefused) while you\nare integrating, because it is the only place the difference shows.\n\n⚠️ **Bake these in. Never build them from anything the page can\ninfluence** — not `location`, not a query parameter, not\n`document.referrer`. A value the embedder supplies is a check that can never\nfail. Do not add a `localhost:<port>` entry for your dev host either; the\nSDK admits one on your own machine by rule.",
|
|
8374
8378
|
"deprecated": null,
|
|
8375
8379
|
"params": [],
|
|
8376
8380
|
"returns": null,
|
|
@@ -10107,6 +10111,27 @@
|
|
|
10107
10111
|
"defaultValue": null,
|
|
10108
10112
|
"guides": []
|
|
10109
10113
|
},
|
|
10114
|
+
{
|
|
10115
|
+
"id": "Gem.moments",
|
|
10116
|
+
"name": "moments",
|
|
10117
|
+
"anchor": "gem-moments",
|
|
10118
|
+
"group": "start",
|
|
10119
|
+
"signature": "readonly moments: MomentsApi",
|
|
10120
|
+
"documented": true,
|
|
10121
|
+
"summary": "Platform moments — **every game, at every break.** Signal one from the\nplayer's press that leaves a natural break in play (Next, Retry, Back to\nmenu), and the arcade may show one of its own offers over your paused\ngame. It usually shows nothing.\n\nRequired by the release checklist at every important break, whether or\nnot your game has a shop of its own. Never resolves a reason, never\nrejects, and `shown` grants nothing.",
|
|
10122
|
+
"deprecated": null,
|
|
10123
|
+
"params": [],
|
|
10124
|
+
"returns": null,
|
|
10125
|
+
"throws": null,
|
|
10126
|
+
"example": null,
|
|
10127
|
+
"defaultValue": null,
|
|
10128
|
+
"guides": [
|
|
10129
|
+
{
|
|
10130
|
+
"slug": "platform-moments",
|
|
10131
|
+
"anchor": null
|
|
10132
|
+
}
|
|
10133
|
+
]
|
|
10134
|
+
},
|
|
10110
10135
|
{
|
|
10111
10136
|
"id": "Gem.ads",
|
|
10112
10137
|
"name": "ads",
|
|
@@ -10114,7 +10139,7 @@
|
|
|
10114
10139
|
"group": "start",
|
|
10115
10140
|
"signature": "readonly ads: AdsApi",
|
|
10116
10141
|
"documented": true,
|
|
10117
|
-
"summary": "
|
|
10142
|
+
"summary": "Rewarded ads, and the deprecated interstitial ask.\n\n**`format: 'interstitial'` is deprecated: signal a platform moment with\n`gem.moments.signal` instead.** It keeps working until at least `14.0.0`\n(`docs/deprecation-policy.md`); the arcade treats it as a platform moment.\n\nA rewarded ask comes from the player's press on your own button. **The\nanswer is usually no, and that is ordinary rather than a failure:** carry\nstraight on, and draw no placeholder while you wait.\n\nNothing here rejects. The arcade pauses your game while a panel is up and\nresumes it when the promise settles, so you do not pause anything yourself.\n\n⚠️ `shown` **grants nothing.** It is not a receipt and not an entitlement,\nand a reward must never be handed out on the strength of it.\n\nYou are told no reason for a no, and there is nothing to infer from one\nabout the player. What you can read is your own economy:\n`rewardedStatus(placement)` says whether a rewarded placement could pay\nright now under your own declared caps — never whether an ad will play.",
|
|
10118
10143
|
"deprecated": null,
|
|
10119
10144
|
"params": [],
|
|
10120
10145
|
"returns": null,
|
|
@@ -10123,7 +10148,7 @@
|
|
|
10123
10148
|
"defaultValue": null,
|
|
10124
10149
|
"guides": [
|
|
10125
10150
|
{
|
|
10126
|
-
"slug": "
|
|
10151
|
+
"slug": "rewarded-ads",
|
|
10127
10152
|
"anchor": null
|
|
10128
10153
|
}
|
|
10129
10154
|
]
|
|
@@ -12122,6 +12147,151 @@
|
|
|
12122
12147
|
"defaultValue": null,
|
|
12123
12148
|
"guides": []
|
|
12124
12149
|
},
|
|
12150
|
+
{
|
|
12151
|
+
"id": "MomentKind",
|
|
12152
|
+
"name": "MomentKind",
|
|
12153
|
+
"anchor": "momentkind",
|
|
12154
|
+
"group": "moments",
|
|
12155
|
+
"signature": "type MomentKind = \"level_end\" | \"run_end\" | \"match_end\" | \"menu_return\" | \"other_break\"",
|
|
12156
|
+
"documented": true,
|
|
12157
|
+
"summary": "Which kind of break a platform moment is. A closed set — there will not be a\nsixth:\n\n- `level_end` — a level, stage, wave or puzzle finished, won or lost.\n- `run_end` — game over, death, or a run banked, in an endless, arcade or\n roguelike game.\n- `match_end` — a multiplayer round or match finished and the player is\n leaving its results.\n- `menu_return` — the player chose to go back to the title, a main menu or\n level select. A return, never the first arrival.\n- `other_break` — any other natural stop: a story chapter, a between-waves\n shop.\n\nIt is a label, for the arcade's analytics, the dev host's log and the release\nchecklist. It never changes what the arcade decides for your game.",
|
|
12158
|
+
"deprecated": null,
|
|
12159
|
+
"params": [],
|
|
12160
|
+
"returns": null,
|
|
12161
|
+
"throws": null,
|
|
12162
|
+
"example": null,
|
|
12163
|
+
"defaultValue": null,
|
|
12164
|
+
"guides": [
|
|
12165
|
+
{
|
|
12166
|
+
"slug": "platform-moments",
|
|
12167
|
+
"anchor": null
|
|
12168
|
+
}
|
|
12169
|
+
]
|
|
12170
|
+
},
|
|
12171
|
+
{
|
|
12172
|
+
"id": "MomentSignal",
|
|
12173
|
+
"name": "MomentSignal",
|
|
12174
|
+
"anchor": "momentsignal",
|
|
12175
|
+
"group": "moments",
|
|
12176
|
+
"signature": "interface MomentSignal",
|
|
12177
|
+
"documented": true,
|
|
12178
|
+
"summary": "A platform moment: which kind of break the player is leaving, and optionally\nyour own name for the screen.",
|
|
12179
|
+
"deprecated": null,
|
|
12180
|
+
"params": [],
|
|
12181
|
+
"returns": null,
|
|
12182
|
+
"throws": null,
|
|
12183
|
+
"example": null,
|
|
12184
|
+
"defaultValue": null,
|
|
12185
|
+
"guides": []
|
|
12186
|
+
},
|
|
12187
|
+
{
|
|
12188
|
+
"id": "MomentSignal.kind",
|
|
12189
|
+
"name": "kind",
|
|
12190
|
+
"anchor": "momentsignal-kind",
|
|
12191
|
+
"group": "moments",
|
|
12192
|
+
"signature": "kind: MomentKind",
|
|
12193
|
+
"documented": true,
|
|
12194
|
+
"summary": "Which kind of break this is. Required.",
|
|
12195
|
+
"deprecated": null,
|
|
12196
|
+
"params": [],
|
|
12197
|
+
"returns": null,
|
|
12198
|
+
"throws": null,
|
|
12199
|
+
"example": null,
|
|
12200
|
+
"defaultValue": null,
|
|
12201
|
+
"guides": []
|
|
12202
|
+
},
|
|
12203
|
+
{
|
|
12204
|
+
"id": "MomentSignal.placement",
|
|
12205
|
+
"name": "placement",
|
|
12206
|
+
"anchor": "momentsignal-placement",
|
|
12207
|
+
"group": "moments",
|
|
12208
|
+
"signature": "placement?: string",
|
|
12209
|
+
"documented": true,
|
|
12210
|
+
"summary": "Optional: your own name for the screen the player is leaving —\n`world-1-3`, `results`. **1 to 32 characters of `a-z`, `0-9`, `-` and\n`_`.** It appears only in the dev host's log, so a reviewer can map each\nmoment to a screen. It is never sent to the platform, never stored, never\nshown to a player and never sent to an ad network.",
|
|
12211
|
+
"deprecated": null,
|
|
12212
|
+
"params": [],
|
|
12213
|
+
"returns": null,
|
|
12214
|
+
"throws": null,
|
|
12215
|
+
"example": null,
|
|
12216
|
+
"defaultValue": null,
|
|
12217
|
+
"guides": []
|
|
12218
|
+
},
|
|
12219
|
+
{
|
|
12220
|
+
"id": "MomentResult",
|
|
12221
|
+
"name": "MomentResult",
|
|
12222
|
+
"anchor": "momentresult",
|
|
12223
|
+
"group": "moments",
|
|
12224
|
+
"signature": "interface MomentResult",
|
|
12225
|
+
"documented": true,
|
|
12226
|
+
"summary": "What the arcade answers at a platform moment.",
|
|
12227
|
+
"deprecated": null,
|
|
12228
|
+
"params": [],
|
|
12229
|
+
"returns": null,
|
|
12230
|
+
"throws": null,
|
|
12231
|
+
"example": null,
|
|
12232
|
+
"defaultValue": null,
|
|
12233
|
+
"guides": []
|
|
12234
|
+
},
|
|
12235
|
+
{
|
|
12236
|
+
"id": "MomentResult.shown",
|
|
12237
|
+
"name": "shown",
|
|
12238
|
+
"anchor": "momentresult-shown",
|
|
12239
|
+
"group": "moments",
|
|
12240
|
+
"signature": "readonly shown: boolean",
|
|
12241
|
+
"documented": true,
|
|
12242
|
+
"summary": "Whether the arcade showed anything of its own at this break. Usually\n`false`. Either way, carry on exactly as you would have. **It grants\nnothing**, and it never says what was shown or why nothing was.",
|
|
12243
|
+
"deprecated": null,
|
|
12244
|
+
"params": [],
|
|
12245
|
+
"returns": null,
|
|
12246
|
+
"throws": null,
|
|
12247
|
+
"example": null,
|
|
12248
|
+
"defaultValue": null,
|
|
12249
|
+
"guides": []
|
|
12250
|
+
},
|
|
12251
|
+
{
|
|
12252
|
+
"id": "MomentsApi",
|
|
12253
|
+
"name": "MomentsApi",
|
|
12254
|
+
"anchor": "momentsapi",
|
|
12255
|
+
"group": "moments",
|
|
12256
|
+
"signature": "interface MomentsApi",
|
|
12257
|
+
"documented": true,
|
|
12258
|
+
"summary": "Platform moments. `gem.moments` is one of these.",
|
|
12259
|
+
"deprecated": null,
|
|
12260
|
+
"params": [],
|
|
12261
|
+
"returns": null,
|
|
12262
|
+
"throws": null,
|
|
12263
|
+
"example": null,
|
|
12264
|
+
"defaultValue": null,
|
|
12265
|
+
"guides": []
|
|
12266
|
+
},
|
|
12267
|
+
{
|
|
12268
|
+
"id": "MomentsApi.signal",
|
|
12269
|
+
"name": "signal",
|
|
12270
|
+
"anchor": "momentsapi-signal",
|
|
12271
|
+
"group": "moments",
|
|
12272
|
+
"signature": "signal(moment: MomentSignal): Promise<MomentResult>",
|
|
12273
|
+
"documented": true,
|
|
12274
|
+
"summary": "Signal a platform moment: the player is leaving a natural break in play.\nThe arcade may show one of its own offers over your paused game, and\nusually shows nothing.\n\n**Call it from the player's press that LEAVES the break** — Next, Retry,\nBack to menu — once per break. Start your own transition in parallel, and\nstart play when this settles. **Never** mid-input, during a timed\nsequence, on a timer, on boot or the first arrival at the title, or from a\nplayer-opened pause menu (choosing Quit to menu from it is a\n`menu_return`). No press is required by the SDK; the rule is about when a\nbreak really is one.\n\nNOTHING REJECTS. It resolves `{ shown: false }` without asking for an\narcade that does not do platform moments, a `kind` or `placement` outside\nthe grammar, a call before `gem.frame.ready()`, a call while another is in\nflight, a call within 10 seconds of the last answer, or no answer within\nthree minutes — and each of those, except an arcade that does not do it\n(said once a session), is reported to your `onError` handler with code\n`'internal'` and a message beginning with a stable token:\n`moments_unsupported`, `moments_invalid_argument`, `moments_before_ready`,\n`moments_in_flight`, `moments_called_repeatedly` or `moments_timeout`.\n\nThe arcade pauses and resumes your game through the ordinary\n`gem.pause`, so you do not pause anything yourself.\n\n⚠️ `shown` grants nothing. Never hand out a reward, an unlock or a currency\non it, and never pop your own offer at a break that settled `shown: true`.",
|
|
12275
|
+
"deprecated": null,
|
|
12276
|
+
"params": [
|
|
12277
|
+
{
|
|
12278
|
+
"name": "moment",
|
|
12279
|
+
"type": "MomentSignal",
|
|
12280
|
+
"optional": false,
|
|
12281
|
+
"summary": "Which kind of break, and optionally your name for the screen."
|
|
12282
|
+
}
|
|
12283
|
+
],
|
|
12284
|
+
"returns": "Whether the arcade showed anything. Always resolves; never rejects.",
|
|
12285
|
+
"throws": null,
|
|
12286
|
+
"example": "```ts\n// From the press that LEAVES the break — Next, Retry, Back to menu.\nasync function onNextPressed() {\n startLevelTransition(); // your fade or loading beat, in parallel\n await gem.moments.signal({ kind: 'level_end', placement: 'world-1-3' });\n startNextLevel(); // settled either way; never grant anything on `shown`\n}\n```",
|
|
12287
|
+
"defaultValue": null,
|
|
12288
|
+
"guides": [
|
|
12289
|
+
{
|
|
12290
|
+
"slug": "platform-moments",
|
|
12291
|
+
"anchor": null
|
|
12292
|
+
}
|
|
12293
|
+
]
|
|
12294
|
+
},
|
|
12125
12295
|
{
|
|
12126
12296
|
"id": "DedicatedServerFailure",
|
|
12127
12297
|
"name": "DedicatedServerFailure",
|
|
@@ -21802,6 +21972,22 @@
|
|
|
21802
21972
|
"defaultValue": null,
|
|
21803
21973
|
"guides": []
|
|
21804
21974
|
},
|
|
21975
|
+
{
|
|
21976
|
+
"id": "RequestOptions.retryRateLimited",
|
|
21977
|
+
"name": "retryRateLimited",
|
|
21978
|
+
"anchor": "requestoptions-retryratelimited",
|
|
21979
|
+
"group": "calls",
|
|
21980
|
+
"signature": "readonly retryRateLimited?: boolean",
|
|
21981
|
+
"documented": true,
|
|
21982
|
+
"summary": "Set `false` to make a 429 final for this call instead of waiting out\n`Retry-After` and trying again.\n\nFor a caller that keeps its own queue and its own cooldown, such as a\nbatch sender on a timer: retrying inside the call only spends more of the\nsame limit, and holds the caller's queue while it does. Every other\nfailure is retried exactly as before. Defaults to `true`.",
|
|
21983
|
+
"deprecated": null,
|
|
21984
|
+
"params": [],
|
|
21985
|
+
"returns": null,
|
|
21986
|
+
"throws": null,
|
|
21987
|
+
"example": null,
|
|
21988
|
+
"defaultValue": null,
|
|
21989
|
+
"guides": []
|
|
21990
|
+
},
|
|
21805
21991
|
{
|
|
21806
21992
|
"id": "VersionedGetOptions",
|
|
21807
21993
|
"name": "VersionedGetOptions",
|
|
@@ -23984,7 +24170,7 @@
|
|
|
23984
24170
|
"group": "analytics",
|
|
23985
24171
|
"signature": "flush(signal?: AbortSignal): Promise<AnalyticsFlushResult>",
|
|
23986
24172
|
"documented": true,
|
|
23987
|
-
"summary": "Send what is queued now, and find out what happened to it.\n\nResolves with the platform's own answer, INCLUDING per-event rejections, which\narrive inside a 200 rather than as an error. A refusal that could clear on\nits own — a 429, a 503 — keeps the queue and rejects; one that cannot\ndiscards the batch, because resending it can only fail the same way.",
|
|
24173
|
+
"summary": "Send what is queued now, and find out what happened to it.\n\nResolves with the platform's own answer, INCLUDING per-event rejections, which\narrive inside a 200 rather than as an error. A refusal that could clear on\nits own — a 429, a 503 — keeps the queue and rejects, and the SDK's own\nflushes then hold off for the longer of the server's `Retry-After` and a\ncooldown that doubles from 10 seconds, up to five minutes; one that cannot\ndiscards the batch, because resending it can only fail the same way.\n\nOne flush at a time: called while another is on the wire, this waits for\nit and then sends what is left.",
|
|
23988
24174
|
"deprecated": null,
|
|
23989
24175
|
"params": [
|
|
23990
24176
|
{
|
|
@@ -26539,7 +26725,7 @@
|
|
|
26539
26725
|
"usageRoots": [
|
|
26540
26726
|
"GemFunction"
|
|
26541
26727
|
],
|
|
26542
|
-
"install": "A function imports the same package your game already depends on, written out in [getting-started](../getting-started.md#3-add-the-sdk) — the only place the install is written out. Your function’s build bundles this module, so the version your function runs is the one your `package.json` names: pin the version your game pins. A function published importing the SDK from the engine runs the engine’s built-in copy instead; [serverless-functions-js](../serverless-functions-js.md) says what that means.",
|
|
26728
|
+
"install": "⚠️ **JavaScript functions are disabled for now, due to performance issues.** Uploading a new JavaScript function is refused with `400 js_functions_disabled`: write a new function in Rust, as [serverless-functions](../serverless-functions.md#3-writing-one) shows. A JavaScript function already published keeps running, and this page is what it imports.\n\nA function imports the same package your game already depends on, written out in [getting-started](../getting-started.md#3-add-the-sdk) — the only place the install is written out. Your function’s build bundles this module, so the version your function runs is the one your `package.json` names: pin the version your game pins. A function published importing the SDK from the engine runs the engine’s built-in copy instead; [serverless-functions-js](../serverless-functions-js.md) says what that means.",
|
|
26543
26729
|
"entrySymbol": "createGem",
|
|
26544
26730
|
"groups": [
|
|
26545
26731
|
{
|
|
@@ -26859,7 +27045,7 @@
|
|
|
26859
27045
|
"group": "analytics",
|
|
26860
27046
|
"signature": "class AnalyticsApi",
|
|
26861
27047
|
"documented": true,
|
|
26862
|
-
"summary": "Your game's own analytics — the events you declared in your game config,\nplus the `gem_*` session and match events the SDK emits for you.\n\nReached as `gem.analytics`. [AnalyticsApi.track](api-reference-function.md#analyticsapi-track) is synchronous, never\nthrows and never rejects: it puts an event on an in-memory queue, and the\nSDK posts that queue on its own timer, when it reaches 50 events, and on the\nway out of the page.\n\n```js\ngem.analytics.setContext({ build: 'v1.4.2' });\n\ngem.analytics.track('level_completed', {\n outcome: 'win', // a dimension — a declared, closed set\n difficulty: 'normal', // a dimension\n duration_ms: 48_120, // a measure — summed, averaged, min and max\n score: 3_100, // a measure\n});\n```\n\n⚠️ **Every key has to be declared.** `analytics_events` in your game config\nis what says which events exist, which dimensions each has and which values\neach dimension may take. An undeclared name or key is not stored — it comes\nback as a rejection, which the SDK prints to the console during development.\n\n⚠️ **Detail older than
|
|
27048
|
+
"summary": "Your game's own analytics — the events you declared in your game config,\nplus the `gem_*` session and match events the SDK emits for you.\n\nReached as `gem.analytics`. [AnalyticsApi.track](api-reference-function.md#analyticsapi-track) is synchronous, never\nthrows and never rejects: it puts an event on an in-memory queue, and the\nSDK posts that queue on its own timer, when it reaches 50 events, and on the\nway out of the page.\n\n```js\ngem.analytics.setContext({ build: 'v1.4.2' });\n\ngem.analytics.track('level_completed', {\n outcome: 'win', // a dimension — a declared, closed set\n difficulty: 'normal', // a dimension\n duration_ms: 48_120, // a measure — summed, averaged, min and max\n score: 3_100, // a measure\n});\n```\n\n⚠️ **Every key has to be declared.** `analytics_events` in your game config\nis what says which events exist, which dimensions each has and which values\neach dimension may take. An undeclared name or key is not stored — it comes\nback as a rejection, which the SDK prints to the console during development.\n\n⚠️ **Detail older than 60 days is gone.** Raw events live 60 days; the daily\ncounters live 400. Anything you want to be able to ask about a year from now\nhas to be a declared dimension today.\n\n⚠️ **These are your numbers, not the platform's.** Client events are\ngame-asserted browser facts. They feed your analytics and nothing the\nplatform treats as authoritative — not rewards, not dailies, not store\nranking.",
|
|
26863
27049
|
"deprecated": null,
|
|
26864
27050
|
"params": [],
|
|
26865
27051
|
"returns": null,
|
|
@@ -26948,7 +27134,7 @@
|
|
|
26948
27134
|
"group": "analytics",
|
|
26949
27135
|
"signature": "flush(options?: AnalyticsFlushOptions): Promise<AnalyticsFlushResult>",
|
|
26950
27136
|
"documented": true,
|
|
26951
|
-
"summary": "Send what is queued now, and find out what happened to it.\n\nResolves with the platform's own answer, INCLUDING per-event rejections, which\narrive inside a 200 — see [AnalyticsFlushResult.rejected](api-reference-function.md#analyticsflushresult-rejected). You do\nnot have to call this: the SDK flushes on its own, and on the way out of\nthe page.\n\nA refusal that could clear on its own — a 429 from the rate limiter, a 503\nwhile the platform's idempotency store is unreachable — KEEPS the queue\nand rejects, so the next flush sends the same events rather than losing\nthem. A refusal that cannot clear discards the batch it was sent for,\nbecause resending it can only fail identically.",
|
|
27137
|
+
"summary": "Send what is queued now, and find out what happened to it.\n\nResolves with the platform's own answer, INCLUDING per-event rejections, which\narrive inside a 200 — see [AnalyticsFlushResult.rejected](api-reference-function.md#analyticsflushresult-rejected). You do\nnot have to call this: the SDK flushes on its own, and on the way out of\nthe page.\n\nA refusal that could clear on its own — a 429 from the rate limiter, a 503\nwhile the platform's idempotency store is unreachable — KEEPS the queue\nand rejects, so the next flush sends the same events rather than losing\nthem. The SDK's own flushes then hold off for the longer of the server's\n`Retry-After` and a cooldown that doubles from 10 seconds, up to five\nminutes. A refusal that cannot clear discards the batch it was sent for,\nbecause resending it can only fail identically.\n\nOne flush at a time: called while another is still on the wire, this\nresolves at once with nothing sent, and what is queued goes out after it.",
|
|
26952
27138
|
"deprecated": null,
|
|
26953
27139
|
"params": [
|
|
26954
27140
|
{
|
|
@@ -27252,6 +27438,22 @@
|
|
|
27252
27438
|
"defaultValue": null,
|
|
27253
27439
|
"guides": []
|
|
27254
27440
|
},
|
|
27441
|
+
{
|
|
27442
|
+
"id": "RequestOptions.retryRateLimited",
|
|
27443
|
+
"name": "retryRateLimited",
|
|
27444
|
+
"anchor": "requestoptions-retryratelimited",
|
|
27445
|
+
"group": "calls",
|
|
27446
|
+
"signature": "readonly retryRateLimited?: boolean",
|
|
27447
|
+
"documented": true,
|
|
27448
|
+
"summary": "Set `false` to make a 429 final for this call instead of waiting out\n`Retry-After` and trying again.\n\nFor a caller that keeps its own queue and its own cooldown, such as a\nbatch sender on a timer: retrying inside the call only spends more of the\nsame limit, and holds the caller's queue while it does. Every other\nfailure is retried exactly as before. Defaults to `true`.",
|
|
27449
|
+
"deprecated": null,
|
|
27450
|
+
"params": [],
|
|
27451
|
+
"returns": null,
|
|
27452
|
+
"throws": null,
|
|
27453
|
+
"example": null,
|
|
27454
|
+
"defaultValue": null,
|
|
27455
|
+
"guides": []
|
|
27456
|
+
},
|
|
27255
27457
|
{
|
|
27256
27458
|
"id": "VersionedGetOptions",
|
|
27257
27459
|
"name": "VersionedGetOptions",
|
package/docs/changelog.md
CHANGED
|
@@ -16,6 +16,28 @@ 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
|
+
## 13.2.1
|
|
20
|
+
|
|
21
|
+
Upgrading from 13.2.0. Impact: none.
|
|
22
|
+
|
|
23
|
+
Nothing in this release asks anything of a game.
|
|
24
|
+
|
|
25
|
+
## 13.2.0
|
|
26
|
+
|
|
27
|
+
Upgrading from 13.1.0. Impact: required.
|
|
28
|
+
|
|
29
|
+
### Rebuild
|
|
30
|
+
|
|
31
|
+
- The message-type lists and unions between game and Arcade gained the moment request and moment result messages, and the capability type gained moments. Recompiling is enough. If your code switches exhaustively over frame messages, parent messages or capabilities, add handling for the new members so it still compiles.
|
|
32
|
+
|
|
33
|
+
### New capability
|
|
34
|
+
|
|
35
|
+
- A game can now signal a platform moment at a natural break through Gem.moments. You pass one of a fixed set of moment kinds and an optional placement, and the answer says whether a moment was shown. The call never rejects. If the SDK refuses locally, for example because the host does not support moments, the argument is invalid, ready has not been called, a request is already in flight, or the call was repeated or timed out, the answer is not shown. Check the moments capability before relying on it. Rate limits and the settle time are the same as for ad requests.
|
|
36
|
+
- On the frame side, GemFrame gains signalMoment for sending a moment request to the Arcade, and readyCalled so you can tell whether ready has been reported yet. Signalling before ready is refused. Frame options and callbacks gain onMomentResult to receive the answer.
|
|
37
|
+
- If you build a host that embeds games, for example a development host, you can now receive moment requests through onSignalMoment in the host options and callbacks, and answer them with answerMoment. The new moment request and result message types come with validators for incoming messages.
|
|
38
|
+
- The package now ships a JSON schema describing the log line that development hosts write for moments. Use it if your tooling reads or checks those log lines.
|
|
39
|
+
- Request options gain retryRateLimited, which defaults to true so existing behaviour is unchanged. Set it to false on a call if you want a rate-limit response handed straight back instead of being retried by the transport. Other failures are still retried. This applies in the browser SDK and the server package.
|
|
40
|
+
|
|
19
41
|
## 13.1.0
|
|
20
42
|
|
|
21
43
|
Upgrading from 13.0.0. Impact: required.
|
|
@@ -99,12 +99,18 @@ deprecated.
|
|
|
99
99
|
| `MatchHosting.stop` | `MatchHosting.end` when the match is over, `MatchHosting.leave` to hand it to another host | `5.0.0` | Package API surface |
|
|
100
100
|
| `GemPlayer.displayHandle` | `PlayerIdentity.displayTag` — the same value | `7.0.0` | Package API surface |
|
|
101
101
|
| `GemPlayer.avatarUrl` | `PlayerIdentity.avatarIconUrl` — the same value | `7.0.0` | Package API surface |
|
|
102
|
+
| `AdRequest.format: 'interstitial'` | `gem.moments.signal` — a platform moment at the same break, from the press that leaves it | `14.0.0` | Package API surface |
|
|
102
103
|
| `profile(playerId)` | The room read's per-member display identity | Not a package release | Runtime delivery only |
|
|
103
104
|
|
|
104
105
|
**"For the member" is the split from section 3**, and it is the row's real answer: `4.0.0` is
|
|
105
106
|
the earliest release that may take the property off the type, and it says nothing about when a
|
|
106
107
|
server stops putting a value in it.
|
|
107
108
|
|
|
109
|
+
**The interstitial row is one value of a field, not a whole symbol.** `format: 'rewarded'` is
|
|
110
|
+
not deprecated, so the reference cannot mark `AdRequest.format` itself; the notice is in the
|
|
111
|
+
field's description and in [`platform-moments.md`](platform-moments.md#12-moving-off-format-interstitial).
|
|
112
|
+
It is still package surface, and section 2 governs it unchanged.
|
|
113
|
+
|
|
108
114
|
The last row is the one that is only a guide's. `profile(playerId)` is marked deprecated in
|
|
109
115
|
[`server-bundles.md`](server-bundles.md) and carries no mark in the generated reference, so
|
|
110
116
|
there is no package release to name for it and section 3 is its whole rule.
|
package/docs/getting-started.md
CHANGED
|
@@ -50,7 +50,7 @@ either way.
|
|
|
50
50
|
```jsonc
|
|
51
51
|
// package.json
|
|
52
52
|
"dependencies": {
|
|
53
|
-
"@indiegems/gem-web-sdk": "13.1
|
|
53
|
+
"@indiegems/gem-web-sdk": "13.2.1"
|
|
54
54
|
}
|
|
55
55
|
```
|
|
56
56
|
|
|
@@ -67,9 +67,12 @@ is involved, and nothing is built when you install.
|
|
|
67
67
|
dedicated server bundle and a listen host all import the same path and the same names.
|
|
68
68
|
`.`, `/game` and `/server` stay published as narrow doors over the same modules — a game
|
|
69
69
|
wants none of them unless it knows why. `/function` is the fourth door, for code that runs as
|
|
70
|
-
a serverless function
|
|
71
|
-
|
|
72
|
-
|
|
70
|
+
a serverless function written in JavaScript
|
|
71
|
+
([serverless-functions-js.md](serverless-functions-js.md)). JavaScript functions are disabled
|
|
72
|
+
for now, due to performance issues: write a new function in Rust
|
|
73
|
+
([serverless-functions.md](serverless-functions.md)). A JavaScript function already published
|
|
74
|
+
keeps running, and its build resolves `/sdk` and `/game` to `/function`, so verification code
|
|
75
|
+
shared with your game keeps its import.
|
|
73
76
|
|
|
74
77
|
**`dependencies`, not `devDependencies`.** The SDK is part of what your game ships — your
|
|
75
78
|
bundler compiles it into the browser build and into the server bundle — so a `--production`
|
package/docs/manifest.json
CHANGED
|
@@ -13,8 +13,8 @@
|
|
|
13
13
|
},
|
|
14
14
|
{
|
|
15
15
|
"file": "analytics.md",
|
|
16
|
-
"sha256": "
|
|
17
|
-
"updated": "2026-10-
|
|
16
|
+
"sha256": "06bc70771dec1fe04451b93f1c55643759edb389927227fae67f246e0b147af6",
|
|
17
|
+
"updated": "2026-10-15T00:02:19Z"
|
|
18
18
|
},
|
|
19
19
|
{
|
|
20
20
|
"file": "arcade-avatars.md",
|
|
@@ -38,13 +38,8 @@
|
|
|
38
38
|
},
|
|
39
39
|
{
|
|
40
40
|
"file": "changelog.md",
|
|
41
|
-
"sha256": "
|
|
42
|
-
"updated": "2026-10-
|
|
43
|
-
},
|
|
44
|
-
{
|
|
45
|
-
"file": "cross-promotion.md",
|
|
46
|
-
"sha256": "92d4a1ffe19ceaaae1812bfdb77ebbdc04a7eca878ff28995eb6c388c7e876b3",
|
|
47
|
-
"updated": "2026-10-08T16:19:25Z"
|
|
41
|
+
"sha256": "474618b1fbfa9d43ae4cb797019f6c799e1ad2de9fad8fa66875f428c7fe599d",
|
|
42
|
+
"updated": "2026-10-15T15:43:44Z"
|
|
48
43
|
},
|
|
49
44
|
{
|
|
50
45
|
"file": "daily-challenges.md",
|
|
@@ -58,8 +53,8 @@
|
|
|
58
53
|
},
|
|
59
54
|
{
|
|
60
55
|
"file": "deprecation-policy.md",
|
|
61
|
-
"sha256": "
|
|
62
|
-
"updated": "2026-
|
|
56
|
+
"sha256": "8134d5463e2737116ed23341bf177c51cede4bd86dabec54d76a82c5bfdfade1",
|
|
57
|
+
"updated": "2026-10-14T22:38:02Z"
|
|
63
58
|
},
|
|
64
59
|
{
|
|
65
60
|
"file": "friends.md",
|
|
@@ -73,8 +68,8 @@
|
|
|
73
68
|
},
|
|
74
69
|
{
|
|
75
70
|
"file": "getting-started.md",
|
|
76
|
-
"sha256": "
|
|
77
|
-
"updated": "2026-10-
|
|
71
|
+
"sha256": "b3617f6056165a881a786f331a809f200d79ed8151d06cb26acba544a9c3dde9",
|
|
72
|
+
"updated": "2026-10-15T15:43:44Z"
|
|
78
73
|
},
|
|
79
74
|
{
|
|
80
75
|
"file": "inventory-and-catalogs.md",
|
|
@@ -96,6 +91,11 @@
|
|
|
96
91
|
"sha256": "3887bc706d098014cefab84e9b8bd5b964841888c242a7ccb1f799a9b9723fc0",
|
|
97
92
|
"updated": "2026-09-24T01:00:22Z"
|
|
98
93
|
},
|
|
94
|
+
{
|
|
95
|
+
"file": "platform-moments.md",
|
|
96
|
+
"sha256": "c9909452a49fe50bac2266e56e5a44ad6495b8464ce1b5c889d0be39604bbff0",
|
|
97
|
+
"updated": "2026-10-14T22:43:26Z"
|
|
98
|
+
},
|
|
99
99
|
{
|
|
100
100
|
"file": "platform-rules.md",
|
|
101
101
|
"sha256": "b259bcebc6ae12b1db9c7279a6e2adb174af9d60090bf31001e23616f6423fbf",
|
|
@@ -108,8 +108,8 @@
|
|
|
108
108
|
},
|
|
109
109
|
{
|
|
110
110
|
"file": "rewarded-ads.md",
|
|
111
|
-
"sha256": "
|
|
112
|
-
"updated": "2026-10-
|
|
111
|
+
"sha256": "ab1096ca610b8f009f0aeb17c573a17fbd2ce6567963285f7b2f73c072170dc2",
|
|
112
|
+
"updated": "2026-10-14T22:38:02Z"
|
|
113
113
|
},
|
|
114
114
|
{
|
|
115
115
|
"file": "room-shapes.md",
|
|
@@ -128,18 +128,18 @@
|
|
|
128
128
|
},
|
|
129
129
|
{
|
|
130
130
|
"file": "server-bundles.md",
|
|
131
|
-
"sha256": "
|
|
132
|
-
"updated": "2026-10-
|
|
131
|
+
"sha256": "3ec75df469f44b3f299c45e237fabc74ede5a42444a14f0b35e068198109558d",
|
|
132
|
+
"updated": "2026-10-15T15:43:44Z"
|
|
133
133
|
},
|
|
134
134
|
{
|
|
135
135
|
"file": "serverless-functions-js.md",
|
|
136
|
-
"sha256": "
|
|
137
|
-
"updated": "2026-10-
|
|
136
|
+
"sha256": "0436dafdc5dcb036d45073ea9c9d4cbebdb5880decc7db8012e04c3b2cfc1016",
|
|
137
|
+
"updated": "2026-10-14T23:28:53Z"
|
|
138
138
|
},
|
|
139
139
|
{
|
|
140
140
|
"file": "serverless-functions.md",
|
|
141
|
-
"sha256": "
|
|
142
|
-
"updated": "2026-10-
|
|
141
|
+
"sha256": "eef5fa14dced0bca39b4f4b9d6688d4f81d6c4d2441ec3497380c21b7482022a",
|
|
142
|
+
"updated": "2026-10-15T14:37:57Z"
|
|
143
143
|
},
|
|
144
144
|
{
|
|
145
145
|
"file": "trading-and-gifting.md",
|
|
@@ -153,8 +153,8 @@
|
|
|
153
153
|
},
|
|
154
154
|
{
|
|
155
155
|
"file": "what-a-game-must-do.md",
|
|
156
|
-
"sha256": "
|
|
157
|
-
"updated": "2026-10-
|
|
156
|
+
"sha256": "439683ca206b2230e48ab46d5eb9a945964fb6f2e308ffcc8ec1e1b9423fc5e8",
|
|
157
|
+
"updated": "2026-10-15T13:34:34Z"
|
|
158
158
|
}
|
|
159
159
|
]
|
|
160
160
|
}
|