@indiegems/gem-web-sdk 24.0.2 → 24.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +24 -0
- package/dist/conformance.d.ts +1 -1
- package/dist/gem-web-sdk.conformance.js +3 -3
- package/dist/gem-web-sdk.esm.js +1 -1
- package/dist/gem-web-sdk.function.js +1 -1
- package/dist/gem-web-sdk.game.js +3 -3
- package/dist/gem-web-sdk.min.js +2 -2
- package/dist/gem-web-sdk.sdk.js +2 -2
- package/dist/gem-web-sdk.server.js +1 -1
- package/docs/api/api-reference-function.md +6 -1
- package/docs/api/api-reference-game.md +22 -10
- package/docs/api/api-reference-server.md +1 -1
- package/docs/api/reference.json +6 -6
- package/docs/arcade-and-store.md +48 -22
- package/docs/changelog.md +24 -0
- package/docs/getting-started.md +1 -1
- package/docs/inventory-and-catalogs.md +28 -15
- package/docs/manifest.json +12 -12
- package/docs/platform-rules.md +3 -2
- package/docs/server-bundles.md +2 -2
- package/package.json +1 -1
- package/schema/bridge.v1.json +7 -3
package/docs/arcade-and-store.md
CHANGED
|
@@ -318,9 +318,8 @@ game never names a SKU ([below](#a-pass-by-its-slug-showpass)). `showOffer(skuId
|
|
|
318
318
|
sheet for any other SKU, such as a bundle.
|
|
319
319
|
|
|
320
320
|
`showOffer(skuId)` asks the Arcade to draw one SKU from `offers()` in its own sheet, over your paused
|
|
321
|
-
and hidden game. The Arcade draws the art, what the player gets
|
|
322
|
-
|
|
323
|
-
`purchase` opens.
|
|
321
|
+
and hidden game. The Arcade draws the art, what the player gets and a sale if one is running. The
|
|
322
|
+
sheet's Buy opens the same checkout `purchase` opens.
|
|
324
323
|
|
|
325
324
|
Pick by who can say what the offer is:
|
|
326
325
|
|
|
@@ -330,20 +329,24 @@ Pick by who can say what the offer is:
|
|
|
330
329
|
| Your button | says what it buys and shows `priceDisplay` | says what it opens, such as "Get VIP" |
|
|
331
330
|
| The press opens | the checkout, straight away | the Arcade's sheet, then the checkout if the player presses Buy |
|
|
332
331
|
| Perks and sales | not available to you | always current, in the player's language |
|
|
333
|
-
| A
|
|
332
|
+
| A held pass | your button cannot tell | the Arcade decides whether the holder is offered it again |
|
|
334
333
|
|
|
335
334
|
- **Never write an offer's perks into your game.** They change without a new build of your game, and
|
|
336
335
|
yours would go stale. Your button only needs the offer's name.
|
|
337
336
|
- **Do not show a price on the button.** The sheet shows the right one for this player, which a
|
|
338
337
|
sale or a pass can change, and `priceDisplay` does not say either of those. If you want a price on
|
|
339
338
|
screen, draw the offer yourself and use `purchase`.
|
|
340
|
-
- **The sheet does not tell you what the player holds.**
|
|
341
|
-
|
|
342
|
-
|
|
339
|
+
- **The sheet does not tell you what the player holds.** To change your own screen for a holder,
|
|
340
|
+
read `gem.me.passes` ([§11](#11-what-the-player-holds-and-a-vip-door)).
|
|
341
|
+
- **A player who already holds a pass may or may not be offered it again.** The Arcade decides,
|
|
342
|
+
and your game must not assume either. `passOnSale` answers for a holder as it does for anyone:
|
|
343
|
+
where it is `false`, `showPass` answers `unavailable` / `not_found` and opens nothing.
|
|
343
344
|
- **Hide the button when the pass is not on sale.** `passOnSale(slug)` says so, and is `false`
|
|
344
|
-
where the store is off, for a player the platform won't sell it to,
|
|
345
|
-
its sheet
|
|
346
|
-
`unavailable` / `not_found`, and the player would have pressed a button that does nothing.
|
|
345
|
+
where the store is off, for a player the platform won't sell it to, and on an older Arcade. A
|
|
346
|
+
player closing its sheet never takes it off sale. A pass that is not on sale answers
|
|
347
|
+
`unavailable` / `not_found`, and the player would have pressed a button that does nothing. To
|
|
348
|
+
test as a VIP holder before VIP is on sale, give yourself VIP with gemctl
|
|
349
|
+
([§11](#testing-what-a-vip-player-sees)).
|
|
347
350
|
- **Read `showOfferAvailable`, not `available`.** An older Arcade can sell through `purchase` and
|
|
348
351
|
still not draw this sheet. It answers `unavailable` / `unsupported` and sends nothing. Hide the
|
|
349
352
|
button there.
|
|
@@ -351,9 +354,10 @@ Pick by who can say what the offer is:
|
|
|
351
354
|
Every rule of `purchase` holds ([§9](#9-buying-from-the-arcade-store)), and the answers are the
|
|
352
355
|
same, with one addition. `declined` / `cancelled` also covers the player leaving the sheet without
|
|
353
356
|
pressing Buy, and you are not told which of the two they closed. Nothing backs off: the player's
|
|
354
|
-
next press on either button opens it again.
|
|
355
|
-
|
|
356
|
-
|
|
357
|
+
next press on either button opens it again. That holds for a pass too: **there is no limit on how
|
|
358
|
+
often your game opens a pass's sheet**, and a player closing it any number of times leaves
|
|
359
|
+
`passOnSale` and `upsells` as they were. The Arcade records how often each pass sheet is opened in
|
|
360
|
+
your game and how long it stays open, for reporting; it never uses that to stop an offer.
|
|
357
361
|
|
|
358
362
|
### A pass, by its slug: `showPass`
|
|
359
363
|
|
|
@@ -376,7 +380,8 @@ something:
|
|
|
376
380
|
answers `ok` like anyone else.
|
|
377
381
|
- **After a paid sheet it waits a little.** It reads `gem.me.passes` for about ten seconds before it
|
|
378
382
|
settles for `pending`, because the payment provider's notice can trail the checkout.
|
|
379
|
-
- **A player who already holds the pass**
|
|
383
|
+
- **A player who already holds the pass** may or may not be offered it again. Where they are not,
|
|
384
|
+
the answer is `unavailable` / `not_found`, as for any pass that is not on sale to the player.
|
|
380
385
|
|
|
381
386
|
### At a reward: the VIP extra roll
|
|
382
387
|
|
|
@@ -393,7 +398,7 @@ openButton.onclick = async () => {
|
|
|
393
398
|
const upsell = preview.upsells[0];
|
|
394
399
|
if (upsell) {
|
|
395
400
|
// "VIP: +1 roll, 45 Coins" — upsell.name, and the extras locked, never what a roll would land on.
|
|
396
|
-
const choice = await vipCard.ask(upsell); // Get VIP
|
|
401
|
+
const choice = await vipCard.ask(upsell); // Get VIP, or Continue
|
|
397
402
|
if (choice === 'get') await gem.store.showPass(upsell.pass);
|
|
398
403
|
openButton.focus(); // focus goes back where it was
|
|
399
404
|
}
|
|
@@ -403,11 +408,11 @@ openButton.onclick = async () => {
|
|
|
403
408
|
```
|
|
404
409
|
|
|
405
410
|
`upsells` is already empty wherever no upsell may be shown: for a minor, a store that is off, a pass
|
|
406
|
-
the player
|
|
407
|
-
list means draw no card. **
|
|
408
|
-
holds nothing back, so a pass bought afterwards cannot reach a chest already opened.
|
|
411
|
+
the player already holds, and a claim no pass would change. A player declining a pass's sheet does
|
|
412
|
+
not empty it. So an empty list means draw no card. **The offer comes before the grant**: the
|
|
413
|
+
platform holds nothing back, so a pass bought afterwards cannot reach a chest already opened.
|
|
409
414
|
[inventory-and-catalogs.md](inventory-and-catalogs.md#offering-vip-before-a-reward-the-upsell) has
|
|
410
|
-
the
|
|
415
|
+
how the upsell works, the one rule for it (disclose a bonus roll's odds), and `bonuses` in full.
|
|
411
416
|
|
|
412
417
|
## 11. What the player holds, and a VIP door
|
|
413
418
|
|
|
@@ -416,7 +421,7 @@ const passes = await gem.me.passes.list();
|
|
|
416
421
|
// [{ pass: 'vip', held: true, daysLeft: 7, endsAt: '2026-11-29T12:34:56Z' }]
|
|
417
422
|
|
|
418
423
|
const vip = await gem.me.passes.get('vip'); // one pass by its slug
|
|
419
|
-
if (vip.held) drawVipBadge(vip.daysLeft
|
|
424
|
+
if (vip.held) drawVipBadge(vip.daysLeft < 7 ? `VIP · ${vip.daysLeft} days left` : 'VIP');
|
|
420
425
|
```
|
|
421
426
|
|
|
422
427
|
`gem.me.passes` says which store passes the player holds. **VIP is the pass `vip`**, its offering
|
|
@@ -428,8 +433,9 @@ slug. Key on the slug, which is stable and not localized, and never on a display
|
|
|
428
433
|
- **A pass is a slug, whether it is held, and how many days it has left.** No price, no name, no
|
|
429
434
|
perks, nothing about how the player got it, and nothing about a pass that has ended.
|
|
430
435
|
- **`daysLeft` is whole days, rounded up and capped at 7**: `7` means "7 or more", and is also what
|
|
431
|
-
a pass held with no end reads (support can grant one). `0` when it is not held.
|
|
432
|
-
|
|
436
|
+
a pass held with no end reads (support can grant one). `0` when it is not held. **`7` is not a
|
|
437
|
+
week's notice:** a pass can read `7` for as long as it is held, and a lower count can rise again.
|
|
438
|
+
Show a count only below `7`, or show `7` as "7+ days left". Never show a date.
|
|
433
439
|
- **`endsAt` is deprecated.** It is the exact end, to the second, and two games could match that
|
|
434
440
|
value to link one player's accounts, so the platform will stop sending it. Use `daysLeft`.
|
|
435
441
|
- **A guest holds nothing.** When the Arcade says the player is a guest, every pass reads
|
|
@@ -579,6 +585,26 @@ export default defineServer((gem) => ({
|
|
|
579
585
|
`gem.match.playerBoost(playerId)` reads the player's Boost for **display** — "2x Coins" on a results
|
|
580
586
|
screen — never for a decision.
|
|
581
587
|
|
|
588
|
+
### Testing what a VIP player sees
|
|
589
|
+
|
|
590
|
+
VIP may not be on sale where you test yet, so `passOnSale('vip')` reads `false` and you cannot buy
|
|
591
|
+
it to test your door, badge or upsell. Give it instead, for 1 to 30 days, with gemctl:
|
|
592
|
+
|
|
593
|
+
```sh
|
|
594
|
+
gemctl org test-accounts pass grant <org-id> me vip --days 7 # your own account
|
|
595
|
+
gemctl org test-accounts pass grant <org-id> "Playtest 3" vip --days 7 # one of the org's test accounts
|
|
596
|
+
```
|
|
597
|
+
|
|
598
|
+
- **You need the Contributor role or above** in the organization. `me` reaches your own account and
|
|
599
|
+
no one else's; any other name is one of the organization's test accounts.
|
|
600
|
+
- **It is not a purchase.** VIP held this way reads like a bought one in `gem.me.passes`,
|
|
601
|
+
`gem.match.playerPasses` and a `holds_pass` requirement, but no purchase is recorded, it is never
|
|
602
|
+
revenue, and nothing renews it: it ends after the days you gave.
|
|
603
|
+
- **One VIP at a time per account.** An account that already holds VIP, granted or bought, is
|
|
604
|
+
refused until it ends. The organization's audit log records every grant.
|
|
605
|
+
- **A personal access token is refused:** sign in to gemctl as yourself.
|
|
606
|
+
`gemctl org test-accounts pass grant --help` has the rest.
|
|
607
|
+
|
|
582
608
|
## 12. Outcomes and reasons
|
|
583
609
|
|
|
584
610
|
| Call | `ok` | `declined` | `unavailable` |
|
package/docs/changelog.md
CHANGED
|
@@ -17,6 +17,30 @@ A release opens with a short summary of what it is about, and its entries are gr
|
|
|
17
17
|
- **New capability**: something a game can now do. Nothing changes unless you use it.
|
|
18
18
|
- **Fixes and improvements**: bugs fixed and behaviour improved without a change to the API. Nothing to change in your game.
|
|
19
19
|
|
|
20
|
+
## 24.1.0
|
|
21
|
+
|
|
22
|
+
Upgrading from 24.0.2. Impact: required.
|
|
23
|
+
|
|
24
|
+
Declining a pass's sheet no longer takes the pass off sale, so a game may open a pass's sheet as often as it likes. A held pass may or may not be offered again, so a game should follow `passOnSale` and assume neither. The only API change is the parent conformance kit version, which moves from 9 to 10 and asks a game to change nothing beyond a rebuild.
|
|
25
|
+
|
|
26
|
+
### Rebuild
|
|
27
|
+
|
|
28
|
+
- `PARENT_CONFORMANCE_VERSION` is now `10` instead of `9`. This constant belongs to the parent conformance kit, which Arcade parents use. A game does not use it, and no message, field, outcome or offer kind was added. Recompiling is enough, and no adapter member is new. A parent that passed kit 9 passes kit 10 unchanged.
|
|
29
|
+
|
|
30
|
+
Only code that compares this constant to the literal `9` needs updating:
|
|
31
|
+
|
|
32
|
+
```ts
|
|
33
|
+
// before
|
|
34
|
+
if (PARENT_CONFORMANCE_VERSION === 9) { /* ... */ }
|
|
35
|
+
// after
|
|
36
|
+
if (PARENT_CONFORMANCE_VERSION === 10) { /* ... */ }
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### Fixes and improvements
|
|
40
|
+
|
|
41
|
+
- Declining a pass's sheet no longer takes the pass off sale. `passOnSale` and the reward preview's `upsells` no longer change after declines, and there is no daily limit on pass offers. The Arcade now records how often a sheet is opened and how long it stays open, for reporting only. Remove any game logic that assumed a pass would stop being offered after two declines.
|
|
42
|
+
- Whether a player who already holds a pass is offered it again is the parent's decision. A pass left out of the holder's listing answers `gem.requestStoreOfferSheet` with one `unavailable` and shows nothing. A pass in the listing opens exactly one sheet. Follow `passOnSale` for a holder as you would for anyone. Do not keep a "Get VIP" button up for a holder on the assumption that its sheet will open, and do not assume a pass lasts 30 days.
|
|
43
|
+
|
|
20
44
|
## 24.0.2
|
|
21
45
|
|
|
22
46
|
Upgrading from 24.0.1. Impact: none.
|
package/docs/getting-started.md
CHANGED
|
@@ -1208,23 +1208,36 @@ now without the bonus. `declined` or `unavailable` means run it as it is — and
|
|
|
1208
1208
|
`error` may still mean the player paid, so read `gem.me.passes` again rather than telling them
|
|
1209
1209
|
they did not.
|
|
1210
1210
|
|
|
1211
|
-
`upsells` is empty for a minor, a store that is off, a pass the player
|
|
1212
|
-
|
|
1211
|
+
`upsells` is empty for a minor, a store that is off, a pass the player already holds, and anywhere
|
|
1212
|
+
the store sells no pass: draw nothing then. Declining a pass's sheet does not empty it: there is no
|
|
1213
|
+
limit on how often a game offers a pass. A guest **does** get
|
|
1213
1214
|
upsells, and buying takes them through sign-up first.
|
|
1214
1215
|
|
|
1215
|
-
**
|
|
1216
|
-
|
|
1217
|
-
- **
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
- **
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
- **
|
|
1224
|
-
-
|
|
1225
|
-
- **
|
|
1226
|
-
|
|
1227
|
-
|
|
1216
|
+
**How it works:**
|
|
1217
|
+
|
|
1218
|
+
- **The offer comes before the grant.** That is the point of the upsell: a bonus applies only if the
|
|
1219
|
+
player holds VIP when the claim runs, and the platform holds nothing back, so a pass bought after
|
|
1220
|
+
the grant cannot reach a reward already given.
|
|
1221
|
+
- **The numbers and the name come from the preview.** Show the preview's numbers and `upsell.name`
|
|
1222
|
+
rather than your own copy of them, and pass `upsell.pass` straight to `showPass` rather than a
|
|
1223
|
+
literal `'vip'`: what is on sale, and what it adds, can change without a release of your game.
|
|
1224
|
+
- **The price comes from the Arcade's sheet.** It varies by player and by sale, so your game does not
|
|
1225
|
+
hard-code one; the sheet shows it.
|
|
1226
|
+
- **A locked roll shows as "+1 roll", not what it would land on.** The roll has not happened: it
|
|
1227
|
+
happens when the recipe runs.
|
|
1228
|
+
|
|
1229
|
+
**The one rule: disclose the odds** for a bonus roll, as for any roll
|
|
1230
|
+
([§12](#12-loot-tables-and-odds)). Store rules require it for a paid random item (Apple's App Store
|
|
1231
|
+
Review Guideline 3.1.1 among them).
|
|
1232
|
+
|
|
1233
|
+
**Recommended, for accessibility:**
|
|
1234
|
+
|
|
1235
|
+
- Show a lock as an icon plus text, not colour alone.
|
|
1236
|
+
- Return focus to the button that opened the sheet when it closes.
|
|
1237
|
+
- Announce an applied bonus in a polite live region (`aria-live="polite"`).
|
|
1238
|
+
|
|
1239
|
+
The card itself is your game's design: how it looks, which button leads and when it appears. The
|
|
1240
|
+
platform sets no rule on them.
|
|
1228
1241
|
|
|
1229
1242
|
### Reading the player's balance
|
|
1230
1243
|
|
package/docs/manifest.json
CHANGED
|
@@ -18,8 +18,8 @@
|
|
|
18
18
|
},
|
|
19
19
|
{
|
|
20
20
|
"file": "arcade-and-store.md",
|
|
21
|
-
"sha256": "
|
|
22
|
-
"updated": "2026-11-
|
|
21
|
+
"sha256": "d5b3d7753bf00b87d39f16741e743605214059d4b4a499a69b3a5e7350ec288f",
|
|
22
|
+
"updated": "2026-11-13T00:22:09Z"
|
|
23
23
|
},
|
|
24
24
|
{
|
|
25
25
|
"file": "arcade-avatars.md",
|
|
@@ -48,8 +48,8 @@
|
|
|
48
48
|
},
|
|
49
49
|
{
|
|
50
50
|
"file": "changelog.md",
|
|
51
|
-
"sha256": "
|
|
52
|
-
"updated": "2026-11-
|
|
51
|
+
"sha256": "8339a777114f7d8ad7acfcc61d47af25b3e027dc3bdc42c092e9064bd578f78e",
|
|
52
|
+
"updated": "2026-11-13T03:35:34Z"
|
|
53
53
|
},
|
|
54
54
|
{
|
|
55
55
|
"file": "daily-challenges.md",
|
|
@@ -78,8 +78,8 @@
|
|
|
78
78
|
},
|
|
79
79
|
{
|
|
80
80
|
"file": "getting-started.md",
|
|
81
|
-
"sha256": "
|
|
82
|
-
"updated": "2026-11-
|
|
81
|
+
"sha256": "35401b63060f02364bad539eef2718c5907e8e418b0456582759b65e2313bbe2",
|
|
82
|
+
"updated": "2026-11-13T03:35:34Z"
|
|
83
83
|
},
|
|
84
84
|
{
|
|
85
85
|
"file": "graphics-route.md",
|
|
@@ -88,8 +88,8 @@
|
|
|
88
88
|
},
|
|
89
89
|
{
|
|
90
90
|
"file": "inventory-and-catalogs.md",
|
|
91
|
-
"sha256": "
|
|
92
|
-
"updated": "2026-11-
|
|
91
|
+
"sha256": "f32ea4291e47763e7ec664f1c1fee71e0cd1b443f4c617194ab3d27f556aec79",
|
|
92
|
+
"updated": "2026-11-13T00:16:49Z"
|
|
93
93
|
},
|
|
94
94
|
{
|
|
95
95
|
"file": "leaderboards.md",
|
|
@@ -113,8 +113,8 @@
|
|
|
113
113
|
},
|
|
114
114
|
{
|
|
115
115
|
"file": "platform-rules.md",
|
|
116
|
-
"sha256": "
|
|
117
|
-
"updated": "2026-11-
|
|
116
|
+
"sha256": "ba07fc48837dce46500478ae958e03394e7af9650f8314a1868d30e4c3b70354",
|
|
117
|
+
"updated": "2026-11-12T20:29:05Z"
|
|
118
118
|
},
|
|
119
119
|
{
|
|
120
120
|
"file": "player-sign-in.md",
|
|
@@ -143,8 +143,8 @@
|
|
|
143
143
|
},
|
|
144
144
|
{
|
|
145
145
|
"file": "server-bundles.md",
|
|
146
|
-
"sha256": "
|
|
147
|
-
"updated": "2026-11-
|
|
146
|
+
"sha256": "4eca8bb4a1b9018257bb9411ecaf3e65cdef472d8b0cc5653b49ab991680eb6c",
|
|
147
|
+
"updated": "2026-11-13T03:35:34Z"
|
|
148
148
|
},
|
|
149
149
|
{
|
|
150
150
|
"file": "serverless-functions-js.md",
|
package/docs/platform-rules.md
CHANGED
|
@@ -357,8 +357,9 @@ A game **must not gate** anything on the multiplier:
|
|
|
357
357
|
`requires: [{ kind: 'holds_pass', pass: 'vip' }]` on the item, recipe, bonus or track, which the
|
|
358
358
|
platform checks when it runs; or a decision your function or server makes from its pass read
|
|
359
359
|
(`gem.me.passes` in a function, `gem.match.playerPasses` on a server). The browser's
|
|
360
|
-
`gem.me.passes` draws the door; it never opens it.
|
|
361
|
-
[inventory-and-catalogs.md](inventory-and-catalogs.md#offering-vip-before-a-reward-the-upsell)
|
|
360
|
+
`gem.me.passes` draws the door; it never opens it. To offer VIP at a reward, offer it before the
|
|
361
|
+
grant, as [inventory-and-catalogs.md](inventory-and-catalogs.md#offering-vip-before-a-reward-the-upsell)
|
|
362
|
+
describes.
|
|
362
363
|
|
|
363
364
|
Three reasons not to gate on the multiplier, any one of which would be enough. It is **advisory**:
|
|
364
365
|
the browser value is pushed on connect and on change, so it can lag, and the platform's own figure
|
package/docs/server-bundles.md
CHANGED
|
@@ -80,8 +80,8 @@ To check the download yourself: the package npm serves is, byte for byte, the fi
|
|
|
80
80
|
also publishes on its download host, with the hash beside it:
|
|
81
81
|
|
|
82
82
|
```bash
|
|
83
|
-
curl -fsSL -O https://downloads.gemarcade.com/sdk/v24.0
|
|
84
|
-
curl -fsSL https://downloads.gemarcade.com/sdk/v24.0
|
|
83
|
+
curl -fsSL -O https://downloads.gemarcade.com/sdk/v24.1.0/gem-web-sdk.tgz
|
|
84
|
+
curl -fsSL https://downloads.gemarcade.com/sdk/v24.1.0/gem-web-sdk.tgz.sri
|
|
85
85
|
openssl dgst -sha384 -binary gem-web-sdk.tgz | openssl base64 -A
|
|
86
86
|
```
|
|
87
87
|
|
package/package.json
CHANGED
package/schema/bridge.v1.json
CHANGED
|
@@ -6099,11 +6099,15 @@
|
|
|
6099
6099
|
"multipliers it carries in `earnMultipliers`, so a game can find VIP by",
|
|
6100
6100
|
"slug and preview what it would add without naming a SKU or a number.",
|
|
6101
6101
|
"A parent lists a pass only where it would open that pass's sheet: an",
|
|
6102
|
-
"offering with `visible_to_games
|
|
6103
|
-
"
|
|
6102
|
+
"offering with `visible_to_games`. A player declining its sheet never",
|
|
6103
|
+
"takes it out of the listing. For a KNOWN MINOR it leaves the pass out",
|
|
6104
6104
|
"exactly as if it were not on sale - the same `ok`, the same other",
|
|
6105
6105
|
"offers, no reason - so a game can never tell a minor from a pass that",
|
|
6106
|
-
"is not sold here. A
|
|
6106
|
+
"is not sold here. A HOLDER may be listed the pass or not, as the",
|
|
6107
|
+
"parent's store decides, and a frame must not assume either. The rule",
|
|
6108
|
+
"is only that the sheet follows the listing: a listed pass opens its",
|
|
6109
|
+
"sheet, and gem.requestStoreOfferSheet for a pass that is not listed is",
|
|
6110
|
+
"answered `unavailable` with nothing shown.",
|
|
6107
6111
|
"",
|
|
6108
6112
|
"COIN PACKS ARE `gem_pack`. The kind enum is closed (a fifth value",
|
|
6109
6113
|
"would be refused whole by an N-1 frame), so a pack of any platform",
|